1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

PM-21651 [For Automation Purposes] add test IDs to notification bar (#14863)

* PM-21651 [For Automation Purposes] Please add IDs to some of the main components

* option items

* dynamic test id

* mitigate feedback

* clean up logic
This commit is contained in:
Daniel Riera
2025-05-23 11:48:23 -04:00
committed by GitHub
parent 0e0be0a3de
commit c6af80f3eb
12 changed files with 70 additions and 15 deletions

View File

@@ -10,6 +10,7 @@ import { AngleUp, AngleDown } from "../icons";
export type OptionSelectionButtonProps = {
disabled: boolean;
icon?: Option["icon"];
id: string;
text?: string;
theme: Theme;
toggledOn: boolean;
@@ -19,6 +20,7 @@ export type OptionSelectionButtonProps = {
export function OptionSelectionButton({
disabled,
icon,
id,
text,
theme,
toggledOn,
@@ -31,6 +33,7 @@ export function OptionSelectionButton({
return html`
<button
class=${selectionButtonStyles({ disabled, toggledOn, theme })}
data-testid="${id}-option-selection"
title=${text}
type="button"
aria-haspopup="menu"

View File

@@ -12,6 +12,7 @@ export default {
argTypes: {
disabled: { control: "boolean" },
handleButtonClick: { control: false },
id: { control: "text" },
text: { control: "text" },
theme: { control: "select", options: [...Object.values(ThemeTypes)] },
toggledOn: { control: "boolean" },
@@ -19,6 +20,7 @@ export default {
args: {
disabled: false,
handleButtonClick: () => alert("Clicked"),
id: "example-id",
text: "Click Me",
theme: ThemeTypes.Light,
toggledOn: false,

View File

@@ -3,7 +3,10 @@ import { Meta, StoryObj } from "@storybook/web-components";
import { ThemeTypes } from "@bitwarden/common/platform/enums";
import { NotificationTypes } from "../../../../../notification/abstractions/notification-bar";
import { getConfirmationHeaderMessage } from "../../../../../notification/bar";
import {
getConfirmationHeaderMessage,
getNotificationTestId,
} from "../../../../../notification/bar";
import {
NotificationConfirmationContainer,
NotificationConfirmationContainerProps,
@@ -38,7 +41,8 @@ export default {
const Template = (args: NotificationConfirmationContainerProps) => {
const headerMessage = getConfirmationHeaderMessage(args.i18n, args.type, args.error);
return NotificationConfirmationContainer({ ...args, headerMessage });
const notificationTestId = getNotificationTestId(args.type, true);
return NotificationConfirmationContainer({ ...args, headerMessage, notificationTestId });
};
export const Default: StoryObj<NotificationConfirmationContainerProps> = {

View File

@@ -5,7 +5,7 @@ import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { NotificationTypes } from "../../../../notification/abstractions/notification-bar";
import { getNotificationHeaderMessage } from "../../../../notification/bar";
import { getNotificationHeaderMessage, getNotificationTestId } from "../../../../notification/bar";
import { NotificationContainer, NotificationContainerProps } from "../../notification/container";
import { mockBrowserI18nGetMessage, mockI18n } from "../mock-data";
@@ -49,7 +49,8 @@ export default {
const Template = (args: NotificationContainerProps) => {
const headerMessage = getNotificationHeaderMessage(args.i18n, args.type);
return NotificationContainer({ ...args, headerMessage });
const notificationTestId = getNotificationTestId(args.type);
return NotificationContainer({ ...args, headerMessage, notificationTestId });
};
export const Default: StoryObj<NotificationContainerProps> = {

View File

@@ -16,6 +16,7 @@ const mockOptions: Option[] = [
type ComponentProps = {
disabled?: boolean;
id: string;
label?: string;
options: Option[];
theme: Theme;
@@ -31,16 +32,18 @@ export default {
},
args: {
disabled: false,
id: "example-id",
label: undefined,
options: mockOptions,
theme: ThemeTypes.Light,
},
} as Meta<ComponentProps>;
const BaseComponent = ({ disabled, label, options, theme }: ComponentProps) => {
const BaseComponent = ({ disabled, id, label, options, theme }: ComponentProps) => {
return html`
<option-selection
.disabled=${disabled}
.id=${id}
.label="${label}"
.options=${options}
theme=${theme}

View File

@@ -28,6 +28,7 @@ export type NotificationConfirmationContainerProps = NotificationBarIframeInitDa
headerMessage?: string;
i18n: I18n;
itemName: string;
notificationTestId: string;
task?: NotificationTaskInfo;
type: NotificationType;
};
@@ -40,6 +41,7 @@ export function NotificationConfirmationContainer({
headerMessage,
i18n,
itemName,
notificationTestId,
task,
theme = ThemeTypes.Light,
type,
@@ -68,7 +70,7 @@ export function NotificationConfirmationContainer({
}
return html`
<div class=${notificationContainerStyles(theme)}>
<div data-testid="${notificationTestId}" class=${notificationContainerStyles(theme)}>
${NotificationHeader({
handleCloseNotification,
i18n,

View File

@@ -31,6 +31,7 @@ export type NotificationContainerProps = NotificationBarIframeInitData & {
i18n: I18n;
organizations?: OrgView[];
personalVaultIsAllowed?: boolean;
notificationTestId: string;
type: NotificationType; // @TODO typing override for generic `NotificationBarIframeInitData.type`
};
@@ -45,13 +46,14 @@ export function NotificationContainer({
i18n,
organizations,
personalVaultIsAllowed = true,
notificationTestId,
theme = ThemeTypes.Light,
type,
}: NotificationContainerProps) {
const showBody = type !== NotificationTypes.Unlock;
return html`
<div class=${notificationContainerStyles(theme)}>
<div data-testid="${notificationTestId}" class=${notificationContainerStyles(theme)}>
${NotificationHeader({
handleCloseNotification,
i18n,

View File

@@ -13,6 +13,7 @@ const { css } = createEmotion({
});
export type OptionItemProps = Option & {
id: string;
contextLabel?: string;
theme: Theme;
handleSelection: () => void;
@@ -20,6 +21,7 @@ export type OptionItemProps = Option & {
export function OptionItem({
contextLabel,
id,
icon,
text,
theme,
@@ -44,6 +46,7 @@ export function OptionItem({
return html`<div
class=${optionItemStyles}
data-testid="${id}-option-item"
key=${value}
tabindex="0"
title=${text}

View File

@@ -15,6 +15,7 @@ const { css } = createEmotion({
});
export type OptionItemsProps = {
id: string;
theme: Theme;
topOffset: number;
label?: string;
@@ -23,6 +24,7 @@ export type OptionItemsProps = {
};
export function OptionItems({
id,
theme,
topOffset,
label,
@@ -42,6 +44,7 @@ export function OptionItems({
<div class=${optionsWrapper({ isSafari, theme })}>
${options.map((option) =>
OptionItem({
id,
...option,
theme,
contextLabel: label,

View File

@@ -20,6 +20,9 @@ export class OptionSelection extends LitElement {
@property()
disabled: boolean = false;
@property()
id: string = "";
@property()
label?: string;
@@ -130,6 +133,7 @@ export class OptionSelection extends LitElement {
${OptionSelectionButton({
disabled: this.disabled,
icon: this.selection?.icon,
id: this.id,
text: this.selection?.text,
theme: this.theme,
toggledOn: this.showMenu,
@@ -137,6 +141,7 @@ export class OptionSelection extends LitElement {
})}
${this.showMenu
? OptionItems({
id: this.id,
label: this.label,
options: this.options,
theme: this.theme,

View File

@@ -38,6 +38,7 @@ export function ButtonRow({ theme, primaryButton, selectButtons }: ButtonRowProp
<option-selection
key=${id}
theme=${theme}
.id=${id}
.label=${label}
.options=${options}
.handleSelectionUpdate=${handleSelectionUpdate}

View File

@@ -175,6 +175,27 @@ function resolveNotificationType(initData: NotificationBarIframeInitData): Notif
return initData.type as NotificationType;
}
/**
* Returns the appropriate test ID based on the resolved notification type.
*
* @param type - The resolved NotificationType.
* @param isConfirmation - Optional flag for confirmation vs. notification container.
*/
export function getNotificationTestId(
notificationType: NotificationType,
isConfirmation = false,
): string {
if (isConfirmation) {
return "confirmation-notification-bar";
}
return {
[NotificationTypes.Unlock]: "unlock-notification-bar",
[NotificationTypes.Add]: "save-notification-bar",
[NotificationTypes.Change]: "update-notification-bar",
}[notificationType];
}
/**
* Sets the text content of an element identified by ID within a template's content.
*
@@ -212,6 +233,7 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
if (useComponentBar) {
const resolvedType = resolveNotificationType(notificationBarIframeInitData);
const headerMessage = getNotificationHeaderMessage(i18n, resolvedType);
const notificationTestId = getNotificationTestId(resolvedType);
appendHeaderMessageToTitle(headerMessage);
document.body.innerHTML = "";
@@ -224,6 +246,7 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
...notificationBarIframeInitData,
headerMessage,
type: resolvedType,
notificationTestId,
theme: resolvedTheme,
personalVaultIsAllowed: !personalVaultDisallowed,
handleCloseNotification,
@@ -269,6 +292,7 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
headerMessage,
type: resolvedType,
theme: resolvedTheme,
notificationTestId,
personalVaultIsAllowed: !personalVaultDisallowed,
handleCloseNotification,
handleSaveAction,
@@ -499,23 +523,25 @@ function handleSaveCipherConfirmation(message: NotificationBarWindowMessage) {
const resolvedTheme = getResolvedTheme(theme ?? ThemeTypes.Light);
const resolvedType = resolveNotificationType(notificationBarIframeInitData);
const headerMessage = getConfirmationHeaderMessage(i18n, resolvedType, error);
const notificationTestId = getNotificationTestId(resolvedType, true);
globalThis.setTimeout(() => sendPlatformMessage({ command: "bgCloseNotificationBar" }), 5000);
return render(
NotificationConfirmationContainer({
...notificationBarIframeInitData,
type: type as NotificationType,
theme: resolvedTheme,
handleCloseNotification,
headerMessage,
i18n,
error,
itemName: itemName ?? i18n.typeLogin,
task,
handleCloseNotification,
handleOpenTasks: () => sendPlatformMessage({ command: "bgOpenAtRisksPasswords" }),
handleOpenVault: (e: Event) =>
cipherId ? openViewVaultItemPopout(cipherId) : openAddEditVaultItemPopout(e, {}),
handleOpenTasks: () => sendPlatformMessage({ command: "bgOpenAtRisksPasswords" }),
headerMessage,
i18n,
itemName: itemName ?? i18n.typeLogin,
notificationTestId,
task,
theme: resolvedTheme,
type: type as NotificationType,
}),
document.body,
);