From 82a775a982c8bd3f7d2ffdb4b508b75d9c2c44b8 Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Fri, 14 Jul 2023 15:51:38 +0100 Subject: [PATCH] test: Basic Blocks: verify drag blocks works properly (#7291) * refactor(tests): Have testSetup accept a URL Have testSetup accept a URL. Make testFileLocations a dictionary whose values are URLs, and change the spellings of the keys to be CONSTANT_CASE. This allows tests to specify their own URL, or to add a query or fragment. * feat(tests): Add getAllBlocks helper * test: Verify drag blocks works properly * chore(tests): Reformat comments Manually reformat comments to comply with styleguide. --- .../browser/test/basic_block_factory_test.js | 2 +- tests/browser/test/basic_block_test.js | 55 +++++++++ tests/browser/test/basic_playground_test.js | 4 +- tests/browser/test/block_undo_test.js | 2 +- tests/browser/test/extensive_test.js | 2 +- tests/browser/test/field_edits_test.js | 2 +- tests/browser/test/procedure_test.js | 2 +- tests/browser/test/test_setup.js | 113 +++++++++++------- 8 files changed, 130 insertions(+), 52 deletions(-) create mode 100644 tests/browser/test/basic_block_test.js diff --git a/tests/browser/test/basic_block_factory_test.js b/tests/browser/test/basic_block_factory_test.js index cd9f09800..def7ef0ef 100644 --- a/tests/browser/test/basic_block_factory_test.js +++ b/tests/browser/test/basic_block_factory_test.js @@ -18,7 +18,7 @@ suite('Testing Connecting Blocks', function (done) { // Setup Selenium for all of the tests suiteSetup(async function () { - browser = await testSetup(testFileLocations.blockfactory); + browser = await testSetup(testFileLocations.BLOCK_FACTORY); }); test('Testing Block Drag', async function () { diff --git a/tests/browser/test/basic_block_test.js b/tests/browser/test/basic_block_test.js new file mode 100644 index 000000000..4a2a3ea20 --- /dev/null +++ b/tests/browser/test/basic_block_test.js @@ -0,0 +1,55 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Node.js script to run Automated tests in Chrome, via + * webdriver, of basic Blockly block functionality. + */ + +const chai = require('chai'); +const { + testSetup, + testFileLocations, + getAllBlocks, + getSelectedBlockElement, + switchRTL, + dragBlockTypeFromFlyout, + screenDirection, +} = require('./test_setup'); +const {Key} = require('webdriverio'); + +let browser; + +suite('Basic block tests', function (done) { + // Setting timeout to unlimited as the webdriver takes a longer time + // to run than most mocha test + this.timeout(0); + + // Setup Selenium for all of the tests + suiteSetup(async function () { + browser = await testSetup( + testFileLocations.PLAYGROUND + '?toolbox=test-blocks' + ); + }); + + test('Drag three blocks into the workspace', async function () { + for (let i = 1; i <= 3; i++) { + await dragBlockTypeFromFlyout( + browser, + 'Basic', + 'test_basic_empty', + 250, + 50 * i + ); + chai.assert.equal((await getAllBlocks(browser)).length, i); + } + }); + + // Teardown entire suite after test are done running + suiteTeardown(async function () { + await browser.deleteSession(); + }); +}); diff --git a/tests/browser/test/basic_playground_test.js b/tests/browser/test/basic_playground_test.js index dd9354929..7f9fb3a70 100644 --- a/tests/browser/test/basic_playground_test.js +++ b/tests/browser/test/basic_playground_test.js @@ -41,7 +41,7 @@ suite('Testing Connecting Blocks', function (done) { // Setup Selenium for all of the tests suiteSetup(async function () { - browser = await testSetup(testFileLocations.playground); + browser = await testSetup(testFileLocations.PLAYGROUND); }); test('Testing Block Flyout', async function () { @@ -80,7 +80,7 @@ suite('Right Clicking on Blocks', function (done) { // Setup Selenium for all of the tests suiteSetup(async function () { - browser = await testSetup(testFileLocations.playground); + browser = await testSetup(testFileLocations.PLAYGROUND); this.block = await dragNthBlockFromFlyout(browser, 'Loops', 0, 20, 20); this.blockId = this.block.id; }); diff --git a/tests/browser/test/block_undo_test.js b/tests/browser/test/block_undo_test.js index d11c95c53..1acfbd6f9 100644 --- a/tests/browser/test/block_undo_test.js +++ b/tests/browser/test/block_undo_test.js @@ -25,7 +25,7 @@ suite('Testing undo block movement', function (done) { // Setup Selenium for all of the tests suiteSetup(async function () { - browser = await testSetup(testFileLocations.playground); + browser = await testSetup(testFileLocations.PLAYGROUND); }); test('Undoing Block Movement LTR', async function () { diff --git a/tests/browser/test/extensive_test.js b/tests/browser/test/extensive_test.js index edfd5d3b9..015877400 100644 --- a/tests/browser/test/extensive_test.js +++ b/tests/browser/test/extensive_test.js @@ -23,7 +23,7 @@ suite('This tests loading Large Configuration and Deletion', function (done) { // Setup Selenium for all of the tests suiteSetup(async function () { - browser = await testSetup(testFileLocations.playground); + browser = await testSetup(testFileLocations.PLAYGROUND); }); test('This test loading from JSON results in the correct number of blocks', async function () { diff --git a/tests/browser/test/field_edits_test.js b/tests/browser/test/field_edits_test.js index b7a25c75b..5a88b7b8f 100644 --- a/tests/browser/test/field_edits_test.js +++ b/tests/browser/test/field_edits_test.js @@ -26,7 +26,7 @@ suite('Testing Field Edits', function (done) { // Setup Selenium for all of the tests suiteSetup(async function () { - browser = await testSetup(testFileLocations.playground); + browser = await testSetup(testFileLocations.PLAYGROUND); }); test('Testing Field Edits LTR', async function () { diff --git a/tests/browser/test/procedure_test.js b/tests/browser/test/procedure_test.js index e8890d988..0a675a836 100644 --- a/tests/browser/test/procedure_test.js +++ b/tests/browser/test/procedure_test.js @@ -26,7 +26,7 @@ suite('Testing Connecting Blocks', function (done) { // Setup Selenium for all of the tests suiteSetup(async function () { - browser = await testSetup(testFileLocations.code); + browser = await testSetup(testFileLocations.CODE_DEMO); }); test('Testing Procedure', async function () { diff --git a/tests/browser/test/test_setup.js b/tests/browser/test/test_setup.js index 588b2c52d..6797b47ac 100644 --- a/tests/browser/test/test_setup.js +++ b/tests/browser/test/test_setup.js @@ -5,8 +5,11 @@ */ /** - * @fileoverview Node.js script to run automated functional tests in Chrome, via webdriver. + * @fileoverview Node.js script to run automated functional tests in + * Chrome, via webdriver. + * * This file is to be used in the suiteSetup for any automated fuctional test. + * * Note: In this file many functions return browser elements that can * be clicked or otherwise interacted with through Selenium WebDriver. These * elements are not the raw HTML and SVG elements on the page; they are @@ -18,8 +21,7 @@ const path = require('path'); const {posixPath} = require('../../../scripts/helpers'); let browser; -async function testSetup(testFile) { - let url; +async function testSetup(url) { const options = { capabilities: { 'browserName': 'chrome', @@ -45,25 +47,6 @@ async function testSetup(testFile) { options.capabilities['goog:chromeOptions'].args.push('--disable-gpu'); } // Use Selenium to bring up the page - if (testFile == testFileLocations.blockfactory) { - url = - 'file://' + - posixPath( - path.join(__dirname, '..', '..', '..', 'demos', 'blockfactory') - ) + - '/index.html'; - } else if (testFile == testFileLocations.code) { - url = - 'file://' + - posixPath(path.join(__dirname, '..', '..', '..', 'demos', 'code')) + - '/index.html'; - } else { - url = - 'file://' + - posixPath(path.join(__dirname, '..', '..')) + - '/playground.html'; - } - console.log(url); console.log('Starting webdriverio...'); browser = await webdriverio.remote(options); console.log('Loading URL: ' + url); @@ -72,13 +55,23 @@ async function testSetup(testFile) { } const testFileLocations = { - blockfactory: 0, - code: 1, - playground: 2, + BLOCK_FACTORY: + 'file://' + + posixPath(path.join(__dirname, '..', '..', '..', 'demos', 'blockfactory')) + + '/index.html', + CODE_DEMO: + 'file://' + + posixPath(path.join(__dirname, '..', '..', '..', 'demos', 'code')) + + '/index.html', + PLAYGROUND: + 'file://' + + posixPath(path.join(__dirname, '..', '..')) + + '/playground.html', }; /** * Enum for both LTR and RTL use cases. + * * @readonly * @enum {number} */ @@ -146,8 +139,8 @@ async function getCategory(browser, categoryName) { * @param browser The active WebdriverIO Browser object. * @param categoryName The name of the toolbox category to search. * @param n Which block to select, 0-indexed from the top of the category. - * @return A Promise that resolves to the root element of the nth block in the - * given category. + * @return A Promise that resolves to the root element of the nth + * block in the given category. */ async function getNthBlockOfCategory(browser, categoryName, n) { const category = await getCategory(browser, categoryName); @@ -163,8 +156,8 @@ async function getNthBlockOfCategory(browser, categoryName, n) { * @param browser The active WebdriverIO Browser object. * @param categoryName The name of the toolbox category to search. * @param blockType The type of the block to search for. - * @return A Promise that resolves to the root element of the first block with the - * given type in the given category. + * @return A Promise that resolves to the root element of the first + * block with the given type in the given category. */ async function getBlockTypeFromCategory(browser, categoryName, blockType) { const category = await getCategory(browser, categoryName); @@ -203,10 +196,11 @@ async function getBlockTypeFromWorkspace(browser, blockType, position) { /** * @param browser The active WebdriverIO Browser object. * @param id The ID of the block the connection is on. - * @param connectionName Which connection to return. An input name - * to get a value or statement connection, and otherwise the type of the connection. - * @return A Promise that resolves to the location of the specific connection in screen - * coordinates. + * @param connectionName Which connection to return. An input name to + * get a value or statement connection, and otherwise the type of + * the connection. + * @return A Promise that resolves to the location of the specific + * connection in screen coordinates. */ async function getLocationOfBlockConnection(browser, id, connectionName) { return await browser.execute( @@ -233,6 +227,7 @@ async function getLocationOfBlockConnection(browser, id, connectionName) { block.getRelativeToSurfaceXY(), connection.getOffsetInBlock() ); + console.log(Blockly); return Blockly.utils.svgMath.wsToScreenCoordinates( Blockly.getMainWorkspace(), loc @@ -245,6 +240,7 @@ async function getLocationOfBlockConnection(browser, id, connectionName) { /** * Drags a block toward another block so that the specified connections attach. + * * @param browser The active WebdriverIO Browser object. * @param draggedBlock The block to drag. * @param draggedConnection The active connection on the block being dragged. @@ -279,6 +275,7 @@ async function connect( /** * Switch the playground to RTL mode. + * * @param browser The active WebdriverIO Browser object. * @return A Promise that resolves when the actions are completed. */ @@ -290,14 +287,16 @@ async function switchRTL(browser) { /** * Drag the specified block from the flyout and return the root element * of the block. + * * @param browser The active WebdriverIO Browser object. * @param categoryName The name of the toolbox category to search. * @param n Which block to select, indexed from the top of the category. - * @param x The x-distance to drag, as a delta from the block's initial location - * on screen. - * @param y The y-distance to drag, as a delta from the block's initial location - * on screen. - * @return A Promise that resolves to the root element of the newly created block. + * @param x The x-distance to drag, as a delta from the block's + * initial location on screen. + * @param y The y-distance to drag, as a delta from the block's + * initial location on screen. + * @return A Promise that resolves to the root element of the newly + * created block. */ async function dragNthBlockFromFlyout(browser, categoryName, n, x, y) { const flyoutBlock = await getNthBlockOfCategory(browser, categoryName, n); @@ -308,14 +307,16 @@ async function dragNthBlockFromFlyout(browser, categoryName, n, x, y) { /** * Drag the specified block from the flyout and return the root element * of the block. + * * @param browser The active WebdriverIO Browser object. * @param categoryName The name of the toolbox category to search. * @param type The type of the block to search for. - * @param x The x-distance to drag, as a delta from the block's initial location - * on screen. - * @param y The y-distance to drag, as a delta from the block's initial location - * on screen. - * @return A Promise that resolves to the root element of the newly created block. + * @param x The x-distance to drag, as a delta from the block's + * initial location on screen. + * @param y The y-distance to drag, as a delta from the block's + * initial location on screen. + * @return A Promise that resolves to the root element of the newly + * created block. */ async function dragBlockTypeFromFlyout(browser, categoryName, type, x, y) { const flyoutBlock = await getBlockTypeFromCategory( @@ -328,8 +329,9 @@ async function dragBlockTypeFromFlyout(browser, categoryName, type, x, y) { } /** - * Right-click on the specified block, then click on the specified context menu - * item. + * Right-click on the specified block, then click on the specified + * context menu item. + * * @param browser The active WebdriverIO Browser object. * @param block The block to click, as an interactable element. * @param itemText The display text of the context menu item to click. @@ -343,6 +345,26 @@ async function contextMenuSelect(browser, block, itemText) { await browser.pause(200); } +/** + * Get all blocks on the main workspace. Because the blocks have circular + * references that can't be JSON-encoded they can't be returned directly, so + * extract relevant properties only. + * + * @param browser The active WebdriverIO Browser object. + * @return A Promise that resolves to an array of blocks on the main workspace. + */ +async function getAllBlocks(browser) { + return browser.execute(() => { + // return Blockly.getMainWorkspace().getAllBlocks(false); + return Blockly.getMainWorkspace() + .getAllBlocks(false) + .map((block) => ({ + type: block.type, + id: block.id, + })); + }); +} + module.exports = { testSetup, testFileLocations, @@ -359,4 +381,5 @@ module.exports = { dragBlockTypeFromFlyout, screenDirection, getBlockTypeFromWorkspace, + getAllBlocks, };