mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
Правило из #334 требовало объявлять только сцены со статусом different, а missing-baseline пропускало без вопросов. При закрытии #346 из-за этого три эталона каталога устройств стали контрактом без единого взгляда. Половина прежнего обоснования верна и остаётся: расхождение растеризации новая сцена выявить не может, параллельность среды доказывают только сцены с эталонами. Но правило отвечало лишь на вопрос «та ли это среда» и молчало про второй — «правильный ли это кадр». Пустой, обрезанный или снятый в неверном состоянии кадр новая сцена закрепляет так же надёжно, как испорченный старый, и README об этом предупреждает прямо. Поэтому флагов два и они утверждают разное: --expect-change — «я знаю, почему старый кадр изменился», --expect-new — «я посмотрел на новый кадр». Имя в чужом флаге тоже останавливает приёмку: путаница означает, что ревьюер думал об одной сцене, а утверждал про другую. Новые эталоны печатаются отдельной строкой «СТАНУТ КОНТРАКТОМ ВПЕРВЫЕ», а не растворяются в общем списке — раньше они там и растворились. Прежний тест «новая сцена объявления не требует» заменён: он кодировал снятое правило. Два мутанта проверены руками — возврат молчаливого пропуска и разрешённая путаница флагов, — каждый убит. Issue: #350 User-Visible: no
77 lines
4.0 KiB
JavaScript
77 lines
4.0 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* Приёмка эталонов с объявлением намерения (#334).
|
||
*
|
||
* node scripts/golden-accept.mjs --reviewed --expect-change=<id,id>
|
||
* node scripts/golden-accept.mjs --reviewed --expect-new=<id,id>
|
||
* node scripts/golden-accept.mjs --reviewed --expect-change=<id> --from=<распакованный артефакт>
|
||
*
|
||
* Два флага утверждают разное: `--expect-change` — «я знаю, почему старый кадр
|
||
* изменился», `--expect-new` — «я посмотрел на новый кадр». Путаница между ними
|
||
* останавливает приёмку (#350).
|
||
*
|
||
* Обёртка над `demo/golden/accept.mjs`, а не правка его самого: любой `.mjs` из
|
||
* `demo/golden` входит в `sourceFingerprint`, поэтому его правка объявляет
|
||
* устаревшими бандл и оба манифеста — см. `scripts/golden-acceptance.mjs`.
|
||
*
|
||
* Проверка идёт ДО делегирования: `accept.mjs` копирует картинки целым набором,
|
||
* и запрет обязан сработать раньше, чем каталог эталонов будет тронут.
|
||
*/
|
||
import { spawnSync } from 'node:child_process';
|
||
import { existsSync, readFileSync } from 'node:fs';
|
||
import { dirname, resolve } from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
import { goldenAcceptanceRefusal, goldenSilentDeclarations } from './golden-acceptance.mjs';
|
||
|
||
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||
const argv = process.argv.slice(2);
|
||
const value = (name) => {
|
||
const found = argv.find((item) => item.startsWith(`--${name}=`));
|
||
return found ? found.slice(name.length + 3) : '';
|
||
};
|
||
|
||
if (!argv.includes('--reviewed')) {
|
||
console.error('приёмка требует явного --reviewed');
|
||
process.exit(2);
|
||
}
|
||
const from = resolve(value('from') || resolve(ROOT, 'artifacts/golden'));
|
||
const list = (name) => value(name).split(',').map((id) => id.trim()).filter(Boolean);
|
||
const declared = list('expect-change');
|
||
const declaredNew = list('expect-new');
|
||
|
||
const reportPath = resolve(from, 'golden-report.json');
|
||
if (!existsSync(reportPath)) {
|
||
console.error(`отчёт кандидатов не найден: ${reportPath}`);
|
||
process.exit(2);
|
||
}
|
||
const report = JSON.parse(readFileSync(reportPath, 'utf8'));
|
||
|
||
const refusal = goldenAcceptanceRefusal(report.results, declared, declaredNew);
|
||
if (refusal) {
|
||
console.error(refusal);
|
||
process.exit(1);
|
||
}
|
||
|
||
const silent = goldenSilentDeclarations(report.results, declared);
|
||
if (silent.length) {
|
||
console.log(`Объявлены как изменённые, но совпали с эталоном: ${silent.join(', ')}.`);
|
||
}
|
||
// Новые эталоны печатаются отдельной строкой, а не в общем списке: раньше они
|
||
// растворялись среди изменившихся, и три кадра каталога устройств уехали в
|
||
// контракт незамеченными (#350).
|
||
const fresh = (report.results || []).filter((result) => result.status === 'missing-baseline')
|
||
.map((result) => result.id).sort();
|
||
const changed = (report.results || []).filter((result) => result.status === 'different')
|
||
.map((result) => result.id).sort();
|
||
console.log(changed.length
|
||
? `Будут заменены эталоны: ${changed.join(', ')}.`
|
||
: 'Ни один существующий эталон не изменился.');
|
||
if (fresh.length) console.log(`СТАНУТ КОНТРАКТОМ ВПЕРВЫЕ: ${fresh.join(', ')}.`);
|
||
if (!changed.length && !fresh.length) console.log('Будет перезаписан только манифест.');
|
||
console.log(`Съёмка: chromium ${report.chromium || '?'}, матрица ${report.matrixVersion}.`);
|
||
|
||
const accept = spawnSync(process.execPath, [
|
||
resolve(ROOT, 'demo/golden/accept.mjs'), '--reviewed', `--from=${from}`,
|
||
], { cwd: ROOT, stdio: 'inherit' });
|
||
process.exit(accept.status ?? 1);
|