mirror of
https://github.com/google/blockly.git
synced 2025-12-16 06:10:12 +01:00
Merge branch 'google:develop' into fix-browser-tests-2025-06
This commit is contained in:
@@ -29,7 +29,10 @@ suite('Connection checker', function () {
|
||||
}
|
||||
|
||||
test('Target Null', function () {
|
||||
const connection = new Blockly.Connection({}, ConnectionType.INPUT_VALUE);
|
||||
const connection = new Blockly.Connection(
|
||||
{id: 'test'},
|
||||
ConnectionType.INPUT_VALUE,
|
||||
);
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
connection,
|
||||
@@ -38,7 +41,7 @@ suite('Connection checker', function () {
|
||||
);
|
||||
});
|
||||
test('Target Self', function () {
|
||||
const block = {workspace: 1};
|
||||
const block = {id: 'test', workspace: 1};
|
||||
const connection1 = new Blockly.Connection(
|
||||
block,
|
||||
ConnectionType.INPUT_VALUE,
|
||||
@@ -57,11 +60,11 @@ suite('Connection checker', function () {
|
||||
});
|
||||
test('Different Workspaces', function () {
|
||||
const connection1 = new Blockly.Connection(
|
||||
{workspace: 1},
|
||||
{id: 'test1', workspace: 1},
|
||||
ConnectionType.INPUT_VALUE,
|
||||
);
|
||||
const connection2 = new Blockly.Connection(
|
||||
{workspace: 2},
|
||||
{id: 'test2', workspace: 2},
|
||||
ConnectionType.OUTPUT_VALUE,
|
||||
);
|
||||
|
||||
@@ -76,10 +79,10 @@ suite('Connection checker', function () {
|
||||
setup(function () {
|
||||
// We have to declare each separately so that the connections belong
|
||||
// on different blocks.
|
||||
const prevBlock = {isShadow: function () {}};
|
||||
const nextBlock = {isShadow: function () {}};
|
||||
const outBlock = {isShadow: function () {}};
|
||||
const inBlock = {isShadow: function () {}};
|
||||
const prevBlock = {id: 'test1', isShadow: function () {}};
|
||||
const nextBlock = {id: 'test2', isShadow: function () {}};
|
||||
const outBlock = {id: 'test3', isShadow: function () {}};
|
||||
const inBlock = {id: 'test4', isShadow: function () {}};
|
||||
this.previous = new Blockly.Connection(
|
||||
prevBlock,
|
||||
ConnectionType.PREVIOUS_STATEMENT,
|
||||
@@ -197,11 +200,13 @@ suite('Connection checker', function () {
|
||||
suite('Shadows', function () {
|
||||
test('Previous Shadow', function () {
|
||||
const prevBlock = {
|
||||
id: 'test1',
|
||||
isShadow: function () {
|
||||
return true;
|
||||
},
|
||||
};
|
||||
const nextBlock = {
|
||||
id: 'test2',
|
||||
isShadow: function () {
|
||||
return false;
|
||||
},
|
||||
@@ -224,11 +229,13 @@ suite('Connection checker', function () {
|
||||
});
|
||||
test('Next Shadow', function () {
|
||||
const prevBlock = {
|
||||
id: 'test1',
|
||||
isShadow: function () {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
const nextBlock = {
|
||||
id: 'test2',
|
||||
isShadow: function () {
|
||||
return true;
|
||||
},
|
||||
@@ -251,11 +258,13 @@ suite('Connection checker', function () {
|
||||
});
|
||||
test('Prev and Next Shadow', function () {
|
||||
const prevBlock = {
|
||||
id: 'test1',
|
||||
isShadow: function () {
|
||||
return true;
|
||||
},
|
||||
};
|
||||
const nextBlock = {
|
||||
id: 'test2',
|
||||
isShadow: function () {
|
||||
return true;
|
||||
},
|
||||
@@ -278,11 +287,13 @@ suite('Connection checker', function () {
|
||||
});
|
||||
test('Output Shadow', function () {
|
||||
const outBlock = {
|
||||
id: 'test1',
|
||||
isShadow: function () {
|
||||
return true;
|
||||
},
|
||||
};
|
||||
const inBlock = {
|
||||
id: 'test2',
|
||||
isShadow: function () {
|
||||
return false;
|
||||
},
|
||||
@@ -305,11 +316,13 @@ suite('Connection checker', function () {
|
||||
});
|
||||
test('Input Shadow', function () {
|
||||
const outBlock = {
|
||||
id: 'test1',
|
||||
isShadow: function () {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
const inBlock = {
|
||||
id: 'test2',
|
||||
isShadow: function () {
|
||||
return true;
|
||||
},
|
||||
@@ -332,11 +345,13 @@ suite('Connection checker', function () {
|
||||
});
|
||||
test('Output and Input Shadow', function () {
|
||||
const outBlock = {
|
||||
id: 'test1',
|
||||
isShadow: function () {
|
||||
return true;
|
||||
},
|
||||
};
|
||||
const inBlock = {
|
||||
id: 'test2',
|
||||
isShadow: function () {
|
||||
return true;
|
||||
},
|
||||
@@ -373,9 +388,11 @@ suite('Connection checker', function () {
|
||||
};
|
||||
test('Output connected, adding previous', function () {
|
||||
const outBlock = {
|
||||
id: 'test1',
|
||||
isShadow: function () {},
|
||||
};
|
||||
const inBlock = {
|
||||
id: 'test2',
|
||||
isShadow: function () {},
|
||||
};
|
||||
const outCon = new Blockly.Connection(
|
||||
@@ -394,6 +411,7 @@ suite('Connection checker', function () {
|
||||
ConnectionType.PREVIOUS_STATEMENT,
|
||||
);
|
||||
const nextBlock = {
|
||||
id: 'test3',
|
||||
isShadow: function () {},
|
||||
};
|
||||
const nextCon = new Blockly.Connection(
|
||||
@@ -410,9 +428,11 @@ suite('Connection checker', function () {
|
||||
});
|
||||
test('Previous connected, adding output', function () {
|
||||
const prevBlock = {
|
||||
id: 'test1',
|
||||
isShadow: function () {},
|
||||
};
|
||||
const nextBlock = {
|
||||
id: 'test2',
|
||||
isShadow: function () {},
|
||||
};
|
||||
const prevCon = new Blockly.Connection(
|
||||
@@ -431,6 +451,7 @@ suite('Connection checker', function () {
|
||||
ConnectionType.OUTPUT_VALUE,
|
||||
);
|
||||
const inBlock = {
|
||||
id: 'test3',
|
||||
isShadow: function () {},
|
||||
};
|
||||
const inCon = new Blockly.Connection(
|
||||
@@ -449,8 +470,14 @@ suite('Connection checker', function () {
|
||||
});
|
||||
suite('Check Types', function () {
|
||||
setup(function () {
|
||||
this.con1 = new Blockly.Connection({}, ConnectionType.PREVIOUS_STATEMENT);
|
||||
this.con2 = new Blockly.Connection({}, ConnectionType.NEXT_STATEMENT);
|
||||
this.con1 = new Blockly.Connection(
|
||||
{id: 'test1'},
|
||||
ConnectionType.PREVIOUS_STATEMENT,
|
||||
);
|
||||
this.con2 = new Blockly.Connection(
|
||||
{id: 'test2'},
|
||||
ConnectionType.NEXT_STATEMENT,
|
||||
);
|
||||
});
|
||||
function assertCheckTypes(checker, one, two) {
|
||||
assert.isTrue(checker.doTypeChecks(one, two));
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import {ConnectionType} from '../../build/src/core/connection_type.js';
|
||||
import * as idGenerator from '../../build/src/core/utils/idgenerator.js';
|
||||
import {assert} from '../../node_modules/chai/chai.js';
|
||||
import {
|
||||
sharedTestSetup,
|
||||
@@ -31,7 +32,7 @@ suite('Connection Database', function () {
|
||||
};
|
||||
workspace.connectionDBList[type] = opt_database || this.database;
|
||||
const connection = new Blockly.RenderedConnection(
|
||||
{workspace: workspace},
|
||||
{id: idGenerator.getNextUniqueId(), workspace: workspace},
|
||||
type,
|
||||
);
|
||||
connection.x = x;
|
||||
|
||||
@@ -355,6 +355,7 @@ suite('Events', function () {
|
||||
|
||||
suite('With variable getter blocks', function () {
|
||||
setup(function () {
|
||||
this.TEST_BLOCK_ID = 'test_block_id';
|
||||
this.genUidStub = createGenUidStubWithReturns([
|
||||
this.TEST_BLOCK_ID,
|
||||
'test_var_id',
|
||||
|
||||
@@ -294,4 +294,300 @@ suite('Text Input Fields', function () {
|
||||
this.assertValue('test text');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Use editor', function () {
|
||||
setup(function () {
|
||||
this.blockJson = {
|
||||
'type': 'math_arithmetic',
|
||||
'id': 'test_arithmetic_block',
|
||||
'fields': {
|
||||
'OP': 'ADD',
|
||||
},
|
||||
'inputs': {
|
||||
'A': {
|
||||
'shadow': {
|
||||
'type': 'math_number',
|
||||
'id': 'left_input_block',
|
||||
'name': 'test_name',
|
||||
'fields': {
|
||||
'NUM': 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
'B': {
|
||||
'shadow': {
|
||||
'type': 'math_number',
|
||||
'id': 'right_input_block',
|
||||
'fields': {
|
||||
'NUM': 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
this.getFieldFromShadowBlock = function (shadowBlock) {
|
||||
return shadowBlock.getFields().next().value;
|
||||
};
|
||||
|
||||
this.simulateTypingIntoInput = (inputElem, newText) => {
|
||||
// Typing into an input field changes its value directly and then fires
|
||||
// an InputEvent (which FieldInput relies on to automatically
|
||||
// synchronize its state).
|
||||
inputElem.value = newText;
|
||||
inputElem.dispatchEvent(new InputEvent('input'));
|
||||
};
|
||||
});
|
||||
|
||||
// The block being tested doesn't use full-block fields in Geras.
|
||||
suite('Geras theme', function () {
|
||||
setup(function () {
|
||||
this.workspace = Blockly.inject('blocklyDiv', {
|
||||
renderer: 'geras',
|
||||
});
|
||||
Blockly.serialization.blocks.append(this.blockJson, this.workspace);
|
||||
|
||||
// The workspace actually needs to be visible for focus.
|
||||
document.getElementById('blocklyDiv').style.visibility = 'visible';
|
||||
});
|
||||
teardown(function () {
|
||||
document.getElementById('blocklyDiv').style.visibility = 'hidden';
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
test('No editor open by default', function () {
|
||||
// The editor is only opened if its indicated that it should be open.
|
||||
assert.isNull(document.querySelector('.blocklyHtmlInput'));
|
||||
});
|
||||
|
||||
test('Type in editor with escape does not change field value', async function () {
|
||||
const block = this.workspace.getBlockById('left_input_block');
|
||||
const field = this.getFieldFromShadowBlock(block);
|
||||
field.showEditor();
|
||||
// This must be called to avoid editor resize logic throwing an error.
|
||||
await Blockly.renderManagement.finishQueuedRenders();
|
||||
|
||||
// Change the value of the field's input through its editor.
|
||||
const fieldEditor = document.querySelector('.blocklyHtmlInput');
|
||||
this.simulateTypingIntoInput(fieldEditor, 'updated value');
|
||||
fieldEditor.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'Escape',
|
||||
}),
|
||||
);
|
||||
|
||||
// 'Escape' will avoid saving the edited field value and close the editor.
|
||||
assert.equal(field.getValue(), 1);
|
||||
assert.isNull(document.querySelector('.blocklyHtmlInput'));
|
||||
});
|
||||
|
||||
test('Type in editor with enter changes field value', async function () {
|
||||
const block = this.workspace.getBlockById('left_input_block');
|
||||
const field = this.getFieldFromShadowBlock(block);
|
||||
field.showEditor();
|
||||
// This must be called to avoid editor resize logic throwing an error.
|
||||
await Blockly.renderManagement.finishQueuedRenders();
|
||||
|
||||
// Change the value of the field's input through its editor.
|
||||
const fieldEditor = document.querySelector('.blocklyHtmlInput');
|
||||
this.simulateTypingIntoInput(fieldEditor, '10');
|
||||
fieldEditor.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'Enter',
|
||||
}),
|
||||
);
|
||||
|
||||
// 'Enter' will save the edited result and close the editor.
|
||||
assert.equal(field.getValue(), 10);
|
||||
assert.isNull(document.querySelector('.blocklyHtmlInput'));
|
||||
});
|
||||
|
||||
test('Not finishing editing does not return ephemeral focus', async function () {
|
||||
const block = this.workspace.getBlockById('left_input_block');
|
||||
const field = this.getFieldFromShadowBlock(block);
|
||||
Blockly.getFocusManager().focusNode(field);
|
||||
field.showEditor();
|
||||
// This must be called to avoid editor resize logic throwing an error.
|
||||
await Blockly.renderManagement.finishQueuedRenders();
|
||||
|
||||
// Change the value of the field's input through its editor.
|
||||
const fieldEditor = document.querySelector('.blocklyHtmlInput');
|
||||
this.simulateTypingIntoInput(fieldEditor, '10');
|
||||
|
||||
// If the editor doesn't restore focus then the current focused element is
|
||||
// still the editor.
|
||||
assert.strictEqual(document.activeElement, fieldEditor);
|
||||
});
|
||||
|
||||
test('Finishing editing returns ephemeral focus', async function () {
|
||||
const block = this.workspace.getBlockById('left_input_block');
|
||||
const field = this.getFieldFromShadowBlock(block);
|
||||
Blockly.getFocusManager().focusNode(field);
|
||||
field.showEditor();
|
||||
// This must be called to avoid editor resize logic throwing an error.
|
||||
await Blockly.renderManagement.finishQueuedRenders();
|
||||
|
||||
// Change the value of the field's input through its editor.
|
||||
const fieldEditor = document.querySelector('.blocklyHtmlInput');
|
||||
this.simulateTypingIntoInput(fieldEditor, '10');
|
||||
fieldEditor.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'Escape',
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify that exiting the editor restores focus back to the field.
|
||||
assert.strictEqual(Blockly.getFocusManager().getFocusedNode(), field);
|
||||
assert.strictEqual(document.activeElement, field.getFocusableElement());
|
||||
});
|
||||
|
||||
test('Opening an editor, tabbing, then editing changes the second field', async function () {
|
||||
const leftInputBlock = this.workspace.getBlockById('left_input_block');
|
||||
const rightInputBlock =
|
||||
this.workspace.getBlockById('right_input_block');
|
||||
const leftField = this.getFieldFromShadowBlock(leftInputBlock);
|
||||
const rightField = this.getFieldFromShadowBlock(rightInputBlock);
|
||||
leftField.showEditor();
|
||||
// This must be called to avoid editor resize logic throwing an error.
|
||||
await Blockly.renderManagement.finishQueuedRenders();
|
||||
|
||||
// Tab, then edit and close the editor.
|
||||
document.querySelector('.blocklyHtmlInput').dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'Tab',
|
||||
}),
|
||||
);
|
||||
const rightFieldEditor = document.querySelector('.blocklyHtmlInput');
|
||||
this.simulateTypingIntoInput(rightFieldEditor, '15');
|
||||
rightFieldEditor.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'Enter',
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify that only the right field changed (due to the tab).
|
||||
assert.equal(leftField.getValue(), 1);
|
||||
assert.equal(rightField.getValue(), 15);
|
||||
assert.isNull(document.querySelector('.blocklyHtmlInput'));
|
||||
});
|
||||
|
||||
test('Opening an editor, tabbing, then editing changes focus to the second field', async function () {
|
||||
const leftInputBlock = this.workspace.getBlockById('left_input_block');
|
||||
const rightInputBlock =
|
||||
this.workspace.getBlockById('right_input_block');
|
||||
const leftField = this.getFieldFromShadowBlock(leftInputBlock);
|
||||
const rightField = this.getFieldFromShadowBlock(rightInputBlock);
|
||||
Blockly.getFocusManager().focusNode(leftField);
|
||||
leftField.showEditor();
|
||||
// This must be called to avoid editor resize logic throwing an error.
|
||||
await Blockly.renderManagement.finishQueuedRenders();
|
||||
|
||||
// Tab, then edit and close the editor.
|
||||
document.querySelector('.blocklyHtmlInput').dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'Tab',
|
||||
}),
|
||||
);
|
||||
const rightFieldEditor = document.querySelector('.blocklyHtmlInput');
|
||||
this.simulateTypingIntoInput(rightFieldEditor, '15');
|
||||
rightFieldEditor.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'Enter',
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify that the tab causes focus to change to the right field.
|
||||
assert.strictEqual(
|
||||
Blockly.getFocusManager().getFocusedNode(),
|
||||
rightField,
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.activeElement,
|
||||
rightField.getFocusableElement(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// The block being tested uses full-block fields in Zelos.
|
||||
suite('Zelos theme', function () {
|
||||
setup(function () {
|
||||
this.workspace = Blockly.inject('blocklyDiv', {
|
||||
renderer: 'zelos',
|
||||
});
|
||||
Blockly.serialization.blocks.append(this.blockJson, this.workspace);
|
||||
|
||||
// The workspace actually needs to be visible for focus.
|
||||
document.getElementById('blocklyDiv').style.visibility = 'visible';
|
||||
});
|
||||
teardown(function () {
|
||||
document.getElementById('blocklyDiv').style.visibility = 'hidden';
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
test('Opening an editor, tabbing, then editing full block field changes the second field', async function () {
|
||||
const leftInputBlock = this.workspace.getBlockById('left_input_block');
|
||||
const rightInputBlock =
|
||||
this.workspace.getBlockById('right_input_block');
|
||||
const leftField = this.getFieldFromShadowBlock(leftInputBlock);
|
||||
const rightField = this.getFieldFromShadowBlock(rightInputBlock);
|
||||
leftField.showEditor();
|
||||
// This must be called to avoid editor resize logic throwing an error.
|
||||
await Blockly.renderManagement.finishQueuedRenders();
|
||||
|
||||
// Tab, then edit and close the editor.
|
||||
document.querySelector('.blocklyHtmlInput').dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'Tab',
|
||||
}),
|
||||
);
|
||||
const rightFieldEditor = document.querySelector('.blocklyHtmlInput');
|
||||
this.simulateTypingIntoInput(rightFieldEditor, '15');
|
||||
rightFieldEditor.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'Enter',
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify that only the right field changed (due to the tab).
|
||||
assert.equal(leftField.getValue(), 1);
|
||||
assert.equal(rightField.getValue(), 15);
|
||||
assert.isNull(document.querySelector('.blocklyHtmlInput'));
|
||||
});
|
||||
|
||||
test('Opening an editor, tabbing, then editing full block field changes focus to the second field', async function () {
|
||||
const leftInputBlock = this.workspace.getBlockById('left_input_block');
|
||||
const rightInputBlock =
|
||||
this.workspace.getBlockById('right_input_block');
|
||||
const leftField = this.getFieldFromShadowBlock(leftInputBlock);
|
||||
Blockly.getFocusManager().focusNode(leftInputBlock);
|
||||
leftField.showEditor();
|
||||
// This must be called to avoid editor resize logic throwing an error.
|
||||
await Blockly.renderManagement.finishQueuedRenders();
|
||||
|
||||
// Tab, then edit and close the editor.
|
||||
document.querySelector('.blocklyHtmlInput').dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'Tab',
|
||||
}),
|
||||
);
|
||||
const rightFieldEditor = document.querySelector('.blocklyHtmlInput');
|
||||
this.simulateTypingIntoInput(rightFieldEditor, '15');
|
||||
rightFieldEditor.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'Enter',
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify that the tab causes focus to change to the right field block.
|
||||
assert.strictEqual(
|
||||
Blockly.getFocusManager().getFocusedNode(),
|
||||
rightInputBlock,
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.activeElement,
|
||||
rightInputBlock.getFocusableElement(),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -249,6 +249,54 @@ suite('FocusManager', function () {
|
||||
// The second register should not fail since the tree was previously unregistered.
|
||||
});
|
||||
|
||||
test('for tree with missing ID throws error', function () {
|
||||
const rootNode = this.testFocusableTree1.getRootFocusableNode();
|
||||
const rootElem = rootNode.getFocusableElement();
|
||||
const oldId = rootElem.id;
|
||||
rootElem.removeAttribute('id');
|
||||
|
||||
const errorMsgRegex =
|
||||
/Attempting to register a tree with a root element that has an invalid ID.+?/;
|
||||
assert.throws(
|
||||
() => this.focusManager.registerTree(this.testFocusableTree1),
|
||||
errorMsgRegex,
|
||||
);
|
||||
// Restore the ID for other tests.
|
||||
rootElem.id = oldId;
|
||||
});
|
||||
|
||||
test('for tree with null ID throws error', function () {
|
||||
const rootNode = this.testFocusableTree1.getRootFocusableNode();
|
||||
const rootElem = rootNode.getFocusableElement();
|
||||
const oldId = rootElem.id;
|
||||
rootElem.setAttribute('id', null);
|
||||
|
||||
const errorMsgRegex =
|
||||
/Attempting to register a tree with a root element that has an invalid ID.+?/;
|
||||
assert.throws(
|
||||
() => this.focusManager.registerTree(this.testFocusableTree1),
|
||||
errorMsgRegex,
|
||||
);
|
||||
// Restore the ID for other tests.
|
||||
rootElem.id = oldId;
|
||||
});
|
||||
|
||||
test('for tree with empty throws error', function () {
|
||||
const rootNode = this.testFocusableTree1.getRootFocusableNode();
|
||||
const rootElem = rootNode.getFocusableElement();
|
||||
const oldId = rootElem.id;
|
||||
rootElem.setAttribute('id', '');
|
||||
|
||||
const errorMsgRegex =
|
||||
/Attempting to register a tree with a root element that has an invalid ID.+?/;
|
||||
assert.throws(
|
||||
() => this.focusManager.registerTree(this.testFocusableTree1),
|
||||
errorMsgRegex,
|
||||
);
|
||||
// Restore the ID for other tests.
|
||||
rootElem.id = oldId;
|
||||
});
|
||||
|
||||
test('for unmanaged tree does not overwrite tab index', function () {
|
||||
this.focusManager.registerTree(this.testFocusableTree1, false);
|
||||
|
||||
|
||||
@@ -348,6 +348,80 @@ suite('FocusableTreeTraverser', function () {
|
||||
});
|
||||
|
||||
suite('findFocusableNodeFor()', function () {
|
||||
test('for element without ID returns null', function () {
|
||||
const tree = this.testFocusableTree1;
|
||||
const rootNode = tree.getRootFocusableNode();
|
||||
const rootElem = rootNode.getFocusableElement();
|
||||
const oldId = rootElem.id;
|
||||
// Normally it's not valid to miss an ID, but it can realistically happen.
|
||||
rootElem.removeAttribute('id');
|
||||
|
||||
const finding = FocusableTreeTraverser.findFocusableNodeFor(
|
||||
rootElem,
|
||||
tree,
|
||||
);
|
||||
// Restore the ID for other tests.
|
||||
rootElem.setAttribute('id', oldId);
|
||||
|
||||
assert.isNull(finding);
|
||||
});
|
||||
|
||||
test('for element with null ID returns null', function () {
|
||||
const tree = this.testFocusableTree1;
|
||||
const rootNode = tree.getRootFocusableNode();
|
||||
const rootElem = rootNode.getFocusableElement();
|
||||
const oldId = rootElem.id;
|
||||
// Normally it's not valid to miss an ID, but it can realistically happen.
|
||||
rootElem.setAttribute('id', null);
|
||||
|
||||
const finding = FocusableTreeTraverser.findFocusableNodeFor(
|
||||
rootElem,
|
||||
tree,
|
||||
);
|
||||
// Restore the ID for other tests.
|
||||
rootElem.setAttribute('id', oldId);
|
||||
|
||||
assert.isNull(finding);
|
||||
});
|
||||
|
||||
test('for element with null ID string returns null', function () {
|
||||
const tree = this.testFocusableTree1;
|
||||
const rootNode = tree.getRootFocusableNode();
|
||||
const rootElem = rootNode.getFocusableElement();
|
||||
const oldId = rootElem.id;
|
||||
// This is a quirky version of the null variety above that's actually
|
||||
// functionallity equivalent (since 'null' is converted to a string).
|
||||
rootElem.setAttribute('id', 'null');
|
||||
|
||||
const finding = FocusableTreeTraverser.findFocusableNodeFor(
|
||||
rootElem,
|
||||
tree,
|
||||
);
|
||||
// Restore the ID for other tests.
|
||||
rootElem.setAttribute('id', oldId);
|
||||
|
||||
assert.isNull(finding);
|
||||
});
|
||||
|
||||
test('for element with empty ID returns null', function () {
|
||||
const tree = this.testFocusableTree1;
|
||||
const rootNode = tree.getRootFocusableNode();
|
||||
const rootElem = rootNode.getFocusableElement();
|
||||
const oldId = rootElem.id;
|
||||
// An empty ID is invalid since it will potentially conflict with other
|
||||
// elements, and element IDs must be unique for focus management.
|
||||
rootElem.setAttribute('id', '');
|
||||
|
||||
const finding = FocusableTreeTraverser.findFocusableNodeFor(
|
||||
rootElem,
|
||||
tree,
|
||||
);
|
||||
// Restore the ID for other tests.
|
||||
rootElem.setAttribute('id', oldId);
|
||||
|
||||
assert.isNull(finding);
|
||||
});
|
||||
|
||||
test('for root element returns root', function () {
|
||||
const tree = this.testFocusableTree1;
|
||||
const rootNode = tree.getRootFocusableNode();
|
||||
|
||||
@@ -434,13 +434,13 @@ suite('Keyboard Shortcut Items', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
// Do not undo if a gesture is in progress.
|
||||
suite('Gesture in progress', function () {
|
||||
// Do not undo if a drag is in progress.
|
||||
suite('Drag in progress', function () {
|
||||
testCases.forEach(function (testCase) {
|
||||
const testCaseName = testCase[0];
|
||||
const keyEvent = testCase[1];
|
||||
test(testCaseName, function () {
|
||||
sinon.stub(Blockly.Gesture, 'inProgress').returns(true);
|
||||
sinon.stub(this.workspace, 'isDragging').returns(true);
|
||||
this.injectionDiv.dispatchEvent(keyEvent);
|
||||
sinon.assert.notCalled(this.undoSpy);
|
||||
sinon.assert.notCalled(this.hideChaffSpy);
|
||||
@@ -494,13 +494,13 @@ suite('Keyboard Shortcut Items', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
// Do not undo if a gesture is in progress.
|
||||
suite('Gesture in progress', function () {
|
||||
// Do not redo if a drag is in progress.
|
||||
suite('Drag in progress', function () {
|
||||
testCases.forEach(function (testCase) {
|
||||
const testCaseName = testCase[0];
|
||||
const keyEvent = testCase[1];
|
||||
test(testCaseName, function () {
|
||||
sinon.stub(Blockly.Gesture, 'inProgress').returns(true);
|
||||
sinon.stub(this.workspace, 'isDragging').returns(true);
|
||||
this.injectionDiv.dispatchEvent(keyEvent);
|
||||
sinon.assert.notCalled(this.redoSpy);
|
||||
sinon.assert.notCalled(this.hideChaffSpy);
|
||||
@@ -534,8 +534,8 @@ suite('Keyboard Shortcut Items', function () {
|
||||
sinon.assert.calledWith(this.undoSpy, true);
|
||||
sinon.assert.calledOnce(this.hideChaffSpy);
|
||||
});
|
||||
test('Not called when a gesture is in progress', function () {
|
||||
sinon.stub(Blockly.Gesture, 'inProgress').returns(true);
|
||||
test('Not called when a drag is in progress', function () {
|
||||
sinon.stub(this.workspace, 'isDragging').returns(true);
|
||||
this.injectionDiv.dispatchEvent(this.ctrlYEvent);
|
||||
sinon.assert.notCalled(this.undoSpy);
|
||||
sinon.assert.notCalled(this.hideChaffSpy);
|
||||
|
||||
Reference in New Issue
Block a user