修改备注没有带入的问题
This commit is contained in:
@@ -2,6 +2,12 @@ import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import vm from 'node:vm';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { webcrypto } from 'node:crypto';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const plans = require('../chrome-extension/ltjt-order-assistant/operation-plans.js');
|
||||
const hotelBatch = require('../chrome-extension/ltjt-order-assistant/hotel-batch.js');
|
||||
|
||||
const inpage = readFileSync(new URL('../chrome-extension/ltjt-order-assistant/inpage.js', import.meta.url), 'utf8');
|
||||
const adapter = readFileSync(new URL('../chrome-extension/ltjt-order-assistant/lifecycle-adapters.js', import.meta.url), 'utf8')
|
||||
@@ -15,7 +21,7 @@ const adapter = readFileSync(new URL('../chrome-extension/ltjt-order-assistant/l
|
||||
teamSummaryRequery = async (operation, subpage) => subpage;
|
||||
assistant.preflightLifecycleOperation = preflightLifecycleOperation;`);
|
||||
|
||||
function fixture({ occupied = 1, rows = occupied + 2, onSelection = () => {}, onValidate = () => {}, sort = false, onPost = () => {}, linkageDelays = [], onLinkageTick = () => {} } = {}) {
|
||||
function fixture({ occupied = 1, rows = occupied + 2, onSelection = () => {}, onValidate = () => {}, sort = false, onPost = () => {}, linkageDelays = [], onLinkageTick = () => {}, onFieldEvent = () => {}, crypto = webcrypto } = {}) {
|
||||
let now = 10000;
|
||||
let posts = 0;
|
||||
let selections = 0;
|
||||
@@ -25,13 +31,13 @@ function fixture({ occupied = 1, rows = occupied + 2, onSelection = () => {}, on
|
||||
const fields = new Map();
|
||||
const document = { querySelector: () => form, querySelectorAll: () => [], readyState: 'complete', body: { innerText: '' } };
|
||||
const location = { href: 'https://erp.invalid/System/Business/teams_jiudian.asp?tdID=123', pathname: '/System/Business/teams_jiudian.asp' };
|
||||
const window = { document, Event: class {}, location, testValidate: () => onValidate(fields), getComputedStyle: () => ({ display: 'block', visibility: 'visible', opacity: '1' }) };
|
||||
const window = { document, crypto, Event: class {}, location, testValidate: () => onValidate(fields), getComputedStyle: () => ({ display: 'block', visibility: 'visible', opacity: '1' }) };
|
||||
window.parent = window;
|
||||
document.defaultView = window;
|
||||
form.ownerDocument = document;
|
||||
const add = (name, value) => {
|
||||
const control = { name, id: name, value: String(value), type: name === 'SubmitButton' ? 'submit' : 'text', tagName: 'INPUT', disabled: false,
|
||||
ownerDocument: document, dispatchEvent() {},
|
||||
ownerDocument: document, dispatchEvent() { onFieldEvent(fields, name); },
|
||||
getAttribute: (key) => key.toLowerCase() === 'data' ? '270◆测试新酒店◆TWN H◆CNY◆120' : key.toLowerCase() === 'setval' ? name : null };
|
||||
fields.set(name, control); form.elements.push(control);
|
||||
};
|
||||
@@ -204,3 +210,100 @@ test('requery cannot report an old row, duplicate new rows, changed existing row
|
||||
assert.equal(f.posts(), 1);
|
||||
}
|
||||
});
|
||||
|
||||
test('omitted hotel remarks survive selection/date callbacks, save and read-only requery', async () => {
|
||||
for (const omitted of [undefined, '']) for (const crypto of [webcrypto, null]) {
|
||||
const f = fixture({ sort: true, crypto,
|
||||
onSelection(fields, index) { fields.get(`beizhu${index}`).value = '6月21号起 按照包房12间\n有房损另计'; },
|
||||
onFieldEvent(fields, name) { if (/^(danwei|riqi|riqis|jianshu)1$/.test(name)) fields.get('beizhu1').value = ''; }
|
||||
});
|
||||
if (omitted === undefined) delete f.operation.data.arrangement.remark;
|
||||
const before = JSON.stringify(f.operation);
|
||||
const result = await f.api.liveSubmitLifecycleOperation(f.operation);
|
||||
assert.equal(result.status, 'lifecycle_completed', JSON.stringify(result));
|
||||
assert.equal(f.fields.get('beizhu1').value, '6月21号起 按照包房12间\n有房损另计');
|
||||
assert.equal(f.fields.get('beizhu0').value, '保留备注0');
|
||||
assert.equal(JSON.stringify(f.operation), before);
|
||||
assert.equal(result.preflight.hotel_create.version, 2);
|
||||
const checked = result.requery.checks.find(check => check.field === 'beizhu0');
|
||||
assert.equal(checked.actual, f.fields.get('beizhu1').value);
|
||||
assert.equal(checked.matched, true);
|
||||
const verified = await f.api.verifyLifecycleOperation(f.operation, { hotel_create: result.preflight.hotel_create });
|
||||
assert.equal(verified.status, 'lifecycle_requery_matched', JSON.stringify(verified));
|
||||
assert.equal(f.posts(), 1);
|
||||
}
|
||||
});
|
||||
|
||||
test('explicit hotel remark overrides the native default, while missing native notes remain empty', async () => {
|
||||
const f = fixture({ onSelection(fields, index) { fields.get(`beizhu${index}`).value = '酒店默认备注'; } });
|
||||
f.operation.data.arrangement.remark = '用户备注:晚到保留';
|
||||
const result = await f.api.liveSubmitLifecycleOperation(f.operation);
|
||||
assert.equal(result.status, 'lifecycle_completed', JSON.stringify(result));
|
||||
assert.equal(f.fields.get('beizhu1').value, '用户备注:晚到保留');
|
||||
assert.equal(result.preflight.hotel_create.version, 1);
|
||||
const empty = fixture();
|
||||
const blank = await empty.api.liveSubmitLifecycleOperation(empty.operation);
|
||||
assert.equal(blank.status, 'lifecycle_completed', JSON.stringify(blank));
|
||||
assert.equal(empty.fields.get('beizhu1').value, '');
|
||||
// Old executions stored v1 and intentionally wrote the omitted note as ''.
|
||||
const oldProof = { ...blank.preflight.hotel_create, version: 1 };
|
||||
delete oldProof.native_remark_sha256;
|
||||
assert.equal((await empty.api.verifyLifecycleOperation(empty.operation, { hotel_create: oldProof })).status, 'lifecycle_requery_matched');
|
||||
});
|
||||
|
||||
test('native remark changes at validation block POST; missing or changed saved evidence cannot report success', async () => {
|
||||
const selection = (fields, index) => { fields.get(`beizhu${index}`).value = '酒店默认备注'; };
|
||||
const f = fixture({ onSelection: selection, onValidate(fields) { fields.get('beizhu1').value = ''; } });
|
||||
const blocked = await f.api.liveSubmitLifecycleOperation(f.operation);
|
||||
assert.equal(blocked.status, 'lifecycle_apply_blocked', JSON.stringify(blocked));
|
||||
assert.equal(f.posts(), 0);
|
||||
const changed = fixture({ onSelection: selection, onPost(form) { form.elements.find(control => control.name === 'beizhu1').value = ''; } });
|
||||
const uncertain = await changed.api.liveSubmitLifecycleOperation(changed.operation);
|
||||
assert.equal(uncertain.status, 'lifecycle_execution_uncertain');
|
||||
assert.ok(uncertain.requery.blockers.includes('arrangement_create_native_remark_changed_after_save'));
|
||||
const good = fixture({ onSelection: selection });
|
||||
const result = await good.api.liveSubmitLifecycleOperation(good.operation);
|
||||
const proof = structuredClone(result.preflight.hotel_create);
|
||||
delete proof.native_remark_sha256;
|
||||
assert.equal((await good.api.verifyLifecycleOperation(good.operation, { hotel_create: proof })).status, 'lifecycle_requery_not_matched');
|
||||
good.savedForm().elements.find(control => control.name === 'beizhu1').value = 'ERP后续变动';
|
||||
assert.equal((await good.api.verifyLifecycleOperation(good.operation, { hotel_create: result.preflight.hotel_create })).status, 'lifecycle_requery_not_matched');
|
||||
assert.equal(good.posts(), 1);
|
||||
});
|
||||
|
||||
test('batch orchestration preserves each hotel native or explicit remark through actual lifecycle execution', async () => {
|
||||
const base = fixture().operation;
|
||||
const arrangements = [5, 6, 7].map(day => ({ mode: 'create', resource: { name: '测试新酒店TWN', keyword: '测试新酒店TWN' },
|
||||
start_date: `2026-09-0${day}`, end_date: `2026-09-0${day + 1}`, room_count: 12,
|
||||
...(day === 6 ? { remark: '用户独立备注' } : {}) }));
|
||||
const operation = { action: 'arrangement_hotel', order_nature: 'formal', submit_mode: 'dry_run',
|
||||
data: { existing_refs: { identifier: 'LW-TEST-HOTEL' }, arrangements } };
|
||||
const before = JSON.stringify(operation);
|
||||
const results = {}, reports = [];
|
||||
let final;
|
||||
const context = { operationPlans: plans, hotelBatch, throwIfTaskCancelled() {},
|
||||
publishHotelBatch: async (_id, _op, batch) => { final = batch; },
|
||||
chrome: { storage: { local: { get: async () => ({ businessTaskResults: results }) } } },
|
||||
executeLifecycleOperation: async (id, child) => {
|
||||
const index = reports.length;
|
||||
const f = fixture({ onSelection(fields, slot) { fields.get(`beizhu${slot}`).value = `酒店备注${index}`; },
|
||||
onFieldEvent(fields, name) { if (/^(riqi|riqis|jianshu)1$/.test(name)) fields.get('beizhu1').value = ''; },
|
||||
onPost(form) { form.elements.find(control => control.name === 'id1').value = String(999 + index); }
|
||||
});
|
||||
f.operation.data.arrangement = { ...base.data.arrangement, ...child.data.arrangement, resource: base.data.arrangement.resource };
|
||||
const report = await f.api.liveSubmitLifecycleOperation(f.operation);
|
||||
assert.equal(report.status, 'lifecycle_completed', JSON.stringify(report));
|
||||
report.requery = { matched: report.requery.matched, subpage: report.requery };
|
||||
reports.push(report);
|
||||
results[id] = { status: 'completed', report, write_attempted: true, no_erp_write: false };
|
||||
}
|
||||
};
|
||||
const background = readFileSync(new URL('../chrome-extension/ltjt-order-assistant/background.js', import.meta.url), 'utf8');
|
||||
vm.createContext(context);
|
||||
vm.runInContext(background.slice(background.indexOf('async function executeHotelBatch('), background.indexOf('async function runAutoTask(')), context);
|
||||
await context.executeHotelBatch('TASK', operation);
|
||||
assert.equal(hotelBatch.inspect(final).allCompleted, true);
|
||||
assert.deepEqual(reports.map(report => report.requery.subpage.checks.find(check => /^beizhu\d+$/.test(check.field)).actual),
|
||||
['酒店备注0', '用户独立备注', '酒店备注2']);
|
||||
assert.equal(JSON.stringify(operation), before);
|
||||
});
|
||||
|
||||
@@ -123,6 +123,19 @@ test('fresh ERP MAIN world resolves the reported 2027 hotel update using the act
|
||||
assert.equal(f.fields.get('riqis1').value, '2027-9-13');
|
||||
});
|
||||
|
||||
test('hotel replacement preserves the existing note unless the user supplies a new one', async () => {
|
||||
for (const remark of [undefined, '明确的新备注']) {
|
||||
const f = fixture({ onSelection(fields, slot) { fields.get(`beizhu${slot}`).value = '新酒店默认备注'; } });
|
||||
if (remark !== undefined) f.input.data.arrangement.changes.remark = remark;
|
||||
const resolved = f.api.testResolveHotel(f.form, f.input);
|
||||
assert.equal(resolved.ok, true, JSON.stringify(resolved));
|
||||
const applied = await f.api.testApply(f.form, resolved.operation);
|
||||
assert.equal(applied.blockers.length, 0, JSON.stringify(applied));
|
||||
assert.equal(f.fields.get('beizhu1').value, remark ?? '保留备注1');
|
||||
assert.equal(f.fields.get('beizhu0').value, '保留备注0');
|
||||
}
|
||||
});
|
||||
|
||||
test('missing page date dependency is diagnosed before matching and never reported as zero ERP records', () => {
|
||||
const f = fixture({ omitDateMatcher: true });
|
||||
const before = [...f.fields].map(([key, control]) => [key, control.value]);
|
||||
|
||||
@@ -1374,7 +1374,7 @@ test('passenger explicit server success is terminal without a post-save row requ
|
||||
assert.doesNotMatch(passengerBranch, /passengerRequery|verifyLifecycleOperation/);
|
||||
assert.doesNotMatch(background, /attemptAutomaticPassengerReconciliation/);
|
||||
assert.match(background, /名单已取得 ERP 明确成功响应,按业务规则确认录入成功/);
|
||||
assert.match(platformApp, /REQUIRED_EXTENSION_VERSION = '0\.5\.190'/);
|
||||
assert.match(platformApp, /REQUIRED_EXTENSION_VERSION = '0\.5\.191'/);
|
||||
});
|
||||
|
||||
test('uncertain lifecycle writes can only converge through a read-only plugin requery', async () => {
|
||||
@@ -1584,7 +1584,7 @@ test('lifecycle execution keeps the MV3 worker alive until the durable task sett
|
||||
assert.match(background, /arrangement_resource_candidate_data_missing/);
|
||||
});
|
||||
|
||||
test('extension 0.5.190 keeps reload dormant unless its durable execution state is fully idle', async () => {
|
||||
test('extension 0.5.191 keeps reload dormant unless its durable execution state is fully idle', async () => {
|
||||
const [background, bridge] = await Promise.all([
|
||||
readFile(new URL('../chrome-extension/ltjt-order-assistant/background.js', import.meta.url), 'utf8'),
|
||||
readFile(new URL('../chrome-extension/ltjt-order-assistant/business-bridge.js', import.meta.url), 'utf8')
|
||||
@@ -2037,7 +2037,7 @@ test('delete guard distinguishes independent, shared child, and shared parent ro
|
||||
for (const operation of operations) assert.equal(plans.validateOperation(operation).ok, true, plans.validateOperation(operation).blockers.join('; '));
|
||||
|
||||
const mapping = JSON.parse(await readFile(new URL('../mappings/lifecycle.mapping.json', import.meta.url), 'utf8'));
|
||||
assert.equal(mapping.current_extension_version, '0.5.190');
|
||||
assert.equal(mapping.current_extension_version, '0.5.191');
|
||||
assert.equal(
|
||||
mapping.updates.order_update_independent.field_mapped_pending_live_validation['pax.child_no_bed'],
|
||||
'ertrenshu'
|
||||
@@ -2421,10 +2421,10 @@ test('schema and browser adapters contain the v2 safety fields and no confirm ov
|
||||
assert.match(inpage, /`ys_danweiid\$\{index\}`, resolvedCustomerId/);
|
||||
assert.doesNotMatch(inpage, /product_customer_source_region|sourceRegionCheck|source_reference/);
|
||||
assert.doesNotMatch(teamBatchInpage, /product_customer_source_region|sourceRegionCheck/);
|
||||
assert.equal(extensionManifest.version, '0.5.190');
|
||||
assert.match(inpage, /version: '0\.5\.190'/);
|
||||
assert.match(teamBatchInpage, /version: '0\.5\.190'/);
|
||||
assert.match(platformApp, /REQUIRED_EXTENSION_VERSION = '0\.5\.190'/);
|
||||
assert.equal(extensionManifest.version, '0.5.191');
|
||||
assert.match(inpage, /version: '0\.5\.191'/);
|
||||
assert.match(teamBatchInpage, /version: '0\.5\.191'/);
|
||||
assert.match(platformApp, /REQUIRED_EXTENSION_VERSION = '0\.5\.191'/);
|
||||
assert.match(inpage, /function strictIsoDate\(value\)/);
|
||||
assert.match(inpage, /const startDate = strictIsoDate\(current\('riqi'\)\)/);
|
||||
assert.match(inpage, /const endDate = strictIsoDate\(current\('riqis'\)\)/);
|
||||
|
||||
Reference in New Issue
Block a user