Skip to content

Commit fe7fba2

Browse files
committed
chore: Add new tests for flyout & toolbox.
1 parent 2b237e3 commit fe7fba2

File tree

2 files changed

+240
-0
lines changed

2 files changed

+240
-0
lines changed

test/webdriverio/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262

6363
<body>
6464
<div id="root">
65+
<div id="focusableDiv" tabindex="0">Simple div to focus.</div>
6566
<div id="blocklyDiv"></div>
6667
<div id="shortcuts"></div>
6768
</div>
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import * as chai from 'chai';
8+
import * as Blockly from 'blockly';
9+
import {
10+
testSetup,
11+
testFileLocations,
12+
PAUSE_TIME,
13+
tabNavigateForward,
14+
getFocusedBlockType,
15+
keyDown,
16+
tabNavigateBackward,
17+
tabNavigateToWorkspace,
18+
keyRight,
19+
} from './test_setup.js';
20+
21+
suite.only('Toolbox and flyout test', function () {
22+
// Clear the workspace and load start blocks
23+
setup(async function () {
24+
this.browser = await testSetup(testFileLocations.BASE);
25+
await this.browser.pause(PAUSE_TIME);
26+
});
27+
28+
test('Tab navigating to toolbox should open flyout', async function () {
29+
// Two tabs should navigate to the toolbox (initial element is skipped).
30+
await tabNavigateForward(this.browser);
31+
32+
await tabNavigateForward(this.browser);
33+
34+
// The flyout should now be open.
35+
const flyoutIsOpen = await checkIfFlyoutIsOpen(this.browser);
36+
chai.assert.isTrue(flyoutIsOpen);
37+
});
38+
39+
test('Tab navigating to flyout should auto-select first block', async function () {
40+
// Three tabs should navigate to the flyout (initial element is skipped).
41+
await tabNavigateForward(this.browser);
42+
await tabNavigateForward(this.browser);
43+
44+
await tabNavigateForward(this.browser);
45+
46+
// The top block of the category should be automatically selected. See:
47+
// https://github.com/google/blockly/issues/8978.
48+
const blockType = await getFocusedBlockType(this.browser);
49+
chai.assert.strictEqual(blockType, 'controls_if');
50+
});
51+
52+
test('Tab navigating to toolbox then right arrow key should auto-select first block in flyout', async function () {
53+
// Two tabs should navigate to the toolbox (initial element is skipped). One
54+
// right arrow key should select a block on the flyout.
55+
await tabNavigateForward(this.browser);
56+
await tabNavigateForward(this.browser);
57+
58+
await keyRight(this.browser);
59+
60+
// The top block of the category should be automatically selected. See:
61+
// https://github.com/google/blockly/issues/8978.
62+
const blockType = await getFocusedBlockType(this.browser);
63+
chai.assert.strictEqual(blockType, 'controls_if');
64+
});
65+
66+
test('Keyboard nav to different toolbox category should auto-select first block', async function () {
67+
// Two tabs should navigate to the toolbox (initial element is skipped),
68+
// then keys for a different category with a tab to select the flyout.
69+
await tabNavigateForward(this.browser);
70+
await tabNavigateForward(this.browser);
71+
72+
await keyDown(this.browser, 3);
73+
await tabNavigateForward(this.browser);
74+
75+
// The top block of the category should be automatically selected.
76+
const blockType = await getFocusedBlockType(this.browser);
77+
chai.assert.strictEqual(blockType, 'text');
78+
});
79+
80+
test('Keyboard nav to different toolbox category and block should select different block', async function () {
81+
// Two tabs should navigate to the toolbox (initial element is skipped),
82+
// then keys for a different category with a tab to select the flyout and
83+
// finally keys to select a different block.
84+
await tabNavigateForward(this.browser);
85+
await tabNavigateForward(this.browser);
86+
87+
await keyDown(this.browser, 3);
88+
await tabNavigateForward(this.browser);
89+
await keyDown(this.browser, 2);
90+
91+
// A non-top blockshould be manually selected.
92+
const blockType = await getFocusedBlockType(this.browser);
93+
chai.assert.strictEqual(blockType, 'text_append');
94+
});
95+
96+
test('Tab navigate away from toolbox restores focus to initial element', async function () {
97+
// Two tabs should navigate to the toolbox. One tab back should leave it.
98+
await tabNavigateForward(this.browser);
99+
await tabNavigateForward(this.browser);
100+
101+
await tabNavigateBackward(this.browser);
102+
103+
// Focus should restore to the initial div element. See:
104+
// https://github.com/google/blockly-keyboard-experimentation/issues/523.
105+
const activeElementId = await this.browser.execute(
106+
() => document.activeElement?.id);
107+
chai.assert.strictEqual(activeElementId, 'focusableDiv');
108+
});
109+
110+
test('Tab navigate away from toolbox closes flyout', async function () {
111+
// Two tabs should navigate to the toolbox. One tab back should leave it.
112+
await tabNavigateForward(this.browser);
113+
await tabNavigateForward(this.browser);
114+
115+
await tabNavigateBackward(this.browser);
116+
117+
// The flyout should be closed since the toolbox lost focus. See:
118+
// https://github.com/google/blockly/issues/8970.
119+
const flyoutIsOpen = await checkIfFlyoutIsOpen(this.browser);
120+
chai.assert.isFalse(flyoutIsOpen);
121+
});
122+
123+
test('Tab navigate away from flyout to toolbox and away closes flyout', async function () {
124+
// Three tabs should navigate to the flyout, and two tabs back should close.
125+
await tabNavigateForward(this.browser);
126+
await tabNavigateForward(this.browser);
127+
await tabNavigateForward(this.browser);
128+
129+
await tabNavigateBackward(this.browser);
130+
await tabNavigateBackward(this.browser);
131+
132+
// The flyout should be closed since the toolbox lost focus.
133+
const flyoutIsOpen = await checkIfFlyoutIsOpen(this.browser);
134+
chai.assert.isFalse(flyoutIsOpen);
135+
});
136+
137+
test('Tabbing to the workspace should close the flyout', async function () {
138+
await tabNavigateToWorkspace(this.browser);
139+
140+
// The flyout should be closed since it lost focus.
141+
const flyoutIsOpen = await checkIfFlyoutIsOpen(this.browser);
142+
chai.assert.isFalse(flyoutIsOpen);
143+
});
144+
145+
test('Tabbing to the workspace after selecting flyout block should close the flyout', async function () {
146+
// Two tabs should navigate to the toolbox (initial element is skipped),
147+
// then use right key to specifically navigate into the flyout before
148+
// navigating to the workspace.
149+
await tabNavigateForward(this.browser);
150+
await tabNavigateForward(this.browser);
151+
152+
await keyRight(this.browser);
153+
await tabNavigateForward(this.browser);
154+
155+
// The flyout should be closed since it lost focus. See:
156+
// https://github.com/google/blockly-keyboard-experimentation/issues/547.
157+
const flyoutIsOpen = await checkIfFlyoutIsOpen(this.browser);
158+
chai.assert.isFalse(flyoutIsOpen);
159+
});
160+
161+
test('Tabbing to the workspace after selecting flyout block via workspace toolbox shortcut should close the flyout', async function () {
162+
await tabNavigateToWorkspace(this.browser);
163+
164+
await this.browser.keys('t');
165+
await keyRight(this.browser);
166+
await tabNavigateForward(this.browser);
167+
168+
// The flyout should now be closed and unfocused. See:
169+
// https://github.com/google/blockly/pull/9079#issuecomment-2913759646.
170+
const flyoutIsOpen = await checkIfFlyoutIsOpen(this.browser);
171+
chai.assert.isFalse(flyoutIsOpen);
172+
});
173+
174+
test('Tabbing back from workspace should reopen the flyout', async function () {
175+
await tabNavigateToWorkspace(this.browser);
176+
177+
await tabNavigateBackward(this.browser);
178+
179+
// The flyout should be open again since the toolbox is again focused. See:
180+
// https://github.com/google/blockly/issues/8965.
181+
const flyoutIsOpen = await checkIfFlyoutIsOpen(this.browser);
182+
chai.assert.isTrue(flyoutIsOpen);
183+
});
184+
185+
test('Navigation position in workspace should be retained when tabbing to flyout and back', async function () {
186+
// Navigate to the workspace and select a non-default block.
187+
await tabNavigateToWorkspace(this.browser);
188+
189+
// Note that two tabs are needed here to move past the flyout.
190+
await keyDown(this.browser, 3);
191+
await tabNavigateBackward(this.browser);
192+
await tabNavigateForward(this.browser);
193+
await tabNavigateForward(this.browser);
194+
195+
// The previously selected block should be retained upon returning. See:
196+
// https://github.com/google/blockly/issues/8965#issuecomment-2900479280.
197+
const blockType = await getFocusedBlockType(this.browser);
198+
chai.assert.strictEqual(blockType, 'p5_draw');
199+
});
200+
201+
test('Clicking outside Blockly with focused toolbox closes the flyout', async function () {
202+
// Two tabs should navigate to the toolbox. One click to unfocus.
203+
await tabNavigateForward(this.browser);
204+
await tabNavigateForward(this.browser);
205+
206+
const elem = this.browser.$('#focusableDiv');
207+
await elem.click();
208+
209+
// The flyout should be closed due to clicking an outside element.
210+
const flyoutIsOpen = await checkIfFlyoutIsOpen(this.browser);
211+
chai.assert.isFalse(flyoutIsOpen);
212+
});
213+
214+
test('Clicking outside Blockly with focused flyout closes the flyout', async function () {
215+
// Two tabs should navigate to the toolbox. One key to select the flyout.
216+
await tabNavigateForward(this.browser);
217+
await tabNavigateForward(this.browser);
218+
await keyRight(this.browser);
219+
220+
const elem = this.browser.$('#focusableDiv');
221+
await elem.click();
222+
223+
// The flyout should be closed due to clicking an outside element. See:
224+
// https://github.com/google/blockly/pull/9079#issuecomment-2914628810.
225+
const flyoutIsOpen = await checkIfFlyoutIsOpen(this.browser);
226+
chai.assert.isFalse(flyoutIsOpen);
227+
});
228+
});
229+
230+
async function checkIfFlyoutIsOpen(
231+
browser: WebdriverIO.Browser,
232+
): Promise<boolean> {
233+
return await browser.execute(() => {
234+
const workspaceSvg = Blockly.getMainWorkspace() as Blockly.WorkspaceSvg;
235+
const flyout = workspaceSvg.getFlyout();
236+
if (!flyout) throw new Error('Workspace has no flyout.');
237+
return flyout.isVisible();
238+
});
239+
}

0 commit comments

Comments
 (0)