mirror of
https://github.com/whyour/qinglong.git
synced 2026-11-02 06:58:52 +08:00
Package production dependencies per architecture for online and offline upgrades. Validate dependencies and isolated backend startup before readiness, repair once when permitted, and preserve service/data through failed preparation or rollback. Publish release bundles with master images and expose an opt-in develop build sharing the development index across Alpine and Debian images.
107 lines
3.2 KiB
JavaScript
107 lines
3.2 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const ts = require('typescript');
|
|
|
|
function layout(width, legacy = false) {
|
|
const values = [],
|
|
listeners = new Set(),
|
|
attributes = new Map(),
|
|
effects = [];
|
|
let cursor = 0,
|
|
mounted = false;
|
|
const media = { matches: width < 768 };
|
|
const add = (fn) => listeners.add(fn),
|
|
remove = (fn) => listeners.delete(fn);
|
|
Object.assign(
|
|
media,
|
|
legacy
|
|
? { addListener: add, removeListener: remove }
|
|
: {
|
|
addEventListener: (_, fn) => add(fn),
|
|
removeEventListener: (_, fn) => remove(fn),
|
|
},
|
|
);
|
|
const module = { exports: {} };
|
|
const source = ts.transpileModule(
|
|
fs.readFileSync(path.join(__dirname, '../../src/utils/hooks.ts'), 'utf8'),
|
|
{
|
|
compilerOptions: {
|
|
target: ts.ScriptTarget.ES2020,
|
|
module: ts.ModuleKind.CommonJS,
|
|
},
|
|
},
|
|
).outputText;
|
|
new Function('require', 'module', 'exports', 'window', 'document', source)(
|
|
(name) => {
|
|
assert.equal(name, 'react');
|
|
return {
|
|
useState: (initial) => {
|
|
const index = cursor++;
|
|
if (!(index in values))
|
|
values[index] = typeof initial === 'function' ? initial() : initial;
|
|
return [
|
|
values[index],
|
|
(value) => {
|
|
values[index] = value;
|
|
},
|
|
];
|
|
},
|
|
useEffect: (fn) => {
|
|
if (!mounted) effects.push(fn);
|
|
},
|
|
};
|
|
},
|
|
module,
|
|
module.exports,
|
|
{ matchMedia: () => media, navigator: { userAgent: 'Desktop browser' } },
|
|
{ body: { setAttribute: (key, value) => attributes.set(key, value) } },
|
|
);
|
|
const render = () => {
|
|
cursor = 0;
|
|
return module.exports.useCtx();
|
|
};
|
|
const initial = render();
|
|
const cleanup = effects.map((fn) => fn());
|
|
mounted = true;
|
|
return {
|
|
initial,
|
|
attributes,
|
|
listeners,
|
|
resize: (width) => {
|
|
media.matches = width < 768;
|
|
listeners.forEach((fn) => fn());
|
|
return render();
|
|
},
|
|
unmount: () => cleanup.forEach((fn) => fn?.()),
|
|
};
|
|
}
|
|
|
|
test('narrow desktop windows retain the top navigation and user controls, and follow viewport changes', () => {
|
|
const view = layout(499);
|
|
assert.equal(view.initial.isPhone, true);
|
|
assert.equal(view.initial.headerStyle.marginTop, 0);
|
|
assert.equal(view.attributes.get('data-mode'), 'phone');
|
|
assert.equal(view.listeners.size, 1);
|
|
const desktop = view.resize(1024);
|
|
assert.equal(desktop.isPhone, false);
|
|
assert.equal(desktop.headerStyle.marginTop, -48);
|
|
assert.equal(view.attributes.get('data-mode'), 'desktop');
|
|
const narrow = view.resize(767.5);
|
|
assert.equal(narrow.isPhone, true);
|
|
assert.equal(narrow.headerStyle.marginTop, 0);
|
|
assert.equal(view.attributes.get('data-mode'), 'phone');
|
|
assert.equal(view.resize(768).isPhone, false);
|
|
view.unmount();
|
|
assert.equal(view.listeners.size, 0);
|
|
});
|
|
|
|
test('older media query listeners are removed when the layout unmounts', () => {
|
|
const view = layout(1024, true);
|
|
assert.equal(view.resize(600).isPhone, true);
|
|
assert.equal(view.attributes.get('data-mode'), 'phone');
|
|
view.unmount();
|
|
assert.equal(view.listeners.size, 0);
|
|
});
|