diff --git a/__tests__/test.js b/__tests__/test.js index 5583e7063..b677bd9dd 100644 --- a/__tests__/test.js +++ b/__tests__/test.js @@ -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(() => { + 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); + + 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); + }); + }); +}); \ No newline at end of file