feat: add configurable operation delay

This commit is contained in:
root
2026-05-08 12:50:20 -04:00
parent 872f382815
commit c23461261b
44 changed files with 4770 additions and 193 deletions
+58 -5
View File
@@ -57,6 +57,9 @@ const logs = [];
const completions = [];
const clicks = [];
const scheduled = [];
const events = [];
let releaseSubmitDelay;
const submitDelay = new Promise((resolve) => { releaseSubmitDelay = resolve; });
const snapshot = {
state: 'password_page',
@@ -66,10 +69,23 @@ const snapshot = {
};
const window = {
setTimeout(fn) {
scheduled.push(fn);
setTimeout(fn, ms) {
scheduled.push({ fn, ms });
return scheduled.length;
},
CodexOperationDelay: {
async performOperationWithDelay(metadata, operation) {
events.push('operation:' + metadata.label + ':start');
const result = await operation();
events.push('operation:' + metadata.label + ':end');
if (metadata.kind === 'submit') {
events.push('delay:' + metadata.label + ':pending');
await submitDelay;
}
events.push('delay:' + metadata.label + ':2000');
return result;
},
},
};
const location = {
@@ -114,6 +130,7 @@ async function waitForElementByText() {
function fillInput(input, value) {
input.value = value;
events.push('fill-password:' + value);
}
async function humanPause() {}
@@ -125,14 +142,21 @@ function isStopError() {
function log(message, level = 'info') {
logs.push({ message, level });
events.push('log:' + message);
}
function reportComplete(step, payload) {
completions.push({ step, payload });
events.push('report:' + payload.deferredSubmit);
}
function simulateClick(target) {
clicks.push(target.textContent || 'button');
events.push('click:' + (target.textContent || 'button'));
}
function getOperationDelayRunner() {
return window.CodexOperationDelay.performOperationWithDelay;
}
${extractFunction('step3_fillEmailPassword')}
@@ -145,28 +169,42 @@ return {
if (!scheduled.length) {
throw new Error('missing deferred submit');
}
await scheduled[0]();
await scheduled[0].fn();
},
releaseSubmitDelay() {
releaseSubmitDelay();
},
snapshot() {
return {
logs,
completions,
clicks,
events,
passwordValue: snapshot.passwordInput.value,
scheduledCount: scheduled.length,
scheduledDelayMs: scheduled[0]?.ms,
};
},
};
`)();
const result = await api.run({
let settled = false;
const tracked = api.run({
email: 'user@example.com',
password: 'Secret123!',
}).then((value) => {
settled = true;
return value;
});
await new Promise((resolve) => setImmediate(resolve));
assert.equal(settled, true, 'step 3 must return completion before deferred submit can navigate');
const result = await tracked;
const beforeSubmit = api.snapshot();
assert.equal(beforeSubmit.passwordValue, 'Secret123!');
assert.equal(beforeSubmit.scheduledCount, 1);
assert.equal(beforeSubmit.scheduledDelayMs, 120);
assert.deepStrictEqual(beforeSubmit.clicks, []);
assert.equal(beforeSubmit.completions.length, 1);
assert.equal(beforeSubmit.completions[0].step, 3);
@@ -174,9 +212,24 @@ return {
assert.equal(result.email, 'user@example.com');
assert.equal(result.deferredSubmit, true);
assert.equal(typeof result.signupVerificationRequestedAt, 'number');
assert.equal(beforeSubmit.events.includes('report:true'), true);
assert.equal(beforeSubmit.events.includes('operation:submit-signup-password:start'), false);
await api.flushDeferredSubmit();
let flushSettled = false;
const flushed = api.flushDeferredSubmit().then(() => {
flushSettled = true;
});
await new Promise((resolve) => setImmediate(resolve));
const duringSubmit = api.snapshot();
assert.equal(duringSubmit.events.includes('operation:submit-signup-password:start'), true);
assert.equal(duringSubmit.events.includes('delay:submit-signup-password:pending'), true);
assert.equal(flushSettled, false);
api.releaseSubmitDelay();
await flushed;
const afterSubmit = api.snapshot();
assert.deepStrictEqual(afterSubmit.clicks, ['Continue']);
assert.equal(afterSubmit.events.includes('delay:submit-signup-password:2000'), true);
});