mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-15 21:13:00 +08:00
89 lines
4.6 KiB
JavaScript
89 lines
4.6 KiB
JavaScript
const fs = require('fs');
|
|
const vm = require('vm');
|
|
const assert = require('assert');
|
|
const source = fs.readFileSync(process.argv[2], 'utf8');
|
|
function section(start, end) {
|
|
const from = source.indexOf(start), to = source.indexOf(end, from);
|
|
assert(from >= 0 && to > from, 'shipped handler must exist');
|
|
return source.slice(from, to);
|
|
}
|
|
const noticeCode = section(' function initVersionNotice()', ' window.smartFetchJson');
|
|
function notice(responses) {
|
|
const requests = [], timers = [], opened = [];
|
|
function node() {
|
|
return {style: {}, children: [], textContent: '', title: '',
|
|
setAttribute(name, value) { this[name] = value; },
|
|
appendChild(child) { this.children.push(child); },
|
|
addEventListener(name, handler) { this[name] = handler; }};
|
|
}
|
|
const nodes = Object.fromEntries(['smart-srun-version-info', 'smart-srun-version-link',
|
|
'smart-srun-update-dot'].map(id => [id, node()]));
|
|
const context = {window: {}, document: {getElementById: id => nodes[id], createElement: node},
|
|
RELEASES_PAGE_URL: 'https://github.com/matthewlu070111/smart-srun/releases',
|
|
UPDATE_CHECK_URL: '/check', UPDATE_STATUS_URL: '/status',
|
|
fetchJson(url, callback) {
|
|
requests.push(url);
|
|
const next = responses.shift();
|
|
assert(next, 'unexpected request');
|
|
callback(next.error || null, next.data);
|
|
}, setTimeout(fn) { timers.push(fn); }, openUpdateModal(plan) { opened.push(plan); }};
|
|
vm.runInNewContext(noticeCode + '\ninitVersionNotice();', context);
|
|
return {context, requests, timers, opened, link: nodes['smart-srun-version-link'],
|
|
dot: nodes['smart-srun-update-dot'], message: nodes['smart-srun-version-info'].children[0],
|
|
click() { let prevented = false; this.link.click({preventDefault() { prevented = true; }}); return prevented; }};
|
|
}
|
|
for (const failure of [
|
|
{data: {ok: false, code: 'DNSFailure', message: '无法解析更新域名'}},
|
|
{data: {ok: true, update_available: false, code: 'PackageIncompatible', message: '固件 25.12 不兼容'}},
|
|
{error: new Error('request_timeout')}
|
|
]) {
|
|
const fixture = notice([failure, {data: {ok: true, update_available: true, plan_id: 'safe-plan', latest_tag: '2.0.0rc3'}}]);
|
|
assert(fixture.message.textContent.includes('点击版本号重试'));
|
|
if (failure.data) assert(fixture.message.textContent.includes(failure.data.message));
|
|
assert.strictEqual(fixture.dot.style.display, 'none');
|
|
assert(fixture.click());
|
|
assert.strictEqual(fixture.requests.length, 2);
|
|
assert.strictEqual(fixture.dot.style.display, 'inline-block');
|
|
assert.strictEqual(fixture.message.textContent, '');
|
|
assert(fixture.click());
|
|
assert.strictEqual(fixture.opened[0].plan_id, 'safe-plan');
|
|
vm.runInNewContext('initVersionNotice();', fixture.context);
|
|
assert.strictEqual(fixture.requests.length, 2, 'initialization must not duplicate checks');
|
|
}
|
|
const pending = notice([{data: {ok: true, running: true, job_id: 'job/1'}},
|
|
{data: {ok: true, update_available: false, message: '没有更新的兼容版本'}}]);
|
|
assert.strictEqual(pending.requests.length, 1);
|
|
pending.timers.shift()();
|
|
assert.strictEqual(pending.requests[1], '/status?job_id=job%2F1');
|
|
assert.strictEqual(pending.message.textContent, '');
|
|
assert.strictEqual(pending.click(), false);
|
|
|
|
const requestCode = section(' function fetchJson(', ' function initConfigBackup(');
|
|
for (const method of ['fetchJson', 'startUpdate']) {
|
|
for (const outcome of ['timeout', 'error', 'success', 'http', 'json']) {
|
|
const requests = [], replies = [];
|
|
function XHR() { requests.push(this); }
|
|
XHR.prototype.open = function(method, url) { this.method = method; this.url = url; };
|
|
XHR.prototype.setRequestHeader = function() {};
|
|
XHR.prototype.send = function(body) { this.body = body; };
|
|
const context = {XMLHttpRequest: XHR, UPDATE_START_URL: '/start', window: {},
|
|
document: {querySelector() { return {value: 'csrf-token'}; }},
|
|
callback(error, data) { replies.push({error, data}); }};
|
|
vm.runInNewContext(requestCode + `\n${method}('plan&1', callback);`, context);
|
|
const xhr = requests[0];
|
|
assert.strictEqual(xhr.timeout, 30000);
|
|
if (method === 'startUpdate') assert(xhr.body.includes('plan_id=plan%261'));
|
|
if (outcome === 'timeout') xhr.ontimeout();
|
|
if (outcome === 'error') xhr.onerror();
|
|
xhr.readyState = 4;
|
|
xhr.status = outcome === 'http' ? 503 : 200;
|
|
xhr.responseText = outcome === 'json' ? '{invalid' : '{"ok":true}';
|
|
xhr.onreadystatechange();
|
|
xhr.onerror();
|
|
xhr.ontimeout();
|
|
assert.strictEqual(replies.length, 1, `${method}/${outcome} completes once`);
|
|
assert.strictEqual(!!replies[0].error, outcome !== 'success');
|
|
}
|
|
}
|
|
console.log('update UI retry, polling and XHR completion passed');
|