Skip to content
Open
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
7 changes: 5 additions & 2 deletions bin/ss-pages.test.stub
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ describe( 'Starter Sites VR - {{site}}', () => {
let frontpage = "{{site}}"
cy.visit(frontpage );
cy.captureDocument();
cy.get( '#nv-primary-navigation-main' ).then( $headerMenu => {
cy.get( '[id^="nv-primary-navigation"]' ).then( $headerMenu => {
[ ...$headerMenu.find( '.menu-item a' ) ].forEach( $url => {
let url = $url.href;
if(url.includes("#")){
Expand All @@ -13,8 +13,11 @@ describe( 'Starter Sites VR - {{site}}', () => {
if(frontpage.replace(/\/*$/, "") === url.replace(/\/*$/, "")){
return;
}
if(pages.includes(url)){
return;
}

pages.push( $url.href );
pages.push( url );
} );
} );
} );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"hfg_footer_layout_v2": "{\"desktop\":{\"top\":{\"left\":[],\"c-left\":[],\"center\":[],\"c-right\":[],\"right\":[]},\"main\":{\"left\":[],\"c-left\":[],\"center\":[],\"c-right\":[],\"right\":[]},\"bottom\":{\"left\":[],\"c-left\":[],\"center\":[{\"id\":\"footer-menu\"}],\"c-right\":[],\"right\":[]}},\"mobile\":{\"top\":{\"left\":[],\"c-left\":[],\"center\":[{\"id\":\"footer-menu\"}],\"c-right\":[],\"right\":[]},\"main\":{\"left\":[],\"c-left\":[],\"center\":[],\"c-right\":[],\"right\":[]},\"bottom\":{\"left\":[],\"c-left\":[],\"center\":[],\"c-right\":[],\"right\":[]}}}",
"nav_menu_locations[footer]": 177
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"hfg_header_layout_v2": "{\"desktop\":{\"top\":{\"left\":[],\"c-left\":[],\"center\":[],\"c-right\":[],\"right\":[]},\"main\":{\"left\":[{\"id\":\"logo\"}],\"c-left\":[],\"center\":[],\"c-right\":[],\"right\":[{\"id\":\"primary-menu\"}]},\"bottom\":{\"left\":[],\"c-left\":[],\"center\":[],\"c-right\":[],\"right\":[]}},\"mobile\":{\"top\":{\"left\":[],\"c-left\":[],\"center\":[],\"c-right\":[],\"right\":[]},\"main\":{\"left\":[{\"id\":\"logo\"}],\"c-left\":[],\"center\":[],\"c-right\":[],\"right\":[{\"id\":\"primary-menu\"},{\"id\":\"nav-icon\"}]},\"bottom\":{\"left\":[],\"c-left\":[],\"center\":[],\"c-right\":[],\"right\":[]},\"sidebar\":[{\"id\":\"primary-menu\"}]}}",
"nav_menu_locations[primary]": 177
}
153 changes: 151 additions & 2 deletions e2e-tests/specs/customizer/hfg/hfg-footer-menu-component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
import { test, expect, Page, Locator } from '@playwright/test';
import { setCustomizeSettings } from '../../../utils';
import { test, expect, Page, Locator, Frame } from '@playwright/test';
import { loginWithRequest, setCustomizeSettings } from '../../../utils';
import data from '../../../fixtures/customizer/hfg/footer-menu-setup.json';
import menuData from '../../../fixtures/customizer/hfg/footer-menu-both-devices-setup.json';

const PREVIEW_FRAME = 'iframe[name="customize-preview-0"]';

/**
* Collect the ids of every footer menu list in a document.
*
* @param {Frame} frame Frame to read from.
*/
const footerMenuIds = (frame: Frame): Promise<string[]> =>
frame.evaluate(() =>
Array.from(document.querySelectorAll('.hfg_footer ul.footer-menu')).map(
(el) => el.id
)
);

test.describe('Footer Menu component', function () {
let page: Page;
Expand Down Expand Up @@ -35,3 +50,137 @@ test.describe('Footer Menu component', function () {
}
});
});

test.describe(
'Footer Menu component in both desktop and mobile layouts',
function () {
test.beforeAll(async ({ request, baseURL }) => {
await setCustomizeSettings('hfgFooterMenuBothDevices', menuData, {
request,
baseURL,
});
});

test('Both layout variants are rendered', async ({ page }) => {
await page.goto('/?test_name=hfgFooterMenuBothDevices');

await expect(
page.locator(
'.hfg_footer .footer--row[data-show-on="desktop"] .nav-menu-footer'
)
).toHaveCount(1);

await expect(
page.locator(
'.hfg_footer .footer--row[data-show-on="mobile"] .nav-menu-footer'
)
).toHaveCount(1);
});

test('Menu IDs are unique across the whole document', async ({
page,
}) => {
await page.goto('/?test_name=hfgFooterMenuBothDevices');

await expect(page.locator('ul#footer-menu')).toHaveCount(0);
await expect(
page.locator('ul#footer-menu-desktop-bottom')
).toHaveCount(1);
await expect(page.locator('ul#footer-menu-mobile-top')).toHaveCount(
1
);

const duplicated = await page.evaluate(() => {
const ids = Array.from(
document.querySelectorAll('.hfg_footer [id]')
).map((el) => el.id);

return ids.filter((id, index) => ids.indexOf(id) !== index);
});

expect(duplicated).toEqual([]);
});

test('The footer-menu class is kept for styling', async ({ page }) => {
await page.goto('/?test_name=hfgFooterMenuBothDevices');

await expect(
page.locator('.hfg_footer ul.footer-menu')
).toHaveCount(2);
});

test('Menu IDs survive a selective refresh of the component', async ({
page,
}) => {
const previewUrl = '/?test_name=hfgFooterMenuBothDevices';
await loginWithRequest(
'/wp-admin/customize.php?url=' + encodeURIComponent(previewUrl),
page
);

await page.waitForSelector('.wp-full-overlay-sidebar', {
state: 'visible',
});

const preview = page.frameLocator(PREVIEW_FRAME);
await preview
.locator('.hfg_footer ul.footer-menu')
.first()
.waitFor();

const frame = page.frame({ name: 'customize-preview-0' });
if (!frame) {
throw new Error('Customizer preview frame not found.');
}

// Every placement has to advertise where it sits, otherwise selective refresh
// renders the partial once and copies that markup into all of them.
const contexts = await frame.evaluate(() =>
Array.from(
document.querySelectorAll('.builder-item--footer-menu')
).map((el) =>
el.getAttribute('data-customize-partial-placement-context')
)
);
expect(contexts).toEqual([
'{"device":"desktop","row":"bottom"}',
'{"device":"mobile","row":"top"}',
]);

const before = await footerMenuIds(frame);
expect(before).toEqual([
'footer-menu-desktop-bottom',
'footer-menu-mobile-top',
]);

// Changing a footer-menu setting refreshes the component partial.
await page.evaluate(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).wp
.customize('footer-menu_style')
.set('style-border-bottom');
});

// The style class only lands through a re-render, so this also proves the
// refresh actually ran before the ids are re-checked.
await expect(
preview
.locator(
'.footer--row[data-show-on="desktop"] .nav-menu-footer'
)
.first()
).toHaveClass(/style-border-bottom/);
await expect(
preview
.locator(
'.footer--row[data-show-on="mobile"] .nav-menu-footer'
)
.first()
).toHaveClass(/style-border-bottom/);

const after = await footerMenuIds(frame);
expect(after).toEqual(before);
await expect(preview.locator('ul#footer-menu')).toHaveCount(0);
});
}
);
127 changes: 127 additions & 0 deletions e2e-tests/specs/customizer/hfg/hfg-header-menu-component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { test, expect, Frame } from '@playwright/test';
import { loginWithRequest, setCustomizeSettings } from '../../../utils';
import data from '../../../fixtures/customizer/hfg/primary-menu-both-devices-setup.json';

const PREVIEW_FRAME = 'iframe[name="customize-preview-0"]';

/**
* Collect the ids of every primary menu list in a document.
*
* @param {Frame} frame Frame to read from.
*/
const primaryMenuIds = (frame: Frame): Promise<string[]> =>
frame.evaluate(() =>
Array.from(
document.querySelectorAll('.hfg_header ul.primary-menu-ul')
).map((el) => el.id)
);

test.describe(
'Primary Menu component in both desktop and mobile layouts',
function () {
test.beforeAll(async ({ request, baseURL }) => {
await setCustomizeSettings('hfgPrimaryMenuBothDevices', data, {
request,
baseURL,
});
});

test('Menu IDs are unique across the whole document', async ({
page,
}) => {
await page.goto('/?test_name=hfgPrimaryMenuBothDevices');

// The row alone is not enough to tell the placements apart: both layouts
// use the same row names.
await expect(
page.locator('ul#nv-primary-navigation-main')
).toHaveCount(0);

await expect(
page.locator('ul#nv-primary-navigation-desktop-main')
).toHaveCount(1);
await expect(
page.locator('ul#nv-primary-navigation-mobile-main')
).toHaveCount(1);
await expect(
page.locator('ul#nv-primary-navigation-mobile-sidebar')
).toHaveCount(1);

const duplicated = await page.evaluate(() => {
const ids = Array.from(
document.querySelectorAll('.hfg_header [id]')
).map((el) => el.id);

return ids.filter((id, index) => ids.indexOf(id) !== index);
});

expect(duplicated).toEqual([]);
});

test('Menu IDs survive a selective refresh of the component', async ({
page,
}) => {
const previewUrl = '/?test_name=hfgPrimaryMenuBothDevices';
await loginWithRequest(
'/wp-admin/customize.php?url=' + encodeURIComponent(previewUrl),
page
);

await page.waitForSelector('.wp-full-overlay-sidebar', {
state: 'visible',
});

const preview = page.frameLocator(PREVIEW_FRAME);
await preview
.locator('.hfg_header ul.primary-menu-ul')
.first()
.waitFor();

const frame = page.frame({ name: 'customize-preview-0' });
if (!frame) {
throw new Error('Customizer preview frame not found.');
}

const before = await primaryMenuIds(frame);
expect(before).toEqual([
'nv-primary-navigation-desktop-main',
'nv-primary-navigation-mobile-main',
'nv-primary-navigation-mobile-sidebar',
]);

// Every placement has to advertise where it sits, otherwise selective refresh
// renders the partial once and copies that markup into all of them.
const contexts = await frame.evaluate(() =>
Array.from(
document.querySelectorAll('.builder-item--primary-menu')
).map((el) =>
el.getAttribute('data-customize-partial-placement-context')
)
);
expect(contexts).toEqual([
'{"device":"desktop","row":"main"}',
'{"device":"mobile","row":"main"}',
'{"device":"mobile","row":"sidebar"}',
]);

// Refresh the partial and check that all placements were refreshed and that the menu ids are still unique.
const refreshed = await frame.evaluate(async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const sr = (window as any).wp.customize.selectiveRefresh;
const partial = sr.partial('primary-menu_partial');
if (!partial) {
throw new Error('primary-menu_partial is not registered.');
}
const placements = await partial.refresh();

return placements.length;
});
expect(refreshed).toBe(3);

expect(await primaryMenuIds(frame)).toEqual(before);
await expect(
preview.locator('ul#nv-primary-navigation-main')
).toHaveCount(0);
});
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,23 @@ test.describe('Menu item alignment', function () {
await page.locator('.mobile-left .navbar-toggle').click();
await expect(
page.locator(
'#nv-primary-navigation-sidebar .menu-item-title-wrap:has-text("About The Tests")'
'#nv-primary-navigation-mobile-sidebar .menu-item-title-wrap:has-text("About The Tests")'
)
).toHaveCSS('text-align', 'left');

// Located by label: menu item IDs are DB auto-increments and differ
// between a fresh CI import and a long-lived local database.
await page
.locator('#nv-primary-navigation-sidebar > li.menu-item-has-children')
.locator(
'#nv-primary-navigation-mobile-sidebar > li.menu-item-has-children'
)
.filter({ hasText: 'Level 1' })
.locator('.caret-wrap')
.first()
.click();
await expect(
page.locator(
'#nv-primary-navigation-sidebar .menu-item-title-wrap:has-text("Level 2")'
'#nv-primary-navigation-mobile-sidebar .menu-item-title-wrap:has-text("Level 2")'
)
).toHaveCSS('text-align', 'left');
});
Expand Down
4 changes: 2 additions & 2 deletions e2e-tests/specs/customizer/hfg/hfg-menu-item-wrap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ test.describe('Menu item alignment', function () {
.click();

const firstLevelItem = page
.locator('#nv-primary-navigation-sidebar')
.locator('#nv-primary-navigation-mobile-sidebar')
.getByRole('link', {
name: 'Page Markup And Formatting',
});
Expand All @@ -23,7 +23,7 @@ test.describe('Menu item alignment', function () {
await page.getByRole('button', { name: 'Toggle Level 2' }).click();

const secondLevelItem = page
.locator('#nv-primary-navigation-sidebar')
.locator('#nv-primary-navigation-mobile-sidebar')
.getByRole('link', {
name: 'Level 3b',
});
Expand Down
Loading
Loading