@khanacademy/wonder-blocks-button 6.3.9 → 6.3.11

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,850 +0,0 @@
1
- /**
2
- * Test for Wonder Blocks Button component.
3
- *
4
- * The test for buttons with icons are in a separate file
5
- * (button-with-icon.test.tsx) since this one is already too long.
6
- */
7
- import * as React from "react";
8
- import {MemoryRouter, Route, Switch} from "react-router-dom";
9
- import {render, screen, waitFor} from "@testing-library/react";
10
- import {userEvent} from "@testing-library/user-event";
11
-
12
- import Button from "../button";
13
-
14
- describe("Button", () => {
15
- const {location} = window;
16
-
17
- beforeAll(() => {
18
- // @ts-expect-error [FEI-5019] - TS2790 - The operand of a 'delete' operator must be optional.
19
- delete window.location;
20
- // @ts-expect-error [FEI-5019] - TS2740 - Type '{ assign: Mock<any, any, any>; }' is missing the following properties from type 'Location': ancestorOrigins, hash, host, hostname, and 8 more.
21
- window.location = {assign: jest.fn()};
22
- });
23
-
24
- afterAll(() => {
25
- window.location = location;
26
- });
27
-
28
- test("client-side navigation", async () => {
29
- // Arrange
30
- render(
31
- <MemoryRouter>
32
- <div>
33
- <Button href="/foo">Click me!</Button>
34
- <Switch>
35
- <Route path="/foo">
36
- <div id="foo">Hello, world!</div>
37
- </Route>
38
- </Switch>
39
- </div>
40
- </MemoryRouter>,
41
- );
42
-
43
- // Act
44
- const button = await screen.findByRole("button");
45
- await userEvent.click(button);
46
-
47
- // Assert
48
- expect(await screen.findByText("Hello, world!")).toBeInTheDocument();
49
- });
50
-
51
- test("beforeNav rejection blocks client-side navigation", async () => {
52
- // Arrange
53
- render(
54
- <MemoryRouter>
55
- <div>
56
- <Button href="/foo" beforeNav={() => Promise.reject()}>
57
- Click me!
58
- </Button>
59
- <Switch>
60
- <Route path="/foo">
61
- <div id="foo">Hello, world!</div>
62
- </Route>
63
- </Switch>
64
- </div>
65
- </MemoryRouter>,
66
- );
67
-
68
- // Act
69
- const button = await screen.findByRole("button");
70
- await userEvent.click(button);
71
-
72
- // Assert
73
- expect(screen.queryByText("Hello, world!")).not.toBeInTheDocument();
74
- });
75
-
76
- test("beforeNav rejection blocks calling safeWithNav", async () => {
77
- // Arrange
78
- const safeWithNavMock = jest.fn();
79
- render(
80
- <MemoryRouter>
81
- <div>
82
- <Button
83
- href="/foo"
84
- beforeNav={() => Promise.reject()}
85
- safeWithNav={safeWithNavMock}
86
- >
87
- Click me!
88
- </Button>
89
- <Switch>
90
- <Route path="/foo">
91
- <div id="foo">Hello, world!</div>
92
- </Route>
93
- </Switch>
94
- </div>
95
- </MemoryRouter>,
96
- );
97
-
98
- // Act
99
- const button = await screen.findByRole("button");
100
- await userEvent.click(button);
101
-
102
- // Assert
103
- expect(safeWithNavMock).not.toHaveBeenCalled();
104
- });
105
-
106
- test("beforeNav resolution results in client-side navigation", async () => {
107
- // Arrange
108
- render(
109
- <MemoryRouter>
110
- <div>
111
- <Button href="/foo" beforeNav={() => Promise.resolve()}>
112
- Click me!
113
- </Button>
114
- <Switch>
115
- <Route path="/foo">
116
- <div id="foo">Hello, world!</div>
117
- </Route>
118
- </Switch>
119
- </div>
120
- </MemoryRouter>,
121
- );
122
-
123
- // Act
124
- await userEvent.click(await screen.findByRole("button"));
125
-
126
- // Assert
127
- await waitFor(async () => {
128
- expect(
129
- await screen.findByText("Hello, world!"),
130
- ).toBeInTheDocument();
131
- });
132
- });
133
-
134
- test("beforeNav resolution results in safeWithNav being called", async () => {
135
- // Arrange
136
- const safeWithNavMock = jest.fn();
137
- render(
138
- <MemoryRouter>
139
- <div>
140
- <Button
141
- href="/foo"
142
- beforeNav={() => Promise.resolve()}
143
- safeWithNav={safeWithNavMock}
144
- >
145
- Click me!
146
- </Button>
147
- <Switch>
148
- <Route path="/foo">
149
- <div id="foo">Hello, world!</div>
150
- </Route>
151
- </Switch>
152
- </div>
153
- </MemoryRouter>,
154
- );
155
-
156
- // Act
157
- await userEvent.click(await screen.findByRole("button"));
158
-
159
- // Assert
160
- await waitFor(() => {
161
- expect(safeWithNavMock).toHaveBeenCalled();
162
- });
163
- });
164
-
165
- // Unfortunately we can't test the spinner here after upgrading to
166
- // user-event v14 as the render happens before we're aable to check on the
167
- // state of the spinner.
168
- test.skip("show circular spinner before beforeNav resolves", async () => {
169
- // Arrange
170
- render(
171
- <MemoryRouter>
172
- <div>
173
- <Button href="/foo" beforeNav={() => Promise.resolve()}>
174
- Click me!
175
- </Button>
176
- <Switch>
177
- <Route path="/foo">
178
- <div id="foo">Hello, world!</div>
179
- </Route>
180
- </Switch>
181
- </div>
182
- </MemoryRouter>,
183
- );
184
-
185
- // Act
186
- const button = await screen.findByRole("button");
187
- await userEvent.click(button);
188
-
189
- // Assert
190
- // TODO(juan): Find a way to use a more accessible query.
191
- expect(await screen.findByTestId("button-spinner")).toBeInTheDocument();
192
- });
193
-
194
- test("safeWithNav with skipClientNav=true waits for promise resolution", async () => {
195
- // Arrange
196
- jest.spyOn(window.location, "assign");
197
- render(
198
- <MemoryRouter>
199
- <div>
200
- <Button
201
- href="/foo"
202
- safeWithNav={() => Promise.resolve()}
203
- skipClientNav={true}
204
- >
205
- Click me!
206
- </Button>
207
- <Switch>
208
- <Route path="/foo">
209
- <div id="foo">Hello, world!</div>
210
- </Route>
211
- </Switch>
212
- </div>
213
- </MemoryRouter>,
214
- );
215
-
216
- // Act
217
- await userEvent.click(await screen.findByRole("button"));
218
-
219
- // Assert
220
- await waitFor(() => {
221
- expect(window.location.assign).toHaveBeenCalledWith("/foo");
222
- });
223
- });
224
-
225
- test("safeWithNav with skipClientNav=true shows spinner", async () => {
226
- // Arrange
227
- jest.spyOn(window.location, "assign");
228
- render(
229
- <MemoryRouter>
230
- <div>
231
- <Button
232
- href="/foo"
233
- safeWithNav={() => Promise.resolve()}
234
- skipClientNav={true}
235
- >
236
- Click me!
237
- </Button>
238
- <Switch>
239
- <Route path="/foo">
240
- <div id="foo">Hello, world!</div>
241
- </Route>
242
- </Switch>
243
- </div>
244
- </MemoryRouter>,
245
- );
246
-
247
- // Act
248
- const button = await screen.findByRole("button");
249
- await userEvent.click(button);
250
-
251
- // Assert
252
- expect(await screen.findByTestId("button-spinner")).toBeInTheDocument();
253
- });
254
-
255
- test("beforeNav resolution and safeWithNav with skipClientNav=true waits for promise resolution", async () => {
256
- // Arrange
257
- jest.spyOn(window.location, "assign");
258
- render(
259
- <MemoryRouter>
260
- <div>
261
- <Button
262
- href="/foo"
263
- beforeNav={() => Promise.resolve()}
264
- safeWithNav={() => Promise.resolve()}
265
- skipClientNav={true}
266
- >
267
- Click me!
268
- </Button>
269
- <Switch>
270
- <Route path="/foo">
271
- <div id="foo">Hello, world!</div>
272
- </Route>
273
- </Switch>
274
- </div>
275
- </MemoryRouter>,
276
- );
277
-
278
- // Act
279
- const button = await screen.findByRole("button");
280
- await userEvent.click(button);
281
-
282
- // Assert
283
- await waitFor(() => {
284
- expect(window.location.assign).toHaveBeenCalledWith("/foo");
285
- });
286
- });
287
-
288
- test("safeWithNav with skipClientNav=true waits for promise rejection", async () => {
289
- // Arrange
290
- jest.spyOn(window.location, "assign");
291
- render(
292
- <MemoryRouter>
293
- <div>
294
- <Button
295
- href="/foo"
296
- safeWithNav={() => Promise.reject()}
297
- skipClientNav={true}
298
- >
299
- Click me!
300
- </Button>
301
- <Switch>
302
- <Route path="/foo">
303
- <div id="foo">Hello, world!</div>
304
- </Route>
305
- </Switch>
306
- </div>
307
- </MemoryRouter>,
308
- );
309
-
310
- // Act
311
- const button = await screen.findByRole("button");
312
- await userEvent.click(button);
313
-
314
- // Assert
315
- expect(window.location.assign).toHaveBeenCalledWith("/foo");
316
- });
317
-
318
- test("safeWithNav with skipClientNav=false calls safeWithNav but doesn't wait to navigate", async () => {
319
- // Arrange
320
- jest.spyOn(window.location, "assign");
321
- const safeWithNavMock = jest.fn();
322
- render(
323
- <MemoryRouter>
324
- <div>
325
- <Button
326
- href="/foo"
327
- safeWithNav={safeWithNavMock}
328
- skipClientNav={false}
329
- >
330
- Click me!
331
- </Button>
332
- <Switch>
333
- <Route path="/foo">
334
- <div id="foo">Hello, world!</div>
335
- </Route>
336
- </Switch>
337
- </div>
338
- </MemoryRouter>,
339
- );
340
-
341
- // Act
342
- const button = await screen.findByRole("button");
343
- await userEvent.click(button);
344
-
345
- // Assert
346
- expect(safeWithNavMock).toHaveBeenCalled();
347
- expect(window.location.assign).toHaveBeenCalledWith("/foo");
348
- });
349
-
350
- test("safeWithNav with beforeNav resolution and skipClientNav=false calls safeWithNav but doesn't wait to navigate", async () => {
351
- // Arrange
352
- jest.spyOn(window.location, "assign");
353
- const safeWithNavMock = jest.fn();
354
- render(
355
- <MemoryRouter>
356
- <div>
357
- <Button
358
- href="/foo"
359
- beforeNav={() => Promise.resolve()}
360
- safeWithNav={safeWithNavMock}
361
- skipClientNav={false}
362
- >
363
- Click me!
364
- </Button>
365
- <Switch>
366
- <Route path="/foo">
367
- <div id="foo">Hello, world!</div>
368
- </Route>
369
- </Switch>
370
- </div>
371
- </MemoryRouter>,
372
- );
373
-
374
- // Act
375
- await userEvent.click(await screen.findByRole("button"));
376
-
377
- // Assert
378
- await waitFor(() => {
379
- expect(safeWithNavMock).toHaveBeenCalled();
380
- });
381
- await waitFor(() => {
382
- expect(window.location.assign).toHaveBeenCalledWith("/foo");
383
- });
384
- });
385
-
386
- test("client-side navigation with unknown URL fails", async () => {
387
- // Arrange
388
- render(
389
- <MemoryRouter>
390
- <div>
391
- <Button href="/unknown">Click me!</Button>
392
- <Switch>
393
- <Route path="/foo">
394
- <div id="foo">Hello, world!</div>
395
- </Route>
396
- </Switch>
397
- </div>
398
- </MemoryRouter>,
399
- );
400
-
401
- // Act
402
- const button = await screen.findByRole("button");
403
- await userEvent.click(button);
404
-
405
- // Assert
406
- expect(screen.queryByText("Hello, world!")).not.toBeInTheDocument();
407
- });
408
-
409
- test("client-side navigation with `skipClientNav` set to `true` fails", async () => {
410
- // Arrange
411
- render(
412
- <MemoryRouter>
413
- <div>
414
- <Button href="/foo" skipClientNav>
415
- Click me!
416
- </Button>
417
- <Switch>
418
- <Route path="/foo">
419
- <div id="foo">Hello, world!</div>
420
- </Route>
421
- </Switch>
422
- </div>
423
- </MemoryRouter>,
424
- );
425
-
426
- // Act
427
- const button = await screen.findByRole("button");
428
- await userEvent.click(button);
429
-
430
- // Assert
431
- expect(screen.queryByText("Hello, world!")).not.toBeInTheDocument();
432
- });
433
-
434
- test("disallow navigation when href and disabled are both set", async () => {
435
- // Arrange
436
- render(
437
- <MemoryRouter>
438
- <div>
439
- <Button href="/foo" disabled={true}>
440
- Click me!
441
- </Button>
442
- <Switch>
443
- <Route path="/foo">
444
- <div id="foo">Hello, world!</div>
445
- </Route>
446
- </Switch>
447
- </div>
448
- </MemoryRouter>,
449
- );
450
-
451
- // Act
452
- const button = await screen.findByRole("button");
453
- await userEvent.click(button);
454
-
455
- // Assert
456
- expect(screen.queryByText("Hello, world!")).not.toBeInTheDocument();
457
- });
458
-
459
- test("don't call beforeNav when href and disabled are both set", async () => {
460
- // Arrange
461
- const beforeNavMock = jest.fn();
462
- render(
463
- <MemoryRouter>
464
- <div>
465
- <Button
466
- href="/foo"
467
- disabled={true}
468
- beforeNav={beforeNavMock}
469
- >
470
- Click me!
471
- </Button>
472
- <Switch>
473
- <Route path="/foo">
474
- <div id="foo">Hello, world!</div>
475
- </Route>
476
- </Switch>
477
- </div>
478
- </MemoryRouter>,
479
- );
480
-
481
- // Act
482
- const button = await screen.findByRole("button");
483
- await userEvent.click(button);
484
-
485
- // Assert
486
- expect(beforeNavMock).not.toHaveBeenCalled();
487
- });
488
-
489
- test("don't call safeWithNav when href and disabled are both set", async () => {
490
- // Arrange
491
- const safeWithNavMock = jest.fn();
492
- render(
493
- <MemoryRouter>
494
- <div>
495
- <Button
496
- href="/foo"
497
- disabled={true}
498
- safeWithNav={safeWithNavMock}
499
- >
500
- Click me!
501
- </Button>
502
- <Switch>
503
- <Route path="/foo">
504
- <div id="foo">Hello, world!</div>
505
- </Route>
506
- </Switch>
507
- </div>
508
- </MemoryRouter>,
509
- );
510
-
511
- // Act
512
- const button = await screen.findByRole("button");
513
- await userEvent.click(button);
514
-
515
- // Assert
516
- expect(safeWithNavMock).not.toHaveBeenCalled();
517
- });
518
-
519
- it("should set attribute on the underlying button", async () => {
520
- // Arrange
521
-
522
- // Act
523
- render(
524
- <Button id="foo" onClick={() => {}}>
525
- Click me!
526
- </Button>,
527
- );
528
-
529
- // Assert
530
- expect(await screen.findByRole("button")).toHaveAttribute("id", "foo");
531
- });
532
-
533
- it("should set attribute on the underlying link", async () => {
534
- // Arrange
535
-
536
- // Act
537
- render(
538
- <Button id="foo" href="/bar">
539
- Click me!
540
- </Button>,
541
- );
542
-
543
- // Assert
544
- expect(await screen.findByRole("button")).toHaveAttribute(
545
- "href",
546
- "/bar",
547
- );
548
- });
549
-
550
- describe("client-side navigation with keyboard", () => {
551
- it("should navigate on pressing the space key", async () => {
552
- // Arrange
553
- render(
554
- <MemoryRouter>
555
- <div>
556
- <Button href="/foo">Click me!</Button>
557
- <Switch>
558
- <Route path="/foo">
559
- <div id="foo">Hello, world!</div>
560
- </Route>
561
- </Switch>
562
- </div>
563
- </MemoryRouter>,
564
- );
565
-
566
- // Act
567
- const button = await screen.findByRole("button");
568
- await userEvent.type(button, "{space}");
569
-
570
- // Assert
571
- expect(
572
- await screen.findByText("Hello, world!"),
573
- ).toBeInTheDocument();
574
- });
575
-
576
- it("should navigate on pressing the enter key", async () => {
577
- // Arrange
578
- render(
579
- <MemoryRouter>
580
- <div>
581
- <Button href="/foo">Click me!</Button>
582
- <Switch>
583
- <Route path="/foo">
584
- <div id="foo">Hello, world!</div>
585
- </Route>
586
- </Switch>
587
- </div>
588
- </MemoryRouter>,
589
- );
590
-
591
- // Act
592
- const button = await screen.findByRole("button");
593
- await userEvent.type(button, "{enter}");
594
-
595
- // Assert
596
- expect(
597
- await screen.findByText("Hello, world!"),
598
- ).toBeInTheDocument();
599
- });
600
-
601
- test("beforeNav rejection blocks client-side navigation ", async () => {
602
- // Arrange
603
- render(
604
- <MemoryRouter>
605
- <div>
606
- <Button href="/foo" beforeNav={() => Promise.reject()}>
607
- Click me!
608
- </Button>
609
- <Switch>
610
- <Route path="/foo">
611
- <div id="foo">Hello, world!</div>
612
- </Route>
613
- </Switch>
614
- </div>
615
- </MemoryRouter>,
616
- );
617
-
618
- // Act
619
- const button = await screen.findByRole("button");
620
- await userEvent.type(button, "{enter}");
621
-
622
- // Assert
623
- expect(screen.queryByText("Hello, world!")).not.toBeInTheDocument();
624
- });
625
-
626
- test("beforeNav resolution results in client-side navigation", async () => {
627
- // Arrange
628
- render(
629
- <MemoryRouter>
630
- <div>
631
- <Button href="/foo" beforeNav={() => Promise.resolve()}>
632
- Click me!
633
- </Button>
634
- <Switch>
635
- <Route path="/foo">
636
- <div id="foo">Hello, world!</div>
637
- </Route>
638
- </Switch>
639
- </div>
640
- </MemoryRouter>,
641
- );
642
-
643
- // Act
644
- const button = await screen.findByRole("button");
645
- await userEvent.type(button, "{enter}");
646
-
647
- // Assert
648
- await waitFor(async () => {
649
- expect(
650
- await screen.findByText("Hello, world!"),
651
- ).toBeInTheDocument();
652
- });
653
- });
654
-
655
- test("safeWithNav with skipClientNav=true waits for promise resolution", async () => {
656
- // Arrange
657
- jest.spyOn(window.location, "assign");
658
- render(
659
- <MemoryRouter>
660
- <div>
661
- <Button
662
- href="/foo"
663
- safeWithNav={() => Promise.resolve()}
664
- skipClientNav={true}
665
- >
666
- Click me!
667
- </Button>
668
- <Switch>
669
- <Route path="/foo">
670
- <div id="foo">Hello, world!</div>
671
- </Route>
672
- </Switch>
673
- </div>
674
- </MemoryRouter>,
675
- );
676
-
677
- // Act
678
- const button = await screen.findByRole("button");
679
- await userEvent.type(button, "{enter}");
680
-
681
- // Assert
682
- expect(window.location.assign).toHaveBeenCalledWith("/foo");
683
- });
684
-
685
- test("safeWithNav with skipClientNav=true waits for promise rejection", async () => {
686
- // Arrange
687
- jest.spyOn(window.location, "assign");
688
- render(
689
- <MemoryRouter>
690
- <div>
691
- <Button
692
- href="/foo"
693
- safeWithNav={() => Promise.reject()}
694
- skipClientNav={true}
695
- >
696
- Click me!
697
- </Button>
698
- <Switch>
699
- <Route path="/foo">
700
- <div id="foo">Hello, world!</div>
701
- </Route>
702
- </Switch>
703
- </div>
704
- </MemoryRouter>,
705
- );
706
-
707
- // Act
708
- const button = await screen.findByRole("button");
709
- await userEvent.type(button, "{enter}");
710
-
711
- // Assert
712
- await waitFor(() => {
713
- expect(window.location.assign).toHaveBeenCalledWith("/foo");
714
- });
715
- });
716
-
717
- test("safeWithNav with skipClientNav=false calls safeWithNav but doesn't wait to navigate", async () => {
718
- // Arrange
719
- jest.spyOn(window.location, "assign");
720
- const safeWithNavMock = jest.fn();
721
- render(
722
- <MemoryRouter>
723
- <div>
724
- <Button
725
- href="/foo"
726
- safeWithNav={safeWithNavMock}
727
- skipClientNav={false}
728
- >
729
- Click me!
730
- </Button>
731
- <Switch>
732
- <Route path="/foo">
733
- <div id="foo">Hello, world!</div>
734
- </Route>
735
- </Switch>
736
- </div>
737
- </MemoryRouter>,
738
- );
739
-
740
- // Act
741
- const button = await screen.findByRole("button");
742
- await userEvent.type(button, "{enter}");
743
-
744
- // Assert
745
- await waitFor(() => {
746
- expect(safeWithNavMock).toHaveBeenCalled();
747
- });
748
- await waitFor(() => {
749
- expect(window.location.assign).toHaveBeenCalledWith("/foo");
750
- });
751
- });
752
- });
753
-
754
- describe("button focus", () => {
755
- test("primary button can have focus", async () => {
756
- // Arrange
757
- render(<Button testId={"button-focus-test"}>Label</Button>);
758
-
759
- // Act
760
- const button = await screen.findByTestId("button-focus-test");
761
- button.focus();
762
-
763
- // Assert
764
- expect(button).toHaveFocus();
765
- });
766
-
767
- test("primary button can have focus when disabled", async () => {
768
- // Arrange
769
- render(
770
- <Button disabled={true} testId={"button-focus-test"}>
771
- Label
772
- </Button>,
773
- );
774
-
775
- // Act
776
- const button = await screen.findByTestId("button-focus-test");
777
- button.focus();
778
-
779
- // Assert
780
- expect(button).toHaveFocus();
781
- });
782
-
783
- test("tertiary button can have focus when disabled", async () => {
784
- // Arrange
785
- render(
786
- <Button
787
- disabled={true}
788
- testId={"button-focus-test"}
789
- kind="tertiary"
790
- >
791
- Label
792
- </Button>,
793
- );
794
-
795
- // Act
796
- const button = await screen.findByTestId("button-focus-test");
797
- button.focus();
798
-
799
- // Assert
800
- expect(button).toHaveFocus();
801
- });
802
- });
803
-
804
- describe("type='submit'", () => {
805
- test("submit button within form via click", async () => {
806
- // Arrange
807
- const submitFnMock = jest.fn();
808
- render(
809
- <form onSubmit={submitFnMock}>
810
- <Button type="submit">Click me!</Button>
811
- </form>,
812
- );
813
-
814
- // Act
815
- const button = await screen.findByRole("button");
816
- await userEvent.click(button);
817
-
818
- // Assert
819
- expect(submitFnMock).toHaveBeenCalled();
820
- });
821
-
822
- test("submit button within form via keyboard", async () => {
823
- // Arrange
824
- const submitFnMock = jest.fn();
825
- render(
826
- <form onSubmit={submitFnMock}>
827
- <Button type="submit">Click me!</Button>
828
- </form>,
829
- );
830
-
831
- // Act
832
- const button = await screen.findByRole("button");
833
- await userEvent.type(button, "{enter}");
834
-
835
- // Assert
836
- expect(submitFnMock).toHaveBeenCalled();
837
- });
838
-
839
- test("submit button doesn't break if it's not in a form", async () => {
840
- // Arrange
841
- render(<Button type="submit">Click me!</Button>);
842
-
843
- // Act
844
- expect(async () => {
845
- // Assert
846
- await userEvent.click(await screen.findByRole("button"));
847
- }).not.toThrow();
848
- });
849
- });
850
- });