mirror of
https://github.com/google/blockly.git
synced 2026-01-10 10:27:08 +01:00
Test gesture triggered events and event assertion refactor (#4155)
* Refactor event assertion helpers and add assertions to gesture test.
This commit is contained in:
@@ -9,7 +9,8 @@
|
||||
"sinon": false,
|
||||
"assertArrayEquals": true,
|
||||
"assertEventEquals": true,
|
||||
"assertLastCallEventArgEquals": true,
|
||||
"assertEventFired": true,
|
||||
"assertEventNotFired": true,
|
||||
"assertNthCallEventArgEquals": true,
|
||||
"assertVariableValues": true,
|
||||
"captureWarnings": true,
|
||||
|
||||
@@ -49,9 +49,10 @@ suite('Comments', function() {
|
||||
this.comment.setVisible(true);
|
||||
chai.assert.isTrue(this.comment.isVisible());
|
||||
assertEditable(this.comment);
|
||||
assertLastCallEventArgEquals(
|
||||
this.eventsFireStub, Blockly.Events.UI, this.workspace.id, this.block.id,
|
||||
{element: 'commentOpen', oldValue: false, newValue: true});
|
||||
assertEventFired(
|
||||
this.eventsFireStub, Blockly.Events.Ui,
|
||||
{element: 'commentOpen', oldValue: false, newValue: true},
|
||||
this.workspace.id, this.block.id);
|
||||
});
|
||||
test('Not Editable', function() {
|
||||
sinon.stub(this.block, 'isEditable').returns(false);
|
||||
@@ -59,9 +60,10 @@ suite('Comments', function() {
|
||||
this.comment.setVisible(true);
|
||||
chai.assert.isTrue(this.comment.isVisible());
|
||||
assertNotEditable(this.comment);
|
||||
assertLastCallEventArgEquals(
|
||||
this.eventsFireStub, Blockly.Events.UI, this.workspace.id, this.block.id,
|
||||
{element: 'commentOpen', oldValue: false, newValue: true});
|
||||
assertEventFired(
|
||||
this.eventsFireStub, Blockly.Events.Ui,
|
||||
{element: 'commentOpen', oldValue: false, newValue: true},
|
||||
this.workspace.id, this.block.id);
|
||||
});
|
||||
test('Editable -> Not Editable', function() {
|
||||
this.comment.setVisible(true);
|
||||
@@ -69,12 +71,11 @@ suite('Comments', function() {
|
||||
|
||||
this.comment.updateEditable();
|
||||
chai.assert.isTrue(this.comment.isVisible());
|
||||
assertNotEditable(this.comment);assertLastCallEventArgEquals(
|
||||
this.eventsFireStub, Blockly.Events.UI, this.workspace.id, this.block.id,
|
||||
{element: 'commentOpen', oldValue: false, newValue: true});
|
||||
assertLastCallEventArgEquals(
|
||||
this.eventsFireStub, Blockly.Events.UI, this.workspace.id, this.block.id,
|
||||
{element: 'commentOpen', oldValue: false, newValue: true});
|
||||
assertNotEditable(this.comment);
|
||||
assertEventFired(
|
||||
this.eventsFireStub, Blockly.Events.Ui,
|
||||
{element: 'commentOpen', oldValue: false, newValue: true},
|
||||
this.workspace.id, this.block.id);
|
||||
});
|
||||
test('Not Editable -> Editable', function() {
|
||||
var editableStub = sinon.stub(this.block, 'isEditable').returns(false);
|
||||
@@ -84,9 +85,10 @@ suite('Comments', function() {
|
||||
this.comment.updateEditable();
|
||||
chai.assert.isTrue(this.comment.isVisible());
|
||||
assertEditable(this.comment);
|
||||
assertLastCallEventArgEquals(
|
||||
this.eventsFireStub, Blockly.Events.UI, this.workspace.id, this.block.id,
|
||||
{element: 'commentOpen', oldValue: false, newValue: true});
|
||||
assertEventFired(
|
||||
this.eventsFireStub, Blockly.Events.Ui,
|
||||
{element: 'commentOpen', oldValue: false, newValue: true},
|
||||
this.workspace.id, this.block.id);
|
||||
});
|
||||
});
|
||||
suite('Set/Get Bubble Size', function() {
|
||||
|
||||
@@ -624,7 +624,6 @@ suite('Events', function() {
|
||||
try {
|
||||
var toolbox = document.getElementById('toolbox-categories');
|
||||
var workspaceSvg = Blockly.inject('blocklyDiv', {toolbox: toolbox});
|
||||
var changeListenerSpy = createFireChangeListenerSpy(workspaceSvg);
|
||||
var TEST_BLOCK_ID = 'test_block_id';
|
||||
var genUidStub = createGenUidStubWithReturns(
|
||||
[TEST_BLOCK_ID, 'test_group_id']);
|
||||
@@ -635,6 +634,11 @@ suite('Events', function() {
|
||||
var expectedOldXml = Blockly.Xml.blockToDomWithXY(block);
|
||||
var expectedId = block.id;
|
||||
|
||||
// Run all queued events.
|
||||
this.clock.runAll();
|
||||
|
||||
this.eventsFireSpy.resetHistory();
|
||||
var changeListenerSpy = createFireChangeListenerSpy(workspaceSvg);
|
||||
block.dispose();
|
||||
|
||||
// Run all queued events.
|
||||
@@ -644,12 +648,14 @@ suite('Events', function() {
|
||||
// the event group's ID for creating block.
|
||||
sinon.assert.calledTwice(genUidStub);
|
||||
|
||||
assertLastCallEventArgEquals(
|
||||
this.eventsFireSpy, Blockly.Events.DELETE, workspaceSvg.id,
|
||||
expectedId, {oldXml: expectedOldXml, group: ''});
|
||||
assertLastCallEventArgEquals(
|
||||
changeListenerSpy, Blockly.Events.DELETE, workspaceSvg.id,
|
||||
expectedId, {oldXml: expectedOldXml, group: ''});
|
||||
assertNthCallEventArgEquals(
|
||||
this.eventsFireSpy, 0, Blockly.Events.Delete,
|
||||
{oldXml: expectedOldXml, group: ''},
|
||||
workspaceSvg.id, expectedId);
|
||||
assertNthCallEventArgEquals(
|
||||
changeListenerSpy, 0, Blockly.Events.Delete,
|
||||
{oldXml: expectedOldXml, group: ''},
|
||||
workspaceSvg.id, expectedId);
|
||||
|
||||
// Expect the workspace to not have a variable with ID 'test_block_id'.
|
||||
chai.assert.isNull(this.workspace.getVariableById(TEST_BLOCK_ID));
|
||||
@@ -683,12 +689,12 @@ suite('Events', function() {
|
||||
'Undo stack length');
|
||||
|
||||
assertNthCallEventArgEquals(
|
||||
this.changeListenerSpy, 0, Blockly.Events.VAR_CREATE, this.workspace.id,
|
||||
undefined, {group: TEST_GROUP_ID, varId: TEST_VAR_ID,
|
||||
varName: TEST_VAR_NAME}, 'varCreate:');
|
||||
this.changeListenerSpy, 0, Blockly.Events.VarCreate,
|
||||
{group: TEST_GROUP_ID, varId: TEST_VAR_ID, varName: TEST_VAR_NAME},
|
||||
this.workspace.id, undefined);
|
||||
assertNthCallEventArgEquals(
|
||||
this.changeListenerSpy, 1, Blockly.Events.CREATE, this.workspace.id,
|
||||
TEST_BLOCK_ID, {group: TEST_GROUP_ID}, 'block create:');
|
||||
this.changeListenerSpy, 1, Blockly.Events.Create,
|
||||
{group: TEST_GROUP_ID}, this.workspace.id, TEST_BLOCK_ID);
|
||||
|
||||
// Expect the workspace to have a variable with ID 'test_var_id'.
|
||||
chai.assert.isNotNull(this.workspace.getVariableById(TEST_VAR_ID));
|
||||
@@ -728,17 +734,17 @@ suite('Events', function() {
|
||||
'Undo stack length');
|
||||
|
||||
assertNthCallEventArgEquals(
|
||||
this.changeListenerSpy, 0, Blockly.Events.VAR_CREATE, this.workspace.id,
|
||||
undefined, {group: TEST_GROUP_ID, varId: TEST_VAR_ID,
|
||||
varName: TEST_VAR_NAME}, 'varCreate:');
|
||||
this.changeListenerSpy, 0, Blockly.Events.VarCreate,
|
||||
{group: TEST_GROUP_ID, varId: TEST_VAR_ID, varName: TEST_VAR_NAME},
|
||||
this.workspace.id, undefined);
|
||||
assertNthCallEventArgEquals(
|
||||
this.changeListenerSpy, 1, Blockly.Events.CREATE, this.workspace.id,
|
||||
TEST_BLOCK_ID, {group: TEST_GROUP_ID}, 'block create:');
|
||||
this.changeListenerSpy, 1, Blockly.Events.Create,
|
||||
{group: TEST_GROUP_ID}, this.workspace.id, TEST_BLOCK_ID);
|
||||
|
||||
// Finished loading event should not be part of event group.
|
||||
assertNthCallEventArgEquals(
|
||||
this.changeListenerSpy, 2, Blockly.Events.FINISHED_LOADING, this.workspace.id,
|
||||
undefined, {group: ''}, 'finished loading:');
|
||||
this.changeListenerSpy, 2, Blockly.Events.FinishedLoading,
|
||||
{group: ''}, this.workspace.id, undefined);
|
||||
|
||||
// Expect the workspace to have a variable with ID 'test_var_id'.
|
||||
chai.assert.isNotNull(this.workspace.getVariableById(TEST_VAR_ID));
|
||||
|
||||
@@ -11,12 +11,13 @@
|
||||
'use strict';
|
||||
|
||||
suite('Gesture', function() {
|
||||
function testGestureIsFieldClick(block, isFieldClick){
|
||||
function testGestureIsFieldClick(block, isFieldClick, eventsFireStub){
|
||||
var field = block.getField('NAME');
|
||||
var eventTarget = field.getClickTarget_();
|
||||
chai.assert.exists(eventTarget,
|
||||
'Precondition: missing click target for field');
|
||||
|
||||
eventsFireStub.resetHistory();
|
||||
dispatchPointerEvent(eventTarget, 'pointerdown');
|
||||
|
||||
var fieldWorkspace = field.sourceBlock_.workspace;
|
||||
@@ -31,6 +32,12 @@ suite('Gesture', function() {
|
||||
|
||||
sinon.assert.called(isFieldClickSpy);
|
||||
chai.assert.isTrue(isFieldClickSpy.alwaysReturned(isFieldClick));
|
||||
|
||||
|
||||
assertEventFired(eventsFireStub, Blockly.Events.Ui,
|
||||
{element: 'selected', oldValue: null, newValue: block.id},
|
||||
fieldWorkspace.id, null);
|
||||
assertEventNotFired(eventsFireStub, Blockly.Events.Ui, {element: 'click'});
|
||||
}
|
||||
|
||||
function getTopFlyoutBlock(flyout) {
|
||||
@@ -61,7 +68,7 @@ suite('Gesture', function() {
|
||||
block.initSvg();
|
||||
block.render();
|
||||
|
||||
testGestureIsFieldClick(block, true);
|
||||
testGestureIsFieldClick(block, true, this.eventsFireStub);
|
||||
});
|
||||
|
||||
test('Field click - Auto close flyout', function() {
|
||||
@@ -71,7 +78,7 @@ suite('Gesture', function() {
|
||||
flyout.autoClose = true;
|
||||
|
||||
var block = getTopFlyoutBlock(flyout);
|
||||
testGestureIsFieldClick(block, false);
|
||||
testGestureIsFieldClick(block, false, this.eventsFireStub);
|
||||
});
|
||||
|
||||
test('Field click - Always open flyout', function() {
|
||||
@@ -81,7 +88,7 @@ suite('Gesture', function() {
|
||||
flyout.autoClose = false;
|
||||
|
||||
var block = getTopFlyoutBlock(flyout);
|
||||
testGestureIsFieldClick(block, true);
|
||||
testGestureIsFieldClick(block, true, this.eventsFireStub);
|
||||
});
|
||||
|
||||
test('Shift click in accessibility mode - moves the cursor', function() {
|
||||
|
||||
@@ -175,6 +175,52 @@ function createFireChangeListenerSpy(workspace) {
|
||||
return sinon.spy(workspace, 'fireChangeListener');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts whether the given xml property has the expected property.
|
||||
* @param {!Node} xmlValue The xml value to check.
|
||||
* @param {!Node|string} expectedValue The expected value.
|
||||
* @param {string=} message Optional message to use in assert message.
|
||||
* @private
|
||||
*/
|
||||
function assertXmlPropertyEqual_(xmlValue, expectedValue, message) {
|
||||
var value = Blockly.Xml.domToText(xmlValue);
|
||||
if (expectedValue instanceof Node) {
|
||||
expectedValue = Blockly.Xml.domToText(expectedValue);
|
||||
}
|
||||
chai.assert.equal(value, expectedValue, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given object has the expected xml properties.
|
||||
* @param {Object} obj The object to check.
|
||||
* @param {Object<string, Node|string>} expectedXmlProperties The expected xml
|
||||
* properties.
|
||||
* @private
|
||||
*/
|
||||
function assertXmlProperties_(obj, expectedXmlProperties) {
|
||||
Object.keys(expectedXmlProperties).map((key) => {
|
||||
var value = obj[key];
|
||||
var expectedValue = expectedXmlProperties[key];
|
||||
if (expectedValue === undefined) {
|
||||
chai.assert.isUndefined(value,
|
||||
'Expected ' + key + ' property to be undefined');
|
||||
return;
|
||||
}
|
||||
chai.assert.exists(value, 'Expected ' + key + ' property to exist');
|
||||
assertXmlPropertyEqual_(value, expectedValue, 'Checking property ' + key);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether given key indicates that the property is xml.
|
||||
* @param {string} key The key to check.
|
||||
* @return {boolean} Whether the given key is for xml property.
|
||||
* @private
|
||||
*/
|
||||
function isXmlProperty_(key) {
|
||||
return key.toLowerCase().endsWith('xml');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given event has the expected values.
|
||||
* @param {!Blockly.Event.Abstract} event The event to check.
|
||||
@@ -184,44 +230,100 @@ function createFireChangeListenerSpy(workspace) {
|
||||
* @param {!Object<string, *>} expectedProperties Map of of additional expected
|
||||
* properties to check on fired event.
|
||||
* @param {string=} message Optional message to prepend assert messages.
|
||||
* @private
|
||||
*/
|
||||
function assertEventEquals(event, expectedType,
|
||||
expectedWorkspaceId, expectedBlockId, expectedProperties, message) {
|
||||
var prependMessage = message ? message + ' ' : '';
|
||||
prependMessage += 'Event fired ';
|
||||
chai.assert.equal(event.type, expectedType,
|
||||
prependMessage + 'Event fired type');
|
||||
prependMessage + 'type');
|
||||
chai.assert.equal(event.workspaceId, expectedWorkspaceId,
|
||||
prependMessage + 'Event fired workspace id');
|
||||
prependMessage + 'workspace id');
|
||||
chai.assert.equal(event.blockId, expectedBlockId,
|
||||
prependMessage + 'Event fired block id');
|
||||
prependMessage + 'block id');
|
||||
Object.keys(expectedProperties).map((key) => {
|
||||
var value = event[key];
|
||||
var expectedValue = expectedProperties[key];
|
||||
if (key.endsWith('Xml')) {
|
||||
value = Blockly.Xml.domToText(value);
|
||||
expectedValue = Blockly.Xml.domToText(expectedValue);
|
||||
if (expectedValue === undefined) {
|
||||
chai.assert.isUndefined(value, prependMessage + key);
|
||||
return;
|
||||
}
|
||||
chai.assert.exists(value, prependMessage + key);
|
||||
if (isXmlProperty_(key)) {
|
||||
assertXmlPropertyEqual_(value, expectedValue,
|
||||
prependMessage + key);
|
||||
} else {
|
||||
chai.assert.equal(value, expectedValue,
|
||||
prependMessage + key);
|
||||
}
|
||||
chai.assert.equal(value, expectedValue, prependMessage + 'Event fired ' + key);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the event passed to the last call of the given spy has the
|
||||
* expected values. Assumes that the event is passed as the first argument.
|
||||
* @param {!SinonSpy} spy The spy to use.
|
||||
* @param {string} expectedType Expected type of event fired.
|
||||
* @param {string} expectedWorkspaceId Expected workspace id of event fired.
|
||||
* @param {string} expectedBlockId Expected block id of event fired.
|
||||
* Asserts that an event with the given values was fired.
|
||||
* @param {!SinonSpy|!SinonSpyCall} spy The spy or spy call to use.
|
||||
* @param {function(new:Blockly.Events.Abstract)} instanceType Expected instance
|
||||
* type of event fired.
|
||||
* @param {!Object<string, *>} expectedProperties Map of of expected properties
|
||||
* to check on fired event.
|
||||
* @param {string=} message Optional message to prepend assert messages.
|
||||
* @param {string} expectedWorkspaceId Expected workspace id of event fired.
|
||||
* @param {?string=} expectedBlockId Expected block id of event fired.
|
||||
*/
|
||||
function assertLastCallEventArgEquals(spy, expectedType,
|
||||
expectedWorkspaceId, expectedBlockId, expectedProperties, message) {
|
||||
var event = spy.lastCall.firstArg;
|
||||
assertEventEquals(event, expectedType, expectedWorkspaceId, expectedBlockId,
|
||||
expectedProperties, message);
|
||||
function assertEventFired(spy, instanceType, expectedProperties,
|
||||
expectedWorkspaceId, expectedBlockId) {
|
||||
expectedProperties = Object.assign({
|
||||
type: instanceType.prototype.type,
|
||||
workspaceId: expectedWorkspaceId,
|
||||
blockId: expectedBlockId,
|
||||
}, expectedProperties);
|
||||
var expectedEvent =
|
||||
sinon.match.instanceOf(instanceType).and(sinon.match(expectedProperties));
|
||||
sinon.assert.calledWith(spy, expectedEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that an event with the given values was not fired.
|
||||
* @param {!SpyCall} spy The spy to use.
|
||||
* @param {function(new:Blockly.Events.Abstract)} instanceType Expected instance
|
||||
* type of event fired.
|
||||
* @param {!Object<string, *>} expectedProperties Map of of expected properties
|
||||
* to check on fired event.
|
||||
* @param {string=} expectedWorkspaceId Expected workspace id of event fired.
|
||||
* @param {?string=} expectedBlockId Expected block id of event fired.
|
||||
*/
|
||||
function assertEventNotFired(spy, instanceType, expectedProperties,
|
||||
expectedWorkspaceId, expectedBlockId) {
|
||||
expectedProperties.type = instanceType.prototype.type;
|
||||
if (expectedWorkspaceId !== undefined) {
|
||||
expectedProperties.workspaceId = expectedWorkspaceId;
|
||||
}
|
||||
if (expectedBlockId !== undefined) {
|
||||
expectedProperties.blockId = expectedBlockId;
|
||||
}
|
||||
var expectedEvent =
|
||||
sinon.match.instanceOf(instanceType).and(sinon.match(expectedProperties));
|
||||
sinon.assert.neverCalledWith(spy, expectedEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out xml properties from given object based on key.
|
||||
* @param {Object<string, *>} properties The properties to filter.
|
||||
* @return {[Object<string, *>, Object<string, *>]} A list containing split non
|
||||
* xml properties and xml properties.
|
||||
* @private
|
||||
*/
|
||||
function splitByXmlProperties_(properties) {
|
||||
var xmlProperties = {};
|
||||
var nonXmlProperties = {};
|
||||
Object.keys(properties).forEach((key) => {
|
||||
if (isXmlProperty_(key)) {
|
||||
xmlProperties[key] = properties[key];
|
||||
return false;
|
||||
} else {
|
||||
nonXmlProperties[key] = properties[key];
|
||||
}
|
||||
});
|
||||
return [nonXmlProperties, xmlProperties];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,18 +331,24 @@ function assertLastCallEventArgEquals(spy, expectedType,
|
||||
* expected values. Assumes that the event is passed as the first argument.
|
||||
* @param {!SinonSpy} spy The spy to use.
|
||||
* @param {number} n Which call to check.
|
||||
* @param {string} expectedType Expected type of event fired.
|
||||
* @param {string} expectedWorkspaceId Expected workspace id of event fired.
|
||||
* @param {string} expectedBlockId Expected block id of event fired.
|
||||
* @param {function(new:Blockly.Events.Abstract)} instanceType Expected instance
|
||||
* type of event fired.
|
||||
* @param {Object<string, *>} expectedProperties Map of of expected properties
|
||||
* to check on fired event.
|
||||
* @param {string=} message Optional message to prepend assert messages.
|
||||
* @param {string} expectedWorkspaceId Expected workspace id of event fired.
|
||||
* @param {?string=} expectedBlockId Expected block id of event fired.
|
||||
*/
|
||||
function assertNthCallEventArgEquals(spy, n, expectedType,
|
||||
expectedWorkspaceId, expectedBlockId, expectedProperties, message) {
|
||||
var event = spy.getCall(n).firstArg;
|
||||
assertEventEquals(event, expectedType, expectedWorkspaceId, expectedBlockId,
|
||||
expectedProperties, message);
|
||||
function assertNthCallEventArgEquals(spy, n, instanceType, expectedProperties,
|
||||
expectedWorkspaceId, expectedBlockId) {
|
||||
var nthCall = spy.getCall(n);
|
||||
var splitProperties = splitByXmlProperties_(expectedProperties);
|
||||
var nonXmlProperties = splitProperties[0];
|
||||
var xmlProperties = splitProperties[1];
|
||||
|
||||
assertEventFired(nthCall, instanceType, nonXmlProperties, expectedWorkspaceId,
|
||||
expectedBlockId);
|
||||
var eventArg = nthCall.firstArg;
|
||||
assertXmlProperties_(eventArg, xmlProperties);
|
||||
}
|
||||
|
||||
function defineStackBlock() {
|
||||
|
||||
@@ -149,9 +149,9 @@ suite('Theme', function() {
|
||||
// Checks that the toolbox refreshed method was called
|
||||
sinon.assert.calledOnce(refreshToolboxSelectionStub);
|
||||
|
||||
assertLastCallEventArgEquals(
|
||||
this.eventsFireStub, Blockly.Events.UI, workspace.id,
|
||||
null, {element: 'theme'});
|
||||
assertEventFired(
|
||||
this.eventsFireStub, Blockly.Events.Ui, {element: 'theme'},
|
||||
workspace.id, null);
|
||||
} finally {
|
||||
workspaceTeardown.call(this, workspace);
|
||||
undefineThemeTestBlocks();
|
||||
|
||||
@@ -72,9 +72,11 @@ suite("Trashcan", function() {
|
||||
// Stub flyout interaction.
|
||||
var showFlyoutStub = sinon.stub(this.trashcan.flyout, "show");
|
||||
this.trashcan.click();
|
||||
assertLastCallEventArgEquals(
|
||||
this.eventsFireStub, Blockly.Events.UI, this.workspace.id, undefined,
|
||||
{element: 'trashcanOpen', oldValue: null, newValue: true});
|
||||
|
||||
assertEventFired(
|
||||
this.eventsFireStub, Blockly.Events.Ui,
|
||||
{element: 'trashcanOpen', oldValue: null, newValue: true},
|
||||
this.workspace.id, null);
|
||||
sinon.assert.calledOnce(showFlyoutStub);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user