Bug 1291322 - Manually throttle CssRuleView tests; r?tromey
This patch provides a mechanism for overriding the throttle function in
inspector tests with a manually controllable throttle. I did modify a
few tests to not check for a second "ruleview-changed" as it required
using a setTimeout to defer the code to the next frame to get it to work
as it currently exists. I figured it was safer to just not flush the
throttle, and reduce how many "ruleview-changed" events that the test
expected.
MozReview-Commit-ID: 8AIMouZjLBf
--- a/devtools/client/inspector/rules/test/browser_rules_add-property_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_add-property_02.js
@@ -35,20 +35,19 @@ add_task(function* () {
// (see bug 761665).
EventUtils.synthesizeMouse(input, 1, 1, {}, view.styleWindow);
input.select();
info("Entering the property name");
editor.input.value = "background-color";
info("Pressing RETURN and waiting for the value field focus");
- // Pressing ENTER triggeres 2 changed events, one for the new property, and
- // one for the preview.
- let onNameAdded = waitForNEvents(view, "ruleview-changed", 2);
+ let onNameAdded = view.once("ruleview-changed");
EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
+
yield onNameAdded;
editor = inplaceEditor(view.styleDocument.activeElement);
is(ruleEditor.rule.textProps.length, 2,
"Should have created a new text property.");
is(ruleEditor.propertyList.children.length, 2,
"Should have created a property editor.");
--- a/devtools/client/inspector/rules/test/browser_rules_completion-existing-property_01.js
+++ b/devtools/client/inspector/rules/test/browser_rules_completion-existing-property_01.js
@@ -115,16 +115,19 @@ function* testCompletion([key, completio
// Also listening for popup opened/closed events if needed.
let popupEvent = open ? "popup-opened" : "popup-closed";
let onPopupEvent = editor.popup.isOpen !== open ? once(editor.popup, popupEvent) : null;
info("Synthesizing key " + key);
EventUtils.synthesizeKey(key, {}, view.styleWindow);
+ // Flush the throttle for the preview text.
+ view.throttle.flush();
+
yield onSuggest;
yield onPopupEvent;
info("Checking the state");
if (completion !== null) {
is(editor.input.value, completion, "Correct value is autocompleted");
}
if (!open) {
--- a/devtools/client/inspector/rules/test/browser_rules_completion-existing-property_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_completion-existing-property_02.js
@@ -93,16 +93,20 @@ function* testCompletion([key, modifiers
info("Synthesizing key " + key + ", modifiers: " + Object.keys(modifiers));
// Also listening for popup opened/closed events if needed.
let popupEvent = open ? "popup-opened" : "popup-closed";
let onPopupEvent = editor.popup.isOpen !== open ? once(editor.popup, popupEvent) : null;
EventUtils.synthesizeKey(key, modifiers, view.styleWindow);
+
+ // Flush the throttle for the preview text.
+ view.throttle.flush();
+
yield onDone;
yield onPopupEvent;
// The key might have been a TAB or shift-TAB, in which case the editor will
// be a new one
editor = inplaceEditor(view.styleDocument.activeElement);
info("Checking the state");
--- a/devtools/client/inspector/rules/test/browser_rules_completion-new-property_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_completion-new-property_02.js
@@ -101,16 +101,20 @@ function* testCompletion([key, modifiers
}
// Also listening for popup opened/closed events if needed.
let popupEvent = open ? "popup-opened" : "popup-closed";
let onPopupEvent = editor.popup.isOpen !== open ? once(editor.popup, popupEvent) : null;
info("Synthesizing key " + key + ", modifiers: " + Object.keys(modifiers));
EventUtils.synthesizeKey(key, modifiers, view.styleWindow);
+
+ // Flush the throttle for the preview text.
+ view.throttle.flush();
+
yield onDone;
yield onPopupEvent;
info("Checking the state");
if (completion !== null) {
// The key might have been a TAB or shift-TAB, in which case the editor will
// be a new one
editor = inplaceEditor(view.styleDocument.activeElement);
--- a/devtools/client/inspector/rules/test/browser_rules_completion-new-property_04.js
+++ b/devtools/client/inspector/rules/test/browser_rules_completion-new-property_04.js
@@ -36,22 +36,24 @@ add_task(function* () {
is(bgcItem.label, "background-color",
"Check the expected completion element is background-color.");
editor.popup.selectedIndex = itemIndex;
info("Select the background-color suggestion with a mouse click.");
let onSuggest = editor.once("after-suggest");
let node = editor.popup.elements.get(bgcItem);
EventUtils.synthesizeMouseAtCenter(node, {}, editor.popup._window);
+
yield onSuggest;
is(editor.input.value, "background-color", "Correct value is autocompleted");
info("Press RETURN to move the focus to a property value editor.");
- let onModifications = waitForNEvents(view, "ruleview-changed", 2);
+ let onModifications = view.once("ruleview-changed");
EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
+
yield onModifications;
// Getting the new value editor after focus
editor = inplaceEditor(view.styleDocument.activeElement);
let textProp = ruleEditor.rule.textProps[1];
is(ruleEditor.rule.textProps.length, 2,
"Created a new text property.");
--- a/devtools/client/inspector/rules/test/browser_rules_completion-new-property_multiline.js
+++ b/devtools/client/inspector/rules/test/browser_rules_completion-new-property_multiline.js
@@ -94,16 +94,17 @@ add_task(function* () {
info("Select the background-color suggestion with a mouse click.");
let onRuleviewChanged = view.once("ruleview-changed");
let onSuggest = editor.once("after-suggest");
let node = editor.popup._list.childNodes[editor.popup.selectedIndex];
EventUtils.synthesizeMouseAtCenter(node, {}, editor.popup._window);
+ view.throttle.flush();
yield onSuggest;
yield onRuleviewChanged;
is(editor.input.value, EXPECTED_CSS_VALUE,
"Input value correctly autocompleted");
info("Press ESCAPE to leave the input.");
onRuleviewChanged = view.once("ruleview-changed");
--- a/devtools/client/inspector/rules/test/browser_rules_edit-property-commit.js
+++ b/devtools/client/inspector/rules/test/browser_rules_edit-property-commit.js
@@ -67,16 +67,17 @@ function* runTestData(view, {value, comm
let editor = yield focusEditableField(view, propEditor.valueSpan);
is(inplaceEditor(propEditor.valueSpan), editor,
"Focused editor should be the value span.");
info("Entering test data " + value);
let onRuleViewChanged = view.once("ruleview-changed");
EventUtils.sendString(value, view.styleWindow);
+ view.throttle.flush();
yield onRuleViewChanged;
info("Entering the commit key " + commitKey + " " + modifiers);
onRuleViewChanged = view.once("ruleview-changed");
let onBlur = once(editor.input, "blur");
EventUtils.synthesizeKey(commitKey, modifiers);
yield onBlur;
yield onRuleViewChanged;
--- a/devtools/client/inspector/rules/test/browser_rules_edit-property-computed.js
+++ b/devtools/client/inspector/rules/test/browser_rules_edit-property-computed.js
@@ -38,16 +38,18 @@ function* editAndCheck(view) {
"padding-top", newPaddingValue);
let onRefreshAfterPreview = once(view, "ruleview-changed");
info("Entering a new value");
EventUtils.sendString(newPaddingValue, view.styleWindow);
info("Waiting for the throttled previewValue to apply the " +
"changes to document");
+
+ view.throttle.flush();
yield onPropertyChange;
info("Waiting for ruleview-refreshed after previewValue was applied.");
yield onRefreshAfterPreview;
let onBlur = once(editor.input, "blur");
info("Entering the commit key and finishing edit");
--- a/devtools/client/inspector/rules/test/browser_rules_edit-property-increments.js
+++ b/devtools/client/inspector/rules/test/browser_rules_edit-property-increments.js
@@ -230,16 +230,17 @@ function* runIncrementTest(propertyEdito
for (let test in tests) {
yield testIncrement(editor, tests[test], view, propertyEditor);
}
// Blur the field to put back the UI in its initial state (and avoid pending
// requests when the test ends).
let onRuleViewChanged = view.once("ruleview-changed");
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
+ view.throttle.flush();
yield onRuleViewChanged;
}
function* testIncrement(editor, options, view) {
editor.input.value = options.start;
let input = editor.input;
if (options.selectAll) {
@@ -258,16 +259,19 @@ function* testIncrement(editor, options,
if (options.pageDown) {
key = "VK_PAGE_DOWN";
} else if (options.pageUp) {
key = "VK_PAGE_UP";
}
EventUtils.synthesizeKey(key, {altKey: options.alt, shiftKey: options.shift},
view.styleWindow);
+
yield onKeyUp;
+
// Only expect a change if the value actually changed!
- if (options.start !== options.end) {
+ if (options.start !== options.end) {
+ view.throttle.flush();
yield onRuleViewChanged;
}
is(input.value, options.end, "Value changed to " + options.end);
}
--- a/devtools/client/inspector/rules/test/browser_rules_edit-property_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_edit-property_02.js
@@ -64,16 +64,17 @@ function* testEditProperty(inspector, ru
info("Entering a value following by a semi-colon to commit it");
let onBlur = once(editor.input, "blur");
// Use sendChar() to pass each character as a string so that we can test
// prop.editor.warning.hidden after each character.
for (let ch of "red;") {
let onPreviewDone = ruleView.once("ruleview-changed");
EventUtils.sendChar(ch, ruleView.styleWindow);
+ ruleView.throttle.flush();
yield onPreviewDone;
is(prop.editor.warning.hidden, true,
"warning triangle is hidden or shown as appropriate");
}
yield onBlur;
let newValue = yield executeInContent("Test:GetRulePropertyValue", {
styleSheetIndex: 0,
--- a/devtools/client/inspector/rules/test/browser_rules_keybindings.js
+++ b/devtools/client/inspector/rules/test/browser_rules_keybindings.js
@@ -17,21 +17,20 @@ add_task(function* () {
info("Focus the new property editable field to create a color property");
let ruleEditor = getRuleViewRuleEditor(view, 0);
let editor = yield focusNewRuleViewProperty(ruleEditor);
editor.input.value = "color";
info("Typing ENTER to focus the next field: property value");
let onFocus = once(brace.parentNode, "focus", true);
- // The rule view changes twice, once for the first field to loose focus
- // and a second time for the second field to gain focus
- let onRuleViewChanged = view.once("ruleview-changed").then(
- () => view.once("ruleview-changed"));
+ let onRuleViewChanged = view.once("ruleview-changed");
+
EventUtils.sendKey("return");
+
yield onFocus;
yield onRuleViewChanged;
ok(true, "The value field was focused");
info("Entering a property value");
editor = getCurrentInplaceEditor(view);
editor.input.value = "green";
--- a/devtools/client/inspector/rules/test/browser_rules_livepreview.js
+++ b/devtools/client/inspector/rules/test/browser_rules_livepreview.js
@@ -48,16 +48,17 @@ function* testLivePreviewData(data, rule
info("Focusing the property value inplace-editor");
let editor = yield focusEditableField(ruleView, propEditor.valueSpan);
is(inplaceEditor(propEditor.valueSpan), editor,
"The focused editor is the value");
info("Entering value in the editor: " + data.value);
let onPreviewDone = ruleView.once("ruleview-changed");
EventUtils.sendString(data.value, ruleView.styleWindow);
+ ruleView.throttle.flush();
yield onPreviewDone;
let onValueDone = ruleView.once("ruleview-changed");
if (data.escape) {
EventUtils.synthesizeKey("VK_ESCAPE", {});
} else {
EventUtils.synthesizeKey("VK_RETURN", {});
}
--- a/devtools/client/inspector/rules/test/browser_rules_multiple-properties-unfinished_01.js
+++ b/devtools/client/inspector/rules/test/browser_rules_multiple-properties-unfinished_01.js
@@ -14,19 +14,17 @@ add_task(function* () {
let {inspector, view} = yield openRuleView();
yield selectNode("div", inspector);
yield testCreateNewMultiUnfinished(inspector, view);
});
function* testCreateNewMultiUnfinished(inspector, view) {
let ruleEditor = getRuleViewRuleEditor(view, 0);
let onMutation = inspector.once("markupmutation");
- // There are 2 rule-view updates: one for the preview and one for
- // the final commit.
- let onRuleViewChanged = waitForNEvents(view, "ruleview-changed", 2);
+ let onRuleViewChanged = view.once("ruleview-changed");
yield createNewRuleViewProperty(ruleEditor,
"color:blue;background : orange ; text-align:center; border-color: ");
yield onMutation;
yield onRuleViewChanged;
is(ruleEditor.rule.textProps.length, 4,
"Should have created new text properties.");
is(ruleEditor.propertyList.children.length, 4,
--- a/devtools/client/inspector/rules/test/browser_rules_multiple_properties_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_multiple_properties_02.js
@@ -10,20 +10,17 @@
const TEST_URI = "<div>Test Element</div>";
add_task(function* () {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {inspector, view} = yield openRuleView();
yield selectNode("div", inspector);
let ruleEditor = getRuleViewRuleEditor(view, 0);
- // Expect 2 ruleview-changed events.
- // - one when focusing the property-name editor
- // - one after pressing RETURN, which will focus the property-value editor
- let onDone = waitForNEvents(view, "ruleview-changed", 2);
+ let onDone = view.once("ruleview-changed");
yield createNewRuleViewProperty(ruleEditor, "width:");
yield onDone;
is(ruleEditor.rule.textProps.length, 1,
"Should have created a new text property.");
is(ruleEditor.propertyList.children.length, 1,
"Should have created a property editor.");
--- a/devtools/client/inspector/rules/test/browser_rules_search-filter_09.js
+++ b/devtools/client/inspector/rules/test/browser_rules_search-filter_09.js
@@ -56,16 +56,17 @@ add_task(function* () {
// Getting the new value editor after focus
editor = inplaceEditor(view.styleDocument.activeElement);
let propEditor = ruleEditor.rule.textProps[2].editor;
info("Entering a value and bluring the field to expect a rule change");
onRuleViewChanged = view.once("ruleview-changed");
editor.input.value = "100%";
+ view.throttle.flush();
yield onRuleViewChanged;
onRuleViewChanged = view.once("ruleview-changed");
editor.input.blur();
yield onRuleViewChanged;
ok(propEditor.container.classList.contains("ruleview-highlight"),
"margin-left text property is correctly highlighted.");
--- a/devtools/client/inspector/rules/test/browser_rules_user-property-reset.js
+++ b/devtools/client/inspector/rules/test/browser_rules_user-property-reset.js
@@ -47,20 +47,20 @@ function* modifyRuleViewWidth(value, rul
ok(editor.input, "The inplace-editor field is ready");
info("Setting the new value");
editor.input.value = value;
info("Pressing return and waiting for the field to blur and for the " +
"markup-view to show the mutation");
let onBlur = once(editor.input, "blur", true);
- let onMutation = inspector.once("markupmutation");
+ let onStyleChanged = waitForStyleModification(inspector);
EventUtils.sendKey("return");
yield onBlur;
- yield onMutation;
+ yield onStyleChanged;
info("Escaping out of the new property field that has been created after " +
"the value was edited");
let onNewFieldBlur = once(ruleView.styleDocument.activeElement, "blur", true);
EventUtils.sendKey("escape");
yield onNewFieldBlur;
}
--- a/devtools/client/inspector/rules/test/head.js
+++ b/devtools/client/inspector/rules/test/head.js
@@ -525,16 +525,17 @@ var addProperty = Task.async(function* (
is(editor, inplaceEditor(textProp.editor.valueSpan),
"The inplace editor appeared for the value");
info("Adding value " + value);
// Setting the input value schedules a preview to be shown in 10ms which
// triggers a ruleview-changed event (see bug 1209295).
let onPreview = view.once("ruleview-changed");
editor.input.value = value;
+ view.throttle.flush();
yield onPreview;
let onValueAdded = view.once("ruleview-changed");
EventUtils.synthesizeKey(commitValueWith, {}, view.styleWindow);
yield onValueAdded;
if (blurNewProperty) {
view.styleDocument.activeElement.blur();
@@ -563,16 +564,17 @@ var setProperty = Task.async(function* (
yield focusEditableField(view, textProp.editor.valueSpan);
let onPreview = view.once("ruleview-changed");
if (value === null) {
EventUtils.synthesizeKey("VK_DELETE", {}, view.styleWindow);
} else {
EventUtils.sendString(value, view.styleWindow);
}
+ view.throttle.flush();
yield onPreview;
let onValueDone = view.once("ruleview-changed");
EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
yield onValueDone;
if (blurNewProperty) {
view.styleDocument.activeElement.blur();
@@ -785,8 +787,28 @@ function openStyleContextMenuAndGetAllIt
if (item.submenu) {
return addItem(item.submenu.items);
}
return item;
}));
return allItems;
}
+
+/**
+ * Wait for a markupmutation event on the inspector that is for a style modification.
+ * @param {InspectorPanel} inspector
+ * @return {Promise}
+ */
+function waitForStyleModification(inspector) {
+ return new Promise(function (resolve) {
+ function checkForStyleModification(name, mutations) {
+ for (let mutation of mutations) {
+ if (mutation.type === "attributes" && mutation.attributeName === "style") {
+ inspector.off("markupmutation", checkForStyleModification);
+ resolve();
+ return;
+ }
+ }
+ }
+ inspector.on("markupmutation", checkForStyleModification);
+ });
+}
--- a/devtools/client/inspector/test/shared-head.js
+++ b/devtools/client/inspector/test/shared-head.js
@@ -62,16 +62,20 @@ var openInspectorSidebarTab = Task.async
* Open the toolbox, with the inspector tool visible, and the rule-view
* sidebar tab selected.
*
* @return a promise that resolves when the inspector is ready and the rule view
* is visible and ready
*/
function openRuleView() {
return openInspectorSidebarTab("ruleview").then(data => {
+ // Replace the view to use a custom throttle function that can be triggered manually
+ // through an additional ".flush()" property.
+ data.inspector.ruleview.view.throttle = manualThrottle();
+
return {
toolbox: data.toolbox,
inspector: data.inspector,
testActor: data.testActor,
view: data.inspector.ruleview.view
};
});
}
@@ -145,8 +149,38 @@ function getNodeFront(selector, {walker}
*/
var selectNode = Task.async(function* (selector, inspector, reason = "test") {
info("Selecting the node for '" + selector + "'");
let nodeFront = yield getNodeFront(selector, inspector);
let updated = inspector.once("inspector-updated");
inspector.selection.setNodeFront(nodeFront, reason);
yield updated;
});
+
+/**
+ * Create a throttling function that can be manually "flushed". This is to replace the
+ * use of the `throttle` function from `devtools/client/inspector/shared/utils.js`, which
+ * has a setTimeout that can cause intermittents.
+ * @return {Function} This function has the same function signature as throttle, but
+ * the property `.flush()` has been added for flushing out any
+ * throttled calls.
+ */
+function manualThrottle() {
+ let calls = [];
+
+ function throttle(func, wait, scope) {
+ return function () {
+ let existingCall = calls.find(call => call.func === func);
+ if (existingCall) {
+ existingCall.args = arguments;
+ } else {
+ calls.push({ func, wait, scope, args: arguments });
+ }
+ };
+ }
+
+ throttle.flush = function () {
+ calls.forEach(({func, scope, args}) => func.apply(scope, args));
+ calls = [];
+ };
+
+ return throttle;
+}