import { render, RenderResult } from '@stencil/vitest';
import OntarioHeaderDefaultData from '../ontario-header-default-data.json';
import { default as translations } from '../../../translations/global.i18n.json';
// Used to help with Stencil Component Lifecycles
// the _ variables denote unsued variables
// easier than using ESLint and TypeScript disable comments
globalThis.MutationObserver = class {
constructor(_callback: MutationCallback) {}
observe(_target: Node, _options?: MutationObserverInit) {}
disconnect() {}
takeRecords(): MutationRecord[] {
return [];
}
} as typeof MutationObserver;
describe('ontario-header', () => {
let page: RenderResult;
let host: HTMLOntarioHeaderElement;
let hostShadow: ShadowRoot;
beforeEach(async () => {
page = await render(``);
host = page.root as HTMLOntarioHeaderElement;
hostShadow = host.shadowRoot as ShadowRoot;
await page.waitForChanges();
});
it('should render', async () => {
expect(host).not.toBeNull();
expect(host.tagName).toBe('ONTARIO-HEADER');
expect(hostShadow).not.toBeNull();
});
it('should render child components', async () => {
const page = await render(``);
await page.waitForChanges();
const host = page.root as HTMLOntarioHeaderElement;
const hostShadow = host.shadowRoot as ShadowRoot;
// Find the child language toggle component inside the shadow root
const childLangToggle = hostShadow.querySelector('ontario-language-toggle') as HTMLOntarioLanguageToggleElement;
expect(childLangToggle).not.toBeNull();
expect(childLangToggle.tagName).toBe('ONTARIO-LANGUAGE-TOGGLE');
// Ensure the child language toggle component shadow root is present
const childLangToggleShadow = childLangToggle.shadowRoot as ShadowRoot;
expect(childLangToggleShadow).not.toBeNull();
// Ensure the hild language toggle component has expected html
const childLangToggleAnchor = childLangToggleShadow.querySelector('.ontario-language-toggler') as HTMLElement;
expect(childLangToggleAnchor).not.toBeNull();
expect(childLangToggleAnchor.textContent).toBe(translations.siteLanguage.fullWord.fr);
// Find the child overflow menu component inside the shadow root
const childOverflowMenu = hostShadow.querySelector(
'ontario-header-overflow-menu',
) as HTMLOntarioHeaderOverflowMenuElement;
expect(childOverflowMenu).not.toBeNull();
expect(childOverflowMenu.tagName).toBe('ONTARIO-HEADER-OVERFLOW-MENU');
// Ensure the child overflow menu component shadow root is present
const childOverflowMenuShadow = childOverflowMenu.shadowRoot as ShadowRoot;
expect(childOverflowMenuShadow).not.toBeNull();
// Ensure the child overflow menu component has expected html
const childOverflowMenuNav = childOverflowMenuShadow.querySelector(
'.ontario-application-navigation',
) as HTMLElement;
expect(childOverflowMenuNav).not.toBeNull();
});
it('should render the ontario header', async () => {
host.type = 'ontario';
await page.waitForChanges();
expect(hostShadow).not.toBeNull();
expect(hostShadow.querySelectorAll('.ontario-header').length).toBe(1);
});
it('should render the ontario header menu items', async () => {
const page = await render(``);
await page.waitForChanges();
const host = page.root as HTMLOntarioHeaderElement;
const hostShadow = host.shadowRoot as ShadowRoot;
host.type = 'ontario';
await page.waitForChanges();
// Find the child component inside the shadow root
const childOverflowMenu = hostShadow.querySelector(
'ontario-header-overflow-menu',
) as HTMLOntarioHeaderOverflowMenuElement;
// Check if the menuItem data is set correctly in the menu overflow child component
const childOverflowMenuItemsData = childOverflowMenu.menuItems;
// Should match the number of ontario.ca global topics
expect(childOverflowMenuItemsData.length).toBe(14);
// Access the child's shadow root
const childOverflowMenuShadow = childOverflowMenu.shadowRoot as ShadowRoot;
const childOverflowMenuRenderedMenuItems = childOverflowMenuShadow.querySelectorAll(
'a.ontario-menu-item',
) as NodeListOf;
// Check to see the HTML output of the rendered menu items is as expected
expect(childOverflowMenuRenderedMenuItems.length).toBe(14);
expect(childOverflowMenuRenderedMenuItems[0].textContent?.trim()).toBe(OntarioHeaderDefaultData.en[0].title);
expect(childOverflowMenuRenderedMenuItems[0].href).toContain(OntarioHeaderDefaultData.en[0].href);
expect(childOverflowMenuRenderedMenuItems[13].textContent?.trim()).toBe(OntarioHeaderDefaultData.en[13].title);
expect(childOverflowMenuRenderedMenuItems[13].href).toContain(OntarioHeaderDefaultData.en[13].href);
});
it('should render the application header', async () => {
host.type = 'application';
await page.waitForChanges();
expect(hostShadow).not.toBeNull();
expect(hostShadow.querySelectorAll('.ontario-application-header').length).toBe(1);
});
it('should render the application header menu items', async () => {
const page = await render(``);
await page.waitForChanges();
const host = page.root as HTMLOntarioHeaderElement;
host.type = 'application';
host.menuItems = [
{ title: 'Item 1', href: '/item-1' },
{ title: 'Item 2', href: '/item-2' },
{ title: 'Item 3', href: '/item-3' },
];
await page.waitForChanges();
const hostShadow = host.shadowRoot as ShadowRoot;
// Find the child component inside the shadow root
const childOverflowMenu = hostShadow.querySelector(
'ontario-header-overflow-menu',
) as HTMLOntarioHeaderOverflowMenuElement;
// Check if the menuItem data is set correctly in the menu overflow child component
const childOverflowMenuItemsData = childOverflowMenu.menuItems;
// Should match the number of menu items set on the host
expect(childOverflowMenuItemsData.length).toBe(3);
// Access the child's shadow root
const childOverflowMenuShadow = childOverflowMenu.shadowRoot as ShadowRoot;
const childOverflowMenuRenderedMenuItems = childOverflowMenuShadow.querySelectorAll(
'a.ontario-menu-item',
) as NodeListOf;
// Check to see the HTML output of the rendered menu items is as expected
expect(childOverflowMenuRenderedMenuItems.length).toBe(3);
expect(childOverflowMenuRenderedMenuItems[0].textContent?.trim()).toBe('Item 1');
expect(childOverflowMenuRenderedMenuItems[0].href).toContain('/item-1');
expect(childOverflowMenuRenderedMenuItems[2].textContent?.trim()).toBe('Item 3');
expect(childOverflowMenuRenderedMenuItems[2].href).toContain('/item-3');
});
it('should render the ServiceOntario header', async () => {
host.type = 'serviceOntario';
await page.waitForChanges();
expect(hostShadow).not.toBeNull();
expect(hostShadow?.querySelectorAll('.ontario-service-subheader').length).toBe(1);
});
it('should call menu item onClickHandler for subheader menu item', async () => {
const onClickHandler = vi.fn((event: Event) => event.preventDefault());
host.type = 'application';
host.applicationHeaderInfo = {
title: 'Application',
href: '/application',
maxSubheaderLinks: { desktop: 1, tablet: 1, mobile: 1 },
};
host.menuItems = [
{ title: 'Subheader Item', href: '/subheader-item', onClickHandler },
{ title: 'Overflow Item', href: '/overflow-item' },
];
await page.waitForChanges();
const subheaderItem = hostShadow.querySelector(
'.ontario-application-subheader__menu a.ontario-menu-item',
) as HTMLAnchorElement;
expect(subheaderItem).not.toBeNull();
subheaderItem.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
expect(onClickHandler).toHaveBeenCalledTimes(1);
});
it('should call menu item onClickHandler for overflow menu item', async () => {
const onClickHandler = vi.fn((event: Event) => event.preventDefault());
const page = await render(``);
const host = page.root as HTMLOntarioHeaderElement;
const hostShadow = host.shadowRoot as ShadowRoot;
host.type = 'application';
host.applicationHeaderInfo = {
title: 'Application',
href: '/application',
maxSubheaderLinks: { desktop: 1, tablet: 1, mobile: 1 },
};
host.menuItems = [
{ title: 'Subheader Item', href: '/subheader-item' },
{ title: 'Overflow Item', href: '/overflow-item', onClickHandler },
];
await page.waitForChanges();
const childOverflowMenu = hostShadow.querySelector(
'ontario-header-overflow-menu',
) as HTMLOntarioHeaderOverflowMenuElement;
expect(childOverflowMenu).not.toBeNull();
const childOverflowMenuShadow = childOverflowMenu.shadowRoot as ShadowRoot;
const overflowItem = childOverflowMenuShadow.querySelector('a.ontario-menu-item') as HTMLAnchorElement;
expect(overflowItem).not.toBeNull();
overflowItem.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
expect(onClickHandler).toHaveBeenCalledTimes(1);
});
});