Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 61 additions & 4 deletions apps/desktop/src/main/__tests__/app-quit-coordinator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ describe('app quit coordinator', () => {
let resumeQuitCount = 0;
let preventedCount = 0;
const coordinator = createAppQuitCoordinator({
prepareToQuit: async () => {},
cleanup: async () => {},
focusOrCreateWindow: () => {},
onPreparationError: () => {},
onCleanupError: () => {},
onWindowCreationError: () => {},
resumeQuit: () => {
Expand All @@ -48,7 +50,7 @@ describe('app quit coordinator', () => {
assert.equal(resumeQuitCount, 0);
assert.equal(preventedCount, 2);

await new Promise<void>((resolve) => setImmediate(resolve));
await flushQuitCoordinator();

assert.equal(resumeQuitCount, 1);
});
Expand All @@ -62,13 +64,15 @@ describe('app quit coordinator', () => {
releaseCleanup = resolve;
});
const coordinator = createAppQuitCoordinator({
prepareToQuit: async () => {},
cleanup: async () => {
cleanupCount += 1;
await cleanupPending;
},
focusOrCreateWindow: () => {
focusOrCreateCount += 1;
},
onPreparationError: () => {},
onCleanupError: () => {},
onWindowCreationError: () => {},
resumeQuit: () => {
Expand All @@ -84,14 +88,15 @@ describe('app quit coordinator', () => {

coordinator.handleBeforeQuit(event);
coordinator.handleBeforeQuit(event);
await flushQuitCoordinator();
assert.equal(cleanupCount, 1);
assert.equal(preventedCount, 2);
assert.equal(resumeQuitCount, 0);

releaseCleanup();
await cleanupPending;
await Promise.resolve();
await new Promise<void>((resolve) => setImmediate(resolve));
await flushQuitCoordinator();

assert.equal(resumeQuitCount, 1);

Expand All @@ -108,11 +113,13 @@ describe('app quit coordinator', () => {
let focusOrCreateCount = 0;
let windowCreationSignal: AbortSignal | undefined;
const coordinator = createAppQuitCoordinator({
prepareToQuit: async () => {},
cleanup: () => new Promise<void>(() => {}),
focusOrCreateWindow: (signal) => {
focusOrCreateCount += 1;
windowCreationSignal = signal;
},
onPreparationError: () => {},
onCleanupError: () => {},
onWindowCreationError: () => {},
resumeQuit: () => {},
Expand All @@ -130,10 +137,12 @@ describe('app quit coordinator', () => {
const failure = new Error('window load failed');
const reportedErrors: unknown[] = [];
const coordinator = createAppQuitCoordinator({
prepareToQuit: async () => {},
cleanup: async () => {},
focusOrCreateWindow: async () => {
throw failure;
},
onPreparationError: () => {},
onCleanupError: () => {},
onWindowCreationError: (error) => reportedErrors.push(error),
resumeQuit: () => {},
Expand All @@ -146,18 +155,62 @@ describe('app quit coordinator', () => {
assert.deepEqual(reportedErrors, [failure]);
});

it('cancels quit without closing resources when Host retirement preparation fails', async () => {
const preparationError = new Error('retirement failed');
const reportedErrors: unknown[] = [];
let preparationCount = 0;
let cleanupCount = 0;
let focusOrCreateCount = 0;
let resumeQuitCount = 0;
const coordinator = createAppQuitCoordinator({
prepareToQuit: async () => {
preparationCount += 1;
if (preparationCount === 1) throw preparationError;
},
cleanup: async () => {
cleanupCount += 1;
},
focusOrCreateWindow: () => {
focusOrCreateCount += 1;
},
onPreparationError: (error) => reportedErrors.push(error),
onCleanupError: () => {},
onWindowCreationError: () => {},
resumeQuit: () => {
resumeQuitCount += 1;
},
});

coordinator.handleBeforeQuit({ preventDefault: () => {} });
await flushQuitCoordinator();

assert.deepEqual(reportedErrors, [preparationError]);
assert.equal(cleanupCount, 0);
assert.equal(resumeQuitCount, 0);
assert.equal(focusOrCreateCount, 1);

coordinator.handleBeforeQuit({ preventDefault: () => {} });
await flushQuitCoordinator();

assert.equal(preparationCount, 2);
assert.equal(cleanupCount, 1);
assert.equal(resumeQuitCount, 1);
});

it('reports cleanup failure without leaking an unhandled rejection', async () => {
const cleanupError = new Error('close failed');
const reportedErrors: unknown[] = [];
let focusOrCreateCount = 0;
let resumeQuitCount = 0;
const deps = {
prepareToQuit: async () => {},
cleanup: async () => {
throw cleanupError;
},
focusOrCreateWindow: () => {
focusOrCreateCount += 1;
},
onPreparationError: () => {},
onCleanupError: (error: unknown) => {
reportedErrors.push(error);
},
Expand All @@ -169,8 +222,7 @@ describe('app quit coordinator', () => {
const coordinator = createAppQuitCoordinator(deps);

coordinator.handleBeforeQuit({ preventDefault: () => {} });
await Promise.resolve();
await new Promise<void>((resolve) => setImmediate(resolve));
await flushQuitCoordinator();
let secondQuitPrevented = false;
coordinator.focusOrCreateWindow();
coordinator.handleBeforeQuit({
Expand All @@ -185,3 +237,8 @@ describe('app quit coordinator', () => {
assert.equal(secondQuitPrevented, false);
});
});

async function flushQuitCoordinator(): Promise<void> {
await new Promise<void>((resolve) => setImmediate(resolve));
await new Promise<void>((resolve) => setImmediate(resolve));
}
Loading