diff --git a/scripts/__tests__/plugin-published-stylesheet.test.ts b/scripts/__tests__/plugin-published-stylesheet.test.ts index f3cc15176..2e3463d5e 100644 --- a/scripts/__tests__/plugin-published-stylesheet.test.ts +++ b/scripts/__tests__/plugin-published-stylesheet.test.ts @@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url'; import * as gridStylesheet from '../../packages/plugin-grid/scripts/build-css.mjs'; import * as kanbanStylesheet from '../../packages/plugin-kanban/scripts/build-css.mjs'; import * as fieldsStylesheet from '../../packages/fields/scripts/build-css.mjs'; -import { classesOf, COMPONENTS_ENTRY, REPO_ROOT } from '../build-plugin-stylesheet.mjs'; +import { classesOf, COMPONENTS_ENTRY, REPO_ROOT, defaultHeader } from '../build-plugin-stylesheet.mjs'; /** * objectui#4929: `@object-ui/plugin-grid` and `@object-ui/plugin-kanban` now @@ -199,6 +199,54 @@ describe('published supplement stylesheets (objectui#4929, objectui#6438)', () = describe.each(SUBJECTS.map(({ name }) => name))('%s', (name) => { const themed = CARD_THEMED[name]; + /** + * objectui#7044: the emitted sheet's banner is part of the published + * artifact (objectui#6405's acceptance gate was byte-identity of that + * file), yet nothing here read it. Two checks, deliberately independent + * of each other, because the obvious single check is vacuous: recomputing + * "expected" by calling the very function that ALSO produced "actual" + * (`defaultHeader`, or fields' own `buildOptions.header`) can never be + * surprised by a mutation to that function's wording — both sides move + * together. Neither check below has that shape: + * + * 1. the emitted sheet starts with the header this subject is + * CONFIGURED to use — its own declared `buildOptions.header` when it + * has one (`fields`, objectui#4059/#6405), else the shared + * `defaultHeader(PACKAGE_NAME)` this module now exports for exactly + * this. Catches the ASSEMBLY breaking (`header` no longer prepended, + * or some third, unrelated value used) — never a second hand-spelled + * copy of either wording, which would be free to drift from the real + * one while staying green. + * 2. that header actually NAMES this package: `mod.PACKAGE_NAME` is a + * plain string constant each build script declares independently + * (never derived from `defaultHeader`), so a broken interpolation + * inside `defaultHeader` cannot hide behind (1)'s self-consistent + * recomputation — this is what gives (1) teeth against a mutation to + * `defaultHeader` itself, for the two subjects that inherit it. + */ + it('opens the emitted sheet with the banner it declares', () => { + const { css } = built.get(name) as Built; + const { mod } = SUBJECTS.find((s) => s.name === name)!; + const declaredHeader = (mod.buildOptions as { header?: string }).header; + const expectedHeader = declaredHeader ?? defaultHeader(mod.PACKAGE_NAME); + expect(css.startsWith(`${expectedHeader}\n`)).toBe(true); + expect(css.slice(0, expectedHeader.length)).toContain(mod.PACKAGE_NAME); + }); + + // fields-only: the control that (1) above cannot provide, because + // dropping fields' `header` override moves both its "expected" and + // "actual" to the shared default IN LOCKSTEP — exactly the failure mode + // objectui#7044 was filed over ("an accidental drop of fields' per-package + // `header`, would ship silently"). This checks the emitted sheet against + // the OTHER branch's value, independent of whichever branch (1) took. + if (name === 'fields') { + it('does not fall back to the shared default banner', () => { + const { css } = built.get('fields') as Built; + const { mod } = SUBJECTS.find((s) => s.name === 'fields')!; + expect(css.startsWith(`${defaultHeader(mod.PACKAGE_NAME)}\n`)).toBe(false); + }); + } + it('emits the themed utilities only this build can produce', () => { const { classes } = built.get(name) as Built; expect(themed.filter((cls) => !classes.has(cls))).toEqual([]); diff --git a/scripts/build-plugin-stylesheet.mjs b/scripts/build-plugin-stylesheet.mjs index 1805f2cf4..a9356572a 100644 --- a/scripts/build-plugin-stylesheet.mjs +++ b/scripts/build-plugin-stylesheet.mjs @@ -214,8 +214,12 @@ export function indexSheet(rootNode) { * those exact bytes are part of a published artifact (objectui#6405). Wording * that would be an improvement everywhere else is a diff in a published file * there. A package with no such history passes no `header` and inherits this. + * + * Exported (objectui#7044) so `scripts/__tests__/plugin-published-stylesheet.test.ts` + * can pin the emitted banner against the real default instead of a second, + * hand-spelled copy that would be free to drift from it while staying green. */ -function defaultHeader(packageName) { +export function defaultHeader(packageName) { return [ `/*! ${packageName} — utilities this package adds on top of @object-ui/components.`, ' *',