Migrate remaining legacy operator workflows

This commit is contained in:
2026-07-12 05:39:27 +09:00
parent ffb8f43c19
commit a01836a2d7
132 changed files with 20566 additions and 720 deletions

View File

@@ -53,11 +53,35 @@ function enableManualStore(context) {
assert.equal(context.controller.handleMessage("manual-operator-data-status", {
requestId,
available: true,
code: "READY",
writeQuarantined: false,
message: null
}), true);
}
test("interrupted import status remains explicit and disables every manual store action", () => {
const context = fixture();
context.controller.openVi();
const requestId = context.controller.getState().pending.status;
assert.equal(context.controller.handleMessage("manual-operator-data-status", {
requestId,
available: false,
code: "IMPORT_INTERRUPTED",
writeQuarantined: false,
message: "A prior import is unresolved; no FSell or VI file was opened."
}), true);
const state = context.controller.getState();
assert.equal(state.available, false);
assert.equal(state.unavailableCode, "IMPORT_INTERRUPTED");
assert.equal(
state.unavailableMessage,
"A prior import is unresolved; no FSell or VI file was opened.");
assert.equal(context.controller.actions.importLegacyManualData(), false);
assert.equal(context.controller.actions.saveVi(), false);
});
test("FSell cut opens only after five saved rows are re-read unchanged", () => {
const context = fixture();
const { controller } = context;
@@ -343,6 +367,89 @@ test("explicit and host locks block edits, saves, and cut creation", () => {
assert.equal(controller.getState().locked, true);
});
test("legacy FSell and VI import requires explicit confirmation and sends no path", () => {
const context = fixture();
const { controller } = context;
const previousConfirm = globalThis.confirm;
try {
controller.openVi();
enableManualStore(context);
controller.handleMessage("vi-manual-list-data", {
requestId: controller.getState().pending.viRead,
itemCount: 0,
pageCount: 0,
items: [],
version: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
});
globalThis.confirm = () => false;
const before = context.posts.length;
assert.equal(controller.actions.importLegacyManualData(), false);
assert.equal(context.posts.length, before);
let prompt = "";
globalThis.confirm = message => {
prompt = message;
return true;
};
assert.equal(controller.actions.importLegacyManualData(), true);
const request = context.latest("import-legacy-manual-operator-data");
assert.deepEqual(Object.keys(request.payload), ["requestId"]);
assert.equal(/path|directory|sourceRoot/iu.test(JSON.stringify(request)), false);
assert.match(prompt, /기존 대상 파일이 하나라도 있으면/u);
assert.match(prompt, /덮어쓰지 않고 중단/u);
assert.equal(controller.handleMessage("legacy-manual-operator-import-result", {
requestId: request.payload.requestId,
markerVersion: 1,
sourceSha256: "b".repeat(64),
importedAt: "2026-07-12T01:02:03+00:00",
netSellRowCount: 15,
viItemCount: 9
}), true);
assert.ok(controller.getState().pending.viRead);
assert.ok(context.toasts.some(item => item.message.includes("가져왔습니다")));
} finally {
if (previousConfirm === undefined) delete globalThis.confirm;
else globalThis.confirm = previousConfirm;
}
});
test("legacy import failure is terminal and never auto-retries", () => {
const context = fixture();
const { controller } = context;
const previousConfirm = globalThis.confirm;
try {
globalThis.confirm = () => true;
controller.openVi();
enableManualStore(context);
controller.handleMessage("vi-manual-list-data", {
requestId: controller.getState().pending.viRead,
itemCount: 0,
pageCount: 0,
items: [],
version: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
});
controller.actions.importLegacyManualData();
const request = context.latest("import-legacy-manual-operator-data");
const before = context.posts.length;
assert.equal(controller.handleMessage("manual-operator-data-error", {
requestId: request.payload.requestId,
operation: "import-legacy-manual-data",
code: "DESTINATION_NOT_EMPTY",
message: "기존 데이터가 있어 아무 파일도 덮어쓰지 않았습니다.",
retryable: false,
outcomeUnknown: false
}), true);
assert.equal(context.posts.length, before);
assert.equal(controller.getState().pending.legacyImport, null);
} finally {
if (previousConfirm === undefined) delete globalThis.confirm;
else globalThis.confirm = previousConfirm;
}
});
test("controller source constructs DOM without HTML injection or filesystem paths", () => {
const source = fs.readFileSync(
path.resolve(__dirname, "..", "..", "Web", "manual-lists-ui.js"),