diff --git a/e2e/appActions.js b/e2e/appActions.js index 56cc0e5a7f..230602334b 100644 --- a/e2e/appActions.js +++ b/e2e/appActions.js @@ -314,15 +314,13 @@ async function _isInEditMode(page, identifier) { */ async function setTimeConductorMode(page, isFixedTimespan = true) { // Click 'mode' button - const timeConductorMode = await page.locator('.c-compact-tc'); - await timeConductorMode.click(); - await timeConductorMode.locator('.js-mode-button').click(); - + await page.getByRole('button', { name: 'Time Conductor Mode', exact: true }).click(); + await page.getByRole('button', { name: 'Time Conductor Mode Menu' }).click(); // Switch time conductor mode if (isFixedTimespan) { - await page.locator('data-testid=conductor-modeOption-fixed').click(); + await page.getByRole('menuitem', { name: /Fixed Timespan/ }).click(); } else { - await page.locator('data-testid=conductor-modeOption-realtime').click(); + await page.getByRole('menuitem', { name: /Real-Time/ }).click(); } } @@ -344,9 +342,12 @@ async function setRealTimeMode(page) { /** * @typedef {Object} OffsetValues - * @property {string | undefined} hours - * @property {string | undefined} mins - * @property {string | undefined} secs + * @property {string | undefined} startHours + * @property {string | undefined} startMins + * @property {string | undefined} startSecs + * @property {string | undefined} endHours + * @property {string | undefined} endMins + * @property {string | undefined} endSecs */ /** @@ -355,19 +356,32 @@ async function setRealTimeMode(page) { * @param {OffsetValues} offset * @param {import('@playwright/test').Locator} offsetButton */ -async function setTimeConductorOffset(page, { hours, mins, secs }) { - // await offsetButton.click(); - - if (hours) { - await page.fill('.pr-time-input__hrs', hours); +async function setTimeConductorOffset( + page, + { startHours, startMins, startSecs, endHours, endMins, endSecs } +) { + if (startHours) { + await page.getByRole('spinbutton', { name: 'Start offset hours' }).fill(startHours); } - if (mins) { - await page.fill('.pr-time-input__mins', mins); + if (startMins) { + await page.getByRole('spinbutton', { name: 'Start offset minutes' }).fill(startMins); } - if (secs) { - await page.fill('.pr-time-input__secs', secs); + if (startSecs) { + await page.getByRole('spinbutton', { name: 'Start offset seconds' }).fill(startSecs); + } + + if (endHours) { + await page.getByRole('spinbutton', { name: 'End offset hours' }).fill(endHours); + } + + if (endMins) { + await page.getByRole('spinbutton', { name: 'End offset minutes' }).fill(endMins); + } + + if (endSecs) { + await page.getByRole('spinbutton', { name: 'End offset seconds' }).fill(endSecs); } // Click the check button @@ -381,8 +395,7 @@ async function setTimeConductorOffset(page, { hours, mins, secs }) { */ async function setStartOffset(page, offset) { // Click 'mode' button - const timeConductorMode = await page.locator('.c-compact-tc'); - await timeConductorMode.click(); + await page.getByRole('button', { name: 'Time Conductor Mode', exact: true }).click(); await setTimeConductorOffset(page, offset); } @@ -393,11 +406,53 @@ async function setStartOffset(page, offset) { */ async function setEndOffset(page, offset) { // Click 'mode' button - const timeConductorMode = await page.locator('.c-compact-tc'); - await timeConductorMode.click(); + await page.getByRole('button', { name: 'Time Conductor Mode', exact: true }).click(); await setTimeConductorOffset(page, offset); } +async function setTimeConductorBounds(page, startDate, endDate) { + // Bring up the time conductor popup + await page.click('.l-shell__time-conductor.c-compact-tc'); + + await setTimeBounds(page, startDate, endDate); + + await page.keyboard.press('Enter'); +} + +async function setIndependentTimeConductorBounds(page, startDate, endDate) { + // Activate Independent Time Conductor in Fixed Time Mode + await page.getByRole('switch').click(); + + // Bring up the time conductor popup + await page.click('.c-conductor-holder--compact .c-compact-tc'); + + await expect(page.locator('.itc-popout')).toBeVisible(); + + await setTimeBounds(page, startDate, endDate); + + await page.keyboard.press('Enter'); +} + +async function setTimeBounds(page, startDate, endDate) { + if (startDate) { + // Fill start time + await page + .getByRole('textbox', { name: 'Start date' }) + .fill(startDate.toString().substring(0, 10)); + await page + .getByRole('textbox', { name: 'Start time' }) + .fill(startDate.toString().substring(11, 19)); + } + + if (endDate) { + // Fill end time + await page.getByRole('textbox', { name: 'End date' }).fill(endDate.toString().substring(0, 10)); + await page + .getByRole('textbox', { name: 'End time' }) + .fill(endDate.toString().substring(11, 19)); + } +} + /** * Selects an inspector tab based on the provided tab name * @@ -509,6 +564,8 @@ module.exports = { setRealTimeMode, setStartOffset, setEndOffset, + setTimeConductorBounds, + setIndependentTimeConductorBounds, selectInspectorTab, waitForPlotsToRender }; diff --git a/e2e/tests/functional/notification.e2e.spec.js b/e2e/tests/functional/notification.e2e.spec.js index 69719ea0b3..040fbc666d 100644 --- a/e2e/tests/functional/notification.e2e.spec.js +++ b/e2e/tests/functional/notification.e2e.spec.js @@ -28,10 +28,10 @@ const { createDomainObjectWithDefaults, createNotification } = require('../../ap const { test, expect } = require('../../pluginFixtures'); test.describe('Notifications List', () => { - test('Notifications can be dismissed individually', async ({ page }) => { + test.fixme('Notifications can be dismissed individually', async ({ page }) => { test.info().annotations.push({ type: 'issue', - description: 'https://github.com/nasa/openmct/issues/6122' + description: 'https://github.com/nasa/openmct/issues/6820' }); // Go to baseURL diff --git a/e2e/tests/functional/planning/timelist.e2e.spec.js b/e2e/tests/functional/planning/timelist.e2e.spec.js index 54f65019f6..b5208e909c 100644 --- a/e2e/tests/functional/planning/timelist.e2e.spec.js +++ b/e2e/tests/functional/planning/timelist.e2e.spec.js @@ -110,7 +110,7 @@ test.describe('Time List', () => { await test.step('Does not show milliseconds in times', async () => { // Get the first activity - const row = await page.locator('.js-list-item').first(); + const row = page.locator('.js-list-item').first(); // Verify that none fo the times have milliseconds displayed. // Example: 2024-11-17T16:00:00Z is correct and 2024-11-17T16:00:00.000Z is wrong diff --git a/e2e/tests/functional/planning/timestrip.e2e.spec.js b/e2e/tests/functional/planning/timestrip.e2e.spec.js index 0bff22ffd4..5b882df74d 100644 --- a/e2e/tests/functional/planning/timestrip.e2e.spec.js +++ b/e2e/tests/functional/planning/timestrip.e2e.spec.js @@ -21,7 +21,11 @@ *****************************************************************************/ const { test, expect } = require('../../../pluginFixtures'); -const { createDomainObjectWithDefaults, createPlanFromJSON } = require('../../../appActions'); +const { + createDomainObjectWithDefaults, + createPlanFromJSON, + setIndependentTimeConductorBounds +} = require('../../../appActions'); const testPlan = { TEST_GROUP: [ @@ -78,9 +82,6 @@ test.describe('Time Strip', () => { }); // Constant locators - const independentTimeConductorInputs = page.locator( - '.l-shell__main-independent-time-conductor .c-input--datetime' - ); const activityBounds = page.locator('.activity-bounds'); // Goto baseURL @@ -122,9 +123,7 @@ test.describe('Time Strip', () => { }); await test.step('TimeStrip can use the Independent Time Conductor', async () => { - // Activate Independent Time Conductor in Fixed Time Mode - await page.click('.c-toggle-switch__slider'); - expect(await activityBounds.count()).toEqual(0); + expect(await activityBounds.count()).toEqual(5); // Set the independent time bounds so that only one event is shown const startBound = testPlan.TEST_GROUP[0].start; @@ -132,12 +131,7 @@ test.describe('Time Strip', () => { const startBoundString = new Date(startBound).toISOString().replace('T', ' '); const endBoundString = new Date(endBound).toISOString().replace('T', ' '); - await independentTimeConductorInputs.nth(0).fill(''); - await independentTimeConductorInputs.nth(0).fill(startBoundString); - await page.keyboard.press('Enter'); - await independentTimeConductorInputs.nth(1).fill(''); - await independentTimeConductorInputs.nth(1).fill(endBoundString); - await page.keyboard.press('Enter'); + await setIndependentTimeConductorBounds(page, startBoundString, endBoundString); expect(await activityBounds.count()).toEqual(1); }); @@ -156,9 +150,6 @@ test.describe('Time Strip', () => { await page.click("button[title='Save']"); await page.click("li[title='Save and Finish Editing']"); - // Activate Independent Time Conductor in Fixed Time Mode - await page.click('.c-toggle-switch__slider'); - // All events should be displayed at this point because the // initial independent context bounds will match the global bounds expect(await activityBounds.count()).toEqual(5); @@ -169,12 +160,7 @@ test.describe('Time Strip', () => { const startBoundString = new Date(startBound).toISOString().replace('T', ' '); const endBoundString = new Date(endBound).toISOString().replace('T', ' '); - await independentTimeConductorInputs.nth(0).fill(''); - await independentTimeConductorInputs.nth(0).fill(startBoundString); - await page.keyboard.press('Enter'); - await independentTimeConductorInputs.nth(1).fill(''); - await independentTimeConductorInputs.nth(1).fill(endBoundString); - await page.keyboard.press('Enter'); + await setIndependentTimeConductorBounds(page, startBoundString, endBoundString); // Verify that two events are displayed expect(await activityBounds.count()).toEqual(2); diff --git a/e2e/tests/functional/plugins/clocks/clock.e2e.spec.js b/e2e/tests/functional/plugins/clocks/clock.e2e.spec.js index 565da1e116..b0afc5167f 100644 --- a/e2e/tests/functional/plugins/clocks/clock.e2e.spec.js +++ b/e2e/tests/functional/plugins/clocks/clock.e2e.spec.js @@ -41,7 +41,7 @@ test.describe('Clock Generator CRUD Operations', () => { await page.click('button:has-text("Create")'); // Click Clock - await page.click('text=Clock'); + await page.getByRole('menuitem').first().click(); // Click .icon-arrow-down await page.locator('.icon-arrow-down').click(); diff --git a/e2e/tests/functional/plugins/displayLayout/displayLayout.e2e.spec.js b/e2e/tests/functional/plugins/displayLayout/displayLayout.e2e.spec.js index 37840b6b62..e231074076 100644 --- a/e2e/tests/functional/plugins/displayLayout/displayLayout.e2e.spec.js +++ b/e2e/tests/functional/plugins/displayLayout/displayLayout.e2e.spec.js @@ -25,7 +25,8 @@ const { createDomainObjectWithDefaults, setStartOffset, setFixedTimeMode, - setRealTimeMode + setRealTimeMode, + setIndependentTimeConductorBounds } = require('../../../../appActions'); test.describe('Display Layout', () => { @@ -231,20 +232,27 @@ test.describe('Display Layout', () => { let layoutGridHolder = page.locator('.l-layout__grid-holder'); await exampleImageryTreeItem.dragTo(layoutGridHolder); + //adjust so that we can see the independent time conductor toggle + // Adjust object height + await page.locator('div[title="Resize object height"] > input').click(); + await page.locator('div[title="Resize object height"] > input').fill('70'); + + // Adjust object width + await page.locator('div[title="Resize object width"] > input').click(); + await page.locator('div[title="Resize object width"] > input').fill('70'); + await page.locator('button[title="Save"]').click(); await page.locator('text=Save and Finish Editing').click(); - // flip on independent time conductor - await page.getByTitle('Enable independent Time Conductor').first().locator('label').click(); - await page.getByRole('textbox').nth(1).fill('2021-12-30 01:11:00.000Z'); - await page.getByRole('textbox').nth(0).fill('2021-12-30 01:01:00.000Z'); - await page.getByRole('textbox').nth(1).click(); + const startDate = '2021-12-30 01:01:00.000Z'; + const endDate = '2021-12-30 01:11:00.000Z'; + await setIndependentTimeConductorBounds(page, startDate, endDate); // check image date await expect(page.getByText('2021-12-30 01:11:00.000Z').first()).toBeVisible(); // flip it off - await page.getByTitle('Disable independent Time Conductor').first().locator('label').click(); + await page.getByRole('switch').click(); // timestamp shouldn't be in the past anymore await expect(page.getByText('2021-12-30 01:11:00.000Z')).toBeHidden(); }); diff --git a/e2e/tests/functional/plugins/flexibleLayout/flexibleLayout.e2e.spec.js b/e2e/tests/functional/plugins/flexibleLayout/flexibleLayout.e2e.spec.js index ce16227ad0..f81b5298a0 100644 --- a/e2e/tests/functional/plugins/flexibleLayout/flexibleLayout.e2e.spec.js +++ b/e2e/tests/functional/plugins/flexibleLayout/flexibleLayout.e2e.spec.js @@ -21,7 +21,10 @@ *****************************************************************************/ const { test, expect } = require('../../../../pluginFixtures'); -const { createDomainObjectWithDefaults } = require('../../../../appActions'); +const { + createDomainObjectWithDefaults, + setIndependentTimeConductorBounds +} = require('../../../../appActions'); test.describe('Flexible Layout', () => { let sineWaveObject; @@ -187,16 +190,17 @@ test.describe('Flexible Layout', () => { await page.locator('text=Save and Finish Editing').click(); // flip on independent time conductor - await page.getByTitle('Enable independent Time Conductor').first().locator('label').click(); - await page.getByRole('textbox').nth(1).fill('2021-12-30 01:11:00.000Z'); - await page.getByRole('textbox').nth(0).fill('2021-12-30 01:01:00.000Z'); - await page.getByRole('textbox').nth(1).click(); + await setIndependentTimeConductorBounds( + page, + '2021-12-30 01:01:00.000Z', + '2021-12-30 01:11:00.000Z' + ); // check image date await expect(page.getByText('2021-12-30 01:11:00.000Z').first()).toBeVisible(); // flip it off - await page.getByTitle('Disable independent Time Conductor').first().locator('label').click(); + await page.getByRole('switch').click(); // timestamp shouldn't be in the past anymore await expect(page.getByText('2021-12-30 01:11:00.000Z')).toBeHidden(); }); diff --git a/e2e/tests/functional/plugins/imagery/exampleImagery.e2e.spec.js b/e2e/tests/functional/plugins/imagery/exampleImagery.e2e.spec.js index 5ef4a6a06b..d64688e044 100644 --- a/e2e/tests/functional/plugins/imagery/exampleImagery.e2e.spec.js +++ b/e2e/tests/functional/plugins/imagery/exampleImagery.e2e.spec.js @@ -27,7 +27,7 @@ but only assume that example imagery is present. /* globals process */ const { waitForAnimations } = require('../../../../baseFixtures'); const { test, expect } = require('../../../../pluginFixtures'); -const { createDomainObjectWithDefaults } = require('../../../../appActions'); +const { createDomainObjectWithDefaults, setRealTimeMode } = require('../../../../appActions'); const backgroundImageSelector = '.c-imagery__main-image__background-image'; const panHotkey = process.platform === 'linux' ? ['Shift', 'Alt'] : ['Alt']; const tagHotkey = ['Shift', 'Alt']; @@ -46,6 +46,7 @@ test.describe('Example Imagery Object', () => { // Verify that the created object is focused await expect(page.locator('.l-browse-bar__object-name')).toContainText(exampleImagery.name); await page.locator('.c-imagery__main-image__bg').hover({ trial: true }); + await page.locator(backgroundImageSelector).waitFor(); }); test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => { @@ -71,46 +72,60 @@ test.describe('Example Imagery Object', () => { }); test('Can use independent time conductor to change time', async ({ page }) => { + test.info().annotations.push({ + type: 'issue', + description: 'https://github.com/nasa/openmct/issues/6821' + }); // Test independent fixed time with global fixed time // flip on independent time conductor - await page.getByTitle('Enable independent Time Conductor').locator('label').click(); - await page.getByRole('textbox').nth(1).fill('2021-12-30 01:11:00.000Z'); - await page.getByRole('textbox').nth(0).fill('2021-12-30 01:01:00.000Z'); - await page.getByRole('textbox').nth(1).click(); + await page.getByRole('switch', { name: 'Enable Independent Time Conductor' }).click(); + await page.getByRole('button', { name: 'Independent Time Conductor Settings' }).click(); + await page.getByRole('textbox', { name: 'Start date' }).click(); + await page.getByRole('textbox', { name: 'Start date' }).fill(''); + await page.getByRole('textbox', { name: 'Start date' }).fill('2021-12-30'); + await page.getByRole('textbox', { name: 'Start time' }).click(); + await page.getByRole('textbox', { name: 'Start time' }).fill(''); + await page.getByRole('textbox', { name: 'Start time' }).fill('01:01:00'); + await page.getByRole('textbox', { name: 'End date' }).click(); + await page.getByRole('textbox', { name: 'End date' }).fill(''); + await page.getByRole('textbox', { name: 'End date' }).fill('2021-12-30'); + await page.getByRole('textbox', { name: 'End time' }).click(); + await page.getByRole('textbox', { name: 'End time' }).fill(''); + await page.getByRole('textbox', { name: 'End time' }).fill('01:11:00'); + await page.getByRole('button', { name: 'Submit time bounds' }).click(); // check image date await expect(page.getByText('2021-12-30 01:11:00.000Z').first()).toBeVisible(); // flip it off - await page.getByTitle('Disable independent Time Conductor').locator('label').click(); + await page.getByRole('switch', { name: 'Disable Independent Time Conductor' }).click(); // timestamp shouldn't be in the past anymore await expect(page.getByText('2021-12-30 01:11:00.000Z')).toBeHidden(); // Test independent fixed time with global realtime - await page.getByRole('button', { name: /Fixed Timespan/ }).click(); - await page.getByTestId('conductor-modeOption-realtime').click(); - await page.getByTitle('Enable independent Time Conductor').locator('label').click(); + await setRealTimeMode(page); + await page.getByRole('switch', { name: 'Enable Independent Time Conductor' }).click(); // check image date to be in the past await expect(page.getByText('2021-12-30 01:11:00.000Z').first()).toBeVisible(); // flip it off - await page.getByTitle('Disable independent Time Conductor').locator('label').click(); + await page.getByRole('switch', { name: 'Disable Independent Time Conductor' }).click(); // timestamp shouldn't be in the past anymore await expect(page.getByText('2021-12-30 01:11:00.000Z')).toBeHidden(); // Test independent realtime with global realtime - await page.getByTitle('Enable independent Time Conductor').locator('label').click(); + await page.getByRole('switch', { name: 'Enable Independent Time Conductor' }).click(); // check image date await expect(page.getByText('2021-12-30 01:11:00.000Z').first()).toBeVisible(); // change independent time to realtime - await page.getByRole('button', { name: /Fixed Timespan/ }).click(); - await page.getByRole('menuitem', { name: /Local Clock/ }).click(); + await page.getByRole('button', { name: 'Independent Time Conductor Settings' }).click(); + await page.getByRole('button', { name: 'Independent Time Conductor Mode Menu' }).click(); + await page.getByRole('menuitem', { name: /Real-Time/ }).click(); // timestamp shouldn't be in the past anymore await expect(page.getByText('2021-12-30 01:11:00.000Z')).toBeHidden(); // back to the past - await page - .getByRole('button', { name: /Local Clock/ }) - .first() - .click(); + await page.getByRole('button', { name: 'Independent Time Conductor Mode Menu' }).click(); + await page.getByRole('menuitem', { name: /Real-Time/ }).click(); + await page.getByRole('button', { name: 'Independent Time Conductor Mode Menu' }).click(); await page.getByRole('menuitem', { name: /Fixed Timespan/ }).click(); // check image date to be in the past await expect(page.getByText('2021-12-30 01:11:00.000Z').first()).toBeVisible(); @@ -247,7 +262,7 @@ test.describe('Example Imagery Object', () => { test('Uses low fetch priority', async ({ page }) => { const priority = await page.locator('.js-imageryView-image').getAttribute('fetchpriority'); - await expect(priority).toBe('low'); + expect(priority).toBe('low'); }); }); @@ -281,7 +296,7 @@ test.describe('Example Imagery in Display Layout', () => { await setRealTimeMode(page); // pause/play button - const pausePlayButton = await page.locator('.c-button.pause-play'); + const pausePlayButton = page.locator('.c-button.pause-play'); await expect.soft(pausePlayButton).not.toHaveClass(/is-paused/); @@ -304,7 +319,7 @@ test.describe('Example Imagery in Display Layout', () => { await setRealTimeMode(page); // pause/play button - const pausePlayButton = await page.locator('.c-button.pause-play'); + const pausePlayButton = page.locator('.c-button.pause-play'); await pausePlayButton.click(); await expect.soft(pausePlayButton).toHaveClass(/is-paused/); @@ -928,15 +943,3 @@ async function createImageryView(page) { page.waitForSelector('.c-message-banner__message') ]); } - -/** - * @param {import('@playwright/test').Page} page - */ -async function setRealTimeMode(page) { - await page.locator('.c-compact-tc').click(); - await page.waitForSelector('.c-tc-input-popup', { state: 'visible' }); - // Click mode dropdown - await page.getByRole('button', { name: ' Fixed Timespan ' }).click(); - // Click realtime - await page.getByTestId('conductor-modeOption-realtime').click(); -} diff --git a/e2e/tests/functional/plugins/notebook/notebookWithCouchDB.e2e.spec.js b/e2e/tests/functional/plugins/notebook/notebookWithCouchDB.e2e.spec.js index 377e6de07f..646e08d0e9 100644 --- a/e2e/tests/functional/plugins/notebook/notebookWithCouchDB.e2e.spec.js +++ b/e2e/tests/functional/plugins/notebook/notebookWithCouchDB.e2e.spec.js @@ -51,10 +51,9 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => { page.on('request', (request) => notebookElementsRequests.push(request)); //Clicking Add Page generates - let [notebookUrlRequest, allDocsRequest] = await Promise.all([ + let [notebookUrlRequest] = await Promise.all([ // Waits for the next request with the specified url page.waitForRequest(`**/openmct/${testNotebook.uuid}`), - page.waitForRequest('**/openmct/_all_docs?include_docs=true'), // Triggers the request page.click('[aria-label="Add Page"]') ]); @@ -64,15 +63,13 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => { // Assert that only two requests are made // Network Requests are: // 1) The actual POST to create the page - // 2) The shared worker event from 👆 request - expect(notebookElementsRequests.length).toBe(2); + expect(notebookElementsRequests.length).toBe(1); // Assert on request object expect(notebookUrlRequest.postDataJSON().metadata.name).toBe(testNotebook.name); expect(notebookUrlRequest.postDataJSON().model.persisted).toBeGreaterThanOrEqual( notebookUrlRequest.postDataJSON().model.modified ); - expect(allDocsRequest.postDataJSON().keys).toContain(testNotebook.uuid); // Add an entry // Network Requests are: diff --git a/e2e/tests/functional/plugins/notebook/restrictedNotebook.e2e.spec.js b/e2e/tests/functional/plugins/notebook/restrictedNotebook.e2e.spec.js index 584a1c0401..5f14593937 100644 --- a/e2e/tests/functional/plugins/notebook/restrictedNotebook.e2e.spec.js +++ b/e2e/tests/functional/plugins/notebook/restrictedNotebook.e2e.spec.js @@ -134,7 +134,7 @@ test.describe('Restricted Notebook with at least one entry and with the page loc // Click the context menu button for the new page await page.getByTitle('Open context menu').click(); // Delete the page - await page.getByRole('listitem', { name: 'Delete Page' }).click(); + await page.getByRole('menuitem', { name: 'Delete Page' }).click(); // Click OK button await page.getByRole('button', { name: 'Ok' }).click(); diff --git a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js index 821015e283..3c1237a71c 100644 --- a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js +++ b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js @@ -24,7 +24,7 @@ Testsuite for plot autoscale. */ -const { selectInspectorTab } = require('../../../../appActions'); +const { selectInspectorTab, setTimeConductorBounds } = require('../../../../appActions'); const { test, expect } = require('../../../../pluginFixtures'); test.use({ viewport: { @@ -107,7 +107,7 @@ test.describe('Autoscale', () => { await page.keyboard.up('Alt'); // Ensure the drag worked. - await testYTicks(page, ['0.00', '0.50', '1.00', '1.50', '2.00', '2.50', '3.00', '3.50']); + await testYTicks(page, ['-0.50', '0.00', '0.50', '1.00', '1.50', '2.00', '2.50', '3.00']); //Wait for canvas to stablize. await canvas.hover({ trial: true }); @@ -131,12 +131,7 @@ async function setTimeRange( // Set a specific time range for consistency, otherwise it will change // on every test to a range based on the current time. - const timeInputs = page.locator('input.c-input--datetime'); - await timeInputs.first().click(); - await timeInputs.first().fill(start); - - await timeInputs.nth(1).click(); - await timeInputs.nth(1).fill(end); + await setTimeConductorBounds(page, start, end); } /** diff --git a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin.png b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin.png index e9e82fd14e..abdb37b8dc 100644 Binary files a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin.png and b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin.png differ diff --git a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-linux.png b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-linux.png index dde9cbf044..1ff030d958 100644 Binary files a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-linux.png and b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-linux.png differ diff --git a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin.png b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin.png index 0c0e7c6e70..148089b4bd 100644 Binary files a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin.png and b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin.png differ diff --git a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-linux.png b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-linux.png index e97b591291..7b60adcdb2 100644 Binary files a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-linux.png and b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-linux.png differ diff --git a/e2e/tests/functional/plugins/plot/logPlot.e2e.spec.js b/e2e/tests/functional/plugins/plot/logPlot.e2e.spec.js index ab498741b3..e7c3a7b081 100644 --- a/e2e/tests/functional/plugins/plot/logPlot.e2e.spec.js +++ b/e2e/tests/functional/plugins/plot/logPlot.e2e.spec.js @@ -26,7 +26,7 @@ necessarily be used for reference when writing new tests in this area. */ const { test, expect } = require('../../../../pluginFixtures'); -const { selectInspectorTab } = require('../../../../appActions'); +const { selectInspectorTab, setTimeConductorBounds } = require('../../../../appActions'); test.describe('Log plot tests', () => { test('Log Plot ticks are functionally correct in regular and log mode and after refresh', async ({ @@ -87,12 +87,10 @@ async function makeOverlayPlot(page, myItemsFolderName) { // Set a specific time range for consistency, otherwise it will change // on every test to a range based on the current time. - const timeInputs = page.locator('input.c-input--datetime'); - await timeInputs.first().click(); - await timeInputs.first().fill('2022-03-29 22:00:00.000Z'); + const start = '2022-03-29 22:00:00.000Z'; + const end = '2022-03-29 22:00:30.000Z'; - await timeInputs.nth(1).click(); - await timeInputs.nth(1).fill('2022-03-29 22:00:30.000Z'); + await setTimeConductorBounds(page, start, end); // create overlay plot diff --git a/e2e/tests/functional/plugins/plot/tagging.e2e.spec.js b/e2e/tests/functional/plugins/plot/tagging.e2e.spec.js index bf49c1d407..aa3b6e5279 100644 --- a/e2e/tests/functional/plugins/plot/tagging.e2e.spec.js +++ b/e2e/tests/functional/plugins/plot/tagging.e2e.spec.js @@ -32,7 +32,7 @@ const { waitForPlotsToRender } = require('../../../../appActions'); -test.describe('Plot Tagging', () => { +test.describe.fixme('Plot Tagging', () => { /** * Given a canvas and a set of points, tags the points on the canvas. * @param {import('@playwright/test').Page} page @@ -167,6 +167,10 @@ test.describe('Plot Tagging', () => { }); test('Tags work with Overlay Plots', async ({ page }) => { + test.info().annotations.push({ + type: 'issue', + description: 'https://github.com/nasa/openmct/issues/6822' + }); //Test.slow decorator is currently broken. Needs to be fixed in https://github.com/nasa/openmct/issues/5374 test.slow(); diff --git a/e2e/tests/functional/plugins/telemetryTable/telemetryTable.e2e.spec.js b/e2e/tests/functional/plugins/telemetryTable/telemetryTable.e2e.spec.js index 089c99c421..295e63dacd 100644 --- a/e2e/tests/functional/plugins/telemetryTable/telemetryTable.e2e.spec.js +++ b/e2e/tests/functional/plugins/telemetryTable/telemetryTable.e2e.spec.js @@ -20,7 +20,10 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -const { createDomainObjectWithDefaults } = require('../../../../appActions'); +const { + createDomainObjectWithDefaults, + setTimeConductorBounds +} = require('../../../../appActions'); const { test, expect } = require('../../../../pluginFixtures'); test.describe('Telemetry Table', () => { @@ -51,18 +54,14 @@ test.describe('Telemetry Table', () => { await expect(tableWrapper).toHaveClass(/is-paused/); // Subtract 5 minutes from the current end bound datetime and set it - const endTimeInput = page.locator('input[type="text"].c-input--datetime').nth(1); - await endTimeInput.click(); - - let endDate = await endTimeInput.inputValue(); + // Bring up the time conductor popup + let endDate = await page.locator('[aria-label="End bounds"]').textContent(); endDate = new Date(endDate); endDate.setUTCMinutes(endDate.getUTCMinutes() - 5); endDate = endDate.toISOString().replace(/T/, ' '); - await endTimeInput.fill(''); - await endTimeInput.fill(endDate); - await page.keyboard.press('Enter'); + await setTimeConductorBounds(page, undefined, endDate); await expect(tableWrapper).not.toHaveClass(/is-paused/); diff --git a/e2e/tests/functional/plugins/timeConductor/timeConductor.e2e.spec.js b/e2e/tests/functional/plugins/timeConductor/timeConductor.e2e.spec.js index 89fa1346d1..5dba7ef998 100644 --- a/e2e/tests/functional/plugins/timeConductor/timeConductor.e2e.spec.js +++ b/e2e/tests/functional/plugins/timeConductor/timeConductor.e2e.spec.js @@ -25,7 +25,8 @@ const { setFixedTimeMode, setRealTimeMode, setStartOffset, - setEndOffset + setEndOffset, + setTimeConductorBounds } = require('../../../../appActions'); test.describe('Time conductor operations', () => { @@ -40,38 +41,36 @@ test.describe('Time conductor operations', () => { let endDate = 'xxxx-01-01 02:00:00.000Z'; endDate = year + endDate.substring(4); - const startTimeLocator = page.locator('input[type="text"]').first(); - const endTimeLocator = page.locator('input[type="text"]').nth(1); - - // Click start time - await startTimeLocator.click(); - - // Click end time - await endTimeLocator.click(); - - await endTimeLocator.fill(endDate.toString()); - await startTimeLocator.fill(startDate.toString()); + await setTimeConductorBounds(page, startDate, endDate); // invalid start date startDate = year + 1 + startDate.substring(4); - await startTimeLocator.fill(startDate.toString()); - await endTimeLocator.click(); + await setTimeConductorBounds(page, startDate); - const startDateValidityStatus = await startTimeLocator.evaluate((element) => + // Bring up the time conductor popup + const timeConductorMode = await page.locator('.c-compact-tc'); + await timeConductorMode.click(); + const startDateLocator = page.locator('input[type="text"]').first(); + const endDateLocator = page.locator('input[type="text"]').nth(2); + + await endDateLocator.click(); + + const startDateValidityStatus = await startDateLocator.evaluate((element) => element.checkValidity() ); expect(startDateValidityStatus).not.toBeTruthy(); // fix to valid start date startDate = year - 1 + startDate.substring(4); - await startTimeLocator.fill(startDate.toString()); + await setTimeConductorBounds(page, startDate); // invalid end date endDate = year - 2 + endDate.substring(4); - await endTimeLocator.fill(endDate.toString()); - await startTimeLocator.click(); + await setTimeConductorBounds(page, undefined, endDate); - const endDateValidityStatus = await endTimeLocator.evaluate((element) => + await startDateLocator.click(); + + const endDateValidityStatus = await endDateLocator.evaluate((element) => element.checkValidity() ); expect(endDateValidityStatus).not.toBeTruthy(); @@ -83,11 +82,11 @@ test.describe('Time conductor operations', () => { test.describe('Time conductor input fields real-time mode', () => { test('validate input fields in real-time mode', async ({ page }) => { const startOffset = { - secs: '23' + startSecs: '23' }; const endOffset = { - secs: '31' + endSecs: '31' }; // Go to baseURL @@ -100,15 +99,13 @@ test.describe('Time conductor input fields real-time mode', () => { await setStartOffset(page, startOffset); // Verify time was updated on time offset button - await expect(page.locator('data-testid=conductor-start-offset-button')).toContainText( - '00:30:23' - ); + await expect(page.locator('.c-compact-tc__setting-value.icon-minus')).toContainText('00:30:23'); // Set end time offset await setEndOffset(page, endOffset); // Verify time was updated on preceding time offset button - await expect(page.locator('data-testid=conductor-end-offset-button')).toContainText('00:00:31'); + await expect(page.locator('.c-compact-tc__setting-value.icon-plus')).toContainText('00:00:31'); }); /** @@ -119,12 +116,12 @@ test.describe('Time conductor input fields real-time mode', () => { page }) => { const startOffset = { - mins: '30', - secs: '23' + startMins: '30', + startSecs: '23' }; const endOffset = { - secs: '01' + endSecs: '01' }; // Convert offsets to milliseconds @@ -150,12 +147,10 @@ test.describe('Time conductor input fields real-time mode', () => { await setRealTimeMode(page); // Verify updated start time offset persists after mode switch - await expect(page.locator('data-testid=conductor-start-offset-button')).toContainText( - '00:30:23' - ); + await expect(page.locator('.c-compact-tc__setting-value.icon-minus')).toContainText('00:30:23'); // Verify updated end time offset persists after mode switch - await expect(page.locator('data-testid=conductor-end-offset-button')).toContainText('00:00:01'); + await expect(page.locator('.c-compact-tc__setting-value.icon-plus')).toContainText('00:00:01'); // Verify url parameters persist after mode switch await page.waitForNavigation({ waitUntil: 'networkidle' }); @@ -203,11 +198,11 @@ test.describe('Time Conductor History', () => { // with startBound at 2022-01-01 00:00:00.000Z // and endBound at 2022-01-01 00:00:00.200Z await page.goto( - './#/browse/mine?view=grid&tc.mode=fixed&tc.startBound=1640995200000&tc.endBound=1640995200200&tc.timeSystem=utc&hideInspector=true', - { waitUntil: 'networkidle' } + './#/browse/mine?view=grid&tc.mode=fixed&tc.startBound=1640995200000&tc.endBound=1640995200200&tc.timeSystem=utc&hideInspector=true' ); - await page.locator("[aria-label='Time Conductor History']").hover({ trial: true }); - await page.locator("[aria-label='Time Conductor History']").click(); + await page.getByRole('button', { name: 'Time Conductor Settings' }).click(); + await page.getByRole('button', { name: 'Time Conductor History' }).hover({ trial: true }); + await page.getByRole('button', { name: 'Time Conductor History' }).click(); // Validate history item format const historyItem = page.locator('text="2022-01-01 00:00:00 + 200ms"'); diff --git a/e2e/tests/functional/recentObjects.e2e.spec.js b/e2e/tests/functional/recentObjects.e2e.spec.js index a97c32d88c..da7a2dcb3f 100644 --- a/e2e/tests/functional/recentObjects.e2e.spec.js +++ b/e2e/tests/functional/recentObjects.e2e.spec.js @@ -59,53 +59,60 @@ test.describe('Recent Objects', () => { await page.mouse.move(0, 100); await page.mouse.up(); }); - test('Navigated objects show up in recents, object renames and deletions are reflected', async ({ - page - }) => { - // Verify that both created objects appear in the list and are in the correct order - await assertInitialRecentObjectsListState(); - - // Navigate to the folder by clicking on the main object name in the recent objects list item - await page.getByRole('listitem', { name: folderA.name }).getByText(folderA.name).click(); - await page.waitForURL(`**/${folderA.uuid}?*`); - expect(recentObjectsList.getByRole('listitem').nth(0).getByText(folderA.name)).toBeTruthy(); - - // Rename - folderA.name = `${folderA.name}-NEW!`; - await page.locator('.l-browse-bar__object-name').fill(''); - await page.locator('.l-browse-bar__object-name').fill(folderA.name); - await page.keyboard.press('Enter'); - - // Verify rename has been applied in recent objects list item and objects paths - expect( - await page - .getByRole('navigation', { - name: clock.name - }) - .locator('a') - .filter({ - hasText: folderA.name - }) - .count() - ).toBeGreaterThan(0); - expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeTruthy(); - - // Delete - await page.click('button[title="Show selected item in tree"]'); - // Delete the folder via the left tree pane treeitem context menu - await page - .getByRole('treeitem', { name: new RegExp(folderA.name) }) - .locator('a') - .click({ - button: 'right' + test.fixme( + 'Navigated objects show up in recents, object renames and deletions are reflected', + async ({ page }) => { + test.info().annotations.push({ + type: 'issue', + description: 'https://github.com/nasa/openmct/issues/6818' }); - await page.getByRole('menuitem', { name: /Remove/ }).click(); - await page.getByRole('button', { name: 'OK' }).click(); - // Verify that the folder and clock are no longer in the recent objects list - await expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeHidden(); - await expect(recentObjectsList.getByRole('listitem', { name: clock.name })).toBeHidden(); - }); + // Verify that both created objects appear in the list and are in the correct order + await assertInitialRecentObjectsListState(); + + // Navigate to the folder by clicking on the main object name in the recent objects list item + await page.getByRole('listitem', { name: folderA.name }).getByText(folderA.name).click(); + await page.waitForURL(`**/${folderA.uuid}?*`); + expect(recentObjectsList.getByRole('listitem').nth(0).getByText(folderA.name)).toBeTruthy(); + + // Rename + folderA.name = `${folderA.name}-NEW!`; + await page.locator('.l-browse-bar__object-name').fill(''); + await page.locator('.l-browse-bar__object-name').fill(folderA.name); + await page.keyboard.press('Enter'); + + // Verify rename has been applied in recent objects list item and objects paths + expect( + await page + .getByRole('navigation', { + name: clock.name + }) + .locator('a') + .filter({ + hasText: folderA.name + }) + .count() + ).toBeGreaterThan(0); + expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeTruthy(); + + // Delete + await page.click('button[title="Show selected item in tree"]'); + // Delete the folder via the left tree pane treeitem context menu + await page + .getByRole('treeitem', { name: new RegExp(folderA.name) }) + .locator('a') + .click({ + button: 'right' + }); + await page.getByRole('menuitem', { name: /Remove/ }).click(); + await page.getByRole('button', { name: 'OK' }).click(); + + // Verify that the folder and clock are no longer in the recent objects list + await expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeHidden(); + await expect(recentObjectsList.getByRole('listitem', { name: clock.name })).toBeHidden(); + } + ); + test('Clicking on an object in the path of a recent object navigates to the object', async ({ page, openmctConfig diff --git a/e2e/tests/functional/search.e2e.spec.js b/e2e/tests/functional/search.e2e.spec.js index 846f8afda9..3f93814923 100644 --- a/e2e/tests/functional/search.e2e.spec.js +++ b/e2e/tests/functional/search.e2e.spec.js @@ -77,11 +77,11 @@ test.describe('Grand Search', () => { // Click [aria-label="OpenMCT Search"] a >> nth=0 await page.locator('[aria-label="Search Result"] >> nth=0').click(); - await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeHidden(); + await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeInViewport(); // Fill [aria-label="OpenMCT Search"] input[type="search"] await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('foo'); - await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeHidden(); + await expect(page.locator('[aria-label="Search Result"] >> nth=0')).not.toBeInViewport(); // Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1 await page diff --git a/package.json b/package.json index a9dcf6ff17..41131d921e 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,7 @@ "test:e2e:updatesnapshots": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot --update-snapshots", "test:e2e:visual": "percy exec --config ./e2e/.percy.yml -- npx playwright test --config=e2e/playwright-visual.config.js --grep-invert @unstable", "test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js --grep-invert @couchdb", + "test:e2e:watch": "npx playwright test --ui --config=e2e/playwright-ci.config.js", "test:perf": "npx playwright test --config=e2e/playwright-performance.config.js", "update-about-dialog-copyright": "perl -pi -e 's/20\\d\\d\\-202\\d/2014\\-2023/gm' ./src/ui/layout/AboutDialog.vue", "update-copyright-date": "npm run update-about-dialog-copyright && grep -lr --null --include=*.{js,scss,vue,ts,sh,html,md,frag} 'Copyright (c) 20' . | xargs -r0 perl -pi -e 's/Copyright\\s\\(c\\)\\s20\\d\\d\\-20\\d\\d/Copyright \\(c\\)\\ 2014\\-2023/gm'", diff --git a/src/api/menu/MenuAPISpec.js b/src/api/menu/MenuAPISpec.js index 68f6f3b04a..5ff8cb4a01 100644 --- a/src/api/menu/MenuAPISpec.js +++ b/src/api/menu/MenuAPISpec.js @@ -23,6 +23,7 @@ import MenuAPI from './MenuAPI'; import Menu from './menu'; import { createOpenMct, createMouseEvent, resetApplicationState } from '../../utils/testing'; +import Vue from 'vue'; describe('The Menu API', () => { let openmct; @@ -137,14 +138,13 @@ describe('The Menu API', () => { it('invokes the destroy method when menu is dismissed', (done) => { menuOptions.onDestroy = done; - menuAPI.showMenu(x, y, actionsArray, menuOptions); + spyOn(menuAPI, '_clearMenuComponent').and.callThrough(); - const vueComponent = menuAPI.menuComponent.component; - spyOn(vueComponent, '$destroy'); + menuAPI.showMenu(x, y, actionsArray, menuOptions); document.body.click(); - expect(vueComponent.$destroy).toHaveBeenCalled(); + expect(menuAPI._clearMenuComponent).toHaveBeenCalled(); }); it('invokes the onDestroy callback if passed in', (done) => { @@ -185,7 +185,7 @@ describe('The Menu API', () => { superMenuItem.dispatchEvent(mouseOverEvent); const itemDescription = document.querySelector('.l-item-description__description'); - menuAPI.menuComponent.component.$nextTick(() => { + Vue.nextTick(() => { expect(menuElement).not.toBeNull(); expect(itemDescription.innerText).toEqual(actionsArray[0].description); diff --git a/src/api/menu/components/Menu.vue b/src/api/menu/components/Menu.vue index 6c86bd56b2..68b1136b3d 100644 --- a/src/api/menu/components/Menu.vue +++ b/src/api/menu/components/Menu.vue @@ -30,7 +30,6 @@ role="menuitem" :class="[action.cssClass, action.isDisabled ? 'disabled' : '']" :title="action.description" - :data-testid="action.testId || null" @click="action.onItemClicked" > {{ action.name }} @@ -53,7 +52,6 @@ role="menuitem" :class="[action.cssClass, action.isDisabled ? 'disabled' : '']" :title="action.description" - :data-testid="action.testId || null" @click="action.onItemClicked" > {{ action.name }} diff --git a/src/api/menu/components/SuperMenu.vue b/src/api/menu/components/SuperMenu.vue index 6d3b2451a0..808750d8ca 100644 --- a/src/api/menu/components/SuperMenu.vue +++ b/src/api/menu/components/SuperMenu.vue @@ -34,7 +34,6 @@ role="menuitem" :class="[action.cssClass, action.isDisabled ? 'disabled' : '']" :title="action.description" - :data-testid="action.testId || null" @click="action.onItemClicked" @mouseover="toggleItemDescription(action)" @mouseleave="toggleItemDescription()" @@ -59,7 +58,6 @@ role="menuitem" :class="action.cssClass" :title="action.description" - :data-testid="action.testId || null" @click="action.onItemClicked" @mouseover="toggleItemDescription(action)" @mouseleave="toggleItemDescription()" diff --git a/src/api/menu/menu.js b/src/api/menu/menu.js index 18f259cff3..1a829fc4d4 100644 --- a/src/api/menu/menu.js +++ b/src/api/menu/menu.js @@ -52,12 +52,12 @@ class Menu extends EventEmitter { } dismiss() { - this.emit('destroy'); if (this.destroy) { this.destroy(); this.destroy = null; } document.removeEventListener('click', this.dismiss); + this.emit('destroy'); } showMenu() { diff --git a/src/api/objects/InMemorySearchProvider.js b/src/api/objects/InMemorySearchProvider.js index 364e4c7605..15e66a2e79 100644 --- a/src/api/objects/InMemorySearchProvider.js +++ b/src/api/objects/InMemorySearchProvider.js @@ -374,7 +374,7 @@ class InMemorySearchProvider { delete provider.pendingIndex[keyString]; try { - if (domainObject) { + if (domainObject && domainObject.identifier) { await provider.index(domainObject); } } catch (error) { diff --git a/src/api/objects/ObjectAPISpec.js b/src/api/objects/ObjectAPISpec.js index 1d910ae6b5..b7c56121ae 100644 --- a/src/api/objects/ObjectAPISpec.js +++ b/src/api/objects/ObjectAPISpec.js @@ -23,6 +23,9 @@ describe('The Object API', () => { return USERNAME; } }); + }, + getPossibleRoles() { + return Promise.resolve([]); } }; openmct = createOpenMct(); diff --git a/src/api/overlays/components/OverlayComponent.vue b/src/api/overlays/components/OverlayComponent.vue index e61ae7b0e0..2cae0807d4 100644 --- a/src/api/overlays/components/OverlayComponent.vue +++ b/src/api/overlays/components/OverlayComponent.vue @@ -27,7 +27,7 @@ v-if="dismissable" aria-label="Close" class="c-click-icon c-overlay__close-button icon-x" - @click="destroy" + @click.stop="destroy" >