Skip to content

Commit 274891d

Browse files
committed
Responses to comments
- Switch to using scrollBoundsIntoView instead of scrolling the flyout - Use webdriverio Key.Escape instead of the string code for it
1 parent b890e32 commit 274891d

File tree

2 files changed

+58
-155
lines changed

2 files changed

+58
-155
lines changed

tests/browser/test/test_setup.mjs

Lines changed: 54 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -172,43 +172,52 @@ export async function getBlockElementById(browser, id) {
172172
* @return A Promise that resolves when the actions are completed.
173173
*/
174174
export async function clickBlock(browser, blockId, clickOptions) {
175-
const findableId = 'clickTargetElement';
176175
// In the browser context, find the element that we want and give it a findable ID.
177-
await browser.execute(
178-
(blockId, newElemId) => {
179-
const block = Blockly.getMainWorkspace().getBlockById(blockId);
180-
// Ensure the block we want to click is within the viewport.
181-
Blockly.getMainWorkspace().scrollBoundsIntoView(
182-
block.getBoundingRectangleWithoutChildren(),
183-
10,
184-
);
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);
185201
if (!block.isCollapsed()) {
186202
for (const input of block.inputList) {
187203
for (const field of input.fieldRow) {
188204
if (field instanceof Blockly.FieldLabel) {
189-
field.getSvgRoot().id = newElemId;
190-
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;
191208
}
192209
}
193210
}
194211
}
195-
// No label field found. Fall back to the block's SVG root.
196-
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;
197215
},
198216
blockId,
199-
findableId,
217+
toolbox,
200218
);
201219

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

214223
/**
@@ -255,27 +264,14 @@ export async function getCategory(browser, categoryName) {
255264
}
256265

257266
/**
258-
* @param browser The active WebdriverIO Browser object.
259-
* @param categoryName The name of the toolbox category to search.
260-
* @param n Which block to select, 0-indexed from the top of the category.
261-
* @return A Promise that resolves to the root element of the nth
262-
* block in the given category.
263-
*/
264-
export async function getNthBlockOfCategory(browser, categoryName, n) {
265-
const category = await getCategory(browser, categoryName);
266-
await category.click();
267-
const block = (
268-
await browser.$$(`.blocklyFlyout .blocklyBlockCanvas > .blocklyDraggable`)
269-
)[n];
270-
return block;
271-
}
272-
273-
/**
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+
*
274270
* @param browser The active WebdriverIO Browser object.
275271
* @param categoryName The name of the toolbox category to search.
276272
* Null if the toolbox has no categories (simple).
277273
* @param blockType The type of the block to search for.
278-
* @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
279275
* block with the given type in the given category.
280276
*/
281277
export async function getBlockTypeFromCategory(
@@ -290,61 +286,12 @@ export async function getBlockTypeFromCategory(
290286

291287
await browser.pause(PAUSE_TIME);
292288
const id = await browser.execute((blockType) => {
293-
return Blockly.getMainWorkspace()
294-
.getFlyout()
295-
.getWorkspace()
296-
.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;
297293
}, blockType);
298-
return getBlockElementById(browser, id);
299-
}
300-
301-
/**
302-
* @param browser The active WebdriverIO Browser object.
303-
* @param categoryName The name of the toolbox category to search.
304-
* Null if the toolbox has no categories (simple).
305-
* @param blockType The type of the block to search for.
306-
* @return A Promise that resolves to a reasonable drag target element of the
307-
* first block with the given type in the given category.
308-
*/
309-
export async function getDraggableBlockElementByType(
310-
browser,
311-
categoryName,
312-
blockType,
313-
) {
314-
if (categoryName) {
315-
const category = await getCategory(browser, categoryName);
316-
await category.click();
317-
}
318-
319-
const findableId = 'dragTargetElement';
320-
// In the browser context, find the element that we want and give it a findable ID.
321-
await browser.execute(
322-
(blockType, newElemId) => {
323-
const block = Blockly.getMainWorkspace()
324-
.getFlyout()
325-
.getWorkspace()
326-
.getBlocksByType(blockType)[0];
327-
if (!block.isCollapsed()) {
328-
for (const input of block.inputList) {
329-
for (const field of input.fieldRow) {
330-
if (field instanceof Blockly.FieldLabel) {
331-
const svgRoot = field.getSvgRoot();
332-
if (svgRoot) {
333-
svgRoot.id = newElemId;
334-
return;
335-
}
336-
}
337-
}
338-
}
339-
}
340-
// No label field found. Fall back to the block's SVG root.
341-
block.getSvgRoot().id = newElemId;
342-
},
343-
blockType,
344-
findableId,
345-
);
346-
// In the test context, get the Webdriverio Element that we've identified.
347-
return await browser.$(`#${findableId}`);
294+
return getTargetableBlockElement(browser, id, true);
348295
}
349296

350297
/**
@@ -499,10 +446,16 @@ export async function switchRTL(browser) {
499446
* created block.
500447
*/
501448
export async function dragNthBlockFromFlyout(browser, categoryName, n, x, y) {
502-
const flyoutBlock = await getNthBlockOfCategory(browser, categoryName, n);
503-
while (!(await elementInBounds(browser, flyoutBlock))) {
504-
await scrollFlyout(browser, 0, 50);
505-
}
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);
506459
await flyoutBlock.dragAndDrop({x: x, y: y});
507460
return await getSelectedBlockElement(browser);
508461
}
@@ -529,44 +482,16 @@ export async function dragBlockTypeFromFlyout(
529482
x,
530483
y,
531484
) {
532-
const flyoutBlock = await getDraggableBlockElementByType(
485+
const flyoutBlock = await getBlockTypeFromCategory(
533486
browser,
534487
categoryName,
535488
type,
536489
);
537-
while (!(await elementInBounds(browser, flyoutBlock))) {
538-
await scrollFlyout(browser, 0, 50);
539-
}
540490
await flyoutBlock.dragAndDrop({x: x, y: y});
541491
await browser.pause(PAUSE_TIME);
542492
return await getSelectedBlockElement(browser);
543493
}
544494

545-
/**
546-
* Check whether an element is fully inside the bounds of the Blockly div. You can use this
547-
* to determine whether a block on the workspace or flyout is inside the Blockly div.
548-
* This does not check whether there are other Blockly elements (such as a toolbox or
549-
* flyout) on top of the element. A partially visible block is considered out of bounds.
550-
* @param browser The active WebdriverIO Browser object.
551-
* @param element The element to look for.
552-
* @returns A Promise resolving to true if the element is in bounds and false otherwise.
553-
*/
554-
async function elementInBounds(browser, element) {
555-
return await browser.execute((elem) => {
556-
const rect = elem.getBoundingClientRect();
557-
558-
const blocklyDiv = document.getElementsByClassName('blocklySvg')[0];
559-
const blocklyRect = blocklyDiv.getBoundingClientRect();
560-
561-
const vertInView =
562-
rect.top >= blocklyRect.top && rect.bottom <= blocklyRect.bottom;
563-
const horInView =
564-
rect.left >= blocklyRect.left && rect.right <= blocklyRect.right;
565-
566-
return vertInView && horInView;
567-
}, element);
568-
}
569-
570495
/**
571496
* Drags the specified block type from the mutator flyout of the given block
572497
* and returns the root element of the block.
@@ -667,27 +592,4 @@ export async function getAllBlocks(browser) {
667592
id: block.id,
668593
}));
669594
});
670-
}
671-
672-
/**
673-
* Find the flyout's scrollbar and scroll by the specified amount.
674-
* This makes several assumptions:
675-
* - A flyout with a valid scrollbar exists, is open, and is in view.
676-
* - The workspace has a trash can, which means it has a second (hidden) flyout.
677-
* @param browser The active WebdriverIO Browser object.
678-
* @param xDelta How far to drag the flyout in the x direction. Positive is right.
679-
* @param yDelta How far to drag the flyout in the y direction. Positive is down.
680-
* @return A Promise that resolves when the actions are completed.
681-
*/
682-
export async function scrollFlyout(browser, xDelta, yDelta) {
683-
// There are two flyouts on the playground workspace: one for the trash can
684-
// and one for the toolbox. We want the second one.
685-
// This assumes there is only one scrollbar handle in the flyout, but it could
686-
// be either horizontal or vertical.
687-
await browser.pause(PAUSE_TIME);
688-
const scrollbarHandle = await browser
689-
.$$(`.blocklyFlyoutScrollbar`)[1]
690-
.$(`rect.blocklyScrollbarHandle`);
691-
await scrollbarHandle.dragAndDrop({x: xDelta, y: yDelta});
692-
await browser.pause(PAUSE_TIME);
693-
}
595+
}

tests/browser/test/toolbox_drag_test.mjs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
testFileLocations,
1818
testSetup,
1919
} from './test_setup.mjs';
20+
import {Key} from 'webdriverio';
2021

2122
// Categories in the basic toolbox.
2223
const basicCategories = [
@@ -77,7 +78,7 @@ async function getNthBlockType(browser, categoryName, n) {
7778
}, n);
7879

7980
// Unicode escape to close flyout.
80-
await browser.keys(['\uE00C']);
81+
await browser.keys([Key.Escape]);
8182
await browser.pause(PAUSE_TIME);
8283
return blockType;
8384
}
@@ -102,7 +103,7 @@ async function getBlockCount(browser, categoryName) {
102103
});
103104

104105
// Unicode escape to close flyout.
105-
await browser.keys(['\uE00C']);
106+
await browser.keys([Key.Escape]);
106107
await browser.pause(PAUSE_TIME);
107108
return blockCount;
108109
}
@@ -142,7 +143,7 @@ async function openCategories(browser, categoryList, directionMultiplier) {
142143
await category.click();
143144
if (await isBlockDisabled(browser, i)) {
144145
// Unicode escape to close flyout.
145-
await browser.keys(['\uE00C']);
146+
await browser.keys([Key.Escape]);
146147
await browser.pause(PAUSE_TIME);
147148
continue;
148149
}

0 commit comments

Comments
 (0)