mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
ci: prove the screenshot environment instead of trusting the place
Правило приёмки скриншотов было про место: снимать только в CI. Обоснование измерено — съёмка в другом окружении переписывает файлы без содержательных изменений, в #231 два из девяти на 7–8 байт, набор с беты все девять. Но держалось правило на комментарии, а не на механизме: кандидат проверялся на самосогласованность и принимался целиком, ни разу не сравниваясь с тем, что лежит в репозитории. Цена видна на #390: правка типов, которая физически не может сдвинуть пиксель, потребовала прогона workflow, а затем правки одиннадцати полей манифеста руками. Теперь правило про доказательство, и оно то же, что у golden с #334: среда доказана, если каждый кадр, который менять не собирались, совпал с закоммиченным байт-в-байт. Расхождение растеризации спрятать нельзя — оно задевает все кадры с текстом сразу. Снимать можно где угодно, включая WSL; принять получится только оттуда, где кадры воспроизводятся, и перестанет получаться в тот день, когда обновятся шрифты. Остальное следует из того же правила: намерение объявляется --expect-change, необъявленное расхождение останавливает приёмку, объявленное без расхождения — тоже (ложная декларация обесценивает список), заменяются ровно объявленные файлы, а тотальная перерисовка требует --no-witnesses --reason, и причина уезжает в манифест. Частый случай закрылся сам: ничего не объявлено, все кадры совпали — принимается один манифест, руками ничего писать не надо. Проверено шестью сквозными прогонами на подделанном артефакте, не только юнитами: идентичный кандидат, необъявленное расхождение, объявленное, молчаливая декларация, тотальная перерисовка без причины и с ней. Issue: #401 User-Visible: no
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import {
|
||||
docsAcceptancePlan, docsSilentDeclarations, docsUnknownDeclarations, docsWitnessFloor,
|
||||
} from '../scripts/docs-acceptance.mjs';
|
||||
|
||||
// #401. Правило приёмки скриншотов перестало быть про место съёмки и стало про
|
||||
// доказательство: среда доказана, если каждый кадр, который менять не
|
||||
// собирались, совпал с закоммиченным байт-в-байт. Здесь закреплён каждый отказ
|
||||
// и каждый путь приёмки — иначе правило живёт только в комментарии, как жило
|
||||
// предыдущее.
|
||||
|
||||
const IDS = ['alpha', 'beta', 'gamma'];
|
||||
const same = { alpha: 'a', beta: 'b', gamma: 'c' };
|
||||
|
||||
test('ничего не объявлено и всё совпало — принимается один манифест (#401)', () => {
|
||||
// Частый случай: правка исходников, которая не может сдвинуть пиксель.
|
||||
// Раньше он требовал прогона workflow и правки манифеста руками (#390).
|
||||
const plan = docsAcceptancePlan({ ids: IDS, committed: same, candidate: { ...same } });
|
||||
assert.equal(plan.refusal, null);
|
||||
assert.deepEqual(plan.replace, []);
|
||||
assert.deepEqual(plan.keep, IDS);
|
||||
assert.equal(plan.witnesses.length, 3);
|
||||
});
|
||||
|
||||
test('объявленный кадр заменяется, остальные не трогаются (#401)', () => {
|
||||
const plan = docsAcceptancePlan({
|
||||
ids: IDS, committed: same, candidate: { ...same, beta: 'иное' }, declared: ['beta'],
|
||||
});
|
||||
assert.equal(plan.refusal, null);
|
||||
assert.deepEqual(plan.replace, ['beta']);
|
||||
// Половина принятого набора хуже непринятого: рядом окажется кадр от одного
|
||||
// дерева и манифест от другого.
|
||||
assert.deepEqual(plan.keep, ['alpha', 'gamma']);
|
||||
assert.deepEqual(plan.witnesses, ['alpha', 'gamma']);
|
||||
});
|
||||
|
||||
test('расхождение без декларации останавливает приёмку (#401)', () => {
|
||||
const plan = docsAcceptancePlan({ ids: IDS, committed: same, candidate: { ...same, gamma: 'иное' } });
|
||||
assert.match(plan.refusal, /разошлись, но не объявлены: gamma/);
|
||||
// Отказ обязан называть оба объяснения: автор не знает, какое из них его.
|
||||
assert.match(plan.refusal, /изменение продукта/);
|
||||
assert.match(plan.refusal, /другой среде/);
|
||||
assert.deepEqual(plan.replace, []);
|
||||
});
|
||||
|
||||
test('молчаливая декларация — тоже отказ (#401)', () => {
|
||||
const plan = docsAcceptancePlan({
|
||||
ids: IDS, committed: same, candidate: { ...same, beta: 'иное' }, declared: ['beta', 'alpha'],
|
||||
});
|
||||
assert.match(plan.refusal, /не изменились: alpha/);
|
||||
});
|
||||
|
||||
test('объявленного имени нет в наборе — отказ до всякой проверки (#401)', () => {
|
||||
const plan = docsAcceptancePlan({
|
||||
ids: IDS, committed: same, candidate: { ...same }, declared: ['опечатка'],
|
||||
});
|
||||
assert.match(plan.refusal, /которых нет в наборе: опечатка/);
|
||||
});
|
||||
|
||||
test('тотальная перерисовка требует явного обхода с причиной (#401)', () => {
|
||||
const all = { alpha: 'x', beta: 'y', gamma: 'z' };
|
||||
const declared = [...IDS];
|
||||
const refused = docsAcceptancePlan({ ids: IDS, committed: same, candidate: all, declared });
|
||||
assert.match(refused.refusal, /свидетелей недостаточно: 0 из необходимых 1/);
|
||||
|
||||
const noReason = docsAcceptancePlan({
|
||||
ids: IDS, committed: same, candidate: all, declared, skipWitnesses: true,
|
||||
});
|
||||
assert.match(noReason.refusal, /требует --reason/);
|
||||
|
||||
const bypassed = docsAcceptancePlan({
|
||||
ids: IDS, committed: same, candidate: all, declared,
|
||||
skipWitnesses: true, skipReason: 'сменился шрифтовый стек',
|
||||
});
|
||||
assert.equal(bypassed.refusal, null);
|
||||
assert.deepEqual(bypassed.replace, IDS);
|
||||
});
|
||||
|
||||
test('порог свидетелей считается так же, как в golden (#401)', () => {
|
||||
// Два набора картинок в одном репозитории не должны требовать от человека
|
||||
// помнить два разных правила.
|
||||
assert.equal(docsWitnessFloor(0), 0);
|
||||
assert.equal(docsWitnessFloor(10), 1);
|
||||
assert.equal(docsWitnessFloor(143), 10);
|
||||
});
|
||||
|
||||
test('вспомогательные проверки называют виновников по именам (#401)', () => {
|
||||
assert.deepEqual(docsUnknownDeclarations(IDS, ['gamma', 'нет-такого']), ['нет-такого']);
|
||||
assert.deepEqual(
|
||||
docsSilentDeclarations({ committed: same, candidate: { ...same }, declared: ['beta', 'alpha'] }),
|
||||
['alpha', 'beta'],
|
||||
);
|
||||
});
|
||||
|
||||
test('кадр без закоммиченной пары не может быть свидетелем (#401)', () => {
|
||||
// Первичная съёмка ничего не доказывает про среду: сравнивать не с чем.
|
||||
const plan = docsAcceptancePlan({
|
||||
ids: IDS,
|
||||
committed: { alpha: 'a' },
|
||||
candidate: { alpha: 'a', beta: 'новое', gamma: 'новое' },
|
||||
declared: ['beta', 'gamma'],
|
||||
});
|
||||
assert.equal(plan.refusal, null);
|
||||
assert.deepEqual(plan.witnesses, ['alpha']);
|
||||
assert.equal(plan.floor, 1);
|
||||
});
|
||||
Reference in New Issue
Block a user