145 lines
5.2 KiB
JavaScript
145 lines
5.2 KiB
JavaScript
/**
|
||
* main.postSafe.test.js — main.postSafe() 错误守卫封装测试
|
||
* ============================================================================
|
||
* 目的:验证 postSafe 正确将 jQuery Deferred 的 .done/.fail 映射到 Promise 的 resolve/reject,
|
||
* 防止「业务错误走 reject、.done 不触发」陷阱复发。
|
||
*
|
||
* 覆盖场景:
|
||
* 1. 成功请求 → Promise resolve
|
||
* 2. 业务错误(resp.success === false)→ Promise reject
|
||
* 3. 网络异常 → Promise reject
|
||
* 4. async/await 语法兼容性
|
||
* 5. 与 main.post 行为对比(陷阱复现)
|
||
*
|
||
* 运行:cd YLErpWeb/fe-tests && npx jest main.postSafe
|
||
* ============================================================================
|
||
*/
|
||
|
||
// 搭建最小 DOM 环境
|
||
const { JSDOM } = require('jsdom');
|
||
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
|
||
global.window = dom.window;
|
||
global.document = dom.window.document;
|
||
global.navigator = dom.window.navigator;
|
||
|
||
// Mock main 对象(与 main.js 中的逻辑等价)
|
||
var main = {};
|
||
|
||
// main.post: 返回 jQuery Deferred promise,测试中通过 _lastDeferred 控制结果
|
||
main.post = function (url, data, options) {
|
||
var doneCb = [], failCb = [];
|
||
var promise = {
|
||
done: function (cb) { doneCb.push(cb); return promise; },
|
||
fail: function (cb) { failCb.push(cb); return promise; },
|
||
};
|
||
main._lastDeferred = {
|
||
resolve: function (resp) { doneCb.forEach(function (cb) { cb(resp); }); },
|
||
reject: function (resp) { failCb.forEach(function (cb) { cb(resp); }); },
|
||
promise: function () { return promise; },
|
||
};
|
||
return main._lastDeferred.promise();
|
||
};
|
||
|
||
// main.postSafe: 与 main.js 中完全相同的实现
|
||
main.postSafe = function (url, data, options) {
|
||
return new Promise(function (resolve, reject) {
|
||
main.post(url, data, options)
|
||
.done(function (resp) { resolve(resp); })
|
||
.fail(function (resp) { reject(resp); });
|
||
});
|
||
};
|
||
|
||
describe('main.postSafe — Promise 封装守卫', function () {
|
||
|
||
test('成功请求 → Promise resolve(resp)', function () {
|
||
var p = main.postSafe('/api/test', { foo: 1 });
|
||
// postSafe 内部已调用 main.post,_lastDeferred 已就绪
|
||
main._lastDeferred.resolve({ success: true, data: 'ok' });
|
||
return p.then(function (resp) {
|
||
expect(resp.success).toBe(true);
|
||
expect(resp.data).toBe('ok');
|
||
});
|
||
});
|
||
|
||
test('业务错误(resp.success===false)→ Promise reject(resp)', function () {
|
||
var p = main.postSafe('/api/test', { foo: 1 });
|
||
main._lastDeferred.reject({ success: false, msg: '算不出来' });
|
||
return p.catch(function (resp) {
|
||
expect(resp.success).toBe(false);
|
||
expect(resp.msg).toBe('算不出来');
|
||
});
|
||
});
|
||
|
||
test('网络异常 → Promise reject', function () {
|
||
var p = main.postSafe('/api/test', { foo: 1 });
|
||
main._lastDeferred.reject({ errcode: 500, msg: '请求失败' });
|
||
return p.catch(function (resp) {
|
||
expect(resp.errcode).toBe(500);
|
||
});
|
||
});
|
||
|
||
test('async/await 语法兼容 — 成功路径', async function () {
|
||
var p = main.postSafe('/api/test', { foo: 1 });
|
||
main._lastDeferred.resolve({ success: true, value: 42 });
|
||
var resp = await p;
|
||
expect(resp.success).toBe(true);
|
||
expect(resp.value).toBe(42);
|
||
});
|
||
|
||
test('async/await 语法兼容 — 失败路径', async function () {
|
||
var p = main.postSafe('/api/test', { foo: 1 });
|
||
main._lastDeferred.reject({ success: false, msg: '计算失败' });
|
||
|
||
var caught = null;
|
||
try {
|
||
await p;
|
||
} catch (resp) {
|
||
caught = resp;
|
||
}
|
||
expect(caught).not.toBeNull();
|
||
expect(caught.success).toBe(false);
|
||
expect(caught.msg).toBe('计算失败');
|
||
});
|
||
});
|
||
|
||
describe('main.postSafe — 与 main.post 行为对比(陷阱复现)', function () {
|
||
|
||
test('main.post 的 .done 在业务错误时不触发(陷阱复现)', function () {
|
||
var doneCalled = false;
|
||
var failCalled = false;
|
||
|
||
main.post('/api/test', {}).done(function () {
|
||
doneCalled = true;
|
||
}).fail(function () {
|
||
failCalled = true;
|
||
});
|
||
|
||
// 业务错误 → reject
|
||
main._lastDeferred.reject({ success: false });
|
||
|
||
expect(doneCalled).toBe(false); // .done 没触发!这就是陷阱
|
||
expect(failCalled).toBe(true); // .fail 触发了
|
||
});
|
||
|
||
test('main.postSafe 的 .then 在业务错误时不触发(安全)', function () {
|
||
var thenCalled = false;
|
||
var caught = null;
|
||
|
||
var p = main.postSafe('/api/test', {}).then(function () {
|
||
thenCalled = true;
|
||
}).catch(function (resp) {
|
||
caught = resp;
|
||
});
|
||
|
||
// 业务错误 → reject
|
||
main._lastDeferred.reject({ success: false, msg: 'biz error' });
|
||
|
||
// Promise 是微任务,需要等一轮
|
||
return p.then(function () {
|
||
expect(thenCalled).toBe(false); // .then 没触发
|
||
expect(caught).not.toBeNull(); // .catch 触发了
|
||
expect(caught.msg).toBe('biz error');
|
||
});
|
||
});
|
||
});
|