-
-
Notifications
You must be signed in to change notification settings - Fork 202
Added Puppeteer UI interaction tests #1713
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
8a34823
3c11aed
edfcacc
f005cb5
c5e3650
2e59cc6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,6 +6,55 @@ describe("Universal Viewer", () => { | |||||||||||||||||
| let browser; | ||||||||||||||||||
| let page; | ||||||||||||||||||
|
|
||||||||||||||||||
| const getRotationFromNavigator = async () => { | ||||||||||||||||||
| return await page.evaluate(() => { | ||||||||||||||||||
| const el = document.querySelector(".displayregioncontainer"); | ||||||||||||||||||
| if (!el) return 0; | ||||||||||||||||||
|
|
||||||||||||||||||
| const transform = el.style.transform || ""; | ||||||||||||||||||
| const match = transform.match(/rotate\((-?\d+(?:\.\d+)?)deg\)/); | ||||||||||||||||||
| if (!match) return 0; | ||||||||||||||||||
|
|
||||||||||||||||||
| const deg = Number(match[1]); | ||||||||||||||||||
| return ((deg % 360) + 360) % 360; | ||||||||||||||||||
| }); | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| const getCanvasValue = (url) => { | ||||||||||||||||||
| const match = url.match(/(?:^|[?&#])(cv|canvas|page)=([^&#]*)/); | ||||||||||||||||||
| if (!match) return 0; | ||||||||||||||||||
|
|
||||||||||||||||||
| const rawValue = match[2]; | ||||||||||||||||||
| if (rawValue === "") return 0; | ||||||||||||||||||
|
|
||||||||||||||||||
| const value = Number(rawValue); | ||||||||||||||||||
| if (Number.isNaN(value)) return 0; | ||||||||||||||||||
|
|
||||||||||||||||||
| return value; | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| const waitForCanvasValue = async (page, expected) => { | ||||||||||||||||||
| await page.waitForFunction( | ||||||||||||||||||
| (expectedValue) => { | ||||||||||||||||||
| const match = window.location.href.match(/(?:^|[?&#])(cv|canvas|page)=([^&#]*)/); | ||||||||||||||||||
| if (!match) return expectedValue === 0; | ||||||||||||||||||
|
|
||||||||||||||||||
| const rawValue = match[2]; | ||||||||||||||||||
| if (rawValue === "") return expectedValue === 0; | ||||||||||||||||||
|
|
||||||||||||||||||
| const value = Number(rawValue); | ||||||||||||||||||
| return value === expectedValue; | ||||||||||||||||||
| }, | ||||||||||||||||||
| {}, | ||||||||||||||||||
| expected | ||||||||||||||||||
| ); | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| const getXywhValue = (url) => { | ||||||||||||||||||
| const match = url.match(/[?&#]xywh=([^&]+)/); | ||||||||||||||||||
| return match ? decodeURIComponent(match[1]) : null; | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| beforeAll(async () => { | ||||||||||||||||||
| browser = await puppeteer.launch(); | ||||||||||||||||||
| page = await browser.newPage(); | ||||||||||||||||||
|
|
@@ -124,4 +173,169 @@ describe("Universal Viewer", () => { | |||||||||||||||||
|
|
||||||||||||||||||
| expect(isSettingsButtonVisible).toBe(true); | ||||||||||||||||||
| }); | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| describe("viewer controls", () => { | ||||||||||||||||||
| afterEach(async () => { | ||||||||||||||||||
| await page.goto("http://localhost:4444"); | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| // navigate to next image | ||||||||||||||||||
| it("can navigate to next image", async () => { | ||||||||||||||||||
| await page.waitForSelector(".btn.imageBtn.next", { visible: true }); | ||||||||||||||||||
|
|
||||||||||||||||||
| const urlBefore = page.url(); | ||||||||||||||||||
| const before = getCanvasValue(urlBefore); | ||||||||||||||||||
|
|
||||||||||||||||||
| await page.click(".btn.imageBtn.next"); | ||||||||||||||||||
| await waitForCanvasValue(page, 1); | ||||||||||||||||||
|
|
||||||||||||||||||
| const urlAfter = page.url(); | ||||||||||||||||||
| const after = getCanvasValue(urlAfter); | ||||||||||||||||||
|
|
||||||||||||||||||
| expect(before).toBe(0); | ||||||||||||||||||
| expect(after).toBe(1); | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| // navigate to previous image | ||||||||||||||||||
| it("can navigate to previous image", async () => { | ||||||||||||||||||
| await page.waitForSelector(".btn.imageBtn.next", { visible: true }); | ||||||||||||||||||
| await page.waitForSelector(".btn.imageBtn.prev", { visible: true }); | ||||||||||||||||||
|
|
||||||||||||||||||
| const startValue = getCanvasValue(page.url()); | ||||||||||||||||||
|
|
||||||||||||||||||
| const isPrevDisabledInitially = await page.$eval( | ||||||||||||||||||
| ".btn.imageBtn.prev", | ||||||||||||||||||
| (btn) => btn.disabled | ||||||||||||||||||
| ); | ||||||||||||||||||
| expect(isPrevDisabledInitially).toBe(true); | ||||||||||||||||||
|
|
||||||||||||||||||
| await page.click(".btn.imageBtn.next"); | ||||||||||||||||||
| await waitForCanvasValue(page, 1); | ||||||||||||||||||
|
|
||||||||||||||||||
| const nextValue = getCanvasValue(page.url()); | ||||||||||||||||||
| const isPrevDisabledAfterNext = await page.$eval( | ||||||||||||||||||
| ".btn.imageBtn.prev", | ||||||||||||||||||
| (btn) => btn.disabled | ||||||||||||||||||
| ); | ||||||||||||||||||
| expect(isPrevDisabledAfterNext).toBe(false); | ||||||||||||||||||
|
|
||||||||||||||||||
| await page.click(".btn.imageBtn.prev"); | ||||||||||||||||||
| await waitForCanvasValue(page, 0); | ||||||||||||||||||
|
|
||||||||||||||||||
| const previousValue = getCanvasValue(page.url()); | ||||||||||||||||||
| const isPrevDisabledAgain = await page.$eval( | ||||||||||||||||||
| ".btn.imageBtn.prev", | ||||||||||||||||||
| (btn) => btn.disabled | ||||||||||||||||||
| ); | ||||||||||||||||||
| expect(isPrevDisabledAgain).toBe(true); | ||||||||||||||||||
|
|
||||||||||||||||||
| expect(startValue).toBe(0); | ||||||||||||||||||
| expect(nextValue).toBe(1); | ||||||||||||||||||
| expect(previousValue).toBe(0); | ||||||||||||||||||
|
|
||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| // zoom in and zoom out | ||||||||||||||||||
| it("can zoom in and zoom out", async () => { | ||||||||||||||||||
| await page.waitForSelector(".zoomIn.viewportNavButton", { | ||||||||||||||||||
| visible: true, | ||||||||||||||||||
| }); | ||||||||||||||||||
| await page.waitForSelector(".zoomOut.viewportNavButton", { | ||||||||||||||||||
| visible: true, | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| const initialUrl = page.url(); | ||||||||||||||||||
| const initialXywh = getXywhValue(initialUrl); | ||||||||||||||||||
|
|
||||||||||||||||||
| await page.click(".zoomIn.viewportNavButton"); | ||||||||||||||||||
| await page.waitForFunction( | ||||||||||||||||||
| (prev) => window.location.href !== prev, | ||||||||||||||||||
| {}, | ||||||||||||||||||
| initialUrl | ||||||||||||||||||
| ); | ||||||||||||||||||
|
|
||||||||||||||||||
| const zoomInUrl = page.url(); | ||||||||||||||||||
| const zoomInXywh = getXywhValue(zoomInUrl); | ||||||||||||||||||
|
|
||||||||||||||||||
| expect(zoomInXywh).not.toBeNull(); | ||||||||||||||||||
| expect(zoomInXywh).not.toBe(initialXywh); | ||||||||||||||||||
| expect(zoomInXywh).toMatch(/^-?\d+,-?\d+,\d+,\d+$/); | ||||||||||||||||||
|
|
||||||||||||||||||
| await page.click(".zoomOut.viewportNavButton"); | ||||||||||||||||||
| await page.waitForFunction( | ||||||||||||||||||
| (prev) => window.location.href !== prev, | ||||||||||||||||||
| {}, | ||||||||||||||||||
| zoomInUrl | ||||||||||||||||||
| ); | ||||||||||||||||||
|
|
||||||||||||||||||
| const zoomOutUrl = page.url(); | ||||||||||||||||||
| const zoomOutXywh = getXywhValue(zoomOutUrl); | ||||||||||||||||||
|
|
||||||||||||||||||
| expect(zoomOutXywh).not.toBeNull(); | ||||||||||||||||||
| expect(zoomOutXywh).not.toBe(zoomInXywh); | ||||||||||||||||||
| expect(zoomOutXywh).toMatch(/^-?\d+,-?\d+,\d+,\d+$/); | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| // rotate image | ||||||||||||||||||
| it("can rotate image", async () => { | ||||||||||||||||||
| await page.waitForSelector(".rotate.viewportNavButton", { | ||||||||||||||||||
| visible: true, | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| const initialRot = await getRotationFromNavigator(); | ||||||||||||||||||
| const initialTransform = await page.evaluate(() => { | ||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any reason not to use getRotationFromNavigator instead of directly accessing the .displayregioncontainer transform style in multiple places? I imagine it's possible that a future OpenSeadragon release might change the way this works, so having it centralized in a single support method would make it easier to fix if something breaks it in the future. Of course, if there's a reason you need "raw" access to the value, I can live with it as-is. :-) |
||||||||||||||||||
| return ( | ||||||||||||||||||
| document.querySelector(".displayregioncontainer")?.style.transform || "" | ||||||||||||||||||
| ); | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| await page.click(".rotate.viewportNavButton"); | ||||||||||||||||||
| await page.waitForFunction( | ||||||||||||||||||
| (prev) => { | ||||||||||||||||||
| const current = document.querySelector(".displayregioncontainer")?.style.transform || ""; | ||||||||||||||||||
| return current !== prev; | ||||||||||||||||||
| }, | ||||||||||||||||||
| {}, | ||||||||||||||||||
| initialTransform | ||||||||||||||||||
| ); | ||||||||||||||||||
| const rotatedRot = await getRotationFromNavigator(); | ||||||||||||||||||
| expect(initialRot).toBe(0); | ||||||||||||||||||
| expect(rotatedRot).toBe(90); | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| // open and close adjust image control | ||||||||||||||||||
| it("can open and close adjust image control", async () => { | ||||||||||||||||||
| const btn = "button.viewportNavButton.adjustImage"; | ||||||||||||||||||
| const overlay = "div.overlay.adjustImage"; | ||||||||||||||||||
| const heading = "div.overlay.adjustImage .content .heading"; | ||||||||||||||||||
| const closeBtn = ".btn.btn-default.close"; | ||||||||||||||||||
|
|
||||||||||||||||||
| await page.waitForSelector(btn, { visible: true }); | ||||||||||||||||||
| await page.click(btn); | ||||||||||||||||||
|
|
||||||||||||||||||
| await page.waitForSelector(overlay, { visible: true }); | ||||||||||||||||||
|
|
||||||||||||||||||
| const text = await page.$eval(heading, (el) => el.textContent.trim()); | ||||||||||||||||||
| expect(text).toBe("Adjust image"); | ||||||||||||||||||
|
|
||||||||||||||||||
| await page.evaluate((selector) => { | ||||||||||||||||||
| const el = document.querySelector(selector); | ||||||||||||||||||
| if (el) el.click(); | ||||||||||||||||||
| }, closeBtn); | ||||||||||||||||||
|
Comment on lines
+321
to
+324
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I understand why evaluate is being used here. Can this be simplified? If nothing else, maybe this would be less confusing (if I understand the syntax correctly):
Suggested change
|
||||||||||||||||||
|
|
||||||||||||||||||
| const isOverlayVisible = await page.evaluate(() => { | ||||||||||||||||||
| const isOverlayVisible = document.querySelector( | ||||||||||||||||||
| "div.overlay.adjustImage" | ||||||||||||||||||
| ); | ||||||||||||||||||
| const style = window.getComputedStyle(isOverlayVisible); | ||||||||||||||||||
|
|
||||||||||||||||||
| return ( | ||||||||||||||||||
| style.getPropertyValue("display") === "none" || | ||||||||||||||||||
| style.getPropertyValue("visibility") === "hidden" | ||||||||||||||||||
| ); | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| expect(isOverlayVisible).toBe(false); | ||||||||||||||||||
| }); | ||||||||||||||||||
| }); | ||||||||||||||||||
| }); | ||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this test? It seems to be mostly redundant with the following one. Should we rename that from "can navigate to previous image" to "can navigate back and forth" and then delete this first test?