@intuned/runtime-dev 1.3.14-ts-runtime-helpers-1 → 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.
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  var _vitest = require("vitest");
4
+ var _intunedExtensionServer = require("../common/extension/intunedExtensionServer");
4
5
  var _captcha = require("./captcha");
5
6
  _vitest.vi.mock("fastify", () => {
6
7
  const fastify = () => ({
@@ -13,48 +14,93 @@ _vitest.vi.mock("fastify", () => {
13
14
  default: fastify
14
15
  };
15
16
  });
16
- class FakeExtensionServer {
17
- captchas = new Map();
18
- subscribers = new Set();
19
- subscribeCalls = 0;
20
- unsubscribeCalls = 0;
21
- seed(captchas) {
22
- this.captchas.clear();
23
- for (const c of captchas) this.captchas.set(c.id, c);
24
- }
25
- async upsertCaptcha(captcha) {
26
- this.captchas.set(captcha.id, captcha);
27
- for (const h of this.subscribers) {
28
- await h(captcha);
29
- }
30
- }
31
- async getCaptchas(_page, status) {
32
- const list = [...this.captchas.values()];
33
- return status ? list.filter(c => c.status === status) : list;
34
- }
35
- async subscribe(_page, handler) {
36
- this.subscribeCalls += 1;
37
- this.subscribers.add(handler);
38
- }
39
- async unsubscribe(_page, handler) {
40
- this.unsubscribeCalls += 1;
41
- this.subscribers.delete(handler);
42
- }
43
- }
44
17
  let server;
45
- _vitest.vi.mock("../common/extension/intunedExtensionServer", () => {
18
+ _vitest.vi.mock("../common/extension/intunedExtensionServer", async importOriginal => {
19
+ const actual = await importOriginal();
46
20
  return {
47
- getIntunedExtensionServer: () => server
21
+ ...actual,
22
+ getIntunedExtensionServer: () => server,
23
+ getTabId: _vitest.vi.fn(async () => 0)
48
24
  };
49
25
  });
50
26
  function makeMockPage() {
51
27
  return {
52
- waitForLoadState: _vitest.vi.fn().mockResolvedValue(undefined)
28
+ waitForLoadState: _vitest.vi.fn().mockResolvedValue(undefined),
29
+ waitForFunction: _vitest.vi.fn().mockResolvedValue(undefined),
30
+ evaluate: _vitest.vi.fn().mockResolvedValue(0)
53
31
  };
54
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
+ }
55
101
  (0, _vitest.beforeEach)(() => {
56
102
  _vitest.vi.useFakeTimers();
57
- server = new FakeExtensionServer();
103
+ server = new _intunedExtensionServer.ExtensionServer();
58
104
  });
59
105
  (0, _vitest.afterEach)(() => {
60
106
  _vitest.vi.useRealTimers();
@@ -65,13 +111,11 @@ function makeMockPage() {
65
111
  const page = makeMockPage();
66
112
  const p = (0, _captcha.waitForCaptchaSolve)(page, {
67
113
  timeoutInMs: 1000,
68
- settlePeriodInMs: 10
114
+ settleDurationMs: 10
69
115
  });
70
116
  await _vitest.vi.advanceTimersByTimeAsync(10);
71
117
  await p;
72
118
  (0, _vitest.expect)(page.waitForLoadState).not.toHaveBeenCalled();
73
- (0, _vitest.expect)(server.subscribeCalls).toBe(1);
74
- (0, _vitest.expect)(server.unsubscribeCalls).toBe(1);
75
119
  });
76
120
  (0, _vitest.it)("withWaitForCaptchaSolve: preserves return value and waits for networkidle by default", async () => {
77
121
  const page = makeMockPage();
@@ -98,19 +142,19 @@ function makeMockPage() {
98
142
  });
99
143
  (0, _vitest.it)("waitForCaptchaSolve: completes when a solving captcha becomes solved", async () => {
100
144
  const page = makeMockPage();
101
- server.seed([{
145
+ await server.handleUpsertCaptcha({
102
146
  id: "c1",
103
147
  tabId: 0,
104
148
  type: "recaptcha",
105
149
  status: "solving",
106
150
  retryCount: 0
107
- }]);
151
+ });
108
152
  const p = (0, _captcha.waitForCaptchaSolve)(page, {
109
153
  timeoutInMs: 1000,
110
- settlePeriodInMs: 10
154
+ settleDurationMs: 10
111
155
  });
112
156
  await Promise.resolve();
113
- await server.upsertCaptcha({
157
+ await server.handleUpsertCaptcha({
114
158
  id: "c1",
115
159
  tabId: 0,
116
160
  type: "recaptcha",
@@ -122,20 +166,20 @@ function makeMockPage() {
122
166
  });
123
167
  (0, _vitest.it)("waitForCaptchaSolve: throws on captcha error", async () => {
124
168
  const page = makeMockPage();
125
- server.seed([{
169
+ await server.handleUpsertCaptcha({
126
170
  id: "c1",
127
171
  tabId: 0,
128
172
  type: "recaptcha",
129
173
  status: "solving",
130
174
  retryCount: 0
131
- }]);
175
+ });
132
176
  const p = (0, _captcha.waitForCaptchaSolve)(page, {
133
177
  timeoutInMs: 1000,
134
- settlePeriodInMs: 10
178
+ settleDurationMs: 10
135
179
  });
136
180
  const assertion = (0, _vitest.expect)(p).rejects.toThrow("CAPTCHA Solve Error: UNEXPECTED_ERROR");
137
- await Promise.resolve();
138
- await server.upsertCaptcha({
181
+ await _vitest.vi.advanceTimersByTimeAsync(0);
182
+ await server.handleUpsertCaptcha({
139
183
  id: "c1",
140
184
  tabId: 0,
141
185
  type: "recaptcha",
@@ -147,49 +191,48 @@ function makeMockPage() {
147
191
  });
148
192
  await _vitest.vi.advanceTimersByTimeAsync(10);
149
193
  await assertion;
150
- (0, _vitest.expect)(server.unsubscribeCalls).toBe(1);
151
194
  });
152
195
  (0, _vitest.it)("waitForCaptchaSolve: times out if captchas are still pending", async () => {
153
196
  const page = makeMockPage();
154
- server.seed([{
197
+ await server.handleUpsertCaptcha({
155
198
  id: "c1",
156
199
  tabId: 0,
157
200
  type: "recaptcha",
158
201
  status: "solving",
159
202
  retryCount: 0
160
- }]);
203
+ });
161
204
  const p = (0, _captcha.waitForCaptchaSolve)(page, {
162
205
  timeoutInMs: 5,
163
- settlePeriodInMs: 1
206
+ settleDurationMs: 1
164
207
  });
165
208
  const assertion = (0, _vitest.expect)(p).rejects.toThrow("CAPTCHA Solve timed out with pending captchas.");
166
209
  await Promise.resolve();
167
210
  await _vitest.vi.advanceTimersByTimeAsync(6);
168
211
  await _vitest.vi.advanceTimersByTimeAsync(1);
169
212
  await assertion;
170
- (0, _vitest.expect)(server.unsubscribeCalls).toBe(1);
171
213
  });
172
214
  (0, _vitest.it)("waitForCaptchaSolve: waits for all captchas to resolve", async () => {
173
215
  const page = makeMockPage();
174
- server.seed([{
216
+ await server.handleUpsertCaptcha({
175
217
  id: "c1",
176
218
  tabId: 0,
177
219
  type: "recaptcha",
178
220
  status: "solving",
179
221
  retryCount: 0
180
- }, {
222
+ });
223
+ await server.handleUpsertCaptcha({
181
224
  id: "c2",
182
225
  tabId: 0,
183
226
  type: "hcaptcha",
184
227
  status: "solving",
185
228
  retryCount: 0
186
- }]);
229
+ });
187
230
  const p = (0, _captcha.waitForCaptchaSolve)(page, {
188
231
  timeoutInMs: 1000,
189
- settlePeriodInMs: 10
232
+ settleDurationMs: 10
190
233
  });
191
234
  await Promise.resolve();
192
- await server.upsertCaptcha({
235
+ await server.handleUpsertCaptcha({
193
236
  id: "c1",
194
237
  tabId: 0,
195
238
  type: "recaptcha",
@@ -201,7 +244,7 @@ function makeMockPage() {
201
244
  p.then(() => settled = true).catch(() => settled = true);
202
245
  await Promise.resolve();
203
246
  (0, _vitest.expect)(settled).toBe(false);
204
- await server.upsertCaptcha({
247
+ await server.handleUpsertCaptcha({
205
248
  id: "c2",
206
249
  tabId: 0,
207
250
  type: "hcaptcha",
@@ -211,4 +254,568 @@ function makeMockPage() {
211
254
  await _vitest.vi.advanceTimersByTimeAsync(10);
212
255
  await (0, _vitest.expect)(p).resolves.toBeUndefined();
213
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
+ });
214
821
  });