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:
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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> = {
|
||||
|
||||
@@ -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> = {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user