Skip to content

Commit 89af298

Browse files
authored
Merge pull request #9183 from RoboErikG/fix-browser-tests-2025-06
fix: Fix more browser tests
2 parents fc9164d + 2fba036 commit 89af298

File tree

6 files changed

+116
-135
lines changed

6 files changed

+116
-135
lines changed

tests/browser/test/delete_blocks_test.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as chai from 'chai';
88
import {Key} from 'webdriverio';
99
import {
1010
clickBlock,
11+
clickWorkspace,
1112
contextMenuSelect,
1213
getAllBlocks,
1314
getBlockElementById,
@@ -176,8 +177,7 @@ suite('Delete blocks', function (done) {
176177
);
177178
});
178179

179-
// TODO(#9029) enable this test once deleting a block doesn't lose focus
180-
test.skip('Undo block deletion', async function () {
180+
test('Undo block deletion', async function () {
181181
const before = (await getAllBlocks(this.browser)).length;
182182
// Get first print block, click to select it, and delete it using backspace key.
183183
await clickBlock(this.browser, this.firstBlock.id, {button: 1});
@@ -204,6 +204,7 @@ suite('Delete blocks', function (done) {
204204
await this.browser.keys([Key.Ctrl, 'z']);
205205
await this.browser.pause(PAUSE_TIME);
206206
// Redo
207+
await clickWorkspace(this.browser);
207208
await this.browser.keys([Key.Ctrl, Key.Shift, 'z']);
208209
await this.browser.pause(PAUSE_TIME);
209210
const after = (await getAllBlocks(this.browser)).length;

tests/browser/test/extensive_test.mjs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ suite('This tests loading Large Configuration and Deletion', function (done) {
4040
chai.assert.equal(allBlocks.length, 10);
4141
});
4242

43-
// TODO(#8793) Re-enable test after deleting a block updates focus correctly.
44-
test.skip('undoing delete block results in the correct number of blocks', async function () {
43+
test('undoing delete block results in the correct number of blocks', async function () {
4544
await this.browser.keys([Key.Ctrl, 'z']);
4645
await this.browser.pause(PAUSE_TIME);
4746
const allBlocks = await getAllBlocks(this.browser);

tests/browser/test/procedure_test.mjs

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@
1111
import * as chai from 'chai';
1212
import {
1313
connect,
14-
getBlockTypeFromCategory,
15-
getNthBlockOfCategory,
16-
getSelectedBlockElement,
14+
dragBlockTypeFromFlyout,
15+
dragNthBlockFromFlyout,
1716
PAUSE_TIME,
1817
testFileLocations,
1918
testSetup,
@@ -33,43 +32,41 @@ suite('Testing Connecting Blocks', function (done) {
3332

3433
test('Testing Procedure', async function () {
3534
// Drag out first function
36-
let proceduresDefReturn = await getBlockTypeFromCategory(
35+
const doSomething = await dragBlockTypeFromFlyout(
3736
this.browser,
3837
'Functions',
3938
'procedures_defreturn',
39+
50,
40+
20,
4041
);
41-
await proceduresDefReturn.dragAndDrop({x: 50, y: 20});
42-
const doSomething = await getSelectedBlockElement(this.browser);
4342

44-
// Drag out second function.
45-
proceduresDefReturn = await getBlockTypeFromCategory(
43+
const doSomething2 = await dragBlockTypeFromFlyout(
4644
this.browser,
4745
'Functions',
4846
'procedures_defreturn',
47+
50,
48+
20,
4949
);
50-
await proceduresDefReturn.dragAndDrop({x: 300, y: 200});
51-
const doSomething2 = await getSelectedBlockElement(this.browser);
5250

53-
// Drag out numeric
54-
const mathNumeric = await getBlockTypeFromCategory(
51+
const numeric = await dragBlockTypeFromFlyout(
5552
this.browser,
5653
'Math',
5754
'math_number',
55+
50,
56+
20,
5857
);
59-
await mathNumeric.dragAndDrop({x: 50, y: 20});
60-
const numeric = await getSelectedBlockElement(this.browser);
6158

6259
// Connect numeric to first procedure
6360
await connect(this.browser, numeric, 'OUTPUT', doSomething, 'RETURN');
6461

6562
// Drag out doSomething caller from flyout.
66-
const doSomethingFlyout = await getNthBlockOfCategory(
63+
const doSomethingCaller = await dragNthBlockFromFlyout(
6764
this.browser,
6865
'Functions',
6966
3,
67+
50,
68+
20,
7069
);
71-
await doSomethingFlyout.dragAndDrop({x: 50, y: 20});
72-
const doSomethingCaller = await getSelectedBlockElement(this.browser);
7370

7471
// Connect the doSomething caller to doSomething2
7572
await connect(
@@ -81,22 +78,22 @@ suite('Testing Connecting Blocks', function (done) {
8178
);
8279

8380
// Drag out print from flyout.
84-
const printFlyout = await getBlockTypeFromCategory(
81+
const print = await dragBlockTypeFromFlyout(
8582
this.browser,
8683
'Text',
8784
'text_print',
85+
50,
86+
0,
8887
);
89-
await printFlyout.dragAndDrop({x: 50, y: 20});
90-
const print = await getSelectedBlockElement(this.browser);
9188

9289
// Drag out doSomething2 caller from flyout.
93-
const doSomething2Flyout = await getNthBlockOfCategory(
90+
const doSomething2Caller = await dragNthBlockFromFlyout(
9491
this.browser,
9592
'Functions',
9693
4,
94+
50,
95+
20,
9796
);
98-
await doSomething2Flyout.dragAndDrop({x: 130, y: 20});
99-
const doSomething2Caller = await getSelectedBlockElement(this.browser);
10097

10198
// Connect doSomething2 caller with print.
10299
await connect(this.browser, doSomething2Caller, 'OUTPUT', print, 'TEXT');

tests/browser/test/test_setup.mjs

Lines changed: 57 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ export async function driverSetup() {
6262
// Use Selenium to bring up the page
6363
console.log('Starting webdriverio...');
6464
driver = await webdriverio.remote(options);
65+
driver.setWindowSize(800, 600);
66+
driver.setViewport({width: 800, height: 600});
6567
return driver;
6668
}
6769

@@ -170,43 +172,52 @@ export async function getBlockElementById(browser, id) {
170172
* @return A Promise that resolves when the actions are completed.
171173
*/
172174
export async function clickBlock(browser, blockId, clickOptions) {
173-
const findableId = 'clickTargetElement';
174175
// In the browser context, find the element that we want and give it a findable ID.
175-
await browser.execute(
176-
(blockId, newElemId) => {
177-
const block = Blockly.getMainWorkspace().getBlockById(blockId);
178-
// Ensure the block we want to click is within the viewport.
179-
Blockly.getMainWorkspace().scrollBoundsIntoView(
180-
block.getBoundingRectangleWithoutChildren(),
181-
10,
182-
);
176+
const elem = await getTargetableBlockElement(browser, blockId, false);
177+
await elem.click(clickOptions);
178+
}
179+
180+
/**
181+
* Find an element on the block that is suitable for a click or drag.
182+
*
183+
* We can't always use the block's SVG root because clicking will always happen
184+
* in the middle of the block's bounds (including children) by default, which
185+
* causes problems if it has holes (e.g. statement inputs). Instead, this tries
186+
* to get the first text field on the block. It falls back on the block's SVG root.
187+
* @param browser The active WebdriverIO Browser object.
188+
* @param blockId The id of the block to click, as an interactable element.
189+
* @param toolbox True if this block is in the toolbox (which must be open already).
190+
* @return A Promise that returns an appropriate element.
191+
*/
192+
async function getTargetableBlockElement(browser, blockId, toolbox) {
193+
const id = await browser.execute(
194+
(blockId, toolbox, newElemId) => {
195+
const ws = toolbox
196+
? Blockly.getMainWorkspace().getFlyout().getWorkspace()
197+
: Blockly.getMainWorkspace();
198+
const block = ws.getBlockById(blockId);
199+
// Ensure the block we want to click/drag is within the viewport.
200+
ws.scrollBoundsIntoView(block.getBoundingRectangleWithoutChildren(), 10);
183201
if (!block.isCollapsed()) {
184202
for (const input of block.inputList) {
185203
for (const field of input.fieldRow) {
186204
if (field instanceof Blockly.FieldLabel) {
187-
field.getSvgRoot().id = newElemId;
188-
return;
205+
// Expose the id of the element we want to target
206+
field.getSvgRoot().setAttribute('data-id', field.id_);
207+
return field.getSvgRoot().id;
189208
}
190209
}
191210
}
192211
}
193-
// No label field found. Fall back to the block's SVG root.
194-
block.getSvgRoot().id = newElemId;
212+
// No label field found. Fall back to the block's SVG root, which should
213+
// already use the block id.
214+
return block.id;
195215
},
196216
blockId,
197-
findableId,
217+
toolbox,
198218
);
199219

200-
// In the test context, get the Webdriverio Element that we've identified.
201-
const elem = await browser.$(`#${findableId}`);
202-
203-
await elem.click(clickOptions);
204-
205-
// In the browser context, remove the ID.
206-
await browser.execute((elemId) => {
207-
const clickElem = document.getElementById(elemId);
208-
clickElem.removeAttribute('id');
209-
}, findableId);
220+
return await getBlockElementById(browser, id);
210221
}
211222

212223
/**
@@ -215,7 +226,7 @@ export async function clickBlock(browser, blockId, clickOptions) {
215226
* @return A Promise that resolves when the actions are completed.
216227
*/
217228
export async function clickWorkspace(browser) {
218-
const workspace = await browser.$('#blocklyDiv > div > svg.blocklySvg > g');
229+
const workspace = await browser.$('svg.blocklySvg > g');
219230
await workspace.click();
220231
await browser.pause(PAUSE_TIME);
221232
}
@@ -253,27 +264,14 @@ export async function getCategory(browser, categoryName) {
253264
}
254265

255266
/**
256-
* @param browser The active WebdriverIO Browser object.
257-
* @param categoryName The name of the toolbox category to search.
258-
* @param n Which block to select, 0-indexed from the top of the category.
259-
* @return A Promise that resolves to the root element of the nth
260-
* block in the given category.
261-
*/
262-
export async function getNthBlockOfCategory(browser, categoryName, n) {
263-
const category = await getCategory(browser, categoryName);
264-
await category.click();
265-
const block = (
266-
await browser.$$(`.blocklyFlyout .blocklyBlockCanvas > .blocklyDraggable`)
267-
)[n];
268-
return block;
269-
}
270-
271-
/**
267+
* Opens the specified category, finds the first block of the given type,
268+
* scrolls it into view, and returns a draggable element on that block.
269+
*
272270
* @param browser The active WebdriverIO Browser object.
273271
* @param categoryName The name of the toolbox category to search.
274272
* Null if the toolbox has no categories (simple).
275273
* @param blockType The type of the block to search for.
276-
* @return A Promise that resolves to the root element of the first
274+
* @return A Promise that resolves to a draggable element of the first
277275
* block with the given type in the given category.
278276
*/
279277
export async function getBlockTypeFromCategory(
@@ -286,13 +284,14 @@ export async function getBlockTypeFromCategory(
286284
await category.click();
287285
}
288286

287+
await browser.pause(PAUSE_TIME);
289288
const id = await browser.execute((blockType) => {
290-
return Blockly.getMainWorkspace()
291-
.getFlyout()
292-
.getWorkspace()
293-
.getBlocksByType(blockType)[0].id;
289+
const ws = Blockly.getMainWorkspace().getFlyout().getWorkspace();
290+
const block = ws.getBlocksByType(blockType)[0];
291+
ws.scrollBoundsIntoView(block.getBoundingRectangleWithoutChildren());
292+
return block.id;
294293
}, blockType);
295-
return getBlockElementById(browser, id);
294+
return getTargetableBlockElement(browser, id, true);
296295
}
297296

298297
/**
@@ -447,7 +446,16 @@ export async function switchRTL(browser) {
447446
* created block.
448447
*/
449448
export async function dragNthBlockFromFlyout(browser, categoryName, n, x, y) {
450-
const flyoutBlock = await getNthBlockOfCategory(browser, categoryName, n);
449+
const category = await getCategory(browser, categoryName);
450+
await category.click();
451+
452+
await browser.pause(PAUSE_TIME);
453+
const id = await browser.execute((n) => {
454+
const ws = Blockly.getMainWorkspace().getFlyout().getWorkspace();
455+
const block = ws.getTopBlocks(true)[n];
456+
return block.id;
457+
}, n);
458+
const flyoutBlock = await getTargetableBlockElement(browser, id, true);
451459
await flyoutBlock.dragAndDrop({x: x, y: y});
452460
return await getSelectedBlockElement(browser);
453461
}
@@ -480,6 +488,7 @@ export async function dragBlockTypeFromFlyout(
480488
type,
481489
);
482490
await flyoutBlock.dragAndDrop({x: x, y: y});
491+
await browser.pause(PAUSE_TIME);
483492
return await getSelectedBlockElement(browser);
484493
}
485494

@@ -584,26 +593,3 @@ export async function getAllBlocks(browser) {
584593
}));
585594
});
586595
}
587-
588-
/**
589-
* Find the flyout's scrollbar and scroll by the specified amount.
590-
* This makes several assumptions:
591-
* - A flyout with a valid scrollbar exists, is open, and is in view.
592-
* - The workspace has a trash can, which means it has a second (hidden) flyout.
593-
* @param browser The active WebdriverIO Browser object.
594-
* @param xDelta How far to drag the flyout in the x direction. Positive is right.
595-
* @param yDelta How far to drag the flyout in the y direction. Positive is down.
596-
* @return A Promise that resolves when the actions are completed.
597-
*/
598-
export async function scrollFlyout(browser, xDelta, yDelta) {
599-
// There are two flyouts on the playground workspace: one for the trash can
600-
// and one for the toolbox. We want the second one.
601-
// This assumes there is only one scrollbar handle in the flyout, but it could
602-
// be either horizontal or vertical.
603-
await browser.pause(PAUSE_TIME);
604-
const scrollbarHandle = await browser
605-
.$$(`.blocklyFlyoutScrollbar`)[1]
606-
.$(`rect.blocklyScrollbarHandle`);
607-
await scrollbarHandle.dragAndDrop({x: xDelta, y: yDelta});
608-
await browser.pause(PAUSE_TIME);
609-
}

0 commit comments

Comments
 (0)