@khanacademy/wonder-blocks-clickable 4.2.7 → 4.2.8

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,1438 +0,0 @@
1
- /* eslint-disable testing-library/prefer-user-event */
2
- /* eslint-disable max-lines */
3
- import * as React from "react";
4
- import {render, screen, fireEvent, waitFor} from "@testing-library/react";
5
- import {MemoryRouter, Switch, Route} from "react-router-dom";
6
- import {userEvent} from "@testing-library/user-event";
7
-
8
- import getClickableBehavior from "../../util/get-clickable-behavior";
9
- import ClickableBehavior from "../clickable-behavior";
10
- import type {ClickableState} from "../clickable-behavior";
11
-
12
- const keyCodes = {
13
- tab: 9,
14
- enter: 13,
15
- space: 32,
16
- } as const;
17
-
18
- const labelForState = (state: ClickableState): string => {
19
- const labels: Array<any> = [];
20
- if (state.hovered) {
21
- labels.push("hovered");
22
- }
23
- if (state.focused) {
24
- labels.push("focused");
25
- }
26
- if (state.pressed) {
27
- labels.push("pressed");
28
- }
29
- return labels.join(" ");
30
- };
31
-
32
- describe("ClickableBehavior", () => {
33
- beforeEach(() => {
34
- // Note: window.location.assign and window.open need mock functions in
35
- // the testing environment
36
- // @ts-expect-error [FEI-5019] - TS2790 - The operand of a 'delete' operator must be optional.
37
- delete window.location;
38
- // @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.
39
- window.location = {assign: jest.fn()};
40
- window.open = jest.fn();
41
- });
42
-
43
- afterEach(() => {
44
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mockClear' does not exist on type '(url: string | URL) => void'.
45
- window.location.assign.mockClear();
46
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mockClear' does not exist on type '((url?: string | URL | undefined, target?: string | undefined, features?: string | undefined) => Window | null) & ((url?: string | URL | undefined, target?: string | undefined, features?: string | undefined) => Window | null)'.
47
- window.open.mockClear();
48
- });
49
-
50
- it("renders a label", async () => {
51
- const onClick = jest.fn();
52
- render(
53
- <ClickableBehavior
54
- disabled={false}
55
- onClick={(e: any) => onClick(e)}
56
- >
57
- {(state: any, childrenProps: any) => {
58
- return <button {...childrenProps}>Label</button>;
59
- }}
60
- </ClickableBehavior>,
61
- );
62
- expect(onClick).not.toHaveBeenCalled();
63
- await userEvent.click(await screen.findByRole("button"));
64
- expect(onClick).toHaveBeenCalled();
65
- });
66
-
67
- it("changes hovered state on mouse enter/leave", async () => {
68
- const onClick = jest.fn();
69
- render(
70
- <ClickableBehavior
71
- disabled={false}
72
- onClick={(e: any) => onClick(e)}
73
- >
74
- {(state: any, childrenProps: any) => {
75
- const label = labelForState(state);
76
- return <button {...childrenProps}>{label}</button>;
77
- }}
78
- </ClickableBehavior>,
79
- );
80
- const button = await screen.findByRole("button");
81
- expect(button).not.toHaveTextContent("hovered");
82
- await userEvent.hover(button);
83
- expect(button).toHaveTextContent("hovered");
84
- await userEvent.unhover(button);
85
- expect(button).not.toHaveTextContent("hovered");
86
- });
87
-
88
- it("changes hovered state on mouse enter while dragging", async () => {
89
- const onClick = jest.fn();
90
- render(
91
- <ClickableBehavior
92
- disabled={false}
93
- onClick={(e: any) => onClick(e)}
94
- >
95
- {(state: any, childrenProps: any) => {
96
- const label = labelForState(state);
97
- return <button {...childrenProps}>{label}</button>;
98
- }}
99
- </ClickableBehavior>,
100
- );
101
- const button = await screen.findByRole("button");
102
- expect(button).not.toHaveTextContent("hovered");
103
- expect(button).not.toHaveTextContent("pressed");
104
-
105
- fireEvent.mouseEnter(button, {buttons: 1});
106
- expect(button).not.toHaveTextContent("pressed");
107
- expect(button).toHaveTextContent("hovered");
108
- });
109
-
110
- it("changes pressed and hover states on mouse leave while dragging", async () => {
111
- const onClick = jest.fn();
112
- render(
113
- <ClickableBehavior
114
- disabled={false}
115
- onClick={(e: any) => onClick(e)}
116
- >
117
- {(state: any, childrenProps: any) => {
118
- const label = labelForState(state);
119
- return <button {...childrenProps}>{label}</button>;
120
- }}
121
- </ClickableBehavior>,
122
- );
123
- const button = await screen.findByRole("button");
124
- expect(button).not.toHaveTextContent("hovered");
125
- expect(button).not.toHaveTextContent("pressed");
126
-
127
- fireEvent.mouseDown(button);
128
- expect(button).toHaveTextContent("pressed");
129
-
130
- fireEvent.mouseLeave(button);
131
- expect(button).not.toHaveTextContent("hovered");
132
- expect(button).not.toHaveTextContent("pressed");
133
- });
134
-
135
- it("changes pressed state on mouse down/up", async () => {
136
- const onClick = jest.fn();
137
- render(
138
- <ClickableBehavior
139
- disabled={false}
140
- onClick={(e: any) => onClick(e)}
141
- >
142
- {(state: any, childrenProps: any) => {
143
- const label = labelForState(state);
144
- return <button {...childrenProps}>{label}</button>;
145
- }}
146
- </ClickableBehavior>,
147
- );
148
- const button = await screen.findByRole("button");
149
- expect(button).not.toHaveTextContent("pressed");
150
- fireEvent.mouseDown(button);
151
- expect(button).toHaveTextContent("pressed");
152
- fireEvent.mouseUp(button);
153
- expect(button).not.toHaveTextContent("pressed");
154
- });
155
-
156
- it("changes pressed state on touch start/end/cancel", async () => {
157
- const onClick = jest.fn();
158
- render(
159
- <ClickableBehavior
160
- disabled={false}
161
- onClick={(e: any) => onClick(e)}
162
- >
163
- {(state: any, childrenProps: any) => {
164
- const label = labelForState(state);
165
- return <button {...childrenProps}>{label}</button>;
166
- }}
167
- </ClickableBehavior>,
168
- );
169
- const button = await screen.findByRole("button");
170
- expect(button).not.toHaveTextContent("pressed");
171
- fireEvent.touchStart(button);
172
- expect(button).toHaveTextContent("pressed");
173
- fireEvent.touchEnd(button);
174
- expect(button).not.toHaveTextContent("pressed");
175
-
176
- expect(button).not.toHaveTextContent("pressed");
177
- fireEvent.touchStart(button);
178
- expect(button).toHaveTextContent("pressed");
179
- fireEvent.touchCancel(button);
180
- expect(button).not.toHaveTextContent("pressed");
181
- });
182
-
183
- it("enters focused state on key press after click", async () => {
184
- const onClick = jest.fn();
185
- render(
186
- <ClickableBehavior
187
- disabled={false}
188
- onClick={(e: any) => onClick(e)}
189
- >
190
- {(state: any, childrenProps: any) => {
191
- const label = labelForState(state);
192
- return <button {...childrenProps}>{label}</button>;
193
- }}
194
- </ClickableBehavior>,
195
- );
196
- const button = await screen.findByRole("button");
197
- expect(button).not.toHaveTextContent("focused");
198
- fireEvent.keyDown(button, {keyCode: keyCodes.space});
199
- fireEvent.keyUp(button, {keyCode: keyCodes.space});
200
- // NOTE(kevinb): await userEvent.click() fires other events that we don't want
201
- // affecting this test case.
202
- fireEvent.click(button);
203
- expect(button).toHaveTextContent("focused");
204
- });
205
-
206
- it("exits focused state on click after key press", async () => {
207
- const onClick = jest.fn();
208
-
209
- render(
210
- <ClickableBehavior
211
- disabled={false}
212
- onClick={(e: any) => onClick(e)}
213
- >
214
- {(state: any, childrenProps: any) => {
215
- const label = labelForState(state);
216
- return <button {...childrenProps}>{label}</button>;
217
- }}
218
- </ClickableBehavior>,
219
- );
220
- const button = await screen.findByRole("button");
221
- expect(button).not.toHaveTextContent("focused");
222
- fireEvent.keyDown(button, {keyCode: keyCodes.space});
223
- fireEvent.keyUp(button, {keyCode: keyCodes.space});
224
- // NOTE(kevinb): await userEvent.click() fires other events that we don't want
225
- // affecting this test case.
226
- fireEvent.click(button);
227
- expect(button).toHaveTextContent("focused");
228
- await userEvent.click(button);
229
- expect(button).not.toHaveTextContent("focused");
230
- });
231
-
232
- it("changes pressed state on space/enter key down/up if <button>", async () => {
233
- const onClick = jest.fn();
234
- render(
235
- <ClickableBehavior
236
- disabled={false}
237
- onClick={(e: any) => onClick(e)}
238
- >
239
- {(state: any, childrenProps: any) => {
240
- const label = labelForState(state);
241
- return <button {...childrenProps}>{label}</button>;
242
- }}
243
- </ClickableBehavior>,
244
- );
245
- const button = await screen.findByRole("button");
246
- expect(button).not.toHaveTextContent("pressed");
247
- fireEvent.keyDown(button, {keyCode: keyCodes.space});
248
- expect(button).toHaveTextContent("pressed");
249
- fireEvent.keyUp(button, {keyCode: keyCodes.space});
250
- expect(button).not.toHaveTextContent("pressed");
251
-
252
- fireEvent.keyDown(button, {keyCode: keyCodes.enter});
253
- expect(button).toHaveTextContent("pressed");
254
- fireEvent.keyUp(button, {keyCode: keyCodes.enter});
255
- expect(button).not.toHaveTextContent("pressed");
256
- });
257
-
258
- it("changes pressed state on only enter key down/up for a link", async () => {
259
- const onClick = jest.fn();
260
- // Use mount instead of a shallow render to trigger event defaults
261
- render(
262
- <ClickableBehavior
263
- disabled={false}
264
- onClick={(e: any) => onClick(e)}
265
- href="https://www.khanacademy.org"
266
- role="link"
267
- >
268
- {(state: any, childrenProps: any) => {
269
- const label = labelForState(state);
270
- return (
271
- <a
272
- href="https://www.khanacademy.org"
273
- {...childrenProps}
274
- >
275
- {label}
276
- </a>
277
- );
278
- }}
279
- </ClickableBehavior>,
280
- );
281
- const link = await screen.findByRole("link");
282
- expect(link).not.toHaveTextContent("pressed");
283
- fireEvent.keyDown(link, {keyCode: keyCodes.enter});
284
- expect(link).toHaveTextContent("pressed");
285
- fireEvent.keyUp(link, {keyCode: keyCodes.enter});
286
- expect(link).not.toHaveTextContent("pressed");
287
-
288
- fireEvent.keyDown(link, {keyCode: keyCodes.space});
289
- expect(link).not.toHaveTextContent("pressed");
290
- fireEvent.keyUp(link, {keyCode: keyCodes.space});
291
- expect(link).not.toHaveTextContent("pressed");
292
- });
293
-
294
- it("gains focused state on focus event", async () => {
295
- const onClick = jest.fn();
296
- render(
297
- <ClickableBehavior
298
- disabled={false}
299
- onClick={(e: any) => onClick(e)}
300
- >
301
- {(state: any, childrenProps: any) => {
302
- const label = labelForState(state);
303
- return <button {...childrenProps}>{label}</button>;
304
- }}
305
- </ClickableBehavior>,
306
- );
307
- const button = await screen.findByRole("button");
308
- fireEvent.focus(button);
309
- expect(button).toHaveTextContent("focused");
310
- });
311
-
312
- it("changes focused state on blur", async () => {
313
- const onClick = jest.fn();
314
- render(
315
- <ClickableBehavior
316
- disabled={false}
317
- onClick={(e: any) => onClick(e)}
318
- >
319
- {(state: any, childrenProps: any) => {
320
- const label = labelForState(state);
321
- return <button {...childrenProps}>{label}</button>;
322
- }}
323
- </ClickableBehavior>,
324
- );
325
- const button = await screen.findByRole("button");
326
- fireEvent.focus(button);
327
- fireEvent.blur(button);
328
- expect(button).not.toHaveTextContent("focused");
329
- });
330
-
331
- test("should not have a tabIndex if one is not passed in", async () => {
332
- // Arrange
333
- // Act
334
- render(
335
- <ClickableBehavior disabled={false} onClick={(e: any) => {}}>
336
- {(state: any, childrenProps: any) => {
337
- return (
338
- <button data-testid="test-button-1" {...childrenProps}>
339
- Label
340
- </button>
341
- );
342
- }}
343
- </ClickableBehavior>,
344
- );
345
-
346
- // Assert
347
- const button = await screen.findByTestId("test-button-1");
348
- expect(button).not.toHaveAttribute("tabIndex");
349
- });
350
-
351
- test("should have the tabIndex that is passed in", async () => {
352
- // Arrange
353
- // Act
354
- render(
355
- <ClickableBehavior
356
- disabled={false}
357
- onClick={(e: any) => {}}
358
- tabIndex={1}
359
- >
360
- {(state: any, childrenProps: any) => {
361
- return (
362
- <button data-testid="test-button-2" {...childrenProps}>
363
- Label
364
- </button>
365
- );
366
- }}
367
- </ClickableBehavior>,
368
- );
369
-
370
- // Assert
371
- const button = await screen.findByTestId("test-button-2");
372
- expect(button).toHaveAttribute("tabIndex", "1");
373
- });
374
-
375
- test("should have the tabIndex that is passed in even if disabled", async () => {
376
- // Arrange
377
- // Act
378
- render(
379
- <ClickableBehavior
380
- disabled={true}
381
- onClick={(e: any) => {}}
382
- tabIndex={1}
383
- >
384
- {(state: any, childrenProps: any) => {
385
- return (
386
- <button data-testid="test-button-3" {...childrenProps}>
387
- Label
388
- </button>
389
- );
390
- }}
391
- </ClickableBehavior>,
392
- );
393
-
394
- // Assert
395
- const button = await screen.findByTestId("test-button-3");
396
- expect(button).toHaveAttribute("tabIndex", "1");
397
- });
398
-
399
- test("should make non-interactive children keyboard focusable if tabIndex 0 is passed", async () => {
400
- // Arrange
401
- render(
402
- <ClickableBehavior
403
- disabled={false}
404
- onClick={(e: any) => {}}
405
- tabIndex={1}
406
- >
407
- {(state: any, childrenProps: any) => {
408
- return (
409
- <div data-testid="test-div-1" {...childrenProps}>
410
- Label
411
- </div>
412
- );
413
- }}
414
- </ClickableBehavior>,
415
- );
416
-
417
- // Act
418
- const button = await screen.findByTestId("test-div-1");
419
- await userEvent.tab();
420
-
421
- // Assert
422
- expect(button).toHaveFocus();
423
- });
424
-
425
- it("does not change state if disabled", async () => {
426
- const onClick = jest.fn();
427
- render(
428
- <ClickableBehavior disabled={true} onClick={(e: any) => onClick(e)}>
429
- {(state: any, childrenProps: any) => {
430
- const label = labelForState(state);
431
- return <button {...childrenProps}>{label}</button>;
432
- }}
433
- </ClickableBehavior>,
434
- );
435
-
436
- const button = await screen.findByRole("button");
437
- expect(onClick).not.toHaveBeenCalled();
438
- fireEvent.click(button);
439
- expect(onClick).not.toHaveBeenCalled();
440
-
441
- expect(button).not.toHaveTextContent("hovered");
442
- fireEvent.mouseEnter(button);
443
- expect(button).not.toHaveTextContent("hovered");
444
- fireEvent.mouseLeave(button);
445
- expect(button).not.toHaveTextContent("hovered");
446
-
447
- expect(button).not.toHaveTextContent("pressed");
448
- fireEvent.mouseDown(button);
449
- expect(button).not.toHaveTextContent("pressed");
450
- fireEvent.mouseUp(button);
451
- expect(button).not.toHaveTextContent("pressed");
452
-
453
- expect(button).not.toHaveTextContent("pressed");
454
- fireEvent.touchStart(button);
455
- expect(button).not.toHaveTextContent("pressed");
456
- fireEvent.touchEnd(button);
457
- expect(button).not.toHaveTextContent("pressed");
458
-
459
- fireEvent.touchStart(button);
460
- fireEvent.touchCancel(button);
461
- expect(button).not.toHaveTextContent("pressed");
462
-
463
- expect(button).not.toHaveTextContent("focused");
464
- fireEvent.keyUp(button, {
465
- keyCode: keyCodes.tab,
466
- });
467
- expect(button).not.toHaveTextContent("focused");
468
- fireEvent.keyDown(button, {keyCode: keyCodes.tab});
469
- expect(button).not.toHaveTextContent("focused");
470
-
471
- expect(button).not.toHaveTextContent("pressed");
472
- fireEvent.keyDown(button, {keyCode: keyCodes.space});
473
- expect(button).not.toHaveTextContent("pressed");
474
- fireEvent.keyUp(button, {keyCode: keyCodes.space});
475
- expect(button).not.toHaveTextContent("pressed");
476
-
477
- fireEvent.keyDown(button, {keyCode: keyCodes.space});
478
- fireEvent.blur(button);
479
- expect(button).not.toHaveTextContent("pressed");
480
-
481
- fireEvent.focus(button);
482
- expect(button).toHaveTextContent("focused");
483
-
484
- render(
485
- <ClickableBehavior
486
- disabled={true}
487
- href="https://www.khanacademy.org"
488
- >
489
- {(state: any, childrenProps: any) => {
490
- return (
491
- <a
492
- href="https://www.khanacademy.org"
493
- {...childrenProps}
494
- >
495
- Label
496
- </a>
497
- );
498
- }}
499
- </ClickableBehavior>,
500
- );
501
-
502
- const anchor = await screen.findByRole("link");
503
- expect(anchor).not.toHaveTextContent("pressed");
504
- fireEvent.keyDown(anchor, {keyCode: keyCodes.enter});
505
- expect(anchor).not.toHaveTextContent("pressed");
506
- fireEvent.keyUp(anchor, {keyCode: keyCodes.enter});
507
- expect(anchor).not.toHaveTextContent("pressed");
508
- });
509
-
510
- it("has onClick triggered just once per click by various means", async () => {
511
- const onClick = jest.fn();
512
- render(
513
- <ClickableBehavior
514
- disabled={false}
515
- onClick={(e: any) => onClick(e)}
516
- >
517
- {(state: any, childrenProps: any) => {
518
- return <button {...childrenProps}>Label</button>;
519
- }}
520
- </ClickableBehavior>,
521
- );
522
- const button = await screen.findByRole("button");
523
- expect(onClick).not.toHaveBeenCalled();
524
-
525
- await userEvent.click(button);
526
- expect(onClick).toHaveBeenCalledTimes(1);
527
-
528
- fireEvent.keyDown(button, {keyCode: keyCodes.space});
529
- fireEvent.keyUp(button, {keyCode: keyCodes.space});
530
- expect(onClick).toHaveBeenCalledTimes(2);
531
-
532
- fireEvent.keyDown(button, {keyCode: keyCodes.enter});
533
- fireEvent.keyUp(button, {keyCode: keyCodes.enter});
534
- expect(onClick).toHaveBeenCalledTimes(3);
535
-
536
- fireEvent.touchStart(button, {keyCode: keyCodes.space});
537
- fireEvent.touchEnd(button, {keyCode: keyCodes.space});
538
- fireEvent.click(button);
539
- expect(onClick).toHaveBeenCalledTimes(4);
540
- });
541
-
542
- it("resets state when set to disabled", async () => {
543
- const onClick = jest.fn();
544
- const {rerender} = render(
545
- <ClickableBehavior
546
- disabled={false}
547
- onClick={(e: any) => onClick(e)}
548
- >
549
- {(state: any, childrenProps: any) => {
550
- const label = labelForState(state);
551
- return <button {...childrenProps}>{label}</button>;
552
- }}
553
- </ClickableBehavior>,
554
- );
555
- const button = await screen.findByRole("button");
556
- await userEvent.tab(); // focus
557
- await userEvent.hover(button);
558
-
559
- rerender(
560
- <ClickableBehavior disabled={true} onClick={(e: any) => onClick(e)}>
561
- {(state: any, childrenProps: any) => {
562
- const label = labelForState(state);
563
- return <button {...childrenProps}>{label}</button>;
564
- }}
565
- </ClickableBehavior>,
566
- );
567
-
568
- expect(button).not.toHaveTextContent("pressed");
569
- expect(button).not.toHaveTextContent("hovered");
570
-
571
- // The button remains focused even after it's been disabled
572
- expect(button).toHaveTextContent("focused");
573
- });
574
-
575
- describe("full page load navigation", () => {
576
- it("both navigates and calls onClick for an anchor link", async () => {
577
- const onClick = jest.fn();
578
- // Use mount instead of a shallow render to trigger event defaults
579
- render(
580
- <ClickableBehavior
581
- href="https://khanacademy.org/"
582
- onClick={(e: any) => onClick(e)}
583
- role="link"
584
- >
585
- {(state: any, childrenProps: any) => {
586
- // The base element here doesn't matter in this testing
587
- // environment, but the simulated events in the test are in
588
- // line with what browsers do for this element.
589
- return (
590
- <a
591
- href="https://khanacademy.org/"
592
- {...childrenProps}
593
- >
594
- Label
595
- </a>
596
- );
597
- }}
598
- </ClickableBehavior>,
599
- );
600
- const link = await screen.findByRole("link");
601
-
602
- // Space press should not trigger the onClick
603
- fireEvent.keyDown(link, {keyCode: keyCodes.space});
604
- fireEvent.keyUp(link, {keyCode: keyCodes.space});
605
- expect(onClick).toHaveBeenCalledTimes(0);
606
-
607
- // Navigation didn't happen with space
608
- expect(window.location.assign).toHaveBeenCalledTimes(0);
609
-
610
- // Enter press should trigger the onClick after keyup
611
- fireEvent.keyDown(link, {keyCode: keyCodes.enter});
612
- expect(onClick).toHaveBeenCalledTimes(0);
613
-
614
- // Navigation doesn't happen until after enter is released
615
- expect(window.location.assign).toHaveBeenCalledTimes(0);
616
-
617
- fireEvent.keyUp(link, {keyCode: keyCodes.enter});
618
- expect(onClick).toHaveBeenCalledTimes(1);
619
-
620
- // Navigation happened after enter click
621
- expect(window.location.assign).toHaveBeenCalledTimes(1);
622
- });
623
-
624
- it("waits for safeWithNav to resolve before navigation", async () => {
625
- // Arrange
626
- render(
627
- <ClickableBehavior
628
- href="https://khanacademy.org/"
629
- safeWithNav={() => Promise.resolve()}
630
- role="link"
631
- >
632
- {(state: any, childrenProps: any) => {
633
- // The base element here doesn't matter in this testing
634
- // environment, but the simulated events in the test are in
635
- // line with what browsers do for this element.
636
- return (
637
- <a
638
- href="https://khanacademy.org/"
639
- {...childrenProps}
640
- >
641
- Label
642
- </a>
643
- );
644
- }}
645
- </ClickableBehavior>,
646
- );
647
-
648
- // Act
649
- const link = await screen.findByRole("link");
650
- await userEvent.click(link);
651
-
652
- // Assert
653
- await waitFor(() => {
654
- expect(window.location.assign).toHaveBeenCalledTimes(1);
655
- });
656
- });
657
-
658
- it("should show waiting UI before safeWithNav resolves", async () => {
659
- // Arrange
660
- render(
661
- <ClickableBehavior
662
- href="https://khanacademy.org/"
663
- safeWithNav={() => Promise.resolve()}
664
- role="link"
665
- >
666
- {(state: any, childrenProps: any) => {
667
- // The base element here doesn't matter in this testing
668
- // environment, but the simulated events in the test are in
669
- // line with what browsers do for this element.
670
- return (
671
- <a
672
- href="https://khanacademy.org/"
673
- {...childrenProps}
674
- >
675
- {state.waiting ? "waiting" : "Label"}
676
- </a>
677
- );
678
- }}
679
- </ClickableBehavior>,
680
- );
681
-
682
- // Act
683
- const link = await screen.findByRole("link");
684
- await userEvent.click(link);
685
-
686
- // Assert
687
- expect(link).toHaveTextContent("waiting");
688
- });
689
-
690
- it("If onClick calls e.preventDefault() then we won't navigate", async () => {
691
- // Arrange
692
- render(
693
- <ClickableBehavior
694
- href="/foo"
695
- onClick={(e: any) => e.preventDefault()}
696
- role="checkbox"
697
- >
698
- {(state: any, childrenProps: any) => {
699
- // The base element here doesn't matter in this testing
700
- // environment, but the simulated events in the test are in
701
- // line with what browsers do for this element.
702
- return <button {...childrenProps}>label</button>;
703
- }}
704
- </ClickableBehavior>,
705
- );
706
-
707
- // Act
708
- const button = await screen.findByRole("button");
709
- await userEvent.click(button);
710
-
711
- // Assert
712
- expect(window.location.assign).not.toHaveBeenCalled();
713
- });
714
- });
715
-
716
- it("calls onClick correctly for a component that doesn't respond to enter", async () => {
717
- const onClick = jest.fn();
718
- // Use mount instead of a shallow render to trigger event defaults
719
- render(
720
- // triggerOnEnter may be false for some elements e.g. checkboxes
721
- <ClickableBehavior onClick={(e: any) => onClick(e)} role="checkbox">
722
- {(state: any, childrenProps: any) => {
723
- // The base element here doesn't matter in this testing
724
- // environment, but the simulated events in the test are in
725
- // line with what browsers do for this element.
726
- return <input type="checkbox" {...childrenProps} />;
727
- }}
728
- </ClickableBehavior>,
729
- );
730
-
731
- // Enter press should not do anything
732
- const checkbox = await screen.findByRole("checkbox");
733
- fireEvent.keyDown(checkbox, {keyCode: keyCodes.enter});
734
- expect(onClick).toHaveBeenCalledTimes(0);
735
- fireEvent.keyUp(checkbox, {keyCode: keyCodes.enter});
736
- expect(onClick).toHaveBeenCalledTimes(0);
737
-
738
- // Space press should trigger the onClick
739
- fireEvent.keyDown(checkbox, {keyCode: keyCodes.space});
740
- fireEvent.keyUp(checkbox, {keyCode: keyCodes.space});
741
- expect(onClick).toHaveBeenCalledTimes(1);
742
- });
743
-
744
- it("calls onClick for a button component on both enter/space", async () => {
745
- const onClick = jest.fn();
746
- // Use mount instead of a shallow render to trigger event defaults
747
- render(
748
- <ClickableBehavior onClick={(e: any) => onClick(e)}>
749
- {(state: any, childrenProps: any) => {
750
- // The base element here doesn't matter in this testing
751
- // environment, but the simulated events in the test are in
752
- // line with what browsers do for this element.
753
- return <button {...childrenProps}>Label</button>;
754
- }}
755
- </ClickableBehavior>,
756
- );
757
-
758
- // Enter press
759
- const button = await screen.findByRole("button");
760
- fireEvent.keyDown(button, {keyCode: keyCodes.enter});
761
- expect(onClick).toHaveBeenCalledTimes(0);
762
- fireEvent.keyUp(button, {keyCode: keyCodes.enter});
763
- expect(onClick).toHaveBeenCalledTimes(1);
764
-
765
- // Space press
766
- fireEvent.keyDown(button, {keyCode: keyCodes.space});
767
- expect(onClick).toHaveBeenCalledTimes(1);
768
- fireEvent.keyUp(button, {keyCode: keyCodes.space});
769
- expect(onClick).toHaveBeenCalledTimes(2);
770
- });
771
-
772
- // This tests the case where we attach the childrenProps to an element that is
773
- // not canonically clickable (like a div). The browser doesn't naturally
774
- // trigger keyboard click events for such an element.
775
- it("calls onClick listener on space/enter with a non-usually clickable element", async () => {
776
- const onClick = jest.fn();
777
- // Use mount instead of a shallow render to trigger event defaults
778
- const {container} = render(
779
- <ClickableBehavior onClick={(e: any) => onClick(e)}>
780
- {(state: any, childrenProps: any) => {
781
- // The base element here doesn't matter in this testing
782
- // environment, but the simulated events in the test are in
783
- // line with what browsers do for this element.
784
- return <div {...childrenProps}>Label</div>;
785
- }}
786
- </ClickableBehavior>,
787
- );
788
-
789
- let expectedNumberTimesCalled = 0;
790
- // eslint-disable-next-line testing-library/no-node-access, testing-library/no-container
791
- const clickableDiv = container.querySelector("div");
792
- if (!clickableDiv) {
793
- throw new Error("couldn't find clickable div");
794
- }
795
-
796
- // Enter press on a div
797
- fireEvent.keyDown(clickableDiv, {keyCode: keyCodes.enter});
798
- expect(onClick).toHaveBeenCalledTimes(expectedNumberTimesCalled);
799
- fireEvent.keyUp(clickableDiv, {
800
- keyCode: keyCodes.enter,
801
- });
802
- expectedNumberTimesCalled += 1;
803
- expect(onClick).toHaveBeenCalledTimes(expectedNumberTimesCalled);
804
-
805
- // Simulate a mouse click.
806
- await userEvent.click(clickableDiv);
807
- expectedNumberTimesCalled += 1;
808
- expect(onClick).toHaveBeenCalledTimes(expectedNumberTimesCalled);
809
-
810
- // Space press on a div
811
- fireEvent.keyDown(clickableDiv, {keyCode: keyCodes.space});
812
- expect(onClick).toHaveBeenCalledTimes(expectedNumberTimesCalled);
813
- fireEvent.keyUp(clickableDiv, {keyCode: keyCodes.space});
814
- expectedNumberTimesCalled += 1;
815
- expect(onClick).toHaveBeenCalledTimes(expectedNumberTimesCalled);
816
-
817
- // Simulate another mouse click.
818
- await userEvent.click(clickableDiv);
819
- expectedNumberTimesCalled += 1;
820
- expect(onClick).toHaveBeenCalledTimes(expectedNumberTimesCalled);
821
- });
822
-
823
- // The following two tests involve click behavior when dragging.
824
- // Here are some notable related actions that cannot be tested using
825
- // existing jest/RTL events since these click types are handled
826
- // by browsers but aren't registered as clicks by RTL/jest:
827
- // 1. Mousedown in the button, drag within the button, and mouseup
828
- // in the button (mouse doesn't leave the button at any point).
829
- // This should result in a successful click.
830
- // 2. Mouse down in the button, drag out of the button (don't let go),
831
- // drag back into the button, and mouseup inside the button.
832
- // This should result in a successful click.
833
- it("does not call onClick on mouseup when the mouse presses inside and drags away", async () => {
834
- const onClick = jest.fn();
835
- render(
836
- <ClickableBehavior
837
- disabled={false}
838
- onClick={(e: any) => onClick(e)}
839
- >
840
- {(state: any, childrenProps: any) => {
841
- return <button {...childrenProps}>Label</button>;
842
- }}
843
- </ClickableBehavior>,
844
- );
845
-
846
- const button = await screen.findByRole("button");
847
- fireEvent.mouseDown(button);
848
- fireEvent.mouseLeave(button);
849
- fireEvent.mouseUp(button);
850
- expect(onClick).toHaveBeenCalledTimes(0);
851
- });
852
-
853
- it("does not call onClick on mouseup when the mouse presses outside and drags in", async () => {
854
- const onClick = jest.fn();
855
- render(
856
- <ClickableBehavior
857
- disabled={false}
858
- onClick={(e: any) => onClick(e)}
859
- >
860
- {(state: any, childrenProps: any) => {
861
- return <button {...childrenProps}>Label</button>;
862
- }}
863
- </ClickableBehavior>,
864
- );
865
-
866
- const button = await screen.findByRole("button");
867
- fireEvent.mouseEnter(button, {buttons: 1});
868
- fireEvent.mouseUp(button);
869
- expect(onClick).toHaveBeenCalledTimes(0);
870
- });
871
-
872
- it("doesn't trigger enter key when browser doesn't stop the click", async () => {
873
- const onClick = jest.fn();
874
- // Use mount instead of a shallow render to trigger event defaults
875
- render(
876
- <ClickableBehavior onClick={(e: any) => onClick(e)} role="checkbox">
877
- {(state: any, childrenProps: any) => {
878
- // The base element here doesn't matter in this testing
879
- // environment, but the simulated events in the test are in
880
- // line with what browsers do for this element.
881
- return <input type="checkbox" {...childrenProps} />;
882
- }}
883
- </ClickableBehavior>,
884
- );
885
-
886
- const checkbox = await screen.findByRole("checkbox");
887
- // Enter press should not do anything
888
- fireEvent.keyDown(checkbox, {keyCode: keyCodes.enter});
889
- // This element still wants to have a click on enter press
890
- fireEvent.click(checkbox);
891
- fireEvent.keyUp(checkbox, {keyCode: keyCodes.enter});
892
- expect(onClick).toHaveBeenCalledTimes(0);
893
- });
894
-
895
- describe("client-side navgiation", () => {
896
- const ClickableBehaviorWithRouter = getClickableBehavior(
897
- "/foo",
898
- false,
899
- true, // router
900
- );
901
-
902
- it("handles client-side navigation when there's a router context", async () => {
903
- // Arrange
904
- render(
905
- <MemoryRouter>
906
- <div>
907
- <ClickableBehaviorWithRouter
908
- href="/foo"
909
- onClick={(e: any) => {}}
910
- role="checkbox"
911
- >
912
- {(state: any, childrenProps: any) => {
913
- // The base element here doesn't matter in this testing
914
- // environment, but the simulated events in the test are in
915
- // line with what browsers do for this element.
916
- return (
917
- <button {...childrenProps}>label</button>
918
- );
919
- }}
920
- </ClickableBehaviorWithRouter>
921
- <Switch>
922
- <Route path="/foo">
923
- <div>Hello, world!</div>
924
- </Route>
925
- </Switch>
926
- </div>
927
- </MemoryRouter>,
928
- );
929
-
930
- // Act
931
- await userEvent.click(await screen.findByRole("button"));
932
-
933
- // Assert
934
- expect(
935
- await screen.findByText("Hello, world!"),
936
- ).toBeInTheDocument();
937
- });
938
-
939
- describe("beforeNav", () => {
940
- it("waits for beforeNav to resolve before client-side navigating", async () => {
941
- // Arrange
942
- render(
943
- <MemoryRouter>
944
- <div>
945
- <ClickableBehaviorWithRouter
946
- href="/foo"
947
- onClick={(e: any) => {}}
948
- role="checkbox"
949
- beforeNav={() => Promise.resolve()}
950
- >
951
- {(state: any, childrenProps: any) => {
952
- // The base element here doesn't matter in this testing
953
- // environment, but the simulated events in the test are in
954
- // line with what browsers do for this element.
955
- return (
956
- <button {...childrenProps}>
957
- {state.waiting
958
- ? "waiting"
959
- : "label"}
960
- </button>
961
- );
962
- }}
963
- </ClickableBehaviorWithRouter>
964
- <Switch>
965
- <Route path="/foo">
966
- <div>Hello, world!</div>
967
- </Route>
968
- </Switch>
969
- </div>
970
- </MemoryRouter>,
971
- );
972
-
973
- // Act
974
- await userEvent.click(await screen.findByRole("button"));
975
-
976
- // Assert
977
- await waitFor(async () => {
978
- expect(
979
- await screen.findByText("Hello, world!"),
980
- ).toBeInTheDocument();
981
- });
982
- });
983
-
984
- // NOTE(john): This no longer works after upgrading to user-event v14.
985
- // The wait state is resolved before we can confirm that it's rendered.
986
- it.skip("shows waiting state before navigating", async () => {
987
- // Arrange
988
- render(
989
- <MemoryRouter>
990
- <div>
991
- <ClickableBehaviorWithRouter
992
- href="/foo"
993
- onClick={(e: any) => {}}
994
- role="checkbox"
995
- beforeNav={() => Promise.resolve()}
996
- >
997
- {(state: any, childrenProps: any) => {
998
- // The base element here doesn't matter in this testing
999
- // environment, but the simulated events in the test are in
1000
- // line with what browsers do for this element.
1001
- return (
1002
- <button {...childrenProps}>
1003
- {state.waiting
1004
- ? "waiting"
1005
- : "label"}
1006
- </button>
1007
- );
1008
- }}
1009
- </ClickableBehaviorWithRouter>
1010
- <Switch>
1011
- <Route path="/foo">
1012
- <div>Hello, world!</div>
1013
- </Route>
1014
- </Switch>
1015
- </div>
1016
- </MemoryRouter>,
1017
- );
1018
-
1019
- // Act
1020
- await userEvent.click(await screen.findByRole("button"));
1021
-
1022
- // Assert
1023
- expect(await screen.findByText("waiting")).toBeInTheDocument();
1024
- });
1025
-
1026
- it("does not navigate if beforeNav rejects", async () => {
1027
- // Arrange
1028
- render(
1029
- <MemoryRouter>
1030
- <div>
1031
- <ClickableBehaviorWithRouter
1032
- href="/foo"
1033
- onClick={(e: any) => {}}
1034
- role="checkbox"
1035
- beforeNav={() => Promise.reject()}
1036
- >
1037
- {(state: any, childrenProps: any) => {
1038
- // The base element here doesn't matter in this testing
1039
- // environment, but the simulated events in the test are in
1040
- // line with what browsers do for this element.
1041
- return (
1042
- <button {...childrenProps}>
1043
- label
1044
- </button>
1045
- );
1046
- }}
1047
- </ClickableBehaviorWithRouter>
1048
- <Switch>
1049
- <Route path="/foo">
1050
- <div>Hello, world!</div>
1051
- </Route>
1052
- </Switch>
1053
- </div>
1054
- </MemoryRouter>,
1055
- );
1056
-
1057
- // Act
1058
- await userEvent.click(await screen.findByRole("button"));
1059
-
1060
- // Assert
1061
- expect(
1062
- screen.queryByText("Hello, world!"),
1063
- ).not.toBeInTheDocument();
1064
- });
1065
-
1066
- it("calls safeWithNav if provided if beforeNav resolves", async () => {
1067
- // Arrange
1068
- const safeWithNavMock = jest.fn();
1069
- render(
1070
- <MemoryRouter>
1071
- <div>
1072
- <ClickableBehaviorWithRouter
1073
- href="/foo"
1074
- onClick={(e: any) => {}}
1075
- role="checkbox"
1076
- beforeNav={() => Promise.resolve()}
1077
- safeWithNav={safeWithNavMock}
1078
- >
1079
- {(state: any, childrenProps: any) => {
1080
- // The base element here doesn't matter in this testing
1081
- // environment, but the simulated events in the test are in
1082
- // line with what browsers do for this element.
1083
- return (
1084
- <button {...childrenProps}>
1085
- {state.waiting
1086
- ? "waiting"
1087
- : "label"}
1088
- </button>
1089
- );
1090
- }}
1091
- </ClickableBehaviorWithRouter>
1092
- <Switch>
1093
- <Route path="/foo">
1094
- <div>Hello, world!</div>
1095
- </Route>
1096
- </Switch>
1097
- </div>
1098
- </MemoryRouter>,
1099
- );
1100
-
1101
- // Act
1102
- await userEvent.click(await screen.findByRole("button"));
1103
-
1104
- // Assert
1105
- await waitFor(() => {
1106
- expect(safeWithNavMock).toHaveBeenCalled();
1107
- });
1108
- });
1109
- });
1110
-
1111
- it("doesn't wait for safeWithNav to resolve before client-side navigating", async () => {
1112
- // Arrange
1113
- render(
1114
- <MemoryRouter>
1115
- <div>
1116
- <ClickableBehaviorWithRouter
1117
- href="/foo"
1118
- onClick={(e: any) => {}}
1119
- role="checkbox"
1120
- safeWithNav={() => Promise.resolve()}
1121
- >
1122
- {(state: any, childrenProps: any) => {
1123
- // The base element here doesn't matter in this testing
1124
- // environment, but the simulated events in the test are in
1125
- // line with what browsers do for this element.
1126
- return (
1127
- <button {...childrenProps}>label</button>
1128
- );
1129
- }}
1130
- </ClickableBehaviorWithRouter>
1131
- <Switch>
1132
- <Route path="/foo">
1133
- <div>Hello, world!</div>
1134
- </Route>
1135
- </Switch>
1136
- </div>
1137
- </MemoryRouter>,
1138
- );
1139
-
1140
- // Act
1141
- await userEvent.click(await screen.findByRole("button"));
1142
-
1143
- // Assert
1144
- expect(
1145
- await screen.findByText("Hello, world!"),
1146
- ).toBeInTheDocument();
1147
- });
1148
-
1149
- it("If onClick calls e.preventDefault() then we won't navigate", async () => {
1150
- // Arrange
1151
- render(
1152
- <MemoryRouter>
1153
- <div>
1154
- <ClickableBehaviorWithRouter
1155
- href="/foo"
1156
- onClick={(e: any) => e.preventDefault()}
1157
- role="checkbox"
1158
- >
1159
- {(state: any, childrenProps: any) => {
1160
- // The base element here doesn't matter in this testing
1161
- // environment, but the simulated events in the test are in
1162
- // line with what browsers do for this element.
1163
- return (
1164
- <button {...childrenProps}>label</button>
1165
- );
1166
- }}
1167
- </ClickableBehaviorWithRouter>
1168
- <Switch>
1169
- <Route path="/foo">
1170
- <div>Hello, world!</div>
1171
- </Route>
1172
- </Switch>
1173
- </div>
1174
- </MemoryRouter>,
1175
- );
1176
-
1177
- // Act
1178
- await userEvent.click(await screen.findByRole("button"));
1179
-
1180
- // Assert
1181
- expect(screen.queryByText("Hello, world!")).not.toBeInTheDocument();
1182
- });
1183
- });
1184
-
1185
- describe("target='_blank'", () => {
1186
- it("opens a new tab", async () => {
1187
- // Arrange
1188
- render(
1189
- <ClickableBehavior
1190
- disabled={false}
1191
- href="https://www.khanacademy.org"
1192
- role="link"
1193
- target="_blank"
1194
- >
1195
- {(state: any, childrenProps: any) => {
1196
- return (
1197
- <a
1198
- href="https://www.khanacademy.org"
1199
- {...childrenProps}
1200
- >
1201
- Label
1202
- </a>
1203
- );
1204
- }}
1205
- </ClickableBehavior>,
1206
- );
1207
-
1208
- // Act
1209
- await userEvent.click(await screen.findByRole("link"));
1210
-
1211
- // Assert
1212
- expect(window.open).toHaveBeenCalledWith(
1213
- "https://www.khanacademy.org",
1214
- "_blank",
1215
- );
1216
- });
1217
-
1218
- it("opens a new tab when using 'safeWithNav'", async () => {
1219
- // Arrange
1220
- // @ts-expect-error [FEI-5019] - TS2554 - Expected 1 arguments, but got 0.
1221
- const safeWithNavMock = jest.fn().mockResolvedValue();
1222
- render(
1223
- <ClickableBehavior
1224
- disabled={false}
1225
- href="https://www.khanacademy.org"
1226
- role="link"
1227
- target="_blank"
1228
- safeWithNav={safeWithNavMock}
1229
- >
1230
- {(state: any, childrenProps: any) => {
1231
- return (
1232
- <a
1233
- href="https://www.khanacademy.org"
1234
- {...childrenProps}
1235
- >
1236
- Label
1237
- </a>
1238
- );
1239
- }}
1240
- </ClickableBehavior>,
1241
- );
1242
-
1243
- // Act
1244
- await userEvent.click(await screen.findByRole("link"));
1245
-
1246
- // Assert
1247
- expect(window.open).toHaveBeenCalledWith(
1248
- "https://www.khanacademy.org",
1249
- "_blank",
1250
- );
1251
- });
1252
-
1253
- it("calls 'safeWithNav'", async () => {
1254
- // Arrange
1255
- // @ts-expect-error [FEI-5019] - TS2554 - Expected 1 arguments, but got 0.
1256
- const safeWithNavMock = jest.fn().mockResolvedValue();
1257
- render(
1258
- <ClickableBehavior
1259
- disabled={false}
1260
- href="https://www.khanacademy.org"
1261
- role="link"
1262
- target="_blank"
1263
- safeWithNav={safeWithNavMock}
1264
- >
1265
- {(state: any, childrenProps: any) => {
1266
- return (
1267
- <a
1268
- href="https://www.khanacademy.org"
1269
- {...childrenProps}
1270
- >
1271
- Label
1272
- </a>
1273
- );
1274
- }}
1275
- </ClickableBehavior>,
1276
- );
1277
-
1278
- // Act
1279
- await userEvent.click(await screen.findByRole("link"));
1280
-
1281
- // Assert
1282
- expect(safeWithNavMock).toHaveBeenCalled();
1283
- });
1284
-
1285
- it("opens a new tab when inside a router", async () => {
1286
- // Arrange
1287
- render(
1288
- <MemoryRouter initialEntries={["/"]}>
1289
- <ClickableBehavior
1290
- disabled={false}
1291
- href="https://www.khanacademy.org"
1292
- role="link"
1293
- target="_blank"
1294
- >
1295
- {(state: any, childrenProps: any) => {
1296
- return (
1297
- <a
1298
- href="https://www.khanacademy.org"
1299
- {...childrenProps}
1300
- >
1301
- Label
1302
- </a>
1303
- );
1304
- }}
1305
- </ClickableBehavior>
1306
- </MemoryRouter>,
1307
- );
1308
-
1309
- // Act
1310
- await userEvent.click(await screen.findByRole("link"));
1311
-
1312
- // Assert
1313
- expect(window.open).toHaveBeenCalledWith(
1314
- "https://www.khanacademy.org",
1315
- "_blank",
1316
- );
1317
- });
1318
-
1319
- it("opens a new tab when using 'safeWithNav' inside a router", async () => {
1320
- // Arrange
1321
- // @ts-expect-error [FEI-5019] - TS2554 - Expected 1 arguments, but got 0.
1322
- const safeWithNavMock = jest.fn().mockResolvedValue();
1323
- render(
1324
- <MemoryRouter initialEntries={["/"]}>
1325
- <ClickableBehavior
1326
- disabled={false}
1327
- href="https://www.khanacademy.org"
1328
- role="link"
1329
- target="_blank"
1330
- safeWithNav={safeWithNavMock}
1331
- >
1332
- {(state: any, childrenProps: any) => {
1333
- return (
1334
- <a
1335
- href="https://www.khanacademy.org"
1336
- {...childrenProps}
1337
- >
1338
- Label
1339
- </a>
1340
- );
1341
- }}
1342
- </ClickableBehavior>
1343
- </MemoryRouter>,
1344
- );
1345
-
1346
- // Act
1347
- await userEvent.click(await screen.findByRole("link"));
1348
-
1349
- // Assert
1350
- expect(window.open).toHaveBeenCalledWith(
1351
- "https://www.khanacademy.org",
1352
- "_blank",
1353
- );
1354
- });
1355
-
1356
- it("calls 'safeWithNav' inside a router", async () => {
1357
- // Arrange
1358
- // @ts-expect-error [FEI-5019] - TS2554 - Expected 1 arguments, but got 0.
1359
- const safeWithNavMock = jest.fn().mockResolvedValue();
1360
- render(
1361
- <MemoryRouter initialEntries={["/"]}>
1362
- <ClickableBehavior
1363
- disabled={false}
1364
- href="https://www.khanacademy.org"
1365
- role="link"
1366
- target="_blank"
1367
- safeWithNav={safeWithNavMock}
1368
- >
1369
- {(state: any, childrenProps: any) => {
1370
- return (
1371
- <a
1372
- href="https://www.khanacademy.org"
1373
- {...childrenProps}
1374
- >
1375
- Label
1376
- </a>
1377
- );
1378
- }}
1379
- </ClickableBehavior>
1380
- </MemoryRouter>,
1381
- );
1382
-
1383
- // Act
1384
- await userEvent.click(await screen.findByRole("link"));
1385
-
1386
- // Assert
1387
- expect(safeWithNavMock).toHaveBeenCalled();
1388
- });
1389
- });
1390
-
1391
- describe("rel", () => {
1392
- it("should use the 'rel' that was passed in", async () => {
1393
- // Arrange
1394
- const childrenMock = jest.fn().mockImplementation(() => null);
1395
- render(
1396
- <ClickableBehavior
1397
- href="https://www.khanacademy.org"
1398
- rel="something_else"
1399
- target="_blank"
1400
- >
1401
- {childrenMock}
1402
- </ClickableBehavior>,
1403
- );
1404
-
1405
- const childrenProps = childrenMock.mock.calls[0][1];
1406
- expect(childrenProps.rel).toEqual("something_else");
1407
- });
1408
-
1409
- it("should use 'noopener noreferrer' as a default when target='_blank'", async () => {
1410
- // Arrange
1411
- const childrenMock = jest.fn().mockImplementation(() => null);
1412
- render(
1413
- <ClickableBehavior
1414
- href="https://www.khanacademy.org"
1415
- target="_blank"
1416
- >
1417
- {childrenMock}
1418
- </ClickableBehavior>,
1419
- );
1420
-
1421
- const childrenProps = childrenMock.mock.calls[0][1];
1422
- expect(childrenProps.rel).toEqual("noopener noreferrer");
1423
- });
1424
-
1425
- it("should not use the default if target != '_blank'", async () => {
1426
- // Arrange
1427
- const childrenMock = jest.fn().mockImplementation(() => null);
1428
- render(
1429
- <ClickableBehavior href="https://www.khanacademy.org">
1430
- {childrenMock}
1431
- </ClickableBehavior>,
1432
- );
1433
-
1434
- const childrenProps = childrenMock.mock.calls[0][1];
1435
- expect(childrenProps.rel).toBeUndefined();
1436
- });
1437
- });
1438
- });