feat: add persistent book navigation and public-only pages

This commit is contained in:
2026-10-15 17:53:48 +09:00
parent f6905074b1
commit 839eb2a6a4
22 changed files with 1191 additions and 269 deletions
+22 -15
View File
@@ -1,12 +1,13 @@
import { expect, test } from '@playwright/test';
import { bookURL, documentPaths, treeNodeCount } from './documents';
test('home and book are usable without client JavaScript', async ({ page }, testInfo) => {
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('[data-selected-content] h1')).toHaveText('1. コンセプト');
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true);
await expect(page.locator('.branch-prefix').first()).toHaveAttribute('data-prefix', '├── ');
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;
@@ -19,24 +20,30 @@ test('home and book are usable without client JavaScript', async ({ page }, test
expect(Math.abs(root!.y + root!.height - spine.y)).toBeLessThan(1);
await page.screenshot({ path: testInfo.outputPath('home.png'), fullPage: true });
await page.getByRole('link', { name: 'Book', exact: true }).click();
await expect(page.getByRole('heading', { level: 1 })).toContainText('通信の約束まで');
await page.getByRole('link', { name: '第1章から読む' }).click();
await expect(page.getByRole('heading', { level: 1 })).toHaveText('1. コンセプト');
if (testInfo.project.name === 'mobile') {
await page.locator('.mobile-chapters summary').click();
await page.locator('.mobile-chapters').getByRole('link', { name: '3. Protocol', exact: true }).click();
} else {
await page.locator('.sidebar').getByRole('link', { name: '3. Protocol', exact: true }).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.getByRole('heading', { level: 1 })).toHaveText('3. Protocol');
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').getByRole('link', { name: '4.2. Text View' }).first().click();
await expect(page.getByRole('heading', { level: 1 })).toHaveText('4.2. Text View');
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 page.goto('book/draft/filesystem-discovery/');
await expect(page.locator('.draft-notice')).toContainText('Draft');
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([]);
});