判断修改

This commit is contained in:
andy committed 2026-09-14 12:47:15 +08:00
1 parent e06afc8c33
commit 4194a200c1
65 files changed
+9617 -109

No files matched your search

+101 -15
View File
@@ -14,7 +14,7 @@ const testSource = adapterSource.replace(
);
const hotelFields = ['riqi0', 'riqis0', 'danwei0', 'danweiid0', 'fangxing0', 'jianshu0', 'zt0', 'beizhu0', 'id0', 'yf0', 'sh0', 'MaxI', 'tdID', 'ddID', 'chufari', 'SubmitButton'];
function fixture({ count = 154, maxI = '6', mode = 'create', tick = () => {}, proofs } = {}) {
function fixture({ count = 76, maxI = '2', rows = Number(maxI) || 2, mode = 'create', tick = () => {}, proofs } = {}) {
let now = 10000;
const initialTime = now;
const values = { MaxI: maxI, tdID: '123', ddID: '0', chufari: '2026-09-15' };
@@ -23,8 +23,11 @@ function fixture({ count = 154, maxI = '6', mode = 'create', tick = () => {}, pr
disabled: false, value: values[name] ?? ''
});
const form = { id: 'InfoForm1', isConnected: true, elements: hotelFields.map(field) };
for (let i = hotelFields.length - 1; i < count; i += 1) form.elements.push(field(`extra${i}`));
const state = { loading: false };
for (let row = 1; row < rows; row += 1) {
form.elements.push(...hotelFields.filter((name) => name.endsWith('0')).map((name) => field(`${name.slice(0, -1)}${row}`)));
}
for (let i = form.elements.length - 1; i < count; i += 1) form.elements.push(field(`extra${i}`));
const state = { loading: false, currentForm: form };
const loadingElement = {
className: 'datagrid-mask',
getBoundingClientRect: () => ({ width: 50, height: 50, left: 0, top: 0, right: 50, bottom: 50 }),
@@ -32,7 +35,7 @@ function fixture({ count = 154, maxI = '6', mode = 'create', tick = () => {}, pr
};
const document = {
querySelectorAll: (selector) => selector.startsWith('.datagrid-mask') && state.loading ? [loadingElement] : [],
querySelector: () => form,
querySelector: () => state.currentForm,
readyState: 'complete'
};
const window = {
@@ -59,11 +62,13 @@ function fixture({ count = 154, maxI = '6', mode = 'create', tick = () => {}, pr
};
}
test('hotel create accepts the reported complete 154-field form after one stable second', async () => {
test('hotel create accepts a complete two-row 76-field form after one stable second', async () => {
const page = fixture();
const result = await page.run();
assert.equal(result.successful_control_count, 154);
assert.equal(result.successful_control_count, 76);
assert.equal(result.ready, true, JSON.stringify(result.blockers));
assert.equal(result.hotel_row_count, 2);
assert.equal(result.native_max_i, 2);
assert.ok(page.elapsed() >= 1000 && page.elapsed() < 2000);
});
@@ -74,13 +79,12 @@ test('hotel create fails closed without a form', async () => {
assert.ok(missing.blockers.includes('lifecycle_complete_form_missing'));
});
test('hotel create uses the requested 150-field boundary', async () => {
for (const count of [149, 150, 154, 195, 442]) {
const result = await fixture({ count }).run();
test('hotel create accepts complete variable-size forms without a serialized count threshold', async () => {
for (const [count, maxI] of [[76, '2'], [149, '5'], [150, '5'], [154, '5'], [195, '7'], [442, '16']]) {
const result = await fixture({ count, maxI }).run();
assert.equal(result.successful_control_count, count);
assert.equal(result.minimum_successful_controls, 150);
assert.equal(result.ready, count >= 150, `${count}: ${JSON.stringify(result.blockers)}`);
if (count < 150) assert.ok(result.blockers.includes(`lifecycle_form_incomplete:${count}:150`));
assert.equal(result.minimum_successful_controls, null);
assert.equal(result.ready, true, `${count}: ${JSON.stringify(result.blockers)}`);
}
});
@@ -106,7 +110,7 @@ test('hotel create waits for visible loading to disappear and then stabilize', a
assert.ok(result.blockers.includes('lifecycle_loading_overlay_visible'));
});
test('hotel create retains the existing one-second count stability gate', async () => {
test('hotel create requires a full observed stable second, including the final timeout sample', async () => {
const grow = (form) => form.elements.push({ name: `extra${form.elements.length}`, tagName: 'INPUT', type: 'text', value: '' });
const settling = fixture({ tick: ({ elapsed, form }) => { if (elapsed === 750) grow(form); } });
assert.equal((await settling.run()).ready, true);
@@ -115,6 +119,10 @@ test('hotel create retains the existing one-second count stability gate', async
const result = await changing.run();
assert.equal(result.ready, false);
assert.ok(result.blockers.includes('lifecycle_form_not_stable'));
assert.equal((await fixture().run(999)).ready, false);
assert.equal((await fixture().run(1000)).ready, true);
const lastSample = fixture({ tick: ({ elapsed, form }) => { if (elapsed === 1000) grow(form); } });
assert.equal((await lastSample.run(1000)).ready, false);
});
test('hotel update/clear and unrelated lifecycle actions retain their current count gates', async () => {
@@ -132,7 +140,7 @@ test('hotel update/clear and unrelated lifecycle actions retain their current co
assert.equal(result.minimum_successful_controls, 90);
});
test('154-field readiness still leaves occupied slots and payment/audit state blocked', async () => {
test('76-field readiness still leaves occupied slots and payment/audit state blocked', async () => {
for (const [name, value, blocker] of [
['id0', '1234', 'arrangement_slot_occupied:id0'],
['danweiid0', '5678', 'arrangement_slot_occupied:danweiid0'],
@@ -148,7 +156,7 @@ test('154-field readiness still leaves occupied slots and payment/audit state bl
}
});
test('full hotel preflight preserves identity, resource and ownership gates after the count check', async () => {
test('full hotel preflight preserves identity, resource and ownership gates after structural readiness', async () => {
for (const failure of ['', 'identity', 'resource', 'ownership', 'slot']) {
const calls = [];
const page = fixture({ proofs: {
@@ -174,3 +182,81 @@ test('full hotel preflight preserves identity, resource and ownership gates afte
assert.equal(result.write_attempted, false);
}
});
test('hotel create rejects missing controls in later rows even on a large form', async () => {
for (const name of hotelFields.filter((name) => name.endsWith('0')).map((name) => `${name.slice(0, -1)}1`)) {
const page = fixture({ count: 442 });
page.form.elements = page.form.elements.filter((control) => control.name !== name);
const result = await page.run();
assert.equal(result.ready, false, name);
assert.ok(result.missing_required_fields.includes(name), name);
}
});
test('hotel create rejects invalid row markers, omitted rows and gaps', async () => {
for (const maxI of ['', '0', '-1', '2.5', '2x', 'Infinity', '9007199254740992']) {
const result = await fixture({ maxI, rows: 2 }).run();
assert.equal(result.ready, false, maxI);
assert.ok(result.blockers.includes('lifecycle_hotel_max_i_invalid'));
}
for (const [maxI, rows] of [['3', 2], ['1', 2], ['999999999', 2]]) {
const result = await fixture({ maxI, rows }).run();
assert.equal(result.ready, false);
assert.ok(result.blockers.includes(`lifecycle_hotel_rows_incomplete:${rows}:${maxI}`));
}
const gap = fixture({ maxI: '3' });
gap.form.elements = gap.form.elements.filter((control) => !/^(riqi|riqis|danwei|danweiid|fangxing|jianshu|zt|beizhu|id|yf|sh)1$/.test(control.name));
assert.equal((await gap.run()).ready, false);
});
test('hotel create rejects disabled, duplicate and id-only required data controls', async () => {
for (const change of [
(control) => { control.disabled = true; },
(control) => { control.matches = (selector) => selector === ':disabled'; },
(control, form) => { form.elements.push({ ...control }); },
(control) => { control.name = ''; },
(control) => { control.type = 'button'; },
(control) => { control.isConnected = false; }
]) {
const page = fixture();
change(page.form.elements.find((control) => control.name === 'jianshu0'), page.form);
const result = await page.run();
assert.equal(result.ready, false);
assert.ok(result.unavailable_required_fields.includes('jianshu0'));
}
const readonly = fixture();
readonly.form.elements.find((control) => control.name === 'fangxing0').readOnly = true;
assert.equal((await readonly.run()).ready, true);
});
test('hotel create rejects detached or replaced forms and unfinished documents', async () => {
for (const change of [
(page) => { page.form.isConnected = false; },
(page) => { page.state.currentForm = { ...page.form }; },
(page) => { page.form.ownerDocument.readyState = 'loading'; }
]) {
const page = fixture();
change(page);
assert.equal((await page.run()).ready, false);
}
});
test('same-count control replacement and select-option changes restart stability', async () => {
for (const change of [
(form) => { form.elements[0] = { ...form.elements[0] }; },
(form) => { form.elements.at(-1).id += 'changed'; },
(form) => { form.elements.at(-1).options = [{}]; }
]) {
const page = fixture({ tick: ({ elapsed, form }) => { if (elapsed === 750) change(form); } });
assert.equal((await page.run()).ready, true);
assert.equal(page.elapsed(), 1750);
}
});
test('native pending ajax blocks hotel readiness even without a visible overlay', async () => {
const page = fixture();
page.form.ownerDocument.defaultView.jQuery = { active: 1 };
const result = await page.run();
assert.equal(result.ready, false);
assert.equal(result.native_ajax_active, true);
});