Skip to content

Commit 17483e9

Browse files
authored
chore: Add tests for focus after deletion. (#531)
* chore: Add tests for focus after deletion. * Formatting fixes. * Added comment clarifying why the test is adding a block. * Remove ".only" from test suite to enable other tests. * Fix lint errors.
1 parent 84c17b0 commit 17483e9

File tree

3 files changed

+271
-18
lines changed

3 files changed

+271
-18
lines changed
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
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 {
9+
blockIsPresent,
10+
currentFocusIsMainWorkspace,
11+
setCurrentCursorNodeById,
12+
getCurrentFocusNodeId,
13+
getFocusedBlockType,
14+
moveToToolboxCategory,
15+
testSetup,
16+
testFileLocations,
17+
PAUSE_TIME,
18+
tabNavigateToWorkspace,
19+
} from './test_setup.js';
20+
import {Key} from 'webdriverio';
21+
22+
suite('Deleting Blocks', function () {
23+
// Setting timeout to unlimited as these tests take a longer time to run than most mocha test
24+
this.timeout(0);
25+
26+
setup(async function () {
27+
this.browser = await testSetup(testFileLocations.NAVIGATION_TEST_BLOCKS);
28+
await this.browser.pause(PAUSE_TIME);
29+
});
30+
31+
test('Deleting block selects previous connection', async function () {
32+
await tabNavigateToWorkspace(this.browser);
33+
await this.browser.pause(PAUSE_TIME);
34+
await setCurrentCursorNodeById(this.browser, 'controls_if_2');
35+
await this.browser.pause(PAUSE_TIME);
36+
37+
chai
38+
.expect(await blockIsPresent(this.browser, 'controls_if_2'))
39+
.equal(true);
40+
41+
await this.browser.keys(Key.Backspace);
42+
await this.browser.pause(PAUSE_TIME);
43+
44+
chai
45+
.expect(await blockIsPresent(this.browser, 'controls_if_2'))
46+
.equal(false);
47+
48+
chai
49+
.expect(await getCurrentFocusNodeId(this.browser))
50+
.to.include('controls_if_1_connection_');
51+
});
52+
53+
test('Cutting block selects previous connection', async function () {
54+
await tabNavigateToWorkspace(this.browser);
55+
await this.browser.pause(PAUSE_TIME);
56+
await setCurrentCursorNodeById(this.browser, 'controls_if_2');
57+
await this.browser.pause(PAUSE_TIME);
58+
59+
chai
60+
.expect(await blockIsPresent(this.browser, 'controls_if_2'))
61+
.equal(true);
62+
63+
await this.browser.keys([Key.Ctrl, 'x']);
64+
await this.browser.pause(PAUSE_TIME);
65+
66+
chai
67+
.expect(await blockIsPresent(this.browser, 'controls_if_2'))
68+
.equal(false);
69+
70+
chai
71+
.expect(await getCurrentFocusNodeId(this.browser))
72+
.to.include('controls_if_1_connection_');
73+
});
74+
75+
test('Deleting block also deletes children and inputs', async function () {
76+
await tabNavigateToWorkspace(this.browser);
77+
await this.browser.pause(PAUSE_TIME);
78+
await setCurrentCursorNodeById(this.browser, 'controls_if_2');
79+
await this.browser.pause(PAUSE_TIME);
80+
81+
chai
82+
.expect(await blockIsPresent(this.browser, 'logic_boolean_1'))
83+
.equal(true);
84+
chai.expect(await blockIsPresent(this.browser, 'text_print_1')).equal(true);
85+
86+
await this.browser.keys(Key.Backspace);
87+
await this.browser.pause(PAUSE_TIME);
88+
89+
chai
90+
.expect(await blockIsPresent(this.browser, 'logic_boolean_1'))
91+
.equal(false);
92+
chai
93+
.expect(await blockIsPresent(this.browser, 'text_print_1'))
94+
.equal(false);
95+
});
96+
97+
test('Cutting block also removes children and inputs', async function () {
98+
await tabNavigateToWorkspace(this.browser);
99+
await this.browser.pause(PAUSE_TIME);
100+
await setCurrentCursorNodeById(this.browser, 'controls_if_2');
101+
await this.browser.pause(PAUSE_TIME);
102+
103+
chai
104+
.expect(await blockIsPresent(this.browser, 'logic_boolean_1'))
105+
.equal(true);
106+
chai.expect(await blockIsPresent(this.browser, 'text_print_1')).equal(true);
107+
108+
await this.browser.keys([Key.Ctrl, 'x']);
109+
await this.browser.pause(PAUSE_TIME);
110+
111+
chai
112+
.expect(await blockIsPresent(this.browser, 'logic_boolean_1'))
113+
.equal(false);
114+
chai
115+
.expect(await blockIsPresent(this.browser, 'text_print_1'))
116+
.equal(false);
117+
});
118+
119+
test('Deleting inline input selects parent connection', async function () {
120+
await tabNavigateToWorkspace(this.browser);
121+
await this.browser.pause(PAUSE_TIME);
122+
await setCurrentCursorNodeById(this.browser, 'logic_boolean_1');
123+
await this.browser.pause(PAUSE_TIME);
124+
125+
chai
126+
.expect(await blockIsPresent(this.browser, 'logic_boolean_1'))
127+
.equal(true);
128+
129+
await this.browser.keys(Key.Backspace);
130+
await this.browser.pause(PAUSE_TIME);
131+
132+
chai
133+
.expect(await blockIsPresent(this.browser, 'logic_boolean_1'))
134+
.equal(false);
135+
136+
chai
137+
.expect(await getCurrentFocusNodeId(this.browser))
138+
.to.include('controls_if_2_connection_');
139+
});
140+
141+
test('Cutting inline input selects parent connection', async function () {
142+
await tabNavigateToWorkspace(this.browser);
143+
await this.browser.pause(PAUSE_TIME);
144+
await setCurrentCursorNodeById(this.browser, 'logic_boolean_1');
145+
await this.browser.pause(PAUSE_TIME);
146+
147+
chai
148+
.expect(await blockIsPresent(this.browser, 'logic_boolean_1'))
149+
.equal(true);
150+
151+
await this.browser.keys([Key.Ctrl, 'x']);
152+
await this.browser.pause(PAUSE_TIME);
153+
154+
chai
155+
.expect(await blockIsPresent(this.browser, 'logic_boolean_1'))
156+
.equal(false);
157+
158+
chai
159+
.expect(await getCurrentFocusNodeId(this.browser))
160+
.to.include('controls_if_2_connection_');
161+
});
162+
163+
test('Deleting stranded block selects workspace', async function () {
164+
await tabNavigateToWorkspace(this.browser);
165+
await this.browser.pause(PAUSE_TIME);
166+
167+
// The test workspace doesn't already contain a stranded block, so add one.
168+
await moveToToolboxCategory(this.browser, 'Math');
169+
await this.browser.pause(PAUSE_TIME);
170+
// Move to flyout.
171+
await this.browser.keys(Key.ArrowRight);
172+
await this.browser.pause(PAUSE_TIME);
173+
// Select number block.
174+
await this.browser.keys(Key.Enter);
175+
await this.browser.pause(PAUSE_TIME);
176+
// Confirm move.
177+
await this.browser.keys(Key.Enter);
178+
await this.browser.pause(PAUSE_TIME);
179+
180+
chai.assert.equal('math_number', await getFocusedBlockType(this.browser));
181+
182+
await this.browser.keys(Key.Backspace);
183+
await this.browser.pause(PAUSE_TIME);
184+
185+
chai.expect(await currentFocusIsMainWorkspace(this.browser)).equal(true);
186+
});
187+
188+
test('Cutting stranded block selects workspace', async function () {
189+
await tabNavigateToWorkspace(this.browser);
190+
await this.browser.pause(PAUSE_TIME);
191+
192+
// The test workspace doesn't already contain a stranded block, so add one.
193+
await moveToToolboxCategory(this.browser, 'Math');
194+
await this.browser.pause(PAUSE_TIME);
195+
// Move to flyout.
196+
await this.browser.keys(Key.ArrowRight);
197+
await this.browser.pause(PAUSE_TIME);
198+
// Select number block.
199+
await this.browser.keys(Key.Enter);
200+
await this.browser.pause(PAUSE_TIME);
201+
// Confirm move.
202+
await this.browser.keys(Key.Enter);
203+
await this.browser.pause(PAUSE_TIME);
204+
205+
chai.assert.equal('math_number', await getFocusedBlockType(this.browser));
206+
207+
await this.browser.keys([Key.Ctrl, 'x']);
208+
await this.browser.pause(PAUSE_TIME);
209+
210+
chai.expect(await currentFocusIsMainWorkspace(this.browser)).equal(true);
211+
});
212+
});

test/webdriverio/test/insert_test.ts

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
*/
66

77
import * as chai from 'chai';
8-
import {Browser, Key} from 'webdriverio';
8+
import {Key} from 'webdriverio';
99
import {
1010
getFocusedBlockType,
11+
moveToToolboxCategory,
1112
PAUSE_TIME,
1213
setCurrentCursorNodeById,
1314
tabNavigateToWorkspace,
@@ -44,19 +45,3 @@ suite('Insert test', function () {
4445
);
4546
});
4647
});
47-
48-
async function moveToToolboxCategory(browser: Browser, category: string) {
49-
await browser.keys('t');
50-
const categoryIndex = await browser.execute((category) => {
51-
const all = Array.from(
52-
document.querySelectorAll('.blocklyToolboxCategoryLabel'),
53-
).map((node) => node.textContent);
54-
return all.indexOf(category);
55-
}, category);
56-
if (categoryIndex < 0) {
57-
throw new Error(`No category found: ${category}`);
58-
}
59-
if (categoryIndex > 0) {
60-
await browser.keys(Key.ArrowDown.repeat(categoryIndex));
61-
}
62-
}

test/webdriverio/test/test_setup.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,62 @@ export async function focusWorkspace(browser: WebdriverIO.Browser) {
171171
await workspaceElement.click();
172172
}
173173

174+
/**
175+
* Focuses the toolbox category with the given name.
176+
*
177+
* @param browser The active WebdriverIO Browser object.
178+
* @param category The name of the toolbox category to focus.
179+
*/
180+
export async function moveToToolboxCategory(
181+
browser: WebdriverIO.Browser,
182+
category: string,
183+
) {
184+
await browser.keys('t');
185+
const categoryIndex = await browser.execute((category) => {
186+
const all = Array.from(
187+
document.querySelectorAll('.blocklyToolboxCategoryLabel'),
188+
).map((node) => node.textContent);
189+
return all.indexOf(category);
190+
}, category);
191+
if (categoryIndex < 0) {
192+
throw new Error(`No category found: ${category}`);
193+
}
194+
if (categoryIndex > 0) {
195+
await browser.keys(webdriverio.Key.ArrowDown.repeat(categoryIndex));
196+
}
197+
}
198+
199+
/**
200+
* Returns whether the workspace contains a block with the given id.
201+
*
202+
* @param browser The active WebdriverIO Browser object.
203+
* @param blockId The id of the block.
204+
*/
205+
export async function blockIsPresent(
206+
browser: WebdriverIO.Browser,
207+
blockId: string,
208+
): Promise<boolean> {
209+
return await browser.execute((blockId) => {
210+
const workspaceSvg = Blockly.getMainWorkspace() as Blockly.WorkspaceSvg;
211+
const block = workspaceSvg.getBlockById(blockId);
212+
return block !== null;
213+
}, blockId);
214+
}
215+
216+
/**
217+
* Returns whether the main workspace is the current focus.
218+
*
219+
* @param browser The active WebdriverIO Browser object.
220+
*/
221+
export async function currentFocusIsMainWorkspace(
222+
browser: WebdriverIO.Browser,
223+
): Promise<boolean> {
224+
return await browser.execute(() => {
225+
const workspaceSvg = Blockly.getMainWorkspace() as Blockly.WorkspaceSvg;
226+
return Blockly.getFocusManager().getFocusedNode() === workspaceSvg;
227+
});
228+
}
229+
174230
/**
175231
* Select a block with the given id as the current cursor node.
176232
*
@@ -191,7 +247,7 @@ export async function setCurrentCursorNodeById(
191247
}
192248

193249
/**
194-
* Select a block with the given id as the current cursor node.
250+
* Select a block's field with the given block id and field name.
195251
*
196252
* @param browser The active WebdriverIO Browser object.
197253
* @param blockId The id of the block to select.

0 commit comments

Comments
 (0)