@@ -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 */
172174export 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 */
217228export 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 */
279277export 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 */
449448export 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