Skip to content

Commit c6af80f

Browse files
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
1 parent 0e0be0a commit c6af80f

File tree

12 files changed

+68
-13
lines changed

12 files changed

+68
-13
lines changed

apps/browser/src/autofill/content/components/buttons/option-selection-button.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { AngleUp, AngleDown } from "../icons";
1010
export type OptionSelectionButtonProps = {
1111
disabled: boolean;
1212
icon?: Option["icon"];
13+
id: string;
1314
text?: string;
1415
theme: Theme;
1516
toggledOn: boolean;
@@ -19,6 +20,7 @@ export type OptionSelectionButtonProps = {
1920
export function OptionSelectionButton({
2021
disabled,
2122
icon,
23+
id,
2224
text,
2325
theme,
2426
toggledOn,
@@ -31,6 +33,7 @@ export function OptionSelectionButton({
3133
return html`
3234
<button
3335
class=${selectionButtonStyles({ disabled, toggledOn, theme })}
36+
data-testid="${id}-option-selection"
3437
title=${text}
3538
type="button"
3639
aria-haspopup="menu"

apps/browser/src/autofill/content/components/lit-stories/buttons/option-selection-button.lit-stories.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ export default {
1212
argTypes: {
1313
disabled: { control: "boolean" },
1414
handleButtonClick: { control: false },
15+
id: { control: "text" },
1516
text: { control: "text" },
1617
theme: { control: "select", options: [...Object.values(ThemeTypes)] },
1718
toggledOn: { control: "boolean" },
1819
},
1920
args: {
2021
disabled: false,
2122
handleButtonClick: () => alert("Clicked"),
23+
id: "example-id",
2224
text: "Click Me",
2325
theme: ThemeTypes.Light,
2426
toggledOn: false,

apps/browser/src/autofill/content/components/lit-stories/notification/confirmation/container.lit-stories.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import { Meta, StoryObj } from "@storybook/web-components";
33
import { ThemeTypes } from "@bitwarden/common/platform/enums";
44

55
import { NotificationTypes } from "../../../../../notification/abstractions/notification-bar";
6-
import { getConfirmationHeaderMessage } from "../../../../../notification/bar";
6+
import {
7+
getConfirmationHeaderMessage,
8+
getNotificationTestId,
9+
} from "../../../../../notification/bar";
710
import {
811
NotificationConfirmationContainer,
912
NotificationConfirmationContainerProps,
@@ -38,7 +41,8 @@ export default {
3841

3942
const Template = (args: NotificationConfirmationContainerProps) => {
4043
const headerMessage = getConfirmationHeaderMessage(args.i18n, args.type, args.error);
41-
return NotificationConfirmationContainer({ ...args, headerMessage });
44+
const notificationTestId = getNotificationTestId(args.type, true);
45+
return NotificationConfirmationContainer({ ...args, headerMessage, notificationTestId });
4246
};
4347

4448
export const Default: StoryObj<NotificationConfirmationContainerProps> = {

apps/browser/src/autofill/content/components/lit-stories/notification/container.lit-stories.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { CipherType } from "@bitwarden/common/vault/enums";
55
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
66

77
import { NotificationTypes } from "../../../../notification/abstractions/notification-bar";
8-
import { getNotificationHeaderMessage } from "../../../../notification/bar";
8+
import { getNotificationHeaderMessage, getNotificationTestId } from "../../../../notification/bar";
99
import { NotificationContainer, NotificationContainerProps } from "../../notification/container";
1010
import { mockBrowserI18nGetMessage, mockI18n } from "../mock-data";
1111

@@ -49,7 +49,8 @@ export default {
4949

5050
const Template = (args: NotificationContainerProps) => {
5151
const headerMessage = getNotificationHeaderMessage(args.i18n, args.type);
52-
return NotificationContainer({ ...args, headerMessage });
52+
const notificationTestId = getNotificationTestId(args.type);
53+
return NotificationContainer({ ...args, headerMessage, notificationTestId });
5354
};
5455

5556
export const Default: StoryObj<NotificationContainerProps> = {

apps/browser/src/autofill/content/components/lit-stories/options-selection/option-selection.lit-stories.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const mockOptions: Option[] = [
1616

1717
type ComponentProps = {
1818
disabled?: boolean;
19+
id: string;
1920
label?: string;
2021
options: Option[];
2122
theme: Theme;
@@ -31,16 +32,18 @@ export default {
3132
},
3233
args: {
3334
disabled: false,
35+
id: "example-id",
3436
label: undefined,
3537
options: mockOptions,
3638
theme: ThemeTypes.Light,
3739
},
3840
} as Meta<ComponentProps>;
3941

40-
const BaseComponent = ({ disabled, label, options, theme }: ComponentProps) => {
42+
const BaseComponent = ({ disabled, id, label, options, theme }: ComponentProps) => {
4143
return html`
4244
<option-selection
4345
.disabled=${disabled}
46+
.id=${id}
4447
.label="${label}"
4548
.options=${options}
4649
theme=${theme}

apps/browser/src/autofill/content/components/notification/confirmation/container.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export type NotificationConfirmationContainerProps = NotificationBarIframeInitDa
2828
headerMessage?: string;
2929
i18n: I18n;
3030
itemName: string;
31+
notificationTestId: string;
3132
task?: NotificationTaskInfo;
3233
type: NotificationType;
3334
};
@@ -40,6 +41,7 @@ export function NotificationConfirmationContainer({
4041
headerMessage,
4142
i18n,
4243
itemName,
44+
notificationTestId,
4345
task,
4446
theme = ThemeTypes.Light,
4547
type,
@@ -68,7 +70,7 @@ export function NotificationConfirmationContainer({
6870
}
6971

7072
return html`
71-
<div class=${notificationContainerStyles(theme)}>
73+
<div data-testid="${notificationTestId}" class=${notificationContainerStyles(theme)}>
7274
${NotificationHeader({
7375
handleCloseNotification,
7476
i18n,

apps/browser/src/autofill/content/components/notification/container.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export type NotificationContainerProps = NotificationBarIframeInitData & {
3131
i18n: I18n;
3232
organizations?: OrgView[];
3333
personalVaultIsAllowed?: boolean;
34+
notificationTestId: string;
3435
type: NotificationType; // @TODO typing override for generic `NotificationBarIframeInitData.type`
3536
};
3637

@@ -45,13 +46,14 @@ export function NotificationContainer({
4546
i18n,
4647
organizations,
4748
personalVaultIsAllowed = true,
49+
notificationTestId,
4850
theme = ThemeTypes.Light,
4951
type,
5052
}: NotificationContainerProps) {
5153
const showBody = type !== NotificationTypes.Unlock;
5254

5355
return html`
54-
<div class=${notificationContainerStyles(theme)}>
56+
<div data-testid="${notificationTestId}" class=${notificationContainerStyles(theme)}>
5557
${NotificationHeader({
5658
handleCloseNotification,
5759
i18n,

apps/browser/src/autofill/content/components/option-selection/option-item.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ const { css } = createEmotion({
1313
});
1414

1515
export type OptionItemProps = Option & {
16+
id: string;
1617
contextLabel?: string;
1718
theme: Theme;
1819
handleSelection: () => void;
1920
};
2021

2122
export function OptionItem({
2223
contextLabel,
24+
id,
2325
icon,
2426
text,
2527
theme,
@@ -44,6 +46,7 @@ export function OptionItem({
4446

4547
return html`<div
4648
class=${optionItemStyles}
49+
data-testid="${id}-option-item"
4750
key=${value}
4851
tabindex="0"
4952
title=${text}

apps/browser/src/autofill/content/components/option-selection/option-items.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const { css } = createEmotion({
1515
});
1616

1717
export type OptionItemsProps = {
18+
id: string;
1819
theme: Theme;
1920
topOffset: number;
2021
label?: string;
@@ -23,6 +24,7 @@ export type OptionItemsProps = {
2324
};
2425

2526
export function OptionItems({
27+
id,
2628
theme,
2729
topOffset,
2830
label,
@@ -42,6 +44,7 @@ export function OptionItems({
4244
<div class=${optionsWrapper({ isSafari, theme })}>
4345
${options.map((option) =>
4446
OptionItem({
47+
id,
4548
...option,
4649
theme,
4750
contextLabel: label,

apps/browser/src/autofill/content/components/option-selection/option-selection.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ export class OptionSelection extends LitElement {
2020
@property()
2121
disabled: boolean = false;
2222

23+
@property()
24+
id: string = "";
25+
2326
@property()
2427
label?: string;
2528

@@ -130,13 +133,15 @@ export class OptionSelection extends LitElement {
130133
${OptionSelectionButton({
131134
disabled: this.disabled,
132135
icon: this.selection?.icon,
136+
id: this.id,
133137
text: this.selection?.text,
134138
theme: this.theme,
135139
toggledOn: this.showMenu,
136140
handleButtonClick: this.handleButtonClick,
137141
})}
138142
${this.showMenu
139143
? OptionItems({
144+
id: this.id,
140145
label: this.label,
141146
options: this.options,
142147
theme: this.theme,

apps/browser/src/autofill/content/components/rows/button-row.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export function ButtonRow({ theme, primaryButton, selectButtons }: ButtonRowProp
3838
<option-selection
3939
key=${id}
4040
theme=${theme}
41+
.id=${id}
4142
.label=${label}
4243
.options=${options}
4344
.handleSelectionUpdate=${handleSelectionUpdate}

apps/browser/src/autofill/notification/bar.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,27 @@ function resolveNotificationType(initData: NotificationBarIframeInitData): Notif
175175
return initData.type as NotificationType;
176176
}
177177

178+
/**
179+
* Returns the appropriate test ID based on the resolved notification type.
180+
*
181+
* @param type - The resolved NotificationType.
182+
* @param isConfirmation - Optional flag for confirmation vs. notification container.
183+
*/
184+
export function getNotificationTestId(
185+
notificationType: NotificationType,
186+
isConfirmation = false,
187+
): string {
188+
if (isConfirmation) {
189+
return "confirmation-notification-bar";
190+
}
191+
192+
return {
193+
[NotificationTypes.Unlock]: "unlock-notification-bar",
194+
[NotificationTypes.Add]: "save-notification-bar",
195+
[NotificationTypes.Change]: "update-notification-bar",
196+
}[notificationType];
197+
}
198+
178199
/**
179200
* Sets the text content of an element identified by ID within a template's content.
180201
*
@@ -212,6 +233,7 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
212233
if (useComponentBar) {
213234
const resolvedType = resolveNotificationType(notificationBarIframeInitData);
214235
const headerMessage = getNotificationHeaderMessage(i18n, resolvedType);
236+
const notificationTestId = getNotificationTestId(resolvedType);
215237
appendHeaderMessageToTitle(headerMessage);
216238

217239
document.body.innerHTML = "";
@@ -224,6 +246,7 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
224246
...notificationBarIframeInitData,
225247
headerMessage,
226248
type: resolvedType,
249+
notificationTestId,
227250
theme: resolvedTheme,
228251
personalVaultIsAllowed: !personalVaultDisallowed,
229252
handleCloseNotification,
@@ -269,6 +292,7 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
269292
headerMessage,
270293
type: resolvedType,
271294
theme: resolvedTheme,
295+
notificationTestId,
272296
personalVaultIsAllowed: !personalVaultDisallowed,
273297
handleCloseNotification,
274298
handleSaveAction,
@@ -499,23 +523,25 @@ function handleSaveCipherConfirmation(message: NotificationBarWindowMessage) {
499523
const resolvedTheme = getResolvedTheme(theme ?? ThemeTypes.Light);
500524
const resolvedType = resolveNotificationType(notificationBarIframeInitData);
501525
const headerMessage = getConfirmationHeaderMessage(i18n, resolvedType, error);
526+
const notificationTestId = getNotificationTestId(resolvedType, true);
502527

503528
globalThis.setTimeout(() => sendPlatformMessage({ command: "bgCloseNotificationBar" }), 5000);
504529

505530
return render(
506531
NotificationConfirmationContainer({
507532
...notificationBarIframeInitData,
508-
type: type as NotificationType,
509-
theme: resolvedTheme,
533+
error,
510534
handleCloseNotification,
535+
handleOpenTasks: () => sendPlatformMessage({ command: "bgOpenAtRisksPasswords" }),
536+
handleOpenVault: (e: Event) =>
537+
cipherId ? openViewVaultItemPopout(cipherId) : openAddEditVaultItemPopout(e, {}),
511538
headerMessage,
512539
i18n,
513-
error,
514540
itemName: itemName ?? i18n.typeLogin,
541+
notificationTestId,
515542
task,
516-
handleOpenVault: (e: Event) =>
517-
cipherId ? openViewVaultItemPopout(cipherId) : openAddEditVaultItemPopout(e, {}),
518-
handleOpenTasks: () => sendPlatformMessage({ command: "bgOpenAtRisksPasswords" }),
543+
theme: resolvedTheme,
544+
type: type as NotificationType,
519545
}),
520546
document.body,
521547
);

0 commit comments

Comments
 (0)