Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
import { fireEvent, render } from "@testing-library/react";
import { INITIAL_STATE } from "common/Reducers.sys.mjs";
import { actionTypes as at } from "common/Actions.mjs";
import { MozWidgetCard } from "content-src/components/Widgets/MozWidgetCard";
import { WrapWithProvider } from "test/jest/test-utils";
const ENTRY = {
id: "extensionWidget",
telemetryName: "extension_widget",
enabledPref: "widgets.extensionWidget.enabled",
sizePref: "widgets.extensionWidget.size",
validSizes: ["medium", "large"],
extensionID: "fixture-widget@mozilla.org",
name: "Tally",
};
function renderCard(entry = {}) {
const dispatch = jest.fn();
const { container } = render(
<WrapWithProvider state={INITIAL_STATE}>
<MozWidgetCard
dispatch={dispatch}
entry={{ ...ENTRY, ...entry }}
size="medium"
widgetEnabledMap={{}}
/>
</WrapWithProvider>
);
const card = container.querySelector("moz-widget");
const header = card.querySelector("moz-widget-header");
const menu = card.querySelector(':scope > panel-list[slot="menu"]');
return { card, dispatch, header, menu };
}
function findUserEvent(dispatch, userAction) {
return dispatch.mock.calls.find(
([action]) =>
action.type === at.WIDGETS_USER_EVENT &&
action.data.user_action === userAction
)?.[0];
}
function menuItems(menu) {
return [...menu.children].map(el => el.dataset.l10nId ?? el.localName);
}
describe("<MozWidgetCard>", () => {
it("renders the shared widget items", () => {
const { menu } = renderCard();
expect(menuItems(menu)).toEqual([
"panel-item",
"newtab-widget-menu-hide",
"newtab-widget-section-menu-learn-more",
]);
});
it("localizes the card and header with the entry's l10nId", () => {
const { card, header } = renderCard({ l10nId: "tally-name" });
expect(card).toHaveAttribute("data-l10n-id", "tally-name");
expect(header).toHaveAttribute("data-l10n-id", "tally-name");
});
it("titles the card with the widget's name", () => {
const { card } = renderCard();
expect(card).toHaveAttribute("title", "Tally");
});
it("renders only Hide and Learn more without sizes", () => {
const { menu } = renderCard({ validSizes: ["large"] });
expect(menuItems(menu)).toEqual([
"newtab-widget-menu-hide",
"newtab-widget-section-menu-learn-more",
]);
});
it("writes the size pref and records change_size", () => {
const { dispatch, menu } = renderCard();
fireEvent.click(menu.querySelector("panel-item[data-size='large']"));
expect(dispatch).toHaveBeenCalledWith(
expect.objectContaining({
type: at.SET_PREF,
data: { name: ENTRY.sizePref, value: "large" },
})
);
expect(findUserEvent(dispatch, "change_size").data).toMatchObject({
widget_name: "extension_widget",
widget_source: "context_menu",
action_value: "large",
widget_size: "large",
});
});
it("records learn_more", () => {
const { dispatch, menu } = renderCard();
fireEvent.click(
menu.querySelector(
"panel-item[data-l10n-id='newtab-widget-section-menu-learn-more']"
)
);
expect(findUserEvent(dispatch, "learn_more").data).toMatchObject({
widget_name: "extension_widget",
widget_source: "context_menu",
});
});
describe("impression telemetry", () => {
let originalIntersectionObserver;
let observerInstances;
beforeEach(() => {
observerInstances = [];
originalIntersectionObserver = global.IntersectionObserver;
global.IntersectionObserver = class MockIntersectionObserver {
constructor(callback) {
this.callback = callback;
this.observed = [];
observerInstances.push(this);
}
observe(el) {
this.observed.push(el);
}
unobserve() {}
disconnect() {}
};
});
afterEach(() => {
global.IntersectionObserver = originalIntersectionObserver;
});
it("records an impression of the card", () => {
const { card, dispatch } = renderCard();
const [observer] = observerInstances;
expect(observer.observed).toEqual([card]);
observer.callback([{ isIntersecting: true, target: card }], observer);
expect(dispatch).toHaveBeenCalledWith(
expect.objectContaining({
type: at.WIDGETS_IMPRESSION,
data: { widget_name: "extension_widget", widget_size: "medium" },
})
);
});
});
});