Files

54 lines
4.0 KiB
TypeScript

import { expect, test } from '@playwright/test';
import { bookURL, documentPaths, treeNodeCount } from './documents';
test('home and book share the usable tree without client JavaScript', async ({ page }, testInfo) => {
const failures: string[] = [];
page.on('response', (response) => { if (response.status() >= 400) failures.push(response.url()); });
await page.goto('./');
await expect(page.locator('.selected-content.prose[data-selected-content] .root-page[data-root-page]').getByRole('heading', { level: 1, name: 'WIP', exact: true })).toBeVisible();
await expect(page.locator('[data-landing-stage]')).toHaveAttribute('data-current-path', '/');
await expect(page.locator('.root-mark')).toHaveAttribute('aria-current', 'page');
await expect(page.locator('.root-mark')).toHaveAttribute('aria-selected', 'true');
await expect(page.locator('[data-selected-content]')).not.toContainText('1. コンセプト');
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true);
await expect(page.locator('.branch-prefix').first()).toHaveAttribute('data-prefix', '├─ ');
const root = await page.locator('.root-mark').boundingBox();
const spine = await page.locator('.branch-vector').first().evaluate((element) => {
const svg = element as SVGSVGElement;
const top = new DOMPoint(Number(svg.dataset.cellWidth) / 2, 0).matrixTransform(svg.getScreenCTM()!);
return { x: top.x, y: top.y };
});
expect(root).not.toBeNull();
const footX = await page.locator('.root-vector').evaluate((svg) => new DOMPoint(12, 60).matrixTransform((svg as SVGSVGElement).getScreenCTM()!).x);
expect(Math.abs(footX - spine.x)).toBeLessThan(1);
expect(Math.abs(root!.y + root!.height - spine.y)).toBeLessThan(1);
await page.screenshot({ path: testInfo.outputPath('home.png'), fullPage: true });
await page.getByRole('navigation', { name: 'ルートページのナビゲーション', exact: true }).getByRole('link', { name: /^Book(?:\s|$)/ }).click();
await expect(page).toHaveURL(new URL('book/', testInfo.project.use.baseURL!).href);
await expect(page.locator('[data-landing-stage]')).toHaveAttribute('data-current-path', '/book');
await expect(page.locator('.demo-node')).toHaveCount(treeNodeCount);
await expect(page.locator('.sidebar, .mobile-chapters, .mobile-toc, .page-toc, .pagination')).toHaveCount(0);
await expect(page.getByText('世界の見方から、', { exact: false })).toHaveCount(0);
// The directory index lists all 14 physical documents, independent of logical chapter nesting.
await expect(page.locator('.landing a:not(.demo-node)[href*="/book/"]')).toHaveCount(documentPaths.length);
for (const path of documentPaths) {
await expect(page.locator(`.landing a:not(.demo-node)[href="${new URL(bookURL(path, testInfo.project.use.baseURL!)).pathname}"]`)).toBeVisible();
}
await expect(page.locator('.demo-node[data-path^="/draft"], .landing a[href*="/draft/"]')).toHaveCount(0);
await page.locator('.landing a:not(.demo-node)[href$="/book/1-concept/"]').click();
await expect(page.locator('article.prose h1')).toHaveText('1. コンセプト');
await page.locator('.demo-node[data-path="/3-protocol.md"]').click();
await expect(page.locator('article.prose h1')).toHaveText('3. Protocol');
await expect(page.locator('.demo-node')).toHaveCount(treeNodeCount);
await page.screenshot({ path: testInfo.outputPath('book.png'), fullPage: false });
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true);
// Cross-chapter Japanese fragment links must reach the matching heading.
await page.locator('article.prose').getByRole('link', { name: '4.2. Text View' }).first().click();
await expect(page.locator('article.prose h1')).toHaveText('4.2. Text View');
await expect(page).toHaveURL(/4\.2-text-view\/#/);
await expect(page.locator('a[href*="/draft/"]')).toHaveCount(0);
await expect(page.locator('.draft-notice')).toHaveCount(0);
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true);
expect(failures).toEqual([]);
});