@brandlab-digital/conformance 3.12.1 → 3.14.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 +41 -1
- package/dist/analytics-e2e.d.ts +10 -16
- package/dist/analytics-e2e.d.ts.map +1 -1
- package/dist/analytics-e2e.js +61 -3
- package/dist/analytics-e2e.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/current-release.js +5 -5
- package/dist/current-release.js.map +1 -1
- package/dist/edge-probe-cli.d.ts +57 -0
- package/dist/edge-probe-cli.d.ts.map +1 -0
- package/dist/edge-probe-cli.js +134 -0
- package/dist/edge-probe-cli.js.map +1 -0
- package/dist/engineering.d.ts +4 -1
- package/dist/engineering.d.ts.map +1 -1
- package/dist/engineering.js +19 -14
- package/dist/engineering.js.map +1 -1
- package/dist/estate-sample.d.ts +60 -0
- package/dist/estate-sample.d.ts.map +1 -0
- package/dist/estate-sample.js +144 -0
- package/dist/estate-sample.js.map +1 -0
- package/dist/estate.d.ts +45 -0
- package/dist/estate.d.ts.map +1 -1
- package/dist/estate.js +82 -22
- package/dist/estate.js.map +1 -1
- package/dist/origin-region.d.ts +14 -0
- package/dist/origin-region.d.ts.map +1 -0
- package/dist/origin-region.js +46 -0
- package/dist/origin-region.js.map +1 -0
- package/dist/prefetch-probe-cli.d.ts +36 -0
- package/dist/prefetch-probe-cli.d.ts.map +1 -0
- package/dist/prefetch-probe-cli.js +163 -0
- package/dist/prefetch-probe-cli.js.map +1 -0
- package/dist/project.d.ts +20 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +92 -2
- package/dist/project.js.map +1 -1
- package/dist/ready-cli.d.ts.map +1 -1
- package/dist/ready-cli.js +8 -3
- package/dist/ready-cli.js.map +1 -1
- package/dist/rule-registry.d.ts +5 -0
- package/dist/rule-registry.d.ts.map +1 -1
- package/dist/rule-registry.js +1 -0
- package/dist/rule-registry.js.map +1 -1
- package/dist/site-system/checks.d.ts +62 -1
- package/dist/site-system/checks.d.ts.map +1 -1
- package/dist/site-system/checks.js +9 -0
- package/dist/site-system/checks.js.map +1 -1
- package/dist/site-system/config.d.ts +27 -0
- package/dist/site-system/config.d.ts.map +1 -1
- package/dist/site-system/config.js +50 -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/plan.d.ts +9 -2
- package/dist/site-system/journeys/plan.d.ts.map +1 -1
- package/dist/site-system/journeys/plan.js +11 -1
- package/dist/site-system/journeys/plan.js.map +1 -1
- package/dist/site-system/journeys/prefetch-measure.d.ts +28 -0
- package/dist/site-system/journeys/prefetch-measure.d.ts.map +1 -0
- package/dist/site-system/journeys/prefetch-measure.js +94 -0
- package/dist/site-system/journeys/prefetch-measure.js.map +1 -0
- package/dist/site-system/journeys/prefetch-policy.d.ts +85 -0
- package/dist/site-system/journeys/prefetch-policy.d.ts.map +1 -0
- package/dist/site-system/journeys/prefetch-policy.js +152 -0
- package/dist/site-system/journeys/prefetch-policy.js.map +1 -0
- package/dist/site-system/journeys/prefetch.d.ts +3 -0
- package/dist/site-system/journeys/prefetch.d.ts.map +1 -0
- package/dist/site-system/journeys/prefetch.js +37 -0
- package/dist/site-system/journeys/prefetch.js.map +1 -0
- package/dist/site-system/journeys/variants.d.ts +34 -0
- package/dist/site-system/journeys/variants.d.ts.map +1 -0
- package/dist/site-system/journeys/variants.js +139 -0
- package/dist/site-system/journeys/variants.js.map +1 -0
- package/dist/site-system/report.d.ts.map +1 -1
- package/dist/site-system/report.js +19 -5
- package/dist/site-system/report.js.map +1 -1
- package/dist/site-system/rule-modules.d.ts.map +1 -1
- package/dist/site-system/rule-modules.js +10 -0
- package/dist/site-system/rule-modules.js.map +1 -1
- package/dist/site-system/rules/delivery/h13-inbox.d.ts +23 -0
- package/dist/site-system/rules/delivery/h13-inbox.d.ts.map +1 -0
- package/dist/site-system/rules/delivery/h13-inbox.js +208 -0
- package/dist/site-system/rules/delivery/h13-inbox.js.map +1 -0
- package/dist/site-system/rules/delivery/index.d.ts +12 -0
- package/dist/site-system/rules/delivery/index.d.ts.map +1 -0
- package/dist/site-system/rules/delivery/index.js +7 -0
- package/dist/site-system/rules/delivery/index.js.map +1 -0
- package/dist/site-system/rules/full/common.d.ts +3 -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/e1-pattern.d.ts +4 -0
- package/dist/site-system/rules/full/e1-pattern.d.ts.map +1 -0
- package/dist/site-system/rules/full/e1-pattern.js +47 -0
- package/dist/site-system/rules/full/e1-pattern.js.map +1 -0
- package/dist/site-system/rules/full/estate.d.ts +20 -1
- package/dist/site-system/rules/full/estate.d.ts.map +1 -1
- package/dist/site-system/rules/full/estate.js +33 -5
- package/dist/site-system/rules/full/estate.js.map +1 -1
- package/dist/site-system/rules/full/geography.d.ts +8 -2
- package/dist/site-system/rules/full/geography.d.ts.map +1 -1
- package/dist/site-system/rules/full/geography.js +6 -2
- package/dist/site-system/rules/full/geography.js.map +1 -1
- package/dist/site-system/rules/full/index.d.ts +3 -2
- package/dist/site-system/rules/full/index.d.ts.map +1 -1
- package/dist/site-system/rules/full/index.js +27 -9
- package/dist/site-system/rules/full/index.js.map +1 -1
- package/dist/site-system/rules/full/k12-slots.d.ts +4 -0
- package/dist/site-system/rules/full/k12-slots.d.ts.map +1 -0
- package/dist/site-system/rules/full/k12-slots.js +40 -0
- package/dist/site-system/rules/full/k12-slots.js.map +1 -0
- package/dist/site-system/rules/full/loc-index.d.ts +5 -0
- package/dist/site-system/rules/full/loc-index.d.ts.map +1 -1
- package/dist/site-system/rules/full/loc-index.js +43 -2
- package/dist/site-system/rules/full/loc-index.js.map +1 -1
- package/dist/site-system/rules/full/strategy.d.ts +4 -0
- package/dist/site-system/rules/full/strategy.d.ts.map +1 -1
- package/dist/site-system/rules/full/strategy.js +19 -3
- package/dist/site-system/rules/full/strategy.js.map +1 -1
- package/dist/site-system/rules/full/w3-refresh.d.ts +22 -0
- package/dist/site-system/rules/full/w3-refresh.d.ts.map +1 -0
- package/dist/site-system/rules/full/w3-refresh.js +70 -0
- package/dist/site-system/rules/full/w3-refresh.js.map +1 -0
- package/dist/site-system/rules/guards/index.d.ts +22 -0
- package/dist/site-system/rules/guards/index.d.ts.map +1 -0
- package/dist/site-system/rules/guards/index.js +109 -0
- package/dist/site-system/rules/guards/index.js.map +1 -0
- package/dist/site-system/rules/guards/w10-crawlers.d.ts +55 -0
- package/dist/site-system/rules/guards/w10-crawlers.d.ts.map +1 -0
- package/dist/site-system/rules/guards/w10-crawlers.js +182 -0
- package/dist/site-system/rules/guards/w10-crawlers.js.map +1 -0
- package/dist/site-system/rules/guards/w9-params.d.ts +19 -0
- package/dist/site-system/rules/guards/w9-params.d.ts.map +1 -0
- package/dist/site-system/rules/guards/w9-params.js +84 -0
- package/dist/site-system/rules/guards/w9-params.js.map +1 -0
- 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 +6 -2
- package/dist/site-system/rules/sections/k2-skin-coverage.js.map +1 -1
- package/dist/site-system/runners.js +1 -1
- package/dist/site-system/runners.js.map +1 -1
- package/dist/site-system-e2e.d.ts +1 -0
- package/dist/site-system-e2e.d.ts.map +1 -1
- package/dist/site-system-e2e.js +8 -2
- package/dist/site-system-e2e.js.map +1 -1
- package/package.json +6 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"estate-sample.js","sourceRoot":"","sources":["../src/estate-sample.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The sampled estate crawl (3.13.0, client CI v2). A draft pull request checks a\n * sample of the catalogue instead of every route: the hubs, the first and last\n * page of each route kind, the routes a changed data file feeds and a fixed-seed\n * sample of the rest. The full crawl still runs when the pull request is ready\n * for review and before merge; the sample only makes drafts cheaper.\n *\n * CI chooses the scope with `BRANDLAB_ESTATE_SCOPE` (`full`, the default, or\n * `sample`) and lists the changed files in the file `BRANDLAB_CHANGED_FILES`\n * names, one path per line. Any other scope value runs the full crawl.\n */\nimport { createHash } from 'node:crypto';\nimport { readFileSync } from 'node:fs';\n\nexport type BrandlabEstateScope = 'full' | 'sample';\n\n/** The route fields the sample reads; a catalogue route has them. */\nexport interface BrandlabEstateSampleRoute {\n path: string;\n kind?: string;\n id?: string;\n}\n\nexport interface BrandlabEstateSampleOptions {\n /** How many more routes the fixed seed picks beyond hubs, kind ends and changed routes (default 50). */\n size?: number;\n /** The seed; the same seed and catalogue always give the same sample (default `brandlab-estate-sample`). */\n seed?: string;\n /**\n * The routes a changed file feeds, as paths. By default a route is fed by a file\n * named after its id or its last path segment (`src/data/locations/bondi.ts`\n * feeds `/locations/bondi`; an `index` file is named by its folder).\n */\n routesForFile?: (file: string, routes: readonly BrandlabEstateSampleRoute[]) => readonly string[];\n}\n\nexport interface BrandlabEstateSample<T extends BrandlabEstateSampleRoute> {\n scope: BrandlabEstateScope;\n /** The routes to crawl, in catalogue order. */\n routes: T[];\n total: number;\n counts: { hubs: number; kindEnds: number; changed: number; seeded: number };\n /** One line for the test annotation. */\n summary: string;\n}\n\n/** Hubs: home, every index page, the contact page and the not-found page. */\nconst HUB_KINDS = new Set(['home', 'service-index', 'location-index', 'blog-index', 'project-index', 'contact']);\nconst DEFAULT_SIZE = 50;\nconst DEFAULT_SEED = 'brandlab-estate-sample';\nconst GENERIC_STEMS = new Set(['index', 'data', 'content', 'page']);\n\n/** The scope CI asked for. Anything but `sample` is the full crawl, so a typo never thins a check. */\nexport function estateScopeFromEnv(env: NodeJS.ProcessEnv = process.env): { scope: BrandlabEstateScope; note?: string } {\n const value = (env.BRANDLAB_ESTATE_SCOPE ?? '').trim();\n if (value === 'sample') return { scope: 'sample' };\n if (value === '' || value === 'full') return { scope: 'full' };\n return { scope: 'full', note: `BRANDLAB_ESTATE_SCOPE=\"${value}\" is not full or sample; the full estate was crawled` };\n}\n\n/** The changed files CI listed (`BRANDLAB_CHANGED_FILES` names the list). Unreadable or unset lists none. */\nexport function changedFilesFromEnv(env: NodeJS.ProcessEnv = process.env): string[] {\n const listPath = env.BRANDLAB_CHANGED_FILES?.trim();\n if (!listPath) return [];\n try {\n return readFileSync(listPath, 'utf8').split('\\n').map((line) => line.trim()).filter(Boolean);\n } catch {\n return [];\n }\n}\n\n/** The name a data file gives the routes it feeds: its file name without extensions, or its folder's for an index file. */\nexport function changedFileStem(file: string): string | undefined {\n const parts = file.replace(/\\\\/g, '/').split('/').filter(Boolean);\n const stemOf = (part: string | undefined) => part?.replace(/(?:\\.[a-z0-9]+)+$/i, '').toLowerCase() ?? '';\n const own = stemOf(parts.at(-1));\n const stem = GENERIC_STEMS.has(own) ? stemOf(parts.at(-2)) : own;\n return stem && !GENERIC_STEMS.has(stem) ? stem : undefined;\n}\n\nexport function defaultRoutesForFile(file: string, routes: readonly BrandlabEstateSampleRoute[]): string[] {\n const stem = changedFileStem(file);\n if (!stem) return [];\n return routes\n .filter((route) => route.id?.toLowerCase() === stem || lastSegment(route.path) === stem)\n .map((route) => route.path);\n}\n\n/**\n * The routes a sampled crawl checks. The full scope returns every route. The\n * sample keeps the hubs and the not-found page, the first and last route of each\n * kind in catalogue order, the routes the changed files feed and `size` more\n * chosen by a hash of the seed and the path, so the same catalogue always gives\n * the same sample.\n */\nexport function sampleEstateRoutes<T extends BrandlabEstateSampleRoute>(\n routes: readonly T[],\n input: { scope: BrandlabEstateScope; changedFiles?: readonly string[] } & BrandlabEstateSampleOptions,\n): BrandlabEstateSample<T> {\n const total = routes.length;\n if (input.scope === 'full') {\n return { scope: 'full', routes: [...routes], total, counts: { hubs: 0, kindEnds: 0, changed: 0, seeded: 0 }, summary: `full: all ${total} routes` };\n }\n const chosen = new Set<string>();\n const add = (path: string) => {\n const key = normalise(path);\n if (chosen.has(key)) return false;\n chosen.add(key);\n return true;\n };\n let hubs = 0;\n for (const route of routes) {\n if (HUB_KINDS.has(route.kind ?? '') || normalise(route.path) === '/' || normalise(route.path) === '/404') if (add(route.path)) hubs += 1;\n }\n const byKind = new Map<string, T[]>();\n for (const route of routes) {\n if (HUB_KINDS.has(route.kind ?? '')) continue;\n const kind = route.kind ?? '';\n byKind.set(kind, [...(byKind.get(kind) ?? []), route]);\n }\n let kindEnds = 0;\n for (const group of byKind.values()) {\n for (const route of [group[0], group.at(-1)]) if (route && add(route.path)) kindEnds += 1;\n }\n const known = new Set(routes.map((route) => normalise(route.path)));\n const feeds = input.routesForFile ?? defaultRoutesForFile;\n let changed = 0;\n for (const file of input.changedFiles ?? []) {\n for (const path of feeds(file, routes)) if (known.has(normalise(path)) && add(path)) changed += 1;\n }\n const seed = input.seed ?? DEFAULT_SEED;\n const size = Math.max(0, Math.floor(input.size ?? DEFAULT_SIZE));\n const rest = routes\n .filter((route) => !chosen.has(normalise(route.path)))\n .map((route) => ({ path: route.path, rank: seededRank(seed, normalise(route.path)) }))\n .sort((a, b) => a.rank.localeCompare(b.rank) || a.path.localeCompare(b.path))\n .slice(0, size);\n for (const { path } of rest) add(path);\n const sampled = routes.filter((route) => chosen.has(normalise(route.path)));\n // A path the catalogue repeats is checked once.\n const unique = sampled.filter((route, index) => sampled.findIndex((other) => normalise(other.path) === normalise(route.path)) === index);\n return {\n scope: 'sample',\n routes: unique,\n total,\n counts: { hubs, kindEnds, changed, seeded: rest.length },\n summary: `sample: ${unique.length} of ${total} routes (hubs ${hubs}, first and last of each kind ${kindEnds}, from changed files ${changed}, fixed-seed ${rest.length}); the full estate is crawled when the pull request is ready for review`,\n };\n}\n\n/** A fixed-seed sample of other items (redirect rules), in their original order. */\nexport function sampleBySeed<T>(items: readonly T[], keyOf: (item: T) => string, input: { size?: number; seed?: string } = {}): T[] {\n const seed = input.seed ?? DEFAULT_SEED;\n const size = Math.max(0, Math.floor(input.size ?? DEFAULT_SIZE));\n const picked = new Set(\n items\n .map((item, index) => ({ index, rank: seededRank(seed, keyOf(item)) }))\n .sort((a, b) => a.rank.localeCompare(b.rank) || a.index - b.index)\n .slice(0, size)\n .map(({ index }) => index),\n );\n return items.filter((_, index) => picked.has(index));\n}\n\nfunction seededRank(seed: string, key: string): string {\n return createHash('sha256').update(`${seed}\\u0000${key}`).digest('hex');\n}\n\nfunction lastSegment(path: string): string {\n return normalise(path).split('/').filter(Boolean).at(-1)?.toLowerCase() ?? '';\n}\n\nfunction normalise(path: string): string {\n const pathname = path.split(/[?#]/)[0] || '/';\n return pathname.length > 1 ? pathname.replace(/\\/+$/, '') : '/';\n}\n"]}
|
package/dist/estate.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type APIRequestContext } from '@playwright/test';
|
|
2
2
|
import { type LocationCapacityRecord, type SitemapFile, type SuburbCentreLookup } from './page-standard.js';
|
|
3
|
+
import { type BrandlabEstateSampleOptions } from './estate-sample.js';
|
|
4
|
+
export { changedFilesFromEnv, changedFileStem, defaultRoutesForFile, estateScopeFromEnv, sampleBySeed, sampleEstateRoutes, type BrandlabEstateSample, type BrandlabEstateSampleOptions, type BrandlabEstateSampleRoute, type BrandlabEstateScope, } from './estate-sample.js';
|
|
3
5
|
export interface BrandlabEstateRoute {
|
|
4
6
|
path: string;
|
|
5
7
|
canonicalPath?: string;
|
|
@@ -7,6 +9,8 @@ export interface BrandlabEstateRoute {
|
|
|
7
9
|
/** The route kind (a catalogue route has it): sitemap families and location checks read it. */
|
|
8
10
|
kind?: string;
|
|
9
11
|
id?: string;
|
|
12
|
+
/** The catalogue parent: a location page other location pages sit under is a region page. */
|
|
13
|
+
parentId?: string;
|
|
10
14
|
/** The content date the sitemap's lastmod must carry. */
|
|
11
15
|
lastSignificantChange?: string;
|
|
12
16
|
locationDecision?: {
|
|
@@ -77,8 +81,18 @@ export interface BrandlabEstateTestOptions {
|
|
|
77
81
|
};
|
|
78
82
|
/** A place's centre for neighbour order (default: the registry's suburb centres). */
|
|
79
83
|
centreOf?: SuburbCentreLookup;
|
|
84
|
+
/**
|
|
85
|
+
* The site's estate plan (site-core `siteStrategy.estate`, 3.14.0): its public slugs map a location page to the
|
|
86
|
+
* registry suburb it names (`ryde-suburb` → `ryde`) and mark region pages (`ryde`), which are not ranked by distance.
|
|
87
|
+
*/
|
|
88
|
+
estate?: BrandlabEstateSlugPlan | null;
|
|
80
89
|
/** The most inline markup one service-area map may carry (default 60 000 bytes, 3.6.0). */
|
|
81
90
|
areaMapMaxBytes?: number;
|
|
91
|
+
/**
|
|
92
|
+
* The sampled crawl a draft pull request runs (`BRANDLAB_ESTATE_SCOPE=sample`, 3.13.0): how many routes and
|
|
93
|
+
* redirects the fixed seed adds, the seed, and which routes a changed file feeds.
|
|
94
|
+
*/
|
|
95
|
+
sample?: BrandlabEstateSampleOptions;
|
|
82
96
|
concurrency?: number;
|
|
83
97
|
timeout?: number;
|
|
84
98
|
}
|
|
@@ -91,6 +105,8 @@ export interface BrandlabPageQualityTestOptions {
|
|
|
91
105
|
businessId?: string | false;
|
|
92
106
|
/** A place's centre for similarity siblings (default: the registry's suburb centres). */
|
|
93
107
|
centreOf?: SuburbCentreLookup;
|
|
108
|
+
/** The site's estate plan, as for the estate journey: public slugs resolve to the places they name. */
|
|
109
|
+
estate?: BrandlabEstateSlugPlan | null;
|
|
94
110
|
concurrency?: number;
|
|
95
111
|
timeout?: number;
|
|
96
112
|
}
|
|
@@ -99,6 +115,25 @@ export interface BrandlabPageQualityFinding {
|
|
|
99
115
|
kind: 'headings' | 'title' | 'title-format' | 'title-duplicate' | 'title-h1' | 'description' | 'description-length' | 'description-duplicate' | 'html-size' | 'similarity' | 'location-review' | 'orphan' | 'noindex-link' | 'og-url' | 'implementation-wording' | 'structured-data';
|
|
100
116
|
detail: string;
|
|
101
117
|
}
|
|
118
|
+
/** The public-slug part of site-core's SiteEstatePlan (mirrored: conformance compiles without site-core). */
|
|
119
|
+
export interface BrandlabEstateSlugPlan {
|
|
120
|
+
slugs?: {
|
|
121
|
+
/** Registry region slug → public slug, for example `{ 'ryde-area': 'ryde' }`. */
|
|
122
|
+
regions?: Readonly<Record<string, string>>;
|
|
123
|
+
/** Registry suburb slug → public slug, for example `{ ryde: 'ryde-suburb' }`. */
|
|
124
|
+
suburbs?: Readonly<Record<string, string>>;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* A location route's place: the registry suburb slug its last path segment names (through the estate plan's public
|
|
129
|
+
* slugs, as site-core's `estatePublicSlug`), and whether it is a region page. A region page has no centre, so its
|
|
130
|
+
* nearby lists are not ranked by distance: it is a region when other location pages sit under it, when its slug is a
|
|
131
|
+
* region's public slug, or when its slug is a suburb's registry slug that the plan moved elsewhere (AA: `ryde`).
|
|
132
|
+
*/
|
|
133
|
+
export declare function estatePlaceResolver(routes: readonly BrandlabEstateRoute[], estate?: BrandlabEstateSlugPlan | null): (route: BrandlabEstateRoute | undefined) => {
|
|
134
|
+
slug: string;
|
|
135
|
+
region: boolean;
|
|
136
|
+
} | undefined;
|
|
102
137
|
export declare function defineBrandlabEstateTests(options: BrandlabEstateTestOptions): void;
|
|
103
138
|
/**
|
|
104
139
|
* Why a page's BreadcrumbList JSON-LD would fail Google's breadcrumb rich result (3.3.3): every
|
|
@@ -167,6 +202,16 @@ export declare function redirectProbe(rule: Pick<BrandlabEstateRedirect, 'source
|
|
|
167
202
|
* defineBrandlabPageQualityTests({ site, routes })
|
|
168
203
|
*/
|
|
169
204
|
export declare function defineBrandlabPageQualityTests(options: BrandlabPageQualityTestOptions): void;
|
|
205
|
+
/**
|
|
206
|
+
* Whether the advisory page-quality crawl runs (3.13.0, client CI v2). CI v2 runs it on its own when a pull request
|
|
207
|
+
* is ready for review, on a manual run and monthly for large estates, and sets `BRANDLAB_PAGE_QUALITY=skip` for the required journeys so
|
|
208
|
+
* drafts and merges do not crawl every page twice. `BRANDLAB_PAGE_QUALITY_DIR` also writes its reports there.
|
|
209
|
+
*/
|
|
210
|
+
export declare function pageQualityRunFromEnv(env?: NodeJS.ProcessEnv): {
|
|
211
|
+
run: boolean;
|
|
212
|
+
reason: string;
|
|
213
|
+
directory?: string;
|
|
214
|
+
};
|
|
170
215
|
/**
|
|
171
216
|
* Absolute (`https://host`) and protocol-relative (`//host`) destinations leave
|
|
172
217
|
* the site; browsers also read `/\host` and `\\host` as protocol-relative.
|
package/dist/estate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"estate.d.ts","sourceRoot":"","sources":["../src/estate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"estate.d.ts","sourceRoot":"","sources":["../src/estate.ts"],"names":[],"mappings":"AA+CA,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAmBL,KAAK,sBAAsB,EAE3B,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACxB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAA6E,KAAK,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAEjJ,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,EAC9B,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,OAAO,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;IAC1D,+FAA+F;IAC/F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gBAAgB,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACzF;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IACpC,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,yFAAyF;IACzF,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,2DAA2D;IAC3D,QAAQ,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE;QAAE,YAAY,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;IACrC,wFAAwF;IACxF,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACvC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,sBAAsB,EAAE,CAAC;IAC9C,6FAA6F;IAC7F,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,oGAAoG;IACpG,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAC3C;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACjD,8FAA8F;IAC9F,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,qFAAqF;IACrF,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACvC,2FAA2F;IAC3F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,MAAM,CAAC,EAAE,2BAA2B,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,8BAA8B;IAC7C,IAAI,EAAE;QAAE,YAAY,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;IACrC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACvC,2GAA2G;IAC3G,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5B,yFAAyF;IACzF,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,uGAAuG;IACvG,MAAM,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EACA,UAAU,GACV,OAAO,GACP,cAAc,GACd,iBAAiB,GACjB,UAAU,GACV,aAAa,GACb,oBAAoB,GACpB,uBAAuB,GACvB,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,QAAQ,GACR,cAAc,GACd,QAAQ,GACR,wBAAwB,GACxB,iBAAiB,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAID,6GAA6G;AAC7G,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE;QACN,iFAAiF;QACjF,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAC3C,iFAAiF;QACjF,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;KAC5C,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,mBAAmB,EAAE,EACtC,MAAM,CAAC,EAAE,sBAAsB,GAAG,IAAI,GACrC,CAAC,KAAK,EAAE,mBAAmB,GAAG,SAAS,KAAK;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAgB3F;AAYD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,yBAAyB,QAgS3E;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,EAAE,CA0B3E;AAED,4HAA4H;AAC5H,wBAAgB,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAUjH;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAkB9G;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,iBAAiB,EAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,WAAW,EAAE,CAAA;CAAE,CAAC,CAenD;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAG7D;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,GAAG,MAAM,EAAE,CAQrF;AAED,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAK5I;AAED,6GAA6G;AAC7G,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,SAAS,CAEhI;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,GAAG,OAAO,CAAC,GAAG,MAAM,EAAE,CAQrG;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ7C;AAED,iHAAiH;AACjH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,sBAAsB,EAAE,QAAQ,GAAG,aAAa,CAAC,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAajK;AAaD;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,8BAA8B,QAqIrF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAMhI;AA+CD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAElE"}
|
package/dist/estate.js
CHANGED
|
@@ -35,17 +35,45 @@
|
|
|
35
35
|
* links from indexable pages to noindex location pages, centerpiece
|
|
36
36
|
* similarity (warn at 0.5, review list at 0.7) and the location review list
|
|
37
37
|
* (location-review.json); pages on that list stay indexed.
|
|
38
|
+
*
|
|
39
|
+
* From 3.13.0 (client CI v2) a draft pull request sets BRANDLAB_ESTATE_SCOPE=sample:
|
|
40
|
+
* the catalogue and redirect journeys check the hubs, the first and last route of
|
|
41
|
+
* each kind, the routes changed files feed and a fixed-seed sample (estate-sample.ts),
|
|
42
|
+
* and say so in an `estate-scope` annotation. Every other run crawls the full estate.
|
|
43
|
+
* BRANDLAB_PAGE_QUALITY=skip skips the advisory crawl, which CI v2 runs on its own.
|
|
38
44
|
*/
|
|
45
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
39
46
|
import { request as httpRequest } from 'node:http';
|
|
47
|
+
import { join } from 'node:path';
|
|
40
48
|
import { expect, test } from '@playwright/test';
|
|
41
49
|
import { areaMapProblems, buildLocationReviewList, centerpieceSimilarity, checkLocationCapacity, descriptionLengthProblem, HTML_MAX_BYTES, headingTexts, loadBrandlabGeography, neighbourOrderProblems, parseSitemapEntries, SIMILARITY_REVIEW, SIMILARITY_WARN, sitemapFamilyProblems, sitemapLastmodProblems, structuredDataProblems, textBlocks, titleFormatProblems, titleHeadingProblems, } from './page-standard.js';
|
|
42
50
|
import { extractSearchCertificationHtmlSnapshot } from './search-certification.js';
|
|
51
|
+
import { changedFilesFromEnv, estateScopeFromEnv, sampleBySeed, sampleEstateRoutes } from './estate-sample.js';
|
|
52
|
+
export { changedFilesFromEnv, changedFileStem, defaultRoutesForFile, estateScopeFromEnv, sampleBySeed, sampleEstateRoutes, } from './estate-sample.js';
|
|
43
53
|
const LOCATION_KINDS = new Set(['location', 'service-location']);
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
/**
|
|
55
|
+
* A location route's place: the registry suburb slug its last path segment names (through the estate plan's public
|
|
56
|
+
* slugs, as site-core's `estatePublicSlug`), and whether it is a region page. A region page has no centre, so its
|
|
57
|
+
* nearby lists are not ranked by distance: it is a region when other location pages sit under it, when its slug is a
|
|
58
|
+
* region's public slug, or when its slug is a suburb's registry slug that the plan moved elsewhere (AA: `ryde`).
|
|
59
|
+
*/
|
|
60
|
+
export function estatePlaceResolver(routes, estate) {
|
|
61
|
+
const parents = new Set(routes.filter((route) => LOCATION_KINDS.has(route.kind ?? '')).map((route) => route.parentId));
|
|
62
|
+
const regionSlugs = new Set(Object.values(estate?.slugs?.regions ?? {}));
|
|
63
|
+
const suburbRenames = estate?.slugs?.suburbs ?? {};
|
|
64
|
+
const suburbByPublic = new Map(Object.entries(suburbRenames).map(([registry, publicSlug]) => [publicSlug, registry]));
|
|
65
|
+
return (route) => {
|
|
66
|
+
if (!route || !LOCATION_KINDS.has(route.kind ?? ''))
|
|
67
|
+
return undefined;
|
|
68
|
+
const publicSlug = normalisePath(route.path).split('/').filter(Boolean).at(-1);
|
|
69
|
+
if (!publicSlug)
|
|
70
|
+
return undefined;
|
|
71
|
+
const suburb = suburbByPublic.get(publicSlug);
|
|
72
|
+
const region = (route.id !== undefined && parents.has(route.id)) ||
|
|
73
|
+
regionSlugs.has(publicSlug) ||
|
|
74
|
+
(suburb === undefined && Object.hasOwn(suburbRenames, publicSlug));
|
|
75
|
+
return { slug: suburb ?? publicSlug, region };
|
|
76
|
+
};
|
|
49
77
|
}
|
|
50
78
|
const BUSINESS_TYPES = new Set([
|
|
51
79
|
'LocalBusiness', 'HomeAndConstructionBusiness', 'GeneralContractor', 'Plumber', 'Electrician',
|
|
@@ -71,7 +99,13 @@ export function defineBrandlabEstateTests(options) {
|
|
|
71
99
|
const families = options.families ?? [];
|
|
72
100
|
const familyPaths = new Set(families.flatMap((family) => family.paths.map(normalisePath)));
|
|
73
101
|
const routeByPath = new Map(routes.map((route) => [normalisePath(route.path), route]));
|
|
102
|
+
const placeOfRoute = estatePlaceResolver(routes, options.estate);
|
|
74
103
|
const homePath = normalisePath(routes.find((route) => route.kind === 'home')?.path ?? '/');
|
|
104
|
+
// A draft pull request crawls a sample (BRANDLAB_ESTATE_SCOPE=sample); ready for review crawls every route.
|
|
105
|
+
const scope = estateScopeFromEnv();
|
|
106
|
+
const sample = sampleEstateRoutes(routes, { scope: scope.scope, changedFiles: changedFilesFromEnv(), ...options.sample });
|
|
107
|
+
const scopeNote = [sample.summary, scope.note].filter(Boolean).join('; ');
|
|
108
|
+
const crawledRedirects = scope.scope === 'sample' ? sampleBySeed(redirects, (rule) => rule.source, options.sample) : redirects;
|
|
75
109
|
test.describe('Brandlab technical estate', () => {
|
|
76
110
|
test('the sitemap lists exactly the indexable catalogue routes', async ({ request }) => {
|
|
77
111
|
test.setTimeout(timeout);
|
|
@@ -107,7 +141,8 @@ export function defineBrandlabEstateTests(options) {
|
|
|
107
141
|
const internalLinks = new Map();
|
|
108
142
|
const checked = [];
|
|
109
143
|
const areaMapLayers = new Map();
|
|
110
|
-
|
|
144
|
+
testInfo.annotations.push({ type: 'estate-scope', description: scopeNote });
|
|
145
|
+
await eachWithConcurrency(sample.routes, options.concurrency ?? 8, async (route) => {
|
|
111
146
|
const [get, head] = await Promise.all([
|
|
112
147
|
request.get(route.path, { maxRedirects: 0 }),
|
|
113
148
|
request.head(route.path, { maxRedirects: 0 }),
|
|
@@ -158,13 +193,16 @@ export function defineBrandlabEstateTests(options) {
|
|
|
158
193
|
business: options.business,
|
|
159
194
|
}))
|
|
160
195
|
failures.push(`${route.path}: ${problem}`);
|
|
161
|
-
const place =
|
|
162
|
-
if (place && centreOf(place)) {
|
|
196
|
+
const place = placeOfRoute(route);
|
|
197
|
+
if (place && !place.region && centreOf(place.slug)) {
|
|
163
198
|
for (const problem of neighbourOrderProblems({
|
|
164
199
|
html,
|
|
165
|
-
origin: place,
|
|
200
|
+
origin: place.slug,
|
|
166
201
|
pageUrl: expectedCanonical,
|
|
167
|
-
placeOf: (path) =>
|
|
202
|
+
placeOf: (path) => {
|
|
203
|
+
const linked = placeOfRoute(routeByPath.get(path));
|
|
204
|
+
return linked && !linked.region ? linked.slug : undefined;
|
|
205
|
+
},
|
|
168
206
|
centreOf,
|
|
169
207
|
}))
|
|
170
208
|
failures.push(`${route.path}: ${problem}`);
|
|
@@ -223,7 +261,10 @@ export function defineBrandlabEstateTests(options) {
|
|
|
223
261
|
test.setTimeout(timeout);
|
|
224
262
|
const failures = [];
|
|
225
263
|
const skipped = [];
|
|
226
|
-
|
|
264
|
+
if (scope.scope === 'sample') {
|
|
265
|
+
testInfo.annotations.push({ type: 'estate-scope', description: `sample: ${crawledRedirects.length} of ${redirects.length} redirects (fixed seed); every redirect is checked when the pull request is ready for review` });
|
|
266
|
+
}
|
|
267
|
+
for (const rule of crawledRedirects) {
|
|
227
268
|
const probe = redirectProbe(rule);
|
|
228
269
|
if (!probe) {
|
|
229
270
|
skipped.push(`${rule.source} → ${rule.destination}`);
|
|
@@ -556,8 +597,11 @@ export function defineBrandlabPageQualityTests(options) {
|
|
|
556
597
|
const timeout = options.timeout ?? 240_000;
|
|
557
598
|
const routes = options.routes.filter((route) => normalisePath(route.path) !== NOT_FOUND_ROUTE);
|
|
558
599
|
const routeByPath = new Map(routes.map((route) => [normalisePath(route.path), route]));
|
|
600
|
+
const placeOfRoute = estatePlaceResolver(routes, options.estate);
|
|
601
|
+
const run = pageQualityRunFromEnv();
|
|
559
602
|
test.describe('Brandlab page quality (advisory)', () => {
|
|
560
603
|
test('reports page and structured-data quality without failing', async ({ request }, testInfo) => {
|
|
604
|
+
test.skip(!run.run, run.reason);
|
|
561
605
|
test.setTimeout(timeout);
|
|
562
606
|
const findings = [];
|
|
563
607
|
const geography = await loadBrandlabGeography();
|
|
@@ -587,9 +631,9 @@ export function defineBrandlabPageQualityTests(options) {
|
|
|
587
631
|
if (formatProblems.length > 0)
|
|
588
632
|
add('title-format', `"${snapshot.title}": ${formatProblems.join('; ')}`);
|
|
589
633
|
titles.set(snapshot.title, [...(titles.get(snapshot.title) ?? []), route.path]);
|
|
590
|
-
const place =
|
|
634
|
+
const place = placeOfRoute(route);
|
|
591
635
|
if (h1s.length === 1) {
|
|
592
|
-
const problems = titleHeadingProblems(snapshot.title, h1s[0], place ? geography.placeNames.get(place) : undefined);
|
|
636
|
+
const problems = titleHeadingProblems(snapshot.title, h1s[0], place && !place.region ? geography.placeNames.get(place.slug) : undefined);
|
|
593
637
|
if (problems.length > 0)
|
|
594
638
|
add('title-h1', problems.join('; '));
|
|
595
639
|
}
|
|
@@ -612,7 +656,7 @@ export function defineBrandlabPageQualityTests(options) {
|
|
|
612
656
|
for (const failure of inspectStructuredData(route, snapshot.jsonLd, businessId)) {
|
|
613
657
|
add('structured-data', failure.slice(route.path.length + 2));
|
|
614
658
|
}
|
|
615
|
-
const place =
|
|
659
|
+
const place = placeOfRoute(route)?.slug;
|
|
616
660
|
if (indexable && place)
|
|
617
661
|
similarityPages.push({ path: route.path, place, blocks: textBlocks(html).map(geography.maskPlaces) });
|
|
618
662
|
const targets = new Set();
|
|
@@ -673,19 +717,23 @@ export function defineBrandlabPageQualityTests(options) {
|
|
|
673
717
|
findings.push({ path: item.path, kind: 'location-review', detail: item.reasons.join('; ') });
|
|
674
718
|
findings.sort((a, b) => a.path.localeCompare(b.path) || a.kind.localeCompare(b.kind));
|
|
675
719
|
const byKind = Object.fromEntries([...new Set(findings.map((finding) => finding.kind))].sort().map((kind) => [kind, findings.filter((finding) => finding.kind === kind).length]));
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
});
|
|
680
|
-
await testInfo.attach('location-review.json', {
|
|
681
|
-
body: JSON.stringify({
|
|
720
|
+
const reports = {
|
|
721
|
+
'page-quality.json': JSON.stringify({ pages: routes.length, findings: findings.length, byKind, details: findings }, null, 2),
|
|
722
|
+
'location-review.json': JSON.stringify({
|
|
682
723
|
reviewed: review.length,
|
|
683
724
|
note: 'Pages on the review list stay indexed. Each review ends in adding evidence, merging into the nearest owner or hiding with noindex, follow; merging and hiding are Romie\'s decision.',
|
|
684
725
|
similarity: similarity.sort((a, b) => b.score - a.score),
|
|
685
726
|
review,
|
|
686
727
|
}, null, 2),
|
|
687
|
-
|
|
688
|
-
|
|
728
|
+
};
|
|
729
|
+
for (const [name, body] of Object.entries(reports))
|
|
730
|
+
await testInfo.attach(name, { body, contentType: 'application/json' });
|
|
731
|
+
// CI's page-quality run keeps the reports beside the ready report (BRANDLAB_PAGE_QUALITY_DIR=docs/ready).
|
|
732
|
+
if (run.directory) {
|
|
733
|
+
mkdirSync(run.directory, { recursive: true });
|
|
734
|
+
for (const [name, body] of Object.entries(reports))
|
|
735
|
+
writeFileSync(join(run.directory, name), `${body}\n`);
|
|
736
|
+
}
|
|
689
737
|
testInfo.annotations.push({
|
|
690
738
|
type: 'advisory',
|
|
691
739
|
description: `${findings.length} page-quality finding(s) across ${routes.length} page(s)${findings.length ? `: ${Object.entries(byKind).map(([kind, count]) => `${kind} ${count}`).join(', ')}` : ''}; see page-quality.json.`,
|
|
@@ -693,6 +741,18 @@ export function defineBrandlabPageQualityTests(options) {
|
|
|
693
741
|
});
|
|
694
742
|
});
|
|
695
743
|
}
|
|
744
|
+
/**
|
|
745
|
+
* Whether the advisory page-quality crawl runs (3.13.0, client CI v2). CI v2 runs it on its own when a pull request
|
|
746
|
+
* is ready for review, on a manual run and monthly for large estates, and sets `BRANDLAB_PAGE_QUALITY=skip` for the required journeys so
|
|
747
|
+
* drafts and merges do not crawl every page twice. `BRANDLAB_PAGE_QUALITY_DIR` also writes its reports there.
|
|
748
|
+
*/
|
|
749
|
+
export function pageQualityRunFromEnv(env = process.env) {
|
|
750
|
+
const directory = env.BRANDLAB_PAGE_QUALITY_DIR?.trim() || undefined;
|
|
751
|
+
if ((env.BRANDLAB_PAGE_QUALITY ?? '').trim() === 'skip') {
|
|
752
|
+
return { run: false, reason: 'The advisory page-quality crawl runs on its own in CI (ready for review, manual runs and monthly for large estates); BRANDLAB_PAGE_QUALITY=skip.' };
|
|
753
|
+
}
|
|
754
|
+
return { run: true, reason: '', ...(directory ? { directory } : {}) };
|
|
755
|
+
}
|
|
696
756
|
function inspectStructuredData(route, payloads, businessId) {
|
|
697
757
|
const failures = [];
|
|
698
758
|
if (payloads.some((payload) => payload.parseError))
|