@khanacademy/wonder-blocks-tooltip 1.4.1 → 1.4.2

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,10 +1,8 @@
1
- /* eslint-disable max-lines */
2
- /* eslint-disable no-unused-vars */
3
1
  // @flow
4
2
  import * as React from "react";
5
3
  import {View} from "@khanacademy/wonder-blocks-core";
6
- import {mount} from "enzyme";
7
- import "jest-enzyme";
4
+ import {render, screen} from "@testing-library/react";
5
+ import userEvent from "@testing-library/user-event";
8
6
 
9
7
  import TooltipAnchor from "../tooltip-anchor.js";
10
8
  import {
@@ -43,11 +41,6 @@ describe("TooltipAnchor", () => {
43
41
 
44
42
  test("on mount, subscribes to focus and hover events", () => {
45
43
  // Arrange
46
- const nodes = (
47
- <TooltipAnchor anchorRef={() => {}} onActiveChanged={() => {}}>
48
- Anchor text
49
- </TooltipAnchor>
50
- );
51
44
  const addEventListenerSpy = jest.spyOn(
52
45
  HTMLElement.prototype,
53
46
  "addEventListener",
@@ -55,7 +48,11 @@ describe("TooltipAnchor", () => {
55
48
  addEventListenerSpy.mockClear();
56
49
 
57
50
  // Act
58
- mount(nodes);
51
+ render(
52
+ <TooltipAnchor anchorRef={() => {}} onActiveChanged={() => {}}>
53
+ Anchor text
54
+ </TooltipAnchor>,
55
+ );
59
56
 
60
57
  // Assert
61
58
  expect(addEventListenerSpy).toHaveBeenCalledWith(
@@ -78,17 +75,16 @@ describe("TooltipAnchor", () => {
78
75
 
79
76
  test("on unmount, unsubscribes from focus and hover events", () => {
80
77
  // Arrange
81
- const nodes = (
82
- <TooltipAnchor anchorRef={() => {}} onActiveChanged={() => {}}>
83
- Anchor text
84
- </TooltipAnchor>
85
- );
86
78
  const removeEventListenerSpy = jest.spyOn(
87
79
  HTMLElement.prototype,
88
80
  "removeEventListener",
89
81
  );
90
82
  removeEventListenerSpy.mockClear();
91
- const wrapper = mount(nodes);
83
+ const wrapper = render(
84
+ <TooltipAnchor anchorRef={() => {}} onActiveChanged={() => {}}>
85
+ Anchor text
86
+ </TooltipAnchor>,
87
+ );
92
88
 
93
89
  // Act
94
90
  wrapper.unmount();
@@ -112,123 +108,136 @@ describe("TooltipAnchor", () => {
112
108
  );
113
109
  });
114
110
 
111
+ test("ref is properly set", () => {
112
+ // Arrange
113
+ const anchorRef = jest.fn();
114
+
115
+ render(
116
+ <TooltipAnchor
117
+ forceAnchorFocusivity={true}
118
+ anchorRef={anchorRef}
119
+ onActiveChanged={() => {}}
120
+ >
121
+ <View id="portal">This is the anchor</View>
122
+ </TooltipAnchor>,
123
+ );
124
+
125
+ // Act
126
+ const result = screen.getByText("This is the anchor");
127
+
128
+ // Assert
129
+ expect(anchorRef).toHaveBeenCalledWith(result);
130
+ });
131
+
115
132
  describe("forceAnchorFocusivity is true", () => {
116
- test("if not set, sets tabindex on anchor target", async () => {
133
+ test("if not set, sets tabindex on anchor target", () => {
117
134
  // Arrange
118
- const ref = await new Promise((resolve) => {
119
- const nodes = (
120
- <TooltipAnchor
121
- forceAnchorFocusivity={true}
122
- anchorRef={resolve}
123
- onActiveChanged={() => {}}
124
- >
125
- <View id="portal">This is the anchor</View>
126
- </TooltipAnchor>
127
- );
128
- mount(nodes);
129
- });
135
+ render(
136
+ <TooltipAnchor
137
+ forceAnchorFocusivity={true}
138
+ anchorRef={jest.fn()}
139
+ onActiveChanged={() => {}}
140
+ >
141
+ <View id="portal">This is the anchor</View>
142
+ </TooltipAnchor>,
143
+ );
130
144
 
131
145
  // Act
132
- const result = ref?.getAttribute("tabindex");
146
+ const result = screen.getByText("This is the anchor");
133
147
 
134
148
  // Assert
135
- expect(result).toBe("0");
149
+ expect(result).toHaveAttribute("tabindex", "0");
136
150
  });
137
151
 
138
- test("if tabindex already set, leaves it as-is", async () => {
152
+ test("if tabindex already set, leaves it as-is", () => {
139
153
  // Arrange
140
- const ref = await new Promise((resolve) => {
141
- const nodes = (
142
- <TooltipAnchor
143
- forceAnchorFocusivity={true}
144
- anchorRef={resolve}
145
- onActiveChanged={() => {}}
146
- >
147
- <View tabIndex={-1}>This is the anchor</View>
148
- </TooltipAnchor>
149
- );
150
- mount(nodes);
151
- });
154
+ render(
155
+ <TooltipAnchor
156
+ forceAnchorFocusivity={true}
157
+ anchorRef={jest.fn()}
158
+ onActiveChanged={() => {}}
159
+ >
160
+ <View tabIndex={-1}>This is the anchor</View>
161
+ </TooltipAnchor>,
162
+ );
152
163
 
153
164
  // Act
154
- const result = ref?.getAttribute("tabindex");
165
+ const result = screen.getByText("This is the anchor");
155
166
 
156
167
  // Assert
157
- expect(result).toBe("-1");
168
+ expect(result).toHaveAttribute("tabindex", "-1");
158
169
  });
159
170
  });
160
171
 
161
172
  describe("forceAnchorFocusivity is false", () => {
162
- test("does not set tabindex on anchor target", async () => {
173
+ test("does not set tabindex on anchor target", () => {
163
174
  // Arrange
164
- const ref = await new Promise((resolve) => {
165
- const nodes = (
166
- <TooltipAnchor
167
- forceAnchorFocusivity={false}
168
- anchorRef={resolve}
169
- onActiveChanged={() => {}}
170
- >
171
- <View>This is the anchor</View>
172
- </TooltipAnchor>
173
- );
174
- mount(nodes);
175
- });
175
+ render(
176
+ <TooltipAnchor
177
+ forceAnchorFocusivity={false}
178
+ anchorRef={jest.fn()}
179
+ onActiveChanged={() => {}}
180
+ >
181
+ <View>This is the anchor</View>
182
+ </TooltipAnchor>,
183
+ );
176
184
 
177
185
  // Act
178
- const result = ref?.getAttribute("tabindex");
186
+ const result = screen.getByText("This is the anchor");
179
187
 
180
188
  // Assert
181
- expect(result).toBeNull();
189
+ expect(result).not.toHaveAttribute("tabindex");
182
190
  });
183
191
 
184
192
  test("if we had added tabindex, removes it", async () => {
185
193
  // Arrange
186
- let wrapper;
187
- const ref = await new Promise((resolve) => {
188
- const TestFixture = (props: any) => (
189
- <TooltipAnchor
190
- forceAnchorFocusivity={props.force}
191
- anchorRef={resolve}
192
- onActiveChanged={() => {}}
193
- >
194
- <View>This is the anchor</View>
195
- </TooltipAnchor>
196
- );
197
- wrapper = mount(<TestFixture force={true} />);
198
- });
194
+ const TestFixture = (props: any) => (
195
+ <TooltipAnchor
196
+ forceAnchorFocusivity={props.force}
197
+ anchorRef={jest.fn()}
198
+ onActiveChanged={() => {}}
199
+ >
200
+ <View>This is the anchor</View>
201
+ </TooltipAnchor>
202
+ );
203
+ const {rerender} = render(<TestFixture force={true} />);
199
204
 
200
205
  // Act
201
- const tabindex = ref?.getAttribute("tabindex");
202
- expect(tabindex).toBe("0");
206
+ expect(screen.getByText("This is the anchor")).toHaveAttribute(
207
+ "tabindex",
208
+ "0",
209
+ );
203
210
 
204
- wrapper?.setProps({force: false});
205
- const result = ref?.getAttribute("tabindex");
211
+ rerender(<TestFixture force={false} />);
206
212
 
207
213
  // Assert
208
- expect(result).toBeNull();
214
+ expect(screen.getByText("This is the anchor")).not.toHaveAttribute(
215
+ "tabindex",
216
+ );
209
217
  });
210
218
 
211
219
  test("if we had not added tabindex, leaves it", async () => {
212
220
  // Arrange
213
- const ref = await new Promise((resolve) => {
214
- const TestFixture = (props: any) => (
215
- <TooltipAnchor
216
- forceAnchorFocusivity={props.force}
217
- anchorRef={resolve}
218
- onActiveChanged={() => {}}
219
- >
220
- <View tabIndex={-1}>This is the anchor</View>
221
- </TooltipAnchor>
222
- );
223
- const wrapper = mount(<TestFixture force={true} />);
224
- wrapper.setProps({force: false});
225
- });
221
+ const TestFixture = (props: any) => (
222
+ <TooltipAnchor
223
+ forceAnchorFocusivity={props.force}
224
+ anchorRef={jest.fn()}
225
+ onActiveChanged={() => {}}
226
+ >
227
+ <View tabIndex={-1}>This is the anchor</View>
228
+ </TooltipAnchor>
229
+ );
230
+
231
+ const wrapper = render(<TestFixture force={true} />);
226
232
 
227
233
  // Act
228
- const result = ref?.getAttribute("tabindex");
234
+ wrapper.rerender(<TestFixture force={false} />);
229
235
 
230
236
  // Assert
231
- expect(result).not.toBeNull();
237
+ expect(screen.getByText("This is the anchor")).toHaveAttribute(
238
+ "tabindex",
239
+ "-1",
240
+ );
232
241
  });
233
242
  });
234
243
 
@@ -248,26 +257,20 @@ describe("TooltipAnchor", () => {
248
257
 
249
258
  let activeState = false;
250
259
 
251
- const ref = await new Promise((resolve) => {
252
- const nodes = (
253
- <TooltipAnchor
254
- anchorRef={resolve}
255
- onActiveChanged={(active) => {
256
- activeState = active;
257
- }}
258
- >
259
- Anchor Text
260
- </TooltipAnchor>
261
- );
262
- mount(nodes);
263
- });
260
+ render(
261
+ <TooltipAnchor
262
+ anchorRef={jest.fn()}
263
+ onActiveChanged={(active) => {
264
+ activeState = active;
265
+ }}
266
+ >
267
+ Anchor Text
268
+ </TooltipAnchor>,
269
+ );
264
270
 
265
271
  // Act
266
- // Let's fake a focusin (this is the event that the anchor gets
267
- // whether focused directly or a child is focused). We have to
268
- // fake directly because there's no real browser here handling
269
- // focus and real events.
270
- ref?.dispatchEvent(new FocusEvent("focusin"));
272
+ // Let's focus the anchor
273
+ userEvent.tab();
271
274
  // Check that we didn't go active before the delay
272
275
  expect(activeState).toBe(false);
273
276
  expect(timeoutSpy).toHaveBeenLastCalledWith(
@@ -293,26 +296,21 @@ describe("TooltipAnchor", () => {
293
296
  mockTracker.steal.mockImplementationOnce(() => true);
294
297
 
295
298
  let activeState = false;
296
- const ref = await new Promise((resolve) => {
297
- const nodes = (
298
- <TooltipAnchor
299
- anchorRef={resolve}
300
- onActiveChanged={(active) => {
301
- activeState = active;
302
- }}
303
- >
304
- Anchor Text
305
- </TooltipAnchor>
306
- );
307
- mount(nodes);
308
- });
299
+
300
+ render(
301
+ <TooltipAnchor
302
+ anchorRef={jest.fn()}
303
+ onActiveChanged={(active) => {
304
+ activeState = active;
305
+ }}
306
+ >
307
+ Anchor Text
308
+ </TooltipAnchor>,
309
+ );
309
310
 
310
311
  // Act
311
- // Let's fake a focusin (this is the event that the anchor gets
312
- // whether focused directly or a child is focused). We have to
313
- // fake directly because there's no real browser here handling
314
- // focus and real events.
315
- ref?.dispatchEvent(new FocusEvent("focusin"));
312
+ // Let's focus the anchor
313
+ userEvent.tab();
316
314
 
317
315
  // Assert
318
316
  expect(activeState).toBe(true);
@@ -320,25 +318,24 @@ describe("TooltipAnchor", () => {
320
318
  });
321
319
 
322
320
  describe("loses keyboard focus", () => {
323
- test("active state was not stolen, active is set to false with delay", async () => {
321
+ test("active state was not stolen, active is set to false with delay", () => {
324
322
  // Arrange
325
323
  const timeoutSpy = jest.spyOn(global, "setTimeout");
326
324
  let activeState = false;
327
- const ref = await new Promise((resolve) => {
328
- const nodes = (
329
- <TooltipAnchor
330
- anchorRef={resolve}
331
- onActiveChanged={(active) => {
332
- activeState = active;
333
- }}
334
- >
335
- Anchor Text
336
- </TooltipAnchor>
337
- );
338
- mount(nodes);
339
- });
340
-
341
- ref?.dispatchEvent(new FocusEvent("focusin"));
325
+
326
+ render(
327
+ <TooltipAnchor
328
+ anchorRef={jest.fn()}
329
+ onActiveChanged={(active) => {
330
+ activeState = active;
331
+ }}
332
+ >
333
+ Anchor Text
334
+ </TooltipAnchor>,
335
+ );
336
+
337
+ // Let's focus the anchor
338
+ userEvent.tab();
342
339
  expect(timeoutSpy).toHaveBeenLastCalledWith(
343
340
  expect.any(Function),
344
341
  TooltipAppearanceDelay,
@@ -347,8 +344,8 @@ describe("TooltipAnchor", () => {
347
344
  expect(activeState).toBe(true);
348
345
 
349
346
  // Act
350
- ref?.dispatchEvent(new FocusEvent("focusout"));
351
- expect(activeState).toBe(true);
347
+ // Let's blur the anchor
348
+ userEvent.tab();
352
349
  expect(timeoutSpy).toHaveBeenLastCalledWith(
353
350
  expect.any(Function),
354
351
  TooltipDisappearanceDelay,
@@ -370,21 +367,19 @@ describe("TooltipAnchor", () => {
370
367
  const mockTracker = ActiveTracker.mock.instances[0];
371
368
 
372
369
  let activeState = false;
373
- const ref = await new Promise((resolve) => {
374
- const nodes = (
375
- <TooltipAnchor
376
- anchorRef={resolve}
377
- onActiveChanged={(active) => {
378
- activeState = active;
379
- }}
380
- >
381
- Anchor Text
382
- </TooltipAnchor>
383
- );
384
- mount(nodes);
385
- });
386
-
387
- ref?.dispatchEvent(new FocusEvent("focusin"));
370
+ render(
371
+ <TooltipAnchor
372
+ anchorRef={jest.fn()}
373
+ onActiveChanged={(active) => {
374
+ activeState = active;
375
+ }}
376
+ >
377
+ Anchor Text
378
+ </TooltipAnchor>,
379
+ );
380
+
381
+ // Let's focus the anchor
382
+ userEvent.tab();
388
383
  expect(timeoutSpy).toHaveBeenLastCalledWith(
389
384
  expect.any(Function),
390
385
  TooltipAppearanceDelay,
@@ -393,8 +388,8 @@ describe("TooltipAnchor", () => {
393
388
  expect(activeState).toBe(true);
394
389
 
395
390
  // Act
396
- ref?.dispatchEvent(new FocusEvent("focusout"));
397
- expect(activeState).toBe(true);
391
+ // Let's blur the anchor
392
+ userEvent.tab();
398
393
  expect(timeoutSpy).toHaveBeenLastCalledWith(
399
394
  expect.any(Function),
400
395
  TooltipDisappearanceDelay,
@@ -405,26 +400,24 @@ describe("TooltipAnchor", () => {
405
400
  expect(mockTracker.giveup).toHaveBeenCalledTimes(1);
406
401
  });
407
402
 
408
- test("active state was stolen, active is set to false immediately", async () => {
403
+ test("active state was stolen, active is set to false immediately", () => {
409
404
  // Arrange
410
405
  const timeoutSpy = jest.spyOn(global, "setTimeout");
411
- let wrapper;
412
406
  let activeState = false;
413
- const ref = await new Promise((resolve) => {
414
- const nodes = (
415
- <TooltipAnchor
416
- anchorRef={resolve}
417
- onActiveChanged={(active) => {
418
- activeState = active;
419
- }}
420
- >
421
- Anchor Text
422
- </TooltipAnchor>
423
- );
424
- wrapper = mount(nodes);
425
- });
426
-
427
- ref?.dispatchEvent(new FocusEvent("focusin"));
407
+
408
+ render(
409
+ <TooltipAnchor
410
+ anchorRef={jest.fn()}
411
+ onActiveChanged={(active) => {
412
+ activeState = active;
413
+ }}
414
+ >
415
+ Anchor Text
416
+ </TooltipAnchor>,
417
+ );
418
+
419
+ // Focus the anchor
420
+ userEvent.tab();
428
421
  expect(timeoutSpy).toHaveBeenLastCalledWith(
429
422
  expect.any(Function),
430
423
  TooltipAppearanceDelay,
@@ -433,8 +426,9 @@ describe("TooltipAnchor", () => {
433
426
  expect(activeState).toBe(true);
434
427
 
435
428
  // Act
436
- ref?.dispatchEvent(new FocusEvent("focusout"));
437
- wrapper?.instance().activeStateStolen();
429
+ // Blur the anchor
430
+ userEvent.tab();
431
+ jest.runOnlyPendingTimers();
438
432
 
439
433
  // Assert
440
434
  expect(activeState).toBe(false);
@@ -450,22 +444,19 @@ describe("TooltipAnchor", () => {
450
444
  // $FlowFixMe[prop-missing]
451
445
  const mockTracker = ActiveTracker.mock.instances[0];
452
446
  // Arrange
453
- let wrapper;
454
447
  let activeState = false;
455
- const ref = await new Promise((resolve) => {
456
- const nodes = (
457
- <TooltipAnchor
458
- anchorRef={resolve}
459
- onActiveChanged={(active) => {
460
- activeState = active;
461
- }}
462
- >
463
- Anchor Text
464
- </TooltipAnchor>
465
- );
466
- wrapper = mount(nodes);
467
- });
468
- ref?.dispatchEvent(new FocusEvent("focusin"));
448
+ render(
449
+ <TooltipAnchor
450
+ anchorRef={jest.fn()}
451
+ onActiveChanged={(active) => {
452
+ activeState = active;
453
+ }}
454
+ >
455
+ Anchor Text
456
+ </TooltipAnchor>,
457
+ );
458
+ // Focus the anchor
459
+ userEvent.tab();
469
460
  expect(timeoutSpy).toHaveBeenLastCalledWith(
470
461
  expect.any(Function),
471
462
  TooltipAppearanceDelay,
@@ -474,41 +465,41 @@ describe("TooltipAnchor", () => {
474
465
  expect(activeState).toBe(true);
475
466
 
476
467
  // Act
477
- ref?.dispatchEvent(new FocusEvent("focusout"));
478
- wrapper?.instance().activeStateStolen();
468
+ // Blur the anchor
469
+ userEvent.tab();
479
470
 
480
471
  // Assert
481
472
  expect(mockTracker.giveup).not.toHaveBeenCalled();
482
473
  });
483
474
 
484
- test("if hovered, remains active", async () => {
475
+ test("if hovered, remains active", () => {
485
476
  // Arrange
486
477
  const timeoutSpy = jest.spyOn(global, "setTimeout");
487
478
  let activeState = false;
488
- const ref = await new Promise((resolve) => {
489
- const nodes = (
490
- <TooltipAnchor
491
- anchorRef={resolve}
492
- onActiveChanged={(active) => {
493
- activeState = active;
494
- }}
495
- >
496
- Anchor Text
497
- </TooltipAnchor>
498
- );
499
- mount(nodes);
500
- });
501
- ref?.dispatchEvent(new FocusEvent("focusin"));
479
+ render(
480
+ <TooltipAnchor
481
+ anchorRef={jest.fn()}
482
+ onActiveChanged={(active) => {
483
+ activeState = active;
484
+ }}
485
+ >
486
+ Anchor Text
487
+ </TooltipAnchor>,
488
+ );
489
+
490
+ // Focus the anchor
491
+ userEvent.tab();
502
492
  expect(timeoutSpy).toHaveBeenLastCalledWith(
503
493
  expect.any(Function),
504
494
  TooltipAppearanceDelay,
505
495
  );
506
496
  jest.runOnlyPendingTimers();
507
497
  timeoutSpy.mockClear();
508
- ref?.dispatchEvent(new MouseEvent("mouseenter"));
498
+ userEvent.hover(screen.getByText("Anchor Text"));
509
499
 
510
500
  // Act
511
- ref?.dispatchEvent(new FocusEvent("focusout"));
501
+ // Blur the anchor
502
+ userEvent.tab();
512
503
 
513
504
  // Assert
514
505
  // Make sure that we're not delay hiding as well.
@@ -532,22 +523,20 @@ describe("TooltipAnchor", () => {
532
523
  mockTracker.steal.mockImplementationOnce(() => false);
533
524
 
534
525
  let activeState = false;
535
- const ref = await new Promise((resolve) => {
536
- const nodes = (
537
- <TooltipAnchor
538
- anchorRef={resolve}
539
- onActiveChanged={(active) => {
540
- activeState = active;
541
- }}
542
- >
543
- Anchor Text
544
- </TooltipAnchor>
545
- );
546
- mount(nodes);
547
- });
526
+
527
+ render(
528
+ <TooltipAnchor
529
+ anchorRef={jest.fn()}
530
+ onActiveChanged={(active) => {
531
+ activeState = active;
532
+ }}
533
+ >
534
+ Anchor Text
535
+ </TooltipAnchor>,
536
+ );
548
537
 
549
538
  // Act
550
- ref?.dispatchEvent(new MouseEvent("mouseenter"));
539
+ userEvent.hover(screen.getByText("Anchor Text"));
551
540
  // Check that we didn't go active before the delay
552
541
  expect(activeState).toBe(false);
553
542
  expect(timeoutSpy).toHaveBeenLastCalledWith(
@@ -573,22 +562,20 @@ describe("TooltipAnchor", () => {
573
562
  mockTracker.steal.mockImplementationOnce(() => true);
574
563
 
575
564
  let activeState = false;
576
- const ref = await new Promise((resolve) => {
577
- const nodes = (
578
- <TooltipAnchor
579
- anchorRef={resolve}
580
- onActiveChanged={(active) => {
581
- activeState = active;
582
- }}
583
- >
584
- Anchor Text
585
- </TooltipAnchor>
586
- );
587
- mount(nodes);
588
- });
565
+
566
+ render(
567
+ <TooltipAnchor
568
+ anchorRef={jest.fn()}
569
+ onActiveChanged={(active) => {
570
+ activeState = active;
571
+ }}
572
+ >
573
+ Anchor Text
574
+ </TooltipAnchor>,
575
+ );
589
576
 
590
577
  // Act
591
- ref?.dispatchEvent(new MouseEvent("mouseenter"));
578
+ userEvent.hover(screen.getByText("Anchor Text"));
592
579
 
593
580
  // Assert
594
581
  expect(activeState).toBe(true);
@@ -596,24 +583,23 @@ describe("TooltipAnchor", () => {
596
583
  });
597
584
 
598
585
  describe("is unhovered", () => {
599
- test("active state was not stolen, active is set to false with delay", async () => {
586
+ test("active state was not stolen, active is set to false with delay", () => {
600
587
  // Arrange
601
588
  const timeoutSpy = jest.spyOn(global, "setTimeout");
602
589
  let activeState = false;
603
- const ref = await new Promise((resolve) => {
604
- const nodes = (
605
- <TooltipAnchor
606
- anchorRef={resolve}
607
- onActiveChanged={(active) => {
608
- activeState = active;
609
- }}
610
- >
611
- Anchor Text
612
- </TooltipAnchor>
613
- );
614
- mount(nodes);
615
- });
616
- ref?.dispatchEvent(new MouseEvent("mouseenter"));
590
+
591
+ render(
592
+ <TooltipAnchor
593
+ anchorRef={jest.fn()}
594
+ onActiveChanged={(active) => {
595
+ activeState = active;
596
+ }}
597
+ >
598
+ Anchor Text
599
+ </TooltipAnchor>,
600
+ );
601
+
602
+ userEvent.hover(screen.getByText("Anchor Text"));
617
603
  expect(timeoutSpy).toHaveBeenLastCalledWith(
618
604
  expect.any(Function),
619
605
  TooltipAppearanceDelay,
@@ -622,7 +608,7 @@ describe("TooltipAnchor", () => {
622
608
  expect(activeState).toBe(true);
623
609
 
624
610
  // Act
625
- ref?.dispatchEvent(new MouseEvent("mouseleave"));
611
+ userEvent.unhover(screen.getByText("Anchor Text"));
626
612
  expect(activeState).toBe(true);
627
613
  expect(timeoutSpy).toHaveBeenLastCalledWith(
628
614
  expect.any(Function),
@@ -644,20 +630,19 @@ describe("TooltipAnchor", () => {
644
630
  // $FlowFixMe[prop-missing]
645
631
  const mockTracker = ActiveTracker.mock.instances[0];
646
632
  let activeState = false;
647
- const ref = await new Promise((resolve) => {
648
- const nodes = (
649
- <TooltipAnchor
650
- anchorRef={resolve}
651
- onActiveChanged={(active) => {
652
- activeState = active;
653
- }}
654
- >
655
- Anchor Text
656
- </TooltipAnchor>
657
- );
658
- mount(nodes);
659
- });
660
- ref?.dispatchEvent(new MouseEvent("mouseenter"));
633
+
634
+ render(
635
+ <TooltipAnchor
636
+ anchorRef={jest.fn()}
637
+ onActiveChanged={(active) => {
638
+ activeState = active;
639
+ }}
640
+ >
641
+ Anchor Text
642
+ </TooltipAnchor>,
643
+ );
644
+
645
+ userEvent.hover(screen.getByText("Anchor Text"));
661
646
  expect(timeoutSpy).toHaveBeenLastCalledWith(
662
647
  expect.any(Function),
663
648
  TooltipAppearanceDelay,
@@ -666,7 +651,7 @@ describe("TooltipAnchor", () => {
666
651
  expect(activeState).toBe(true);
667
652
 
668
653
  // Act
669
- ref?.dispatchEvent(new MouseEvent("mouseleave"));
654
+ userEvent.unhover(screen.getByText("Anchor Text"));
670
655
  expect(activeState).toBe(true);
671
656
  expect(timeoutSpy).toHaveBeenLastCalledWith(
672
657
  expect.any(Function),
@@ -678,25 +663,23 @@ describe("TooltipAnchor", () => {
678
663
  expect(mockTracker.giveup).toHaveBeenCalledTimes(1);
679
664
  });
680
665
 
681
- test("active state was stolen, active is set to false immediately", async () => {
666
+ test("active state was stolen, active is set to false immediately", () => {
682
667
  // Arrange
683
668
  const timeoutSpy = jest.spyOn(global, "setTimeout");
684
- let wrapper;
685
669
  let activeState = false;
686
- const ref = await new Promise((resolve) => {
687
- const nodes = (
688
- <TooltipAnchor
689
- anchorRef={resolve}
690
- onActiveChanged={(active) => {
691
- activeState = active;
692
- }}
693
- >
694
- Anchor Text
695
- </TooltipAnchor>
696
- );
697
- wrapper = mount(nodes);
698
- });
699
- ref?.dispatchEvent(new MouseEvent("mouseenter"));
670
+
671
+ render(
672
+ <TooltipAnchor
673
+ anchorRef={jest.fn()}
674
+ onActiveChanged={(active) => {
675
+ activeState = active;
676
+ }}
677
+ >
678
+ Anchor Text
679
+ </TooltipAnchor>,
680
+ );
681
+
682
+ userEvent.hover(screen.getByText("Anchor Text"));
700
683
  expect(timeoutSpy).toHaveBeenLastCalledWith(
701
684
  expect.any(Function),
702
685
  TooltipAppearanceDelay,
@@ -705,8 +688,8 @@ describe("TooltipAnchor", () => {
705
688
  expect(activeState).toBe(true);
706
689
 
707
690
  // Act
708
- ref?.dispatchEvent(new MouseEvent("mouseleave"));
709
- wrapper?.instance().activeStateStolen();
691
+ userEvent.unhover(screen.getByText("Anchor Text"));
692
+ jest.runOnlyPendingTimers();
710
693
 
711
694
  // Assert
712
695
  expect(activeState).toBe(false);
@@ -722,22 +705,18 @@ describe("TooltipAnchor", () => {
722
705
  // $FlowFixMe[prop-missing]
723
706
  const mockTracker = ActiveTracker.mock.instances[0];
724
707
  // Arrange
725
- let wrapper;
726
708
  let activeState = false;
727
- const ref = await new Promise((resolve) => {
728
- const nodes = (
729
- <TooltipAnchor
730
- anchorRef={resolve}
731
- onActiveChanged={(active) => {
732
- activeState = active;
733
- }}
734
- >
735
- Anchor Text
736
- </TooltipAnchor>
737
- );
738
- wrapper = mount(nodes);
739
- });
740
- ref?.dispatchEvent(new MouseEvent("mouseenter"));
709
+ render(
710
+ <TooltipAnchor
711
+ anchorRef={jest.fn()}
712
+ onActiveChanged={(active) => {
713
+ activeState = active;
714
+ }}
715
+ >
716
+ Anchor Text
717
+ </TooltipAnchor>,
718
+ );
719
+ userEvent.hover(screen.getByText("Anchor Text"));
741
720
  expect(timeoutSpy).toHaveBeenLastCalledWith(
742
721
  expect.any(Function),
743
722
  TooltipAppearanceDelay,
@@ -746,8 +725,7 @@ describe("TooltipAnchor", () => {
746
725
  expect(activeState).toBe(true);
747
726
 
748
727
  // Act
749
- ref?.dispatchEvent(new MouseEvent("mouseleave"));
750
- wrapper?.instance().activeStateStolen();
728
+ userEvent.unhover(screen.getByText("Anchor Text"));
751
729
 
752
730
  // Assert
753
731
  expect(mockTracker.giveup).not.toHaveBeenCalled();
@@ -757,30 +735,28 @@ describe("TooltipAnchor", () => {
757
735
  // Arrange
758
736
  const timeoutSpy = jest.spyOn(global, "setTimeout");
759
737
  let activeState = false;
760
- const ref = await new Promise((resolve) => {
761
- const nodes = (
762
- <TooltipAnchor
763
- anchorRef={resolve}
764
- onActiveChanged={(active) => {
765
- activeState = active;
766
- }}
767
- >
768
- Anchor Text
769
- </TooltipAnchor>
770
- );
771
- mount(nodes);
772
- });
773
- ref?.dispatchEvent(new MouseEvent("mouseenter"));
738
+ render(
739
+ <TooltipAnchor
740
+ anchorRef={jest.fn()}
741
+ onActiveChanged={(active) => {
742
+ activeState = active;
743
+ }}
744
+ >
745
+ Anchor Text
746
+ </TooltipAnchor>,
747
+ );
748
+ userEvent.hover(screen.getByText("Anchor Text"));
774
749
  expect(timeoutSpy).toHaveBeenLastCalledWith(
775
750
  expect.any(Function),
776
751
  TooltipAppearanceDelay,
777
752
  );
778
753
  jest.runOnlyPendingTimers();
779
754
  timeoutSpy.mockClear();
780
- ref?.dispatchEvent(new FocusEvent("focusin"));
755
+ // Focus the anchor
756
+ userEvent.tab();
781
757
 
782
758
  // Act
783
- ref?.dispatchEvent(new MouseEvent("mouseleave"));
759
+ userEvent.unhover(screen.getByText("Anchor Text"));
784
760
 
785
761
  // Assert
786
762
  // Make sure that we're not delay hiding as well.
@@ -790,24 +766,18 @@ describe("TooltipAnchor", () => {
790
766
  });
791
767
 
792
768
  describe("dismiss behavior", () => {
793
- test("subscribes to keydown event on active", async () => {
769
+ test("subscribes to keydown event on active", () => {
794
770
  // Arrange
795
771
  const timeoutSpy = jest.spyOn(global, "setTimeout");
796
772
  const spy = jest.spyOn(document, "addEventListener");
797
- const ref = await new Promise((resolve) => {
798
- const nodes = (
799
- <TooltipAnchor
800
- anchorRef={resolve}
801
- onActiveChanged={() => {}}
802
- >
803
- Anchor Text
804
- </TooltipAnchor>
805
- );
806
- mount(nodes);
807
- });
773
+ render(
774
+ <TooltipAnchor anchorRef={jest.fn()} onActiveChanged={() => {}}>
775
+ Anchor Text
776
+ </TooltipAnchor>,
777
+ );
808
778
 
809
779
  // Act
810
- ref?.dispatchEvent(new MouseEvent("mouseenter"));
780
+ userEvent.hover(screen.getByText("Anchor Text"));
811
781
  expect(timeoutSpy).toHaveBeenLastCalledWith(
812
782
  expect.any(Function),
813
783
  TooltipAppearanceDelay,
@@ -819,23 +789,18 @@ describe("TooltipAnchor", () => {
819
789
  expect(spy).toHaveBeenLastCalledWith("keyup", expect.any(Function));
820
790
  });
821
791
 
822
- test("does not subscribe to keydown event if already active", async () => {
792
+ test("does not subscribe to keydown event if already active", () => {
823
793
  // Arrange
824
794
  const timeoutSpy = jest.spyOn(global, "setTimeout");
825
795
  const spy = jest.spyOn(document, "addEventListener");
826
- const ref = await new Promise((resolve) => {
827
- const nodes = (
828
- <TooltipAnchor
829
- anchorRef={resolve}
830
- onActiveChanged={() => {}}
831
- >
832
- Anchor Text
833
- </TooltipAnchor>
834
- );
835
- mount(nodes);
836
- });
837
-
838
- ref?.dispatchEvent(new KeyboardEvent("focusin"));
796
+ render(
797
+ <TooltipAnchor anchorRef={jest.fn()} onActiveChanged={() => {}}>
798
+ Anchor Text
799
+ </TooltipAnchor>,
800
+ );
801
+
802
+ // Focus the anchor
803
+ userEvent.tab();
839
804
  expect(timeoutSpy).toHaveBeenLastCalledWith(
840
805
  expect.any(Function),
841
806
  TooltipAppearanceDelay,
@@ -846,29 +811,24 @@ describe("TooltipAnchor", () => {
846
811
  spy.mockClear();
847
812
 
848
813
  // Act
849
- ref?.dispatchEvent(new MouseEvent("mouseenter"));
814
+ userEvent.hover(screen.getByText("Anchor Text"));
850
815
 
851
816
  // Assert
852
817
  expect(spy).not.toHaveBeenCalled();
853
818
  });
854
819
 
855
- test("unsubscribes from keydown event on inactive", async () => {
820
+ test("unsubscribes from keydown event on inactive", () => {
856
821
  // Arrange
857
822
  const timeoutSpy = jest.spyOn(global, "setTimeout");
858
823
  const spy = jest.spyOn(document, "removeEventListener");
859
- const ref = await new Promise((resolve) => {
860
- const nodes = (
861
- <TooltipAnchor
862
- anchorRef={resolve}
863
- onActiveChanged={() => {}}
864
- >
865
- Anchor Text
866
- </TooltipAnchor>
867
- );
868
- mount(nodes);
869
- });
870
-
871
- ref?.dispatchEvent(new KeyboardEvent("focusin"));
824
+ render(
825
+ <TooltipAnchor anchorRef={jest.fn()} onActiveChanged={() => {}}>
826
+ Anchor Text
827
+ </TooltipAnchor>,
828
+ );
829
+
830
+ // Focus the anchor
831
+ userEvent.tab();
872
832
  expect(timeoutSpy).toHaveBeenLastCalledWith(
873
833
  expect.any(Function),
874
834
  TooltipAppearanceDelay,
@@ -876,7 +836,8 @@ describe("TooltipAnchor", () => {
876
836
  jest.runOnlyPendingTimers();
877
837
 
878
838
  // Act
879
- ref?.dispatchEvent(new KeyboardEvent("focusout"));
839
+ // Blur the anchor
840
+ userEvent.tab();
880
841
  expect(timeoutSpy).toHaveBeenLastCalledWith(
881
842
  expect.any(Function),
882
843
  TooltipDisappearanceDelay,
@@ -888,24 +849,19 @@ describe("TooltipAnchor", () => {
888
849
  expect(spy).toHaveBeenLastCalledWith("keyup", expect.any(Function));
889
850
  });
890
851
 
891
- test("unsubscribes from keydown event on unmount", async () => {
852
+ test("unsubscribes from keydown event on unmount", () => {
892
853
  // Arrange
893
854
  const timeoutSpy = jest.spyOn(global, "setTimeout");
894
- let wrapper;
855
+
895
856
  const spy = jest.spyOn(document, "removeEventListener");
896
- const ref = await new Promise((resolve) => {
897
- const nodes = (
898
- <TooltipAnchor
899
- anchorRef={resolve}
900
- onActiveChanged={() => {}}
901
- >
902
- Anchor Text
903
- </TooltipAnchor>
904
- );
905
- wrapper = mount(nodes);
906
- });
907
-
908
- ref?.dispatchEvent(new KeyboardEvent("focusin"));
857
+ const {unmount} = render(
858
+ <TooltipAnchor anchorRef={jest.fn()} onActiveChanged={() => {}}>
859
+ Anchor Text
860
+ </TooltipAnchor>,
861
+ );
862
+
863
+ // Focus the anchor
864
+ userEvent.tab();
909
865
  expect(timeoutSpy).toHaveBeenLastCalledWith(
910
866
  expect.any(Function),
911
867
  TooltipAppearanceDelay,
@@ -913,46 +869,38 @@ describe("TooltipAnchor", () => {
913
869
  jest.runOnlyPendingTimers();
914
870
 
915
871
  // Act
916
- wrapper?.unmount();
872
+ unmount();
917
873
 
918
874
  // Assert
919
875
  expect(spy).toHaveBeenCalledTimes(1);
920
876
  expect(spy).toHaveBeenLastCalledWith("keyup", expect.any(Function));
921
877
  });
922
878
 
923
- test("when active, escape dismisses tooltip", async () => {
879
+ test("when active, escape dismisses tooltip", () => {
924
880
  // Arrange
925
881
  const timeoutSpy = jest.spyOn(global, "setTimeout");
926
882
  let activeState = false;
927
- const ref = await new Promise((resolve) => {
928
- const nodes = (
929
- <TooltipAnchor
930
- anchorRef={resolve}
931
- onActiveChanged={(active) => {
932
- activeState = active;
933
- }}
934
- >
935
- Anchor Text
936
- </TooltipAnchor>
937
- );
938
- mount(nodes);
939
- });
940
-
941
- ref?.dispatchEvent(new KeyboardEvent("focusin"));
883
+ render(
884
+ <TooltipAnchor
885
+ anchorRef={jest.fn()}
886
+ onActiveChanged={(active) => {
887
+ activeState = active;
888
+ }}
889
+ >
890
+ Anchor Text
891
+ </TooltipAnchor>,
892
+ );
893
+
894
+ // Focus the anchor
895
+ userEvent.tab();
942
896
  expect(timeoutSpy).toHaveBeenLastCalledWith(
943
897
  expect.any(Function),
944
898
  TooltipAppearanceDelay,
945
899
  );
946
900
  jest.runOnlyPendingTimers();
947
- const event: KeyboardEvent = (document.createEvent("Event"): any);
948
- // $FlowIgnore[cannot-write]
949
- event.key = "Escape";
950
- // $FlowIgnore[cannot-write]
951
- event.which = 27;
952
- event.initEvent("keyup", true, true);
953
901
 
954
902
  // Act
955
- document.dispatchEvent(event);
903
+ userEvent.keyboard("{esc}");
956
904
  expect(timeoutSpy).toHaveBeenLastCalledWith(
957
905
  expect.any(Function),
958
906
  TooltipDisappearanceDelay,
@@ -966,19 +914,14 @@ describe("TooltipAnchor", () => {
966
914
  test("when active, escape stops event propagation", async () => {
967
915
  // Arrange
968
916
  const timeoutSpy = jest.spyOn(global, "setTimeout");
969
- const ref = await new Promise((resolve) => {
970
- const nodes = (
971
- <TooltipAnchor
972
- anchorRef={resolve}
973
- onActiveChanged={() => {}}
974
- >
975
- Anchor Text
976
- </TooltipAnchor>
977
- );
978
- mount(nodes);
979
- });
980
-
981
- ref?.dispatchEvent(new KeyboardEvent("focusin"));
917
+ render(
918
+ <TooltipAnchor anchorRef={jest.fn()} onActiveChanged={() => {}}>
919
+ Anchor Text
920
+ </TooltipAnchor>,
921
+ );
922
+
923
+ // Focus the anchor
924
+ userEvent.tab();
982
925
  expect(timeoutSpy).toHaveBeenLastCalledWith(
983
926
  expect.any(Function),
984
927
  TooltipAppearanceDelay,