Ctrl+K now opens a corner session with a parsed navigate grammar, persistent history, and no page lock. Unknown targets stay on a closed alias table and never become URLs. Co-authored-by: Cursor <cursoragent@cursor.com>
119 lines
4.4 KiB
TypeScript
119 lines
4.4 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
import { pagePath } from './helpers';
|
|
|
|
test.describe('keyboard and terminal dock', () => {
|
|
test('skip link is first and moves focus to main', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.keyboard.press('Tab');
|
|
const skip = page.locator('.skip-link');
|
|
await expect(skip).toBeFocused();
|
|
await page.keyboard.press('Enter');
|
|
await expect(page.locator('#main-content')).toBeFocused();
|
|
});
|
|
|
|
test('mobile nav toggle keeps aria-expanded in sync', async ({ page, request }) => {
|
|
const ssr = await request.get('/');
|
|
expect(ssr.ok()).toBe(true);
|
|
const html = await ssr.text();
|
|
expect(html).toContain('aria-expanded="true"');
|
|
expect(html).toContain('id="primary-nav"');
|
|
expect(html).toContain('class="site-nav"');
|
|
expect(html).toMatch(/<div[^>]*class="site"/);
|
|
expect(html).not.toMatch(/<div[^>]*class="[^"]*\bsite\b[^"]*\bnav-collapsed\b/);
|
|
|
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
await page.goto('/');
|
|
const toggle = page.locator('.nav-toggle');
|
|
await expect(toggle).toBeVisible();
|
|
await expect(toggle).toHaveAttribute('aria-expanded', 'false');
|
|
await toggle.click();
|
|
await expect(toggle).toHaveAttribute('aria-expanded', 'true');
|
|
await toggle.click();
|
|
await expect(toggle).toHaveAttribute('aria-expanded', 'false');
|
|
});
|
|
|
|
test('collapsed header stays under 25% of the viewport and is not sticky below md', async ({
|
|
page,
|
|
}) => {
|
|
const viewport = { width: 390, height: 844 };
|
|
await page.setViewportSize(viewport);
|
|
await page.goto('/');
|
|
|
|
const header = page.locator('.site-header');
|
|
const box = await header.boundingBox();
|
|
expect(box).toBeTruthy();
|
|
expect(box!.height).toBeLessThan(viewport.height * 0.25);
|
|
|
|
await expect
|
|
.poll(async () => header.evaluate((element) => getComputedStyle(element).position))
|
|
.not.toBe('sticky');
|
|
|
|
await page.locator('.nav-toggle').click();
|
|
await expect(page.locator('.nav-toggle')).toHaveAttribute('aria-expanded', 'true');
|
|
expect(await header.evaluate((element) => getComputedStyle(element).position)).not.toBe(
|
|
'sticky',
|
|
);
|
|
|
|
for (const width of [768, 820, 1024, 1280, 1440]) {
|
|
await page.setViewportSize({ width, height: 900 });
|
|
await page.goto('/');
|
|
expect(
|
|
await header.evaluate((element) => getComputedStyle(element).position),
|
|
`header must not be sticky at ${width}px`,
|
|
).not.toBe('sticky');
|
|
}
|
|
});
|
|
|
|
test('compact services submenu stays keyboard accessible at 1024px', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
test.skip(testInfo.project.name === 'mobile', 'compact submenu applies from md up');
|
|
|
|
await page.setViewportSize({ width: 1024, height: 900 });
|
|
await page.goto('/');
|
|
|
|
const servicesParent = page.locator(
|
|
`.primary-nav > li > a[href="${pagePath('services', 'de')}"]`,
|
|
);
|
|
const firstChild = page.locator(
|
|
`.primary-nav > li > .submenu a[href="${pagePath('servicesSoftware', 'de')}"]`,
|
|
);
|
|
|
|
await servicesParent.focus();
|
|
await expect(firstChild).toBeVisible();
|
|
await page.keyboard.press('Tab');
|
|
await expect(firstChild).toBeFocused();
|
|
|
|
const headerBox = await page.locator('.site-header').boundingBox();
|
|
expect(headerBox, 'header while submenu is open').toBeTruthy();
|
|
expect(headerBox!.height, 'header height while submenu is open').toBeLessThanOrEqual(200);
|
|
|
|
const background = await firstChild.evaluate((element) => {
|
|
const submenu = element.closest('.submenu');
|
|
return submenu ? getComputedStyle(submenu).backgroundColor : '';
|
|
});
|
|
expect(background, 'submenu background must be fully opaque').toMatch(
|
|
/^rgb\(\d+,\s*\d+,\s*\d+\)$/,
|
|
);
|
|
});
|
|
|
|
test('terminal dock opens with Control+K without locking the page and restores on Escape', async ({
|
|
page,
|
|
}) => {
|
|
await page.goto('/');
|
|
const trigger = page.locator('.terminal-dock-trigger');
|
|
await trigger.focus();
|
|
await page.keyboard.press('Control+k');
|
|
|
|
const panel = page.locator('.terminal-dock-panel');
|
|
await expect(panel).toBeVisible();
|
|
await expect(page.locator('.site')).not.toHaveAttribute('inert');
|
|
expect(await page.evaluate(() => document.body.style.overflow)).not.toBe('hidden');
|
|
await expect(page.locator('[role="dialog"]')).toHaveCount(0);
|
|
|
|
await page.keyboard.press('Escape');
|
|
await expect(panel).toHaveCount(0);
|
|
await expect(trigger).toBeFocused();
|
|
});
|
|
});
|