From 2cee30f2e9ec634ec5cd2fad3fe41036d18fa225 Mon Sep 17 00:00:00 2001 From: Cristian Necula Date: Wed, 6 Dec 2023 03:35:24 +0200 Subject: [PATCH] test: re-implement tests using perform --- cosmoz-image-viewer.js | 42 +++-- lib/hooks/use-cosmoz-image-viewer.js | 7 +- package-lock.json | 169 ++++++++++++++++- package.json | 2 +- test/basic.test.js | 248 ++++++++++++------------ test/detach.test.js | 271 ++++++++++++++++++++------- test/helpers/index.js | 44 +++-- 7 files changed, 545 insertions(+), 238 deletions(-) diff --git a/cosmoz-image-viewer.js b/cosmoz-image-viewer.js index d8dddaf..8598a02 100644 --- a/cosmoz-image-viewer.js +++ b/cosmoz-image-viewer.js @@ -50,6 +50,7 @@ const renderCosmozImageViewer = ({ onPrintPdf, detach, detached, + syncDetachedState, }) => detached ? nothing @@ -70,8 +71,12 @@ const renderCosmozImageViewer = ({ ${when( shouldShow(host.showNav, total, 2), () => html` - - + + `, )}
@@ -97,16 +102,28 @@ const renderCosmozImageViewer = ({ ${launch} `, )} - - + ${when( + shouldShow(true, total), + () => + html``, + )} + ${when( + shouldShow(true, total), + () => + html``, + )} ${when( shouldShow(host.showFullscreen, total), () => @@ -147,6 +164,7 @@ const renderCosmozImageViewer = ({ @close=${closeFullscreen} .currentImageIndex=${currentImageIndex} @current-image-index-changed=${syncImageIndex} + @detached-changed=${syncDetachedState} ?show-detach=${host.showDetach} ?loop=${host.loop} >`, diff --git a/lib/hooks/use-cosmoz-image-viewer.js b/lib/hooks/use-cosmoz-image-viewer.js index 6036e45..c0f77ac 100644 --- a/lib/hooks/use-cosmoz-image-viewer.js +++ b/lib/hooks/use-cosmoz-image-viewer.js @@ -64,13 +64,15 @@ const onImageError = (e) => { loop: host.loop, onDetach: () => setDetached(true), onClose: () => setDetached(false), - }); + }), + syncDetachedState = (ev) => setDetached(ev.detail.value); useNotifyProperty('currentImageIndex', index, [index]); + useEffect(() => { host.toggleAttribute('hidden', detached); }, [detached]); - + useNotifyProperty('detached', detached, [detached]); useImperativeApi({ syncState: detach }, [detach]); return { @@ -92,6 +94,7 @@ const onImageError = (e) => { onPrintPdf: () => print({ images }), detached, detach, + syncDetachedState, }; }; diff --git a/package-lock.json b/package-lock.json index 5e3924d..a912f9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "6.0.0", "license": "Apache-2.0", "dependencies": { - "@neovici/cfg": "^1.42.0", + "@neovici/cfg": "^1.43.0", "@neovici/cosmoz-i18next": "^3.2.2", "@neovici/cosmoz-slider": "^4.0.0", "@neovici/cosmoz-utils": "^5.0.0", @@ -2884,15 +2884,17 @@ } }, "node_modules/@neovici/cfg": { - "version": "1.42.0", - "resolved": "https://registry.npmjs.org/@neovici/cfg/-/cfg-1.42.0.tgz", - "integrity": "sha512-VbTJ2eT1vAq2TDby7Y68069EbcRkkwSSePwyreKdV9UZkrjNgtxWjV2WbXXBXjTmkkkf4UIs+ymXuJRbjVXslw==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@neovici/cfg/-/cfg-1.43.0.tgz", + "integrity": "sha512-4uFrBbT/OX35b/XuLQVATPTTuyovGIQNJhbfr/tORkaMtuyhStbYOBG6jjOkicAHi1C+LdEDAyYSHvlzooTzhQ==", "dependencies": { + "@playwright/test": "^1.40.1", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "@web/dev-server": "^0.4.0", "@web/dev-server-esbuild": "^1.0.0", "@web/test-runner": "^0.18.0", + "@web/test-runner-commands": "^0.9.0", "@web/test-runner-playwright": "^0.11.0", "eslint": "^8.42.0", "eslint-config-prettier": "^9.0.0", @@ -2950,6 +2952,11 @@ } } }, + "node_modules/@neovici/cfg/node_modules/@types/convert-source-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/convert-source-map/-/convert-source-map-2.0.3.tgz", + "integrity": "sha512-ag0BfJLZf6CQz8VIuRIEYQ5Ggwk/82uvTQf27RcpyDNbY0Vw49LIPqAxk5tqYfrCs9xDaIMvl4aj7ZopnYL8bA==" + }, "node_modules/@neovici/cfg/node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -2960,6 +2967,17 @@ "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==" }, + "node_modules/@neovici/cfg/node_modules/@web/browser-logs": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@web/browser-logs/-/browser-logs-0.4.0.tgz", + "integrity": "sha512-/EBiDAUCJ2DzZhaFxTPRIznEPeafdLbXShIL6aTu7x73x7ZoxSDv7DGuTsh2rWNMUa4+AKli4UORrpyv6QBOiA==", + "dependencies": { + "errorstacks": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@neovici/cfg/node_modules/@web/config-loader": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@web/config-loader/-/config-loader-0.3.1.tgz", @@ -3052,6 +3070,54 @@ "node": ">=18.0.0" } }, + "node_modules/@neovici/cfg/node_modules/@web/test-runner-commands": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@web/test-runner-commands/-/test-runner-commands-0.9.0.tgz", + "integrity": "sha512-zeLI6QdH0jzzJMDV5O42Pd8WLJtYqovgdt0JdytgHc0d1EpzXDsc7NTCJSImboc2NcayIsWAvvGGeRF69SMMYg==", + "dependencies": { + "@web/test-runner-core": "^0.13.0", + "mkdirp": "^1.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@neovici/cfg/node_modules/@web/test-runner-core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@web/test-runner-core/-/test-runner-core-0.13.0.tgz", + "integrity": "sha512-mUrETPg9n4dHWEk+D46BU3xVhQf+ljT4cG7FSpmF7AIOsXWgWHoaXp6ReeVcEmM5fmznXec2O/apTb9hpGrP3w==", + "dependencies": { + "@babel/code-frame": "^7.12.11", + "@types/babel__code-frame": "^7.0.2", + "@types/co-body": "^6.1.0", + "@types/convert-source-map": "^2.0.0", + "@types/debounce": "^1.2.0", + "@types/istanbul-lib-coverage": "^2.0.3", + "@types/istanbul-reports": "^3.0.0", + "@web/browser-logs": "^0.4.0", + "@web/dev-server-core": "^0.7.0", + "chokidar": "^3.4.3", + "cli-cursor": "^3.1.0", + "co-body": "^6.1.0", + "convert-source-map": "^2.0.0", + "debounce": "^1.2.0", + "dependency-graph": "^0.11.0", + "globby": "^11.0.1", + "ip": "^1.1.5", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.0.2", + "log-update": "^4.0.0", + "nanocolors": "^0.2.1", + "nanoid": "^3.1.25", + "open": "^8.0.2", + "picomatch": "^2.2.2", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@neovici/cfg/node_modules/array-back": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", @@ -3074,6 +3140,11 @@ "node": ">=12.20.0" } }, + "node_modules/@neovici/cfg/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "node_modules/@neovici/cfg/node_modules/es-module-lexer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", @@ -3483,6 +3554,20 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@playwright/test": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.1.tgz", + "integrity": "sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==", + "dependencies": { + "playwright": "1.40.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -22377,15 +22462,17 @@ "dev": true }, "@neovici/cfg": { - "version": "1.42.0", - "resolved": "https://registry.npmjs.org/@neovici/cfg/-/cfg-1.42.0.tgz", - "integrity": "sha512-VbTJ2eT1vAq2TDby7Y68069EbcRkkwSSePwyreKdV9UZkrjNgtxWjV2WbXXBXjTmkkkf4UIs+ymXuJRbjVXslw==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@neovici/cfg/-/cfg-1.43.0.tgz", + "integrity": "sha512-4uFrBbT/OX35b/XuLQVATPTTuyovGIQNJhbfr/tORkaMtuyhStbYOBG6jjOkicAHi1C+LdEDAyYSHvlzooTzhQ==", "requires": { + "@playwright/test": "^1.40.1", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "@web/dev-server": "^0.4.0", "@web/dev-server-esbuild": "^1.0.0", "@web/test-runner": "^0.18.0", + "@web/test-runner-commands": "^0.9.0", "@web/test-runner-playwright": "^0.11.0", "eslint": "^8.42.0", "eslint-config-prettier": "^9.0.0", @@ -22421,6 +22508,11 @@ "picomatch": "^2.3.1" } }, + "@types/convert-source-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/convert-source-map/-/convert-source-map-2.0.3.tgz", + "integrity": "sha512-ag0BfJLZf6CQz8VIuRIEYQ5Ggwk/82uvTQf27RcpyDNbY0Vw49LIPqAxk5tqYfrCs9xDaIMvl4aj7ZopnYL8bA==" + }, "@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -22431,6 +22523,14 @@ "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==" }, + "@web/browser-logs": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@web/browser-logs/-/browser-logs-0.4.0.tgz", + "integrity": "sha512-/EBiDAUCJ2DzZhaFxTPRIznEPeafdLbXShIL6aTu7x73x7ZoxSDv7DGuTsh2rWNMUa4+AKli4UORrpyv6QBOiA==", + "requires": { + "errorstacks": "^2.2.0" + } + }, "@web/config-loader": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@web/config-loader/-/config-loader-0.3.1.tgz", @@ -22504,6 +22604,48 @@ "parse5": "^6.0.1" } }, + "@web/test-runner-commands": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@web/test-runner-commands/-/test-runner-commands-0.9.0.tgz", + "integrity": "sha512-zeLI6QdH0jzzJMDV5O42Pd8WLJtYqovgdt0JdytgHc0d1EpzXDsc7NTCJSImboc2NcayIsWAvvGGeRF69SMMYg==", + "requires": { + "@web/test-runner-core": "^0.13.0", + "mkdirp": "^1.0.4" + } + }, + "@web/test-runner-core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@web/test-runner-core/-/test-runner-core-0.13.0.tgz", + "integrity": "sha512-mUrETPg9n4dHWEk+D46BU3xVhQf+ljT4cG7FSpmF7AIOsXWgWHoaXp6ReeVcEmM5fmznXec2O/apTb9hpGrP3w==", + "requires": { + "@babel/code-frame": "^7.12.11", + "@types/babel__code-frame": "^7.0.2", + "@types/co-body": "^6.1.0", + "@types/convert-source-map": "^2.0.0", + "@types/debounce": "^1.2.0", + "@types/istanbul-lib-coverage": "^2.0.3", + "@types/istanbul-reports": "^3.0.0", + "@web/browser-logs": "^0.4.0", + "@web/dev-server-core": "^0.7.0", + "chokidar": "^3.4.3", + "cli-cursor": "^3.1.0", + "co-body": "^6.1.0", + "convert-source-map": "^2.0.0", + "debounce": "^1.2.0", + "dependency-graph": "^0.11.0", + "globby": "^11.0.1", + "ip": "^1.1.5", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.0.2", + "log-update": "^4.0.0", + "nanocolors": "^0.2.1", + "nanoid": "^3.1.25", + "open": "^8.0.2", + "picomatch": "^2.2.2", + "source-map": "^0.7.3" + } + }, "array-back": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", @@ -22520,6 +22662,11 @@ "typical": "^7.1.1" } }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "es-module-lexer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", @@ -22844,6 +22991,14 @@ "tslib": "^2.4.0" } }, + "@playwright/test": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.1.tgz", + "integrity": "sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==", + "requires": { + "playwright": "1.40.1" + } + }, "@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", diff --git a/package.json b/package.json index 014a59d..b94ca8d 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ } }, "dependencies": { - "@neovici/cfg": "^1.42.0", + "@neovici/cfg": "^1.43.0", "@neovici/cosmoz-i18next": "^3.2.2", "@neovici/cosmoz-slider": "^4.0.0", "@neovici/cosmoz-utils": "^5.0.0", diff --git a/test/basic.test.js b/test/basic.test.js index 79502c8..f5da6e2 100644 --- a/test/basic.test.js +++ b/test/basic.test.js @@ -1,169 +1,151 @@ -import { - assert, fixture, html, aTimeout, nextFrame -} from '@open-wc/testing'; - import '../cosmoz-image-viewer.js'; -import sinon from 'sinon'; - -const cosmozImageViewerFixture = html``; +import { perform } from '@neovici/cfg/web/perform.js'; +import { assert, fixture, html } from '@open-wc/testing'; +import { absolute, ignoreResizeLoopError } from './helpers/index.js'; -sinon.assert.expose(assert, { prefix: '' }); - -// eslint-disable-next-line max-lines-per-function suite('cosmoz-image-viewer', () => { - let imageViewer; - + suiteSetup(ignoreResizeLoopError); setup(async () => { - imageViewer = await fixture(cosmozImageViewerFixture); - imageViewer.images = [ - '/stories/images/stockholm.jpg', - '/stories/images/strasbourg.jpg', - '/stories/images/cosmos1.jpg' - ]; - await nextFrame(); + await fixture( + html``, + ); }); - test('nextImage updates selected', async () => { - assert.equal(imageViewer.selectedImageNumber, 1); - imageViewer.nextImage(); - await nextFrame(); - assert.equal(imageViewer.selectedImageNumber, 2); - }); + test('can move through images', async () => { + await perform(async ({ page, expect }) => { + await expect( + page.locator('img[src$="/stories/images/stockholm.jpg"]'), + ).toBeVisible(); - test('previousImage updates selected', async () => { - imageViewer.nextImage(); + await page.locator('cosmoz-image-viewer').hover(); + await page.locator('button[name="next"]').click(); + await expect( + page.locator('img[src$="/stories/images/strasbourg.jpg"]'), + ).toBeVisible(); - await nextFrame(); - assert.equal(imageViewer.selectedImageNumber, 2); + await page.locator('button[name="next"]').click(); + await expect( + page.locator('img[src$="/stories/images/cosmos1.jpg"]'), + ).toBeVisible(); - imageViewer.nextImage(); + // wait for the slider to finish the animation + await expect(page.locator('img')).toHaveCount(1); - await nextFrame(); - assert.equal(imageViewer.selectedImageNumber, 3); + await page.locator('button[name="prev"]').click(); - imageViewer.previousImage(); - await nextFrame(); + await expect( + page.locator('img[src$="/stories/images/strasbourg.jpg"]'), + ).toBeVisible(); - assert.equal(imageViewer.selectedImageNumber, 2); + await page.locator('button[name="prev"]').click(); + await expect( + page.locator('img[src$="/stories/images/stockholm.jpg"]'), + ).toBeVisible(); + }); }); - test('openFullScreen creates dialog', () => { - imageViewer.openFullscreen(); - const dialog = imageViewer.imageOverlay; - assert.isDefined(dialog, 'Expected openFullscreen to create dialog if it was undefined'); - assert.equal(dialog.tagName, 'COSMOZ-IMAGE-VIEWER-OVERLAY'); - assert.isFunction(dialog.open, 'Expected openFullscreen to create dialog and dilog.open to be a function'); - assert.isTrue(dialog.opened); - assert.equal(dialog.id, 'cosmoz-image-viewer-overlay'); - assert.equal(dialog.images.length, 3); - assert.equal(dialog.images, imageViewer.images); - }); + test('can open and close fullscreen', async () => { + await perform(async ({ page, expect }) => { + await page.locator('cosmoz-image-viewer').hover(); + await page.locator('button[title="Fullscreen image"]').click(); - test('pdf creation works', async () => { - const blob = await imageViewer.downloadPdf(imageViewer.images); - assert.isAbove(blob.size, 10000); - }); + await expect(page.locator('cosmoz-image-viewer')).toHaveCount(2); + await expect( + page + .locator('cosmoz-image-viewer[fullscreen]') + .locator('img[src$="/stories/images/stockholm.jpg"]'), + ).toBeVisible(); - test('_close fires event close-tapped', () => { - let called = false; - imageViewer.addEventListener('close-tapped', () => { - called = true; + await page.locator('button[title="Close fullscreen"]').click(); + await expect(page.locator('cosmoz-image-viewer')).toHaveCount(1); }); - imageViewer._close(); - assert.isTrue(called); }); -}); - -suite('cosmoz-image-viewer-no-images', () => { - let imageViewer; - setup(async () => { - imageViewer = await fixture(cosmozImageViewerFixture); - Object.assign(imageViewer, { - images: [], - showZoom: true, - showNav: true, - showFullscreen: true, - showDetach: true + test('can create pdf', async () => { + const filename = await perform(async ({ page }) => { + const downloadPromise = page.waitForEvent('download'); + await page.locator('button[title="Download images"]').click(); + const download = await downloadPromise; + return download.suggestedFilename(); }); - }); - - test('action items are hidden', () => { - assert.equal(imageViewer._showNav, false); - assert.equal(imageViewer._showZoom, false); - assert.equal(imageViewer._showFullscreen, false); - assert.equal(imageViewer._showDetach, false); - }); - test('no image info is shown', () => { - assert.equal(imageViewer._hideNoImageInfo, false); + assert.equal(filename, 'archive.pdf'); }); }); -suite('cosmoz-image-viewer-loading-error', () => { - let imageViewer; - const errorDiv = () => imageViewer.$.slider.querySelector('.error'), - displayNone = element => element.offsetParent === null || element.getAttribute('hidden') === 'true'; - +suite('cosmoz-image-viewer with no images', () => { setup(async () => { - imageViewer = await fixture(cosmozImageViewerFixture); - imageViewer.images = [ - 'xyz.jpg', - '/stories/images/stockholm.jpg', - '/stories/images/strasbourg.jpg' - ]; - await nextFrame(); + await fixture( + html``, + ); }); - test('error is shown', async () => { - const errEl = errorDiv(); - await aTimeout(500); - assert.equal(displayNone(errEl), false); - }); - - test('error is hidden if next image loaded successfully', async () => { - imageViewer.nextImage(); - await nextFrame(); - await aTimeout(500); - const errEl = errorDiv(); - assert.equal(displayNone(errEl), true); + test('action items are hidden', async () => { + await perform(async ({ page, expect }) => { + await page.locator('cosmoz-image-viewer').hover(); + await expect(page.locator('button[name="next"]')).not.toBeVisible(); + await expect(page.locator('button[name="prev"]')).not.toBeVisible(); + await expect( + page.locator('button[title="Fullscreen image"]'), + ).not.toBeVisible(); + await expect( + page.locator('button[title="Download images"]'), + ).not.toBeVisible(); + await expect( + page.locator('button[title="Detach image to separate window"]'), + ).not.toBeVisible(); + }); }); }); -suite('cosmoz-image-viewer-overlay', () => { - let overlay; - +suite('cosmoz-image-viewer-loading-error', () => { setup(async () => { - overlay = await fixture(html``); - overlay.images = [ - '/stories/images/stockholm.jpg', - '/stories/images/strasbourg.jpg', - '/stories/images/cosmos1.jpg' - ]; + await fixture( + html``, + ); }); - test('_trackHandler does not call close if detail state is not end', () => { - const event = new CustomEvent('testEvent', { - detail: { - dy: 1000 - } - }), - spyClose = sinon.spy(overlay, 'close'); - overlay._trackHandler(event); - assert.notCalled(spyClose); - }); - test('_trackHandler does call close', () => { - const event = new CustomEvent('testEvent', { - detail: { - state: 'end', - dy: 1000 - } - }), - spyClose = sinon.spy(overlay, 'close'); - - overlay._trackHandler(event); - assert.calledOnce(spyClose); + test('error is shown', async () => { + await perform(async ({ page, expect }) => { + await expect( + page.locator('"An error occurred while loading the image."'), + ).toBeVisible(); + + await page.locator('cosmoz-image-viewer').hover(); + await page.locator('button[name="next"]').click(); + await expect( + page.locator('img[src$="/stories/images/stockholm.jpg"]'), + ).toBeVisible(); + }); }); - }); diff --git a/test/detach.test.js b/test/detach.test.js index d8eaea8..691f6ae 100644 --- a/test/detach.test.js +++ b/test/detach.test.js @@ -1,75 +1,212 @@ import '../cosmoz-image-viewer.js'; -import { - assert, fixture, html -} from '@open-wc/testing'; -import sinon from 'sinon'; - -sinon.assert.expose(assert, { prefix: '' }); - -const createImageViewer = async () => { - const el = await fixture(html``); - - el.images = [ - '/stories/images/stockholm.jpg', - '/stories/images/strasbourg.jpg', - '/stories/images/cosmos1.jpg' - ]; - - return el; -}; - -suite('cosmoz-image-viewer', () => { - let imageViewer; - - setup(async function () { - imageViewer = await createImageViewer(); - - const w = imageViewer.detach(); - if (w == null) { - /* eslint-disable-next-line no-console */ - console.warn('Only gets tested without popup blocker'); - /* eslint-disable-next-line no-invalid-this */ - this.skip(); - } - w.close(); - }); - - teardown(() => { - imageViewer.window?.close(); - }); - - test('detaching works', () => { - const w = imageViewer.detach(); - assert.isNotNull(w); - }); - - test('detaching to existing window works', () => { - const w = imageViewer.detach(), - w2 = imageViewer.detach(); - assert.isNotNull(w); - assert.deepEqual(w, w2); +import { perform } from '@neovici/cfg/web/perform.js'; +import { fixture, html } from '@open-wc/testing'; +import { absolute, ignoreResizeLoopError } from './helpers/index.js'; + +suite('cosmoz-image-viewer detach', () => { + suiteSetup(ignoreResizeLoopError); + + test('can detach to separate window', async () => { + await fixture( + html``, + ); + + await perform(async ({ page, expect }) => { + // detach to popup + await page.locator('cosmoz-image-viewer').hover(); + const popupPromise = page.waitForEvent('popup'); + await page + .locator('button[title="Detach image to separate window"]') + .click(); + const popup = await popupPromise; + + // the viewer is visible only in the popup + await expect(popup.locator('cosmoz-image-viewer')).toBeVisible(); + await expect( + popup.locator('img[src$="/stories/images/stockholm.jpg"]'), + ).toBeVisible(); + await expect( + page.locator('img[src$="/stories/images/stockholm.jpg"]'), + ).not.toBeVisible(); + + // you can navigate the images in the popup + await popup.locator('cosmoz-image-viewer').hover(); + await popup.locator('button[name="next"]').click(); + await expect( + popup.locator('img[src$="/stories/images/strasbourg.jpg"]'), + ).toBeVisible(); + + // you can close the popp + await popup.close({ runBeforeUnload: true }); + + // the main instance is synchronized to the popup + await expect( + page.locator('img[src$="/stories/images/strasbourg.jpg"]'), + ).toBeVisible(); + }); }); - test('shared detaching works', async () => { - const imageViewer2 = await createImageViewer(), - w = imageViewer.detach(), - w2 = imageViewer2.detach(); - assert.isNotNull(w); - assert.deepEqual(w, w2); + test('shared detach functionality', async () => { + await fixture( + html` + `, + ); + + await perform(async ({ page, expect }) => { + // detach to popup + await page.locator('#first').hover(); + const popupPromise = page.waitForEvent('popup'); + await page + .locator('#first button[title="Detach image to separate window"]') + .click(); + const popup = await popupPromise; + + // the viewer is visible only in the popup + await expect(popup.locator('cosmoz-image-viewer')).toBeVisible(); + await expect( + popup.locator('img[src$="/stories/images/stockholm.jpg"]'), + ).toBeVisible(); + await expect( + page.locator('#first img[src$="/stories/images/stockholm.jpg"]'), + ).not.toBeVisible(); + + // the other viewer is not affected + await expect( + page.locator('#second img[src$="/stories/images/cosmos1.jpg"]'), + ).toBeVisible(); + + // the detach popup is shared by all instances + await page.locator('#second').hover(); + await page + .locator('#second button[title="Detach image to separate window"]') + .click(); + await expect( + popup.locator('img[src$="/stories/images/cosmos1.jpg"]'), + ).toBeVisible(); + await expect( + page.locator('#first img[src$="/stories/images/stockholm.jpg"]'), + ).toBeVisible(); + await expect( + page.locator('#second img[src$="/stories/images/cosmos1.jpg"]'), + ).not.toBeVisible(); + + // you can navigate the popup slideshow + await popup.locator('cosmoz-image-viewer').hover(); + await popup.locator('button[name="next"]').click(); + await expect( + popup.locator('img[src$="/stories/images/cosmos2.jpg"]'), + ).toBeVisible(); + + // you can close the popup + await popup.locator('cosmoz-image-viewer').hover(); + await popup.close({ runBeforeUnload: true }); + + // the slideshow state is synced to the original instance, independently + await expect( + page.locator('#first img[src$="/stories/images/stockholm.jpg"]'), + ).toBeVisible(); + await expect( + page.locator('#second img[src$="/stories/images/cosmos2.jpg"]'), + ).toBeVisible(); + }); }); - test('detach from fullscreen works', () => { - assert.isFalse(imageViewer.hidden); - assert.isFalse(imageViewer.hasWindow); - - imageViewer.openFullscreen(); - assert.isFalse(imageViewer.hasWindow); - - const ov = document.querySelector('cosmoz-image-viewer-overlay'); - - ov.shadowRoot.querySelector('cosmoz-image-viewer').detach(); - assert.isTrue(imageViewer.hasWindow); - assert.isTrue(imageViewer.hidden); + test('detach from fullscreen works', async () => { + await fixture( + html``, + ); + + await perform(async ({ page, expect }) => { + // go fullscreen + await page.locator('cosmoz-image-viewer').hover(); + await page.locator('button[title="Fullscreen image"]').click(); + + await expect(page.locator('cosmoz-image-viewer')).toHaveCount(2); + await expect( + page.locator('[fullscreen] img[src$="/stories/images/stockholm.jpg"]'), + ).toBeVisible(); + + // detach to popup + await page.locator('[fullscreen]').hover(); + const popupPromise = page.waitForEvent('popup'); + await page + .locator('[fullscreen] button[title="Detach image to separate window"]') + .click(); + const popup = await popupPromise; + + // expect to see the image in the popup + await expect( + popup.locator('img[src$="/stories/images/stockholm.jpg"]'), + ).toBeVisible(); + + // expect to not see the image in the page + await expect( + page.locator('img[src$="/stories/images/stockholm.jpg"]'), + ).not.toBeVisible(); + + // close the popup + await popup.close({ runBeforeUnload: true }); + + // expect to see the image in fullscreen mode + await expect( + page + .locator('cosmoz-image-viewer[fullscreen]') + .locator('img[src$="/stories/images/stockholm.jpg"]'), + ).toBeVisible(); + + // exit fullscreen + await page.locator('cosmoz-image-viewer[fullscreen]').hover(); + await page.locator('button[title="Close fullscreen"]').click(); + + // expect to see the image in normal mode + await expect( + page.locator('img[src$="/stories/images/stockholm.jpg"]'), + ).toBeVisible(); + }); }); }); diff --git a/test/helpers/index.js b/test/helpers/index.js index 92c7ab3..b5c1c94 100644 --- a/test/helpers/index.js +++ b/test/helpers/index.js @@ -1,36 +1,33 @@ import { assert } from '@open-wc/testing'; -export const - - HAS_NEW_TOUCH = (() => { +export const HAS_NEW_TOUCH = (() => { try { return Boolean(new TouchEvent('x')); - } catch (_) { /**/ } + } catch (_) { + /**/ + } return false; })(), - - makeTouches = (xyList, node) => { let id = 0; - return xyList.map(xy => { + return xyList.map((xy) => { const touchInit = { identifier: id++, target: node, pageX: xy.x, - pageY: xy.y + pageY: xy.y, }; return HAS_NEW_TOUCH ? new window.Touch(touchInit) : touchInit; }); }, - makeMultiTouchEvent = (type, xyList, node) => { const touches = makeTouches(xyList, node), touchEventInit = { touches, targetTouches: touches, - changedTouches: touches + changedTouches: touches, }; let event; @@ -43,7 +40,7 @@ export const bubbles: true, cancelable: true, // Allow event to go outside a ShadowRoot. - composed: true + composed: true, }); // eslint-disable-next-line guard-for-in for (const property in touchEventInit) { @@ -53,11 +50,26 @@ export const node.dispatchEvent(event); }, - assertFuzzyMatch = (obj, targetObj) => { assert.isFalse( - Object.entries(targetObj) - .some(([key, value]) => Math.abs(obj[key] - value) >= 2), - `fuzzyMatch ${ JSON.stringify(obj) } does not match ${ JSON.stringify(targetObj) }` + Object.entries(targetObj).some( + ([key, value]) => Math.abs(obj[key] - value) >= 2, + ), + `fuzzyMatch ${JSON.stringify(obj)} does not match ${JSON.stringify( + targetObj, + )}`, ); - }; + }, + ignoreResizeLoopError = () => { + const e = window.onerror; + window.onerror = function (err) { + if ( + err === 'ResizeObserver loop limit exceeded' || + err === 'ResizeObserver loop completed with undelivered notifications.' + ) { + return true; + } + return e(...arguments); + }; + }, + absolute = (a) => window.location.origin + a;