mirror of
https://github.com/whyour/qinglong.git
synced 2026-10-17 07:20:51 +08:00
* feat: add script version history * fix: validate canonical save-as paths before backup * fix: stack script history footer on small screens
122 lines
3.8 KiB
JavaScript
122 lines
3.8 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const test = require('node:test');
|
|
const path = require('node:path');
|
|
const load = require('../helpers/load-security-module.cjs');
|
|
|
|
function fixture(send) {
|
|
const dialogs = [],
|
|
notices = [],
|
|
calls = [];
|
|
const request = {};
|
|
for (const method of ['put', 'post'])
|
|
request[method] = async (url, body, options) => {
|
|
calls.push({ method, body });
|
|
const result = await send(body);
|
|
if (result.status) return options.onError(result);
|
|
return result;
|
|
};
|
|
const { saveWithHistory } = load(
|
|
path.resolve(__dirname, '../../src/pages/script/saveWithHistory.ts'),
|
|
{
|
|
'@/utils/http': { request },
|
|
antd: {
|
|
Modal: {
|
|
confirm: (dialog) => {
|
|
dialogs.push(dialog);
|
|
return { update: (options) => Object.assign(dialog, options) };
|
|
},
|
|
},
|
|
message: Object.fromEntries(
|
|
['error', 'warning', 'success'].map((type) => [
|
|
type,
|
|
(text) => notices.push({ type, text }),
|
|
]),
|
|
),
|
|
},
|
|
'react-intl-universal': {
|
|
__esModule: true,
|
|
default: { get: (key) => key },
|
|
},
|
|
},
|
|
);
|
|
return { saveWithHistory, dialogs, notices, calls };
|
|
}
|
|
const unavailable = {
|
|
status: 413,
|
|
data: { historyUnavailable: true, currentHash: 'a'.repeat(64) },
|
|
};
|
|
const tick = () => new Promise((resolve) => setImmediate(resolve));
|
|
|
|
test('oversized editor save requires explicit confirmation and cancellation keeps the draft unsaved', async () => {
|
|
const f = fixture(() => unavailable);
|
|
const pending = f.saveWithHistory('put', '/api/scripts', {
|
|
filename: 'large.js',
|
|
content: 'small',
|
|
});
|
|
await tick();
|
|
assert.equal(f.calls.length, 1);
|
|
assert.equal(f.dialogs.length, 1);
|
|
assert.match(f.dialogs[0].content, /不会保留/);
|
|
f.dialogs[0].onCancel();
|
|
assert.equal(await pending, undefined);
|
|
assert.equal(f.calls.length, 1);
|
|
assert.equal(f.notices.length, 0);
|
|
});
|
|
|
|
test('debugger confirmation retries POST with the exact preview hash and displays a warning', async () => {
|
|
const f = fixture((body) =>
|
|
body.skipHistory
|
|
? { code: 200, data: { historyRecorded: false } }
|
|
: unavailable,
|
|
);
|
|
const pending = f.saveWithHistory('post', '/api/scripts', {
|
|
content: 'small',
|
|
});
|
|
await tick();
|
|
await f.dialogs[0].onOk();
|
|
assert.equal((await pending).code, 200);
|
|
assert.equal(f.dialogs[0].cancelButtonProps.disabled, true);
|
|
assert.equal(f.dialogs[0].keyboard, false);
|
|
assert.equal(f.calls[1].method, 'post');
|
|
assert.equal(f.calls[1].body.expectedHash, unavailable.data.currentHash);
|
|
assert.equal(f.calls[1].body.skipHistory, true);
|
|
assert.deepEqual(
|
|
f.notices.map((v) => v.type),
|
|
['warning'],
|
|
);
|
|
});
|
|
|
|
test('a stale confirmation reports the conflict without marking the editor saved', async () => {
|
|
const f = fixture((body) =>
|
|
body.skipHistory
|
|
? { status: 409, data: { message: 'changed' } }
|
|
: unavailable,
|
|
);
|
|
const pending = f.saveWithHistory('put', '/api/scripts', {});
|
|
await tick();
|
|
await f.dialogs[0].onOk();
|
|
assert.equal(await pending, undefined);
|
|
assert.deepEqual(f.notices, [{ type: 'error', text: 'changed' }]);
|
|
});
|
|
|
|
test('unrelated errors never offer history bypass, and post-commit cleanup failures remain successful saves', async () => {
|
|
const failed = fixture(() => ({
|
|
status: 500,
|
|
data: { message: 'storage failed' },
|
|
}));
|
|
assert.equal(
|
|
await failed.saveWithHistory('put', '/api/scripts', {}),
|
|
undefined,
|
|
);
|
|
assert.equal(failed.dialogs.length, 0);
|
|
const committed = fixture(() => ({
|
|
code: 200,
|
|
data: { cleanupPending: true },
|
|
}));
|
|
assert.equal(
|
|
(await committed.saveWithHistory('put', '/api/scripts', {})).code,
|
|
200,
|
|
);
|
|
assert.equal(committed.notices[0].type, 'warning');
|
|
});
|