feat: add persistent book navigation and public-only pages
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { rootChildCount } from './documents';
|
||||
import { rootChildCount, treeParents } from './documents';
|
||||
|
||||
test.use({ javaScriptEnabled: true });
|
||||
|
||||
@@ -34,27 +34,40 @@ test('self-hosted Lyth Mono and EB Garamond load with the configured base', asyn
|
||||
await expect(page.locator('[data-selected-content] h1')).toHaveText('1. コンセプト');
|
||||
expect(await page.locator('[data-selected-content]').evaluate((element) => getComputedStyle(element).fontFamily)).toContain('EB Garamond');
|
||||
await page.locator('[data-selected-content]').screenshot({ path: testInfo.outputPath('eb-garamond-selected-content.png') });
|
||||
await page.goto('./book/1-concept/');
|
||||
expect(await page.locator('.prose').evaluate((element) => getComputedStyle(element).fontFamily)).toContain('EB Garamond');
|
||||
const tree = await page.locator('.tree-pane').elementHandle();
|
||||
await page.locator('.demo-node[data-path="/1-concept.md"]').click();
|
||||
await expect(page).toHaveURL(/\/book\/1-concept\/$/);
|
||||
await expect(page.locator('[data-landing-stage]')).toHaveAttribute('data-phase', 'complete');
|
||||
expect(await tree!.evaluate((element) => element === document.querySelector('.tree-pane'))).toBe(true);
|
||||
await expect(page.locator('.tree-pane .root-vector .trunk-line')).toHaveCount(1);
|
||||
await expect(page.locator('.tree-pane .trunk-reveal')).toHaveCount(1);
|
||||
// Measure the actual root trunk, not branch-spine glyphs (nested prefixes have their own spines).
|
||||
expect(await page.locator('.tree-pane .root-vector .trunk-reveal').evaluate((rect) => (rect as SVGRectElement).height.baseVal.value)).toBeGreaterThan(0);
|
||||
expect(await page.locator('article.prose').evaluate((element) => getComputedStyle(element).fontFamily)).toContain('EB Garamond');
|
||||
await page.screenshot({ path: testInfo.outputPath('eb-garamond-book.png') });
|
||||
});
|
||||
|
||||
test.describe('static font and tree rendering', () => {
|
||||
test.use({ javaScriptEnabled: false, deviceScaleFactor: 4 });
|
||||
test('glyph joins paint continuously across SVG cell boundaries', async ({ page }, testInfo) => {
|
||||
test('single horizontal branches paint continuously from their stems', async ({ page }, testInfo) => {
|
||||
await page.goto('./');
|
||||
await page.evaluate(() => document.fonts.ready);
|
||||
// Sample every root-child join without clipping the final branch in the scrollable tree.
|
||||
// Sample root and book-child joins without clipping the final branch in the scrollable tree.
|
||||
await page.setViewportSize({ width: page.viewportSize()!.width, height: 2000 });
|
||||
await page.locator('.tree-viewport').evaluate((el) => { (el as HTMLElement).style.height = 'auto'; });
|
||||
const joins = await page.locator('.demo-node[aria-level="2"]').evaluateAll((nodes) => nodes.map((node) => {
|
||||
const joins = await page.locator('.demo-node[aria-level="2"], .demo-node[aria-level="3"]').evaluateAll((nodes) => nodes.map((node) => {
|
||||
const svg = node.parentElement!.querySelector<SVGSVGElement>('.branch-vector')!;
|
||||
const p = new DOMPoint(Number(svg.dataset.cellWidth) / 2, 12).matrixTransform(svg.getScreenCTM()!);
|
||||
return { x: p.x, y: p.y };
|
||||
const level = Number(node.getAttribute('aria-level'));
|
||||
const p = new DOMPoint((3 * (level - 2) + .5) * Number(svg.dataset.cellWidth), 12).matrixTransform(svg.getScreenCTM()!);
|
||||
const end = new DOMPoint((3 * (level - 2) + 2) * Number(svg.dataset.cellWidth), 12).matrixTransform(svg.getScreenCTM()!);
|
||||
const gapEnd = new DOMPoint((3 * (level - 2) + 3) * Number(svg.dataset.cellWidth), 12).matrixTransform(svg.getScreenCTM()!);
|
||||
return { x: p.x, y: p.y, end: end.x, gapEnd: gapEnd.x, level };
|
||||
}));
|
||||
expect(joins).toHaveLength(rootChildCount);
|
||||
expect(rootChildCount).toBe(1);
|
||||
expect(joins.filter(({ level }) => level === 2)).toHaveLength(rootChildCount);
|
||||
expect(joins.filter(({ level }) => level === 3)).toHaveLength(Object.values(treeParents).filter((parent) => parent === '/book').length);
|
||||
for (const [index, join] of joins.entries()) {
|
||||
const clip = { x: Math.floor(join.x), y: Math.floor(join.y) - 1, width: 14, height: 3 };
|
||||
const clip = { x: Math.floor(join.x), y: Math.floor(join.y) - 1, width: Math.ceil(join.gapEnd) - Math.floor(join.x), height: 3 };
|
||||
const png = (await page.screenshot({ clip, path: testInfo.outputPath(`glyph-join-${index}.png`) })).toString('base64');
|
||||
const columns = await page.evaluate(async (png) => {
|
||||
const image = new Image(); image.src = 'data:image/png;base64,' + png;
|
||||
@@ -72,9 +85,15 @@ test.describe('static font and tree rendering', () => {
|
||||
return darkest;
|
||||
});
|
||||
}, png);
|
||||
// Ignore only the outer antialiasing fringe at the trunk's left edge.
|
||||
// The hook and one horizontal glyph are followed by a blank cell.
|
||||
const start = Math.ceil((join.x - clip.x + .25) * 4);
|
||||
expect(columns.slice(start).every((ink) => ink < 710), JSON.stringify(columns)).toBe(true);
|
||||
const end = Math.floor((join.end - clip.x - .25) * 4);
|
||||
expect(end).toBeGreaterThan(start);
|
||||
expect(columns.slice(start, end).every((ink) => ink < 710), JSON.stringify(columns)).toBe(true);
|
||||
const gapStart = Math.ceil((join.end - clip.x + 1) * 4);
|
||||
const gapEnd = Math.floor((join.gapEnd - clip.x - .25) * 4);
|
||||
expect(gapEnd).toBeGreaterThan(gapStart);
|
||||
expect(columns.slice(gapStart, gapEnd).every((ink) => ink > 710), JSON.stringify(columns)).toBe(true);
|
||||
}
|
||||
});
|
||||
test('the same SVG trunk and Lyth Mono joins are visible without JavaScript', async ({ page }, testInfo) => {
|
||||
@@ -82,10 +101,10 @@ test.describe('static font and tree rendering', () => {
|
||||
await expect(page.locator('html')).not.toHaveAttribute('data-landing');
|
||||
await expect(page.locator('.trunk-line')).toBeVisible();
|
||||
await expect(page.locator('.trunk-line')).toHaveCSS('stroke', 'none');
|
||||
await expect(page.locator('.branch-prefix').first()).toHaveAttribute('data-prefix', '├── ');
|
||||
await expect(page.locator('.branch-prefix').first()).toHaveAttribute('data-prefix', '└─ ');
|
||||
await expect(page.locator('.branch-prefix').first().locator('path')).toHaveCount(0);
|
||||
await expect(page.locator('.branch-prefix').first().locator('use')).toHaveCount(4);
|
||||
await expect(page.locator('.branch-prefix').first().locator('.branch-spine use')).toHaveAttribute('href', '#tree-glyph-9500');
|
||||
await expect(page.locator('.branch-prefix').first().locator('use')).toHaveCount(3);
|
||||
await expect(page.locator('.branch-prefix').first().locator('.branch-spine use')).toHaveAttribute('href', '#tree-glyph-9492');
|
||||
await expect(page.locator('.tree-glyphs defs path')).toHaveCount(5);
|
||||
await expect(page.getByRole('link', { name: 'Book', exact: true })).toBeVisible();
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
|
||||
|
||||
Reference in New Issue
Block a user