diff --git a/packages/@d-zero/a11y-check-core/src/scenario-child-process.ts b/packages/@d-zero/a11y-check-core/src/scenario-child-process.ts index 4aed1c27..81b9d3cc 100644 --- a/packages/@d-zero/a11y-check-core/src/scenario-child-process.ts +++ b/packages/@d-zero/a11y-check-core/src/scenario-child-process.ts @@ -1,5 +1,6 @@ import type { Result, NeedAnalysis, Passed, Scenario } from './types.js'; +import { importScenarios, type Violation } from '@d-zero/a11y-check-core'; import { createChildProcess } from '@d-zero/puppeteer-dealer'; import { beforePageScan, @@ -9,8 +10,6 @@ import { import { Cache } from '@d-zero/shared/cache'; import c from 'ansi-colors'; -import { importScenarios, type Violation } from '@d-zero/a11y-check-core'; - export type ChildProcessParams = { readonly scenarios: readonly Scenario[]; readonly cacheDir: string; diff --git a/packages/@d-zero/a11y-check-core/src/scenario-main-process.ts b/packages/@d-zero/a11y-check-core/src/scenario-main-process.ts index 46d3f218..a35577b2 100644 --- a/packages/@d-zero/a11y-check-core/src/scenario-main-process.ts +++ b/packages/@d-zero/a11y-check-core/src/scenario-main-process.ts @@ -11,13 +11,12 @@ import type { DealOptions } from '@d-zero/dealer'; import path from 'node:path'; +import { importScenarios, type Violation } from '@d-zero/a11y-check-core'; import { createProcess, deal } from '@d-zero/puppeteer-dealer'; import c from 'ansi-colors'; import { cleanResults } from './clean-results.js'; -import { importScenarios, type Violation } from '@d-zero/a11y-check-core'; - /** * * @param urlList diff --git a/packages/@d-zero/shared/src/path-list-to-tree.spec.ts b/packages/@d-zero/shared/src/path-list-to-tree.spec.ts index b0141f44..6dec98a6 100644 --- a/packages/@d-zero/shared/src/path-list-to-tree.spec.ts +++ b/packages/@d-zero/shared/src/path-list-to-tree.spec.ts @@ -385,6 +385,74 @@ test('Options: currentPath', () => { }); }); +test('Options: currentPath does not false-positive isAncestor on sibling prefix', () => { + const result = pathListToTree( + ['/', '/foo/index.html', '/foo/bar.html', '/foo/bar_baz.html'], + { + currentPath: '/foo/bar_baz.html', + }, + ); + + const foo = result.children[0]!; + expect(foo.stem).toBe('/foo/'); + expect(foo.isAncestor).toBe(true); + + const bar = foo.children.find((c) => c.stem === '/foo/bar')!; + expect(bar.isAncestor).toBe(false); + + const barBaz = foo.children.find((c) => c.stem === '/foo/bar_baz')!; + expect(barBaz.current).toBe(true); +}); + +test('Options: currentPath file is not treated as child of same-prefix directory', () => { + const result = pathListToTree(['/', '/foo/', '/foo/bar/', '/foo/bar.html'], { + currentPath: '/foo/bar.html', + }); + + const foo = result.children[0]!; + expect(foo.stem).toBe('/foo/'); + expect(foo.isAncestor).toBe(true); + + const fooBarDir = foo.children.find((c) => c.stem === '/foo/bar/')!; + expect(fooBarDir.isAncestor).toBe(false); + + const fooBarFile = foo.children.find((c) => c.stem === '/foo/bar')!; + expect(fooBarFile.current).toBe(true); +}); + +test('Options: currentPath as root marks root current and no ancestors', () => { + const result = pathListToTree(['/', '/a/', '/b.html'], { + currentPath: '/', + }); + + expect(result.current).toBe(true); + expect(result.isAncestor).toBe(false); + expect(result.children[0]!.isAncestor).toBe(false); + expect(result.children[1]!.isAncestor).toBe(false); +}); + +test('Options: currentPath deep nesting marks all ancestor directories', () => { + const result = pathListToTree(['/', '/a/', '/a/b/', '/a/b/c.html'], { + currentPath: '/a/b/c.html', + }); + + expect(result.isAncestor).toBe(true); + expect(result.current).toBe(false); + + const a = result.children[0]!; + expect(a.stem).toBe('/a/'); + expect(a.isAncestor).toBe(true); + + const ab = a.children[0]!; + expect(ab.stem).toBe('/a/b/'); + expect(ab.isAncestor).toBe(true); + + const abc = ab.children[0]!; + expect(abc.stem).toBe('/a/b/c'); + expect(abc.current).toBe(true); + expect(abc.isAncestor).toBe(false); +}); + test('Options: addMetaData adds meta to every node', () => { const result = pathListToTree(['/', '/a/', '/a/b', '/a/c/'], { addMetaData: (node) => ({ diff --git a/packages/@d-zero/shared/src/path-list-to-tree.ts b/packages/@d-zero/shared/src/path-list-to-tree.ts index aac9cfa2..affb48f6 100644 --- a/packages/@d-zero/shared/src/path-list-to-tree.ts +++ b/packages/@d-zero/shared/src/path-list-to-tree.ts @@ -51,7 +51,7 @@ export type Node> = { depth: number; /** True if this node is the current path. */ current: boolean; - /** True if the current path is under this node. */ + /** True if this node is a directory ancestor of the current path. Only directory nodes (stem ending with '/') can be ancestors. */ isAncestor: boolean; /** Present when the node was created as a virtual parent (no real file). */ virtual?: true; @@ -118,7 +118,10 @@ export function pathListToTree>( stem: url.stem, depth: url.depth, current, - isAncestor: !current && currentPath ? currentPath.startsWith(url.stem) : false, + isAncestor: + !current && currentPath + ? url.stem.endsWith('/') && currentPath.startsWith(url.stem) + : false, children: [], }); }