From 4cf63062c0251c9f810dec1af9f64cb7ee2cc904 Mon Sep 17 00:00:00 2001 From: John Hill Date: Mon, 22 Jan 2024 21:41:56 -0800 Subject: [PATCH] Mct7367-tests (#7387) * refactor(ExportAsJSONAction): use private methods * refactor: remove unnecessary webpack alias * refactor: lint * fix: tests for `ExportAsJSONAction` * test: stabilize `InspectorStylesSpec` tests * docs: fix jsdocs * chore: remove dead / redundant code * refactor(LocalStorageObjectProvider): use `getItem()` and `setItem()` * refactor(ExportAsJSONAction): use `Promise.all` where applicable * refactor(MenuAPI): one-liner * feat: add percentage ProgressBar to ExportAsJSONAction * fix(ProgressBar.vue): v-if conditionals * test(fix): update mockLocalStorage * test: fix locators * test: remove unneeded awaits * fix: example imagery urls (moved after NASA wordpress migration) * Revert "refactor(LocalStorageObjectProvider): use `getItem()` and `setItem()`" This reverts commit 4f8403adaba5f62ab3a83e5c28ae9c4856a6046b. * test(e2e): fix logPlot test * Revert "Revert "refactor(LocalStorageObjectProvider): use `getItem()` and `setItem()`"" This reverts commit 0de66401cdd0cbe3e88a89c9c6d2bfe6773257fb. * test(e2e): remove waitForNavigations * driveby and fixes * aria improvement * getting tests back oline * more tests * add last test * Add a11y * lint * lint * driveby * review comments * driveby rename * fix selectors and break up test suites * add test for snapshot in header * last lint fixes * stable --------- Co-authored-by: Jesse Mazzella --- e2e/appActions.js | 2 +- e2e/helper/notebookUtils.js | 2 +- e2e/tests/framework/appActions.e2e.spec.js | 12 +- .../exportAsJson.e2e.spec.js | 202 +++++++++++++++--- .../numericData.e2e.spec.js | 2 +- .../functional/plugins/lad/ladSet.e2e.spec.js | 3 + .../notebookSnapshotImage.e2e.spec.js | 147 +++++++++++++ .../notebook/notebookSnapshots.e2e.spec.js | 141 +----------- ...s.e2e.spec.js => notebookTags.e2e.spec.js} | 0 .../operatorStatus/operatorStatus.e2e.spec.js | 2 +- e2e/tests/functional/userRoles.e2e.spec.js | 6 +- .../components/header.visual.spec.js | 13 +- .../imagery/components/ImageryView.vue | 4 +- src/ui/components/ProgressBar.vue | 4 + src/ui/layout/AppLayout.vue | 2 + 15 files changed, 372 insertions(+), 170 deletions(-) create mode 100644 e2e/tests/functional/plugins/notebook/notebookSnapshotImage.e2e.spec.js rename e2e/tests/functional/plugins/notebook/{tags.e2e.spec.js => notebookTags.e2e.spec.js} (100%) diff --git a/e2e/appActions.js b/e2e/appActions.js index f427424b91..92b71fd281 100644 --- a/e2e/appActions.js +++ b/e2e/appActions.js @@ -284,7 +284,7 @@ async function navigateToObjectWithFixedTimeBounds(page, url, start, end) { */ async function openObjectTreeContextMenu(page, url) { await page.goto(url); - await page.click('button[title="Show selected item in tree"]'); + await page.getByLabel('Show selected item in tree').click(); await page.locator('.is-navigated-object').click({ button: 'right' }); diff --git a/e2e/helper/notebookUtils.js b/e2e/helper/notebookUtils.js index 9ab477cae9..de7177af22 100644 --- a/e2e/helper/notebookUtils.js +++ b/e2e/helper/notebookUtils.js @@ -49,7 +49,7 @@ async function dragAndDropEmbed(page, notebookObject) { // Navigate to notebook await page.goto(notebookObject.url); // Expand the tree to reveal the notebook - await page.click('button[title="Show selected item in tree"]'); + await page.getByLabel('Show selected item in tree').click(); // Drag and drop the SWG into the notebook await page.dragAndDrop(`text=${swg.name}`, NOTEBOOK_DROP_AREA); await commitEntry(page); diff --git a/e2e/tests/framework/appActions.e2e.spec.js b/e2e/tests/framework/appActions.e2e.spec.js index 5b576718d8..4aba0e196e 100644 --- a/e2e/tests/framework/appActions.e2e.spec.js +++ b/e2e/tests/framework/appActions.e2e.spec.js @@ -23,7 +23,8 @@ import { createDomainObjectWithDefaults, createNotification, - expandEntireTree + expandEntireTree, + openObjectTreeContextMenu } from '../../appActions.js'; import { expect, test } from '../../pluginFixtures.js'; @@ -166,4 +167,13 @@ test.describe('AppActions', () => { const locatorTreeCollapsedItems = locatorTree.locator('role=treeitem[expanded=false]'); expect(await locatorTreeCollapsedItems.count()).toBe(0); }); + test('openObjectTreeContextMenu', async ({ page }) => { + await page.goto('./', { waitUntil: 'domcontentloaded' }); + + const folder = await createDomainObjectWithDefaults(page, { + type: 'Folder' + }); + await openObjectTreeContextMenu(page, folder.url); + await expect(page.getByLabel('Menu')).toBeVisible(); + }); }); diff --git a/e2e/tests/functional/plugins/importAndExportAsJSON/exportAsJson.e2e.spec.js b/e2e/tests/functional/plugins/importAndExportAsJSON/exportAsJson.e2e.spec.js index 218b34e693..e8cc3c6344 100644 --- a/e2e/tests/functional/plugins/importAndExportAsJSON/exportAsJson.e2e.spec.js +++ b/e2e/tests/functional/plugins/importAndExportAsJSON/exportAsJson.e2e.spec.js @@ -24,36 +24,182 @@ This test suite is dedicated to tests which verify the basic operations surrounding exportAsJSON. */ -// FIXME: Remove this eslint exception once tests are implemented -// eslint-disable-next-line no-unused-vars +import fs from 'fs/promises'; + +import { + createDomainObjectWithDefaults, + openObjectTreeContextMenu +} from '../../../../appActions.js'; import { expect, test } from '../../../../baseFixtures.js'; +import { navigateToFaultManagementWithExample } from '../../../../helper/faultUtils.js'; test.describe('ExportAsJSON', () => { - test.fixme( - 'Create a basic object and verify that it can be exported as JSON from Tree', - async ({ page }) => { - //Create domain object - //Save Domain Object - //Verify that the newly created domain object can be exported as JSON from the Tree - } - ); - test.fixme( - 'Create a basic object and verify that it can be exported as JSON from 3 dot menu', - async ({ page }) => { - //Create domain object - //Save Domain Object - //Verify that the newly created domain object can be exported as JSON from the 3 dot menu - } - ); - test.fixme('Verify that a nested Object can be exported as JSON', async ({ page }) => { - // Create 2 objects with hierarchy - // Export as JSON - // Verify Hierarchy + let folder; + test.beforeEach(async ({ page }) => { + // Go to baseURL + await page.goto('./'); + // Perform actions to create the domain object + folder = await createDomainObjectWithDefaults(page, { + type: 'Folder', + name: 'e2e folder' + }); + }); + test('Create a basic object and verify that it can be exported as JSON from Tree', async ({ + page + }) => { + // Navigate to the page + await page.goto(folder.url); + + // Open context menu and initiate download + await openObjectTreeContextMenu(page, folder.url); + const [download] = await Promise.all([ + page.waitForEvent('download'), // Waits for the download event + page.getByLabel('Export as JSON').click() // Triggers the download + ]); + + // Wait for the download process to complete + const path = await download.path(); + + // Read the contents of the downloaded file using readFile from fs/promises + const fileContents = await fs.readFile(path, 'utf8'); + const jsonData = JSON.parse(fileContents); + + // Use the function to retrieve the key + const key = getFirstKeyFromOpenMctJson(jsonData); + + // Verify the contents of the JSON file + expect(jsonData.openmct[key]).toHaveProperty('name', 'e2e folder'); + expect(jsonData.openmct[key]).toHaveProperty('type', 'folder'); + }); + test('Create a basic object and verify that it can be exported as JSON from 3 dot menu', async ({ + page + }) => { + // Navigate to the page + await page.goto(folder.url); + //3 dot menu + await page.getByLabel('More actions').click(); + // Open context menu and initiate download + const [download] = await Promise.all([ + page.waitForEvent('download'), // Waits for the download event + page.getByLabel('Export as JSON').click() // Triggers the download + ]); + + // Read the contents of the downloaded file using readFile from fs/promises + const fileContents = await fs.readFile(await download.path(), 'utf8'); + const jsonData = JSON.parse(fileContents); + + // Use the function to retrieve the key + const key = getFirstKeyFromOpenMctJson(jsonData); + + // Verify the contents of the JSON file + expect(jsonData.openmct[key]).toHaveProperty('name', 'e2e folder'); + expect(jsonData.openmct[key]).toHaveProperty('type', 'folder'); + }); + test('Verify that a nested Object can be exported as JSON', async ({ page }) => { + const timer = await createDomainObjectWithDefaults(page, { + type: 'Timer', + name: 'timer', + parent: folder.uuid + }); + // Navigate to the page + await page.goto(timer.url); + + //do this against parent folder.url, NOT timer.url child + await openObjectTreeContextMenu(page, folder.url); + // Open context menu and initiate download + const [download] = await Promise.all([ + page.waitForEvent('download'), // Waits for the download event + page.getByLabel('Export as JSON').click() // Triggers the download + ]); + + // Read the contents of the downloaded file + const fileContents = await fs.readFile(await download.path(), 'utf8'); + const jsonData = JSON.parse(fileContents); + + // Retrieve the keys for folder and timer + const folderKey = getFirstKeyFromOpenMctJson(jsonData); + const timerKey = jsonData.openmct[folderKey].composition[0].key; + + // Verify the folder properties + expect(jsonData.openmct[folderKey]).toHaveProperty('name', 'e2e folder'); + expect(jsonData.openmct[folderKey]).toHaveProperty('type', 'folder'); + + // Verify the timer properties + expect(jsonData.openmct[timerKey]).toHaveProperty('name', 'timer'); + expect(jsonData.openmct[timerKey]).toHaveProperty('type', 'timer'); + + // Verify the composition of the folder includes the timer + expect(jsonData.openmct[folderKey].composition).toEqual( + expect.arrayContaining([expect.objectContaining({ key: timerKey })]) + ); }); - test.fixme( - 'Verify that the ExportAsJSON dropdown does not appear for the item X', - async ({ page }) => { - // Other than non-persistable objects - } - ); }); +test.describe('ExportAsJSON Disabled Actions', () => { + test.beforeEach(async ({ page }) => { + //Use a Fault Management Object which is not composable + await navigateToFaultManagementWithExample(page); + }); + test('Verify that the ExportAsJSON dropdown does not appear for the item X', async ({ page }) => { + await page.getByLabel('More actions').click(); + await expect(await page.getByLabel('Export as JSON')).toHaveCount(0); + + await page.getByRole('treeitem', { name: 'Fault Management' }).click({ + button: 'right' + }); + await expect(await page.getByLabel('Export as JSON')).toHaveCount(0); + }); +}); +test.describe('ExportAsJSON ProgressBar @couchdb', () => { + let folder; + test.beforeEach(async ({ page }) => { + await page.goto('./', { waitUntil: 'networkidle' }); + // Perform actions to create the domain object + folder = await createDomainObjectWithDefaults(page, { + type: 'Folder' + }); + await createDomainObjectWithDefaults(page, { + type: 'Timer', + parent: folder.uuid + }); + await createDomainObjectWithDefaults(page, { + type: 'Timer', + parent: folder.uuid + }); + }); + test('Verify that the ExportAsJSON action creates a progressbar', async ({ page }) => { + // Navigate to the page + await page.goto(folder.url); + + //Export My Items to create a large export + await page.getByRole('treeitem', { name: 'My Items' }).click({ button: 'right' }); + // Open context menu and initiate download + await Promise.all([ + page.getByRole('progressbar'), // This is just a check for the progress bar + page.getByText( + 'Do not navigate away from this page or close this browser tab while this message' + ), // This is the text associated with the download + page.waitForEvent('download'), // Waits for the download event + page.getByLabel('Export as JSON').click() // Triggers the download + ]); + }); +}); + +/** + * Retrieves the first key from the 'openmct' property of the provided JSON object. + * + * @param {Object} jsonData - The JSON object containing the 'openmct' property. + * @returns {string} The first key found in the 'openmct' object. + * @throws {Error} If no keys are found in the 'openmct' object. + */ +function getFirstKeyFromOpenMctJson(jsonData) { + if (!jsonData.openmct) { + throw new Error("The provided JSON object does not have an 'openmct' property."); + } + + const keys = Object.keys(jsonData.openmct); + if (keys.length === 0) { + throw new Error('No keys found in the openmct object'); + } + + return keys[0]; +} diff --git a/e2e/tests/functional/plugins/inspectorDataVisualization/numericData.e2e.spec.js b/e2e/tests/functional/plugins/inspectorDataVisualization/numericData.e2e.spec.js index a3f74b8f16..8bfb1a666a 100644 --- a/e2e/tests/functional/plugins/inspectorDataVisualization/numericData.e2e.spec.js +++ b/e2e/tests/functional/plugins/inspectorDataVisualization/numericData.e2e.spec.js @@ -36,7 +36,7 @@ test.describe('Testing numeric data with inspector data visualization (i.e., dat await page.goto('./', { waitUntil: 'domcontentloaded' }); }); - test('Can click on telemetry and see data in inspector', async ({ page, context }) => { + test('Can click on telemetry and see data in inspector @2p', async ({ page, context }) => { const exampleDataVisualizationSource = await createDomainObjectWithDefaults(page, { type: 'Example Data Visualization Source' }); diff --git a/e2e/tests/functional/plugins/lad/ladSet.e2e.spec.js b/e2e/tests/functional/plugins/lad/ladSet.e2e.spec.js index 838eb57187..f2b8a46aed 100644 --- a/e2e/tests/functional/plugins/lad/ladSet.e2e.spec.js +++ b/e2e/tests/functional/plugins/lad/ladSet.e2e.spec.js @@ -53,6 +53,9 @@ test.describe('LAD Table Sets', () => { await page.goto(ladTableSet.url); + // Wait for the initial value to show after mount + await expect(page.getByLabel('lad value').first()).not.toContainText('---'); + const valueFromFirstSineWave = await page.getByLabel('lad value').first().innerText(); const firstSineWaveNumber = parseFloat(valueFromFirstSineWave); // ensure we have a float value in the cell and it's finite diff --git a/e2e/tests/functional/plugins/notebook/notebookSnapshotImage.e2e.spec.js b/e2e/tests/functional/plugins/notebook/notebookSnapshotImage.e2e.spec.js new file mode 100644 index 0000000000..c6f4f902e8 --- /dev/null +++ b/e2e/tests/functional/plugins/notebook/notebookSnapshotImage.e2e.spec.js @@ -0,0 +1,147 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2024, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + +/* +This test suite is dedicated to tests which verify the basic operations surrounding Notebooks. +*/ + +import fs from 'fs/promises'; +import { fileURLToPath } from 'url'; + +import { createDomainObjectWithDefaults } from '../../../../appActions.js'; +import { expect, test } from '../../../../pluginFixtures.js'; + +const NOTEBOOK_NAME = 'Notebook'; + +test.describe('Snapshot image tests', () => { + test.beforeEach(async ({ page }) => { + //Navigate to baseURL + await page.goto('./', { waitUntil: 'domcontentloaded' }); + + // Create Notebook + await createDomainObjectWithDefaults(page, { + type: NOTEBOOK_NAME + }); + }); + + test('Can drop an image onto a notebook and create a new entry', async ({ page }) => { + const imageData = await fs.readFile( + fileURLToPath( + new URL('../../../../../src/images/favicons/favicon-96x96.png', import.meta.url) + ) + ); + const imageArray = new Uint8Array(imageData); + const fileData = Array.from(imageArray); + + const dropTransfer = await page.evaluateHandle((data) => { + const dataTransfer = new DataTransfer(); + const file = new File([new Uint8Array(data)], 'favicon-96x96.png', { type: 'image/png' }); + dataTransfer.items.add(file); + return dataTransfer; + }, fileData); + + await page.dispatchEvent('.c-notebook__drag-area', 'drop', { dataTransfer: dropTransfer }); + await page.locator('.c-ne__save-button > button').click(); + // be sure that entry was created + await expect(page.getByText('favicon-96x96.png')).toBeVisible(); + + await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).click(); + // expect large image to be displayed + await expect(page.getByRole('dialog').getByText('favicon-96x96.png')).toBeVisible(); + + await page.getByLabel('Close').click(); + + // drop another image onto the entry + await page.dispatchEvent('.c-snapshots', 'drop', { dataTransfer: dropTransfer }); + + const secondThumbnail = page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).nth(1); + await secondThumbnail.waitFor({ state: 'attached' }); + // expect two embedded images now + expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(2); + + await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More actions').click(); + + await page.getByRole('menuitem', { name: /Remove This Embed/ }).click(); + await page.getByRole('button', { name: 'Ok', exact: true }).click(); + // Ensure that the thumbnail is removed before we assert + await secondThumbnail.waitFor({ state: 'detached' }); + + // expect one embedded image now as we deleted the other + expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(1); + }); +}); + +test.describe('Snapshot image failure tests', () => { + test.use({ failOnConsoleError: false }); + test.beforeEach(async ({ page }) => { + //Navigate to baseURL + await page.goto('./', { waitUntil: 'domcontentloaded' }); + + // Create Notebook + await createDomainObjectWithDefaults(page, { + type: NOTEBOOK_NAME + }); + }); + + test('Get an error notification when dropping unknown file onto notebook entry', async ({ + page + }) => { + // fill Uint8Array array with some garbage data + const garbageData = new Uint8Array(100); + const fileData = Array.from(garbageData); + + const dropTransfer = await page.evaluateHandle((data) => { + const dataTransfer = new DataTransfer(); + const file = new File([new Uint8Array(data)], 'someGarbage.foo', { type: 'unknown/garbage' }); + dataTransfer.items.add(file); + return dataTransfer; + }, fileData); + + await page.dispatchEvent('.c-notebook__drag-area', 'drop', { dataTransfer: dropTransfer }); + + // should have gotten a notification from OpenMCT that we couldn't add it + await expect(page.getByText('Unknown object(s) dropped and cannot embed')).toBeVisible(); + }); + + test('Get an error notification when dropping big files onto notebook entry', async ({ + page + }) => { + const garbageSize = 15 * 1024 * 1024; // 15 megabytes + + await page.addScriptTag({ + // make the garbage client side + content: `window.bigGarbageData = new Uint8Array(${garbageSize})` + }); + + const bigDropTransfer = await page.evaluateHandle(() => { + const dataTransfer = new DataTransfer(); + const file = new File([window.bigGarbageData], 'bigBoy.png', { type: 'image/png' }); + dataTransfer.items.add(file); + return dataTransfer; + }); + + await page.dispatchEvent('.c-notebook__drag-area', 'drop', { dataTransfer: bigDropTransfer }); + + // should have gotten a notification from OpenMCT that we couldn't add it as it's too big + await expect(page.getByText('unable to embed')).toBeVisible(); + }); +}); diff --git a/e2e/tests/functional/plugins/notebook/notebookSnapshots.e2e.spec.js b/e2e/tests/functional/plugins/notebook/notebookSnapshots.e2e.spec.js index be7532c316..b6064e8665 100644 --- a/e2e/tests/functional/plugins/notebook/notebookSnapshots.e2e.spec.js +++ b/e2e/tests/functional/plugins/notebook/notebookSnapshots.e2e.spec.js @@ -24,14 +24,8 @@ This test suite is dedicated to tests which verify the basic operations surrounding Notebooks. */ -import fs from 'fs/promises'; -import { fileURLToPath } from 'url'; - -import { createDomainObjectWithDefaults } from '../../../../appActions.js'; import { expect, test } from '../../../../pluginFixtures.js'; -const NOTEBOOK_NAME = 'Notebook'; - test.describe('Snapshot Menu tests', () => { test.fixme( 'When no default notebook is selected, Snapshot Menu dropdown should only have a single option', @@ -91,22 +85,13 @@ test.describe('Snapshot Container tests', () => { await page.getByLabel('Take a Notebook Snapshot').click(); await page.getByRole('menuitem', { name: 'Save to Notebook Snapshots' }).click(); - await page.getByRole('button', { name: 'Show' }).click(); + await page.getByLabel('Show Snapshots').click(); }); test('A snapshot can be Quick Viewed from Container with 3 dot action menu', async ({ page }) => { await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More actions').click(); await page.getByRole('menuitem', { name: 'Quick View' }).click(); await expect(page.locator('.c-overlay__outer')).toBeVisible(); }); - test.fixme('5 Snapshots can be added to a container', async ({ page }) => {}); - test.fixme( - '5 Snapshots can be added to a container and Deleted with Delete All action', - async ({ page }) => {} - ); - test.fixme( - 'A snapshot can be Deleted from Container with 3 dot action menu', - async ({ page }) => {} - ); test.fixme( 'A snapshot can be Viewed, Annotated, display deleted, and saved from Container with 3 dot action menu', async ({ page }) => { @@ -122,7 +107,15 @@ test.describe('Snapshot Container tests', () => { //await expect(await page.locator) } ); - + test.fixme('5 Snapshots can be added to a container', async ({ page }) => {}); + test.fixme( + '5 Snapshots can be added to a container and Deleted with Delete All action', + async ({ page }) => {} + ); + test.fixme( + 'A snapshot can be Deleted from Container with 3 dot action menu', + async ({ page }) => {} + ); test.fixme( 'A snapshot can be Navigated To from Container with 3 dot action menu', async ({ page }) => {} @@ -166,117 +159,3 @@ test.describe('Snapshot Container tests', () => { } ); }); - -test.describe('Snapshot image tests', () => { - test.beforeEach(async ({ page }) => { - //Navigate to baseURL - await page.goto('./', { waitUntil: 'domcontentloaded' }); - - // Create Notebook - await createDomainObjectWithDefaults(page, { - type: NOTEBOOK_NAME - }); - }); - - test('Can drop an image onto a notebook and create a new entry', async ({ page }) => { - const imageData = await fs.readFile( - fileURLToPath( - new URL('../../../../../src/images/favicons/favicon-96x96.png', import.meta.url) - ) - ); - const imageArray = new Uint8Array(imageData); - const fileData = Array.from(imageArray); - - const dropTransfer = await page.evaluateHandle((data) => { - const dataTransfer = new DataTransfer(); - const file = new File([new Uint8Array(data)], 'favicon-96x96.png', { type: 'image/png' }); - dataTransfer.items.add(file); - return dataTransfer; - }, fileData); - - await page.dispatchEvent('.c-notebook__drag-area', 'drop', { dataTransfer: dropTransfer }); - await page.locator('.c-ne__save-button > button').click(); - // be sure that entry was created - await expect(page.getByText('favicon-96x96.png')).toBeVisible(); - - await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).click(); - // expect large image to be displayed - await expect(page.getByRole('dialog').getByText('favicon-96x96.png')).toBeVisible(); - - await page.getByLabel('Close').click(); - - // drop another image onto the entry - await page.dispatchEvent('.c-snapshots', 'drop', { dataTransfer: dropTransfer }); - - const secondThumbnail = page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).nth(1); - await secondThumbnail.waitFor({ state: 'attached' }); - // expect two embedded images now - expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(2); - - await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More actions').click(); - - await page.getByRole('menuitem', { name: /Remove This Embed/ }).click(); - await page.getByRole('button', { name: 'Ok', exact: true }).click(); - // Ensure that the thumbnail is removed before we assert - await secondThumbnail.waitFor({ state: 'detached' }); - - // expect one embedded image now as we deleted the other - expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(1); - }); -}); - -test.describe('Snapshot image failure tests', () => { - test.use({ failOnConsoleError: false }); - test.beforeEach(async ({ page }) => { - //Navigate to baseURL - await page.goto('./', { waitUntil: 'domcontentloaded' }); - - // Create Notebook - await createDomainObjectWithDefaults(page, { - type: NOTEBOOK_NAME - }); - }); - - test('Get an error notification when dropping unknown file onto notebook entry', async ({ - page - }) => { - // fill Uint8Array array with some garbage data - const garbageData = new Uint8Array(100); - const fileData = Array.from(garbageData); - - const dropTransfer = await page.evaluateHandle((data) => { - const dataTransfer = new DataTransfer(); - const file = new File([new Uint8Array(data)], 'someGarbage.foo', { type: 'unknown/garbage' }); - dataTransfer.items.add(file); - return dataTransfer; - }, fileData); - - await page.dispatchEvent('.c-notebook__drag-area', 'drop', { dataTransfer: dropTransfer }); - - // should have gotten a notification from OpenMCT that we couldn't add it - await expect(page.getByText('Unknown object(s) dropped and cannot embed')).toBeVisible(); - }); - - test('Get an error notification when dropping big files onto notebook entry', async ({ - page - }) => { - const garbageSize = 15 * 1024 * 1024; // 15 megabytes - - await page.addScriptTag({ - // make the garbage client side - content: `window.bigGarbageData = new Uint8Array(${garbageSize})` - }); - - const bigDropTransfer = await page.evaluateHandle(() => { - const dataTransfer = new DataTransfer(); - const file = new File([window.bigGarbageData], 'bigBoy.png', { type: 'image/png' }); - dataTransfer.items.add(file); - return dataTransfer; - }); - - await page.dispatchEvent('.c-notebook__drag-area', 'drop', { dataTransfer: bigDropTransfer }); - - // should have gotten a notification from OpenMCT that we couldn't add it as it's too big - await expect(page.getByText('unable to embed')).toBeVisible(); - }); -}); diff --git a/e2e/tests/functional/plugins/notebook/tags.e2e.spec.js b/e2e/tests/functional/plugins/notebook/notebookTags.e2e.spec.js similarity index 100% rename from e2e/tests/functional/plugins/notebook/tags.e2e.spec.js rename to e2e/tests/functional/plugins/notebook/notebookTags.e2e.spec.js diff --git a/e2e/tests/functional/plugins/operatorStatus/operatorStatus.e2e.spec.js b/e2e/tests/functional/plugins/operatorStatus/operatorStatus.e2e.spec.js index 24f17e88c9..d2b75e8f56 100644 --- a/e2e/tests/functional/plugins/operatorStatus/operatorStatus.e2e.spec.js +++ b/e2e/tests/functional/plugins/operatorStatus/operatorStatus.e2e.spec.js @@ -51,7 +51,7 @@ test.describe('Operator Status', () => { // Description should be empty https://github.com/nasa/openmct/issues/6978 await expect(page.locator('.c-message__action-text')).toBeHidden(); // set role - await page.getByRole('button', { name: 'Select' }).click(); + await page.getByRole('button', { name: 'Select', exact: true }).click(); // dismiss role confirmation popup await page.getByRole('button', { name: 'Dismiss' }).click(); }); diff --git a/e2e/tests/functional/userRoles.e2e.spec.js b/e2e/tests/functional/userRoles.e2e.spec.js index ba9f6dbf76..455b096aab 100644 --- a/e2e/tests/functional/userRoles.e2e.spec.js +++ b/e2e/tests/functional/userRoles.e2e.spec.js @@ -34,13 +34,13 @@ test.describe('User Roles', () => { // we have multiple available roles, so it should prompt the user await expect(page.getByText('Select Role')).toBeVisible(); await page.getByRole('combobox').selectOption('driver'); - await page.getByRole('button', { name: 'Select' }).click(); + await page.getByRole('button', { name: 'Select', exact: true }).click(); await expect(page.getByLabel('User Role')).toContainText('driver'); // attempt changing the role to another valid available role await page.getByRole('button', { name: 'Change Role' }).click(); await page.getByRole('combobox').selectOption('flight'); - await page.getByRole('button', { name: 'Select' }).click(); + await page.getByRole('button', { name: 'Select', exact: true }).click(); await expect(page.getByLabel('User Role')).toContainText('flight'); // reload page @@ -63,7 +63,7 @@ test.describe('User Roles', () => { // select real role of "driver" await page.getByRole('combobox').selectOption('driver'); - await page.getByRole('button', { name: 'Select' }).click(); + await page.getByRole('button', { name: 'Select', exact: true }).click(); await expect(page.getByLabel('User Role')).toContainText('driver'); }); }); diff --git a/e2e/tests/visual-a11y/components/header.visual.spec.js b/e2e/tests/visual-a11y/components/header.visual.spec.js index f6485fb5dc..70e7f53480 100644 --- a/e2e/tests/visual-a11y/components/header.visual.spec.js +++ b/e2e/tests/visual-a11y/components/header.visual.spec.js @@ -26,7 +26,7 @@ Tests the branding associated with the default deployment. At least the about mo import percySnapshot from '@percy/playwright'; -import { scanForA11yViolations, test } from '../../../avpFixtures.js'; +import { expect, scanForA11yViolations, test } from '../../../avpFixtures.js'; import { VISUAL_URL } from '../../../constants.js'; //Declare the scope of the visual test @@ -50,6 +50,17 @@ test.describe('Visual - Header @a11y', () => { scope: header }); }); + + test('show snapshot button', async ({ page, theme }) => { + await page.getByLabel('Take a Notebook Snapshot').click(); + + await page.getByRole('menuitem', { name: 'Save to Notebook Snapshots' }).click(); + + await percySnapshot(page, `Notebook Snapshot Show button (theme: '${theme}')`, { + scope: header + }); + await expect(await page.getByLabel('Show Snapshots')).toBeVisible(); + }); }); test.afterEach(async ({ page }, testInfo) => { await scanForA11yViolations(page, testInfo.title); diff --git a/src/plugins/imagery/components/ImageryView.vue b/src/plugins/imagery/components/ImageryView.vue index 97ca81fe85..4615b95ad3 100644 --- a/src/plugins/imagery/components/ImageryView.vue +++ b/src/plugins/imagery/components/ImageryView.vue @@ -56,8 +56,8 @@ {{ formatImageAltText }}
{{ progressPerc }}% complete. diff --git a/src/ui/layout/AppLayout.vue b/src/ui/layout/AppLayout.vue index cc422a6f5b..acbfaae195 100644 --- a/src/ui/layout/AppLayout.vue +++ b/src/ui/layout/AppLayout.vue @@ -85,11 +85,13 @@