Files
jiapuapp/tests/m07-feedback-state-runtime-smoke.js
T
2026-07-23 08:24:07 +08:00

164 lines
6.1 KiB
JavaScript

"use strict";
const assert = require("assert");
const fs = require("fs");
const path = require("path");
const toDataModuleUrl = (source) =>
`data:text/javascript;base64,${Buffer.from(source).toString("base64")}`;
const loadPageState = async (submitFeedback) => {
const source = fs.readFileSync(
path.join(__dirname, "../pages/profile/m07-feedback.vue"),
"utf8",
);
const scriptMatch = source.match(/<script setup>([\s\S]*?)<\/script>/);
if (!scriptMatch) throw new Error("M07 script setup is missing");
const script = scriptMatch[1].replace(/^import[\s\S]*?;\s*$/gm, "");
globalThis.__m07SubmitFeedback = submitFeedback;
const prelude = `
const computed = (getter) => ({ get value() { return getter(); } });
const reactive = (value) => value;
const ref = (value) => ({ value });
const nextTick = (callback) => Promise.resolve().then(callback);
let __watchCallback = null;
const watch = (_source, callback) => { __watchCallback = callback; };
const onBackPress = () => {};
const onUnload = () => {};
const appApi = { submitFeedback: (...args) => globalThis.__m07SubmitFeedback(...args) };
const createRequestController = () => ({ abort() {}, bind() {}, release() {} });
const isRequestCancelled = (error) => error?.code === "REQUEST_CANCELLED";
const createDiscardConfirmation = () => ({
request: () => Promise.resolve(true),
confirm() {},
cancel() {},
dispose() {},
});
const handleBackPress = () => true;
const runBackGuard = () => true;
`;
const exports = `
export const __state = {
feedbackForm,
feedbackState,
feedbackResult,
feedbackResultTone,
formSnapshot,
baseline,
isDirty,
submitDisabled,
submitFeedback,
triggerWatch: () => __watchCallback(formSnapshot.value),
};
`;
const module = await import(
toDataModuleUrl(`${prelude}\n${script}\n${exports}\n// ${Math.random()}`),
);
return module.__state;
};
const deferred = () => {
let resolve;
let reject;
const promise = new Promise((resolvePromise, rejectPromise) => {
resolve = resolvePromise;
reject = rejectPromise;
});
return { promise, resolve, reject };
};
const run = async () => {
let calls = 0;
let pending = deferred();
const successState = await loadPageState(() => {
calls += 1;
return pending.promise;
});
successState.feedbackForm.feedbackContent = "第一份反馈";
let request = successState.submitFeedback();
assert.strictEqual(successState.feedbackState.value, "submitting");
pending.resolve({ accepted: true });
await request;
assert.strictEqual(successState.feedbackState.value, "success");
assert.strictEqual(successState.submitDisabled.value, true);
assert.strictEqual(successState.isDirty.value, false);
await successState.submitFeedback();
assert.strictEqual(calls, 1, "成功快照不得原样重复提交");
successState.feedbackForm.feedbackContent = "编辑后的新反馈";
successState.triggerWatch();
assert.strictEqual(successState.feedbackState.value, "ready");
assert.strictEqual(successState.submitDisabled.value, false);
assert.strictEqual(successState.isDirty.value, true);
pending = deferred();
request = successState.submitFeedback();
successState.feedbackForm.feedbackContent = "请求期间迟到输入";
successState.triggerWatch();
pending.resolve({ accepted: true });
await request;
assert.strictEqual(successState.feedbackState.value, "ready");
assert.match(successState.feedbackResult.value, /上一份反馈已提交.*当前修改尚未提交/);
assert.strictEqual(successState.isDirty.value, true);
let uncertainCalls = 0;
const uncertainState = await loadPageState(async () => {
uncertainCalls += 1;
const error = new Error("timeout");
error.code = "REQUEST_TIMEOUT";
throw error;
});
uncertainState.feedbackForm.feedbackContent = "结果未知反馈";
await uncertainState.submitFeedback();
assert.strictEqual(uncertainState.feedbackState.value, "uncertain");
assert.strictEqual(uncertainState.submitDisabled.value, true);
await uncertainState.submitFeedback();
assert.strictEqual(uncertainCalls, 1, "未知结果快照不得原样重复提交");
uncertainState.feedbackForm.feedbackContent = "修改后的另一份反馈";
uncertainState.triggerWatch();
assert.strictEqual(uncertainState.feedbackState.value, "ready");
assert.strictEqual(uncertainState.submitDisabled.value, false);
const unexpectedSuccessState = await loadPageState(async () => {
const error = new Error("unexpected success status");
error.code = "HTTP_ERROR";
error.httpStatus = 202;
throw error;
});
unexpectedSuccessState.feedbackForm.feedbackContent = "服务返回意外成功状态";
await unexpectedSuccessState.submitFeedback();
assert.strictEqual(
unexpectedSuccessState.feedbackState.value,
"uncertain",
"非幂等 POST 收到意外 2xx 时不能开放原样重试",
);
assert.strictEqual(unexpectedSuccessState.submitDisabled.value, true);
const lateUncertainRequest = deferred();
const lateUncertainState = await loadPageState(() => lateUncertainRequest.promise);
lateUncertainState.feedbackForm.feedbackContent = "正在提交的反馈";
request = lateUncertainState.submitFeedback();
lateUncertainState.feedbackForm.feedbackContent = "请求期间修改的新反馈";
lateUncertainState.triggerWatch();
const timeoutError = new Error("timeout");
timeoutError.code = "REQUEST_TIMEOUT";
lateUncertainRequest.reject(timeoutError);
await request;
assert.strictEqual(
lateUncertainState.feedbackState.value,
"ready",
"旧快照结果未知时,请求期间的新输入必须保持可提交状态",
);
assert.match(lateUncertainState.feedbackResult.value, /结果仍待确认.*当前修改尚未提交/);
assert.strictEqual(lateUncertainState.submitDisabled.value, false);
delete globalThis.__m07SubmitFeedback;
process.stdout.write("M07-FEEDBACK-STATE-RUNTIME-SMOKE PASS\n");
};
run().catch((error) => {
delete globalThis.__m07SubmitFeedback;
process.stderr.write(`${error.stack || error.message}\n`);
process.exit(1);
});