fix: close final portfolio quality gaps
This commit is contained in:
@@ -1,18 +1,18 @@
|
||||
import { expect, expectInsetFocusIndicator, test } from '../helpers/serve-dist';
|
||||
import { expect, expectInsetFocusIndicator, test } from "../helpers/serve-dist";
|
||||
|
||||
const navHrefs = [
|
||||
'#start',
|
||||
'#profile',
|
||||
'#experience',
|
||||
'#infrastructure',
|
||||
'#selected-builds',
|
||||
'#additional-work',
|
||||
'#toolkit',
|
||||
'#contact',
|
||||
"#start",
|
||||
"#profile",
|
||||
"#experience",
|
||||
"#infrastructure",
|
||||
"#selected-builds",
|
||||
"#additional-work",
|
||||
"#toolkit",
|
||||
"#contact",
|
||||
] as const;
|
||||
|
||||
const assertVisibleUnobscuredFocus = async (
|
||||
page: import('@playwright/test').Page,
|
||||
page: import("@playwright/test").Page,
|
||||
): Promise<void> => {
|
||||
await expect
|
||||
.poll(() =>
|
||||
@@ -34,7 +34,7 @@ const assertVisibleUnobscuredFocus = async (
|
||||
(active === topElement ||
|
||||
active.contains(topElement) ||
|
||||
topElement.contains(active)) &&
|
||||
style.outlineStyle !== 'none' &&
|
||||
style.outlineStyle !== "none" &&
|
||||
Number.parseFloat(style.outlineWidth) >= 2
|
||||
);
|
||||
}),
|
||||
@@ -42,119 +42,142 @@ const assertVisibleUnobscuredFocus = async (
|
||||
.toBe(true);
|
||||
};
|
||||
|
||||
test('tabs from the skip link through navigation and every contact action', async ({
|
||||
test("tabs from the skip link through navigation and every contact action", async ({
|
||||
page,
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.goto(distUrl, { waitUntil: 'domcontentloaded' });
|
||||
}, testInfo) => {
|
||||
test.skip(
|
||||
testInfo.project.name === "mobile-chromium",
|
||||
"The compact disclosure path is covered by the adaptive keyboard traversal suite.",
|
||||
);
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
|
||||
const expectedTargets = [
|
||||
{ selector: '.skip-link', href: '#portfolio-content' },
|
||||
{ selector: ".skip-link", href: "#portfolio-content" },
|
||||
...navHrefs.map((href) => ({ selector: `nav a[href="${href}"]`, href })),
|
||||
{ selector: ".hero-journey__action", href: "#selected-builds" },
|
||||
{
|
||||
selector: 'a[href="https://github.com/LucaPisl/footy-tracker"]',
|
||||
href: "https://github.com/LucaPisl/footy-tracker",
|
||||
},
|
||||
{
|
||||
selector:
|
||||
'a[href="https://github.com/LucaPisl/opinion-dynamics-simulation"]',
|
||||
href: "https://github.com/LucaPisl/opinion-dynamics-simulation",
|
||||
},
|
||||
{
|
||||
selector:
|
||||
'a[href="https://github.com/LucaPisl/CurrencyConverterAvalonia"]',
|
||||
href: "https://github.com/LucaPisl/CurrencyConverterAvalonia",
|
||||
},
|
||||
{
|
||||
selector: 'a[href="https://github.com/LucaPisl/LinuxModdingGuide"]',
|
||||
href: "https://github.com/LucaPisl/LinuxModdingGuide",
|
||||
},
|
||||
{
|
||||
selector: '#contact a[href^="mailto:"]',
|
||||
href: 'mailto:luca.pislaru@gmail.com',
|
||||
href: "mailto:luca.pislaru@gmail.com",
|
||||
},
|
||||
{
|
||||
selector: '#contact a[href="https://github.com/LucaPisl"]',
|
||||
href: 'https://github.com/LucaPisl',
|
||||
href: "https://github.com/LucaPisl",
|
||||
},
|
||||
{
|
||||
selector: '#contact a[href="https://gitlab.com/luca_pisl"]',
|
||||
href: 'https://gitlab.com/luca_pisl',
|
||||
href: "https://gitlab.com/luca_pisl",
|
||||
},
|
||||
{
|
||||
selector: '#contact a[href="https://linkedin.com/in/luca-pisl"]',
|
||||
href: 'https://linkedin.com/in/luca-pisl',
|
||||
href: "https://linkedin.com/in/luca-pisl",
|
||||
},
|
||||
];
|
||||
|
||||
for (const target of expectedTargets) {
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press("Tab");
|
||||
const locator = page.locator(target.selector);
|
||||
await expect(locator).toBeFocused();
|
||||
await expect(locator).toHaveAttribute('href', target.href);
|
||||
await expect(locator).toHaveAttribute("href", target.href);
|
||||
await assertVisibleUnobscuredFocus(page);
|
||||
}
|
||||
});
|
||||
|
||||
test('activating the skip link focuses the visible unobscured portfolio content', async ({
|
||||
test("activating the skip link focuses the visible unobscured portfolio content", async ({
|
||||
page,
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.goto(distUrl, { waitUntil: 'domcontentloaded' });
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.locator('.skip-link')).toBeFocused();
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
await page.keyboard.press("Tab");
|
||||
await expect(page.locator(".skip-link")).toBeFocused();
|
||||
|
||||
await page.keyboard.press('Enter');
|
||||
await page.keyboard.press("Enter");
|
||||
|
||||
await expect(page).toHaveURL(`${distUrl}/#portfolio-content`);
|
||||
await expect(page.locator('#portfolio-content')).toBeFocused();
|
||||
await expect(page.locator('#portfolio-content')).toHaveAttribute(
|
||||
'tabindex',
|
||||
'-1',
|
||||
await expect(page.locator("#portfolio-content")).toBeFocused();
|
||||
await expect(page.locator("#portfolio-content")).toHaveAttribute(
|
||||
"tabindex",
|
||||
"-1",
|
||||
);
|
||||
await expectInsetFocusIndicator(page, '#portfolio-content');
|
||||
await expectInsetFocusIndicator(page, "#portfolio-content");
|
||||
});
|
||||
|
||||
test('explicit section activation scrolls to and focuses its heading', async ({
|
||||
test("explicit section activation scrolls to and focuses its heading", async ({
|
||||
page,
|
||||
distUrl,
|
||||
}, testInfo) => {
|
||||
test.skip(
|
||||
testInfo.project.name === 'mobile-chromium',
|
||||
'The full keyboard path already covers the mobile project.',
|
||||
testInfo.project.name === "mobile-chromium",
|
||||
"The full keyboard path already covers the mobile project.",
|
||||
);
|
||||
await page.goto(distUrl, { waitUntil: 'domcontentloaded' });
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
|
||||
for (let index = 0; index < 5; index += 1) await page.keyboard.press('Tab');
|
||||
for (let index = 0; index < 5; index += 1) await page.keyboard.press("Tab");
|
||||
await expect(page.locator('nav a[href="#infrastructure"]')).toBeFocused();
|
||||
await page.keyboard.press('Enter');
|
||||
await page.keyboard.press("Enter");
|
||||
|
||||
await expect(page).toHaveURL(`${distUrl}/#infrastructure`);
|
||||
await expect(page.locator('#infrastructure')).toBeInViewport();
|
||||
await expect(page.locator('#infrastructure-heading')).toHaveAttribute(
|
||||
'tabindex',
|
||||
'-1',
|
||||
await expect(page.locator("#infrastructure")).toBeInViewport();
|
||||
await expect(page.locator("#infrastructure-heading")).toHaveAttribute(
|
||||
"tabindex",
|
||||
"-1",
|
||||
);
|
||||
await expect(page.locator('#infrastructure-heading')).toBeFocused();
|
||||
await expect(page.locator("#infrastructure-heading")).toBeFocused();
|
||||
await assertVisibleUnobscuredFocus(page);
|
||||
});
|
||||
|
||||
test('manual wheel and PageDown scrolling never move focus', async ({
|
||||
test("manual wheel and PageDown scrolling never move focus", async ({
|
||||
page,
|
||||
distUrl,
|
||||
}, testInfo) => {
|
||||
test.skip(
|
||||
testInfo.project.name === 'mobile-chromium',
|
||||
'The focus invariant is engine-level rather than viewport-specific.',
|
||||
testInfo.project.name === "mobile-chromium",
|
||||
"The focus invariant is engine-level rather than viewport-specific.",
|
||||
);
|
||||
await page.goto(distUrl, { waitUntil: 'domcontentloaded' });
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
await page.keyboard.press("Tab");
|
||||
await page.keyboard.press("Tab");
|
||||
const startLink = page.locator('nav a[href="#start"]');
|
||||
await expect(startLink).toBeFocused();
|
||||
|
||||
await page.mouse.wheel(0, 900);
|
||||
await expect(startLink).toBeFocused();
|
||||
await page.keyboard.press('PageDown');
|
||||
await page.keyboard.press("PageDown");
|
||||
await expect(startLink).toBeFocused();
|
||||
});
|
||||
|
||||
test('direct fragments do not steal focus while reduced-motion navigation remains accessible', async ({
|
||||
test("direct fragments do not steal focus while reduced-motion navigation remains accessible", async ({
|
||||
page,
|
||||
distUrl,
|
||||
}, testInfo) => {
|
||||
test.skip(
|
||||
testInfo.project.name === 'mobile-chromium',
|
||||
'The full keyboard path already covers the mobile project.',
|
||||
testInfo.project.name === "mobile-chromium",
|
||||
"The full keyboard path already covers the mobile project.",
|
||||
);
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' });
|
||||
await page.goto(`${distUrl}/#toolkit`, { waitUntil: 'domcontentloaded' });
|
||||
await expect(page.locator('#toolkit-heading')).not.toBeFocused();
|
||||
await page.emulateMedia({ reducedMotion: "reduce" });
|
||||
await page.goto(`${distUrl}/#toolkit`, { waitUntil: "domcontentloaded" });
|
||||
await expect(page.locator("#toolkit-heading")).not.toBeFocused();
|
||||
|
||||
await page.locator('nav a[href="#contact"]').click();
|
||||
await expect(page).toHaveURL(`${distUrl}/#contact`);
|
||||
await expect(page.locator('#contact-heading')).toBeFocused();
|
||||
await expect(page.locator('#contact')).toBeInViewport();
|
||||
await expect(page.locator("#contact-heading")).toBeFocused();
|
||||
await expect(page.locator("#contact")).toBeInViewport();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user