From 7644234f4d126361bafbbf761b5fb8b49f91a3e1 Mon Sep 17 00:00:00 2001 From: Amir Alami Date: Wed, 30 Oct 2024 15:44:54 +0100 Subject: [PATCH] feat: Adds test id to flashbar dom selector --- .../test-utils-selectors.test.tsx.snap | 1 + src/flashbar/__tests__/collapsible.test.tsx | 79 +++++++++++++++++++ src/flashbar/__tests__/flashbar.test.tsx | 61 ++++++++++++++ src/flashbar/collapsible-flashbar.tsx | 1 + src/flashbar/flash.tsx | 2 +- src/flashbar/interfaces.ts | 7 ++ src/flashbar/non-collapsible-flashbar.tsx | 3 +- src/test-utils/dom/anchor-navigation/index.ts | 7 +- src/test-utils/dom/flashbar/index.ts | 16 ++++ 9 files changed, 171 insertions(+), 6 deletions(-) diff --git a/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap b/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap index ea13fb73e6..8e84f3cf83 100644 --- a/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap +++ b/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap @@ -13,6 +13,7 @@ exports[`test-utils selectors 1`] = ` ], "anchor-navigation": [ "awsui_anchor-item--active_17oho", + "awsui_anchor-item_17oho", "awsui_anchor-link-info_17oho", "awsui_anchor-link-text_17oho", "awsui_anchor-link_17oho", diff --git a/src/flashbar/__tests__/collapsible.test.tsx b/src/flashbar/__tests__/collapsible.test.tsx index d9227b7bfb..db8f385307 100644 --- a/src/flashbar/__tests__/collapsible.test.tsx +++ b/src/flashbar/__tests__/collapsible.test.tsx @@ -151,6 +151,85 @@ describe('Collapsible Flashbar', () => { }); }); + test('assigns data-testid to flash items', () => { + const wrapper = renderFlashbar({ + items: [ + { + testId: 'flash-item-1', + content: 'first flash item', + }, + { + testId: 'flash-item-2', + content: 'second flash item', + }, + ], + }); + findNotificationBar(wrapper)!.click(); + const flashbarItemsTestIds = wrapper + .findItems() + .map(flashbar => flashbar.getElement()!.getAttribute('data-testid')); + + expect(flashbarItemsTestIds).toEqual(['flash-item-1', 'flash-item-2']); + }); + + test('findItemByTestId', () => { + const wrapper = renderFlashbar({ + items: [ + { + testId: 'flash-item-1', + content: 'first flash item', + }, + { + testId: 'flash-item-2', + content: 'second flash item', + }, + ], + }); + findNotificationBar(wrapper)!.click(); + const secondFlashItemFromTestId = wrapper.findItemByTestId('flash-item-2')!.getElement(); + + expect(secondFlashItemFromTestId).toHaveTextContent('second flash item'); + }); + + test('findItemByTestId returns the item even if the test ID contains double quotes', () => { + const wrapper = renderFlashbar({ + items: [ + { + testId: '"flash-item-1"', + content: 'first flash item', + }, + { + testId: '"flash-item-2"', + content: 'second flash item', + }, + ], + }); + findNotificationBar(wrapper)!.click(); + const flashItem = wrapper.findItemByTestId('"flash-item-1"')!.getElement(); + + expect(flashItem).toHaveTextContent('first flash item'); + }); + + test('findItemByTestId doesn not return the next items if the collapsible is not expanded', () => { + const wrapper = renderFlashbar({ + items: [ + { + testId: 'flash-item-1', + content: 'first flash item', + }, + { + testId: 'flash-item-2', + content: 'second flash item', + }, + ], + }); + const fistFlashItem = wrapper.findItemByTestId('flash-item-1'); + const secondFlashItem = wrapper.findItemByTestId('flash-item-2'); + + expect(fistFlashItem).toBeTruthy(); + expect(secondFlashItem).not.toBeTruthy(); + }); + test('findItemsByType', () => { { const wrapper = createFlashbarWrapper( diff --git a/src/flashbar/__tests__/flashbar.test.tsx b/src/flashbar/__tests__/flashbar.test.tsx index d7f283c851..494add2ae5 100644 --- a/src/flashbar/__tests__/flashbar.test.tsx +++ b/src/flashbar/__tests__/flashbar.test.tsx @@ -196,6 +196,67 @@ describe('Flashbar component', () => { } }); + test('assigns data-testid to flash items', () => { + const { container } = reactRender( + + ); + const wrapper = createWrapper(container); + const flashbarItemsTestIds = wrapper + .findFlashbar()! + .findItems() + .map(flashbar => flashbar.getElement()!.getAttribute('data-testid')); + + expect(flashbarItemsTestIds).toEqual(['flash-item-1', 'flash-item-2']); + }); + + test('findItemByTestId', () => { + const { container } = reactRender( + + ); + const wrapper = createWrapper(container); + const secondFlashItemFromTestId = wrapper.findFlashbar()!.findItemByTestId('flash-item-2')!.getElement(); + expect(secondFlashItemFromTestId).toHaveTextContent('second flash item'); + }); + + test('findItemByTestId returns the item even if the test ID contains double quotes', () => { + const { container } = reactRender( + + ); + const wrapper = createWrapper(container); + const flashItem = wrapper.findFlashbar()?.findItemByTestId('"flash-item-test-id"')!.getElement(); + + expect(flashItem).toHaveTextContent('flash item'); + }); + test('findItemsByType', () => { const wrapper = createFlashbarWrapper( | undefined) => (
  • { className: string; transitionState?: string; i18nStrings?: FlashbarProps.I18nStrings; diff --git a/src/flashbar/interfaces.ts b/src/flashbar/interfaces.ts index 16cad04c3b..f45bf6382d 100644 --- a/src/flashbar/interfaces.ts +++ b/src/flashbar/interfaces.ts @@ -20,6 +20,13 @@ export namespace FlashbarProps { buttonText?: ButtonProps['children']; onButtonClick?: ButtonProps['onClick']; onDismiss?: ButtonProps['onClick']; + + /** + * Test ID of the flash list item. + * Assigns this value to the `data-testid` attribute of the flash list item. + * Flash component is the direct child of the flash list item. + */ + testId?: string; } export interface I18nStrings { diff --git a/src/flashbar/non-collapsible-flashbar.tsx b/src/flashbar/non-collapsible-flashbar.tsx index 6d2dcfd49d..1a75e606f8 100644 --- a/src/flashbar/non-collapsible-flashbar.tsx +++ b/src/flashbar/non-collapsible-flashbar.tsx @@ -62,7 +62,7 @@ export default function NonCollapsibleFlashbar({ items, i18nStrings, ...restProp in={true} > {(state: string, transitionRootElement: React.Ref | undefined) => ( -
  • +
  • {renderItem(item, item.id ?? index, transitionRootElement, state)}
  • )} @@ -91,6 +91,7 @@ export default function NonCollapsibleFlashbar({ items, i18nStrings, ...restProp
  • {renderItem(item, item.id ?? index)} diff --git a/src/test-utils/dom/anchor-navigation/index.ts b/src/test-utils/dom/anchor-navigation/index.ts index 1a8ca5cae0..9b62f006a6 100644 --- a/src/test-utils/dom/anchor-navigation/index.ts +++ b/src/test-utils/dom/anchor-navigation/index.ts @@ -1,6 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { ComponentWrapper, ElementWrapper, usesDom } from '@cloudscape-design/test-utils-core/dom'; +import { escapeSelector } from '@cloudscape-design/test-utils-core/utils.js'; import testUtilStyles from '../../../anchor-navigation/test-classes/styles.selectors.js'; @@ -42,10 +43,8 @@ export default class AnchorNavigationWrapper extends ComponentWrapper { * @returns {AnchorItemWrapper | null} */ findAnchorByTestId(testId: string): AnchorItemWrapper | null { - return this.findComponent( - `.${testUtilStyles['anchor-item']}[data-testid="${CSS.escape(testId)}"]`, - AnchorItemWrapper - ); + const escapedTestId = escapeSelector(testId); + return this.findComponent(`.${testUtilStyles['anchor-item']}[data-testid="${escapedTestId}"]`, AnchorItemWrapper); } } diff --git a/src/test-utils/dom/flashbar/index.ts b/src/test-utils/dom/flashbar/index.ts index dda96af880..11a659fdb0 100644 --- a/src/test-utils/dom/flashbar/index.ts +++ b/src/test-utils/dom/flashbar/index.ts @@ -1,6 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { ComponentWrapper, ElementWrapper } from '@cloudscape-design/test-utils-core/dom'; +import { escapeSelector } from '@cloudscape-design/test-utils-core/utils'; import FlashWrapper from './flash'; @@ -31,6 +32,21 @@ export default class FlashbarWrapper extends ComponentWrapper { ); } + /** + * Returns the wrapper of the first flash list item that matches the specified test ID. + * If the items are stacked, the hidden items will not be returned. + * + * Looks for the `data-testid` attribute that is assigned via `items` prop. + * If no matching flash list item is found, returns `null`. + * + * @param {string} testId + * @returns {FlashbarWrapper | null} + */ + findItemByTestId(testId: string): FlashWrapper | null { + const escapedTestId = escapeSelector(testId); + return this.findComponent(`.${styles['flash-list-item']}[data-testid="${escapedTestId}"]`, FlashWrapper); + } + /** * Returns the toggle button that expands and collapses stacked notifications. */