-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathback-handler.test.js
More file actions
164 lines (133 loc) · 6.41 KB
/
Copy pathback-handler.test.js
File metadata and controls
164 lines (133 loc) · 6.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import { describe, test, expect, beforeEach } from 'vitest';
// ── estreUi.pushBackHandler / popBackHandler / onBack external stack ──
//
// Roadmap #011 — light-DOM-mounted external embeds register navigation
// handlers; estreUi.onBack runs them LIFO before its own section stack.
describe('estreUi external back handler stack', () => {
beforeEach(() => {
// Drain any handlers a prior test may have left behind. The setup
// bootstraps estreUi once per worker, so state persists across tests.
estreUi.clearAllExternalBackHandlers();
});
test('pushBackHandler returns a positive integer token', () => {
const token = estreUi.pushBackHandler(() => false);
expect(typeof token).toBe('number');
expect(token).toBeGreaterThan(0);
});
test('pushBackHandler rejects non-functions and returns null', () => {
expect(estreUi.pushBackHandler(null)).toBeNull();
expect(estreUi.pushBackHandler(undefined)).toBeNull();
expect(estreUi.pushBackHandler(42)).toBeNull();
expect(estreUi.pushBackHandler({})).toBeNull();
expect(estreUi.externalBackStack.length).toBe(0);
});
test('successive pushes return distinct tokens', () => {
const a = estreUi.pushBackHandler(() => false);
const b = estreUi.pushBackHandler(() => false);
expect(a).not.toBe(b);
});
test('popBackHandler returns true on hit, removes the entry', () => {
const token = estreUi.pushBackHandler(() => false);
expect(estreUi.externalBackStack.length).toBe(1);
expect(estreUi.popBackHandler(token)).toBe(true);
expect(estreUi.externalBackStack.length).toBe(0);
});
test('popBackHandler returns false on unknown token, stack unchanged', () => {
const token = estreUi.pushBackHandler(() => false);
expect(estreUi.popBackHandler(999999)).toBe(false);
expect(estreUi.externalBackStack.length).toBe(1);
// Cleanup
estreUi.popBackHandler(token);
});
test('out-of-order pop is allowed', () => {
const a = estreUi.pushBackHandler(() => false);
const b = estreUi.pushBackHandler(() => false);
const c = estreUi.pushBackHandler(() => false);
expect(estreUi.externalBackStack.length).toBe(3);
// Remove the middle one
expect(estreUi.popBackHandler(b)).toBe(true);
expect(estreUi.externalBackStack.length).toBe(2);
expect(estreUi.externalBackStack[0].token).toBe(a);
expect(estreUi.externalBackStack[1].token).toBe(c);
});
test('same handler can be pushed twice with separate tokens', () => {
const handler = () => false;
const a = estreUi.pushBackHandler(handler);
const b = estreUi.pushBackHandler(handler);
expect(a).not.toBe(b);
expect(estreUi.externalBackStack.length).toBe(2);
expect(estreUi.popBackHandler(a)).toBe(true);
expect(estreUi.externalBackStack.length).toBe(1);
expect(estreUi.popBackHandler(b)).toBe(true);
});
test('clearAllExternalBackHandlers empties the stack', () => {
estreUi.pushBackHandler(() => false);
estreUi.pushBackHandler(() => false);
estreUi.pushBackHandler(() => false);
expect(estreUi.externalBackStack.length).toBe(3);
estreUi.clearAllExternalBackHandlers();
expect(estreUi.externalBackStack.length).toBe(0);
});
test('onBack invokes handlers LIFO and stops at first truthy return', async () => {
const calls = [];
estreUi.pushBackHandler(() => { calls.push('first'); return false; });
estreUi.pushBackHandler(() => { calls.push('second'); return false; });
estreUi.pushBackHandler(() => { calls.push('third'); return true; });
await estreUi.onBack();
// LIFO — third runs first and absorbs; first/second never run.
expect(calls).toEqual(['third']);
});
test('onBack falls through to the next handler when one returns falsy', async () => {
const calls = [];
estreUi.pushBackHandler(() => { calls.push('older'); return true; });
estreUi.pushBackHandler(() => { calls.push('newer'); return false; });
await estreUi.onBack();
// Newer ran first, returned false → older ran and absorbed.
expect(calls).toEqual(['newer', 'older']);
});
test('onBack returns true when an external handler absorbs', async () => {
estreUi.pushBackHandler(() => true);
const result = await estreUi.onBack();
expect(result).toBe(true);
});
test('async handler is awaited', async () => {
let resolved = false;
estreUi.pushBackHandler(async () => {
await new Promise(r => setTimeout(r, 10));
resolved = true;
return true;
});
const result = await estreUi.onBack();
expect(resolved).toBe(true);
expect(result).toBe(true);
});
test('throwing handler is isolated, next handler still runs', async () => {
const calls = [];
// Silence the warn during the test
const originalLogging = window.isLogging;
window.isLogging = false;
estreUi.pushBackHandler(() => { calls.push('older'); return true; });
estreUi.pushBackHandler(() => { calls.push('thrower'); throw new Error('boom'); });
const result = await estreUi.onBack();
expect(calls).toEqual(['thrower', 'older']);
expect(result).toBe(true);
window.isLogging = originalLogging;
});
test('async-rejecting handler is isolated', async () => {
const calls = [];
const originalLogging = window.isLogging;
window.isLogging = false;
estreUi.pushBackHandler(() => { calls.push('older'); return true; });
estreUi.pushBackHandler(async () => { calls.push('rejector'); throw new Error('async-boom'); });
const result = await estreUi.onBack();
expect(calls).toEqual(['rejector', 'older']);
expect(result).toBe(true);
window.isLogging = originalLogging;
});
// Note: a "fall-through to EstreUI section stack" check would need a fully
// initialized estreUi (overlayArea / blindArea / mainArea attached). In the
// jsdom unit-test scope those are null, so we cover the fall-through
// semantics indirectly via the falsy-return test above (which proves the
// loop continues past a falsy handler) and trust the section-stack walk
// itself to other test files.
});