Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`OverlayNotificationsContentService opening the notification bar creates the notification bar elements and appends them to the body 1`] = `
exports[`OverlayNotificationsContentService opening the notification bar creates the notification bar elements and appends them to the body within a shadow root 1`] = `
<div
id="bit-notification-bar"
style="height: 400px !important; width: 430px !important; max-width: calc(100% - 20px) !important; min-height: initial !important; top: 10px !important; right: 0px !important; padding: 0px !important; position: fixed !important; z-index: 2147483647 !important; visibility: visible !important; border-radius: 4px !important; background-color: transparent !important; overflow: hidden !important; transition: box-shadow 0.15s ease !important; transition-delay: 0.15s;"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ describe("OverlayNotificationsContentService", () => {
let domElementVisibilityService: DomElementVisibilityService;
let autofillInit: AutofillInit;
let bodyAppendChildSpy: jest.SpyInstance;
let postMessageSpy: jest.SpyInstance<void, Parameters<Window["postMessage"]>>;

beforeEach(() => {
jest.useFakeTimers();
jest.spyOn(utils, "sendExtensionMessage").mockImplementation(jest.fn());
jest.spyOn(HTMLIFrameElement.prototype, "contentWindow", "get").mockReturnValue(window);
postMessageSpy = jest.spyOn(window, "postMessage").mockImplementation(jest.fn());
domQueryService = mock<DomQueryService>();
domElementVisibilityService = new DomElementVisibilityService();
overlayNotificationsContentService = new OverlayNotificationsContentService();
Expand Down Expand Up @@ -48,7 +51,7 @@ describe("OverlayNotificationsContentService", () => {
});

it("closes the notification bar if the notification bar type has changed", async () => {
overlayNotificationsContentService["currentNotificationBarType"] = "add";
overlayNotificationsContentService["currentNotificationBarType"] = NotificationType.AddLogin;
const closeNotificationBarSpy = jest.spyOn(
overlayNotificationsContentService as any,
"closeNotificationBar",
Expand All @@ -66,7 +69,7 @@ describe("OverlayNotificationsContentService", () => {
expect(closeNotificationBarSpy).toHaveBeenCalled();
});

it("creates the notification bar elements and appends them to the body", async () => {
it("creates the notification bar elements and appends them to the body within a shadow root", async () => {
sendMockExtensionMessage({
command: "openNotificationBar",
data: {
Expand All @@ -77,6 +80,13 @@ describe("OverlayNotificationsContentService", () => {
await flushPromises();

expect(overlayNotificationsContentService["notificationBarElement"]).toMatchSnapshot();

const rootElement = overlayNotificationsContentService["notificationBarRootElement"];
expect(bodyAppendChildSpy).toHaveBeenCalledWith(rootElement);
expect(rootElement?.tagName).toBe("BIT-NOTIFICATION-BAR-ROOT");

expect(document.getElementById("bit-notification-bar")).toBeNull();
expect(document.querySelector("#bit-notification-bar-iframe")).toBeNull();
});

it("sets up a slide in animation when the notification is fresh", async () => {
Expand Down Expand Up @@ -116,6 +126,8 @@ describe("OverlayNotificationsContentService", () => {
});

it("sends an initialization message to the notification bar iframe", async () => {
const addEventListenerSpy = jest.spyOn(globalThis, "addEventListener");

sendMockExtensionMessage({
command: "openNotificationBar",
data: {
Expand All @@ -124,10 +136,7 @@ describe("OverlayNotificationsContentService", () => {
},
});
await flushPromises();
const postMessageSpy = jest.spyOn(
overlayNotificationsContentService["notificationBarIframeElement"].contentWindow,
"postMessage",
);
expect(addEventListenerSpy).toHaveBeenCalledWith("message", expect.any(Function));

globalThis.dispatchEvent(
new MessageEvent("message", {
Expand All @@ -142,7 +151,6 @@ describe("OverlayNotificationsContentService", () => {
);
await flushPromises();

expect(postMessageSpy).toHaveBeenCalledTimes(1);
expect(postMessageSpy).toHaveBeenCalledWith(
{
command: "initNotificationBar",
Expand All @@ -158,7 +166,7 @@ describe("OverlayNotificationsContentService", () => {
sendMockExtensionMessage({
command: "openNotificationBar",
data: {
type: "change",
type: NotificationType.ChangePassword,
typeData: mock<NotificationTypeData>(),
},
});
Expand Down Expand Up @@ -242,20 +250,15 @@ describe("OverlayNotificationsContentService", () => {
});

it("sends a message to the notification bar iframe indicating that the save attempt completed", () => {
jest.spyOn(
overlayNotificationsContentService["notificationBarIframeElement"].contentWindow,
"postMessage",
);

sendMockExtensionMessage({
command: "saveCipherAttemptCompleted",
data: { error: undefined },
});

expect(
overlayNotificationsContentService["notificationBarIframeElement"].contentWindow
.postMessage,
).toHaveBeenCalledWith({ command: "saveCipherAttemptCompleted", error: undefined }, "*");
expect(postMessageSpy).toHaveBeenCalledWith(
{ command: "saveCipherAttemptCompleted", error: undefined },
"*",
);
});
});

Expand All @@ -271,9 +274,10 @@ describe("OverlayNotificationsContentService", () => {
await flushPromises();
});

it("triggers a closure of the notification bar", () => {
it("triggers a closure of the notification bar and cleans up all shadow DOM elements", () => {
overlayNotificationsContentService.destroy();

expect(overlayNotificationsContentService["notificationBarRootElement"]).toBeNull();
expect(overlayNotificationsContentService["notificationBarElement"]).toBeNull();
expect(overlayNotificationsContentService["notificationBarIframeElement"]).toBeNull();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ import {
export class OverlayNotificationsContentService
implements OverlayNotificationsContentServiceInterface
{
private notificationBarRootElement: HTMLElement | null = null;
private notificationBarElement: HTMLElement | null = null;
private notificationBarIframeElement: HTMLIFrameElement | null = null;
private notificationBarShadowRoot: ShadowRoot | null = null;
private currentNotificationBarType: NotificationType | null = null;
private notificationBarContainerStyles: Partial<CSSStyleDeclaration> = {
height: "400px",
Expand Down Expand Up @@ -158,12 +160,12 @@ export class OverlayNotificationsContentService
* @private
*/
private openNotificationBar(initData: NotificationBarIframeInitData) {
if (!this.notificationBarElement && !this.notificationBarIframeElement) {
if (!this.notificationBarRootElement && !this.notificationBarIframeElement) {
this.createNotificationBarIframeElement(initData);
this.createNotificationBarElement();

this.setupInitNotificationBarMessageListener(initData);
globalThis.document.body.appendChild(this.notificationBarElement);
globalThis.document.body.appendChild(this.notificationBarRootElement);
}
}

Expand Down Expand Up @@ -213,15 +215,25 @@ export class OverlayNotificationsContentService
};

/**
* Creates the container for the notification bar iframe.
* Creates the container for the notification bar iframe with shadow DOM.
*/
private createNotificationBarElement() {
if (this.notificationBarIframeElement) {
this.notificationBarRootElement = globalThis.document.createElement(
"bit-notification-bar-root",
);

this.notificationBarShadowRoot = this.notificationBarRootElement.attachShadow({
mode: "closed",
delegatesFocus: true,
});

this.notificationBarElement = globalThis.document.createElement("div");
this.notificationBarElement.id = "bit-notification-bar";

setElementStyles(this.notificationBarElement, this.notificationBarContainerStyles, true);

this.notificationBarShadowRoot.appendChild(this.notificationBarElement);
this.notificationBarElement.appendChild(this.notificationBarIframeElement);
}
}
Expand Down Expand Up @@ -258,7 +270,7 @@ export class OverlayNotificationsContentService
* @param closedByUserAction - Whether the notification bar was closed by the user.
*/
private closeNotificationBar(closedByUserAction: boolean = false) {
if (!this.notificationBarElement && !this.notificationBarIframeElement) {
if (!this.notificationBarRootElement && !this.notificationBarIframeElement) {
return;
}

Expand All @@ -267,6 +279,9 @@ export class OverlayNotificationsContentService

this.notificationBarElement.remove();
this.notificationBarElement = null;
this.notificationBarShadowRoot = null;
this.notificationBarRootElement.remove();
this.notificationBarRootElement = null;

const removableNotificationTypes = new Set([
NotificationTypes.Add,
Expand Down
Loading