mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-12 23:33:43 +08:00
Keep every post-59948b5 change inside verification/. The verifier owns its npm working directory and sanitized Docker context, so no release metadata or root Docker configuration is modified.
147 lines
7.3 KiB
JavaScript
147 lines
7.3 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { assessAction } from '../../src/browser/action-effects.mjs';
|
|
import { fillInput } from '../../src/browser/action-state.mjs';
|
|
import { clickControl, reorderSortable, stateDifference, stateSnapshot } from '../../src/browser/actions.mjs';
|
|
import { semanticDifference } from '../../src/browser/semantic.mjs';
|
|
import { BROWSER_FIXTURE_ORIGIN } from '../../src/browser/init.mjs';
|
|
import { requestFailureBucket } from '../../src/browser/session.mjs';
|
|
import { parseAndroidVersion } from '../../src/checks/android-config.mjs';
|
|
import { coverageArgs } from '../../src/checks/coverage.mjs';
|
|
import { projectSourceFile } from '../../src/checks/source-policy.mjs';
|
|
|
|
function evidence(overrides = {}) {
|
|
return {
|
|
url: '/', localStorage: [], sessionStorage: [], clipboard: '', media: [],
|
|
dom: { hash: 'dom', items: [] }, actionState: { hash: 'state', signatures: [] },
|
|
observed: { requests: 0, responses: 0, dialogs: 0, downloads: 0, popups: 0,
|
|
consoleErrors: 0, pageErrors: 0, failedRequests: 0, httpErrors: 0 },
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test('action state hashes ignore origin and locator paths but preserve semantic controls', () => {
|
|
const left = stateSnapshot('http://127.0.0.1:1/settings', [{ signature: 'button|save|0' }]);
|
|
const right = stateSnapshot('https://example.test/settings', [{ signature: 'button|save|0' }]);
|
|
assert.equal(left.hash, right.hash);
|
|
assert.deepEqual(stateDifference(left, { ...right, signatures: ['button|cancel|0'] }), {
|
|
missing: ['button|save|0'], unexpected: ['button|cancel|0'],
|
|
});
|
|
});
|
|
|
|
test('state graphs distinguish one duplicate but bound unbounded identical instances', () => {
|
|
const once = stateSnapshot('https://example.test/', [{ signature: 'button|tag|0' }]);
|
|
const twice = stateSnapshot('https://example.test/', [{ signature: 'button|tag|0' }, { signature: 'button|tag|1' }]);
|
|
const thrice = stateSnapshot('https://example.test/', [
|
|
{ signature: 'button|tag|0' }, { signature: 'button|tag|1' }, { signature: 'button|tag|2' },
|
|
]);
|
|
assert.notEqual(once.hash, twice.hash);
|
|
assert.equal(twice.hash, thrice.hash);
|
|
});
|
|
|
|
test('state graphs preserve sortable order for deterministic replay', () => {
|
|
const left = stateSnapshot('https://example.test/', [
|
|
{ signature: 'tag|popular|0', roleDescription: 'sortable', path: 'main>div:1' },
|
|
{ signature: 'tag|drama|0', roleDescription: 'sortable', path: 'main>div:2' },
|
|
]);
|
|
const right = stateSnapshot('https://example.test/', [
|
|
{ signature: 'tag|drama|0', roleDescription: 'sortable', path: 'main>div:1' },
|
|
{ signature: 'tag|popular|0', roleDescription: 'sortable', path: 'main>div:2' },
|
|
]);
|
|
assert.notEqual(left.hash, right.hash);
|
|
});
|
|
|
|
test('silent controls fail while storage changes are independently observable', () => {
|
|
const action = { aria: '保存', text: '保存' };
|
|
const before = evidence();
|
|
const silent = assessAction(action, { ok: true, operation: 'click' }, before, evidence());
|
|
assert.equal(silent.ok, false);
|
|
assert.equal(silent.failureKind, 'no-effect');
|
|
const changed = assessAction(action, { ok: true, operation: 'click' }, before,
|
|
evidence({ localStorage: [['saved', 'true']] }));
|
|
assert.equal(changed.ok, true);
|
|
assert.deepEqual(changed.effects, ['localStorage']);
|
|
});
|
|
|
|
test('selected choices and current-location links are explicit idempotent actions', () => {
|
|
const before = evidence({ url: '/settings' });
|
|
const selected = assessAction({ selected: true }, { ok: true }, before, before);
|
|
assert.equal(selected.idempotent, true);
|
|
const link = assessAction({ href: '/settings' }, { ok: true }, before, before);
|
|
assert.equal(link.idempotent, true);
|
|
});
|
|
|
|
test('blank-target clicks arm popup capture before clicking and preserve the final URL', async () => {
|
|
let armed = false;
|
|
let closed = false;
|
|
const popup = { waitForLoadState: async () => {}, url: () => 'https://example.test/result',
|
|
close: async () => { closed = true; } };
|
|
const page = { waitForEvent: async (name) => { assert.equal(name, 'popup'); armed = true; return popup; } };
|
|
const locator = { click: async () => { assert.equal(armed, true); } };
|
|
const result = await clickControl(page, locator, '_blank');
|
|
assert.deepEqual(result, { operation: 'click', popupUrl: 'https://example.test/result' });
|
|
assert.equal(closed, true);
|
|
});
|
|
|
|
test('sortable controls skip singleton groups and use the keyboard sensor otherwise', async () => {
|
|
const singleton = await reorderSortable({ locator: () => ({ count: async () => 1 }) }, {});
|
|
assert.equal(singleton.idempotent, true);
|
|
const keys = [];
|
|
let order = ['|one', '|two'];
|
|
const peers = { count: async () => 2, evaluateAll: async (_callback, targetId) => targetId
|
|
? { index: 0, order: [...order] } : [...order] };
|
|
const page = { locator: () => peers, keyboard: { press: async (key) => {
|
|
keys.push(key); if (key === 'ArrowRight') order = ['|two', '|one'];
|
|
} }, waitForTimeout: async () => {} };
|
|
const locator = { getAttribute: async () => '7', focus: async () => {} };
|
|
const moved = await reorderSortable(page, locator);
|
|
assert.equal(moved.operation, 'keyboard-sort');
|
|
assert.deepEqual(moved.afterOrder, ['|two', '|one']);
|
|
assert.deepEqual(keys, ['Space', 'ArrowRight', 'Space']);
|
|
});
|
|
|
|
test('intentional browser request cancellation is recorded separately from failures', () => {
|
|
assert.equal(requestFailureBucket('net::ERR_ABORTED'), 'abortedRequests');
|
|
assert.equal(requestFailureBucket('net::ERR_CONNECTION_REFUSED'), 'failedRequests');
|
|
});
|
|
|
|
test('seek controls require directional media proof', () => {
|
|
const before = evidence({ media: [{ currentTime: 20, paused: true, muted: true, volume: 1, playbackRate: 1, currentSrc: 'x' }] });
|
|
const after = evidence({ media: [{ currentTime: 10, paused: true, muted: true, volume: 1, playbackRate: 1, currentSrc: 'x' }] });
|
|
const result = assessAction({ aria: '后退 10 秒', text: '' }, { ok: true }, before, after);
|
|
assert.equal(result.ok, true);
|
|
assert.equal(result.proof.kind, 'seek-backward');
|
|
});
|
|
|
|
test('URL inputs use the local interceptable import fixture', async () => {
|
|
let value = '';
|
|
await fillInput({ fill: async (next) => { value = next; } }, 'url');
|
|
assert.equal(value, `${BROWSER_FIXTURE_ORIGIN}/source-import.json`);
|
|
});
|
|
|
|
test('filling an input with its existing deterministic value is idempotent', async () => {
|
|
const result = await fillInput({ inputValue: async () => '验证', fill: async () => {} }, 'text');
|
|
assert.equal(result.idempotent, true);
|
|
});
|
|
|
|
test('semantic deltas expose missing and unexpected visible controls', () => {
|
|
assert.deepEqual(semanticDifference({ items: ['a', 'b'] }, { items: ['b', 'c'] }), {
|
|
missing: ['a'], unexpected: ['c'],
|
|
});
|
|
});
|
|
|
|
test('coverage, Android, and line policies are strict by construction', () => {
|
|
const ctx = { config: { verifyDir: '/verify' } };
|
|
const args = coverageArgs(ctx, '/reports');
|
|
assert.ok(args.includes('--all'));
|
|
assert.ok(args.includes('--100'));
|
|
assert.ok(args.includes('app'));
|
|
assert.ok(args.includes('components'));
|
|
assert.ok(args.includes('lib'));
|
|
assert.ok(args.includes('scripts'));
|
|
assert.deepEqual(parseAndroidVersion('versionCode = 9\nversionName = "4.9.20"'), { versionName: '4.9.20', versionCode: 9 });
|
|
assert.equal(projectSourceFile('/repo/app-release.json'), true);
|
|
assert.equal(projectSourceFile('/repo/Dockerfile'), true);
|
|
assert.equal(projectSourceFile('/repo/package-lock.json'), false);
|
|
});
|