@brandlab-digital/conformance 3.14.0 → 3.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -3
- package/dist/current-release.js +5 -5
- package/dist/current-release.js.map +1 -1
- package/dist/engineering.d.ts.map +1 -1
- package/dist/engineering.js +18 -2
- package/dist/engineering.js.map +1 -1
- package/dist/estate-snapshot.d.ts +54 -0
- package/dist/estate-snapshot.d.ts.map +1 -0
- package/dist/estate-snapshot.js +164 -0
- package/dist/estate-snapshot.js.map +1 -0
- package/dist/estate.d.ts +10 -0
- package/dist/estate.d.ts.map +1 -1
- package/dist/estate.js +36 -16
- package/dist/estate.js.map +1 -1
- package/dist/media-snapshot.d.ts +53 -0
- package/dist/media-snapshot.d.ts.map +1 -0
- package/dist/media-snapshot.js +66 -0
- package/dist/media-snapshot.js.map +1 -0
- package/dist/site-system/checks.d.ts +2 -2
- package/dist/site-system/checks.js +2 -2
- package/dist/site-system/checks.js.map +1 -1
- package/dist/site-system/config.d.ts +13 -0
- package/dist/site-system/config.d.ts.map +1 -1
- package/dist/site-system/config.js +30 -1
- package/dist/site-system/config.js.map +1 -1
- package/dist/site-system/index.d.ts +1 -1
- package/dist/site-system/index.d.ts.map +1 -1
- package/dist/site-system/index.js +1 -1
- package/dist/site-system/index.js.map +1 -1
- package/dist/site-system/journeys/conversion.d.ts.map +1 -1
- package/dist/site-system/journeys/conversion.js +15 -2
- package/dist/site-system/journeys/conversion.js.map +1 -1
- package/dist/site-system/journeys/menu.d.ts.map +1 -1
- package/dist/site-system/journeys/menu.js +24 -1
- package/dist/site-system/journeys/menu.js.map +1 -1
- package/dist/site-system/journeys/page-helpers.d.ts.map +1 -1
- package/dist/site-system/journeys/page-helpers.js +1 -1
- package/dist/site-system/journeys/page-helpers.js.map +1 -1
- package/dist/site-system/journeys/plan.d.ts +13 -3
- package/dist/site-system/journeys/plan.d.ts.map +1 -1
- package/dist/site-system/journeys/plan.js +12 -2
- package/dist/site-system/journeys/plan.js.map +1 -1
- package/dist/site-system/journeys/shell.d.ts +1 -1
- package/dist/site-system/journeys/shell.js +2 -2
- package/dist/site-system/journeys/shell.js.map +1 -1
- package/dist/site-system/report.d.ts.map +1 -1
- package/dist/site-system/report.js +24 -4
- package/dist/site-system/report.js.map +1 -1
- package/dist/site-system/rules/form/c7-thank-you.d.ts +7 -1
- package/dist/site-system/rules/form/c7-thank-you.d.ts.map +1 -1
- package/dist/site-system/rules/form/c7-thank-you.js +26 -3
- package/dist/site-system/rules/form/c7-thank-you.js.map +1 -1
- package/dist/site-system/rules/form/index.d.ts.map +1 -1
- package/dist/site-system/rules/form/index.js +11 -1
- package/dist/site-system/rules/form/index.js.map +1 -1
- package/dist/site-system/rules/full/common.d.ts +2 -0
- package/dist/site-system/rules/full/common.d.ts.map +1 -1
- package/dist/site-system/rules/full/common.js.map +1 -1
- package/dist/site-system/rules/full/estate.d.ts +10 -2
- package/dist/site-system/rules/full/estate.d.ts.map +1 -1
- package/dist/site-system/rules/full/estate.js +35 -5
- package/dist/site-system/rules/full/estate.js.map +1 -1
- package/dist/site-system/rules/full/geography.d.ts +7 -0
- package/dist/site-system/rules/full/geography.d.ts.map +1 -1
- package/dist/site-system/rules/full/geography.js +9 -0
- package/dist/site-system/rules/full/geography.js.map +1 -1
- package/dist/site-system/rules/full/index.d.ts +2 -1
- package/dist/site-system/rules/full/index.d.ts.map +1 -1
- package/dist/site-system/rules/full/index.js +5 -1
- package/dist/site-system/rules/full/index.js.map +1 -1
- package/dist/site-system/rules/full/l10-location-copy.d.ts +1 -1
- package/dist/site-system/rules/full/l10-location-copy.d.ts.map +1 -1
- package/dist/site-system/rules/full/l10-location-copy.js +5 -2
- package/dist/site-system/rules/full/l10-location-copy.js.map +1 -1
- package/dist/site-system/rules/full/l3-neighbours.d.ts +3 -2
- package/dist/site-system/rules/full/l3-neighbours.d.ts.map +1 -1
- package/dist/site-system/rules/full/l3-neighbours.js +4 -1
- package/dist/site-system/rules/full/l3-neighbours.js.map +1 -1
- package/dist/site-system/rules/full/l4-local-copy.d.ts +2 -1
- package/dist/site-system/rules/full/l4-local-copy.d.ts.map +1 -1
- package/dist/site-system/rules/full/l4-local-copy.js +8 -3
- package/dist/site-system/rules/full/l4-local-copy.js.map +1 -1
- package/dist/site-system/rules/full/l9-data-contract.js +2 -2
- package/dist/site-system/rules/full/l9-data-contract.js.map +1 -1
- package/dist/site-system/rules/full/links.d.ts +3 -1
- package/dist/site-system/rules/full/links.d.ts.map +1 -1
- package/dist/site-system/rules/full/links.js +15 -5
- package/dist/site-system/rules/full/links.js.map +1 -1
- package/dist/site-system/rules/full/route-kinds.d.ts +15 -0
- package/dist/site-system/rules/full/route-kinds.d.ts.map +1 -0
- package/dist/site-system/rules/full/route-kinds.js +52 -0
- package/dist/site-system/rules/full/route-kinds.js.map +1 -0
- package/dist/site-system/rules/full/strategy.d.ts +2 -0
- package/dist/site-system/rules/full/strategy.d.ts.map +1 -1
- package/dist/site-system/rules/full/strategy.js +86 -14
- package/dist/site-system/rules/full/strategy.js.map +1 -1
- package/dist/site-system/rules/sections/k2-skin-coverage.d.ts +2 -2
- package/dist/site-system/rules/sections/k2-skin-coverage.d.ts.map +1 -1
- package/dist/site-system/rules/sections/k2-skin-coverage.js +9 -2
- package/dist/site-system/rules/sections/k2-skin-coverage.js.map +1 -1
- package/dist/site-system/rules/sections/t6-media-entries.d.ts.map +1 -1
- package/dist/site-system/rules/sections/t6-media-entries.js +69 -5
- package/dist/site-system/rules/sections/t6-media-entries.js.map +1 -1
- package/dist/site-system-e2e.d.ts +1 -1
- package/dist/site-system-e2e.d.ts.map +1 -1
- package/dist/site-system-e2e.js +17 -4
- package/dist/site-system-e2e.js.map +1 -1
- package/package.json +10 -2
|
@@ -8,13 +8,18 @@
|
|
|
8
8
|
* photographs the business has to vouch for, so they need no entry.
|
|
9
9
|
*
|
|
10
10
|
* The manifest is read from the syntax tree, never run. A manifest the reader cannot resolve to plain data (a spread or
|
|
11
|
-
* a computed value)
|
|
11
|
+
* a computed value, as AA's gallery entries) is read from its snapshot, `src/data/media.generated.json`, which the site
|
|
12
|
+
* writes with `writeMediaSnapshot` (`@brandlab-digital/conformance/media-snapshot`, 3.15.0); without one T6 fails and
|
|
13
|
+
* says how to record it, rather than guessing. A snapshot older than the files it hashed, or one that lists other
|
|
14
|
+
* images than a plain-data media.ts, fails as stale.
|
|
12
15
|
*/
|
|
13
16
|
import { existsSync, readFileSync } from "node:fs";
|
|
14
17
|
import { join } from "node:path";
|
|
15
18
|
import { fail, notApplicable, pass, plural } from "../enquiry/common.js";
|
|
16
19
|
import { LiteralReader, UNRESOLVED, isFullyResolved, isLiteralObject } from "../enquiry/literal.js";
|
|
17
20
|
import { PageProblems, namedList } from "../library/common.js";
|
|
21
|
+
import { estatePlanSourceHash } from "../../../estate-snapshot.js";
|
|
22
|
+
import { MEDIA_SNAPSHOT_FILE, MEDIA_SNAPSHOT_KIND } from "../../../media-snapshot.js";
|
|
18
23
|
import { MEDIA_EXEMPT_NAME, imagesIn, needsMediaEntry, publicImages } from "./images.js";
|
|
19
24
|
export const T6_MEDIA_PATH = "src/data/media.ts";
|
|
20
25
|
export const T6_PROVENANCES = ["genuine", "stock", "illustrative-ai"];
|
|
@@ -34,7 +39,10 @@ export function readMediaManifest(projectRoot) {
|
|
|
34
39
|
const [argument] = reader.callArguments("defineMedia");
|
|
35
40
|
if (!argument)
|
|
36
41
|
return "not-declared";
|
|
37
|
-
|
|
42
|
+
return manifestOf(reader.evaluate(argument));
|
|
43
|
+
}
|
|
44
|
+
/** The entries of a manifest value, or null when they are not plain data. */
|
|
45
|
+
function manifestOf(value) {
|
|
38
46
|
if (!isLiteralObject(value))
|
|
39
47
|
return null;
|
|
40
48
|
const sources = new Map();
|
|
@@ -66,7 +74,24 @@ export function checkT6(pages, projectRoot) {
|
|
|
66
74
|
shown.set(image.file, [...(shown.get(image.file) ?? []), page]);
|
|
67
75
|
}
|
|
68
76
|
}
|
|
69
|
-
const
|
|
77
|
+
const literal = readMediaManifest(projectRoot);
|
|
78
|
+
const snapshot = readMediaSnapshot(projectRoot);
|
|
79
|
+
let manifest = literal;
|
|
80
|
+
let read = T6_MEDIA_PATH;
|
|
81
|
+
if (snapshot !== null) {
|
|
82
|
+
if ("problem" in snapshot)
|
|
83
|
+
return [fail("T6", MEDIA_SNAPSHOT_FILE, snapshot.problem)];
|
|
84
|
+
if (snapshot.stale)
|
|
85
|
+
return [fail("T6", MEDIA_SNAPSHOT_FILE, snapshot.stale)];
|
|
86
|
+
if (typeof literal === "object" && literal !== null && !sameSources(literal.sources, snapshot.manifest.sources)) {
|
|
87
|
+
return [fail("T6", MEDIA_SNAPSHOT_FILE, `${MEDIA_SNAPSHOT_FILE} does not list the same images as ${T6_MEDIA_PATH}, so it is stale; ${REGENERATE}.`)];
|
|
88
|
+
}
|
|
89
|
+
manifest = snapshot.manifest;
|
|
90
|
+
read = MEDIA_SNAPSHOT_FILE;
|
|
91
|
+
}
|
|
92
|
+
else if (literal === null) {
|
|
93
|
+
return [fail("T6", T6_MEDIA_PATH, `${T6_MEDIA_PATH} is not plain data (a spread or a computed value), so its entries cannot be read; write the manifest as a literal, or record it with writeMediaSnapshot from @brandlab-digital/conformance/media-snapshot (${MEDIA_SNAPSHOT_FILE}).`)];
|
|
94
|
+
}
|
|
70
95
|
if (manifest === null)
|
|
71
96
|
return [];
|
|
72
97
|
if (manifest === "missing" || manifest === "not-declared") {
|
|
@@ -77,7 +102,7 @@ export function checkT6(pages, projectRoot) {
|
|
|
77
102
|
return [fail("T6", T6_MEDIA_PATH, `${reason}, but the site has ${count}; every image needs a media.ts entry with provenance.`)];
|
|
78
103
|
}
|
|
79
104
|
const sources = new Set(manifest.sources.values());
|
|
80
|
-
const failures = manifest.entryProblems.map((problem) => fail("T6",
|
|
105
|
+
const failures = manifest.entryProblems.map((problem) => fail("T6", read, `${read}: ${problem}.`));
|
|
81
106
|
const unlisted = onDisk.filter((image) => !covered(sources, image.web)).map((image) => image.web);
|
|
82
107
|
if (unlisted.length > 0) {
|
|
83
108
|
failures.push(fail("T6", "public", `${plural(unlisted.length, "image")} in public/ ${unlisted.length === 1 ? "has" : "have"} no entry in ${T6_MEDIA_PATH}: ${namedList(unlisted)}.`));
|
|
@@ -91,6 +116,45 @@ export function checkT6(pages, projectRoot) {
|
|
|
91
116
|
failures.push(...problems.findings("T6"));
|
|
92
117
|
if (failures.length > 0)
|
|
93
118
|
return failures;
|
|
94
|
-
return [pass("T6", `Checked ${plural(onDisk.length, "image file")} in public/ and ${plural(shown.size, "image")} on the pages: each has an entry in ${
|
|
119
|
+
return [pass("T6", `Checked ${plural(onDisk.length, "image file")} in public/ and ${plural(shown.size, "image")} on the pages: each has an entry in ${read} with provenance.`)];
|
|
120
|
+
}
|
|
121
|
+
const REGENERATE = "run the site's tests (writeMediaSnapshot) to write it again, and commit it";
|
|
122
|
+
function sameSources(left, right) {
|
|
123
|
+
return left.size === right.size && [...left].every(([id, src]) => right.get(id) === src);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The media snapshot (3.15.0), or null when the site has none. `stale` says why it no longer follows its sources: the
|
|
127
|
+
* files it hashed have changed since it was written.
|
|
128
|
+
*/
|
|
129
|
+
function readMediaSnapshot(projectRoot) {
|
|
130
|
+
const file = join(projectRoot, MEDIA_SNAPSHOT_FILE);
|
|
131
|
+
if (!existsSync(file))
|
|
132
|
+
return null;
|
|
133
|
+
let value;
|
|
134
|
+
try {
|
|
135
|
+
value = JSON.parse(readFileSync(file, "utf8"));
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return { problem: `${MEDIA_SNAPSHOT_FILE} is not valid JSON; ${REGENERATE}.` };
|
|
139
|
+
}
|
|
140
|
+
const record = value;
|
|
141
|
+
const sources = isLiteralObject(record) ? record["sources"] : undefined;
|
|
142
|
+
const manifest = isLiteralObject(record) ? manifestOf(record["media"] ?? null) : null;
|
|
143
|
+
if (!isLiteralObject(record) || record["kind"] !== MEDIA_SNAPSHOT_KIND || !isLiteralObject(sources) || typeof sources["sha256"] !== "string"
|
|
144
|
+
|| !Array.isArray(sources["paths"]) || !sources["paths"].every((path) => typeof path === "string") || manifest === null) {
|
|
145
|
+
return { problem: `${MEDIA_SNAPSHOT_FILE} is not a media snapshot (no kind "${MEDIA_SNAPSHOT_KIND}" with its sources and entries); ${REGENERATE}.` };
|
|
146
|
+
}
|
|
147
|
+
const paths = sources["paths"];
|
|
148
|
+
let current;
|
|
149
|
+
try {
|
|
150
|
+
current = estatePlanSourceHash(projectRoot, paths);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
return { problem: `${MEDIA_SNAPSHOT_FILE}: ${error instanceof Error ? error.message : String(error)}` };
|
|
154
|
+
}
|
|
155
|
+
const stale = current === sources["sha256"]
|
|
156
|
+
? null
|
|
157
|
+
: `${MEDIA_SNAPSHOT_FILE} is older than the files it was written from (${paths.join(", ")} changed since), so it may not match media.ts; ${REGENERATE}.`;
|
|
158
|
+
return { manifest, stale };
|
|
95
159
|
}
|
|
96
160
|
//# sourceMappingURL=t6-media-entries.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"t6-media-entries.js","sourceRoot":"","sources":["../../../../src/site-system/rules/sections/t6-media-entries.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * T6: every image has a media.ts entry with provenance (Site System v2 §13.2, F13).\n *\n * `src/data/media.ts` declares the manifest with `defineMedia({ id: { src, width, height, alt, provenance, rights … } })`.\n * Every image file under `public/` and every image the built pages show (a `next/image` URL is read back to the file it\n * optimises) must be the `src` of an entry, and every entry names its provenance (`genuine`, `stock` or\n * `illustrative-ai`) and its rights. Logos, icons, SVG art and third-party profile photos (review avatars) are not\n * photographs the business has to vouch for, so they need no entry.\n *\n * The manifest is read from the syntax tree, never run. A manifest the reader cannot resolve to plain data (a spread or\n * a computed value) reports nothing rather than guessing.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { SiteSystemFinding } from \"../../checks.js\";\nimport { fail, notApplicable, pass, plural, type ParsedPage } from \"../enquiry/common.js\";\nimport { LiteralReader, UNRESOLVED, isFullyResolved, isLiteralObject, type Literal } from \"../enquiry/literal.js\";\nimport { PageProblems, namedList } from \"../library/common.js\";\nimport { MEDIA_EXEMPT_NAME, imagesIn, needsMediaEntry, publicImages } from \"./images.js\";\n\nexport const T6_MEDIA_PATH = \"src/data/media.ts\";\nexport const T6_PROVENANCES = [\"genuine\", \"stock\", \"illustrative-ai\"] as const;\n\nexport interface MediaManifest {\n /** Media id to its `src`. */\n readonly sources: ReadonlyMap<string, string>;\n /** Problems with entries, one plain fragment each (\"media.hero has no provenance\"). */\n readonly entryProblems: readonly string[];\n}\n\n/** \"missing\": no media.ts; \"not-declared\": no defineMedia call in it; null: unreadable; else the manifest. */\nexport function readMediaManifest(projectRoot: string): MediaManifest | \"missing\" | \"not-declared\" | null {\n const file = join(projectRoot, T6_MEDIA_PATH);\n if (!existsSync(file)) return \"missing\";\n let text: string;\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return null;\n }\n const reader = new LiteralReader(text, T6_MEDIA_PATH);\n const [argument] = reader.callArguments(\"defineMedia\");\n if (!argument) return \"not-declared\";\n const value: Literal = reader.evaluate(argument);\n if (!isLiteralObject(value)) return null;\n const sources = new Map<string, string>();\n const entryProblems: string[] = [];\n for (const [id, entry] of Object.entries(value)) {\n if (!isLiteralObject(entry) || !isFullyResolved(entry.src) || typeof entry.src !== \"string\") return null;\n sources.set(id, entry.src);\n if (!(T6_PROVENANCES as readonly unknown[]).includes(entry.provenance)) {\n entryProblems.push(`media.${id} has no provenance (genuine, stock or illustrative-ai)`);\n }\n if (typeof entry.rights !== \"string\" || entry.rights.trim() === \"\") {\n if (entry.rights !== UNRESOLVED) entryProblems.push(`media.${id} does not say who holds the rights`);\n }\n }\n return { sources, entryProblems };\n}\n\n/** The part of an https URL entry that matches a page: the whole URL. */\nfunction covered(sources: ReadonlySet<string>, file: string): boolean {\n return sources.has(file);\n}\n\nexport function checkT6(pages: readonly ParsedPage[], projectRoot: string): SiteSystemFinding[] {\n const onDisk = publicImages(projectRoot).filter((image) => !/\\.svg$/i.test(image.web) && !MEDIA_EXEMPT_NAME.test(image.web));\n const shown = new Map<string, ParsedPage[]>();\n for (const page of pages) {\n for (const image of imagesIn(page.root)) {\n if (needsMediaEntry(image)) shown.set(image.file, [...(shown.get(image.file) ?? []), page]);\n }\n }\n\n const manifest = readMediaManifest(projectRoot);\n if (manifest === null) return [];\n if (manifest === \"missing\" || manifest === \"not-declared\") {\n if (onDisk.length === 0 && shown.size === 0) return [notApplicable(\"T6\", \"The site has no images to record in a media manifest.\")];\n const reason = manifest === \"missing\" ? `${T6_MEDIA_PATH} does not exist` : `${T6_MEDIA_PATH} does not declare the manifest with defineMedia`;\n const count = onDisk.length > 0 ? plural(onDisk.length, \"image file\") + \" in public/\" : plural(shown.size, \"image\") + \" on the pages\";\n return [fail(\"T6\", T6_MEDIA_PATH, `${reason}, but the site has ${count}; every image needs a media.ts entry with provenance.`)];\n }\n\n const sources = new Set(manifest.sources.values());\n const failures: SiteSystemFinding[] = manifest.entryProblems.map((problem) => fail(\"T6\", T6_MEDIA_PATH, `${T6_MEDIA_PATH}: ${problem}.`));\n const unlisted = onDisk.filter((image) => !covered(sources, image.web)).map((image) => image.web);\n if (unlisted.length > 0) {\n failures.push(fail(\"T6\", \"public\", `${plural(unlisted.length, \"image\")} in public/ ${unlisted.length === 1 ? \"has\" : \"have\"} no entry in ${T6_MEDIA_PATH}: ${namedList(unlisted)}.`));\n }\n const problems = new PageProblems();\n for (const page of pages) {\n const missing = [...shown].filter(([file, owners]) => owners.includes(page) && !covered(sources, file)).map(([file]) => file);\n if (missing.length > 0) problems.add(page.path, `${plural(missing.length, \"image\")} shown with no entry in ${T6_MEDIA_PATH} (${namedList(missing, 3)})`);\n }\n failures.push(...problems.findings(\"T6\"));\n if (failures.length > 0) return failures;\n return [pass(\"T6\", `Checked ${plural(onDisk.length, \"image file\")} in public/ and ${plural(shown.size, \"image\")} on the pages: each has an entry in ${T6_MEDIA_PATH} with provenance.`)];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"t6-media-entries.js","sourceRoot":"","sources":["../../../../src/site-system/rules/sections/t6-media-entries.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * T6: every image has a media.ts entry with provenance (Site System v2 §13.2, F13).\n *\n * `src/data/media.ts` declares the manifest with `defineMedia({ id: { src, width, height, alt, provenance, rights … } })`.\n * Every image file under `public/` and every image the built pages show (a `next/image` URL is read back to the file it\n * optimises) must be the `src` of an entry, and every entry names its provenance (`genuine`, `stock` or\n * `illustrative-ai`) and its rights. Logos, icons, SVG art and third-party profile photos (review avatars) are not\n * photographs the business has to vouch for, so they need no entry.\n *\n * The manifest is read from the syntax tree, never run. A manifest the reader cannot resolve to plain data (a spread or\n * a computed value, as AA's gallery entries) is read from its snapshot, `src/data/media.generated.json`, which the site\n * writes with `writeMediaSnapshot` (`@brandlab-digital/conformance/media-snapshot`, 3.15.0); without one T6 fails and\n * says how to record it, rather than guessing. A snapshot older than the files it hashed, or one that lists other\n * images than a plain-data media.ts, fails as stale.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { SiteSystemFinding } from \"../../checks.js\";\nimport { fail, notApplicable, pass, plural, type ParsedPage } from \"../enquiry/common.js\";\nimport { LiteralReader, UNRESOLVED, isFullyResolved, isLiteralObject, type Literal } from \"../enquiry/literal.js\";\nimport { PageProblems, namedList } from \"../library/common.js\";\nimport { estatePlanSourceHash } from \"../../../estate-snapshot.js\";\nimport { MEDIA_SNAPSHOT_FILE, MEDIA_SNAPSHOT_KIND } from \"../../../media-snapshot.js\";\nimport { MEDIA_EXEMPT_NAME, imagesIn, needsMediaEntry, publicImages } from \"./images.js\";\n\nexport const T6_MEDIA_PATH = \"src/data/media.ts\";\nexport const T6_PROVENANCES = [\"genuine\", \"stock\", \"illustrative-ai\"] as const;\n\nexport interface MediaManifest {\n /** Media id to its `src`. */\n readonly sources: ReadonlyMap<string, string>;\n /** Problems with entries, one plain fragment each (\"media.hero has no provenance\"). */\n readonly entryProblems: readonly string[];\n}\n\n/** \"missing\": no media.ts; \"not-declared\": no defineMedia call in it; null: unreadable; else the manifest. */\nexport function readMediaManifest(projectRoot: string): MediaManifest | \"missing\" | \"not-declared\" | null {\n const file = join(projectRoot, T6_MEDIA_PATH);\n if (!existsSync(file)) return \"missing\";\n let text: string;\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return null;\n }\n const reader = new LiteralReader(text, T6_MEDIA_PATH);\n const [argument] = reader.callArguments(\"defineMedia\");\n if (!argument) return \"not-declared\";\n return manifestOf(reader.evaluate(argument));\n}\n\n/** The entries of a manifest value, or null when they are not plain data. */\nfunction manifestOf(value: Literal): MediaManifest | null {\n if (!isLiteralObject(value)) return null;\n const sources = new Map<string, string>();\n const entryProblems: string[] = [];\n for (const [id, entry] of Object.entries(value)) {\n if (!isLiteralObject(entry) || !isFullyResolved(entry.src) || typeof entry.src !== \"string\") return null;\n sources.set(id, entry.src);\n if (!(T6_PROVENANCES as readonly unknown[]).includes(entry.provenance)) {\n entryProblems.push(`media.${id} has no provenance (genuine, stock or illustrative-ai)`);\n }\n if (typeof entry.rights !== \"string\" || entry.rights.trim() === \"\") {\n if (entry.rights !== UNRESOLVED) entryProblems.push(`media.${id} does not say who holds the rights`);\n }\n }\n return { sources, entryProblems };\n}\n\n/** The part of an https URL entry that matches a page: the whole URL. */\nfunction covered(sources: ReadonlySet<string>, file: string): boolean {\n return sources.has(file);\n}\n\nexport function checkT6(pages: readonly ParsedPage[], projectRoot: string): SiteSystemFinding[] {\n const onDisk = publicImages(projectRoot).filter((image) => !/\\.svg$/i.test(image.web) && !MEDIA_EXEMPT_NAME.test(image.web));\n const shown = new Map<string, ParsedPage[]>();\n for (const page of pages) {\n for (const image of imagesIn(page.root)) {\n if (needsMediaEntry(image)) shown.set(image.file, [...(shown.get(image.file) ?? []), page]);\n }\n }\n\n const literal = readMediaManifest(projectRoot);\n const snapshot = readMediaSnapshot(projectRoot);\n let manifest = literal;\n let read = T6_MEDIA_PATH;\n if (snapshot !== null) {\n if (\"problem\" in snapshot) return [fail(\"T6\", MEDIA_SNAPSHOT_FILE, snapshot.problem)];\n if (snapshot.stale) return [fail(\"T6\", MEDIA_SNAPSHOT_FILE, snapshot.stale)];\n if (typeof literal === \"object\" && literal !== null && !sameSources(literal.sources, snapshot.manifest.sources)) {\n return [fail(\"T6\", MEDIA_SNAPSHOT_FILE, `${MEDIA_SNAPSHOT_FILE} does not list the same images as ${T6_MEDIA_PATH}, so it is stale; ${REGENERATE}.`)];\n }\n manifest = snapshot.manifest;\n read = MEDIA_SNAPSHOT_FILE;\n } else if (literal === null) {\n return [fail(\"T6\", T6_MEDIA_PATH, `${T6_MEDIA_PATH} is not plain data (a spread or a computed value), so its entries cannot be read; write the manifest as a literal, or record it with writeMediaSnapshot from @brandlab-digital/conformance/media-snapshot (${MEDIA_SNAPSHOT_FILE}).`)];\n }\n if (manifest === null) return [];\n if (manifest === \"missing\" || manifest === \"not-declared\") {\n if (onDisk.length === 0 && shown.size === 0) return [notApplicable(\"T6\", \"The site has no images to record in a media manifest.\")];\n const reason = manifest === \"missing\" ? `${T6_MEDIA_PATH} does not exist` : `${T6_MEDIA_PATH} does not declare the manifest with defineMedia`;\n const count = onDisk.length > 0 ? plural(onDisk.length, \"image file\") + \" in public/\" : plural(shown.size, \"image\") + \" on the pages\";\n return [fail(\"T6\", T6_MEDIA_PATH, `${reason}, but the site has ${count}; every image needs a media.ts entry with provenance.`)];\n }\n\n const sources = new Set(manifest.sources.values());\n const failures: SiteSystemFinding[] = manifest.entryProblems.map((problem) => fail(\"T6\", read, `${read}: ${problem}.`));\n const unlisted = onDisk.filter((image) => !covered(sources, image.web)).map((image) => image.web);\n if (unlisted.length > 0) {\n failures.push(fail(\"T6\", \"public\", `${plural(unlisted.length, \"image\")} in public/ ${unlisted.length === 1 ? \"has\" : \"have\"} no entry in ${T6_MEDIA_PATH}: ${namedList(unlisted)}.`));\n }\n const problems = new PageProblems();\n for (const page of pages) {\n const missing = [...shown].filter(([file, owners]) => owners.includes(page) && !covered(sources, file)).map(([file]) => file);\n if (missing.length > 0) problems.add(page.path, `${plural(missing.length, \"image\")} shown with no entry in ${T6_MEDIA_PATH} (${namedList(missing, 3)})`);\n }\n failures.push(...problems.findings(\"T6\"));\n if (failures.length > 0) return failures;\n return [pass(\"T6\", `Checked ${plural(onDisk.length, \"image file\")} in public/ and ${plural(shown.size, \"image\")} on the pages: each has an entry in ${read} with provenance.`)];\n}\n\nconst REGENERATE = \"run the site's tests (writeMediaSnapshot) to write it again, and commit it\";\n\nfunction sameSources(left: ReadonlyMap<string, string>, right: ReadonlyMap<string, string>): boolean {\n return left.size === right.size && [...left].every(([id, src]) => right.get(id) === src);\n}\n\n/**\n * The media snapshot (3.15.0), or null when the site has none. `stale` says why it no longer follows its sources: the\n * files it hashed have changed since it was written.\n */\nfunction readMediaSnapshot(projectRoot: string): { manifest: MediaManifest; stale: string | null } | { problem: string } | null {\n const file = join(projectRoot, MEDIA_SNAPSHOT_FILE);\n if (!existsSync(file)) return null;\n let value: unknown;\n try {\n value = JSON.parse(readFileSync(file, \"utf8\"));\n } catch {\n return { problem: `${MEDIA_SNAPSHOT_FILE} is not valid JSON; ${REGENERATE}.` };\n }\n const record = value as { [key: string]: Literal };\n const sources = isLiteralObject(record) ? record[\"sources\"] : undefined;\n const manifest = isLiteralObject(record) ? manifestOf(record[\"media\"] ?? null) : null;\n if (!isLiteralObject(record) || record[\"kind\"] !== MEDIA_SNAPSHOT_KIND || !isLiteralObject(sources) || typeof sources[\"sha256\"] !== \"string\"\n || !Array.isArray(sources[\"paths\"]) || !sources[\"paths\"].every((path) => typeof path === \"string\") || manifest === null) {\n return { problem: `${MEDIA_SNAPSHOT_FILE} is not a media snapshot (no kind \"${MEDIA_SNAPSHOT_KIND}\" with its sources and entries); ${REGENERATE}.` };\n }\n const paths = sources[\"paths\"] as string[];\n let current: string;\n try {\n current = estatePlanSourceHash(projectRoot, paths);\n } catch (error) {\n return { problem: `${MEDIA_SNAPSHOT_FILE}: ${error instanceof Error ? error.message : String(error)}` };\n }\n const stale = current === sources[\"sha256\"]\n ? null\n : `${MEDIA_SNAPSHOT_FILE} is older than the files it was written from (${paths.join(\", \")} changed since), so it may not match media.ts; ${REGENERATE}.`;\n return { manifest, stale };\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type BrandlabSiteSystemTestOptions } from './site-system/journeys/plan.js';
|
|
2
|
-
export { SITE_SYSTEM_DESKTOP_VIEWPORT, SITE_SYSTEM_DESKTOP_WIDTHS, SITE_SYSTEM_JOURNEY_IDS, SITE_SYSTEM_PHONE_VIEWPORT, SITE_SYSTEM_QUOTE_NAME, planBrandlabSiteSystemJourneys, siteSystemJourneyTitle, siteSystemJourneysFor, type BrandlabSiteSystemTestOptions, type SiteSystemActionOrder, type SiteSystemJourneyId, type SiteSystemJourneyPlan, } from './site-system/journeys/plan.js';
|
|
2
|
+
export { SITE_SYSTEM_DESKTOP_VIEWPORT, SITE_SYSTEM_DESKTOP_WIDTHS, SITE_SYSTEM_JOURNEY_IDS, SITE_SYSTEM_PHONE_VIEWPORT, SITE_SYSTEM_QUOTE_NAME, planBrandlabSiteSystemJourneys, siteSystemJourneyRelease, siteSystemJourneyTitle, siteSystemJourneysFor, type BrandlabSiteSystemTestOptions, type SiteSystemActionOrder, type SiteSystemJourneyId, type SiteSystemJourneyPlan, } from './site-system/journeys/plan.js';
|
|
3
3
|
export { HOVER_OPEN_WINDOW_MS, MOUSE_OUT_CLOSE_WINDOW_MS } from './site-system/journeys/menu.js';
|
|
4
4
|
export { SITE_SYSTEM_PHONE_PLACEMENTS } from './site-system/journeys/page-helpers.js';
|
|
5
5
|
export { W9_CACHE_HEADERS, W9_TRACKING_QUERY, W9_UNKNOWN_SLUG, notFoundVariantProblems, trackingVariantProblems, urlVariants, type W9Response, type W9Variants, } from './site-system/journeys/variants.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site-system-e2e.d.ts","sourceRoot":"","sources":["../src/site-system-e2e.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"site-system-e2e.d.ts","sourceRoot":"","sources":["../src/site-system-e2e.ts"],"names":[],"mappings":"AAgCA,OAAO,EAA4D,KAAK,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAK9I,OAAO,EACL,4BAA4B,EAC5B,0BAA0B,EAC1B,uBAAuB,EACvB,0BAA0B,EAC1B,sBAAsB,EACtB,8BAA8B,EAC9B,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AACjG,OAAO,EAAE,4BAA4B,EAAE,MAAM,wCAAwC,CAAC;AACtF,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EACvB,uBAAuB,EACvB,WAAW,EACX,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,oCAAoC,CAAC;AAa5C,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,6BAA6B,GAAG,IAAI,CAuB1F"}
|
package/dist/site-system-e2e.js
CHANGED
|
@@ -15,28 +15,41 @@
|
|
|
15
15
|
*
|
|
16
16
|
* The journeys find the header parts by role, so they test a bespoke header and the shared one alike: the header is
|
|
17
17
|
* the banner landmark, the menu the navigation named "Main", each submenu a `button[aria-expanded][aria-controls]`
|
|
18
|
-
* and its panel the element that names. The tier decides which journeys register (C1 from `enquiry
|
|
18
|
+
* and its panel the element that names. The tier decides which journeys register (C1 from `enquiry`, W9 at `full`
|
|
19
|
+
* with `pages.families`), each from the release that brings it: the site's installed Brandlab release (a canary
|
|
20
|
+
* included), read from its package.json as brandlab-ready reads it, so every journey the gate blocks on runs. They run in
|
|
19
21
|
* Chromium; K5 (focus not hidden) runs in every browser project the site's Playwright config defines, so add a
|
|
20
22
|
* WebKit project to cover Safari. axe comes from `@axe-core/playwright` when the site has it, else `axe-core`.
|
|
21
23
|
* Every request leaves the browser only as a GET or HEAD to the build under test.
|
|
22
24
|
*/
|
|
25
|
+
import { readFileSync } from 'node:fs';
|
|
26
|
+
import { join } from 'node:path';
|
|
23
27
|
import { test } from '@playwright/test';
|
|
24
28
|
import { isolateFromNetwork } from './e2e-support.js';
|
|
25
29
|
import { defineAccessJourneys } from './site-system/journeys/access.js';
|
|
26
30
|
import { installPageHelpers } from './site-system/journeys/browser.js';
|
|
27
31
|
import { defineConversionJourneys } from './site-system/journeys/conversion.js';
|
|
28
32
|
import { defineMenuJourneys } from './site-system/journeys/menu.js';
|
|
29
|
-
import { planBrandlabSiteSystemJourneys } from './site-system/journeys/plan.js';
|
|
33
|
+
import { planBrandlabSiteSystemJourneys, siteSystemJourneyRelease } from './site-system/journeys/plan.js';
|
|
30
34
|
import { definePrefetchJourneys } from './site-system/journeys/prefetch.js';
|
|
31
35
|
import { defineShellJourneys } from './site-system/journeys/shell.js';
|
|
32
36
|
import { defineVariantJourneys } from './site-system/journeys/variants.js';
|
|
33
|
-
export { SITE_SYSTEM_DESKTOP_VIEWPORT, SITE_SYSTEM_DESKTOP_WIDTHS, SITE_SYSTEM_JOURNEY_IDS, SITE_SYSTEM_PHONE_VIEWPORT, SITE_SYSTEM_QUOTE_NAME, planBrandlabSiteSystemJourneys, siteSystemJourneyTitle, siteSystemJourneysFor, } from './site-system/journeys/plan.js';
|
|
37
|
+
export { SITE_SYSTEM_DESKTOP_VIEWPORT, SITE_SYSTEM_DESKTOP_WIDTHS, SITE_SYSTEM_JOURNEY_IDS, SITE_SYSTEM_PHONE_VIEWPORT, SITE_SYSTEM_QUOTE_NAME, planBrandlabSiteSystemJourneys, siteSystemJourneyRelease, siteSystemJourneyTitle, siteSystemJourneysFor, } from './site-system/journeys/plan.js';
|
|
34
38
|
export { HOVER_OPEN_WINDOW_MS, MOUSE_OUT_CLOSE_WINDOW_MS } from './site-system/journeys/menu.js';
|
|
35
39
|
export { SITE_SYSTEM_PHONE_PLACEMENTS } from './site-system/journeys/page-helpers.js';
|
|
36
40
|
export { W9_CACHE_HEADERS, W9_TRACKING_QUERY, W9_UNKNOWN_SLUG, notFoundVariantProblems, trackingVariantProblems, urlVariants, } from './site-system/journeys/variants.js';
|
|
37
41
|
const CHROMIUM_ONLY = 'The site-system journeys run in Chromium; K5 runs in every browser project.';
|
|
42
|
+
/** The site's package.json (Playwright runs from the project root), or null when it cannot be read. */
|
|
43
|
+
function sitePackageJson() {
|
|
44
|
+
try {
|
|
45
|
+
return JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf8'));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
38
51
|
export function defineBrandlabSiteSystemTests(options) {
|
|
39
|
-
const plan = planBrandlabSiteSystemJourneys(options);
|
|
52
|
+
const plan = planBrandlabSiteSystemJourneys({ ...options, release: options.release ?? siteSystemJourneyRelease(sitePackageJson()) });
|
|
40
53
|
const runs = new Set(plan.journeys);
|
|
41
54
|
const chromiumOnly = () => test.skip(({ browserName }) => browserName !== 'chromium', CHROMIUM_ONLY);
|
|
42
55
|
test.describe('Brandlab site system', () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site-system-e2e.js","sourceRoot":"","sources":["../src/site-system-e2e.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"site-system-e2e.js","sourceRoot":"","sources":["../src/site-system-e2e.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Site System v2 browser journeys for a Brandlab production build (release 3.8.0): the header and its menu,\n * the phone drawer and call bar, contrast, targets, focus, the action order, the money-page first screen and phone\n * placements, from 3.13.0 prefetch by link role (W2) and (full tier) the URL variants of each page family (W9). Every\n * test title starts with its ready-gate check id in square brackets (\"[N3] ...\") so `brandlab-ready` can read the\n * Playwright JSON report.\n *\n * // tests/e2e/site-system.spec.ts\n * import { defineBrandlabSiteSystemTests } from '@brandlab-digital/conformance/site-system-e2e'\n *\n * defineBrandlabSiteSystemTests({\n * tier: 'enquiry',\n * pages: { home: '/', money: ['/services/blocked-drains'], contact: '/contact' },\n * })\n *\n * The journeys find the header parts by role, so they test a bespoke header and the shared one alike: the header is\n * the banner landmark, the menu the navigation named \"Main\", each submenu a `button[aria-expanded][aria-controls]`\n * and its panel the element that names. The tier decides which journeys register (C1 from `enquiry`, W9 at `full`\n * with `pages.families`), each from the release that brings it: the site's installed Brandlab release (a canary\n * included), read from its package.json as brandlab-ready reads it, so every journey the gate blocks on runs. They run in\n * Chromium; K5 (focus not hidden) runs in every browser project the site's Playwright config defines, so add a\n * WebKit project to cover Safari. axe comes from `@axe-core/playwright` when the site has it, else `axe-core`.\n * Every request leaves the browser only as a GET or HEAD to the build under test.\n */\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { test } from '@playwright/test';\nimport { isolateFromNetwork } from './e2e-support.js';\nimport { defineAccessJourneys } from './site-system/journeys/access.js';\nimport { installPageHelpers } from './site-system/journeys/browser.js';\nimport { defineConversionJourneys } from './site-system/journeys/conversion.js';\nimport { defineMenuJourneys } from './site-system/journeys/menu.js';\nimport { planBrandlabSiteSystemJourneys, siteSystemJourneyRelease, type BrandlabSiteSystemTestOptions } from './site-system/journeys/plan.js';\nimport { definePrefetchJourneys } from './site-system/journeys/prefetch.js';\nimport { defineShellJourneys } from './site-system/journeys/shell.js';\nimport { defineVariantJourneys } from './site-system/journeys/variants.js';\n\nexport {\n SITE_SYSTEM_DESKTOP_VIEWPORT,\n SITE_SYSTEM_DESKTOP_WIDTHS,\n SITE_SYSTEM_JOURNEY_IDS,\n SITE_SYSTEM_PHONE_VIEWPORT,\n SITE_SYSTEM_QUOTE_NAME,\n planBrandlabSiteSystemJourneys,\n siteSystemJourneyRelease,\n siteSystemJourneyTitle,\n siteSystemJourneysFor,\n type BrandlabSiteSystemTestOptions,\n type SiteSystemActionOrder,\n type SiteSystemJourneyId,\n type SiteSystemJourneyPlan,\n} from './site-system/journeys/plan.js';\nexport { HOVER_OPEN_WINDOW_MS, MOUSE_OUT_CLOSE_WINDOW_MS } from './site-system/journeys/menu.js';\nexport { SITE_SYSTEM_PHONE_PLACEMENTS } from './site-system/journeys/page-helpers.js';\nexport {\n W9_CACHE_HEADERS,\n W9_TRACKING_QUERY,\n W9_UNKNOWN_SLUG,\n notFoundVariantProblems,\n trackingVariantProblems,\n urlVariants,\n type W9Response,\n type W9Variants,\n} from './site-system/journeys/variants.js';\n\nconst CHROMIUM_ONLY = 'The site-system journeys run in Chromium; K5 runs in every browser project.';\n\n/** The site's package.json (Playwright runs from the project root), or null when it cannot be read. */\nfunction sitePackageJson(): Parameters<typeof siteSystemJourneyRelease>[0] {\n try {\n return JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf8')) as Parameters<typeof siteSystemJourneyRelease>[0];\n } catch {\n return null;\n }\n}\n\nexport function defineBrandlabSiteSystemTests(options: BrandlabSiteSystemTestOptions): void {\n const plan = planBrandlabSiteSystemJourneys({ ...options, release: options.release ?? siteSystemJourneyRelease(sitePackageJson()) });\n const runs = new Set<string>(plan.journeys);\n const chromiumOnly = () => test.skip(({ browserName }) => browserName !== 'chromium', CHROMIUM_ONLY);\n\n test.describe('Brandlab site system', () => {\n // Service workers could answer requests outside page and context routing.\n test.use({ serviceWorkers: 'block' });\n test.beforeEach(async ({ page, baseURL }) => {\n await isolateFromNetwork(page, { baseURL, googleTag: 'stub' });\n await installPageHelpers(page);\n });\n\n test.describe('Chromium journeys', () => {\n chromiumOnly();\n defineMenuJourneys(plan, runs);\n defineShellJourneys(plan, runs);\n definePrefetchJourneys(plan, runs);\n defineConversionJourneys(plan, runs);\n defineVariantJourneys(plan, runs);\n });\n defineAccessJourneys(plan, runs, { chromiumOnly });\n });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brandlab-digital/conformance",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.0",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@brandlab-digital/site-core": "3.
|
|
32
|
+
"@brandlab-digital/site-core": "3.15.0",
|
|
33
33
|
"typescript": "6.0.3"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
@@ -81,6 +81,14 @@
|
|
|
81
81
|
"types": "./dist/estate.d.ts",
|
|
82
82
|
"import": "./dist/estate.js"
|
|
83
83
|
},
|
|
84
|
+
"./estate-snapshot": {
|
|
85
|
+
"types": "./dist/estate-snapshot.d.ts",
|
|
86
|
+
"import": "./dist/estate-snapshot.js"
|
|
87
|
+
},
|
|
88
|
+
"./media-snapshot": {
|
|
89
|
+
"types": "./dist/media-snapshot.d.ts",
|
|
90
|
+
"import": "./dist/media-snapshot.js"
|
|
91
|
+
},
|
|
84
92
|
"./contact-e2e": {
|
|
85
93
|
"types": "./dist/contact-e2e.d.ts",
|
|
86
94
|
"import": "./dist/contact-e2e.js"
|