mirror of
https://github.com/google/blockly.git
synced 2025-12-16 06:10:12 +01:00
chore(tests): cleanup and toolbox drag tests (#7350)
* chore(tests): use helpers for the basic drag test in the playground * chore(tests): miscellaneous test cleanup * chore: format * feat(tests): add test that drags out every block from the toolbox * feat(tests): add RTL version of toolbox drag tests * chore: lint * chore(tests): respond to PR feedback
This commit is contained in:
@@ -46,24 +46,13 @@ suite('Testing Connecting Blocks', function () {
|
||||
this.browser = await testSetup(testFileLocations.PLAYGROUND);
|
||||
});
|
||||
|
||||
test('Testing Block Flyout', async function () {
|
||||
const logicButton = await this.browser.$('#blockly-0');
|
||||
logicButton.click();
|
||||
const ifDoBlock = await this.browser.$(
|
||||
'#blocklyDiv > div > svg:nth-child(7) > g > g.blocklyBlockCanvas > g:nth-child(3)',
|
||||
);
|
||||
await ifDoBlock.dragAndDrop({x: 20, y: 20});
|
||||
await this.browser.pause(200);
|
||||
const blockOnWorkspace = await this.browser.execute(() => {
|
||||
const newBlock = Blockly.getMainWorkspace().getAllBlocks(false)[0];
|
||||
if (newBlock.id) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
test('dragging a block from the flyout results in a block on the workspace', async function () {
|
||||
await dragBlockTypeFromFlyout(this.browser, 'Logic', 'controls_if', 20, 20);
|
||||
const blockCount = await this.browser.execute(() => {
|
||||
return Blockly.getMainWorkspace().getAllBlocks(false).length;
|
||||
});
|
||||
|
||||
chai.assert.isTrue(blockOnWorkspace);
|
||||
chai.assert.equal(blockCount, 1);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -13,45 +13,38 @@ const {Key} = require('webdriverio');
|
||||
const {
|
||||
testSetup,
|
||||
testFileLocations,
|
||||
switchRTL,
|
||||
dragBlockTypeFromFlyout,
|
||||
screenDirection,
|
||||
getAllBlocks,
|
||||
} = require('./test_setup');
|
||||
|
||||
suite('Testing undo block movement', 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 () {
|
||||
this.browser = await testSetup(testFileLocations.PLAYGROUND);
|
||||
});
|
||||
|
||||
test('Undoing Block Movement LTR', async function () {
|
||||
this.browser = await testSetup(testFileLocations.PLAYGROUND);
|
||||
await testUndoBlock(this.browser, screenDirection.LTR);
|
||||
});
|
||||
|
||||
test('Undoing Block Movement RTL', async function () {
|
||||
await switchRTL(this.browser);
|
||||
this.browser = await testSetup(testFileLocations.PLAYGROUND_RTL);
|
||||
await testUndoBlock(this.browser, screenDirection.RTL);
|
||||
});
|
||||
});
|
||||
|
||||
async function testUndoBlock(browser, delta) {
|
||||
async function testUndoBlock(browser, direction) {
|
||||
// Drag out first function
|
||||
const defReturnBlock = await dragBlockTypeFromFlyout(
|
||||
await dragBlockTypeFromFlyout(
|
||||
browser,
|
||||
'Functions',
|
||||
'procedures_defreturn',
|
||||
50 * delta,
|
||||
50 * direction,
|
||||
20,
|
||||
);
|
||||
|
||||
await browser.keys([Key.Ctrl, 'z']);
|
||||
|
||||
const blockOnWorkspace = await browser.execute(() => {
|
||||
return !!Blockly.getMainWorkspace().getAllBlocks(false)[0];
|
||||
});
|
||||
|
||||
chai.assert.isFalse(blockOnWorkspace);
|
||||
const allBlocks = await getAllBlocks(browser);
|
||||
chai.assert.equal(allBlocks.length, 0);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ const {
|
||||
testSetup,
|
||||
testFileLocations,
|
||||
getBlockElementById,
|
||||
getAllBlocks,
|
||||
} = require('./test_setup');
|
||||
const {Key} = require('webdriverio');
|
||||
|
||||
@@ -25,12 +26,12 @@ suite('This tests loading Large Configuration and Deletion', function (done) {
|
||||
this.browser = await testSetup(testFileLocations.PLAYGROUND);
|
||||
});
|
||||
|
||||
test('This test loading from JSON results in the correct number of blocks', async function () {
|
||||
test('loading from JSON results in the correct number of blocks', async function () {
|
||||
const blockNum = await testingJSONLoad(this.browser);
|
||||
chai.assert.equal(blockNum, 13);
|
||||
});
|
||||
|
||||
test('This test deleting block results in the correct number of blocks', async function () {
|
||||
test('deleting block results in the correct number of blocks', async function () {
|
||||
const fourthRepeatDo = await getBlockElementById(
|
||||
this.browser,
|
||||
'E8bF[-r:B~cabGLP#QYd',
|
||||
@@ -38,19 +39,15 @@ suite('This tests loading Large Configuration and Deletion', function (done) {
|
||||
await fourthRepeatDo.click({x: -100, y: -40});
|
||||
await this.browser.keys([Key.Delete]);
|
||||
await this.browser.pause(100);
|
||||
const blockNum = await this.browser.execute(() => {
|
||||
return Blockly.getMainWorkspace().getAllBlocks(false).length;
|
||||
});
|
||||
chai.assert.equal(blockNum, 10);
|
||||
const allBlocks = await getAllBlocks(this.browser);
|
||||
chai.assert.equal(allBlocks.length, 10);
|
||||
});
|
||||
|
||||
test('This test undoing delete block results in the correct number of blocks', async function () {
|
||||
test('undoing delete block results in the correct number of blocks', async function () {
|
||||
await this.browser.keys([Key.Ctrl, 'z']);
|
||||
await this.browser.pause(100);
|
||||
const blockNum = await this.browser.execute(() => {
|
||||
return Blockly.getMainWorkspace().getAllBlocks(false).length;
|
||||
});
|
||||
chai.assert.equal(blockNum, 13);
|
||||
const allBlocks = await getAllBlocks(this.browser);
|
||||
chai.assert.equal(allBlocks.length, 13);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ const chai = require('chai');
|
||||
const {
|
||||
testSetup,
|
||||
testFileLocations,
|
||||
getSelectedBlockElement,
|
||||
switchRTL,
|
||||
dragBlockTypeFromFlyout,
|
||||
screenDirection,
|
||||
} = require('./test_setup');
|
||||
@@ -23,47 +21,42 @@ suite('Testing Field Edits', 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 () {
|
||||
this.browser = await testSetup(testFileLocations.PLAYGROUND);
|
||||
});
|
||||
|
||||
test('Testing Field Edits LTR', async function () {
|
||||
this.browser = await testSetup(testFileLocations.PLAYGROUND);
|
||||
await testFieldEdits(this.browser, screenDirection.LTR);
|
||||
});
|
||||
|
||||
test('Testing Field Edits RTL', async function () {
|
||||
await switchRTL(this.browser);
|
||||
this.browser = await testSetup(testFileLocations.PLAYGROUND_RTL);
|
||||
await testFieldEdits(this.browser, screenDirection.RTL);
|
||||
});
|
||||
});
|
||||
|
||||
async function testFieldEdits(browser, delta) {
|
||||
const mathNumber = await dragBlockTypeFromFlyout(
|
||||
async function testFieldEdits(browser, direction) {
|
||||
const numberBlock = await dragBlockTypeFromFlyout(
|
||||
browser,
|
||||
'Math',
|
||||
'math_number',
|
||||
50 * delta,
|
||||
50 * direction,
|
||||
20,
|
||||
);
|
||||
await browser.pause(200);
|
||||
|
||||
// Click on the field to change the value
|
||||
const numeric = await getSelectedBlockElement(browser);
|
||||
await numeric.doubleClick();
|
||||
await numberBlock.click();
|
||||
await browser.keys([Key.Delete]);
|
||||
await numeric.doubleClick();
|
||||
await numberBlock.click();
|
||||
await browser.keys(['1093']);
|
||||
// Click on the workspace
|
||||
// Click on the workspace to exit the field editor
|
||||
const workspace = await browser.$('#blocklyDiv > div > svg.blocklySvg > g');
|
||||
await workspace.click();
|
||||
await browser.pause(200);
|
||||
// Get value of the number
|
||||
const numericText = await browser
|
||||
.$(
|
||||
'#blocklyDiv > div > svg.blocklySvg > g > g.blocklyBlockCanvas > g.blocklyDraggable > g > text',
|
||||
)
|
||||
.getHTML();
|
||||
|
||||
chai.assert.isTrue(numericText.includes('1093'));
|
||||
const fieldValue = await browser.execute((id) => {
|
||||
return Blockly.getMainWorkspace()
|
||||
.getBlockById(id)
|
||||
.getField('NUM')
|
||||
.getValue();
|
||||
}, numberBlock.id);
|
||||
|
||||
chai.assert.equal(fieldValue, '1093');
|
||||
}
|
||||
|
||||
@@ -94,6 +94,10 @@ const testFileLocations = {
|
||||
'file://' +
|
||||
posixPath(path.join(__dirname, '..', '..')) +
|
||||
'/playground.html',
|
||||
PLAYGROUND_RTL:
|
||||
'file://' +
|
||||
posixPath(path.join(__dirname, '..', '..')) +
|
||||
'/playground.html?dir=rtl',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -164,7 +168,6 @@ async function getCategory(browser, categoryName) {
|
||||
async function getNthBlockOfCategory(browser, categoryName, n) {
|
||||
const category = await getCategory(browser, categoryName);
|
||||
await category.click();
|
||||
await browser.pause(100);
|
||||
const block = await browser.$(
|
||||
`.blocklyFlyout .blocklyBlockCanvas > g:nth-child(${3 + n * 2})`,
|
||||
);
|
||||
@@ -440,6 +443,29 @@ async function getAllBlocks(browser) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the flyout's scrollbar and scroll by the specified amount.
|
||||
* This makes several assumptions:
|
||||
* - A flyout with a valid scrollbar exists, is open, and is in view.
|
||||
* - The workspace has a trash can, which means it has a second (hidden) flyout.
|
||||
* @param browser The active WebdriverIO Browser object.
|
||||
* @param xDelta How far to drag the flyout in the x direction. Positive is right.
|
||||
* @param yDelta How far to drag thte flyout in the y direction. Positive is down.
|
||||
* @return A Promise that resolves when the actions are completed.
|
||||
*/
|
||||
async function scrollFlyout(browser, xDelta, yDelta) {
|
||||
// There are two flyouts on the playground workspace: one for the trash can
|
||||
// and one for the toolbox. We want the second one.
|
||||
// This assumes there is only one scrollbar handle in the flyout, but it could
|
||||
// be either horizontal or vertical.
|
||||
await browser.pause(50);
|
||||
const scrollbarHandle = await browser
|
||||
.$$(`.blocklyFlyoutScrollbar`)[1]
|
||||
.$(`rect.blocklyScrollbarHandle`);
|
||||
await scrollbarHandle.dragAndDrop({x: xDelta, y: yDelta});
|
||||
await browser.pause(50);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
testSetup,
|
||||
testFileLocations,
|
||||
@@ -459,4 +485,5 @@ module.exports = {
|
||||
screenDirection,
|
||||
getBlockTypeFromWorkspace,
|
||||
getAllBlocks,
|
||||
scrollFlyout,
|
||||
};
|
||||
|
||||
208
tests/browser/test/toolbox_drag_test.js
Normal file
208
tests/browser/test/toolbox_drag_test.js
Normal file
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2023 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for the dragging out of the toolbox and flyout.
|
||||
*/
|
||||
|
||||
const chai = require('chai');
|
||||
const {
|
||||
testSetup,
|
||||
testFileLocations,
|
||||
getCategory,
|
||||
scrollFlyout,
|
||||
screenDirection,
|
||||
} = require('./test_setup');
|
||||
|
||||
const PAUSE_TIME = 50;
|
||||
|
||||
// Categories in the basic toolbox.
|
||||
const basicCategories = [
|
||||
'Logic',
|
||||
'Loops',
|
||||
'Math',
|
||||
'Text',
|
||||
'Lists',
|
||||
'Colour',
|
||||
'Variables',
|
||||
'Functions',
|
||||
];
|
||||
|
||||
// Categories in the test blocks toolbox.
|
||||
const testCategories = [
|
||||
'Align',
|
||||
'Basic',
|
||||
// Skip connections because it's an accordion that is already open.
|
||||
// 'Connections',
|
||||
'Row',
|
||||
'Stack',
|
||||
'Statement',
|
||||
'Drag',
|
||||
// Skip fields because it's an accordion that is already open.
|
||||
// 'Fields',
|
||||
'Defaults',
|
||||
'Numbers',
|
||||
'Angles',
|
||||
'Drop-downs',
|
||||
// Note: images has a block that has a bad image source, but still builds and renders
|
||||
// just fine.
|
||||
'Images',
|
||||
'Emoji! o((*^ᴗ^*))o',
|
||||
'Validators',
|
||||
'Mutators',
|
||||
'Style',
|
||||
'Serialization',
|
||||
];
|
||||
|
||||
/**
|
||||
* Check whether an element is fully inside the bounds of the Blockly div. You can use this
|
||||
* to determine whether a block on the workspace or flyout is inside the Blockly div.
|
||||
* This does not check whether there are other Blockly elements (such as a toolbox or
|
||||
* flyout) on top of the element. A partially visible block is considered out of bounds.
|
||||
* @param browser The active WebdriverIO Browser object.
|
||||
* @param element The element to look for.
|
||||
* @returns A Promise resolving to true if the element is in bounds and false otherwise.
|
||||
*/
|
||||
async function elementInBounds(browser, element) {
|
||||
return await browser.execute((elem) => {
|
||||
const rect = elem.getBoundingClientRect();
|
||||
|
||||
const blocklyDiv = document.getElementById('blocklyDiv');
|
||||
const blocklyRect = blocklyDiv.getBoundingClientRect();
|
||||
|
||||
const vertInView =
|
||||
rect.top >= blocklyRect.top && rect.bottom <= blocklyRect.bottom;
|
||||
const horInView =
|
||||
rect.left >= blocklyRect.left && rect.right <= blocklyRect.right;
|
||||
|
||||
return vertInView && horInView;
|
||||
}, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get how many top-level blocks there are in the specified category.
|
||||
* @param browser The active WebdriverIO Browser object.
|
||||
* @param categoryName The name of the category to inspect.
|
||||
* @returns A Promise resolving to the number of top-level blocks in the specified
|
||||
* category's flyout.
|
||||
*/
|
||||
async function getBlockCount(browser, categoryName) {
|
||||
const category = await getCategory(browser, categoryName);
|
||||
await category.click();
|
||||
await browser.pause(PAUSE_TIME);
|
||||
|
||||
const blockCount = await browser.execute(() => {
|
||||
return Blockly.getMainWorkspace()
|
||||
.getFlyout()
|
||||
.getWorkspace()
|
||||
.getTopBlocks(false).length;
|
||||
});
|
||||
|
||||
// Unicode escape to close flyout.
|
||||
await browser.keys(['\uE00C']);
|
||||
await browser.pause(PAUSE_TIME);
|
||||
return blockCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the block at the given index in the flyout is disabled.
|
||||
* @param browser The active WebdriverIO Browser object.
|
||||
* @param i The index of the block in the currently open flyout.
|
||||
* @returns A Promise resolving to true if the ith block in the flyout is
|
||||
* disabled, and false otherwise.
|
||||
*/
|
||||
async function isBlockDisabled(browser, i) {
|
||||
return await browser.execute((n) => {
|
||||
return !Blockly.getMainWorkspace()
|
||||
.getFlyout()
|
||||
.getWorkspace()
|
||||
.getTopBlocks()
|
||||
[n].isEnabled();
|
||||
}, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop over a list of categories and click on each one to open it.
|
||||
* @param browser The WebdriverIO Browser instance for this test.
|
||||
* @param categoryList An array of category names, as strings.
|
||||
* @param directionMultiplier 1 for LTR and -1 for RTL.
|
||||
* @returns A Promise that resolves when all actions have finished.
|
||||
*/
|
||||
async function openCategories(browser, categoryList, directionMultiplier) {
|
||||
let failureCount = 0;
|
||||
for (const categoryName of categoryList) {
|
||||
const blockCount = await getBlockCount(browser, categoryName);
|
||||
|
||||
try {
|
||||
for (let i = 0; i < blockCount; i++) {
|
||||
const category = await getCategory(browser, categoryName);
|
||||
await category.click();
|
||||
if (await isBlockDisabled(browser, i)) {
|
||||
// Unicode escape to close flyout.
|
||||
await browser.keys(['\uE00C']);
|
||||
await browser.pause(PAUSE_TIME);
|
||||
} else {
|
||||
const flyoutBlock = await browser.$(
|
||||
`.blocklyFlyout .blocklyBlockCanvas > g:nth-child(${3 + i * 2})`,
|
||||
);
|
||||
if (!(await elementInBounds(browser, flyoutBlock))) {
|
||||
await scrollFlyout(browser, 0, 500);
|
||||
}
|
||||
|
||||
await flyoutBlock.dragAndDrop({x: directionMultiplier * 50, y: 0});
|
||||
await browser.pause(PAUSE_TIME);
|
||||
// Should be one top level block on the workspace.
|
||||
const topBlockCount = await browser.execute(() => {
|
||||
return Blockly.getMainWorkspace().getTopBlocks(false).length;
|
||||
});
|
||||
|
||||
if (topBlockCount != 1) {
|
||||
failureCount++;
|
||||
console.log(`fail: block ${i} in category ${categoryName}`);
|
||||
}
|
||||
|
||||
// Clean up between blocks so they can't interact with each other.
|
||||
await browser.execute(() => {
|
||||
Blockly.getMainWorkspace().clear();
|
||||
});
|
||||
await browser.pause(PAUSE_TIME);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
failureCount++;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
chai.assert.equal(failureCount, 0);
|
||||
}
|
||||
|
||||
suite.skip('Open toolbox categories', function () {
|
||||
this.timeout(0);
|
||||
|
||||
test('opening every toolbox category in the category toolbox in LTR', async function () {
|
||||
this.browser = await testSetup(testFileLocations.PLAYGROUND);
|
||||
await openCategories(this.browser, basicCategories, screenDirection.LTR);
|
||||
});
|
||||
|
||||
test('opening every toolbox category in the category toolbox in RTL', async function () {
|
||||
this.browser = await testSetup(testFileLocations.PLAYGROUND_RTL);
|
||||
await openCategories(this.browser, basicCategories, screenDirection.RTL);
|
||||
});
|
||||
|
||||
test('opening every toolbox category in the test toolbox in LTR', async function () {
|
||||
this.browser = await testSetup(
|
||||
testFileLocations.PLAYGROUND + '?toolbox=test-blocks',
|
||||
);
|
||||
await openCategories(this.browser, testCategories, screenDirection.LTR);
|
||||
});
|
||||
|
||||
test('opening every toolbox category in the test toolbox in RTL', async function () {
|
||||
this.browser = await testSetup(
|
||||
testFileLocations.PLAYGROUND + '?toolbox=test-blocks&dir=rtl',
|
||||
);
|
||||
await openCategories(this.browser, testCategories, screenDirection.RTL);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user