Country Posters wall draws its sheets through makeCountrySheet
The wall had its own inline wrapper around generateCountrySheet, a second
copy of @map/packet/countrySheet.js, which the Regions tab uses. Two copies
of one wrapper drifted twice (a teal Regions print, then India's series). The
wall now calls makeCountrySheet with the same country list, still handing it
the data's own English record rather than the localized card.
Behaviour: identical sheets for every country with records; a country whose
records come back empty now yields no sheet (the wall falls through to its
stored preview), as the Regions tab already did.
check:country-sheet-one-path (in gates:pre, with a self-test) parses every
plugin source: a direct generateCountrySheet call fails, every
makeCountrySheet call must pass the series list, both known callers must be
found, and seriesFor gives named answers over the real data (India 1 of 1,
each 3x3 country N of 173, an absent code no series). It fails on
development c4655072a for all three defects.
This commit is contained in:
parent
ea9211a5f9
commit
ffbb07e4a4
3 changed files with 170 additions and 15 deletions
|
|
@ -49,6 +49,8 @@
|
|||
"check:composite-token-url": "node internal/check-composite-token-url.mjs",
|
||||
"check:composites-mount": "node internal/check-composites-mount.mjs",
|
||||
"check:country-names": "node internal/check-country-names.mjs",
|
||||
"check:country-sheet-one-path": "node plugin-designs/country-posters/scripts/check-country-sheet-one-path.mjs",
|
||||
"check:country-sheet-one-path:self-test": "node plugin-designs/country-posters/scripts/check-country-sheet-one-path.mjs --self-test",
|
||||
"check:coverage-consistent": "node internal/check-gospel-coverage-consistent.mjs",
|
||||
"check:coverage-consistent:chained": "VERIFY_MAX_AGE_DAYS=36500 node internal/check-gospel-coverage-consistent.mjs",
|
||||
"check:coverage-consistent:poison": "node internal/check-gospel-coverage-consistent.mjs --poison",
|
||||
|
|
@ -344,7 +346,7 @@
|
|||
"fix:sri": "node internal/fix-sri.mjs",
|
||||
"gallery": "node internal/generate-gallery.mjs",
|
||||
"gates:post": "node internal/run-parallel.mjs --label=post -- check:capability-derived check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:zoning-declared check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:backlog-refs check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:auto-engine check:engine-contract check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:no-silent-engine:self-test check:regions-uses-library check:shadow-css-parses check:square-map-controls check:preset-config-merge check:instance-config-keys check:frozen-labels check:plural-forms check:region-fill-repaint check:region-paint-single-source check:poster-print-fallback check:posters-exportable check:country-names check:reduced-motion check:no-dead-library check:universal-props check:readme-size check:tree-relative check:leaf-hints check:snippet-engine check:snippet-engine:self-test check:embed-base check:link-coverage check:no-public-binds check:shared-reached check:tree-weight",
|
||||
"gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:gospel-set-fit check:describe-group check:one-scroll-container check:packet-charts check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:i18n-never-translated check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:board-map-options check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin check:ported-globals-identical check:ring-bands check:packet-a1-structure check:batch-selection-export check:zone-title-tier-a check:evangelical-share check:map-labels check:packet-offline check:help-reachable check:gated-not-in-tree check:window-globals-released check:legend-data-contract check:no-new-driver-references check:poster-fetch-guard check:people-group-records check:sentence-slots check:popup-href-escaped check:popup-pan-controls check:country-map-refetch check:pane-config-reforward check:viewport-meta check:viewport-meta:self-test check:label-key-coverage check:poster-browser-equiv check:poster-render-pinned check:posterverse-shelf check:host-locale-fetch check:print-frame-reclaim check:click-sheet check:coverage-consistent:chained check:coverage-consistent:poison check:dashboard-filter-a11y check:filter-multivalue check:hover-channel check:kit-options-reachable check:layout-key-namespaced check:legend-mirrors check:map-unmount-race check:no-conflict-markers check:series-isolation check:snapshot-guard check:stale-sweep-claim check:surfaces-read-the-tier check:translator-contacts check:dxlife check:wall-wysiwyg check:wall-print-watch check:wall-no-bg-photos check:wall-pan check:template-i18n check:list-pattern-doc check:shadow-style-labels-unique check:grid-image-loading check:retry-fetch",
|
||||
"gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:gospel-set-fit check:describe-group check:one-scroll-container check:packet-charts check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:i18n-never-translated check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:board-map-options check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin check:ported-globals-identical check:ring-bands check:packet-a1-structure check:batch-selection-export check:zone-title-tier-a check:evangelical-share check:map-labels check:packet-offline check:help-reachable check:gated-not-in-tree check:window-globals-released check:legend-data-contract check:no-new-driver-references check:poster-fetch-guard check:people-group-records check:sentence-slots check:popup-href-escaped check:popup-pan-controls check:country-map-refetch check:pane-config-reforward check:viewport-meta check:viewport-meta:self-test check:label-key-coverage check:poster-browser-equiv check:poster-render-pinned check:country-sheet-one-path check:country-sheet-one-path:self-test check:posterverse-shelf check:host-locale-fetch check:print-frame-reclaim check:click-sheet check:coverage-consistent:chained check:coverage-consistent:poison check:dashboard-filter-a11y check:filter-multivalue check:hover-channel check:kit-options-reachable check:layout-key-namespaced check:legend-mirrors check:map-unmount-race check:no-conflict-markers check:series-isolation check:snapshot-guard check:stale-sweep-claim check:surfaces-read-the-tier check:translator-contacts check:dxlife check:wall-wysiwyg check:wall-print-watch check:wall-no-bg-photos check:wall-pan check:template-i18n check:list-pattern-doc check:shadow-style-labels-unique check:grid-image-loading check:retry-fetch",
|
||||
"gen:iso3-to-alpha2": "node library/scripts/generate-iso3-to-alpha2.mjs",
|
||||
"internet:adm1": "node plugin-designs/cities-internet/scripts/build-adm1-internet.mjs",
|
||||
"library-index": "node internal/generate-library-index.mjs",
|
||||
|
|
@ -446,6 +448,8 @@
|
|||
"check:composite-token-url": "a composite embedded BARE (no outer ProfileLoader) must honour a tokenUrl-only config: it reads tokenUrl, forwards it, and asks the token service only when no token is given. The expectation is derived from ProfileLoader, not restated. Headless; in gates:pre.",
|
||||
"check:composites-mount": "every capability-4/5 composite's OWN setup() and mounted hooks run without throwing, in en and ar, headless (no browser): vite loads each composite as its bundle entry defines it (configureApp, and ProfileLoader's provides where the entry mounts ProfileLoader), and @vue/runtime-core mounts it on a no-op renderer. Exists because 08f89f55 shipped two composites calling getRuntimeLocale/detectLocale/RTL_LOCALES with no import: the build was green, setup threw a ReferenceError in every locale, and Big Picture and Simple Research rendered nothing. Proves setup runs; cannot prove the children render.",
|
||||
"check:country-names": "every ISO 3166-1 alpha-3 our data uses must resolve to a localized country name. Guards the JOIN, not Intl: library/data/iso3-to-alpha2.json drifting from library/data/countries.json makes a country render as a bare code, which still looks like a value. Checks en/ar/zh (ar is our only RTL locale; zh makes an untranslated Latin code unmistakable) and asserts BOTH directions of drift. Carries two negative arms — an unassigned code and a malformed one — and FAILS if either resolves, because a coverage count cannot otherwise tell a hit from a lookup that returns its input.",
|
||||
"check:country-sheet-one-path": "a country sheet is drawn through ONE path, @map/packet/countrySheet.js, so one country cannot print two posters. Parses (comments cannot count) every plugin source naming generateCountrySheet/makeCountrySheet: a direct generateCountrySheet in code FAILS, every makeCountrySheet call must pass `countries` (the series list), and both known callers (the Country Posters wall, regions-list) must be found. Also runs seriesFor over the real country-posters.json with named answers: India 1 of 1, every 3x3 country N of the 3x3 tier with no repeat, a code absent from the data no series.",
|
||||
"check:country-sheet-one-path:self-test": "plants each defect check:country-sheet-one-path guards (a direct generateCountrySheet call, makeCountrySheet without countries, the old guard that skipped India, a position given to an absent code) and requires each to FAIL; a comment naming generateCountrySheet must NOT.",
|
||||
"check:coverage-consistent": "the gospel-resources surfaces must agree: no resource on an ISO RESERVED code (und/mul/mis/zxx), every resourced code NAMED, table reach recounted off the pins, no invented URL, tiers reconcile. Static, sub-second. Self-test: `bun run check:coverage-consistent:poison`, all 12 clauses (A-L) must fail. Clause L is the one that is not about consistency: it fails when the link-verification record goes stale, because 'link checked' is a claim about the present tense. Written after the same drift defect was found by hand three times.",
|
||||
"check:csv-shape": "the export must OPEN as a table: header on line 0, every row matching. Run the `check:csv-shape:self-test` script for the NEGATIVE test (forces the provenance comment back to line 0 IN MEMORY and requires this gate to REJECT it) — the header used to claim 'NEGATIVE-TESTED' with no way to re-run it.",
|
||||
"check:dashboard-filter-a11y": "the dashboard's cross-filter is operable and audible: every filter chip is a <button type=\"button\"> with an aria-label naming the ACTION (a filter that can be set by keyboard must be clearable by keyboard), the shell carries exactly ONE always-rendered polite role=\"status\" region so a brush that changes a number far from the control is announced once rather than six times or not at all, and no resize handle's accessible name is a raw English literal. AST over the parsed templates plus the built bundle — no browser (rega's alone).",
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import { getRuntimeLocale, detectLocale } from '@map/i18n/index.js'
|
|||
// library/. Generated by library/scripts/posters/emit-countries.mjs, which now writes there.
|
||||
import data from '@map/data/country-posters.json'
|
||||
import { resolveAssetsBase } from '@map/utils/embedBase.js'
|
||||
import { makeCountrySheet } from '@map/packet/countrySheet.js'
|
||||
// The regions catalogue holds the not-yet sentence a host's country gets when no poster exists (all eleven locales);
|
||||
// its keys are English sentences with dots, so it is read by literal key, the way regions-list.vue reads it.
|
||||
import { useRegionsI18n } from '@map/i18n/useRegionsI18n.js'
|
||||
|
|
@ -125,23 +126,20 @@ const recordsFor = createPeopleGroupRecords({
|
|||
profileConfig,
|
||||
})
|
||||
const countryRecords = (c) => recordsFor({ type: 'country', values: [c.iso3] })
|
||||
// ★ GENERATED AT THE CLICK: the country's sheet is drawn in the browser from its people groups and the
|
||||
// world boundaries (packet/posterSheets.js — the same generator the Node script runs), for the poster
|
||||
// print (letter, vector) and the packet's page 1. No stored sheet exists for a country; this is the path.
|
||||
// The series facts are the ones generate.mjs states in country mode: map N of M over the countries on
|
||||
// the same sheet size, and their total groups.
|
||||
// ★ GENERATED AT THE CLICK, through the ONE shared path (@map/packet/countrySheet.js, which the Regions
|
||||
// tab uses too): the country's sheet is drawn in the browser from its people groups and the world
|
||||
// boundaries, for the poster print (letter, vector) and the packet's page 1. No stored sheet exists for a
|
||||
// country. The series facts (map N of M over the countries on the same sheet size) come from the same
|
||||
// list, so this wall and the Regions tab print the same numbers for one country.
|
||||
// ⚠ THE SHEET GETS THE DATA'S OWN RECORD, not the card: the generated poster is English by recorded
|
||||
// decision (series.json english_only_note), so it must not receive a localized label, and the series index
|
||||
// is found by id because a language switch rebuilds the card objects.
|
||||
async function countrySheet(card, format) {
|
||||
const c = byId.get(card.id) || card
|
||||
const [{ generateCountrySheet }, { ISO3_NAME }, records] = await Promise.all([
|
||||
import('@map/packet/posterSheets.js'), import('@map/data/iso3-name.js'), countryRecords(c),
|
||||
])
|
||||
const tier = (data.countries || []).filter((x) => x.sheet === c.sheet)
|
||||
const series = { totalGroups: tier.reduce((a, x) => a + (x.count || 0), 0), totalLists: tier.length, index: tier.findIndex((x) => x.id === c.id) + 1 }
|
||||
return generateCountrySheet({ country: c, records, assetsBase: assetsBase.value, ISO3_NAME, format, series })
|
||||
}
|
||||
const sheetForRecord = makeCountrySheet({
|
||||
records: recordsFor,
|
||||
assetsBase: () => assetsBase.value,
|
||||
countries: data.countries || [],
|
||||
})
|
||||
const countrySheet = (card, format) => sheetForRecord(byId.get(card.id) || card, format)
|
||||
// The name table the generated sheet was labelled from, so a packet in another language relabels page 1's
|
||||
// country names (the same seam the UPG-100 wall passes).
|
||||
const iso3Names = () => import('@map/data/iso3-name.js').then((m) => m.ISO3_NAME)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,153 @@
|
|||
#!/usr/bin/env node
|
||||
// check:country-sheet-one-path — a country's map sheet is drawn through ONE path, so one country
|
||||
// cannot print two different posters.
|
||||
//
|
||||
// WHY: two callers draw a country sheet (the Country Posters wall and the Regions tab's regions-list).
|
||||
// Each once had its own wrapper around generateCountrySheet, and they drifted twice: the Regions tab
|
||||
// printed a country in flat teal while the wall printed it in its region's hue, and the Regions tab
|
||||
// printed India's packet page 1 as "map 1 of 0" while the wall said "map 1 of 1". Both now go through
|
||||
// @map/packet/countrySheet.js (makeCountrySheet + seriesFor).
|
||||
//
|
||||
// ARM A (structure, over the TREE): every plugin file whose CODE (parsed, so comments cannot count)
|
||||
// names generateCountrySheet is a violation; every caller of makeCountrySheet passes `countries`, so
|
||||
// its "map N of M" is computed from the same list; and at least the two known callers are found (a
|
||||
// census that finds fewer is NOT MEASURED, never a pass).
|
||||
// ARM B (behaviour, over the real library/data/country-posters.json): seriesFor gives named answers:
|
||||
// India (sheet null) is 1 of 1, every 3x3 country is N of the 3x3 tier with no repeated N, a code
|
||||
// absent from the data gets no series.
|
||||
//
|
||||
// Exit 0 pass, 1 fail, 75 not measured. `--self-test` plants each defect and requires its arm to fail.
|
||||
import { readFileSync, readdirSync, statSync } from 'node:fs'
|
||||
import { join, relative, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { parse as parseSfc, babelParse } from '@vue/compiler-sfc'
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..')
|
||||
const PD = join(ROOT, 'plugin-designs')
|
||||
const KNOWN_CALLERS = ['plugin-designs/country-posters/country-poster-wall.vue', 'plugin-designs/regions-dashboard/regions-list.vue']
|
||||
|
||||
function scriptsOf(file, src) {
|
||||
if (!file.endsWith('.vue')) return [src]
|
||||
const { descriptor, errors } = parseSfc(src, { filename: file })
|
||||
if (errors.length) throw new Error(`${file}: SFC parse error: ${errors[0].message || errors[0]}`)
|
||||
return [descriptor.script, descriptor.scriptSetup].filter(Boolean).map((b) => b.content)
|
||||
}
|
||||
|
||||
function walk(node, visit) {
|
||||
if (!node || typeof node.type !== 'string') return
|
||||
visit(node)
|
||||
for (const k of Object.keys(node)) {
|
||||
if (k === 'loc' || k === 'start' || k === 'end' || k === 'leadingComments' || k === 'trailingComments') continue
|
||||
const v = node[k]
|
||||
if (Array.isArray(v)) v.forEach((c) => c && typeof c.type === 'string' && walk(c, visit))
|
||||
else if (v && typeof v.type === 'string') walk(v, visit)
|
||||
}
|
||||
}
|
||||
|
||||
// -> { names: Set of identifier names used in code, makeCalls: [{ hasCountries }] }
|
||||
function codeFacts(file, src) {
|
||||
const names = new Set()
|
||||
const makeCalls = []
|
||||
for (const code of scriptsOf(file, src)) {
|
||||
const ast = babelParse(code, { sourceType: 'module', plugins: ['typescript'] })
|
||||
walk(ast.program, (n) => {
|
||||
if (n.type === 'Identifier') names.add(n.name)
|
||||
if (n.type === 'CallExpression' && n.callee.type === 'Identifier' && n.callee.name === 'makeCountrySheet') {
|
||||
const arg = n.arguments[0]
|
||||
const hasCountries = !!(arg && arg.type === 'ObjectExpression'
|
||||
&& arg.properties.some((p) => p.key && (p.key.name === 'countries' || p.key.value === 'countries')))
|
||||
makeCalls.push({ hasCountries })
|
||||
}
|
||||
})
|
||||
}
|
||||
return { names, makeCalls }
|
||||
}
|
||||
|
||||
function listSources(dir, out = []) {
|
||||
for (const e of readdirSync(dir)) {
|
||||
if (e === 'node_modules' || e.startsWith('.')) continue
|
||||
const p = join(dir, e)
|
||||
const st = statSync(p)
|
||||
if (st.isDirectory()) listSources(p, out)
|
||||
else if (/\.(vue|js|mjs)$/.test(e) && !/\/scripts\//.test(p)) out.push(p)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ARM A over a { relPath: source } map. Returns failure strings; `callers` gets the files that call makeCountrySheet.
|
||||
export function armA(files, callers = []) {
|
||||
const fails = []
|
||||
for (const [rel, src] of Object.entries(files)) {
|
||||
if (!/generateCountrySheet|makeCountrySheet/.test(src)) continue // a text prefilter; the parse decides
|
||||
const { names, makeCalls } = codeFacts(rel, src)
|
||||
if (names.has('generateCountrySheet')) fails.push(`${rel}: draws a country sheet directly (generateCountrySheet in code); use makeCountrySheet from @map/packet/countrySheet.js`)
|
||||
if (makeCalls.length) callers.push(rel)
|
||||
makeCalls.forEach((c, i) => { if (!c.hasCountries) fails.push(`${rel}: makeCountrySheet call #${i + 1} passes no \`countries\`, so its "map N of M" is not the wall's`) })
|
||||
}
|
||||
return fails
|
||||
}
|
||||
|
||||
// ARM B: named expectations for seriesFor over the real data.
|
||||
export function armB(seriesFor, countries) {
|
||||
const fails = []
|
||||
const by = (iso) => countries.find((c) => c.iso3 === iso)
|
||||
const ind = by('IND')
|
||||
const s = ind && seriesFor(countries, ind)
|
||||
if (!ind) fails.push('India is missing from country-posters.json')
|
||||
else if (!s || s.index !== 1 || s.totalLists !== 1 || s.totalGroups !== ind.count) fails.push(`India (sheet ${JSON.stringify(ind.sheet)}) should be map 1 of 1 with ${ind.count} groups; got ${JSON.stringify(s)}`)
|
||||
const t3 = countries.filter((c) => c.sheet === '3x3')
|
||||
const seen = new Set()
|
||||
for (const c of t3) {
|
||||
const r = seriesFor(countries, c)
|
||||
if (!r || r.totalLists !== t3.length || r.index < 1 || r.index > t3.length) { fails.push(`${c.iso3} (3x3) should be N of ${t3.length}; got ${JSON.stringify(r)}`); break }
|
||||
seen.add(r.index)
|
||||
}
|
||||
if (seen.size && seen.size !== t3.length) fails.push(`3x3 tier: ${seen.size} distinct indexes for ${t3.length} countries`)
|
||||
const absent = seriesFor(countries, { id: 'country-zzz', iso3: 'ZZZ', label: 'ZZZ', count: 3 })
|
||||
if (absent !== undefined) fails.push(`a country absent from the data should get no series; got ${JSON.stringify(absent)}`)
|
||||
if (seriesFor(null, ind) !== undefined) fails.push('seriesFor with no list should return undefined')
|
||||
return fails
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const selfTest = process.argv.includes('--self-test')
|
||||
const { seriesFor } = await import(join(ROOT, 'library/packet/countrySheet.js'))
|
||||
const data = JSON.parse(readFileSync(join(ROOT, 'library/data/country-posters.json'), 'utf8'))
|
||||
const countries = data.countries || []
|
||||
if (countries.length < 170) { console.log(`NOT MEASURED: country-posters.json parsed ${countries.length} countries (floor 170)`); process.exitCode = 75; return }
|
||||
|
||||
const files = {}
|
||||
for (const p of listSources(PD)) files[relative(ROOT, p)] = readFileSync(p, 'utf8')
|
||||
|
||||
if (selfTest) {
|
||||
const wall = KNOWN_CALLERS[0]
|
||||
const cases = [
|
||||
['direct generateCountrySheet call', () => armA({ ...files, [wall]: files[wall].replace('<script setup>', "<script setup>\nimport { generateCountrySheet } from '@map/packet/posterSheets.js'\ngenerateCountrySheet({})") })],
|
||||
['makeCountrySheet without countries', () => armA({ ...files, [wall]: files[wall].replace(/countries: data\.countries \|\| \[\],/, '') })],
|
||||
['India skipped (the old guard)', () => armB((l, it) => (!it || !it.sheet ? undefined : seriesFor(l, it)), countries)],
|
||||
['absent code given a position', () => armB((l, it) => seriesFor(l, it) || { index: 1, totalLists: 1, totalGroups: 0 }, countries)],
|
||||
]
|
||||
let bad = 0
|
||||
for (const [name, run] of cases) {
|
||||
const f = run()
|
||||
console.log(`${f.length ? '✓ caught' : '✗ MISSED'} ${name}${f.length ? ` -> ${f[0]}` : ''}`)
|
||||
if (!f.length) bad++
|
||||
}
|
||||
// The negative control: a comment naming the forbidden call must NOT fail.
|
||||
const commented = armA({ ...files, [wall]: files[wall].replace('<script setup>', '<script setup>\n// generateCountrySheet(x) is not called here') })
|
||||
console.log(`${commented.length ? '✗ FALSE RED' : '✓ ignored'} a comment naming generateCountrySheet`)
|
||||
if (commented.length) bad++
|
||||
process.exitCode = bad ? 1 : 0
|
||||
return
|
||||
}
|
||||
|
||||
const callers = []
|
||||
const fails = [...armA(files, callers), ...armB(seriesFor, countries)]
|
||||
const missing = KNOWN_CALLERS.filter((k) => !callers.includes(k))
|
||||
if (missing.length) fails.push(`known callers not found calling makeCountrySheet: ${missing.join(', ')}`)
|
||||
if (fails.length) { fails.forEach((f) => console.log(`FAIL ${f}`)); process.exitCode = 1; return }
|
||||
const ind = seriesFor(countries, countries.find((c) => c.iso3 === 'IND'))
|
||||
console.log(`PASS check:country-sheet-one-path — ${Object.keys(files).length} plugin sources parsed-or-skipped, makeCountrySheet callers: ${callers.join(', ')}; seriesFor over ${countries.length} countries (India ${ind.index} of ${ind.totalLists})`)
|
||||
}
|
||||
|
||||
main().catch((e) => { console.error(`FAIL ${e.stack || e}`); process.exitCode = 1 })
|
||||
Loading…
Reference in a new issue