mirror of
https://github.com/nasa/openmct.git
synced 2024-12-24 15:26:39 +00:00
Set location hash if query parameters or path have changed (#7306)
* refactor to es6 class * change URL on path or params change * add test for url * put into edit mode for test * es6 module export * a11y: add `status` role to clock component * a11y: add label to overlay * a11y: update roles for search results * a11y: add `dialog` role and label for PreviewContainer * refactor(e2e): get rid of a bunch of `page.locator()`s * refactor(e2e): spruce up locators * test: fix unit tests * fix tests with new aria labels * fix tests with new aria labels * fix tests with new aria labels --------- Co-authored-by: Jesse Mazzella <jesse.d.mazzella@nasa.gov>
This commit is contained in:
parent
dfba4e23c5
commit
64d4ddd80e
@ -91,27 +91,30 @@ test.describe('Notification Overlay', () => {
|
|||||||
// Create a new Display Layout object
|
// Create a new Display Layout object
|
||||||
await createDomainObjectWithDefaults(page, { type: 'Display Layout' });
|
await createDomainObjectWithDefaults(page, { type: 'Display Layout' });
|
||||||
|
|
||||||
|
// Dismiss notification banner
|
||||||
|
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||||
|
|
||||||
// Click on the button "Review 1 Notification"
|
// Click on the button "Review 1 Notification"
|
||||||
await page.click('button[aria-label="Review 1 Notification"]');
|
await page.getByRole('button', { name: 'Review 1 Notification' }).click();
|
||||||
|
|
||||||
// Verify that Notification List is open
|
// Verify that Notification List is open
|
||||||
expect(await page.locator('div[role="dialog"]').isVisible()).toBe(true);
|
await expect(page.getByRole('dialog', { name: 'Overlay' })).toBeVisible();
|
||||||
|
|
||||||
// Wait until there is no Notification Banner
|
// Wait until there is no Notification Banner
|
||||||
await page.waitForSelector('div[role="alert"]', { state: 'detached' });
|
await expect(page.getByRole('alert')).not.toBeAttached();
|
||||||
|
|
||||||
// Click on the "Close" button of the Notification List
|
// Click on the "Close" button of the Notification List
|
||||||
await page.click('button[aria-label="Close"]');
|
await page.getByRole('button', { name: 'Close' }).click();
|
||||||
|
|
||||||
// On the Display Layout object, click on the "Edit" button
|
// On the Display Layout object, click on the "Edit" button
|
||||||
await page.click('button[title="Edit"]');
|
await page.getByRole('button', { name: 'Edit' }).click();
|
||||||
|
|
||||||
// Click on the "Save" button
|
// Click on the "Save" button
|
||||||
await page.click('button[title="Save"]');
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
// Verify that Notification List is NOT open
|
// Verify that Notification List is NOT open
|
||||||
expect(await page.locator('div[role="dialog"]').isVisible()).toBe(false);
|
await expect(page.getByRole('dialog', { name: 'Overlay' })).toBeHidden();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -290,7 +290,7 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
|
|||||||
await page.getByTitle('Add Container').click();
|
await page.getByTitle('Add Container').click();
|
||||||
expect(await containerHandles.count()).toEqual(3);
|
expect(await containerHandles.count()).toEqual(3);
|
||||||
await page.getByTitle('Remove Container').click();
|
await page.getByTitle('Remove Container').click();
|
||||||
await expect(page.getByRole('dialog')).toHaveText(
|
await expect(page.getByRole('dialog', { name: 'Overlay' })).toHaveText(
|
||||||
'This action will permanently delete this container from this Flexible Layout. Do you want to continue?'
|
'This action will permanently delete this container from this Flexible Layout. Do you want to continue?'
|
||||||
);
|
);
|
||||||
await page.getByRole('button', { name: 'OK' }).click();
|
await page.getByRole('button', { name: 'OK' }).click();
|
||||||
@ -300,7 +300,7 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
|
|||||||
expect(await page.getByRole('group', { name: 'Frame' }).count()).toEqual(2);
|
expect(await page.getByRole('group', { name: 'Frame' }).count()).toEqual(2);
|
||||||
await page.getByRole('group', { name: 'Child Layout 1' }).click();
|
await page.getByRole('group', { name: 'Child Layout 1' }).click();
|
||||||
await page.getByTitle('Remove Frame').click();
|
await page.getByTitle('Remove Frame').click();
|
||||||
await expect(page.getByRole('dialog')).toHaveText(
|
await expect(page.getByRole('dialog', { name: 'Overlay' })).toHaveText(
|
||||||
'This action will remove this frame from this Flexible Layout. Do you want to continue?'
|
'This action will remove this frame from this Flexible Layout. Do you want to continue?'
|
||||||
);
|
);
|
||||||
await page.getByRole('button', { name: 'OK' }).click();
|
await page.getByRole('button', { name: 'OK' }).click();
|
||||||
|
@ -185,16 +185,20 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
|
|||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
||||||
//Partial match for "Science" should only return Science
|
//Partial match for "Science" should only return Science
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Sc');
|
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Sc');
|
||||||
await expect(page.locator('[aria-label="Search Result"]').first()).toContainText('Science');
|
await expect(page.locator('[aria-label="Annotation Search Result"]').first()).toContainText(
|
||||||
await expect(page.locator('[aria-label="Search Result"]').first()).not.toContainText('Driving');
|
'Science'
|
||||||
await expect(page.locator('[aria-label="Search Result"]').first()).not.toContainText(
|
);
|
||||||
|
await expect(page.locator('[aria-label="Annotation Search Result"]').first()).not.toContainText(
|
||||||
|
'Driving'
|
||||||
|
);
|
||||||
|
await expect(page.locator('[aria-label="Annotation Search Result"]').first()).not.toContainText(
|
||||||
'Drilling'
|
'Drilling'
|
||||||
);
|
);
|
||||||
|
|
||||||
//Searching for a tag which does not exist should return an empty result
|
//Searching for a tag which does not exist should return an empty result
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Xq');
|
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Xq');
|
||||||
await expect(page.locator('text=No results found')).toBeVisible();
|
await expect(page.getByText('No results found')).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -88,43 +88,53 @@ test.describe('Tagging in Notebooks @addInit', () => {
|
|||||||
|
|
||||||
await page.locator('[placeholder="Type to select tag"]').click();
|
await page.locator('[placeholder="Type to select tag"]').click();
|
||||||
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
await page.getByRole('search').getByLabel('Search Input').click();
|
||||||
|
|
||||||
await expect(page.locator('button:has-text("Add Tag")')).toBeVisible();
|
await expect(page.locator('button:has-text("Add Tag")')).toBeVisible();
|
||||||
|
|
||||||
// Test canceling adding a tag after we just click "Add Tag"
|
// Test canceling adding a tag after we just click "Add Tag"
|
||||||
await page.locator('button:has-text("Add Tag")').click();
|
await page.locator('button:has-text("Add Tag")').click();
|
||||||
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
await page.getByRole('search').getByLabel('Search Input').click();
|
||||||
|
|
||||||
await expect(page.locator('button:has-text("Add Tag")')).toBeVisible();
|
await expect(page.locator('button:has-text("Add Tag")')).toBeVisible();
|
||||||
});
|
});
|
||||||
test('Can search for tags and preview works properly', async ({ page }) => {
|
test('Can search for tags and preview works properly', async ({ page }) => {
|
||||||
await createNotebookEntryAndTags(page);
|
await createNotebookEntryAndTags(page);
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
await page.getByRole('search').getByLabel('Search Input').click();
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
|
await page.getByRole('search').getByLabel('Search Input').fill('sc');
|
||||||
await expect(page.locator('[aria-label="Search Result"]')).toContainText('Science');
|
await expect(page.getByRole('listitem', { name: 'Annotation Search Result' })).toContainText(
|
||||||
await expect(page.locator('[aria-label="Search Result"]')).not.toContainText('Driving');
|
'Science'
|
||||||
|
);
|
||||||
|
await expect(
|
||||||
|
page.getByRole('listitem', { name: 'Annotation Search Result' })
|
||||||
|
).not.toContainText('Driving');
|
||||||
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
await page.getByRole('search').getByLabel('Search Input').click();
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Sc');
|
await page.getByRole('search').getByLabel('Search Input').fill('Sc');
|
||||||
await expect(page.locator('[aria-label="Search Result"]')).toContainText('Science');
|
await expect(page.getByRole('listitem', { name: 'Annotation Search Result' })).toContainText(
|
||||||
await expect(page.locator('[aria-label="Search Result"]')).not.toContainText('Driving');
|
'Science'
|
||||||
|
);
|
||||||
|
await expect(
|
||||||
|
page.getByRole('listitem', { name: 'Annotation Search Result' })
|
||||||
|
).not.toContainText('Driving');
|
||||||
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
await page.getByRole('search').getByLabel('Search Input').click();
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Xq');
|
await page.getByRole('search').getByLabel('Search Input').fill('Xq');
|
||||||
await expect(page.locator('text=No results found')).toBeVisible();
|
await expect(page.getByText('No results found')).toBeVisible();
|
||||||
|
|
||||||
await createDomainObjectWithDefaults(page, {
|
await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Display Layout'
|
type: 'Display Layout'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Go back into edit mode for the display layout
|
// Go back into edit mode for the display layout
|
||||||
await page.locator('button[title="Edit"]').click();
|
await page.getByRole('button', { name: 'Edit' }).click();
|
||||||
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
await page.getByRole('search').getByLabel('Search Input').click();
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Sc');
|
await page.getByRole('search').getByLabel('Search Input').fill('Sc');
|
||||||
await expect(page.locator('[aria-label="Search Result"]')).toContainText('Science');
|
await expect(page.getByRole('listitem', { name: 'Annotation Search Result' })).toContainText(
|
||||||
|
'Science'
|
||||||
|
);
|
||||||
await page.getByText('Entry 0').click();
|
await page.getByText('Entry 0').click();
|
||||||
await expect(page.locator('.js-preview-window')).toBeVisible();
|
await expect(page.locator('.js-preview-window')).toBeVisible();
|
||||||
});
|
});
|
||||||
@ -138,8 +148,10 @@ test.describe('Tagging in Notebooks @addInit', () => {
|
|||||||
await expect(page.locator('[aria-label="Tags Inspector"]')).toContainText('Science');
|
await expect(page.locator('[aria-label="Tags Inspector"]')).toContainText('Science');
|
||||||
await expect(page.locator('[aria-label="Tags Inspector"]')).not.toContainText('Driving');
|
await expect(page.locator('[aria-label="Tags Inspector"]')).not.toContainText('Driving');
|
||||||
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
|
await page.getByRole('search').getByLabel('Search Input').fill('sc');
|
||||||
await expect(page.locator('[aria-label="Search Result"]')).not.toContainText('Driving');
|
await expect(
|
||||||
|
page.getByRole('listitem', { name: 'Annotation Search Result' })
|
||||||
|
).not.toContainText('Driving');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can delete entries without tags', async ({ page }) => {
|
test('Can delete entries without tags', async ({ page }) => {
|
||||||
@ -172,12 +184,12 @@ test.describe('Tagging in Notebooks @addInit', () => {
|
|||||||
await page.locator('button:has-text("OK")').click();
|
await page.locator('button:has-text("OK")').click();
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Unnamed');
|
await page.getByRole('search').getByLabel('Search Input').fill('Unnamed');
|
||||||
await expect(page.locator('text=No results found')).toBeVisible();
|
await expect(page.getByText('No results found')).toBeVisible();
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sci');
|
await page.getByRole('search').getByLabel('Search Input').fill('sci');
|
||||||
await expect(page.locator('text=No results found')).toBeVisible();
|
await expect(page.getByText('No results found')).toBeVisible();
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('dri');
|
await page.getByRole('search').getByLabel('Search Input').fill('dri');
|
||||||
await expect(page.locator('text=No results found')).toBeVisible();
|
await expect(page.getByText('No results found')).toBeVisible();
|
||||||
});
|
});
|
||||||
test('Tags persist across reload', async ({ page }) => {
|
test('Tags persist across reload', async ({ page }) => {
|
||||||
//Go to baseURL
|
//Go to baseURL
|
||||||
|
@ -29,12 +29,15 @@ import { createDomainObjectWithDefaults } from '../../appActions.js';
|
|||||||
import { expect, test } from '../../pluginFixtures.js';
|
import { expect, test } from '../../pluginFixtures.js';
|
||||||
|
|
||||||
test.describe('Grand Search', () => {
|
test.describe('Grand Search', () => {
|
||||||
const searchResultSelector = '.c-gsearch-result__title';
|
let grandSearchInput;
|
||||||
const searchResultDropDownSelector = '.c-gsearch__results';
|
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
grandSearchInput = page
|
||||||
|
.getByLabel('OpenMCT Search')
|
||||||
|
.getByRole('searchbox', { name: 'Search Input' });
|
||||||
|
|
||||||
// Go to baseURL
|
// Go to baseURL
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can search for objects, and subsequent search dropdown behaves properly', async ({
|
test('Can search for objects, and subsequent search dropdown behaves properly', async ({
|
||||||
@ -45,103 +48,124 @@ test.describe('Grand Search', () => {
|
|||||||
|
|
||||||
const createdObjects = await createObjectsForSearch(page);
|
const createdObjects = await createObjectsForSearch(page);
|
||||||
|
|
||||||
// Click [aria-label="OpenMCT Search"] input[type="search"]
|
// Go back into edit mode for the display layout
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
await page.getByRole('button', { name: 'Edit' }).click();
|
||||||
// Fill [aria-label="OpenMCT Search"] input[type="search"]
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Cl');
|
await grandSearchInput.click();
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(
|
await grandSearchInput.fill('Cl');
|
||||||
|
|
||||||
|
await expect(page.getByLabel('Object Search Result').first()).toContainText(
|
||||||
`Clock A ${myItemsFolderName} Red Folder Blue Folder`
|
`Clock A ${myItemsFolderName} Red Folder Blue Folder`
|
||||||
);
|
);
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=1')).toContainText(
|
await expect(page.getByLabel('Object Search Result').nth(1)).toContainText(
|
||||||
`Clock B ${myItemsFolderName} Red Folder Blue Folder`
|
`Clock B ${myItemsFolderName} Red Folder Blue Folder`
|
||||||
);
|
);
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=2')).toContainText(
|
await expect(page.getByLabel('Object Search Result').nth(2)).toContainText(
|
||||||
`Clock C ${myItemsFolderName} Red Folder Blue Folder`
|
`Clock C ${myItemsFolderName} Red Folder Blue Folder`
|
||||||
);
|
);
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=3')).toContainText(
|
await expect(page.getByLabel('Object Search Result').nth(3)).toContainText(
|
||||||
`Clock D ${myItemsFolderName} Red Folder Blue Folder`
|
`Clock D ${myItemsFolderName} Red Folder Blue Folder`
|
||||||
);
|
);
|
||||||
// Click the Elements pool to dismiss the search menu
|
// Click the Elements pool to dismiss the search menu
|
||||||
await page.getByRole('tab', { name: 'Elements' }).click();
|
await page.getByRole('tab', { name: 'Elements' }).click();
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeHidden();
|
await expect(page.getByLabel('Object Search Result').first()).toBeHidden();
|
||||||
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
|
await grandSearchInput.click();
|
||||||
await page.locator('[aria-label="Clock A clock result"] >> text=Clock A').click();
|
await page.getByLabel('OpenMCT Search').getByText('Clock A').click();
|
||||||
await expect(page.locator('.js-preview-window')).toBeVisible();
|
await expect(page.getByRole('dialog', { name: 'Preview Container' })).toBeVisible();
|
||||||
|
|
||||||
// Click [aria-label="Close"]
|
// Close the Preview window
|
||||||
await page.locator('[aria-label="Close"]').click();
|
await page.getByRole('button', { name: 'Close' }).click();
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeVisible();
|
await expect(page.getByLabel('Object Search Result').first()).toBeVisible();
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(
|
await expect(page.getByLabel('Object Search Result').first()).toContainText(
|
||||||
`Clock A ${myItemsFolderName} Red Folder Blue Folder`
|
`Clock A ${myItemsFolderName} Red Folder Blue Folder`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Click [aria-label="OpenMCT Search"] a >> nth=0
|
await page.getByLabel('Object Search Result').first().click();
|
||||||
await page.locator('[aria-label="Search Result"] >> nth=0').click();
|
await expect(page.getByLabel('Object Search Result').first()).toBeHidden();
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeHidden();
|
|
||||||
|
|
||||||
// Fill [aria-label="OpenMCT Search"] input[type="search"]
|
await grandSearchInput.fill('foo');
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('foo');
|
await expect(page.getByLabel('Object Search Result').first()).toBeHidden();
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeHidden();
|
|
||||||
|
|
||||||
// Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1
|
// Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1
|
||||||
await page
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button')
|
|
||||||
.nth(1)
|
|
||||||
.click();
|
|
||||||
|
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
// Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
|
// Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
|
||||||
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
|
await grandSearchInput.click();
|
||||||
// Fill [aria-label="OpenMCT Search"] [aria-label="Search Input"]
|
// Fill [aria-label="OpenMCT Search"] [aria-label="Search Input"]
|
||||||
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').fill('Cl');
|
await grandSearchInput.fill('Cl');
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.waitForNavigation(),
|
page.waitForNavigation(),
|
||||||
page.locator('[aria-label="Clock A clock result"] >> text=Clock A').click()
|
page.getByLabel('OpenMCT Search').getByText('Clock A').click()
|
||||||
]);
|
]);
|
||||||
await expect(page.locator('.is-object-type-clock')).toBeVisible();
|
await expect(page.getByRole('status', { name: 'Clock' })).toBeVisible();
|
||||||
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').fill('Disp');
|
await grandSearchInput.fill('Disp');
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(
|
await expect(page.getByLabel('Object Search Result').first()).toContainText(
|
||||||
createdObjects.displayLayout.name
|
createdObjects.displayLayout.name
|
||||||
);
|
);
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).not.toContainText('Folder');
|
await expect(page.getByLabel('Object Search Result').first()).not.toContainText('Folder');
|
||||||
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Clock C');
|
await grandSearchInput.fill('Clock C');
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(
|
await expect(page.getByLabel('Object Search Result').first()).toContainText(
|
||||||
`Clock C ${myItemsFolderName} Red Folder Blue Folder`
|
`Clock C ${myItemsFolderName} Red Folder Blue Folder`
|
||||||
);
|
);
|
||||||
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Cloc');
|
await grandSearchInput.fill('Cloc');
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(
|
await expect(page.getByLabel('Object Search Result').first()).toContainText(
|
||||||
`Clock A ${myItemsFolderName} Red Folder Blue Folder`
|
`Clock A ${myItemsFolderName} Red Folder Blue Folder`
|
||||||
);
|
);
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=1')).toContainText(
|
await expect(page.getByLabel('Object Search Result').nth(1)).toContainText(
|
||||||
`Clock B ${myItemsFolderName} Red Folder Blue Folder`
|
`Clock B ${myItemsFolderName} Red Folder Blue Folder`
|
||||||
);
|
);
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=2')).toContainText(
|
await expect(page.getByLabel('Object Search Result').nth(2)).toContainText(
|
||||||
`Clock C ${myItemsFolderName} Red Folder Blue Folder`
|
`Clock C ${myItemsFolderName} Red Folder Blue Folder`
|
||||||
);
|
);
|
||||||
await expect(page.locator('[aria-label="Search Result"] >> nth=3')).toContainText(
|
await expect(page.getByLabel('Object Search Result').nth(3)).toContainText(
|
||||||
`Clock D ${myItemsFolderName} Red Folder Blue Folder`
|
`Clock D ${myItemsFolderName} Red Folder Blue Folder`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await grandSearchInput.click();
|
||||||
|
await grandSearchInput.fill('Sine');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Clicking on a search result changes the URL even if the same type is already selected', async ({
|
||||||
|
page
|
||||||
|
}) => {
|
||||||
|
test.info().annotations.push({
|
||||||
|
type: 'issue',
|
||||||
|
description: 'https://github.com/nasa/openmct/issues/7303'
|
||||||
|
});
|
||||||
|
|
||||||
|
const { sineWaveGeneratorAlpha, sineWaveGeneratorBeta } = await createObjectsForSearch(page);
|
||||||
|
await grandSearchInput.click();
|
||||||
|
await grandSearchInput.fill('Sine');
|
||||||
|
await waitForSearchCompletion(page);
|
||||||
|
await page.getByLabel('OpenMCT Search').getByText('Sine Wave Generator Alpha').click();
|
||||||
|
const alphaPattern = new RegExp(sineWaveGeneratorAlpha.url.substring(1));
|
||||||
|
await expect(page).toHaveURL(alphaPattern);
|
||||||
|
await grandSearchInput.click();
|
||||||
|
await page.getByLabel('OpenMCT Search').getByText('Sine Wave Generator Beta').click();
|
||||||
|
const betaPattern = new RegExp(sineWaveGeneratorBeta.url.substring(1));
|
||||||
|
await expect(page).toHaveURL(betaPattern);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Validate empty search result', async ({ page }) => {
|
test('Validate empty search result', async ({ page }) => {
|
||||||
// Invalid search for objects
|
// Invalid search for objects
|
||||||
await page.type('input[type=search]', 'not found');
|
await grandSearchInput.fill('not found');
|
||||||
|
|
||||||
// Wait for search to complete
|
// Wait for search to complete
|
||||||
await waitForSearchCompletion(page);
|
await waitForSearchCompletion(page);
|
||||||
|
|
||||||
// Get the search results
|
// Get the search results
|
||||||
const searchResults = page.locator(searchResultSelector);
|
const searchResults = page.getByRole('listitem', { name: 'Object Search Result' });
|
||||||
|
|
||||||
// Verify that no results are found
|
// Verify that no results are found
|
||||||
expect(await searchResults.count()).toBe(0);
|
expect(await searchResults.count()).toBe(0);
|
||||||
|
|
||||||
// Verify proper message appears
|
// Verify proper message appears
|
||||||
await expect(page.locator('text=No results found')).toBeVisible();
|
await expect(page.getByText('No results found')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Validate single object in search result @couchdb', async ({ page }) => {
|
test('Validate single object in search result @couchdb', async ({ page }) => {
|
||||||
@ -153,18 +177,18 @@ test.describe('Grand Search', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Full search for object
|
// Full search for object
|
||||||
await page.type('input[type=search]', folderName);
|
await grandSearchInput.fill(folderName);
|
||||||
|
|
||||||
// Wait for search to complete
|
// Wait for search to complete
|
||||||
await waitForSearchCompletion(page);
|
await waitForSearchCompletion(page);
|
||||||
|
|
||||||
// Get the search results
|
// Get the search results
|
||||||
const searchResults = page.locator(searchResultSelector);
|
const searchResults = page.getByLabel('Object Search Result');
|
||||||
|
|
||||||
// Verify that one result is found
|
// Verify that one result is found
|
||||||
await expect(searchResults).toBeVisible();
|
await expect(searchResults).toBeVisible();
|
||||||
expect(await searchResults.count()).toBe(1);
|
expect(await searchResults.count()).toBe(1);
|
||||||
await expect(searchResults).toHaveText(folderName);
|
await expect(searchResults).toContainText(folderName);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Search results are debounced @couchdb', async ({ page }) => {
|
test('Search results are debounced @couchdb', async ({ page }) => {
|
||||||
@ -185,7 +209,7 @@ test.describe('Grand Search', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Full search for object
|
// Full search for object
|
||||||
await page.type('input[type=search]', 'Clock', { delay: 100 });
|
await grandSearchInput.pressSequentially('Clock', { delay: 100 });
|
||||||
|
|
||||||
// Wait for search to finish
|
// Wait for search to finish
|
||||||
await waitForSearchCompletion(page);
|
await waitForSearchCompletion(page);
|
||||||
@ -194,9 +218,7 @@ test.describe('Grand Search', () => {
|
|||||||
// 1. batched request for latest telemetry using the bulk API
|
// 1. batched request for latest telemetry using the bulk API
|
||||||
expect(networkRequests.length).toBe(1);
|
expect(networkRequests.length).toBe(1);
|
||||||
|
|
||||||
const searchResultDropDown = await page.locator(searchResultDropDownSelector);
|
await expect(page.getByRole('list', { name: 'Object Results' })).toContainText('Clock A');
|
||||||
|
|
||||||
await expect(searchResultDropDown).toContainText('Clock A');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Slowly typing after search debounce will abort requests @couchdb', async ({ page }) => {
|
test('Slowly typing after search debounce will abort requests @couchdb', async ({ page }) => {
|
||||||
@ -246,27 +268,40 @@ test.describe('Grand Search', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Partial search for objects
|
// Partial search for objects
|
||||||
await page.type('input[type=search]', 'e928a26e');
|
await grandSearchInput.fill('e928a26e');
|
||||||
|
|
||||||
// Wait for search to finish
|
// Wait for search to finish
|
||||||
await waitForSearchCompletion(page);
|
await waitForSearchCompletion(page);
|
||||||
|
|
||||||
const searchResultDropDown = page.locator(searchResultDropDownSelector);
|
const searchResultDropDown = page.getByRole('dialog', { name: 'Search Results' });
|
||||||
|
|
||||||
// Verify that the search result/s correctly match the search query
|
// Verify that the search result/s correctly match the search query
|
||||||
await expect(searchResultDropDown).toContainText(folderName1);
|
await expect(searchResultDropDown).toContainText(folderName1);
|
||||||
await expect(searchResultDropDown).toContainText(folderName2);
|
await expect(searchResultDropDown).toContainText(folderName2);
|
||||||
|
|
||||||
// Get the search results
|
// Get the search results
|
||||||
const searchResults = page.locator(searchResultSelector);
|
const objectSearchResults = page.getByLabel('Object Search Result');
|
||||||
// Verify that two results are found
|
// Verify that two results are found
|
||||||
expect(await searchResults.count()).toBe(2);
|
expect(await objectSearchResults.count()).toBe(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for search to complete
|
||||||
|
*
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
*/
|
||||||
async function waitForSearchCompletion(page) {
|
async function waitForSearchCompletion(page) {
|
||||||
// Wait loading spinner to disappear
|
// Wait loading spinner to disappear
|
||||||
await page.waitForSelector('.search-finished');
|
await expect(
|
||||||
|
page
|
||||||
|
.getByRole('list', { name: 'Object Results' })
|
||||||
|
.or(
|
||||||
|
page
|
||||||
|
.getByRole('list', { name: 'Annotation Results' })
|
||||||
|
.or(page.getByText('No results found'))
|
||||||
|
)
|
||||||
|
).toBeVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -306,13 +341,20 @@ async function createObjectsForSearch(page) {
|
|||||||
parent: blueFolder.uuid
|
parent: blueFolder.uuid
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const sineWaveGeneratorAlpha = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Sine Wave Generator',
|
||||||
|
name: 'Sine Wave Generator Alpha'
|
||||||
|
});
|
||||||
|
|
||||||
|
const sineWaveGeneratorBeta = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Sine Wave Generator',
|
||||||
|
name: 'Sine Wave Generator Beta'
|
||||||
|
});
|
||||||
|
|
||||||
const displayLayout = await createDomainObjectWithDefaults(page, {
|
const displayLayout = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Display Layout'
|
type: 'Display Layout'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Go back into edit mode for the display layout
|
|
||||||
await page.locator('button[title="Edit"]').click();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
redFolder,
|
redFolder,
|
||||||
blueFolder,
|
blueFolder,
|
||||||
@ -320,6 +362,8 @@ async function createObjectsForSearch(page) {
|
|||||||
clockB,
|
clockB,
|
||||||
clockC,
|
clockC,
|
||||||
clockD,
|
clockD,
|
||||||
displayLayout
|
displayLayout,
|
||||||
|
sineWaveGeneratorAlpha,
|
||||||
|
sineWaveGeneratorBeta
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -45,19 +45,21 @@ test.describe("Visual - Check Notification Info Banner of 'Save successful' @a11
|
|||||||
name: 'Default Clock'
|
name: 'Default Clock'
|
||||||
});
|
});
|
||||||
// Click on the div with role="alert" that has "Save successful" text
|
// Click on the div with role="alert" that has "Save successful" text
|
||||||
await page.locator('div[role="alert"]:has-text("Save successful")').click();
|
await page.getByRole('alert').filter({ hasText: 'Save successful' }).click();
|
||||||
// Verify there is a div with role="dialog"
|
// Verify there is a div with role="dialog"
|
||||||
expect(await page.locator('div[role="dialog"]').isVisible()).toBe(true);
|
await expect(page.getByRole('dialog', { name: 'Overlay' })).toBeVisible();
|
||||||
// Verify the div with role="dialog" contains text "Save successful"
|
// Verify the div with role="dialog" contains text "Save successful"
|
||||||
expect(await page.locator('div[role="dialog"]').innerText()).toContain('Save successful');
|
expect(await page.getByRole('dialog', { name: 'Overlay' }).innerText()).toContain(
|
||||||
|
'Save successful'
|
||||||
|
);
|
||||||
await percySnapshot(page, `Notification banner shows Save successful (theme: '${theme}')`);
|
await percySnapshot(page, `Notification banner shows Save successful (theme: '${theme}')`);
|
||||||
// Verify there is a button with text "Dismiss"
|
// Verify there is a button with text "Dismiss"
|
||||||
expect(await page.locator('button:has-text("Dismiss")').isVisible()).toBe(true);
|
await expect(page.getByText('Dismiss', { exact: true })).toBeVisible();
|
||||||
await percySnapshot(page, `Notification banner shows Dismiss (theme: '${theme}')`);
|
await percySnapshot(page, `Notification banner shows Dismiss (theme: '${theme}')`);
|
||||||
// Click on button with text "Dismiss"
|
// Click on button with text "Dismiss"
|
||||||
await page.locator('button:has-text("Dismiss")').click();
|
await page.getByText('Dismiss', { exact: true }).click();
|
||||||
// Verify there is no div with role="dialog"
|
// Verify there is no div with role="dialog"
|
||||||
expect(await page.locator('div[role="dialog"]').isVisible()).toBe(false);
|
await expect(page.getByRole('dialog', { name: 'Overlay' })).toBeHidden();
|
||||||
await percySnapshot(page, `Notification banner dismissed (theme: '${theme}')`);
|
await percySnapshot(page, `Notification banner dismissed (theme: '${theme}')`);
|
||||||
});
|
});
|
||||||
test.afterEach(async ({ page }, testInfo) => {
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
@ -338,7 +338,7 @@ export class MCT extends EventEmitter {
|
|||||||
component.$nextTick(() => {
|
component.$nextTick(() => {
|
||||||
this.layout = component;
|
this.layout = component;
|
||||||
this.app = appLayout;
|
this.app = appLayout;
|
||||||
Browse(this);
|
this.browseRoutes = new Browse(this);
|
||||||
window.addEventListener('beforeunload', this.destroy);
|
window.addEventListener('beforeunload', this.destroy);
|
||||||
this.router.start();
|
this.router.start();
|
||||||
this.emit('start');
|
this.emit('start');
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
class="c-overlay__contents js-notebook-snapshot-item-wrapper"
|
class="c-overlay__contents js-notebook-snapshot-item-wrapper"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
|
aria-label="Overlay"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
></div>
|
></div>
|
||||||
<div v-if="buttons" class="c-overlay__button-bar">
|
<div v-if="buttons" class="c-overlay__button-bar">
|
||||||
|
@ -22,7 +22,13 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="u-contents">
|
<div class="u-contents">
|
||||||
<div class="c-clock l-time-display u-style-receiver js-style-receiver">
|
<div
|
||||||
|
role="status"
|
||||||
|
aria-live="polite"
|
||||||
|
aria-atomic="true"
|
||||||
|
aria-label="Clock"
|
||||||
|
class="c-clock l-time-display u-style-receiver js-style-receiver"
|
||||||
|
>
|
||||||
<div class="c-clock__timezone">
|
<div class="c-clock__timezone">
|
||||||
{{ timeZoneAbbr }}
|
{{ timeZoneAbbr }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,11 +23,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="c-gsearch-result c-gsearch-result--annotation"
|
class="c-gsearch-result c-gsearch-result--annotation"
|
||||||
aria-label="Search Result"
|
aria-label="Annotation Search Result"
|
||||||
role="presentation"
|
role="listitem"
|
||||||
>
|
>
|
||||||
<div class="c-gsearch-result__type-icon" :class="resultTypeIcon"></div>
|
<div class="c-gsearch-result__type-icon" :class="resultTypeIcon"></div>
|
||||||
<div class="c-gsearch-result__body" aria-label="Annotation Search Result">
|
<div class="c-gsearch-result__body">
|
||||||
<div class="c-gsearch-result__title" @click="clickedResult">
|
<div class="c-gsearch-result__title" @click="clickedResult">
|
||||||
{{ getResultName }}
|
{{ getResultName }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -272,15 +272,15 @@ describe('GrandSearch', () => {
|
|||||||
it('should render an annotation search result', async () => {
|
it('should render an annotation search result', async () => {
|
||||||
await grandSearchComponent.$refs.root.searchEverything('S');
|
await grandSearchComponent.$refs.root.searchEverything('S');
|
||||||
await nextTick();
|
await nextTick();
|
||||||
const annotationResults = document.querySelectorAll('[aria-label="Search Result"]');
|
const annotationResults = document.querySelectorAll('[aria-label="Annotation Search Result"]');
|
||||||
expect(annotationResults.length).toBe(2);
|
expect(annotationResults.length).toBe(1);
|
||||||
expect(annotationResults[1].innerText).toContain('Driving');
|
expect(annotationResults[0].innerText).toContain('Driving');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render no annotation search results if no match', async () => {
|
it('should render no annotation search results if no match', async () => {
|
||||||
await grandSearchComponent.$refs.root.searchEverything('Qbert');
|
await grandSearchComponent.$refs.root.searchEverything('Qbert');
|
||||||
await nextTick();
|
await nextTick();
|
||||||
const annotationResults = document.querySelectorAll('[aria-label="Search Result"]');
|
const annotationResults = document.querySelectorAll('[aria-label="Annotation Search Result"]');
|
||||||
expect(annotationResults.length).toBe(0);
|
expect(annotationResults.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -288,10 +288,9 @@ describe('GrandSearch', () => {
|
|||||||
await grandSearchComponent.$refs.root.searchEverything('Folder');
|
await grandSearchComponent.$refs.root.searchEverything('Folder');
|
||||||
grandSearchComponent.$refs.root.openmct.router.path = [mockDisplayLayout];
|
grandSearchComponent.$refs.root.openmct.router.path = [mockDisplayLayout];
|
||||||
await nextTick();
|
await nextTick();
|
||||||
const searchResults = document.querySelectorAll('[name="Test Folder"]');
|
const folderResult = document.querySelector('[name="Test Folder"]');
|
||||||
expect(searchResults.length).toBe(1);
|
expect(folderResult).not.toBeNull();
|
||||||
expect(searchResults[0].innerText).toContain('Folder');
|
folderResult.click();
|
||||||
searchResults[0].click();
|
|
||||||
const previewWindow = document.querySelector('.js-preview-window');
|
const previewWindow = document.querySelector('.js-preview-window');
|
||||||
expect(previewWindow.innerText).toContain('Snapshot');
|
expect(previewWindow.innerText).toContain('Snapshot');
|
||||||
});
|
});
|
||||||
@ -300,7 +299,7 @@ describe('GrandSearch', () => {
|
|||||||
await grandSearchComponent.$refs.root.searchEverything('Dri');
|
await grandSearchComponent.$refs.root.searchEverything('Dri');
|
||||||
grandSearchComponent.$refs.root.openmct.router.path = [mockDisplayLayout];
|
grandSearchComponent.$refs.root.openmct.router.path = [mockDisplayLayout];
|
||||||
await nextTick();
|
await nextTick();
|
||||||
const annotationResults = document.querySelectorAll('[aria-label="Search Result"]');
|
const annotationResults = document.querySelectorAll('[aria-label="Annotation Search Result"]');
|
||||||
expect(annotationResults.length).toBe(1);
|
expect(annotationResults.length).toBe(1);
|
||||||
expect(annotationResults[0].innerText).toContain('Driving');
|
expect(annotationResults[0].innerText).toContain('Driving');
|
||||||
annotationResults[0].click();
|
annotationResults[0].click();
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="c-gsearch-result c-gsearch-result--object"
|
class="c-gsearch-result c-gsearch-result--object"
|
||||||
aria-label="Search Result"
|
aria-label="Object Search Result"
|
||||||
role="presentation"
|
role="listitem"
|
||||||
>
|
>
|
||||||
<div class="c-gsearch-result__type-icon" :class="resultTypeIcon"></div>
|
<div class="c-gsearch-result__type-icon" :class="resultTypeIcon"></div>
|
||||||
<div
|
<div
|
||||||
|
@ -21,14 +21,15 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="c-gsearch__dropdown">
|
<div role="dialog" aria-label="Search Results Dropdown" class="c-gsearch__dropdown">
|
||||||
<div v-show="resultsShown" class="c-gsearch__results-wrapper">
|
<div v-show="resultsShown" class="c-gsearch__results-wrapper">
|
||||||
<div class="c-gsearch__results" :class="{ 'search-finished': !searchLoading }">
|
<div class="c-gsearch__results" :class="{ 'search-finished': !searchLoading }">
|
||||||
<div
|
<div
|
||||||
v-if="objectResults && objectResults.length"
|
v-if="objectResults?.length"
|
||||||
ref="objectResults"
|
ref="objectResults"
|
||||||
class="c-gsearch__results-section"
|
class="c-gsearch__results-section"
|
||||||
role="listbox"
|
role="list"
|
||||||
|
aria-label="Object Results"
|
||||||
>
|
>
|
||||||
<div class="c-gsearch__results-section-title">Object Results</div>
|
<div class="c-gsearch__results-section-title">Object Results</div>
|
||||||
<object-search-result
|
<object-search-result
|
||||||
@ -39,7 +40,12 @@
|
|||||||
@click="selectedResult"
|
@click="selectedResult"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="annotationResults && annotationResults.length" ref="annotationResults">
|
<div
|
||||||
|
v-if="annotationResults?.length"
|
||||||
|
ref="annotationResults"
|
||||||
|
role="list"
|
||||||
|
aria-label="Annotation Results"
|
||||||
|
>
|
||||||
<div class="c-gsearch__results-section-title">Annotation Results</div>
|
<div class="c-gsearch__results-section-title">Annotation Results</div>
|
||||||
<annotation-search-result
|
<annotation-search-result
|
||||||
v-for="annotationResult in annotationResults"
|
v-for="annotationResult in annotationResults"
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div class="l-preview-window js-preview-window">
|
<div role="dialog" aria-label="Preview Container" class="l-preview-window js-preview-window">
|
||||||
<PreviewHeader
|
<PreviewHeader
|
||||||
ref="previewHeader"
|
ref="previewHeader"
|
||||||
:current-view="currentViewProvider"
|
:current-view="currentViewProvider"
|
||||||
|
@ -306,10 +306,11 @@ class ApplicationRouter extends EventEmitter {
|
|||||||
*
|
*
|
||||||
* @param {string} newPath new path of url
|
* @param {string} newPath new path of url
|
||||||
* @param {string} oldPath old path of url
|
* @param {string} oldPath old path of url
|
||||||
|
* @returns {boolean} true if path changed, false otherwise
|
||||||
*/
|
*/
|
||||||
doPathChange(newPath, oldPath) {
|
doPathChange(newPath, oldPath) {
|
||||||
if (newPath === oldPath) {
|
if (newPath === oldPath) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let route = this.routes.filter((r) => r.matcher.test(newPath))[0];
|
let route = this.routes.filter((r) => r.matcher.test(newPath))[0];
|
||||||
@ -320,6 +321,8 @@ class ApplicationRouter extends EventEmitter {
|
|||||||
this.openmct.telemetry.abortAllRequests();
|
this.openmct.telemetry.abortAllRequests();
|
||||||
|
|
||||||
this.emit('change:path', newPath, oldPath);
|
this.emit('change:path', newPath, oldPath);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -328,10 +331,11 @@ class ApplicationRouter extends EventEmitter {
|
|||||||
*
|
*
|
||||||
* @param {object} newParams new params of url
|
* @param {object} newParams new params of url
|
||||||
* @param {object} oldParams old params of url
|
* @param {object} oldParams old params of url
|
||||||
|
* @returns {boolean} true if params changed, false otherwise
|
||||||
*/
|
*/
|
||||||
doParamsChange(newParams, oldParams) {
|
doParamsChange(newParams, oldParams) {
|
||||||
if (_.isEqual(newParams, oldParams)) {
|
if (_.isEqual(newParams, oldParams)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let changedParams = {};
|
let changedParams = {};
|
||||||
@ -347,6 +351,7 @@ class ApplicationRouter extends EventEmitter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.emit('change:params', newParams, oldParams, changedParams);
|
this.emit('change:params', newParams, oldParams, changedParams);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -368,9 +373,13 @@ class ApplicationRouter extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.doPathChange(newLocation.path, oldLocation.path);
|
const pathChanged = this.doPathChange(newLocation.path, oldLocation.path);
|
||||||
|
|
||||||
this.doParamsChange(newLocation.params, oldLocation.params);
|
const paramsChanged = this.doParamsChange(newLocation.params, oldLocation.params, pathChanged);
|
||||||
|
if (pathChanged || paramsChanged) {
|
||||||
|
// If either path or parameters have changed, we update the URL in the address bar.
|
||||||
|
this.set(newLocation.path, newLocation.getQueryString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,153 +1,167 @@
|
|||||||
export default function install(openmct) {
|
/*****************************************************************************
|
||||||
let navigateCall = 0;
|
* Open MCT, Copyright (c) 2014-2023, United States Government
|
||||||
let browseObject;
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
let unobserve = undefined;
|
* Administration. All rights reserved.
|
||||||
let currentObjectPath;
|
*
|
||||||
let isRoutingInProgress = false;
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
openmct.router.route(/^\/browse\/?$/, navigateToFirstChildOfRoot);
|
export default class Browse {
|
||||||
openmct.router.route(/^\/browse\/(.*)$/, (path, results, params) => {
|
#navigateCall = 0;
|
||||||
isRoutingInProgress = true;
|
#browseObject = null;
|
||||||
let navigatePath = results[1];
|
#unobserve = undefined;
|
||||||
clearMutationListeners();
|
#currentObjectPath = undefined;
|
||||||
|
#isRoutingInProgress = false;
|
||||||
|
#openmct;
|
||||||
|
|
||||||
navigateToPath(navigatePath, params.view);
|
constructor(openmct) {
|
||||||
});
|
this.#openmct = openmct;
|
||||||
|
this.#openmct.router.route(/^\/browse\/?$/, this.#navigateToFirstChildOfRoot.bind(this));
|
||||||
|
this.#openmct.router.route(/^\/browse\/(.*)$/, this.#handleBrowseRoute.bind(this));
|
||||||
|
this.#openmct.router.on('change:params', this.#onParamsChanged.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
openmct.router.on('change:params', onParamsChanged);
|
#onParamsChanged(newParams, oldParams, changed) {
|
||||||
|
if (this.#isRoutingInProgress) {
|
||||||
function onParamsChanged(newParams, oldParams, changed) {
|
|
||||||
if (isRoutingInProgress) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed.view && browseObject) {
|
if (changed.view && this.#browseObject) {
|
||||||
let provider = openmct.objectViews.getByProviderKey(changed.view);
|
const provider = this.#openmct.objectViews.getByProviderKey(changed.view);
|
||||||
viewObject(browseObject, provider);
|
this.#viewObject(this.#browseObject, provider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function viewObject(object, viewProvider) {
|
#viewObject(object, viewProvider) {
|
||||||
currentObjectPath = openmct.router.path;
|
this.#currentObjectPath = this.#openmct.router.path;
|
||||||
|
|
||||||
openmct.layout.$refs.browseObject.show(object, viewProvider.key, true, currentObjectPath);
|
this.#openmct.layout.$refs.browseObject.show(
|
||||||
openmct.layout.$refs.browseBar.domainObject = object;
|
object,
|
||||||
|
viewProvider.key,
|
||||||
openmct.layout.$refs.browseBar.viewKey = viewProvider.key;
|
true,
|
||||||
|
this.#currentObjectPath
|
||||||
|
);
|
||||||
|
this.#openmct.layout.$refs.browseBar.domainObject = object;
|
||||||
|
this.#openmct.layout.$refs.browseBar.viewKey = viewProvider.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDocumentTitleOnNameMutation(newName) {
|
#updateDocumentTitleOnNameMutation(newName) {
|
||||||
if (typeof newName === 'string' && newName !== document.title) {
|
if (typeof newName === 'string' && newName !== document.title) {
|
||||||
document.title = newName;
|
document.title = newName;
|
||||||
openmct.layout.$refs.browseBar.domainObject = {
|
this.#openmct.layout.$refs.browseBar.domainObject = {
|
||||||
...openmct.layout.$refs.browseBar.domainObject,
|
...this.#openmct.layout.$refs.browseBar.domainObject,
|
||||||
name: newName
|
name: newName
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateToPath(path, currentViewKey) {
|
async #navigateToPath(path, currentViewKey) {
|
||||||
navigateCall++;
|
this.#navigateCall++;
|
||||||
let currentNavigation = navigateCall;
|
const currentNavigation = this.#navigateCall;
|
||||||
|
|
||||||
if (unobserve) {
|
if (this.#unobserve) {
|
||||||
unobserve();
|
this.#unobserve();
|
||||||
unobserve = undefined;
|
this.#unobserve = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Split path into object identifiers
|
|
||||||
if (!Array.isArray(path)) {
|
if (!Array.isArray(path)) {
|
||||||
path = path.split('/');
|
path = path.split('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
return pathToObjects(path).then((objects) => {
|
let objects = await this.#pathToObjects(path);
|
||||||
isRoutingInProgress = false;
|
if (currentNavigation !== this.#navigateCall) {
|
||||||
|
|
||||||
if (currentNavigation !== navigateCall) {
|
|
||||||
return; // Prevent race.
|
return; // Prevent race.
|
||||||
}
|
}
|
||||||
|
this.#isRoutingInProgress = false;
|
||||||
objects = objects.reverse();
|
objects = objects.reverse();
|
||||||
|
this.#openmct.router.path = objects;
|
||||||
openmct.router.path = objects;
|
this.#browseObject = objects[0];
|
||||||
openmct.router.emit('afterNavigation');
|
this.#openmct.router.emit('afterNavigation');
|
||||||
browseObject = objects[0];
|
this.#openmct.layout.$refs.browseBar.domainObject = this.#browseObject;
|
||||||
|
if (!this.#browseObject) {
|
||||||
openmct.layout.$refs.browseBar.domainObject = browseObject;
|
this.#openmct.layout.$refs.browseObject.clear();
|
||||||
if (!browseObject) {
|
|
||||||
openmct.layout.$refs.browseObject.clear();
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
document.title = this.#browseObject.name; //change document title to current object in main view
|
||||||
let currentProvider = openmct.objectViews.getByProviderKey(currentViewKey);
|
this.#unobserve = this.#openmct.objects.observe(
|
||||||
document.title = browseObject.name; //change document title to current object in main view
|
this.#browseObject,
|
||||||
// assign listener to global for later clearing
|
'name',
|
||||||
unobserve = openmct.objects.observe(browseObject, 'name', updateDocumentTitleOnNameMutation);
|
this.#updateDocumentTitleOnNameMutation.bind(this)
|
||||||
|
);
|
||||||
if (currentProvider && currentProvider.canView(browseObject, openmct.router.path)) {
|
const currentProvider = this.#openmct.objectViews.getByProviderKey(currentViewKey);
|
||||||
viewObject(browseObject, currentProvider);
|
if (currentProvider && currentProvider.canView(this.#browseObject, this.#openmct.router.path)) {
|
||||||
|
this.#viewObject(this.#browseObject, currentProvider);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const routerPath = this.#openmct.router.path;
|
||||||
let defaultProvider = openmct.objectViews.get(browseObject, openmct.router.path)[0];
|
const retrievedObjectViews = this.#openmct.objectViews.get(this.#browseObject, routerPath);
|
||||||
|
const defaultProvider = retrievedObjectViews?.[0];
|
||||||
if (defaultProvider) {
|
if (defaultProvider) {
|
||||||
openmct.router.updateParams({
|
this.#openmct.router.updateParams({ view: defaultProvider.key });
|
||||||
view: defaultProvider.key
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
openmct.router.updateParams({
|
this.#openmct.router.updateParams({ view: undefined });
|
||||||
view: undefined
|
this.#openmct.layout.$refs.browseObject.clear();
|
||||||
});
|
|
||||||
openmct.layout.$refs.browseObject.clear();
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pathToObjects(path) {
|
#pathToObjects(path) {
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
path.map((keyString) => {
|
path.map((keyString) => {
|
||||||
let identifier = openmct.objects.parseKeyString(keyString);
|
const identifier = this.#openmct.objects.parseKeyString(keyString);
|
||||||
if (openmct.objects.supportsMutation(identifier)) {
|
return this.#openmct.objects.supportsMutation(identifier)
|
||||||
return openmct.objects.getMutable(identifier);
|
? this.#openmct.objects.getMutable(identifier)
|
||||||
} else {
|
: this.#openmct.objects.get(identifier);
|
||||||
return openmct.objects.get(identifier);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateToFirstChildOfRoot() {
|
async #navigateToFirstChildOfRoot() {
|
||||||
openmct.objects
|
try {
|
||||||
.get('ROOT')
|
const rootObject = await this.#openmct.objects.get('ROOT');
|
||||||
.then((rootObject) => {
|
const composition = this.#openmct.composition.get(rootObject);
|
||||||
const composition = openmct.composition.get(rootObject);
|
|
||||||
if (!composition) {
|
if (!composition) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
composition
|
const children = await composition.load();
|
||||||
.load()
|
const lastChild = children[children.length - 1];
|
||||||
.then((children) => {
|
|
||||||
let lastChild = children[children.length - 1];
|
|
||||||
if (lastChild) {
|
if (lastChild) {
|
||||||
let lastChildId = openmct.objects.makeKeyString(lastChild.identifier);
|
const lastChildId = this.#openmct.objects.makeKeyString(lastChild.identifier);
|
||||||
openmct.router.setPath(`#/browse/${lastChildId}`);
|
this.#openmct.router.setPath(`#/browse/${lastChildId}`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.catch((e) => console.error(e));
|
|
||||||
})
|
|
||||||
.catch((e) => console.error(e));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearMutationListeners() {
|
#clearMutationListeners() {
|
||||||
if (openmct.router.path !== undefined) {
|
if (this.#openmct.router.path) {
|
||||||
openmct.router.path.forEach((pathObject) => {
|
this.#openmct.router.path.forEach((pathObject) => {
|
||||||
if (pathObject.isMutable) {
|
if (pathObject.isMutable) {
|
||||||
openmct.objects.destroyMutable(pathObject);
|
this.#openmct.objects.destroyMutable(pathObject);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#handleBrowseRoute(path, results, params) {
|
||||||
|
this.#isRoutingInProgress = true;
|
||||||
|
const navigatePath = results[1];
|
||||||
|
this.#clearMutationListeners();
|
||||||
|
this.#navigateToPath(navigatePath, params.view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user