Files
crossdesk/apps/harmony/tests/autostart_host_test.cjs
T

117 lines
6.3 KiB
JavaScript

const assert = require('node:assert/strict');
const fs = require('node:fs');
const vm = require('node:vm');
const ts = require(process.argv[2]);
const path = require('node:path');
const source = process.argv[3];
const sent = [], queries = [], timers = new Map();
let supported = true, capabilityError = false, nextTimer = 0;
const sdk = {
isAutoStartupSupported() { if (capabilityError) throw Error('service unavailable'); return supported; },
getAutoStartupStatusForSelf() { return new Promise((resolve, reject) => queries.push({ resolve, reject })); }
};
const flush = () => new Promise(resolve => setImmediate(resolve));
function load(file, imports, extra = {}) {
const module = {};
vm.runInNewContext(ts.transpileModule(fs.readFileSync(file, 'utf8'), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }
}).outputText, {
exports: module, require: name => imports[name] ?? {},
console: { warn() {}, error: e => { throw Error(e); } },
setTimeout: fn => { timers.set(++nextTimer, fn); return nextTimer; },
clearTimeout: id => timers.delete(id), ...extra
});
return module;
}
const { AutostartHost: host } = load(source, { '@kit.AbilityKit': { autoStartupManager: sdk } });
const notify = e => sent.push(JSON.parse(JSON.stringify(e)));
const latest = () => sent.at(-1);
const check = value => assert.deepEqual(latest(), { op: 'autostartState', ...value });
(async () => {
host.refresh(); assert.equal(sent.length, 0, 'no queries before startup');
supported = false; host.start(notify);
check({ ok: true, supported: false }); assert.equal(queries.length, 0);
assert.equal(timers.size, 0);
supported = true; host.refresh(); check({ pending: true });
queries.at(-1).resolve(true); await flush();
check({ ok: true, supported: true, active: true });
host.refresh(); queries.at(-1).resolve(false); await flush();
check({ ok: true, supported: true, active: false });
host.refresh(); queries.at(-1).reject(Error('permission/service failure')); await flush();
check({ ok: false }); // Failure must never be displayed as "Disabled".
capabilityError = true; host.refresh(); check({ ok: false });
capabilityError = false;
host.refresh(); const old = queries.at(-1);
host.refresh(); const fresh = queries.at(-1);
assert.equal(timers.size, 1, 'superseding a query clears its timeout');
fresh.resolve(true); await flush(); const count = sent.length;
old.resolve(false); await flush(); assert.equal(sent.length, count);
check({ ok: true, supported: true, active: true });
host.refresh(); const timedOut = queries.at(-1);
[...timers.values()][0](); check({ ok: false });
const afterTimeout = sent.length;
timedOut.resolve(true); await flush(); assert.equal(sent.length, afterTimeout);
host.refresh(); queries.at(-1).resolve(false); await flush();
check({ ok: true, supported: true, active: false });
host.refresh(); const beforeStop = queries.at(-1);
host.stop(); const stopped = sent.length;
beforeStop.resolve(true); await flush(); host.refresh();
assert.equal(sent.length, stopped); assert.equal(timers.size, 0);
host.start(notify); const previousRun = queries.at(-1);
host.start(notify); queries.at(-1).resolve(false); await flush();
const restarted = sent.length;
previousRun.reject(Error('late failure')); await flush(); assert.equal(sent.length, restarted);
host.stop();
// Exercise the real desktop startup, window activation, EntryAbility and
// native-command routing, not only the standalone query helper.
const handlers = new Map(), commands = [], order = [];
const target = {
on: (name, fn) => handlers.set(name, fn),
getWindowDecorVisible: () => false,
getWindowProperties: () => ({ id: 1, windowRect: { left: 0, top: 0 } })
};
const { DesktopHost: desktop } = load(path.join(path.dirname(source), 'DesktopHost.ets'), {
'libslint_desktop.so': { default: {
start: () => order.push('native'), send: text => notify(JSON.parse(text)),
poll: () => JSON.stringify(commands.splice(0))
} },
'./AutostartHost': { AutostartHost: host },
'./FileHost': { FileHost: { prepare: async () => {} } },
'./DesktopAccess': { DesktopAccess: { start() {}, refresh() {}, stop() {} } },
'./DisplayHost': { DisplayHost: {
snapshot: async () => [], observe() {}, global: () => ({ x: 0, y: 0 }), screen: () => ({ densityPixels: 1 })
} },
'@kit.ArkUI': { display: { getDefaultDisplaySync: () => ({ width: 1280, height: 800, densityPixels: 1, id: 0 }) },
window: { WindowEventType: { WINDOW_ACTIVE: 1, WINDOW_INACTIVE: 2 } } },
'@kit.BasicServicesKit': { pasteboard: { getSystemPasteboard: () => ({ on() {}, off() {} }) } },
'@kit.DeviceCertificateKit': { certificateManager: { getCertificateStorePath: () => '/test/ca', CertType: {} } }
}, { LocalStorage: class { setOrCreate() {} }, setInterval: () => 1, clearInterval() {} });
desktop.foreground(); assert.equal(timers.size, 0);
desktop.start({ getMainWindowSync: () => target, loadContent: async () => {} },
{ filesDir: '/test/files', cacheDir: '/test/cache', terminateSelf: () => order.push('exit') });
await flush(); assert.deepEqual(order, ['native']); check({ pending: true });
queries.at(-1).resolve(false); await flush();
check({ ok: true, supported: true, active: false });
const { default: Ability } = load(path.join(path.dirname(source), 'EntryAbility.ets'), {
'@kit.AbilityKit': { UIAbility: class {} }, '../pages/DesktopHost': { DesktopHost: desktop }
});
new Ability().onForeground(); check({ pending: true });
queries.at(-1).resolve(true); await flush(); check({ ok: true, supported: true, active: true });
handlers.get('windowEvent')(1); // Returning from system Settings refreshes.
queries.at(-1).resolve(false); await flush(); check({ ok: true, supported: true, active: false });
commands.push({ op: 'autostartRefresh' }); desktop.poll(); check({ pending: true });
const last = queries.at(-1);
commands.push({ op: 'exit' }); desktop.poll(); await flush();
const exited = sent.length;
last.resolve(true); await flush(); new Ability().onForeground();
assert.equal(sent.length, exited); assert.equal(timers.size, 0);
assert.deepEqual(order, ['native', 'exit']);
console.log('Autostart system state, failures, timeout, stale replies, startup/foreground and exit tests passed');
})().catch(error => { console.error(error); process.exitCode = 1; });