From a5977c3ecf662a38e85e8a8df3f03df52c1e2890 Mon Sep 17 00:00:00 2001 From: Kasim Necdet Percinel Date: Fri, 1 Nov 2024 12:54:13 -0400 Subject: [PATCH] PW tests for zoom controls and centering (#639) * tests for events should be controlled with deepest event nodes * center viewport tests * split zoom tests * zoom tests * fix _ - , also add 0.01 max mismatch ratio for screnshot * forgot to include gitignore for pngs * code style fix --- .gitignore | 1 + .../event_instance_node.spec.ts | 82 ++++++++++++++ .../desktop/normal/zoom/center_image.spec.ts | 78 ++++++++++++++ tests/desktop/normal/zoom/zoom_in.spec.ts | 100 ++++++++++++++++++ tests/desktop/normal/zoom/zoom_out.spec.ts | 98 +++++++++++++++++ .../{zoom.spec.ts => zoom/zoom_state.spec.ts} | 2 +- tests/page_objects/event_tree.ts | 15 +++ tests/page_objects/helioviewer.ts | 22 ++++ 8 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 tests/desktop/normal/features_and_events/event_instance_node.spec.ts create mode 100644 tests/desktop/normal/zoom/center_image.spec.ts create mode 100644 tests/desktop/normal/zoom/zoom_in.spec.ts create mode 100644 tests/desktop/normal/zoom/zoom_out.spec.ts rename tests/desktop/normal/{zoom.spec.ts => zoom/zoom_state.spec.ts} (92%) diff --git a/.gitignore b/.gitignore index aff2e0aff..d1a79339d 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ node_modules /playwright-report/ /blob-report/ /playwright/.cache/ +*.png diff --git a/tests/desktop/normal/features_and_events/event_instance_node.spec.ts b/tests/desktop/normal/features_and_events/event_instance_node.spec.ts new file mode 100644 index 000000000..36704654f --- /dev/null +++ b/tests/desktop/normal/features_and_events/event_instance_node.spec.ts @@ -0,0 +1,82 @@ +import { test, expect } from "@playwright/test"; +import { Helioviewer } from "../../../page_objects/helioviewer"; +import { mockEvents } from "../../../utils/events"; +import { readFile } from "fs/promises"; + +/** + * This test mocks some random events for CCMC + * then selects some specific event instances, + * then asserts all event markers for matching event instances, should be visible + * also asserts all of the other nodes, should be unchecked + */ +test("Event instances should control visibility of event markers", async ({ page, browser }, info) => { + // mocked event data + const events = { + CCMC: { + aet1: { + aet1frm1: { + aet1frm1ei1: {}, + aet1frm1ei2: {} + } + }, + bet2: { + bet2frm1: { + bet2frm1ei1: {}, + bet2frm1ei2: {}, + bet2frm1ei3: {} + }, + bet2frm2: { + bet2frm2ei1: {}, + bet2frm2ei2: {} + } + } + } + }; + + // mock events + await mockEvents(page, events); + + // load helioviewer + let hv = new Helioviewer(page, info); + + // Action 1 : BROWSE TO HELIOVIEWER + await hv.Load(); + await hv.CloseAllNotifications(); + + // Action 2 : Open left sources panel + await hv.OpenSidebar(); + + // Parse event tree pieces + const ccmc = hv.parseTree("CCMC"); + + // Action 3 : Open up all frm branches + await ccmc.toggleBranchFRM("aet1", "aet1frm1"); + await ccmc.toggleBranchFRM("bet2", "bet2frm1"); + await ccmc.toggleBranchFRM("bet2", "bet2frm2"); + + // Action 4: Select some of the event instances + await ccmc.toggleCheckEventInstance("aet1", "aet1frm1", "aet1frm1ei2"); + await ccmc.toggleCheckEventInstance("bet2", "bet2frm1", "bet2frm1ei3"); + await ccmc.toggleCheckEventInstance("bet2", "bet2frm2", "bet2frm2ei1"); + + // Action 5: Assert all event markers belong to matching event instances should be visible, + await ccmc.assertEventVisible("aet1frm1ei2"); + await ccmc.assertEventVisible("bet2frm1ei3"); + await ccmc.assertEventVisible("bet2frm2ei1"); + + // Action 6: Assert all event markers DOES NOT belong to matching event instances should NOT be visible, + await ccmc.assertEventNotVisible("aet1frm1ei1"); + await ccmc.assertEventNotVisible("bet2frm1ei1"); + await ccmc.assertEventNotVisible("bet2frm1ei2"); + await ccmc.assertEventNotVisible("bet2frm2ei2"); + + // Action 7: Select again same event instances to not to select them + await ccmc.toggleCheckEventInstance("aet1", "aet1frm1", "aet1frm1ei2"); + await ccmc.toggleCheckEventInstance("bet2", "bet2frm1", "bet2frm1ei3"); + await ccmc.toggleCheckEventInstance("bet2", "bet2frm2", "bet2frm2ei1"); + + // Action 8: Now matching markers should NOT be visible + await ccmc.assertEventNotVisible("aet1frm1ei2"); + await ccmc.assertEventNotVisible("bet2frm1ei3"); + await ccmc.assertEventNotVisible("bet2frm2ei1"); +}); diff --git a/tests/desktop/normal/zoom/center_image.spec.ts b/tests/desktop/normal/zoom/center_image.spec.ts new file mode 100644 index 000000000..b42c27efc --- /dev/null +++ b/tests/desktop/normal/zoom/center_image.spec.ts @@ -0,0 +1,78 @@ +import { test, expect } from "@playwright/test"; +import { Helioviewer } from "../../../page_objects/helioviewer"; + +/** + * This test; + * - load helioviewer + * - take screenshot + * - drag sun to random place + * - press c to center sun + * - centered sun should match initial sun + */ +test("Pressing C should center sun", async ({ page }, info) => { + let hv = new Helioviewer(page, info); + + // 1. LOAD HV + await hv.Load(); + await hv.CloseAllNotifications(); + await hv.OpenSidebar(); + + // 2. GO NEWEST IMAGE + await hv.UseNewestImage(); + await hv.WaitForLoadingComplete(); + await hv.CloseAllNotifications(); + + // 3. TAKE A SCREENSHOT + const initialCenteredSun = await hv.sunScreenshot(); + + // 4. NOW PULL PAGE TO SOMEWHERE + await hv.moveViewport(300, 30); + + // 5. CENTER VIEWPORT WITH PRESSING C + await page.keyboard.press("c"); + + // 6. TAKE ANOTHER SCREENSHOT + const centeredWithKeyboard = await hv.sunScreenshot(); + + // 7. BOTH SCREENSHOT SHOULD MATCH + + expect(initialCenteredSun).toBe(centeredWithKeyboard); +}); + +/** + * This test; + * - load helioviewer + * - take screenshot + - drag sun to random place + - press sun center button to center sun + - centered sun should match initial sun + */ +test("Pressing sun center button should center sun", async ({ page }, info) => { + let hv = new Helioviewer(page, info); + + // 1. LOAD HV + await hv.Load(); + await hv.CloseAllNotifications(); + await hv.OpenSidebar(); + + // 2. GO NEWEST IMAGE + await hv.UseNewestImage(); + await hv.WaitForLoadingComplete(); + await hv.CloseAllNotifications(); + + // 3. TAKE A SCREENSHOT + const initialCenteredSun = await hv.sunScreenshot(); + + // 4. NOW PULL PAGE TO SOMEWHERE + await hv.moveViewport(300, 30); + + // 5. CENTER VIEWPORT WITH PRESSING SUN CENTER BuTTON + await hv.centerViewport(); + + // 6. TAKE ANOTHER SCREENSHOT + const centeredWithButton = await hv.sunScreenshot(); + + // 7. BOTH SCREENSHOT SHOULD MATCH + + expect(initialCenteredSun).toBe(centeredWithButton); +}); diff --git a/tests/desktop/normal/zoom/zoom_in.spec.ts b/tests/desktop/normal/zoom/zoom_in.spec.ts new file mode 100644 index 000000000..1cf99ab71 --- /dev/null +++ b/tests/desktop/normal/zoom/zoom_in.spec.ts @@ -0,0 +1,100 @@ +import { test, expect } from "@playwright/test"; +import { Helioviewer } from "../../../page_objects/helioviewer"; + +/** + * This test tests zoom out functionality with + * - load helioviewer + * - zoom one in + * - take screenshot + * - load helioviewer with clean state, with ?imageScale=2.42044088 indicating zoom 1 in scale value + * - screenshot should match previous screenshot + */ +test("Pressing zoom in button should zoom sun in", async ({ page }, info) => { + let hv = new Helioviewer(page, info); + + // 1. LOAD HV + await hv.Load(); + await hv.CloseAllNotifications(); + await hv.OpenSidebar(); + + // 2. GO NEWEST IMAGE + await hv.UseNewestImage(); + await hv.WaitForLoadingComplete(); + await hv.CloseAllNotifications(); + + // 3. ZOOM ONE IN + await hv.ZoomIn(1); + await hv.WaitForLoadingComplete(); + + // 4. TAKE A SCREENSHOT + await hv.sunScreenshot("zoom-one-in-with-button-screenshot.png"); + + // 5. LOAD HELIOVIEWER WITH CLEAN STATE, WITH ?IMAGESCALE=2.42044088 INDICATING ZOOM 1 IN SCALE VALUE + await page.evaluate(() => localStorage.clear()); + await hv.Load("/?imageScale=2.42044088"); + await hv.CloseAllNotifications(); + await hv.OpenSidebar(); + + // 6. GO NEWEST IMAGE + await hv.UseNewestImage(); + await hv.WaitForLoadingComplete(); + await hv.CloseAllNotifications(); + + // 7. TAKE ANOTHER SCREENSHOT + const zoomOneInWithURL = await hv.sunScreenshot("zoom-one-in-with-url-screenshot"); + + // 8. BOTH SCREENSHOT SHOULD MATCH + expect(Buffer.from(zoomOneInWithURL, "base64")).toMatchSnapshot("zoom-one-in-with-button-screenshot.png", { + maxDiffPixelRatio: 0.01 + }); +}); + +/** + * This test; + * - load helioviewer + * - zoom one in with keyboard + char + * - take screenshot + * - load helioviewer with clean state, with ?imageScale=9.68176352 indicating zoom 1 in scale value + * - screenshot should match previous screenshot + */ +test("Pressing zoom in with keyboard + should zoom sun in", async ({ page }, info) => { + let hv = new Helioviewer(page, info); + + // 1. LOAD HV + await hv.Load(); + await hv.CloseAllNotifications(); + await hv.OpenSidebar(); + + // 2. GO NEWEST IMAGE + await hv.UseNewestImage(); + await hv.WaitForLoadingComplete(); + await hv.CloseAllNotifications(); + + await hv.sunScreenshot("initial.png"); + + // 3. ZOOM ONE IN WITH KEYBOARD "+" + await page.keyboard.press("+"); + await hv.WaitForLoadingComplete(); + + // 4. TAKE A SCREENSHOT + await hv.sunScreenshot("zoom-one-in-with-keyboard-screenshot.png"); + + // 5. LOAD HELIOVIEWER WITH CLEAN STATE, WITH ?IMAGESCALE=9.68176352 INDICATING ZOOM 1 OUT SCALE VALUE + await page.evaluate(() => localStorage.clear()); + await hv.Load("/?imageScale=2.42044088"); + await hv.CloseAllNotifications(); + await hv.OpenSidebar(); + + // 6. GO NEWEST IMAGE + await hv.UseNewestImage(); + await hv.WaitForLoadingComplete(); + await hv.CloseAllNotifications(); + + // 7. TAKE ANOTHER SCREENSHOT + const zoomOneInWithURL = await hv.sunScreenshot("zoom-one-in-with-url-screenshot"); + + // 8. BOTH SCREENSHOT SHOULD MATCH + expect(Buffer.from(zoomOneInWithURL, "base64")).toMatchSnapshot("zoom-one-in-with-keyboard-screenshot.png", { + maxDiffPixelRatio: 0.01 + }); +}); diff --git a/tests/desktop/normal/zoom/zoom_out.spec.ts b/tests/desktop/normal/zoom/zoom_out.spec.ts new file mode 100644 index 000000000..5e120be47 --- /dev/null +++ b/tests/desktop/normal/zoom/zoom_out.spec.ts @@ -0,0 +1,98 @@ +import { test, expect } from "@playwright/test"; +import { Helioviewer } from "../../../page_objects/helioviewer"; + +/** + * This test; + * - load helioviewer + * - zoom one out with button + * - take screenshot + * - load helioviewer with clean state, with ?imageScale=9.68176352 indicating zoom 1 out scale value + * - screenshot should match previous screenshot + */ +test("Pressing zoom out button should zoom sun out", async ({ page }, info) => { + let hv = new Helioviewer(page, info); + + // 1. LOAD HV + await hv.Load(); + await hv.CloseAllNotifications(); + await hv.OpenSidebar(); + + // 2. GO NEWEST IMAGE + await hv.UseNewestImage(); + await hv.WaitForLoadingComplete(); + await hv.CloseAllNotifications(); + + // 3. ZOOM ONE OUT WITH BUTTON + await hv.ZoomOut(1); + await hv.WaitForLoadingComplete(); + + // 4. TAKE A SCREENSHOT + await hv.sunScreenshot("zoom-one-out-with-button-screenshot.png"); + + // 5. LOAD HELIOVIEWER WITH CLEAN STATE, WITH ?IMAGESCALE=9.68176352 INDICATING ZOOM 1 OUT SCALE VALUE + await page.evaluate(() => localStorage.clear()); + await hv.Load("/?imageScale=9.68176352"); + await hv.CloseAllNotifications(); + await hv.OpenSidebar(); + + // 6. GO NEWEST IMAGE + await hv.UseNewestImage(); + await hv.WaitForLoadingComplete(); + await hv.CloseAllNotifications(); + + // 7. TAKE ANOTHER SCREENSHOT + const zoomOneOutWithURL = await hv.sunScreenshot("zoom-one-out-with-url-screenshot"); + + // 8. BOTH SCREENSHOT SHOULD MATCH + expect(Buffer.from(zoomOneOutWithURL, "base64")).toMatchSnapshot("zoom-one-out-with-button-screenshot.png", { + maxDiffPixelRatio: 0.01 + }); +}); + +/** + * This test; + * - load helioviewer + * - zoom one out with keyboard - char + * - take screenshot + * - load helioviewer with clean state, with ?imageScale=9.68176352 indicating zoom 1 out scale value + * - screenshot should match previous screenshot + */ +test("Pressing zoom out with keyboard - should zoom sun out", async ({ page }, info) => { + let hv = new Helioviewer(page, info); + + // 1. LOAD HV + await hv.Load(); + await hv.CloseAllNotifications(); + await hv.OpenSidebar(); + + // 2. GO NEWEST IMAGE + await hv.UseNewestImage(); + await hv.WaitForLoadingComplete(); + await hv.CloseAllNotifications(); + + // 3. ZOOM ONE OUT WITH KEYBOARD "-" + await page.keyboard.press("-"); + await hv.WaitForLoadingComplete(); + + // 4. TAKE A SCREENSHOT + await hv.sunScreenshot("zoom-one-out-with-keyboard-screenshot.png"); + + // 5. LOAD HELIOVIEWER WITH CLEAN STATE, WITH ?IMAGESCALE=9.68176352 INDICATING ZOOM 1 OUT SCALE VALUE + await page.evaluate(() => localStorage.clear()); + await hv.Load("/?imageScale=9.68176352"); + await hv.CloseAllNotifications(); + await hv.OpenSidebar(); + + // 6. GO NEWEST IMAGE + await hv.UseNewestImage(); + await hv.WaitForLoadingComplete(); + await hv.CloseAllNotifications(); + + // 7. TAKE ANOTHER SCREENSHOT + const zoomOneOutWithURL = await hv.sunScreenshot("zoom-one-out-with-url-screenshot"); + + // 8. BOTH SCREENSHOT SHOULD MATCH + expect(Buffer.from(zoomOneOutWithURL, "base64")).toMatchSnapshot("zoom-one-out-with-keyboard-screenshot.png", { + maxDiffPixelRatio: 0.01 + }); +}); diff --git a/tests/desktop/normal/zoom.spec.ts b/tests/desktop/normal/zoom/zoom_state.spec.ts similarity index 92% rename from tests/desktop/normal/zoom.spec.ts rename to tests/desktop/normal/zoom/zoom_state.spec.ts index 918921d01..d4bbf6113 100644 --- a/tests/desktop/normal/zoom.spec.ts +++ b/tests/desktop/normal/zoom/zoom_state.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from "@playwright/test"; -import { Helioviewer } from "../../page_objects/helioviewer"; +import { Helioviewer } from "../../../page_objects/helioviewer"; /** * This test simply adds and removes images layers diff --git a/tests/page_objects/event_tree.ts b/tests/page_objects/event_tree.ts index 2db692075..f04be3106 100644 --- a/tests/page_objects/event_tree.ts +++ b/tests/page_objects/event_tree.ts @@ -426,6 +426,21 @@ class EventTree { await expect(eventFRMNode).toHaveClass(/jstree-unchecked/); } + /** + * This function checks if the given frm node is halfchecked (not all of its childrens are selected) in event tree. + * @param {string} frm, The frm name pointing to the node in tree (e.g. "NOAA SWPC Observer", "SPoCA"). + * @return {Promise} A promise for you to wait for assertion to complete. + */ + async assertFrmNodeHalfChecked(event_type: string, frm: string): Promise { + const eventTypeLink = this.page.getByRole("link", { name: EventTree.makeNumericRegex(event_type) }); + const eventTypeNode = await this.root.getByRole("listitem").filter({ has: eventTypeLink }); + + const eventFRMLink = this.page.getByRole("link", { name: EventTree.makeNumericRegex(frm) }); + const eventFRMNode = await eventTypeNode.getByRole("listitem").filter({ has: eventFRMLink }); + + await expect(eventFRMNode).toHaveClass(/jstree-undetermined/); + } + /** * This function checks if the given event instance node is unchecked in event tree. * @param {string} event_type, The event type name pointing to the node in tree (e.g. "Active Region", "Corona Hole"). diff --git a/tests/page_objects/helioviewer.ts b/tests/page_objects/helioviewer.ts index c5e1bd2f7..243b0e7de 100644 --- a/tests/page_objects/helioviewer.ts +++ b/tests/page_objects/helioviewer.ts @@ -471,6 +471,28 @@ class Helioviewer implements DesktopInterface { return this.saveScreenshot(filename, options); } + + /** + * Move the viewport by the given amount + * @param {number} x Horizontal amount + * @param {number} y Vertical amount + * @returns {Promise} + */ + async moveViewport(x: number, y: number): Promise { + const INITIAL_POSITION = { x: 650, y: 400 }; + await this.page.mouse.move(INITIAL_POSITION.x, INITIAL_POSITION.y); + await this.page.mouse.down(); + await this.page.mouse.move(INITIAL_POSITION.x + x, INITIAL_POSITION.y + y); + await this.page.mouse.up(); + } + + /** + * Center the viewport for the sun + * @returns {Promise} + */ + async centerViewport(): Promise { + await this.page.locator("#center-button").click(); + } } export { Helioviewer };