feat: add configurable operator appearance and layout

This commit is contained in:
2026-07-23 11:14:10 +09:00
parent cf117de144
commit f43483533a
34 changed files with 5942 additions and 740 deletions

View File

@@ -5,8 +5,10 @@ import { createHash, randomBytes } from "node:crypto";
const exactTargetUrl = "https://legacy-parity.mbn.local/index.html";
const expectedSourceSha256 =
"1EE76BC464FB1C44C8B6546E99CDC21C726C0E2C24AD344F5C19AE41BFC0D2F2";
const expectedInitialDestinationSha256 =
const expectedFreshDestinationSha256 =
"13C07BA64587549EDA9C1A694F3DFA19875CB1F888F02C9E071F17C47739E087";
const expectedImportedDestinationSha256 =
"2575C2420800670B0022EBEE819BA3D9640DB14E9552426DF46075D079F1F697";
const expectedImportConfirmation =
"현재 저장된 비교쌍의 순서는 그대로 유지하고, 원본 프로그램의 비교쌍 중 없는 항목만 뒤에 추가할까요?";
const allowedOutboundTypes = new Set([
@@ -142,8 +144,11 @@ const evidence = {
files: {
expectedSourceSha256,
expectedSourceRowCount: 8,
expectedInitialDestinationSha256,
expectedInitialDestinationRowCount: 1,
expectedFreshDestinationSha256,
expectedFreshDestinationRowCount: 1,
expectedImportedDestinationSha256,
expectedImportedDestinationRowCount: 9,
initialDestinationMode: null,
settingsBaseline: null,
destinationAfterFirstImport: null,
destinationAfterSecondImport: null
@@ -155,6 +160,7 @@ const evidence = {
},
comparison: {
initialPair: null,
initialPairCount: null,
afterFirstImport: null,
afterSecondImport: null,
initialPairPreserved: false,
@@ -651,14 +657,20 @@ async function waitForTrustedPointerClick(targetId, startIndex) {
}
function assertExactPreflightFiles(files) {
const isFresh = files.destination?.sha256 === expectedFreshDestinationSha256 &&
files.destination?.rowCount === 1;
const isAlreadyImported =
files.destination?.sha256 === expectedImportedDestinationSha256 &&
files.destination?.rowCount === 9;
if (files.source?.sha256 !== expectedSourceSha256 || files.source?.rowCount !== 8 ||
files.source?.isReadOnlySource !== true ||
files.destination?.sha256 !== expectedInitialDestinationSha256 ||
files.destination?.rowCount !== 1 ||
(!isFresh && !isAlreadyImported) ||
files.destination?.firstPair !== "KRX100지수|코스피 지수") {
failKnown("The original source or one-row current comparison baseline changed.");
failKnown("The original source or known current comparison baseline changed.");
}
evidence.files.settingsBaseline = files.settings;
evidence.files.initialDestinationMode = isFresh ? "fresh" : "alreadyImported";
return evidence.files.initialDestinationMode;
}
function assertSettingsUnchanged(files, label) {
@@ -676,7 +688,10 @@ async function runSequence() {
{ allowBusy: false, allowMissingState: true });
const preflight = await requestExchange("observe-local-files");
assertExactPreflightFiles(preflight.files);
const baselineMode = assertExactPreflightFiles(preflight.files);
const expectedInitialPairCount = baselineMode === "fresh" ? 1 : 9;
const expectedFirstAdded = baselineMode === "fresh" ? 8 : 0;
const expectedFirstSkipped = baselineMode === "fresh" ? 0 : 8;
evidence.checkpoints.push({ name: "exact-file-preflight", files: preflight.files });
await requestExchange("application-click", "settings-navigation");
@@ -708,19 +723,25 @@ async function runSequence() {
evidence.settings.settingsFileUnchanged = true;
await requestExchange("application-click", "comparison-navigation");
let comparison = await waitFor("comparison one-row baseline", snapshot =>
let comparison = await waitFor(`comparison ${baselineMode} baseline`, snapshot =>
snapshot.state.isBusy !== true && snapshot.dom.activeTabId === "comparison" &&
snapshot.state.comparison?.isBusy !== true &&
snapshot.state.comparison?.savedPairs?.length === 1 &&
snapshot.state.comparison?.savedPairs?.length === expectedInitialPairCount &&
snapshot.state.comparison?.canImportLegacyPairs === true,
{ allowBusy: false });
const initialPair = stablePair(comparison.state.comparison.savedPairs[0]);
{ allowBusy: true });
const initialPairs = comparison.state.comparison.savedPairs.map(stablePair);
const initialPair = initialPairs[0];
if (typeof initialPair?.rowId !== "string" || initialPair.rowId.length === 0 ||
initialPair.rowNumber !== 1 ||
initialPair.displayLabel !== "KRX100지수 ↔ 코스피 지수") {
failKnown("The visible one-row comparison baseline is not the expected known pair.");
failKnown("The visible comparison baseline does not start with the expected known pair.");
}
evidence.comparison.initialPair = initialPair;
if (initialPairs.some((pair, index) => typeof pair?.rowId !== "string" ||
pair.rowId.length === 0 || pair.rowNumber !== index + 1)) {
failKnown("The visible comparison baseline does not retain sequential rows.");
}
evidence.comparison.initialPairCount = initialPairs.length;
await requestExchange("application-click", "comparison-import");
await waitFor("first import confirmation", snapshot =>
@@ -737,12 +758,14 @@ async function runSequence() {
return snapshot.state.isBusy !== true && model?.isBusy !== true &&
snapshot.dom.legacyDialogHidden === true && model?.savedPairs?.length === 9 &&
receipt?.sourceSha256 === expectedSourceSha256 && receipt?.sourceRowCount === 8 &&
receipt?.addedPairCount === 8 && receipt?.skippedDuplicateCount === 0 &&
receipt?.addedPairCount === expectedFirstAdded &&
receipt?.skippedDuplicateCount === expectedFirstSkipped &&
receipt?.totalPairCount === 9;
}, { allowBusy: true, allowDialog: true, timeoutMilliseconds: 90_000 });
const afterFirstPairs = comparison.state.comparison.savedPairs.map(stablePair);
if (JSON.stringify(afterFirstPairs[0]) !== JSON.stringify(initialPair)) {
failKnown("The original current comparison pair was not preserved at the front.");
if (JSON.stringify(afterFirstPairs.slice(0, initialPairs.length)) !==
JSON.stringify(initialPairs)) {
failKnown("The existing ordered comparison pairs were not preserved.");
}
evidence.comparison.initialPairPreserved = true;
evidence.comparison.afterFirstImport = {
@@ -754,8 +777,8 @@ async function runSequence() {
assertSettingsUnchanged(afterFirstFiles.files, "after the first comparison import");
if (afterFirstFiles.files.destination?.rowCount !== 9 ||
afterFirstFiles.files.destination?.firstPair !== "KRX100지수|코스피 지수" ||
afterFirstFiles.files.destination?.sha256 === expectedInitialDestinationSha256) {
failKnown("The first comparison import file is not an appended nine-row result.");
afterFirstFiles.files.destination?.sha256 !== expectedImportedDestinationSha256) {
failKnown("The first comparison import did not retain the known nine-row result.");
}
evidence.files.destinationAfterFirstImport = afterFirstFiles.files.destination;