|
10 | 10 | * the requests both polled feeds actually send, under fake timers and a |
11 | 11 | * `document.hidden` driver (the `sharedInboxFeed.cadence-7249` technique): |
12 | 12 | * |
13 | | - * - under the cut-off, the hidden cadence is today's 60 s; |
| 13 | + * - under the cut-off, the hidden cadence is `HIDDEN_POLL_MS`, 3 minutes — |
| 14 | + * clearly slower than the 1-minute foreground (objectstack#28084); |
| 15 | + * - a tab hidden while visible sends its already-armed foreground tick, then |
| 16 | + * reads at the hidden spacing: at most 4 reads per feed before the cut-off; |
14 | 17 | * - past it, nothing goes out, however long the tab stays hidden; |
15 | 18 | * - the return sends exactly one read per feed, then the foreground cadence; |
16 | 19 | * - a visible spell restarts the clock — "continuously hidden" is the rule; |
@@ -63,11 +66,12 @@ import { HIDDEN_POLL_STOP_MS } from '../hiddenTabPollStop'; |
63 | 66 |
|
64 | 67 | /** |
65 | 68 | * The declared cadences, restated as the pins' units — both the 60 s fallback |
66 | | - * of a push channel since objectstack#27626 (they were 10 s and 30 s). |
| 69 | + * of a push channel since objectstack#27626 (they were 10 s and 30 s), and the |
| 70 | + * hidden cadence, three times slower since objectstack#28084. |
67 | 71 | */ |
68 | 72 | const INBOX_POLL_MS = 60_000; |
69 | 73 | const APPROVALS_POLL_MS = 60_000; |
70 | | -const HIDDEN_POLL_MS = 60_000; |
| 74 | +const HIDDEN_POLL_MS = 180_000; |
71 | 75 |
|
72 | 76 | const approvalsReads = () => |
73 | 77 | (globalThis.fetch as unknown as { mock: { calls: unknown[][] } }).mock.calls.filter((c) => |
@@ -125,14 +129,14 @@ afterEach(() => { |
125 | 129 | }); |
126 | 130 |
|
127 | 131 | describe('objectui#12439 — a forgotten tab stops polling', () => { |
128 | | - it('keeps the 60 s hidden cadence under the cut-off, then sends nothing at all', async () => { |
| 132 | + it('keeps the hidden cadence under the cut-off, then sends nothing at all', async () => { |
129 | 133 | setHidden(true); |
130 | 134 | mountBothFeeds(); |
131 | 135 | await settle(); |
132 | 136 | expect(reads()).toEqual({ approvals: 1, inbox: 1 }); |
133 | 137 |
|
134 | | - // Under the cut-off: today's hidden cadence, unchanged — the mount read |
135 | | - // plus one per HIDDEN_POLL_MS tick that comes due before HIDDEN_POLL_STOP_MS. |
| 138 | + // Under the cut-off: the hidden cadence — the mount read plus one per |
| 139 | + // HIDDEN_POLL_MS tick that comes due before HIDDEN_POLL_STOP_MS. |
136 | 140 | await advance(HIDDEN_POLL_STOP_MS - 1); |
137 | 141 | const ticksBeforeStop = Math.ceil(HIDDEN_POLL_STOP_MS / HIDDEN_POLL_MS) - 1; |
138 | 142 | expect(reads()).toEqual({ approvals: 1 + ticksBeforeStop, inbox: 1 + ticksBeforeStop }); |
@@ -166,6 +170,52 @@ describe('objectui#12439 — a forgotten tab stops polling', () => { |
166 | 170 | }); |
167 | 171 | }); |
168 | 172 |
|
| 173 | + it('a tab hidden while visible reads at most 4 times per feed in its 10 hidden minutes (objectstack#28084)', async () => { |
| 174 | + mountBothFeeds(); |
| 175 | + await settle(); |
| 176 | + expect(reads()).toEqual({ approvals: 1, inbox: 1 }); |
| 177 | + |
| 178 | + // Hide half-way to the first foreground tick: the tick armed while the tab |
| 179 | + // was visible keeps its foreground due time. |
| 180 | + const hiddenAt = APPROVALS_POLL_MS / 2; |
| 181 | + await advance(hiddenAt); |
| 182 | + await switchTab(true); |
| 183 | + const atHide = reads(); |
| 184 | + const sinceHide = () => ({ |
| 185 | + approvals: reads().approvals - atHide.approvals, |
| 186 | + inbox: reads().inbox - atHide.inbox, |
| 187 | + }); |
| 188 | + expect(sinceHide()).toEqual({ approvals: 0, inbox: 0 }); |
| 189 | + |
| 190 | + // The already-armed foreground tick fires once, after the hide. |
| 191 | + await advance(APPROVALS_POLL_MS - hiddenAt); |
| 192 | + expect(sinceHide()).toEqual({ approvals: 1, inbox: 1 }); |
| 193 | + |
| 194 | + // Then the hidden spacing: nothing until a full HIDDEN_POLL_MS has passed. |
| 195 | + let expected = 1; |
| 196 | + let hiddenFor = APPROVALS_POLL_MS - hiddenAt; |
| 197 | + while (hiddenFor + HIDDEN_POLL_MS < HIDDEN_POLL_STOP_MS) { |
| 198 | + await advance(HIDDEN_POLL_MS - 1); |
| 199 | + expect(sinceHide()).toEqual({ approvals: expected, inbox: expected }); |
| 200 | + await advance(1); |
| 201 | + expected += 1; |
| 202 | + hiddenFor += HIDDEN_POLL_MS; |
| 203 | + expect(sinceHide()).toEqual({ approvals: expected, inbox: expected }); |
| 204 | + } |
| 205 | + // At most 4 reads per feed in the first 10 hidden minutes — at the |
| 206 | + // foreground rate it would have been 10. |
| 207 | + expect(expected).toBe(4); |
| 208 | + |
| 209 | + // Nothing after the 10th hidden minute, and nothing left armed. |
| 210 | + await advance(6 * HIDDEN_POLL_STOP_MS); |
| 211 | + expect(sinceHide()).toEqual({ approvals: 4, inbox: 4 }); |
| 212 | + expect(vi.getTimerCount()).toBe(0); |
| 213 | + |
| 214 | + // Coming back: exactly one read per feed. |
| 215 | + await switchTab(false); |
| 216 | + expect(sinceHide()).toEqual({ approvals: 5, inbox: 5 }); |
| 217 | + }); |
| 218 | + |
169 | 219 | it('restarts the clock on each hide — a visible spell resets it', async () => { |
170 | 220 | setHidden(true); |
171 | 221 | mountBothFeeds(); |
|
0 commit comments