164 lines
6.1 KiB
JavaScript
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);
|
|
});
|