@intuned/runtime-dev 1.3.15-hook.0 → 1.3.15-ts-helpers

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,821 @@
1
+ "use strict";
2
+
3
+ var _vitest = require("vitest");
4
+ var _intunedExtensionServer = require("../common/extension/intunedExtensionServer");
5
+ var _captcha = require("./captcha");
6
+ _vitest.vi.mock("fastify", () => {
7
+ const fastify = () => ({
8
+ post: _vitest.vi.fn(),
9
+ setNotFoundHandler: _vitest.vi.fn(),
10
+ listen: _vitest.vi.fn(async () => undefined),
11
+ close: _vitest.vi.fn(async () => undefined)
12
+ });
13
+ return {
14
+ default: fastify
15
+ };
16
+ });
17
+ let server;
18
+ _vitest.vi.mock("../common/extension/intunedExtensionServer", async importOriginal => {
19
+ const actual = await importOriginal();
20
+ return {
21
+ ...actual,
22
+ getIntunedExtensionServer: () => server,
23
+ getTabId: _vitest.vi.fn(async () => 0)
24
+ };
25
+ });
26
+ function makeMockPage() {
27
+ return {
28
+ waitForLoadState: _vitest.vi.fn().mockResolvedValue(undefined),
29
+ waitForFunction: _vitest.vi.fn().mockResolvedValue(undefined),
30
+ evaluate: _vitest.vi.fn().mockResolvedValue(0)
31
+ };
32
+ }
33
+ async function runCaptchaTestScenario({
34
+ page,
35
+ mockStates,
36
+ timeoutMs = 10000,
37
+ settleDurationMs = 100,
38
+ expectErrorCode,
39
+ expectTimeout = false
40
+ }) {
41
+ for (const state of mockStates) {
42
+ await server.handleUpsertCaptcha(state.initial);
43
+ }
44
+ const allTransitions = [];
45
+ for (const state of mockStates) {
46
+ for (const transition of state.transitions) {
47
+ allTransitions.push({
48
+ timeMs: transition.timeMs,
49
+ captchaId: state.initial.id,
50
+ type: state.initial.type,
51
+ status: transition.status,
52
+ errorCode: transition.errorCode
53
+ });
54
+ }
55
+ }
56
+ allTransitions.sort((a, b) => a.timeMs - b.timeMs);
57
+ const p = (0, _captcha.waitForCaptchaSolve)(page, {
58
+ timeoutInMs: timeoutMs,
59
+ settleDurationMs
60
+ });
61
+ let assertion;
62
+ if (expectErrorCode) {
63
+ assertion = (0, _vitest.expect)(p).rejects.toThrow(`CAPTCHA Solve Error: ${expectErrorCode}`);
64
+ } else if (expectTimeout) {
65
+ assertion = (0, _vitest.expect)(p).rejects.toThrow("CAPTCHA Solve timed out with pending captchas.");
66
+ }
67
+ await _vitest.vi.advanceTimersByTimeAsync(0);
68
+ let currentTimeMs = 0;
69
+ for (const transition of allTransitions) {
70
+ const deltaMs = transition.timeMs - currentTimeMs;
71
+ if (deltaMs > 0) {
72
+ await _vitest.vi.advanceTimersByTimeAsync(deltaMs);
73
+ currentTimeMs = transition.timeMs;
74
+ }
75
+ const captcha = transition.status === "error" && transition.errorCode ? {
76
+ id: transition.captchaId,
77
+ tabId: 0,
78
+ type: transition.type,
79
+ status: "error",
80
+ error: {
81
+ code: transition.errorCode
82
+ }
83
+ } : {
84
+ id: transition.captchaId,
85
+ tabId: 0,
86
+ type: transition.type,
87
+ status: transition.status
88
+ };
89
+ await server.handleUpsertCaptcha(captcha);
90
+ }
91
+ await _vitest.vi.advanceTimersByTimeAsync(settleDurationMs + 10);
92
+ if (expectTimeout || expectErrorCode) {
93
+ await _vitest.vi.advanceTimersByTimeAsync(timeoutMs + settleDurationMs);
94
+ }
95
+ if (assertion) {
96
+ await assertion;
97
+ } else {
98
+ await (0, _vitest.expect)(p).resolves.toBeUndefined();
99
+ }
100
+ }
101
+ (0, _vitest.beforeEach)(() => {
102
+ _vitest.vi.useFakeTimers();
103
+ server = new _intunedExtensionServer.ExtensionServer();
104
+ });
105
+ (0, _vitest.afterEach)(() => {
106
+ _vitest.vi.useRealTimers();
107
+ _vitest.vi.restoreAllMocks();
108
+ });
109
+ (0, _vitest.describe)("runtime/captcha", () => {
110
+ (0, _vitest.it)("waitForCaptchaSolve: resolves after settle period when no captchas are present", async () => {
111
+ const page = makeMockPage();
112
+ const p = (0, _captcha.waitForCaptchaSolve)(page, {
113
+ timeoutInMs: 1000,
114
+ settleDurationMs: 10
115
+ });
116
+ await _vitest.vi.advanceTimersByTimeAsync(10);
117
+ await p;
118
+ (0, _vitest.expect)(page.waitForLoadState).not.toHaveBeenCalled();
119
+ });
120
+ (0, _vitest.it)("withWaitForCaptchaSolve: preserves return value and waits for networkidle by default", async () => {
121
+ const page = makeMockPage();
122
+ const p = (0, _captcha.withWaitForCaptchaSolve)(async () => "ok", {
123
+ page,
124
+ timeoutInMs: 1000,
125
+ settleDurationMs: 10
126
+ });
127
+ await _vitest.vi.advanceTimersByTimeAsync(10);
128
+ await (0, _vitest.expect)(p).resolves.toBe("ok");
129
+ (0, _vitest.expect)(page.waitForLoadState).toHaveBeenCalledWith("networkidle");
130
+ });
131
+ (0, _vitest.it)("withWaitForCaptchaSolve: skips networkidle when disabled", async () => {
132
+ const page = makeMockPage();
133
+ const p = (0, _captcha.withWaitForCaptchaSolve)(async () => "ok", {
134
+ page,
135
+ timeoutInMs: 1000,
136
+ settleDurationMs: 10,
137
+ waitForNetworkSettled: false
138
+ });
139
+ await _vitest.vi.advanceTimersByTimeAsync(10);
140
+ await (0, _vitest.expect)(p).resolves.toBe("ok");
141
+ (0, _vitest.expect)(page.waitForLoadState).not.toHaveBeenCalled();
142
+ });
143
+ (0, _vitest.it)("waitForCaptchaSolve: completes when a solving captcha becomes solved", async () => {
144
+ const page = makeMockPage();
145
+ await server.handleUpsertCaptcha({
146
+ id: "c1",
147
+ tabId: 0,
148
+ type: "recaptcha",
149
+ status: "solving",
150
+ retryCount: 0
151
+ });
152
+ const p = (0, _captcha.waitForCaptchaSolve)(page, {
153
+ timeoutInMs: 1000,
154
+ settleDurationMs: 10
155
+ });
156
+ await Promise.resolve();
157
+ await server.handleUpsertCaptcha({
158
+ id: "c1",
159
+ tabId: 0,
160
+ type: "recaptcha",
161
+ status: "solved",
162
+ retryCount: 0
163
+ });
164
+ await _vitest.vi.advanceTimersByTimeAsync(10);
165
+ await (0, _vitest.expect)(p).resolves.toBeUndefined();
166
+ });
167
+ (0, _vitest.it)("waitForCaptchaSolve: throws on captcha error", async () => {
168
+ const page = makeMockPage();
169
+ await server.handleUpsertCaptcha({
170
+ id: "c1",
171
+ tabId: 0,
172
+ type: "recaptcha",
173
+ status: "solving",
174
+ retryCount: 0
175
+ });
176
+ const p = (0, _captcha.waitForCaptchaSolve)(page, {
177
+ timeoutInMs: 1000,
178
+ settleDurationMs: 10
179
+ });
180
+ const assertion = (0, _vitest.expect)(p).rejects.toThrow("CAPTCHA Solve Error: UNEXPECTED_ERROR");
181
+ await _vitest.vi.advanceTimersByTimeAsync(0);
182
+ await server.handleUpsertCaptcha({
183
+ id: "c1",
184
+ tabId: 0,
185
+ type: "recaptcha",
186
+ status: "error",
187
+ retryCount: 0,
188
+ error: {
189
+ code: "UNEXPECTED_ERROR"
190
+ }
191
+ });
192
+ await _vitest.vi.advanceTimersByTimeAsync(10);
193
+ await assertion;
194
+ });
195
+ (0, _vitest.it)("waitForCaptchaSolve: times out if captchas are still pending", async () => {
196
+ const page = makeMockPage();
197
+ await server.handleUpsertCaptcha({
198
+ id: "c1",
199
+ tabId: 0,
200
+ type: "recaptcha",
201
+ status: "solving",
202
+ retryCount: 0
203
+ });
204
+ const p = (0, _captcha.waitForCaptchaSolve)(page, {
205
+ timeoutInMs: 5,
206
+ settleDurationMs: 1
207
+ });
208
+ const assertion = (0, _vitest.expect)(p).rejects.toThrow("CAPTCHA Solve timed out with pending captchas.");
209
+ await Promise.resolve();
210
+ await _vitest.vi.advanceTimersByTimeAsync(6);
211
+ await _vitest.vi.advanceTimersByTimeAsync(1);
212
+ await assertion;
213
+ });
214
+ (0, _vitest.it)("waitForCaptchaSolve: waits for all captchas to resolve", async () => {
215
+ const page = makeMockPage();
216
+ await server.handleUpsertCaptcha({
217
+ id: "c1",
218
+ tabId: 0,
219
+ type: "recaptcha",
220
+ status: "solving",
221
+ retryCount: 0
222
+ });
223
+ await server.handleUpsertCaptcha({
224
+ id: "c2",
225
+ tabId: 0,
226
+ type: "hcaptcha",
227
+ status: "solving",
228
+ retryCount: 0
229
+ });
230
+ const p = (0, _captcha.waitForCaptchaSolve)(page, {
231
+ timeoutInMs: 1000,
232
+ settleDurationMs: 10
233
+ });
234
+ await Promise.resolve();
235
+ await server.handleUpsertCaptcha({
236
+ id: "c1",
237
+ tabId: 0,
238
+ type: "recaptcha",
239
+ status: "solved",
240
+ retryCount: 0
241
+ });
242
+ await _vitest.vi.advanceTimersByTimeAsync(10);
243
+ let settled = false;
244
+ p.then(() => settled = true).catch(() => settled = true);
245
+ await Promise.resolve();
246
+ (0, _vitest.expect)(settled).toBe(false);
247
+ await server.handleUpsertCaptcha({
248
+ id: "c2",
249
+ tabId: 0,
250
+ type: "hcaptcha",
251
+ status: "detached",
252
+ retryCount: 0
253
+ });
254
+ await _vitest.vi.advanceTimersByTimeAsync(10);
255
+ await (0, _vitest.expect)(p).resolves.toBeUndefined();
256
+ });
257
+ (0, _vitest.describe)("captchaStateTransitions", () => {
258
+ (0, _vitest.it)("captcha detached completes successfully", async () => {
259
+ const page = makeMockPage();
260
+ await runCaptchaTestScenario({
261
+ page,
262
+ mockStates: [{
263
+ initial: {
264
+ id: "test1",
265
+ tabId: 0,
266
+ type: "recaptcha",
267
+ status: "solving"
268
+ },
269
+ transitions: [{
270
+ timeMs: 100,
271
+ status: "detached"
272
+ }]
273
+ }],
274
+ settleDurationMs: 100
275
+ });
276
+ });
277
+ (0, _vitest.it)("attached to solving to solved", async () => {
278
+ const page = makeMockPage();
279
+ await runCaptchaTestScenario({
280
+ page,
281
+ mockStates: [{
282
+ initial: {
283
+ id: "captcha1",
284
+ tabId: 0,
285
+ type: "recaptcha",
286
+ status: "attached"
287
+ },
288
+ transitions: [{
289
+ timeMs: 50,
290
+ status: "solving"
291
+ }, {
292
+ timeMs: 350,
293
+ status: "solved"
294
+ }]
295
+ }],
296
+ settleDurationMs: 100
297
+ });
298
+ });
299
+ (0, _vitest.it)("attached to detached", async () => {
300
+ const page = makeMockPage();
301
+ await runCaptchaTestScenario({
302
+ page,
303
+ mockStates: [{
304
+ initial: {
305
+ id: "captcha1",
306
+ tabId: 0,
307
+ type: "recaptcha",
308
+ status: "attached"
309
+ },
310
+ transitions: [{
311
+ timeMs: 100,
312
+ status: "detached"
313
+ }]
314
+ }],
315
+ settleDurationMs: 100
316
+ });
317
+ });
318
+ (0, _vitest.it)("attached to solving to error", async () => {
319
+ const page = makeMockPage();
320
+ await runCaptchaTestScenario({
321
+ page,
322
+ mockStates: [{
323
+ initial: {
324
+ id: "captcha1",
325
+ tabId: 0,
326
+ type: "recaptcha",
327
+ status: "attached"
328
+ },
329
+ transitions: [{
330
+ timeMs: 50,
331
+ status: "solving"
332
+ }, {
333
+ timeMs: 550,
334
+ status: "error",
335
+ errorCode: "UNEXPECTED_ERROR"
336
+ }]
337
+ }],
338
+ settleDurationMs: 100,
339
+ expectErrorCode: "UNEXPECTED_ERROR"
340
+ });
341
+ });
342
+ (0, _vitest.it)("attached to solving to detached", async () => {
343
+ const page = makeMockPage();
344
+ await runCaptchaTestScenario({
345
+ page,
346
+ mockStates: [{
347
+ initial: {
348
+ id: "captcha1",
349
+ tabId: 0,
350
+ type: "recaptcha",
351
+ status: "attached"
352
+ },
353
+ transitions: [{
354
+ timeMs: 50,
355
+ status: "solving"
356
+ }, {
357
+ timeMs: 200,
358
+ status: "detached"
359
+ }]
360
+ }],
361
+ settleDurationMs: 100
362
+ });
363
+ });
364
+ (0, _vitest.it)("visibility toggle then solved", async () => {
365
+ const page = makeMockPage();
366
+ await runCaptchaTestScenario({
367
+ page,
368
+ mockStates: [{
369
+ initial: {
370
+ id: "captcha1",
371
+ tabId: 0,
372
+ type: "recaptcha",
373
+ status: "attached"
374
+ },
375
+ transitions: [{
376
+ timeMs: 50,
377
+ status: "solving"
378
+ }, {
379
+ timeMs: 100,
380
+ status: "attached"
381
+ }, {
382
+ timeMs: 120,
383
+ status: "solving"
384
+ }, {
385
+ timeMs: 420,
386
+ status: "solved"
387
+ }]
388
+ }],
389
+ settleDurationMs: 100
390
+ });
391
+ });
392
+ (0, _vitest.it)("visibility toggle then error", async () => {
393
+ const page = makeMockPage();
394
+ await runCaptchaTestScenario({
395
+ page,
396
+ mockStates: [{
397
+ initial: {
398
+ id: "captcha1",
399
+ tabId: 0,
400
+ type: "recaptcha",
401
+ status: "attached"
402
+ },
403
+ transitions: [{
404
+ timeMs: 50,
405
+ status: "solving"
406
+ }, {
407
+ timeMs: 100,
408
+ status: "attached"
409
+ }, {
410
+ timeMs: 120,
411
+ status: "solving"
412
+ }, {
413
+ timeMs: 620,
414
+ status: "error",
415
+ errorCode: "MAX_RETRIES"
416
+ }]
417
+ }],
418
+ settleDurationMs: 100,
419
+ expectErrorCode: "MAX_RETRIES"
420
+ });
421
+ });
422
+ (0, _vitest.it)("visibility toggle then detached", async () => {
423
+ const page = makeMockPage();
424
+ await runCaptchaTestScenario({
425
+ page,
426
+ mockStates: [{
427
+ initial: {
428
+ id: "captcha1",
429
+ tabId: 0,
430
+ type: "recaptcha",
431
+ status: "attached"
432
+ },
433
+ transitions: [{
434
+ timeMs: 50,
435
+ status: "solving"
436
+ }, {
437
+ timeMs: 100,
438
+ status: "attached"
439
+ }, {
440
+ timeMs: 110,
441
+ status: "detached"
442
+ }]
443
+ }],
444
+ settleDurationMs: 100
445
+ });
446
+ });
447
+ (0, _vitest.it)("visibility toggle solving then detached", async () => {
448
+ const page = makeMockPage();
449
+ await runCaptchaTestScenario({
450
+ page,
451
+ mockStates: [{
452
+ initial: {
453
+ id: "captcha1",
454
+ tabId: 0,
455
+ type: "recaptcha",
456
+ status: "attached"
457
+ },
458
+ transitions: [{
459
+ timeMs: 50,
460
+ status: "solving"
461
+ }, {
462
+ timeMs: 100,
463
+ status: "attached"
464
+ }, {
465
+ timeMs: 120,
466
+ status: "solving"
467
+ }, {
468
+ timeMs: 270,
469
+ status: "detached"
470
+ }]
471
+ }],
472
+ settleDurationMs: 100
473
+ });
474
+ });
475
+ (0, _vitest.it)("rapid visibility toggle then solved", async () => {
476
+ const page = makeMockPage();
477
+ await runCaptchaTestScenario({
478
+ page,
479
+ mockStates: [{
480
+ initial: {
481
+ id: "captcha1",
482
+ tabId: 0,
483
+ type: "recaptcha",
484
+ status: "attached"
485
+ },
486
+ transitions: [{
487
+ timeMs: 50,
488
+ status: "solving"
489
+ }, {
490
+ timeMs: 60,
491
+ status: "attached"
492
+ }, {
493
+ timeMs: 65,
494
+ status: "solving"
495
+ }, {
496
+ timeMs: 75,
497
+ status: "attached"
498
+ }, {
499
+ timeMs: 80,
500
+ status: "solving"
501
+ }, {
502
+ timeMs: 380,
503
+ status: "solved"
504
+ }]
505
+ }],
506
+ settleDurationMs: 100
507
+ });
508
+ });
509
+ (0, _vitest.it)("rapid visibility toggle then detached", async () => {
510
+ const page = makeMockPage();
511
+ await runCaptchaTestScenario({
512
+ page,
513
+ mockStates: [{
514
+ initial: {
515
+ id: "captcha1",
516
+ tabId: 0,
517
+ type: "recaptcha",
518
+ status: "attached"
519
+ },
520
+ transitions: [{
521
+ timeMs: 50,
522
+ status: "solving"
523
+ }, {
524
+ timeMs: 60,
525
+ status: "attached"
526
+ }, {
527
+ timeMs: 65,
528
+ status: "solving"
529
+ }, {
530
+ timeMs: 75,
531
+ status: "attached"
532
+ }, {
533
+ timeMs: 80,
534
+ status: "detached"
535
+ }]
536
+ }],
537
+ settleDurationMs: 100
538
+ });
539
+ });
540
+ (0, _vitest.it)("instant toggles then solved", async () => {
541
+ const page = makeMockPage();
542
+ await runCaptchaTestScenario({
543
+ page,
544
+ mockStates: [{
545
+ initial: {
546
+ id: "captcha1",
547
+ tabId: 0,
548
+ type: "recaptcha",
549
+ status: "attached"
550
+ },
551
+ transitions: [{
552
+ timeMs: 1,
553
+ status: "solving"
554
+ }, {
555
+ timeMs: 2,
556
+ status: "attached"
557
+ }, {
558
+ timeMs: 3,
559
+ status: "solving"
560
+ }, {
561
+ timeMs: 300,
562
+ status: "solved"
563
+ }]
564
+ }],
565
+ settleDurationMs: 100
566
+ });
567
+ });
568
+ (0, _vitest.it)("long solving timeout", async () => {
569
+ const page = makeMockPage();
570
+ await runCaptchaTestScenario({
571
+ page,
572
+ mockStates: [{
573
+ initial: {
574
+ id: "captcha1",
575
+ tabId: 0,
576
+ type: "recaptcha",
577
+ status: "attached"
578
+ },
579
+ transitions: [{
580
+ timeMs: 10,
581
+ status: "solving"
582
+ }, {
583
+ timeMs: 30000,
584
+ status: "error",
585
+ errorCode: "UNEXPECTED_ERROR"
586
+ }]
587
+ }],
588
+ timeoutMs: 10000,
589
+ settleDurationMs: 3000,
590
+ expectTimeout: true
591
+ });
592
+ });
593
+ (0, _vitest.it)("multiple captchas both solve", async () => {
594
+ const page = makeMockPage();
595
+ await runCaptchaTestScenario({
596
+ page,
597
+ mockStates: [{
598
+ initial: {
599
+ id: "captcha1",
600
+ tabId: 0,
601
+ type: "recaptcha",
602
+ status: "solving"
603
+ },
604
+ transitions: [{
605
+ timeMs: 100,
606
+ status: "solved"
607
+ }]
608
+ }, {
609
+ initial: {
610
+ id: "captcha2",
611
+ tabId: 0,
612
+ type: "hcaptcha",
613
+ status: "solving"
614
+ },
615
+ transitions: [{
616
+ timeMs: 200,
617
+ status: "solved"
618
+ }]
619
+ }],
620
+ settleDurationMs: 100
621
+ });
622
+ });
623
+ (0, _vitest.it)("multiple captchas one errors", async () => {
624
+ const page = makeMockPage();
625
+ await runCaptchaTestScenario({
626
+ page,
627
+ mockStates: [{
628
+ initial: {
629
+ id: "captcha1",
630
+ tabId: 0,
631
+ type: "recaptcha",
632
+ status: "solving"
633
+ },
634
+ transitions: []
635
+ }, {
636
+ initial: {
637
+ id: "captcha2",
638
+ tabId: 0,
639
+ type: "hcaptcha",
640
+ status: "solving"
641
+ },
642
+ transitions: [{
643
+ timeMs: 100,
644
+ status: "error",
645
+ errorCode: "HIT_LIMIT"
646
+ }]
647
+ }],
648
+ settleDurationMs: 100,
649
+ expectErrorCode: "HIT_LIMIT"
650
+ });
651
+ });
652
+ (0, _vitest.it)("staggered captcha resolution", async () => {
653
+ const page = makeMockPage();
654
+ await runCaptchaTestScenario({
655
+ page,
656
+ mockStates: [{
657
+ initial: {
658
+ id: "captcha1",
659
+ tabId: 0,
660
+ type: "recaptcha",
661
+ status: "solving"
662
+ },
663
+ transitions: [{
664
+ timeMs: 100,
665
+ status: "solved"
666
+ }]
667
+ }, {
668
+ initial: {
669
+ id: "captcha2",
670
+ tabId: 0,
671
+ type: "hcaptcha",
672
+ status: "solving"
673
+ },
674
+ transitions: [{
675
+ timeMs: 200,
676
+ status: "detached"
677
+ }]
678
+ }, {
679
+ initial: {
680
+ id: "captcha3",
681
+ tabId: 0,
682
+ type: "funcaptcha",
683
+ status: "solving"
684
+ },
685
+ transitions: [{
686
+ timeMs: 300,
687
+ status: "solved"
688
+ }]
689
+ }],
690
+ settleDurationMs: 100
691
+ });
692
+ });
693
+ });
694
+ (0, _vitest.describe)("onCaptchaEvent", () => {
695
+ (0, _vitest.it)("calls callback for matching status", async () => {
696
+ const page = makeMockPage();
697
+ const receivedCaptchas = [];
698
+ await (0, _captcha.onCaptchaEvent)(page, "solved", async captcha => {
699
+ receivedCaptchas.push(captcha);
700
+ });
701
+ await server.handleUpsertCaptcha({
702
+ id: "c1",
703
+ tabId: 0,
704
+ type: "recaptcha",
705
+ status: "solved"
706
+ });
707
+ (0, _vitest.expect)(receivedCaptchas).toHaveLength(1);
708
+ (0, _vitest.expect)(receivedCaptchas[0].id).toBe("c1");
709
+ (0, _vitest.expect)(receivedCaptchas[0].status).toBe("solved");
710
+ });
711
+ (0, _vitest.it)("ignores non-matching status", async () => {
712
+ const page = makeMockPage();
713
+ const receivedCaptchas = [];
714
+ await (0, _captcha.onCaptchaEvent)(page, "solved", async captcha => {
715
+ receivedCaptchas.push(captcha);
716
+ });
717
+ await server.handleUpsertCaptcha({
718
+ id: "c1",
719
+ tabId: 0,
720
+ type: "recaptcha",
721
+ status: "solving"
722
+ });
723
+ (0, _vitest.expect)(receivedCaptchas).toHaveLength(0);
724
+ });
725
+ (0, _vitest.it)("calls callback multiple times for multiple matching events", async () => {
726
+ const page = makeMockPage();
727
+ const receivedCaptchas = [];
728
+ await (0, _captcha.onCaptchaEvent)(page, "solved", async captcha => {
729
+ receivedCaptchas.push(captcha);
730
+ });
731
+ await server.handleUpsertCaptcha({
732
+ id: "c1",
733
+ tabId: 0,
734
+ type: "recaptcha",
735
+ status: "solved"
736
+ });
737
+ await server.handleUpsertCaptcha({
738
+ id: "c2",
739
+ tabId: 0,
740
+ type: "hcaptcha",
741
+ status: "solved"
742
+ });
743
+ (0, _vitest.expect)(receivedCaptchas).toHaveLength(2);
744
+ (0, _vitest.expect)(receivedCaptchas[0].id).toBe("c1");
745
+ (0, _vitest.expect)(receivedCaptchas[1].id).toBe("c2");
746
+ });
747
+ });
748
+ (0, _vitest.describe)("onceCaptchaEvent", () => {
749
+ (0, _vitest.it)("calls callback only once for first matching event", async () => {
750
+ const page = makeMockPage();
751
+ const receivedCaptchas = [];
752
+ await (0, _captcha.onceCaptchaEvent)(page, "solved", async captcha => {
753
+ receivedCaptchas.push(captcha);
754
+ });
755
+ await server.handleUpsertCaptcha({
756
+ id: "c1",
757
+ tabId: 0,
758
+ type: "recaptcha",
759
+ status: "solved"
760
+ });
761
+ await server.handleUpsertCaptcha({
762
+ id: "c2",
763
+ tabId: 0,
764
+ type: "hcaptcha",
765
+ status: "solved"
766
+ });
767
+ (0, _vitest.expect)(receivedCaptchas).toHaveLength(1);
768
+ (0, _vitest.expect)(receivedCaptchas[0].id).toBe("c1");
769
+ });
770
+ (0, _vitest.it)("receives all events until first match, then unsubscribes", async () => {
771
+ const page = makeMockPage();
772
+ const receivedCaptchas = [];
773
+ await (0, _captcha.onceCaptchaEvent)(page, "solved", async captcha => {
774
+ receivedCaptchas.push(captcha);
775
+ });
776
+ await server.handleUpsertCaptcha({
777
+ id: "c1",
778
+ tabId: 0,
779
+ type: "recaptcha",
780
+ status: "solving"
781
+ });
782
+ (0, _vitest.expect)(receivedCaptchas).toHaveLength(0);
783
+ await server.handleUpsertCaptcha({
784
+ id: "c2",
785
+ tabId: 0,
786
+ type: "recaptcha",
787
+ status: "solved"
788
+ });
789
+ (0, _vitest.expect)(receivedCaptchas).toHaveLength(1);
790
+ (0, _vitest.expect)(receivedCaptchas[0].id).toBe("c2");
791
+ await server.handleUpsertCaptcha({
792
+ id: "c3",
793
+ tabId: 0,
794
+ type: "recaptcha",
795
+ status: "solved"
796
+ });
797
+ (0, _vitest.expect)(receivedCaptchas).toHaveLength(1);
798
+ });
799
+ (0, _vitest.it)("unsubscribes after first matching event", async () => {
800
+ const page = makeMockPage();
801
+ let callCount = 0;
802
+ await (0, _captcha.onceCaptchaEvent)(page, "solved", async () => {
803
+ callCount++;
804
+ });
805
+ await server.handleUpsertCaptcha({
806
+ id: "c1",
807
+ tabId: 0,
808
+ type: "recaptcha",
809
+ status: "solved"
810
+ });
811
+ (0, _vitest.expect)(callCount).toBe(1);
812
+ await server.handleUpsertCaptcha({
813
+ id: "c2",
814
+ tabId: 0,
815
+ type: "recaptcha",
816
+ status: "solved"
817
+ });
818
+ (0, _vitest.expect)(callCount).toBe(1);
819
+ });
820
+ });
821
+ });