test(sw): align overview regression checks with approved changes
This commit is contained in:
1 parent
7fd73425c5
commit
522ecbd193
1 file changed
+35
-8
@@ -5,28 +5,55 @@ import { execFileSync } from 'node:child_process'
|
||||
import path from 'node:path'
|
||||
import http from 'node:http'
|
||||
import { parse } from '@babel/parser'
|
||||
import { swStoryEntries, retainedInventory, inventorySignature, retiredDropdownIds, integratedChangedFiles } from './storybook-inventory.mjs'
|
||||
import { swStoryEntries, retainedInventory, inventorySignature, retiredDropdownIds, integratedChangedFiles, integratedStoryIds, retiredPropertyPanelIds, retiredUnusedDemoIds, movedHomeMenuIds, retiredHomeMenuCompareIds, movedToPopupIds } from './storybook-inventory.mjs'
|
||||
import { loadPlaywright } from './playwright-runtime.mjs'
|
||||
|
||||
const out = path.resolve(process.argv[2] ?? 'docs/sw/storybook-overview-qa')
|
||||
await mkdir(out, { recursive: true })
|
||||
const report = { baseCommit: '50bf5bda8e956a4973b923d64131f2f2285bd38c', checks: [], errors: [], sizes: [] }
|
||||
// 2026-10-20 (test alignment): 50bf5bd and its before-index.json stay the baseline. The checks below accept only the
|
||||
// approved changes after it (storybook-inventory.mjs and the ledgers here) and the approved main merge 553e4df.
|
||||
const report = { baseCommit: '50bf5bda8e956a4973b923d64131f2f2285bd38c', approvedMainCommit: '553e4df4a865aaf6514b1c2b86cb2e3e9a658e5f', checks: [], errors: [], sizes: [] }
|
||||
const check = (name, ok, data) => { report.checks.push({ name, status: ok ? 'PASS' : 'FAIL', data }); console.log(ok ? 'PASS' : 'FAIL', name) }
|
||||
const before = JSON.parse(await readFile(path.join(out, 'before-index.json'), 'utf8'))
|
||||
const after = swStoryEntries(JSON.parse(await readFile('storybook-static/index.json', 'utf8')))
|
||||
const index = JSON.parse(await readFile('storybook-static/index.json', 'utf8'))
|
||||
const after = swStoryEntries(index)
|
||||
await writeFile(path.join(out, 'after-index.json'), JSON.stringify(after, null, 2) + '\n')
|
||||
check('24 retained Story IDs, names and tags; only approved Dropdown retirements', after.length === 24 && JSON.stringify(inventorySignature(retainedInventory(before))) === JSON.stringify(inventorySignature(after)))
|
||||
// 2b0e1a2 sidebar simplification: these two keep their ID and direct URL with the verification tag.
|
||||
const verificationTaggedIds = ['sw-header--window-move-motion', 'sw-header--k-guide']
|
||||
const removedHeaderIds = [...retiredPropertyPanelIds, ...retiredUnusedDemoIds, ...movedToPopupIds]
|
||||
// 170a5c2 home menu merge: the sidebar Story sw-header--home-menu plus the moved sw-header--home-menu-* Stories.
|
||||
const addedHeaderIds = [...integratedStoryIds.filter(id => id.startsWith('sw-header--')), 'sw-header--home-menu', ...Object.values(movedHomeMenuIds).filter(id => !retiredHomeMenuCompareIds.includes(id))]
|
||||
const expectedRetained = retainedInventory(before).filter(entry => !removedHeaderIds.includes(entry.id))
|
||||
.map(entry => verificationTaggedIds.includes(entry.id) ? { ...entry, tags: [...entry.tags, 'verification'] } : entry)
|
||||
const inventory = { expected: expectedRetained.length + addedHeaderIds.length, actual: after.length, missingAdded: addedHeaderIds.filter(id => !after.some(e => e.id === id)), movedToPopup: movedToPopupIds.map(id => index.entries[id]?.title ?? null) }
|
||||
check(`${inventory.expected} SW/Header Story IDs: the 24 retained at 50bf5bd (names, tags) minus approved retirements and the Popup move, plus approved additions`,
|
||||
after.length === inventory.expected && inventory.missingAdded.length === 0 && inventory.movedToPopup.every(title => title === 'SW/Popup')
|
||||
&& JSON.stringify(inventorySignature(expectedRetained)) === JSON.stringify(inventorySignature(after.filter(e => !addedHeaderIds.includes(e.id)))), inventory)
|
||||
const source = await readFile('src/sw/sw-header.stories.tsx', 'utf8')
|
||||
const baseline = execFileSync('git', ['show', `${report.baseCommit}:src/sw/sw-header.stories.tsx`], { encoding: 'utf8' })
|
||||
const stories = text => Object.fromEntries(parse(text, { sourceType:'module', plugins:['typescript','jsx'] }).program.body
|
||||
.filter(n => n.type === 'ExportNamedDeclaration' && n.declaration.type === 'VariableDeclaration').map(n => [n.declaration.declarations[0].id.name, text.slice(n.start,n.end).replaceAll('\r\n','\n')]))
|
||||
const oldStories = stories(baseline), newStories = stories(source)
|
||||
const retiredExports = ['LayerDropdown','ColorDropdown','LineStyleDropdown','TextDimensionStyleDropdown','ComposedControls']
|
||||
check('retained non-Overview declarations byte-identical; relocated composition separately verified', Object.keys(oldStories).filter(k => k !== 'Overview' && !retiredExports.includes(k)).every(k => oldStories[k] === newStories[k]))
|
||||
// A declaration changed after 50bf5bd must equal its version at the approving commit; a removed one must be an approved
|
||||
// retirement, or the Popup move compared in its new file. All others stay byte-identical to 50bf5bd.
|
||||
const gitStories = (commit, file) => stories(execFileSync('git', ['show', `${commit}:${file}`], { encoding: 'utf8' }))
|
||||
const storyId = name => `sw-header--${name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}`
|
||||
const approvedDeclarations = { ResponsiveMore: '170a5c23aed10f8fc65e5b20e9b94bd3c1ff1227', WindowMoveMotion: '2b0e1a2262002aedb01c29c5d18ffae1219bf3ef', KGuide: '2b0e1a2262002aedb01c29c5d18ffae1219bf3ef' } // measured More section; verification tag
|
||||
const movedDeclarations = { PropertyPanelCompare: ['src/sw/sw-property-panel.stories.tsx', '2b0e1a2262002aedb01c29c5d18ffae1219bf3ef'] }
|
||||
const movedNow = Object.fromEntries(await Promise.all(Object.entries(movedDeclarations).map(async ([name, [file]]) => [name, stories(await readFile(file, 'utf8'))[name]])))
|
||||
const declarationIssues = Object.keys(oldStories).filter(k => k !== 'Overview' && !retiredExports.includes(k)).filter(k => {
|
||||
if (movedDeclarations[k]) return !!newStories[k] || movedNow[k] !== gitStories(movedDeclarations[k][1], movedDeclarations[k][0])[k]
|
||||
if (!newStories[k]) return ![...retiredPropertyPanelIds, ...retiredUnusedDemoIds].includes(storyId(k))
|
||||
return newStories[k] !== (approvedDeclarations[k] ? gitStories(approvedDeclarations[k], 'src/sw/sw-header.stories.tsx')[k] : oldStories[k])
|
||||
})
|
||||
check('retained non-Overview declarations byte-identical to 50bf5bd or their approving commit; only approved removals; relocated composition separately verified', declarationIssues.length === 0, declarationIssues)
|
||||
check('retired Dropdown IDs absent', retiredDropdownIds.every(id => !after.some(e => e.id === id)))
|
||||
// Since 2026-10-19 the production source changes only in the integrated-improvement files (their own suites verify them).
|
||||
const sourceDiff = execFileSync('git', ['diff', '--name-only', report.baseCommit, '--', 'packages/sw/src'], { encoding:'utf8' }).trim().split(/\r?\n/).filter(Boolean)
|
||||
check('package production source untouched except the integrated-improvement files', sourceDiff.every(file => integratedChangedFiles.includes(file)), sourceDiff.filter(file => !integratedChangedFiles.includes(file)))
|
||||
// The package source may differ from the approved main merge only in the integrated-improvement files (their own suites
|
||||
// verify them). Approved package changes after 50bf5bd are listed as sinceBaseline; update approvedMainCommit on the next approved merge.
|
||||
const sourceFiles = (...refs) => execFileSync('git', ['diff', '--name-only', ...refs, '--', 'packages/sw/src'], { encoding:'utf8' }).trim().split(/\r?\n/).filter(Boolean)
|
||||
const sourceDiff = { unapproved: sourceFiles(report.approvedMainCommit).filter(file => !integratedChangedFiles.includes(file)), sinceBaseline: sourceFiles(report.baseCommit).length }
|
||||
check('package production source unchanged since the approved main merge except the integrated-improvement files', sourceDiff.unapproved.length === 0, sourceDiff)
|
||||
|
||||
const root = path.resolve('storybook-static'), mime = { '.html':'text/html','.js':'text/javascript','.css':'text/css','.json':'application/json','.svg':'image/svg+xml','.ttf':'font/ttf','.png':'image/png' }
|
||||
const server = http.createServer(async(req,res) => {
|
||||
|
||||
Reference in new issue
Block a user