文件
nahaichuan/tests/contact_form_browser.js

63 行
4.3 KiB
JavaScript

// Run on the local contact preview with agent-browser eval. All requests are mocked.
(async function () {
if (!location.hostname.match(/^(127\.0\.0\.1|localhost)$/)) throw new Error('Use local preview only');
var form = document.querySelector('.ct-form');
var submit = form.querySelector('.ct-submit');
var status = form.querySelector('.ct-submit-status');
var originalFetch = window.fetch;
var calls = [];
var mode = 'reject';
var results = [];
function assert(condition, name) { if (!condition) throw new Error(name); results.push(name); }
window.fetch = async function (url, options) {
calls.push({url: String(url), method: options.method || 'GET', body: options.body || ''});
if (!options.method) {
if (mode === 'token-fail') throw new Error('network');
return {ok: true, json: async function () { return {code: 1, data: 'test-single-use-token'}; }};
}
if (mode === 'network-fail') throw new Error('network');
await new Promise(function (resolve) { setTimeout(resolve, 10); });
return {ok: true, json: async function () { return mode === 'success' ? {code: 1} : {code: 0, msg: '验证码不正确'}; }};
};
function fill() {
form.elements.title.value = '本地界面测试';
form.elements.name.value = '测试';
form.elements.phone.value = '0851-12345678';
form.elements.content.value = '本地模拟数据,不会向线上发送。';
if (form.elements.captcha) form.elements.captcha.value = 'test';
}
function send() { form.dispatchEvent(new Event('submit', {bubbles: true, cancelable: true})); }
async function settled() {
for (var i = 0; i < 100; i++) {
if (form.getAttribute('aria-busy') !== 'true') return;
await new Promise(function (resolve) { setTimeout(resolve, 5); });
}
throw new Error('Form did not settle');
}
try {
form.reset(); send();
assert(calls.length === 0 && document.activeElement === form.elements.title, 'Empty form: focus first error, no request');
form.querySelector('[data-topic]').click();
assert(form.elements.title.value === form.querySelector('[data-topic]').dataset.topic, 'Topic button fills title');
fill(); form.elements.phone.value = '123'; send();
assert(calls.length === 0 && form.elements.phone.getAttribute('aria-invalid') === 'true', 'Invalid phone blocked');
fill(); send(); send();
assert(submit.disabled, 'Submitting state disables button');
await settled();
assert(calls.filter(function (c) { return c.method === 'POST'; }).length === 1, 'Duplicate submit sends one POST');
var body = new URLSearchParams(calls.find(function (c) { return c.method === 'POST'; }).body);
assert(body.get('__token__') === 'test-single-use-token' && body.get('phone') === '0851-12345678', 'Uses fresh token and expected backend fields');
assert(status.dataset.state === 'error' && form.elements.content.value.length > 0 && !submit.disabled, 'Server rejection preserves fields and permits retry');
if (form.elements.captcha) assert(form.elements.captcha.value === '', 'Rejected submission refreshes captcha');
calls = []; fill(); mode = 'success'; send(); await settled();
assert(status.dataset.state === 'success' && form.elements.name.value === '' && form.elements.content.value === '', 'Confirmed success clears fields and announces result');
assert(calls.length === 2 && calls[0].method === 'GET', 'Retry obtains a new token');
calls = []; fill(); mode = 'network-fail'; send(); await settled();
assert(status.dataset.state === 'error' && status.textContent.includes('无法确认提交结果') && form.elements.content.value.length > 0, 'Unknown POST outcome preserves data without automatic retry');
assert(calls.length === 2 && !submit.disabled, 'Network failure releases button');
calls = []; fill(); mode = 'token-fail'; send(); await settled();
assert(calls.length === 1 && status.dataset.state === 'error', 'Token fetch failure never sends POST');
return {passed: results.length, checks: results, network: 'Mocked locally; no real messages sent'};
} finally { window.fetch = originalFetch; }
})();