fix: enforce elapsed loading display minimum after timer wakes

This commit is contained in:
2026-09-05 19:12:50 +08:00
parent 73d7826fbe
commit 4414e1bcab
2 changed files with 59 additions and 1 deletions
@@ -143,3 +143,57 @@ test('component stop cancels pending draw and timer work and disables spinner up
assert.equal(timers.size, 0); assert.equal(director.listenerCount('draw'), 0);
assert.equal(component.getDiagnostics().pendingCount, 0);
});
test('minimum display ignores an early timer wake and cancellation clears its remaining wait', async () => {
const path = new URL('../../YouleNexus/assets/scripts/local-platform-login/LocalPlatformLogin.ts', import.meta.url);
const exports: any = {}; const timers = new Map<number, () => void>(); let timerId = 0; let clock = 100;
let hostOptions: any;
runInNewContext(ts.transpileModule(readFileSync(path, 'utf8'), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020, experimentalDecorators: true },
}).outputText, { exports, require: (name: string) => {
if (name === 'cc/env') return { DEBUG: true };
if (name === 'cc') return { Component: class {}, Node, Label, Sprite, UITransform, Prefab: class {}, sys: { isNative: false },
_decorator: { ccclass: () => (target: unknown) => target, property: () => () => {} } };
if (name.endsWith('local-login-host.ts')) return { createLocalLoginHost: (options: unknown) => {
hostOptions = options; return { start: async () => {}, stop() {}, getSnapshot: () => ({}) };
} };
if (name.endsWith('local-login-view.ts')) return load();
if (name.endsWith('remote-config-fetcher.ts')) return { HttpConfigFetcher: class {} };
throw new Error(`Unexpected dependency ${name}`);
}, Error, performance: { now: () => clock }, window: {
location: { search: '?profile=local' }, localStorage: {},
setTimeout: (callback: () => void) => { timers.set(++timerId, callback); return timerId; },
clearTimeout: (id: number) => timers.delete(id),
} });
async function started() {
const component = new exports.LocalPlatformLogin(); const f = fixture();
Object.assign(component, f.refs, { stopButton: new Node(), loadingPrefab: {}, loginPrefab: {} });
const loading = new Node('Layer614_Loading'); const group = new Node('group-40');
const spinner = new Node('载入动画'); spinner.components.set(Sprite, new Sprite());
spinner.components.set(UITransform, new UITransform()); group.children.push(spinner);
loading.children.push(group); component.loadingRoot.children.push(loading);
component.loadingRoot.getComponentsInChildren = () => [];
await component.start(); director.emit('draw'); await Promise.resolve();
return component;
}
const drain = async () => { for (let i = 0; i < 5; i++) await Promise.resolve(); };
function wake(at: number) {
clock = at; const [id, callback] = timers.entries().next().value!;
timers.delete(id); callback();
}
const component = await started(); let completed = false;
const minimum = hostOptions.waitForMinimumDisplay().then(() => { completed = true; });
await drain(); wake(599.8); await drain();
assert.equal(completed, false, '499.8 ms elapsed must not open minimum display gate');
assert.equal(component.getDiagnostics().events.some((event: any) => event.kind === 'minimum-display-ready'), false);
assert.equal(timers.size, 1);
wake(600); await minimum; assert.equal(completed, true); assert.equal(timers.size, 0);
component.stop();
clock = 1000; const cancelled = await started();
const cancelledMinimum = hostOptions.waitForMinimumDisplay();
const rejection = assert.rejects(cancelledMinimum, /cancelled/);
await drain(); wake(1499.8); await drain();
assert.equal(timers.size, 1); cancelled.stop(); await rejection;
assert.equal(timers.size, 0); assert.equal(cancelled.getDiagnostics().pendingCount, 0);
});