From 6d63339b23c94357af24fa2553fb080d0ff10ba3 Mon Sep 17 00:00:00 2001 From: Jesse Mazzella Date: Wed, 1 Feb 2023 02:11:44 -0800 Subject: [PATCH] fix: Navigating from Recent Objects breadcrumb correctly updates the URL hash (#6234) * fix: provide hashUrl for ObjectPath breadcrumbs * a11y: add `navigation` role and aria-label to breadcrumb * test(e2e): add regression test for breadcrumb nav --------- Co-authored-by: Scott Bell --- .../functional/recentObjects.e2e.spec.js | 67 ++++++++++++++++--- src/ui/components/ObjectPath.vue | 15 +++++ 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/e2e/tests/functional/recentObjects.e2e.spec.js b/e2e/tests/functional/recentObjects.e2e.spec.js index 2fdeb94c1c..cd03ce72b1 100644 --- a/e2e/tests/functional/recentObjects.e2e.spec.js +++ b/e2e/tests/functional/recentObjects.e2e.spec.js @@ -24,15 +24,22 @@ const { test, expect } = require('../../pluginFixtures.js'); const { createDomainObjectWithDefaults } = require('../../appActions.js'); test.describe('Recent Objects', () => { - test('Recent Objects CRUD operations', async ({ page }) => { + let recentObjectsList; + let clock; + let folderA; + test.beforeEach(async ({ page }) => { await page.goto('./', { waitUntil: 'networkidle' }); + // Set Recent Objects List locator for subsequent tests + recentObjectsList = page.getByRole('list', { + name: 'Recent Objects' + }); + // Create a folder and nest a Clock within it - const recentObjectsList = page.locator('[aria-label="Recent Objects"]'); - const folderA = await createDomainObjectWithDefaults(page, { + folderA = await createDomainObjectWithDefaults(page, { type: 'Folder' }); - const clock = await createDomainObjectWithDefaults(page, { + clock = await createDomainObjectWithDefaults(page, { type: 'Clock', parent: folderA.uuid }); @@ -42,7 +49,8 @@ test.describe('Recent Objects', () => { await page.mouse.down(); await page.mouse.move(0, 100); await page.mouse.up(); - + }); + test('Recent Objects CRUD operations', async ({ page }) => { // Verify that both created objects appear in the list and are in the correct order expect(recentObjectsList.getByRole('listitem', { name: clock.name })).toBeTruthy(); expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeTruthy(); @@ -52,7 +60,7 @@ test.describe('Recent Objects', () => { expect(recentObjectsList.getByRole('listitem').nth(1).getByText(folderA.name)).toBeTruthy(); // Navigate to the folder by clicking on the main object name in the recent objects list item - await recentObjectsList.getByRole('listitem', { name: folderA.name }).getByText(folderA.name).click(); + 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(); @@ -63,7 +71,11 @@ test.describe('Recent Objects', () => { await page.keyboard.press('Enter'); // Verify rename has been applied in recent objects list item and objects paths - expect(page.getByRole('listitem', { name: clock.name }).locator('a').getByText(folderA.name)).toBeTruthy(); + expect(await page.getByRole('navigation', { + name: `${clock.name} Breadcrumb` + }).locator('a').filter({ + hasText: folderA.name + }).count()).toBeGreaterThan(0); expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeTruthy(); // Delete @@ -79,7 +91,42 @@ test.describe('Recent Objects', () => { await expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeHidden(); await expect(recentObjectsList.getByRole('listitem', { name: clock.name })).toBeHidden(); }); - test.fixme("Clicking on the 'target button' scrolls the object into view in the tree and highlights it"); - test.fixme("Clicking on an object in the path of a recent object navigates to the object"); - test.fixme("Tests for context menu actions from recent objects"); + test("Clicking on an object in the path of a recent object navigates to the object", async ({ page, openmctConfig }) => { + const { myItemsFolderName } = openmctConfig; + test.info().annotations.push({ + type: 'issue', + description: 'https://github.com/nasa/openmct/issues/6151' + }); + await page.goto('./#/browse/mine'); + + // Navigate to the folder by clicking on its entry in the Clock's breadcrumb + const waitForFolderNavigation = page.waitForURL(`**/${folderA.uuid}?*`); + await page.getByRole('navigation', { + name: `${clock.name} Breadcrumb` + }).locator('a').filter({ + hasText: folderA.name + }).click(); + + // Verify that the hash URL updates correctly + await waitForFolderNavigation; + // eslint-disable-next-line no-useless-escape + expect(page.url()).toMatch(new RegExp(`.*${folderA.uuid}\?.*`)); + + // Navigate to My Items by clicking on its entry in the Clock's breadcrumb + const waitForMyItemsNavigation = page.waitForURL(`**/mine?*`); + await page.getByRole('navigation', { + name: `${clock.name} Breadcrumb` + }).locator('a').filter({ + hasText: myItemsFolderName + }).click(); + + // Verify that the hash URL updates correctly + await waitForMyItemsNavigation; + // eslint-disable-next-line no-useless-escape + expect(page.url()).toMatch(new RegExp(`.*mine\?.*`)); + }); + test.fixme("Clicking on the 'target button' scrolls the object into view in the tree and highlights it", async ({ page }) => { + }); + test.fixme("Tests for context menu actions from recent objects", async ({ page }) => { + }); }); diff --git a/src/ui/components/ObjectPath.vue b/src/ui/components/ObjectPath.vue index 8673448d29..eab36d43bd 100644 --- a/src/ui/components/ObjectPath.vue +++ b/src/ui/components/ObjectPath.vue @@ -24,6 +24,8 @@ @@ -110,6 +113,18 @@ export default { this.orderedPath = pathWithDomainObject.slice(1, pathWithDomainObject.length - 1).reverse(); } } + }, + methods: { + /** + * Generate the hash url for the given object path, removing the '/ROOT' prefix if present. + * @param {import('../../api/objects/ObjectAPI').DomainObject[]} objectPath + */ + navigateToPath(objectPath) { + /** @type {String} */ + const path = `/browse/${this.openmct.objects.getRelativePath(objectPath)}`; + + return path.replace('ROOT/', ''); + } } };