From 15ab0dae50692e18eb31d0601e0352fbbb08e282 Mon Sep 17 00:00:00 2001 From: Jesse Mazzella Date: Mon, 12 Sep 2022 09:19:24 -0700 Subject: [PATCH] [e2e] Clean up example imagery tests, fix display layout test (#5685) * Clean up imagery tests, fix display layout test (cherry picked from commit 7d6699fcecb34cf98bac4aa8f9a1b2996736325c) * Code review comments, more cleanup * Add Tabs View suite, extract common actions to functions * Add missing await * remove unnecessary wait * lint * [e2e] playwright v1.25.0 -->v1.25.2 Co-authored-by: John Hill --- .circleci/config.yml | 2 +- .github/workflows/e2e-couchdb.yml | 2 +- .github/workflows/e2e-pr.yml | 2 +- .../imagery/exampleImagery.e2e.spec.js | 671 +++++++----------- package.json | 2 +- 5 files changed, 279 insertions(+), 400 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 45749b0f26..7b6b4a6f05 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 executors: pw-focal-development: docker: - - image: mcr.microsoft.com/playwright:v1.25.0-focal + - image: mcr.microsoft.com/playwright:v1.25.2-focal environment: NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed PERCY_POSTINSTALL_BROWSER: 'true' # Needed to store the percy browser in cache deps diff --git a/.github/workflows/e2e-couchdb.yml b/.github/workflows/e2e-couchdb.yml index 69c33967a1..ee610a51ee 100644 --- a/.github/workflows/e2e-couchdb.yml +++ b/.github/workflows/e2e-couchdb.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: '16' - - run: npx playwright@1.25.0 install + - run: npx playwright@1.25.2 install - run: npm install - run: sh src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.sh - run: npm run test:e2e:couchdb diff --git a/.github/workflows/e2e-pr.yml b/.github/workflows/e2e-pr.yml index 385255797a..317d0ef379 100644 --- a/.github/workflows/e2e-pr.yml +++ b/.github/workflows/e2e-pr.yml @@ -30,7 +30,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: '16' - - run: npx playwright@1.25.0 install + - run: npx playwright@1.25.2 install - run: npx playwright install chrome-beta - run: npm install - run: npm run test:e2e:full diff --git a/e2e/tests/functional/plugins/imagery/exampleImagery.e2e.spec.js b/e2e/tests/functional/plugins/imagery/exampleImagery.e2e.spec.js index f4e6cf7442..b24e511852 100644 --- a/e2e/tests/functional/plugins/imagery/exampleImagery.e2e.spec.js +++ b/e2e/tests/functional/plugins/imagery/exampleImagery.e2e.spec.js @@ -35,45 +35,24 @@ const expectedAltText = process.platform === 'linux' ? 'Ctrl+Alt drag to pan' : //The following block of tests verifies the basic functionality of example imagery and serves as a template for Imagery objects embedded in other objects. test.describe('Example Imagery Object', () => { - test.beforeEach(async ({ page }) => { //Go to baseURL await page.goto('./', { waitUntil: 'networkidle' }); // Create a default 'Example Imagery' object - createDomainObjectWithDefaults(page, { type: 'Example Imagery' }); - - await Promise.all([ - page.waitForNavigation(), - page.locator(backgroundImageSelector).hover({trial: true}), - // eslint-disable-next-line playwright/missing-playwright-await - expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery') - ]); + await createDomainObjectWithDefaults(page, { type: 'Example Imagery' }); // Verify that the created object is focused + await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery'); + await page.locator(backgroundImageSelector).hover({trial: true}); }); test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => { - const deltaYStep = 100; //equivalent to 1x zoom - await page.locator(backgroundImageSelector).hover({trial: true}); - const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox(); - // zoom in - await page.locator(backgroundImageSelector).hover({trial: true}); - await page.mouse.wheel(0, deltaYStep * 2); - // wait for zoom animation to finish - await page.locator(backgroundImageSelector).hover({trial: true}); - const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox(); - // zoom out - await page.locator(backgroundImageSelector).hover({trial: true}); - await page.mouse.wheel(0, -deltaYStep); - // wait for zoom animation to finish - await page.locator(backgroundImageSelector).hover({trial: true}); - const imageMouseZoomedOut = await page.locator(backgroundImageSelector).boundingBox(); + // Zoom in x2 and assert + await mouseZoomOnImageAndAssert(page, 2); - expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height); - expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width); - expect(imageMouseZoomedOut.height).toBeLessThan(imageMouseZoomedIn.height); - expect(imageMouseZoomedOut.width).toBeLessThan(imageMouseZoomedIn.width); + // Zoom out x2 and assert + await mouseZoomOnImageAndAssert(page, -2); }); test('Can adjust image brightness/contrast by dragging the sliders', async ({ page, browserName }) => { @@ -151,30 +130,7 @@ test.describe('Example Imagery Object', () => { }); test('Can use + - buttons to zoom on the image @unstable', async ({ page }) => { - // Get initial image dimensions - const initialBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); - - // Zoom in twice via button - await zoomIntoImageryByButton(page); - await zoomIntoImageryByButton(page); - - // Get and assert zoomed in image dimensions - const zoomedInBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); - expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height); - expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width); - - // Zoom out once via button - await zoomOutOfImageryByButton(page); - - // Get and assert zoomed out image dimensions - const zoomedOutBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); - expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height); - expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width); - - // Zoom out again via button, assert against the initial image dimensions - await zoomOutOfImageryByButton(page); - const finalBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); - expect(finalBoundingBox).toEqual(initialBoundingBox); + await buttonZoomOnImageAndAssert(page); }); test('Can use the reset button to reset the image @unstable', async ({ page }, testInfo) => { @@ -211,47 +167,223 @@ test.describe('Example Imagery Object', () => { await zoomIntoImageryByButton(page); await expect(pausePlayButton).not.toHaveClass(/is-paused/); }); - }); -// The following test case will cover these scenarios -// ('Can use Mouse Wheel to zoom in and out of previous image'); -// ('Can use alt+drag to move around image once zoomed in'); -// ('Clicking on the left arrow should pause the imagery and go to previous image'); -// ('If the imagery view is in pause mode, it should not be updated when new images come in'); -// ('If the imagery view is not in pause mode, it should be updated when new images come in'); -test('Example Imagery in Display layout @unstable', async ({ page }) => { - test.info().annotations.push({ - type: 'issue', - description: 'https://github.com/nasa/openmct/issues/5265' +test.describe('Example Imagery in Display Layout', () => { + let displayLayout; + test.beforeEach(async ({ page }) => { + // Go to baseURL + await page.goto('./', { waitUntil: 'networkidle' }); + + displayLayout = await createDomainObjectWithDefaults(page, { type: 'Display Layout' }); + await page.goto(displayLayout.url); + + /* Create Sine Wave Generator with minimum Image Load Delay */ + // Click the Create button + await page.click('button:has-text("Create")'); + + // Click text=Example Imagery + await page.click('text=Example Imagery'); + + // Clear and set Image load delay to minimum value + await page.locator('input[type="number"]').fill(''); + await page.locator('input[type="number"]').fill('5000'); + + // Click text=OK + await Promise.all([ + page.waitForNavigation({waitUntil: 'networkidle'}), + page.click('text=OK'), + //Wait for Save Banner to appear + page.waitForSelector('.c-message-banner__message') + ]); + + await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery'); + + await page.goto(displayLayout.url); }); - // Go to baseURL - await page.goto('./', { waitUntil: 'networkidle' }); + test('Imagery View operations @unstable', async ({ page }) => { + test.info().annotations.push({ + type: 'issue', + description: 'https://github.com/nasa/openmct/issues/5265' + }); - // Click the Create button - await page.click('button:has-text("Create")'); + // Edit mode + await page.click('button[title="Edit"]'); - // Click text=Example Imagery - await page.click('text=Example Imagery'); + // Click on example imagery to expose toolbar + await page.locator('.c-so-view__header').click(); - // Clear and set Image load delay to minimum value - await page.locator('input[type="number"]').fill(''); - await page.locator('input[type="number"]').fill('5000'); + // Adjust object height + await page.locator('div[title="Resize object height"] > input').click(); + await page.locator('div[title="Resize object height"] > input').fill('50'); - // Click text=OK - await Promise.all([ - page.waitForNavigation({waitUntil: 'networkidle'}), - page.click('text=OK'), - //Wait for Save Banner to appear - page.waitForSelector('.c-message-banner__message') - ]); + // Adjust object width + await page.locator('div[title="Resize object width"] > input').click(); + await page.locator('div[title="Resize object width"] > input').fill('50'); - // Wait until Save Banner is gone - await page.waitForSelector('.c-message-banner__message', { state: 'detached'}); - await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery'); - await page.locator(backgroundImageSelector).hover({trial: true}); + await performImageryViewOperationsAndAssert(page); + }); + test('Resizing the layout changes thumbnail visibility and size', async ({ page }) => { + const thumbsWrapperLocator = page.locator('.c-imagery__thumbs-wrapper'); + // Edit mode + await page.click('button[title="Edit"]'); + + // Click on example imagery to expose toolbar + await page.locator('.c-so-view__header').click(); + + // expect thumbnails not be visible when first added + expect.soft(thumbsWrapperLocator.isHidden()).toBeTruthy(); + + // Resize the example imagery vertically to change the thumbnail visibility + /* + The following arbitrary values are added to observe the separate visual + conditions of the thumbnails (hidden, small thumbnails, regular thumbnails). + Specifically, height is set to 50px for small thumbs and 100px for regular + */ + await page.locator('div[title="Resize object height"] > input').click(); + await page.locator('div[title="Resize object height"] > input').fill('50'); + + expect(thumbsWrapperLocator.isVisible()).toBeTruthy(); + await expect(thumbsWrapperLocator).toHaveClass(/is-small-thumbs/); + + // Resize the example imagery vertically to change the thumbnail visibility + await page.locator('div[title="Resize object height"] > input').click(); + await page.locator('div[title="Resize object height"] > input').fill('100'); + + expect(thumbsWrapperLocator.isVisible()).toBeTruthy(); + await expect(thumbsWrapperLocator).not.toHaveClass(/is-small-thumbs/); + }); +}); + +test.describe('Example Imagery in Flexible layout', () => { + let flexibleLayout; + test.beforeEach(async ({ page }) => { + await page.goto('./', { waitUntil: 'networkidle' }); + + flexibleLayout = await createDomainObjectWithDefaults(page, { type: 'Flexible Layout' }); + await page.goto(flexibleLayout.url); + + /* Create Sine Wave Generator with minimum Image Load Delay */ + // Click the Create button + await page.click('button:has-text("Create")'); + + // Click text=Example Imagery + await page.click('text=Example Imagery'); + + // Clear and set Image load delay to minimum value + await page.locator('input[type="number"]').fill(''); + await page.locator('input[type="number"]').fill('5000'); + + // Click text=OK + await Promise.all([ + page.waitForNavigation({waitUntil: 'networkidle'}), + page.click('text=OK'), + //Wait for Save Banner to appear + page.waitForSelector('.c-message-banner__message') + ]); + + await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery'); + + await page.goto(flexibleLayout.url); + }); + test('Imagery View operations @unstable', async ({ page, browserName }) => { + test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox'); + test.info().annotations.push({ + type: 'issue', + description: 'https://github.com/nasa/openmct/issues/5326' + }); + + await performImageryViewOperationsAndAssert(page); + }); +}); + +test.describe('Example Imagery in Tabs View', () => { + let tabsView; + test.beforeEach(async ({ page }) => { + await page.goto('./', { waitUntil: 'networkidle' }); + + tabsView = await createDomainObjectWithDefaults(page, { type: 'Tabs View' }); + await page.goto(tabsView.url); + + /* Create Sine Wave Generator with minimum Image Load Delay */ + // Click the Create button + await page.click('button:has-text("Create")'); + + // Click text=Example Imagery + await page.click('text=Example Imagery'); + + // Clear and set Image load delay to minimum value + await page.locator('input[type="number"]').fill(''); + await page.locator('input[type="number"]').fill('5000'); + + // Click text=OK + await Promise.all([ + page.waitForNavigation({waitUntil: 'networkidle'}), + page.click('text=OK'), + //Wait for Save Banner to appear + page.waitForSelector('.c-message-banner__message') + ]); + + await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery'); + + await page.goto(tabsView.url); + }); + test('Imagery View operations @unstable', async ({ page }) => { + await performImageryViewOperationsAndAssert(page); + }); +}); + +test.describe('Example Imagery in Time Strip', () => { + let timeStripObject; + test.beforeEach(async ({ page }) => { + await page.goto('./', { waitUntil: 'networkidle' }); + timeStripObject = await createDomainObjectWithDefaults(page, { + type: 'Time Strip', + name: 'Time Strip'.concat(' ', uuid()) + }); + + await createDomainObjectWithDefaults(page, { + type: 'Example Imagery', + name: 'Example Imagery'.concat(' ', uuid()), + parent: timeStripObject.uuid + }); + // Navigate to timestrip + await page.goto(timeStripObject.url); + }); + test('Clicking a thumbnail loads the image in large view', async ({ page, browserName }) => { + test.info().annotations.push({ + type: 'issue', + description: 'https://github.com/nasa/openmct/issues/5632' + }); + await page.locator('.c-imagery-tsv-container').hover(); + // get url of the hovered image + const hoveredImg = page.locator('.c-imagery-tsv div.c-imagery-tsv__image-wrapper:hover img'); + const hoveredImgSrc = await hoveredImg.getAttribute('src'); + expect(hoveredImgSrc).toBeTruthy(); + await page.locator('.c-imagery-tsv-container').click(); + // get image of view large container + const viewLargeImg = page.locator('img.c-imagery__main-image__image'); + const viewLargeImgSrc = await viewLargeImg.getAttribute('src'); + expect(viewLargeImgSrc).toBeTruthy(); + expect(viewLargeImgSrc).toEqual(hoveredImgSrc); + }); +}); + +/** + * Perform the common actions and assertions for the Imagery View. + * This function verifies the following in order: + * 1. Can zoom in/out using the zoom buttons + * 2. Can zoom in/out using the mouse wheel + * 3. Can pan the image using the pan hotkey + mouse drag + * 4. Clicking on the left arrow button pauses imagery and moves to the previous image + * 5. Imagery is updated as new images stream in, regardless of pause status + * 6. Old images are discarded when new images stream in + * 7. Image brightness/contrast can be adjusted by dragging the sliders + * @param {import('@playwright/test').Page} page + */ +async function performImageryViewOperationsAndAssert(page) { // Click previous image button const previousImageButton = page.locator('.c-nav--prev'); await previousImageButton.click(); @@ -260,27 +392,17 @@ test('Example Imagery in Display layout @unstable', async ({ page }) => { const selectedImage = page.locator('.selected'); await expect(selectedImage).toBeVisible(); - // Zoom in - const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox(); - await page.locator(backgroundImageSelector).hover({trial: true}); - const deltaYStep = 100; // equivalent to 1x zoom - await page.mouse.wheel(0, deltaYStep * 2); - const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); - const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2; - const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2; + // Use the zoom buttons to zoom in and out + await buttonZoomOnImageAndAssert(page); - // Wait for zoom animation to finish - await page.locator(backgroundImageSelector).hover({trial: true}); - const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox(); - expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height); - expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width); + // Use Mouse Wheel to zoom in to previous image + await mouseZoomOnImageAndAssert(page, 2); - // Center the mouse pointer - await page.mouse.move(imageCenterX, imageCenterY); + // Use alt+drag to move around image once zoomed in + await panZoomAndAssertImageProperties(page); - // Pan Imagery Hints - const imageryHintsText = await page.locator('.c-imagery__hints').innerText(); - expect(expectedAltText).toEqual(imageryHintsText); + // Use Mouse Wheel to zoom out of previous image + await mouseZoomOnImageAndAssert(page, -2); // Click next image button const nextImageButton = page.locator('.c-nav--next'); @@ -293,21 +415,14 @@ test('Example Imagery in Display layout @unstable', async ({ page }) => { await page.locator('[data-testid=conductor-modeOption-realtime]').click(); // Zoom in on next image - await page.locator(backgroundImageSelector).hover({trial: true}); - await page.mouse.wheel(0, deltaYStep * 2); + await mouseZoomOnImageAndAssert(page, 2); - // Wait for zoom animation to finish - await page.locator(backgroundImageSelector).hover({trial: true}); - const imageNextMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox(); - expect(imageNextMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height); - expect(imageNextMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width); - - // Click previous image button + // Clicking on the left arrow should pause the imagery and go to previous image await previousImageButton.click(); - - // Verify previous image + await expect(page.locator('.c-button.pause-play')).toHaveClass(/is-paused/); await expect(selectedImage).toBeVisible(); + // The imagery view should be updated when new images come in const imageCount = await page.locator('.c-imagery__thumb').count(); await expect.poll(async () => { const newImageCount = await page.locator('.c-imagery__thumb').count(); @@ -315,7 +430,7 @@ test('Example Imagery in Display layout @unstable', async ({ page }) => { return newImageCount; }, { message: "verify that old images are discarded", - timeout: 6 * 1000 + timeout: 7 * 1000 }).toBe(imageCount); // Verify selected image is still displayed @@ -333,283 +448,6 @@ test('Example Imagery in Display layout @unstable', async ({ page }) => { // Drag the brightness and contrast sliders around and assert filter values await dragBrightnessSliderAndAssertFilterValues(page); await dragContrastSliderAndAssertFilterValues(page); -}); - -test.describe('Example imagery thumbnails resize in display layouts', () => { - test('Resizing the layout changes thumbnail visibility and size', async ({ page }) => { - await page.goto('./', { waitUntil: 'networkidle' }); - - const thumbsWrapperLocator = page.locator('.c-imagery__thumbs-wrapper'); - // Click button:has-text("Create") - await page.locator('button:has-text("Create")').click(); - - // Click li:has-text("Display Layout") - await page.locator('li:has-text("Display Layout")').click(); - const displayLayoutTitleField = page.locator('text=Properties Title Notes Horizontal grid (px) Vertical grid (px) Horizontal size ( >> input[type="text"]'); - await displayLayoutTitleField.click(); - - await displayLayoutTitleField.fill('Thumbnail Display Layout'); - - // Click text=OK - await Promise.all([ - page.waitForNavigation(), - page.locator('text=OK').click() - ]); - - // Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1 - await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click(); - - // Click text=Save and Finish Editing - await page.locator('text=Save and Finish Editing').click(); - - // Click button:has-text("Create") - await page.locator('button:has-text("Create")').click(); - - // Click li:has-text("Example Imagery") - await page.locator('li:has-text("Example Imagery")').click(); - - const imageryTitleField = page.locator('text=Properties Title Notes Images url list (comma separated) Image load delay (milli >> input[type="text"]'); - // Click text=Properties Title Notes Images url list (comma separated) Image load delay (milli >> input[type="text"] - await imageryTitleField.click(); - - // Fill text=Properties Title Notes Images url list (comma separated) Image load delay (milli >> input[type="text"] - await imageryTitleField.fill('Thumbnail Example Imagery'); - - // Click text=OK - await Promise.all([ - page.waitForNavigation(), - page.locator('text=OK').click() - ]); - - // Click text=Thumbnail Example Imagery Imagery Layout Snapshot >> button >> nth=0 - await Promise.all([ - page.waitForNavigation(), - page.locator('text=Thumbnail Example Imagery Imagery Layout Snapshot >> button').first().click() - ]); - - // Edit mode - await page.locator('text=Thumbnail Display Layout Snapshot >> button').nth(3).click(); - - // Click on example imagery to expose toolbar - await page.locator('text=Thumbnail Example Imagery Snapshot Large View').click(); - - // expect thumbnails not be visible when first added - expect.soft(thumbsWrapperLocator.isHidden()).toBeTruthy(); - - // Resize the example imagery vertically to change the thumbnail visibility - /* - The following arbitrary values are added to observe the separate visual - conditions of the thumbnails (hidden, small thumbnails, regular thumbnails). - Specifically, height is set to 50px for small thumbs and 100px for regular - */ - // Click #mct-input-id-103 - await page.locator('#mct-input-id-103').click(); - - // Fill #mct-input-id-103 - await page.locator('#mct-input-id-103').fill('50'); - - expect(thumbsWrapperLocator.isVisible()).toBeTruthy(); - await expect(thumbsWrapperLocator).toHaveClass(/is-small-thumbs/); - - // Resize the example imagery vertically to change the thumbnail visibility - // Click #mct-input-id-103 - await page.locator('#mct-input-id-103').click(); - - // Fill #mct-input-id-103 - await page.locator('#mct-input-id-103').fill('100'); - - expect(thumbsWrapperLocator.isVisible()).toBeTruthy(); - await expect(thumbsWrapperLocator).not.toHaveClass(/is-small-thumbs/); - }); -}); - -test.describe('Example Imagery in Flexible layout', () => { - test('Example Imagery in Flexible layout @unstable', async ({ page, browserName, openmctConfig }) => { - const { myItemsFolderName } = openmctConfig; - - // eslint-disable-next-line playwright/no-skipped-test - test.skip(browserName === 'firefox', 'This test needs to be updated to work with firefox'); - test.info().annotations.push({ - type: 'issue', - description: 'https://github.com/nasa/openmct/issues/5326' - }); - - // Go to baseURL - await page.goto('./', { waitUntil: 'networkidle' }); - - // Click the Create button - await page.click('button:has-text("Create")'); - - // Click text=Example Imagery - await page.click('text=Example Imagery'); - - // Clear and set Image load delay (milliseconds) - await page.click('input[type="number"]', {clickCount: 3}); - await page.type('input[type="number"]', "20"); - - // Click text=OK - await Promise.all([ - page.waitForNavigation({waitUntil: 'networkidle'}), - page.click('text=OK'), - //Wait for Save Banner to appear - page.waitForSelector('.c-message-banner__message') - ]); - // Wait until Save Banner is gone - await page.waitForSelector('.c-message-banner__message', { state: 'detached'}); - await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery'); - await page.locator(backgroundImageSelector).hover({trial: true}); - - // Click the Create button - await page.click('button:has-text("Create")'); - - // Click text=Flexible Layout - await page.click('text=Flexible Layout'); - - // Assert Flexible layout - await expect(page.locator('.js-form-title')).toHaveText('Create a New Flexible Layout'); - - await page.locator(`form[name="mctForm"] >> text=${myItemsFolderName}`).click(); - - // Click My Items - await Promise.all([ - page.locator('text=OK').click(), - page.waitForNavigation({waitUntil: 'networkidle'}) - ]); - - // Click My Items - await page.locator('.c-disclosure-triangle').click(); - - // Right click example imagery - await page.click(('text=Unnamed Example Imagery'), { button: 'right' }); - - // Click move - await page.locator('.icon-move').click(); - - // Click triangle to open sub menu - await page.locator('.c-form__section .c-disclosure-triangle').click(); - - // Click Flexable Layout - await page.click('.c-overlay__outer >> text=Unnamed Flexible Layout'); - - // Click text=OK - await page.locator('text=OK').click(); - - // Save template - await saveTemplate(page); - - // Zoom in - await mouseZoomIn(page); - - // Center the mouse pointer - const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); - const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2; - const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2; - await page.mouse.move(imageCenterX, imageCenterY); - - // Pan zoom - await panZoomAndAssertImageProperties(page); - - // Click previous image button - const previousImageButton = page.locator('.c-nav--prev'); - await previousImageButton.click(); - - // Verify previous image - const selectedImage = page.locator('.selected'); - await expect(selectedImage).toBeVisible(); - - // Click time conductor mode button - await page.locator('.c-mode-button').click(); - - // Select local clock mode - await page.locator('[data-testid=conductor-modeOption-realtime]').nth(0).click(); - - // Zoom in on next image - await mouseZoomIn(page); - - // Click previous image button - await previousImageButton.click(); - - // Verify previous image - await expect(selectedImage).toBeVisible(); - - const imageCount = await page.locator('.c-imagery__thumb').count(); - await expect.poll(async () => { - const newImageCount = await page.locator('.c-imagery__thumb').count(); - - return newImageCount; - }, { - message: "verify that old images are discarded", - timeout: 6 * 1000 - }).toBe(imageCount); - - // Verify selected image is still displayed - await expect(selectedImage).toBeVisible(); - - // Unpause imagery - await page.locator('.pause-play').click(); - - //Get background-image url from background-image css prop - await assertBackgroundImageUrlFromBackgroundCss(page); - - // Open the image filter menu - await page.locator('[role=toolbar] button[title="Brightness and contrast"]').click(); - - // Drag the brightness and contrast sliders around and assert filter values - await dragBrightnessSliderAndAssertFilterValues(page); - await dragContrastSliderAndAssertFilterValues(page); - }); -}); - -test.describe('Example Imagery in Tabs view', () => { - test.fixme('Can use Mouse Wheel to zoom in and out of previous image'); - test.fixme('Can use alt+drag to move around image once zoomed in'); - test.fixme('Can zoom into the latest image and the real-time/fixed-time imagery will pause'); - test.fixme('Can zoom into a previous image from thumbstrip in real-time or fixed-time'); - test.fixme('Clicking on the left arrow should pause the imagery and go to previous image'); - test.fixme('If the imagery view is in pause mode, it should not be updated when new images come in'); - test.fixme('If the imagery view is not in pause mode, it should be updated when new images come in'); -}); - -test.describe('Example Imagery in Time Strip', () => { - test('ensure that clicking a thumbnail loads the image in large view', async ({ page, browserName }) => { - test.info().annotations.push({ - type: 'issue', - description: 'https://github.com/nasa/openmct/issues/5632' - }); - await page.goto('./', { waitUntil: 'networkidle' }); - const timeStripObject = await createDomainObjectWithDefaults(page, { - type: 'Time Strip', - name: 'Time Strip'.concat(' ', uuid()) - }); - - await createDomainObjectWithDefaults(page, { - type: 'Example Imagery', - name: 'Example Imagery'.concat(' ', uuid()), - parent: timeStripObject.uuid - }); - // Navigate to timestrip - await page.goto(timeStripObject.url); - - await page.locator('.c-imagery-tsv-container').hover(); - // get url of the hovered image - const hoveredImg = page.locator('.c-imagery-tsv div.c-imagery-tsv__image-wrapper:hover img'); - const hoveredImgSrc = await hoveredImg.getAttribute('src'); - expect(hoveredImgSrc).toBeTruthy(); - await page.locator('.c-imagery-tsv-container').click(); - // get image of view large container - const viewLargeImg = page.locator('img.c-imagery__main-image__image'); - const viewLargeImgSrc = await viewLargeImg.getAttribute('src'); - expect(viewLargeImgSrc).toBeTruthy(); - expect(viewLargeImgSrc).toEqual(hoveredImgSrc); - }); -}); - -/** - * @param {import('@playwright/test').Page} page - */ -async function saveTemplate(page) { - await page.locator('.c-button--menu.c-button--major.icon-save').click(); - await page.locator('text=Save and Finish Editing').click(); } /** @@ -692,7 +530,7 @@ async function assertBackgroundImageUrlFromBackgroundCss(page) { return backgroundImageUrl2; }, { message: "verify next image has updated", - timeout: 6 * 1000 + timeout: 7 * 1000 }).not.toBe(backgroundImageUrl1); console.log('backgroundImageUrl2 ' + backgroundImageUrl2); } @@ -746,14 +584,17 @@ async function panZoomAndAssertImageProperties(page) { } /** + * Use the mouse wheel to zoom in or out of an image and assert that the image + * has successfully zoomed in or out. * @param {import('@playwright/test').Page} page + * @param {number} [factor = 2] The zoom factor. Positive for zoom in, negative for zoom out. */ -async function mouseZoomIn(page) { +async function mouseZoomOnImageAndAssert(page, factor = 2) { // Zoom in const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox(); await page.locator(backgroundImageSelector).hover({trial: true}); const deltaYStep = 100; // equivalent to 1x zoom - await page.mouse.wheel(0, deltaYStep * 2); + await page.mouse.wheel(0, deltaYStep * factor); const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2; const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2; @@ -763,9 +604,47 @@ async function mouseZoomIn(page) { // Wait for zoom animation to finish await page.locator(backgroundImageSelector).hover({trial: true}); - const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox(); - expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height); - expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width); + const imageMouseZoomed = await page.locator(backgroundImageSelector).boundingBox(); + + if (factor > 0) { + expect(imageMouseZoomed.height).toBeGreaterThan(originalImageDimensions.height); + expect(imageMouseZoomed.width).toBeGreaterThan(originalImageDimensions.width); + } else { + expect(imageMouseZoomed.height).toBeLessThan(originalImageDimensions.height); + expect(imageMouseZoomed.width).toBeLessThan(originalImageDimensions.width); + } +} + +/** + * Zoom in and out of the image using the buttons, and assert that the image has + * been successfully zoomed in or out. + * @param {import('@playwright/test').Page} page + */ +async function buttonZoomOnImageAndAssert(page) { + // Get initial image dimensions + const initialBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); + + // Zoom in twice via button + await zoomIntoImageryByButton(page); + await zoomIntoImageryByButton(page); + + // Get and assert zoomed in image dimensions + const zoomedInBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); + expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height); + expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width); + + // Zoom out once via button + await zoomOutOfImageryByButton(page); + + // Get and assert zoomed out image dimensions + const zoomedOutBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); + expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height); + expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width); + + // Zoom out again via button, assert against the initial image dimensions + await zoomOutOfImageryByButton(page); + const finalBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); + expect(finalBoundingBox).toEqual(initialBoundingBox); } /** diff --git a/package.json b/package.json index b901f58d2e..e0a3895a7f 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "@braintree/sanitize-url": "6.0.0", "@percy/cli": "1.7.2", "@percy/playwright": "1.0.4", - "@playwright/test": "1.25.0", + "@playwright/test": "1.25.2", "@types/eventemitter3": "^1.0.0", "@types/jasmine": "^4.0.1", "@types/karma": "^6.3.2",