mirror of
https://github.com/google/blockly.git
synced 2026-01-09 01:50:11 +01:00
612 lines
18 KiB
JavaScript
612 lines
18 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import * as chai from 'chai';
|
|
import {Key} from 'webdriverio';
|
|
import {
|
|
PAUSE_TIME,
|
|
clickWorkspace,
|
|
focusOnBlock,
|
|
getAllBlocks,
|
|
getBlockTypeFromWorkspace,
|
|
getCategory,
|
|
getSelectedBlockId,
|
|
getSelectedBlockType,
|
|
openMutatorForBlock,
|
|
testFileLocations,
|
|
testSetup,
|
|
} from './test_setup.mjs';
|
|
|
|
const testBlockJson = {
|
|
'blocks': {
|
|
'languageVersion': 0,
|
|
'blocks': [
|
|
{
|
|
'type': 'controls_repeat_ext',
|
|
'id': 'controls_repeat_1',
|
|
'x': 88,
|
|
'y': 88,
|
|
'inputs': {
|
|
'TIMES': {
|
|
'shadow': {
|
|
'type': 'math_number',
|
|
'id': 'math_number_shadow_1',
|
|
'fields': {
|
|
'NUM': 10,
|
|
},
|
|
},
|
|
},
|
|
'DO': {
|
|
'block': {
|
|
'type': 'controls_if',
|
|
'id': 'controls_if_1',
|
|
'inputs': {
|
|
'IF0': {
|
|
'block': {
|
|
'type': 'logic_boolean',
|
|
'id': 'logic_boolean_1',
|
|
'fields': {
|
|
'BOOL': 'TRUE',
|
|
},
|
|
},
|
|
},
|
|
'DO0': {
|
|
'block': {
|
|
'type': 'text_print',
|
|
'id': 'text_print_1',
|
|
'inputs': {
|
|
'TEXT': {
|
|
'shadow': {
|
|
'type': 'text',
|
|
'id': 'text_shadow_1',
|
|
'fields': {
|
|
'TEXT': 'abc',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
async function loadStartBlocks(browser) {
|
|
await browser.execute((stringifiedJson) => {
|
|
// Hangs forever if the json isn't stringified ¯\_(ツ)_/¯
|
|
const testBlockJson = JSON.parse(stringifiedJson);
|
|
const workspace = Blockly.common.getMainWorkspace();
|
|
Blockly.serialization.workspaces.load(testBlockJson, workspace);
|
|
}, JSON.stringify(testBlockJson));
|
|
await browser.pause(PAUSE_TIME);
|
|
}
|
|
|
|
suite('Clipboard test', async function () {
|
|
// Setting timeout to unlimited as these tests take longer time to run
|
|
this.timeout(0);
|
|
|
|
// Clear the workspace and load start blocks
|
|
setup(async function () {
|
|
this.browser = await testSetup(testFileLocations.PLAYGROUND);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
});
|
|
|
|
test('Paste block to/from main workspace', async function () {
|
|
await loadStartBlocks(this.browser);
|
|
// Select and copy the "true" block
|
|
await focusOnBlock(this.browser, 'logic_boolean_1');
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
await this.browser.keys([Key.Ctrl, 'c']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check how many blocks there are before pasting
|
|
const allBlocksBeforePaste = await getAllBlocks(this.browser);
|
|
|
|
// Paste the block while still in the main workspace
|
|
await this.browser.keys([Key.Ctrl, 'v']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check result
|
|
const allBlocksAfterPaste = await getAllBlocks(this.browser);
|
|
chai.assert.equal(
|
|
allBlocksAfterPaste.length,
|
|
allBlocksBeforePaste.length + 1,
|
|
'Expected there to be one additional block after paste',
|
|
);
|
|
const focusedBlockId = await getSelectedBlockId(this.browser);
|
|
chai.assert.notEqual(
|
|
focusedBlockId,
|
|
'logic_boolean_1',
|
|
'Newly pasted block should be selected',
|
|
);
|
|
const focusedBlockType = await getSelectedBlockType(this.browser);
|
|
chai.assert.equal(
|
|
focusedBlockType,
|
|
'logic_boolean',
|
|
'Newly pasted block should be selected',
|
|
);
|
|
});
|
|
|
|
test('Copying a block also copies and pastes its children', async function () {
|
|
await loadStartBlocks(this.browser);
|
|
// Select and copy the "if/else" block which has children
|
|
await focusOnBlock(this.browser, 'controls_if_1');
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
await this.browser.keys([Key.Ctrl, 'c']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check how many blocks there are before pasting
|
|
const allBlocksBeforePaste = await getAllBlocks(this.browser);
|
|
|
|
// Paste the block while still in the main workspace
|
|
await this.browser.keys([Key.Ctrl, 'v']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check result
|
|
const allBlocksAfterPaste = await getAllBlocks(this.browser);
|
|
chai.assert.equal(
|
|
allBlocksAfterPaste.length,
|
|
allBlocksBeforePaste.length + 4,
|
|
'Expected there to be four additional blocks after paste',
|
|
);
|
|
});
|
|
|
|
test('Paste shadow block to/from main workspace', async function () {
|
|
await loadStartBlocks(this.browser);
|
|
// Select and copy the shadow number block
|
|
await focusOnBlock(this.browser, 'math_number_shadow_1');
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
await this.browser.keys([Key.Ctrl, 'c']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check how many blocks there are before pasting
|
|
const allBlocksBeforePaste = await getAllBlocks(this.browser);
|
|
|
|
// Paste the block while still in the main workspace
|
|
await this.browser.keys([Key.Ctrl, 'v']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check result
|
|
const allBlocksAfterPaste = await getAllBlocks(this.browser);
|
|
chai.assert.equal(
|
|
allBlocksAfterPaste.length,
|
|
allBlocksBeforePaste.length + 1,
|
|
'Expected there to be one additional block after paste',
|
|
);
|
|
const focusedBlockId = await getSelectedBlockId(this.browser);
|
|
chai.assert.notEqual(
|
|
focusedBlockId,
|
|
'math_number_shadow_1',
|
|
'Newly pasted block should be selected',
|
|
);
|
|
const focusedBlockType = await getSelectedBlockType(this.browser);
|
|
chai.assert.equal(
|
|
focusedBlockType,
|
|
'math_number',
|
|
'Newly pasted block should be selected',
|
|
);
|
|
const focusedBlockIsShadow = await this.browser.execute(() => {
|
|
return Blockly.common.getSelected().isShadow();
|
|
});
|
|
chai.assert.isFalse(
|
|
focusedBlockIsShadow,
|
|
'Expected the pasted version of the block to not be a shadow block',
|
|
);
|
|
});
|
|
|
|
test('Copy block from flyout, paste to main workspace', async function () {
|
|
// Open flyout
|
|
await getCategory(this.browser, 'Logic').then((category) =>
|
|
category.click(),
|
|
);
|
|
|
|
// Focus on first block in flyout
|
|
await this.browser.execute(() => {
|
|
const ws = Blockly.getMainWorkspace().getFlyout().getWorkspace();
|
|
const block = ws.getBlocksByType('controls_if')[0];
|
|
Blockly.getFocusManager().focusNode(block);
|
|
});
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Copy
|
|
await this.browser.keys([Key.Ctrl, 'c']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Select the main workspace
|
|
await clickWorkspace(this.browser);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Paste
|
|
await this.browser.keys([Key.Ctrl, 'v']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check that the block is now on the workspace and selected
|
|
const allBlocks = await getAllBlocks(this.browser);
|
|
chai.assert.equal(
|
|
allBlocks.length,
|
|
1,
|
|
'Expected there to be one block on main workspace after paste from flyout',
|
|
);
|
|
|
|
const focusedBlockType = await getSelectedBlockType(this.browser);
|
|
chai.assert.equal(
|
|
focusedBlockType,
|
|
'controls_if',
|
|
'Newly pasted block should be selected',
|
|
);
|
|
});
|
|
|
|
test('Copy block from flyout, paste while flyout focused', async function () {
|
|
// Open flyout
|
|
await getCategory(this.browser, 'Logic').then((category) =>
|
|
category.click(),
|
|
);
|
|
|
|
// Focus on first block in flyout
|
|
await this.browser.execute(() => {
|
|
const ws = Blockly.getMainWorkspace().getFlyout().getWorkspace();
|
|
const block = ws.getBlocksByType('controls_if')[0];
|
|
Blockly.getFocusManager().focusNode(block);
|
|
});
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Copy
|
|
await this.browser.keys([Key.Ctrl, 'c']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Paste
|
|
await this.browser.keys([Key.Ctrl, 'v']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check that the flyout is closed
|
|
const flyoutIsVisible = await this.browser
|
|
.$('.blocklyToolboxFlyout')
|
|
.then((elem) => elem.isDisplayed());
|
|
chai.assert.isFalse(flyoutIsVisible, 'Expected flyout to not be open');
|
|
|
|
// Check that the block is now on the main workspace and selected
|
|
const allBlocks = await getAllBlocks(this.browser);
|
|
chai.assert.equal(
|
|
allBlocks.length,
|
|
1,
|
|
'Expected there to be one block on main workspace after paste from flyout',
|
|
);
|
|
|
|
const focusedBlockType = await getSelectedBlockType(this.browser);
|
|
chai.assert.equal(
|
|
focusedBlockType,
|
|
'controls_if',
|
|
'Newly pasted block should be selected',
|
|
);
|
|
});
|
|
|
|
test('Copy block from mutator flyout, paste to mutator workspace', async function () {
|
|
// Load the start blocks
|
|
await loadStartBlocks(this.browser);
|
|
|
|
// Open the controls_if mutator
|
|
const block = await getBlockTypeFromWorkspace(
|
|
this.browser,
|
|
'controls_if',
|
|
0,
|
|
);
|
|
await openMutatorForBlock(this.browser, block);
|
|
|
|
// Select the first block in the mutator flyout
|
|
await this.browser.execute(
|
|
(blockId, mutatorBlockType) => {
|
|
const flyoutBlock = Blockly.getMainWorkspace()
|
|
.getBlockById(blockId)
|
|
.mutator.getWorkspace()
|
|
.getFlyout()
|
|
.getWorkspace()
|
|
.getBlocksByType(mutatorBlockType)[0];
|
|
|
|
Blockly.getFocusManager().focusNode(flyoutBlock);
|
|
},
|
|
'controls_if_1',
|
|
'controls_if_elseif',
|
|
);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Copy
|
|
await this.browser.keys([Key.Ctrl, 'c']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Paste
|
|
await this.browser.keys([Key.Ctrl, 'v']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check that the block is now in the mutator workspace and selected
|
|
const numberOfIfElseBlocks = await this.browser.execute(
|
|
(blockId, mutatorBlockType) => {
|
|
return Blockly.getMainWorkspace()
|
|
.getBlockById(blockId)
|
|
.mutator.getWorkspace()
|
|
.getBlocksByType(mutatorBlockType).length;
|
|
},
|
|
'controls_if_1',
|
|
'controls_if_elseif',
|
|
);
|
|
|
|
chai.assert.equal(
|
|
numberOfIfElseBlocks,
|
|
1,
|
|
'Expected there to be one if_else block in mutator workspace',
|
|
);
|
|
|
|
const focusedBlockType = await getSelectedBlockType(this.browser);
|
|
chai.assert.equal(
|
|
focusedBlockType,
|
|
'controls_if_elseif',
|
|
'Newly pasted block should be selected',
|
|
);
|
|
});
|
|
|
|
test('Copy block from mutator flyout, paste to main workspace while mutator open', async function () {
|
|
// Load the start blocks
|
|
await loadStartBlocks(this.browser);
|
|
|
|
// Open the controls_if mutator
|
|
const block = await getBlockTypeFromWorkspace(
|
|
this.browser,
|
|
'controls_if',
|
|
0,
|
|
);
|
|
await openMutatorForBlock(this.browser, block);
|
|
|
|
// Select the first block in the mutator flyout
|
|
await this.browser.execute(
|
|
(blockId, mutatorBlockType) => {
|
|
const flyoutBlock = Blockly.getMainWorkspace()
|
|
.getBlockById(blockId)
|
|
.mutator.getWorkspace()
|
|
.getFlyout()
|
|
.getWorkspace()
|
|
.getBlocksByType(mutatorBlockType)[0];
|
|
|
|
Blockly.getFocusManager().focusNode(flyoutBlock);
|
|
},
|
|
'controls_if_1',
|
|
'controls_if_elseif',
|
|
);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Copy
|
|
await this.browser.keys([Key.Ctrl, 'c']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Click the main workspace
|
|
await clickWorkspace(this.browser);
|
|
|
|
// Paste
|
|
await this.browser.keys([Key.Ctrl, 'v']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check that the block is now in the mutator workspace and selected
|
|
const numberOfIfElseBlocks = await this.browser.execute(
|
|
(blockId, mutatorBlockType) => {
|
|
return Blockly.getMainWorkspace()
|
|
.getBlockById(blockId)
|
|
.mutator.getWorkspace()
|
|
.getBlocksByType(mutatorBlockType).length;
|
|
},
|
|
'controls_if_1',
|
|
'controls_if_elseif',
|
|
);
|
|
|
|
chai.assert.equal(
|
|
numberOfIfElseBlocks,
|
|
1,
|
|
'Expected there to be one if_else block in mutator workspace',
|
|
);
|
|
|
|
const focusedBlockType = await getSelectedBlockType(this.browser);
|
|
chai.assert.equal(
|
|
focusedBlockType,
|
|
'controls_if_elseif',
|
|
'Newly pasted block should be selected',
|
|
);
|
|
|
|
// Check that there are no new blocks on the main workspace
|
|
const numberOfIfElseBlocksOnMainWorkspace = await this.browser.execute(
|
|
(mutatorBlockType) => {
|
|
return Blockly.getMainWorkspace().getBlocksByType(mutatorBlockType)
|
|
.length;
|
|
},
|
|
'controls_if_elseif',
|
|
);
|
|
chai.assert.equal(
|
|
numberOfIfElseBlocksOnMainWorkspace,
|
|
0,
|
|
'Mutator blocks should not appear on main workspace',
|
|
);
|
|
});
|
|
|
|
test('Copy block from mutator flyout, paste to main workspace while mutator closed', async function () {
|
|
// Load the start blocks
|
|
await loadStartBlocks(this.browser);
|
|
|
|
// Open the controls_if mutator
|
|
const block = await getBlockTypeFromWorkspace(
|
|
this.browser,
|
|
'controls_if',
|
|
0,
|
|
);
|
|
await openMutatorForBlock(this.browser, block);
|
|
|
|
// Select the first block in the mutator flyout
|
|
await this.browser.execute(
|
|
(blockId, mutatorBlockType) => {
|
|
const flyoutBlock = Blockly.getMainWorkspace()
|
|
.getBlockById(blockId)
|
|
.mutator.getWorkspace()
|
|
.getFlyout()
|
|
.getWorkspace()
|
|
.getBlocksByType(mutatorBlockType)[0];
|
|
|
|
Blockly.getFocusManager().focusNode(flyoutBlock);
|
|
},
|
|
'controls_if_1',
|
|
'controls_if_elseif',
|
|
);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Copy
|
|
await this.browser.keys([Key.Ctrl, 'c']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Close the mutator flyout (calling this method on open mutator closes it)
|
|
await openMutatorForBlock(this.browser, block);
|
|
|
|
// Click the main workspace
|
|
await clickWorkspace(this.browser);
|
|
|
|
// Paste
|
|
await this.browser.keys([Key.Ctrl, 'v']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check that there are no new blocks on the main workspace
|
|
const numberOfIfElseBlocksOnMainWorkspace = await this.browser.execute(
|
|
(mutatorBlockType) => {
|
|
return Blockly.getMainWorkspace().getBlocksByType(mutatorBlockType)
|
|
.length;
|
|
},
|
|
'controls_if_elseif',
|
|
);
|
|
chai.assert.equal(
|
|
numberOfIfElseBlocksOnMainWorkspace,
|
|
0,
|
|
'Mutator blocks should not appear on main workspace',
|
|
);
|
|
});
|
|
|
|
test('Copy workspace comment, paste to main workspace', async function () {
|
|
// Add a workspace comment to the workspace
|
|
await this.browser.execute(() => {
|
|
const workspace = Blockly.getMainWorkspace();
|
|
const json = {
|
|
'workspaceComments': [
|
|
{
|
|
'height': 100,
|
|
'width': 120,
|
|
'id': 'workspace_comment_1',
|
|
'x': 13,
|
|
'y': -12,
|
|
'text': 'This is a comment',
|
|
},
|
|
],
|
|
};
|
|
Blockly.serialization.workspaces.load(json, workspace);
|
|
});
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Select the workspace comment
|
|
await this.browser.execute(() => {
|
|
const comment = Blockly.getMainWorkspace().getCommentById(
|
|
'workspace_comment_1',
|
|
);
|
|
Blockly.getFocusManager().focusNode(comment);
|
|
});
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Copy
|
|
await this.browser.keys([Key.Ctrl, 'c']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Click the main workspace
|
|
await clickWorkspace(this.browser);
|
|
|
|
// Paste
|
|
await this.browser.keys([Key.Ctrl, 'v']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check that there are 2 comments on the workspace
|
|
const numberOfComments = await this.browser.execute(() => {
|
|
return Blockly.getMainWorkspace().getTopComments().length;
|
|
});
|
|
chai.assert.equal(
|
|
numberOfComments,
|
|
2,
|
|
'Expected 2 workspace comments after pasting',
|
|
);
|
|
});
|
|
|
|
test('Cut block from main workspace, paste to main workspace', async function () {
|
|
await loadStartBlocks(this.browser);
|
|
// Select and cut the "true" block
|
|
await focusOnBlock(this.browser, 'logic_boolean_1');
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
await this.browser.keys([Key.Ctrl, 'x']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check that the "true" block was deleted
|
|
const trueBlock = await this.browser.execute(() => {
|
|
return Blockly.getMainWorkspace().getBlockById('logic_boolean_1') ?? null;
|
|
});
|
|
chai.assert.isNull(trueBlock);
|
|
|
|
// Check how many blocks there are before pasting
|
|
const allBlocksBeforePaste = await getAllBlocks(this.browser);
|
|
|
|
// Paste the block while still in the main workspace
|
|
await this.browser.keys([Key.Ctrl, 'v']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check result
|
|
const allBlocksAfterPaste = await getAllBlocks(this.browser);
|
|
chai.assert.equal(
|
|
allBlocksAfterPaste.length,
|
|
allBlocksBeforePaste.length + 1,
|
|
'Expected there to be one additional block after paste',
|
|
);
|
|
});
|
|
|
|
test('Cannot cut block from flyout', async function () {
|
|
// Open flyout
|
|
await getCategory(this.browser, 'Logic').then((category) =>
|
|
category.click(),
|
|
);
|
|
|
|
// Focus on first block in flyout
|
|
await this.browser.execute(() => {
|
|
const ws = Blockly.getMainWorkspace().getFlyout().getWorkspace();
|
|
const block = ws.getBlocksByType('controls_if')[0];
|
|
Blockly.getFocusManager().focusNode(block);
|
|
});
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Cut
|
|
await this.browser.keys([Key.Ctrl, 'x']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Select the main workspace
|
|
await clickWorkspace(this.browser);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Paste
|
|
await this.browser.keys([Key.Ctrl, 'v']);
|
|
await this.browser.pause(PAUSE_TIME);
|
|
|
|
// Check that no block was pasted
|
|
const allBlocks = await getAllBlocks(this.browser);
|
|
chai.assert.equal(
|
|
allBlocks.length,
|
|
0,
|
|
'Expected no blocks in the workspace because nothing to paste',
|
|
);
|
|
});
|
|
});
|