|
1 | 1 | import { describe, expect, it, vi } from "vite-plus/test"; |
2 | 2 |
|
3 | | -import { render } from "../../test/render"; |
| 3 | +import { render, stubGlobal } from "../../test/render"; |
4 | 4 | import { HistorySwipeNavigation } from "./HistorySwipeNavigation"; |
5 | 5 |
|
6 | 6 | type Scroller = "shadow" | "light" | "none" | "shadow-dialog"; |
7 | | -// Each step is one wheel event, optionally followed by the scroll it caused or a remount. |
8 | | -type Step = readonly [deltaX: number, after?: number | "remount" | "remove"]; |
| 7 | +type Step = readonly [ |
| 8 | + event: number | "begin" | "end" | "zoom", |
| 9 | + after?: number | "remount" | "remove", |
| 10 | +]; |
9 | 11 |
|
10 | 12 | function mountPanel(container: HTMLElement, scroller: Scroller, scrollLeft: number) { |
11 | 13 | const panel = container.appendChild(document.createElement("div")); |
@@ -43,19 +45,62 @@ describe("HistorySwipeNavigation", () => { |
43 | 45 | null, |
44 | 46 | ], |
45 | 47 | ["a scroller removed after a small first event", "shadow", 400, [[1, "remove"], [130]], null], |
| 48 | + [ |
| 49 | + "a scroll begin arriving after the first wheel and scroller removal", |
| 50 | + "shadow", |
| 51 | + 400, |
| 52 | + [[1, "remove"], ["begin"], [130]], |
| 53 | + null, |
| 54 | + ], |
| 55 | + [ |
| 56 | + "a new history gesture after a content scroll ends", |
| 57 | + "shadow", |
| 58 | + 400, |
| 59 | + [[1, "remove"], ["end"], ["begin"], [130]], |
| 60 | + "forward", |
| 61 | + ], |
| 62 | + [ |
| 63 | + "a scroll begin after an ignored zoom wheel", |
| 64 | + "none", |
| 65 | + 0, |
| 66 | + [["zoom"], ["begin"], [130]], |
| 67 | + "forward", |
| 68 | + ], |
46 | 69 | ["swiping inside a dialog in a shadow root", "shadow-dialog", 0, [[-130]], null], |
47 | 70 | ["swiping left over content without overflow", "none", 0, [[-130]], "back"], |
48 | 71 | ["swiping right over content without overflow", "none", 0, [[130]], "forward"], |
49 | 72 | ])("%s", async (_name, scroller, scrollLeft, steps, navigates) => { |
| 73 | + const listeners = { begin: new Set<() => void>(), end: new Set<() => void>() }; |
| 74 | + using _bridge = stubGlobal("desktopBridge", { |
| 75 | + onTrackpadScrollBegin: (listener: () => void) => { |
| 76 | + listeners.begin.add(listener); |
| 77 | + return () => listeners.begin.delete(listener); |
| 78 | + }, |
| 79 | + onTrackpadScrollEnd: (listener: () => void) => { |
| 80 | + listeners.end.add(listener); |
| 81 | + return () => listeners.end.delete(listener); |
| 82 | + }, |
| 83 | + }); |
50 | 84 | await using screen = await render(<HistorySwipeNavigation />); |
51 | 85 | using back = vi.spyOn(window.history, "back").mockImplementation(() => undefined); |
52 | 86 | using forward = vi.spyOn(window.history, "forward").mockImplementation(() => undefined); |
53 | 87 | using _canGoBack = vi.spyOn(window.navigation, "canGoBack", "get").mockReturnValue(true); |
54 | 88 | using _canGoForward = vi.spyOn(window.navigation, "canGoForward", "get").mockReturnValue(true); |
55 | 89 | let { viewport, content } = mountPanel(screen.container, scroller, scrollLeft); |
56 | 90 |
|
57 | | - for (const [deltaX, after] of [...steps, [0] as const]) { |
58 | | - content.dispatchEvent(new WheelEvent("wheel", { deltaX, bubbles: true, composed: true })); |
| 91 | + for (const [event, after] of [...steps, [0] as const]) { |
| 92 | + if (typeof event === "number" || event === "zoom") { |
| 93 | + content.dispatchEvent( |
| 94 | + new WheelEvent("wheel", { |
| 95 | + deltaX: typeof event === "number" ? event : 0, |
| 96 | + ctrlKey: event === "zoom", |
| 97 | + bubbles: true, |
| 98 | + composed: true, |
| 99 | + }), |
| 100 | + ); |
| 101 | + } else { |
| 102 | + for (const listener of listeners[event]) listener(); |
| 103 | + } |
59 | 104 | if (after === "remount") { |
60 | 105 | const replacement = viewport.cloneNode(true) as HTMLDivElement; |
61 | 106 | const { scrollLeft: previous } = viewport; |
|
0 commit comments