Skip to content

Commit afccda1

Browse files
committed
fix: update activation tests and keyboard handling
- Modified the activation test to ensure it checks for the correct activation conditions when the 'C' key is pressed without the Cmd/Ctrl modifier. - Updated the keyboard handling logic in the fixtures to reflect the new activation key behavior, ensuring consistency across tests.
1 parent 9c7688d commit afccda1

File tree

6 files changed

+421
-4
lines changed

6 files changed

+421
-4
lines changed

packages/react-grab/e2e/activation.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ test.describe("Activation Flows", () => {
1111
expect(isVisibleAfter).toBe(true);
1212
});
1313

14-
test("should not activate if key released before threshold", async ({
14+
test("should not activate when pressing C without Cmd/Ctrl modifier", async ({
1515
reactGrab,
1616
}) => {
1717
await reactGrab.page.keyboard.down("c");

packages/react-grab/e2e/fixtures.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { test as base, expect, Page, Locator } from "@playwright/test";
33
const ATTRIBUTE_NAME = "data-react-grab";
44
const DEFAULT_KEY_HOLD_DURATION_MS = 200;
55
const ACTIVATION_BUFFER_MS = 100;
6-
const ACTIVATION_KEY = "c";
76

87
interface ReactGrabPageObject {
98
page: Page;
@@ -16,10 +15,16 @@ interface ReactGrabPageObject {
1615
getShadowRoot: () => Promise<Element | null>;
1716
hoverElement: (selector: string) => Promise<void>;
1817
clickElement: (selector: string) => Promise<void>;
18+
doubleClickElement: (selector: string) => Promise<void>;
1919
dragSelect: (startSelector: string, endSelector: string) => Promise<void>;
2020
getClipboardContent: () => Promise<string>;
2121
waitForSelectionBox: () => Promise<void>;
2222
pressEscape: () => Promise<void>;
23+
pressArrowDown: () => Promise<void>;
24+
pressArrowUp: () => Promise<void>;
25+
pressArrowLeft: () => Promise<void>;
26+
pressArrowRight: () => Promise<void>;
27+
scrollPage: (deltaY: number) => Promise<void>;
2328
}
2429

2530
const createReactGrabPageObject = (page: Page): ReactGrabPageObject => {
@@ -43,7 +48,8 @@ const createReactGrabPageObject = (page: Page): ReactGrabPageObject => {
4348

4449
const holdToActivate = async (durationMs = DEFAULT_KEY_HOLD_DURATION_MS) => {
4550
await page.click("body");
46-
await page.keyboard.down(ACTIVATION_KEY);
51+
await page.keyboard.down("Meta");
52+
await page.keyboard.down("c");
4753
await page.waitForTimeout(durationMs + ACTIVATION_BUFFER_MS);
4854
};
4955

@@ -58,7 +64,8 @@ const createReactGrabPageObject = (page: Page): ReactGrabPageObject => {
5864

5965
const activateViaKeyboard = async () => {
6066
await holdToActivate();
61-
await page.keyboard.up(ACTIVATION_KEY);
67+
await page.keyboard.up("c");
68+
await page.keyboard.up("Meta");
6269
await page.waitForTimeout(100);
6370
};
6471

@@ -114,6 +121,36 @@ const createReactGrabPageObject = (page: Page): ReactGrabPageObject => {
114121
await page.keyboard.press("Escape");
115122
};
116123

124+
const pressArrowDown = async () => {
125+
await page.keyboard.press("ArrowDown");
126+
await page.waitForTimeout(50);
127+
};
128+
129+
const pressArrowUp = async () => {
130+
await page.keyboard.press("ArrowUp");
131+
await page.waitForTimeout(50);
132+
};
133+
134+
const pressArrowLeft = async () => {
135+
await page.keyboard.press("ArrowLeft");
136+
await page.waitForTimeout(50);
137+
};
138+
139+
const pressArrowRight = async () => {
140+
await page.keyboard.press("ArrowRight");
141+
await page.waitForTimeout(50);
142+
};
143+
144+
const doubleClickElement = async (selector: string) => {
145+
const element = page.locator(selector).first();
146+
await element.dblclick({ force: true });
147+
};
148+
149+
const scrollPage = async (deltaY: number) => {
150+
await page.mouse.wheel(0, deltaY);
151+
await page.waitForTimeout(100);
152+
};
153+
117154
return {
118155
page,
119156
activate,
@@ -125,10 +162,16 @@ const createReactGrabPageObject = (page: Page): ReactGrabPageObject => {
125162
getShadowRoot,
126163
hoverElement,
127164
clickElement,
165+
doubleClickElement,
128166
dragSelect,
129167
getClipboardContent,
130168
waitForSelectionBox,
131169
pressEscape,
170+
pressArrowDown,
171+
pressArrowUp,
172+
pressArrowLeft,
173+
pressArrowRight,
174+
scrollPage,
132175
};
133176
};
134177

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { test, expect } from "./fixtures.js";
2+
3+
test.describe("Hold Activation Mode", () => {
4+
test("should not activate when pressing C without Cmd/Ctrl modifier", async ({
5+
reactGrab,
6+
}) => {
7+
await reactGrab.page.click("body");
8+
await reactGrab.page.keyboard.down("c");
9+
await reactGrab.page.waitForTimeout(50);
10+
await reactGrab.page.keyboard.up("c");
11+
12+
const isVisible = await reactGrab.isOverlayVisible();
13+
expect(isVisible).toBe(false);
14+
});
15+
16+
test("should allow multiple API activations in sequence", async ({
17+
reactGrab,
18+
}) => {
19+
await reactGrab.activate();
20+
21+
let isVisible = await reactGrab.isOverlayVisible();
22+
expect(isVisible).toBe(true);
23+
24+
await reactGrab.pressEscape();
25+
await reactGrab.page.waitForTimeout(100);
26+
27+
isVisible = await reactGrab.isOverlayVisible();
28+
expect(isVisible).toBe(false);
29+
30+
await reactGrab.activate();
31+
32+
isVisible = await reactGrab.isOverlayVisible();
33+
expect(isVisible).toBe(true);
34+
});
35+
36+
test("should allow selection after API activation", async ({ reactGrab }) => {
37+
await reactGrab.activate();
38+
39+
await reactGrab.hoverElement("li:first-child");
40+
await reactGrab.waitForSelectionBox();
41+
42+
const isVisible = await reactGrab.isOverlayVisible();
43+
expect(isVisible).toBe(true);
44+
});
45+
46+
test("should allow dragging after API activation", async ({ reactGrab }) => {
47+
await reactGrab.activate();
48+
49+
const firstItem = reactGrab.page.locator("li").first();
50+
const firstBox = await firstItem.boundingBox();
51+
if (!firstBox) throw new Error("Could not get bounding box");
52+
53+
await reactGrab.page.mouse.move(firstBox.x - 10, firstBox.y - 10);
54+
await reactGrab.page.mouse.down();
55+
await reactGrab.page.mouse.move(firstBox.x + 100, firstBox.y + 100, {
56+
steps: 5,
57+
});
58+
59+
const isVisible = await reactGrab.isOverlayVisible();
60+
expect(isVisible).toBe(true);
61+
62+
await reactGrab.page.mouse.up();
63+
});
64+
65+
test("should copy heading element after API activation", async ({
66+
reactGrab,
67+
}) => {
68+
await reactGrab.activate();
69+
70+
await reactGrab.hoverElement("h1");
71+
await reactGrab.waitForSelectionBox();
72+
73+
await reactGrab.clickElement("h1");
74+
await reactGrab.page.waitForTimeout(500);
75+
76+
const clipboardContent = await reactGrab.getClipboardContent();
77+
expect(clipboardContent).toContain("Todo List");
78+
});
79+
});
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { test, expect } from "./fixtures.js";
2+
3+
test.describe("Keyboard Navigation", () => {
4+
test("should navigate to next element with ArrowDown", async ({
5+
reactGrab,
6+
}) => {
7+
await reactGrab.activate();
8+
await reactGrab.hoverElement("li:first-child");
9+
await reactGrab.waitForSelectionBox();
10+
11+
await reactGrab.page.keyboard.press("ArrowDown");
12+
await reactGrab.waitForSelectionBox();
13+
14+
const isVisible = await reactGrab.isOverlayVisible();
15+
expect(isVisible).toBe(true);
16+
});
17+
18+
test("should navigate to previous element with ArrowUp", async ({
19+
reactGrab,
20+
}) => {
21+
await reactGrab.activate();
22+
await reactGrab.hoverElement("li:nth-child(3)");
23+
await reactGrab.waitForSelectionBox();
24+
25+
await reactGrab.page.keyboard.press("ArrowUp");
26+
await reactGrab.waitForSelectionBox();
27+
28+
const isVisible = await reactGrab.isOverlayVisible();
29+
expect(isVisible).toBe(true);
30+
});
31+
32+
test("should navigate to parent element with ArrowLeft", async ({
33+
reactGrab,
34+
}) => {
35+
await reactGrab.activate();
36+
await reactGrab.hoverElement("li:first-child");
37+
await reactGrab.waitForSelectionBox();
38+
39+
await reactGrab.page.keyboard.press("ArrowLeft");
40+
await reactGrab.waitForSelectionBox();
41+
42+
const isVisible = await reactGrab.isOverlayVisible();
43+
expect(isVisible).toBe(true);
44+
});
45+
46+
test("should navigate to child element with ArrowRight", async ({
47+
reactGrab,
48+
}) => {
49+
await reactGrab.activate();
50+
await reactGrab.hoverElement("ul");
51+
await reactGrab.waitForSelectionBox();
52+
53+
await reactGrab.page.keyboard.press("ArrowRight");
54+
await reactGrab.waitForSelectionBox();
55+
56+
const isVisible = await reactGrab.isOverlayVisible();
57+
expect(isVisible).toBe(true);
58+
});
59+
60+
test("should maintain activation during keyboard navigation", async ({
61+
reactGrab,
62+
}) => {
63+
await reactGrab.activate();
64+
await reactGrab.hoverElement("li:first-child");
65+
await reactGrab.waitForSelectionBox();
66+
67+
await reactGrab.page.keyboard.press("ArrowDown");
68+
await reactGrab.page.keyboard.press("ArrowDown");
69+
await reactGrab.page.keyboard.press("ArrowUp");
70+
71+
const isVisible = await reactGrab.isOverlayVisible();
72+
expect(isVisible).toBe(true);
73+
});
74+
75+
test("should copy element after keyboard navigation with click", async ({
76+
reactGrab,
77+
}) => {
78+
await reactGrab.activate();
79+
await reactGrab.hoverElement("li:first-child");
80+
await reactGrab.waitForSelectionBox();
81+
82+
await reactGrab.page.keyboard.press("ArrowDown");
83+
await reactGrab.waitForSelectionBox();
84+
85+
await reactGrab.page.mouse.click(
86+
(await reactGrab.page.locator("li:nth-child(2)").boundingBox())!.x + 10,
87+
(await reactGrab.page.locator("li:nth-child(2)").boundingBox())!.y + 10,
88+
);
89+
await reactGrab.page.waitForTimeout(500);
90+
91+
const clipboardContent = await reactGrab.getClipboardContent();
92+
expect(clipboardContent).toBeTruthy();
93+
});
94+
95+
test("should freeze selection when navigating with arrow keys", async ({
96+
reactGrab,
97+
}) => {
98+
await reactGrab.activate();
99+
await reactGrab.hoverElement("li:first-child");
100+
await reactGrab.waitForSelectionBox();
101+
102+
await reactGrab.page.keyboard.press("ArrowDown");
103+
await reactGrab.waitForSelectionBox();
104+
105+
await reactGrab.page.mouse.move(0, 0);
106+
await reactGrab.page.waitForTimeout(100);
107+
108+
const isVisible = await reactGrab.isOverlayVisible();
109+
expect(isVisible).toBe(true);
110+
});
111+
});
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { test, expect } from "./fixtures.js";
2+
3+
test.describe("Keyboard Shortcuts", () => {
4+
test("should copy selected element when clicking", async ({ reactGrab }) => {
5+
await reactGrab.activate();
6+
await reactGrab.hoverElement("h1");
7+
await reactGrab.waitForSelectionBox();
8+
9+
await reactGrab.clickElement("h1");
10+
await reactGrab.page.waitForTimeout(500);
11+
12+
const clipboardContent = await reactGrab.getClipboardContent();
13+
expect(clipboardContent).toContain("Todo List");
14+
});
15+
16+
test("should deactivate when pressing Escape while hovering", async ({
17+
reactGrab,
18+
}) => {
19+
await reactGrab.activate();
20+
await reactGrab.hoverElement("li:first-child");
21+
await reactGrab.waitForSelectionBox();
22+
23+
await reactGrab.pressEscape();
24+
await reactGrab.page.waitForTimeout(100);
25+
26+
const isVisible = await reactGrab.isOverlayVisible();
27+
expect(isVisible).toBe(false);
28+
});
29+
30+
test("should not activate when pressing C without Cmd/Ctrl modifier", async ({
31+
reactGrab,
32+
}) => {
33+
await reactGrab.page.keyboard.down("c");
34+
await reactGrab.page.waitForTimeout(50);
35+
await reactGrab.page.keyboard.up("c");
36+
37+
const isVisible = await reactGrab.isOverlayVisible();
38+
expect(isVisible).toBe(false);
39+
});
40+
41+
test("should copy list item when clicked", async ({ reactGrab }) => {
42+
await reactGrab.activate();
43+
await reactGrab.hoverElement("li:nth-child(2)");
44+
await reactGrab.waitForSelectionBox();
45+
46+
await reactGrab.clickElement("li:nth-child(2)");
47+
await reactGrab.page.waitForTimeout(500);
48+
49+
const clipboardContent = await reactGrab.getClipboardContent();
50+
expect(clipboardContent).toContain("Write a blog post");
51+
});
52+
53+
test("should keep overlay active while navigating with arrow keys", async ({
54+
reactGrab,
55+
}) => {
56+
await reactGrab.activate();
57+
await reactGrab.hoverElement("li:first-child");
58+
await reactGrab.waitForSelectionBox();
59+
60+
for (let i = 0; i < 5; i++) {
61+
await reactGrab.page.keyboard.press("ArrowDown");
62+
await reactGrab.page.waitForTimeout(50);
63+
}
64+
65+
const isVisible = await reactGrab.isOverlayVisible();
66+
expect(isVisible).toBe(true);
67+
});
68+
69+
test("should deactivate after successful click copy in toggle mode", async ({
70+
reactGrab,
71+
}) => {
72+
await reactGrab.activate();
73+
await reactGrab.hoverElement("li:first-child");
74+
await reactGrab.waitForSelectionBox();
75+
76+
await reactGrab.clickElement("li:first-child");
77+
await reactGrab.page.waitForTimeout(2000);
78+
79+
const isVisible = await reactGrab.isOverlayVisible();
80+
expect(isVisible).toBe(false);
81+
});
82+
});

0 commit comments

Comments
 (0)