Skip to content

Commit 5d20b95

Browse files
committed
test(mover): Add tests for starting moves of statement and value blocks
1 parent 3387a66 commit 5d20b95

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed

test/webdriverio/test/move_test.ts

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7+
import * as Blockly from 'blockly';
78
import * as chai from 'chai';
89
import {Browser, Key} from 'webdriverio';
910
import {
@@ -24,4 +25,184 @@ suite('Move tests', function () {
2425
this.browser = await testSetup(testFileLocations.MOVE_TEST_BLOCKS);
2526
await this.browser.pause(PAUSE_TIME);
2627
});
28+
29+
// When a move of a statement block begins, it is expected that only
30+
// that block (and all blocks connected to its inputs) will be
31+
// moved, with subsequent statement blocks below it in the stack
32+
// reattached to where the moving block was - i.e., that a stack
33+
// heal will occur.
34+
test.only('Start moving statement blocks', async function () {
35+
for (let i = 1; i < 7; i++) {
36+
// Navigate to statement_<i>.
37+
await tabNavigateToWorkspace(this.browser);
38+
await setCurrentCursorNodeById(this.browser, `statement_${i}`);
39+
40+
// Get information about parent connection of selected block,
41+
// and block connected to selected block's next connection.
42+
const info = await getSelectedNeighbourInfo(this.browser);
43+
44+
chai.assert(info.parentId, 'selected block has no parent block');
45+
chai.assert(
46+
typeof info.parentIndex === 'number',
47+
'parent connection index not found',
48+
);
49+
chai.assert(info.nextId, 'selected block has no next block');
50+
51+
// Start move.
52+
await this.browser.keys('m');
53+
54+
// Check that the moving block has nothing connected it its
55+
// next/previous connections, and same thing connected to value
56+
// input.
57+
const newInfo = await getSelectedNeighbourInfo(this.browser);
58+
chai.assert(
59+
newInfo.parentId === null,
60+
'moving block should have no parent block',
61+
);
62+
chai.assert(
63+
newInfo.nextId === null,
64+
'moving block should have no next block',
65+
);
66+
chai.assert.strictEqual(
67+
newInfo.valueId,
68+
info.valueId,
69+
'moving block should have same attached value block',
70+
);
71+
72+
// Get ID of next block now connected to the (former) parent
73+
// connection of the currently-moving block (skipping insertion
74+
// markers), and make sure it's same as the ID of the block that
75+
// was formerly attached to the moving block's next connection.
76+
const newNextId = await this.browser.execute(
77+
(parentId: string, index: number) => {
78+
const parent = Blockly.getMainWorkspace().getBlockById(parentId);
79+
if (!parent) throw new Error('parent block gone');
80+
let block = parent.getConnections_(true)[index].targetBlock();
81+
while (block?.isInsertionMarker()) {
82+
block = block.getNextBlock();
83+
}
84+
return block?.id;
85+
},
86+
info.parentId,
87+
info.parentIndex,
88+
);
89+
chai.assert.strictEqual(
90+
newNextId,
91+
info.nextId,
92+
'former parent connection should be connected to former next block',
93+
);
94+
95+
// Abort move.
96+
await this.browser.keys(Key.Escape);
97+
}
98+
});
99+
100+
// When a move of a value block begins, it is expected that block
101+
// and all blocks connected to its inputs will be moved - i.e., that
102+
// a stack heal (really: unary operator chain heal) will NOT occur.
103+
test.only('Start moving value blocks', async function () {
104+
for (let i = 1; i < 7; i++) {
105+
// Navigate to statement_<i>.
106+
await tabNavigateToWorkspace(this.browser);
107+
await setCurrentCursorNodeById(this.browser, `value_${i}`);
108+
109+
// Get information about parent connection of selected block,
110+
// and block connected to selected block's value input.
111+
const info = await getSelectedNeighbourInfo(this.browser);
112+
113+
chai.assert(info.parentId, 'selected block has no parent block');
114+
chai.assert(
115+
typeof info.parentIndex === 'number',
116+
'parent connection index not found',
117+
);
118+
chai.assert(info.valueId, 'selected block has no child value block');
119+
120+
// Start move.
121+
await this.browser.keys('m');
122+
123+
// Check that the moving block has nothing connected it its
124+
// next/previous connections, and same thing connected to value
125+
// input.
126+
const newInfo = await getSelectedNeighbourInfo(this.browser);
127+
chai.assert(
128+
newInfo.parentId === null,
129+
'moving block should have no parent block',
130+
);
131+
chai.assert(
132+
newInfo.nextId === null,
133+
'moving block should have no next block',
134+
);
135+
chai.assert.strictEqual(
136+
newInfo.valueId,
137+
info.valueId,
138+
'moving block should have same attached value block',
139+
);
140+
141+
// Check the (former) parent connection of the currently-moving
142+
// block is (skipping insertion markers) either unconnected or
143+
// connected to a shadow block, and that is is not the block
144+
// (originally and still) connected to the moving block's zeroth
145+
// value input.
146+
const newValueInfo = await this.browser.execute(
147+
(parentId: string, index: number) => {
148+
const parent = Blockly.getMainWorkspace().getBlockById(parentId);
149+
if (!parent) throw new Error('parent block gone');
150+
let block = parent.getConnections_(true)[index].targetBlock() ?? null;
151+
while (block?.isInsertionMarker()) {
152+
block = block.inputList[0].connection?.targetBlock() ?? null;
153+
}
154+
return {
155+
id: block?.id ?? null,
156+
shadow: block?.isShadow() ?? null,
157+
};
158+
},
159+
info.parentId,
160+
info.parentIndex,
161+
);
162+
chai.assert(
163+
newValueInfo.id === null || newValueInfo.shadow,
164+
'former parent connection should be unconnected (or shadow)',
165+
);
166+
chai.assert.notStrictEqual(
167+
newValueInfo.id,
168+
info.valueId,
169+
'former parent connection should NOT be connected to value block',
170+
);
171+
172+
// Abort move.
173+
await this.browser.keys(Key.Escape);
174+
}
175+
});
27176
});
177+
178+
/**
179+
* Get information about the currently-selected block's parent and
180+
* child blocks.
181+
*
182+
* N.B. explicitly converts any undefined values to null because
183+
* browser.execute does this implicitly and so otherwise this function
184+
* would return values that were not compliant with its own inferred
185+
* type signature!
186+
*
187+
* @returns A promise setting to an object containing the parent block
188+
* ID, index of parent connection, next block ID, and ID of the block
189+
* connected to the zeroth value value input.
190+
*/
191+
function getSelectedNeighbourInfo(browser: WebdriverIO.Browser) {
192+
return browser.execute(() => {
193+
const block = Blockly.getFocusManager().getFocusedNode() as
194+
| Blockly.BlockSvg
195+
| undefined;
196+
if (!block) throw new Error('no selected block');
197+
const parent = block?.getParent();
198+
return {
199+
parentId: parent?.id ?? null,
200+
parentIndex:
201+
parent
202+
?.getConnections_(true)
203+
.findIndex((conn) => conn.targetBlock() === block) ?? null,
204+
nextId: block?.getNextBlock()?.id ?? null,
205+
valueId: block?.inputList[0].connection?.targetBlock()?.id ?? null,
206+
};
207+
});
208+
}

test/webdriverio/test/test_setup.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ export async function getFocusedBlockType(
330330
return block?.type;
331331
});
332332
}
333+
333334
/**
334335
* Get the connection type of the current focused node. Assumes the current node
335336
* is a connection.

0 commit comments

Comments
 (0)