summaryrefslogtreecommitdiffstats
path: root/devtools/server/tests/unit/test_layout-reflows-observer.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/server/tests/unit/test_layout-reflows-observer.js')
-rw-r--r--devtools/server/tests/unit/test_layout-reflows-observer.js286
1 files changed, 286 insertions, 0 deletions
diff --git a/devtools/server/tests/unit/test_layout-reflows-observer.js b/devtools/server/tests/unit/test_layout-reflows-observer.js
new file mode 100644
index 000000000..ff6c07b26
--- /dev/null
+++ b/devtools/server/tests/unit/test_layout-reflows-observer.js
@@ -0,0 +1,286 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test the LayoutChangesObserver
+
+var {
+ getLayoutChangesObserver,
+ releaseLayoutChangesObserver,
+ LayoutChangesObserver
+} = require("devtools/server/actors/reflow");
+
+// Override set/clearTimeout on LayoutChangesObserver to avoid depending on
+// time in this unit test. This means that LayoutChangesObserver.eventLoopTimer
+// will be the timeout callback instead of the timeout itself, so test cases
+// will need to execute it to fake a timeout
+LayoutChangesObserver.prototype._setTimeout = cb => cb;
+LayoutChangesObserver.prototype._clearTimeout = function () {};
+
+// Mock the tabActor since we only really want to test the LayoutChangesObserver
+// and don't want to depend on a window object, nor want to test protocol.js
+function MockTabActor() {
+ this.window = new MockWindow();
+ this.windows = [this.window];
+ this.attached = true;
+}
+
+function MockWindow() {}
+MockWindow.prototype = {
+ QueryInterface: function () {
+ let self = this;
+ return {
+ getInterface: function () {
+ return {
+ QueryInterface: function () {
+ if (!self.docShell) {
+ self.docShell = new MockDocShell();
+ }
+ return self.docShell;
+ }
+ };
+ }
+ };
+ },
+ setTimeout: function (cb) {
+ // Simply return the cb itself so that we can execute it in the test instead
+ // of depending on a real timeout
+ return cb;
+ },
+ clearTimeout: function () {}
+};
+
+function MockDocShell() {
+ this.observer = null;
+}
+MockDocShell.prototype = {
+ addWeakReflowObserver: function (observer) {
+ this.observer = observer;
+ },
+ removeWeakReflowObserver: function () {},
+ get chromeEventHandler() {
+ return {
+ addEventListener: (type, cb) => {
+ if (type === "resize") {
+ this.resizeCb = cb;
+ }
+ },
+ removeEventListener: (type, cb) => {
+ if (type === "resize" && cb === this.resizeCb) {
+ this.resizeCb = null;
+ }
+ }
+ };
+ },
+ mockResize: function () {
+ if (this.resizeCb) {
+ this.resizeCb();
+ }
+ }
+};
+
+function run_test() {
+ instancesOfObserversAreSharedBetweenWindows();
+ eventsAreBatched();
+ noEventsAreSentWhenThereAreNoReflowsAndLoopTimeouts();
+ observerIsAlreadyStarted();
+ destroyStopsObserving();
+ stoppingAndStartingSeveralTimesWorksCorrectly();
+ reflowsArentStackedWhenStopped();
+ stackedReflowsAreResetOnStop();
+}
+
+function instancesOfObserversAreSharedBetweenWindows() {
+ do_print("Checking that when requesting twice an instances of the observer " +
+ "for the same TabActor, the instance is shared");
+
+ do_print("Checking 2 instances of the observer for the tabActor 1");
+ let tabActor1 = new MockTabActor();
+ let obs11 = getLayoutChangesObserver(tabActor1);
+ let obs12 = getLayoutChangesObserver(tabActor1);
+ do_check_eq(obs11, obs12);
+
+ do_print("Checking 2 instances of the observer for the tabActor 2");
+ let tabActor2 = new MockTabActor();
+ let obs21 = getLayoutChangesObserver(tabActor2);
+ let obs22 = getLayoutChangesObserver(tabActor2);
+ do_check_eq(obs21, obs22);
+
+ do_print("Checking that observers instances for 2 different tabActors are " +
+ "different");
+ do_check_neq(obs11, obs21);
+
+ releaseLayoutChangesObserver(tabActor1);
+ releaseLayoutChangesObserver(tabActor1);
+ releaseLayoutChangesObserver(tabActor2);
+ releaseLayoutChangesObserver(tabActor2);
+}
+
+function eventsAreBatched() {
+ do_print("Checking that reflow events are batched and only sent when the " +
+ "timeout expires");
+
+ // Note that in this test, we mock the TabActor and its window property, so we
+ // also mock the setTimeout/clearTimeout mechanism and just call the callback
+ // manually
+ let tabActor = new MockTabActor();
+ let observer = getLayoutChangesObserver(tabActor);
+
+ let reflowsEvents = [];
+ let onReflows = (event, reflows) => reflowsEvents.push(reflows);
+ observer.on("reflows", onReflows);
+
+ let resizeEvents = [];
+ let onResize = () => resizeEvents.push("resize");
+ observer.on("resize", onResize);
+
+ do_print("Fake one reflow event");
+ tabActor.window.docShell.observer.reflow();
+ do_print("Checking that no batched reflow event has been emitted");
+ do_check_eq(reflowsEvents.length, 0);
+
+ do_print("Fake another reflow event");
+ tabActor.window.docShell.observer.reflow();
+ do_print("Checking that still no batched reflow event has been emitted");
+ do_check_eq(reflowsEvents.length, 0);
+
+ do_print("Fake a few of resize events too");
+ tabActor.window.docShell.mockResize();
+ tabActor.window.docShell.mockResize();
+ tabActor.window.docShell.mockResize();
+ do_print("Checking that still no batched resize event has been emitted");
+ do_check_eq(resizeEvents.length, 0);
+
+ do_print("Faking timeout expiration and checking that events are sent");
+ observer.eventLoopTimer();
+ do_check_eq(reflowsEvents.length, 1);
+ do_check_eq(reflowsEvents[0].length, 2);
+ do_check_eq(resizeEvents.length, 1);
+
+ observer.off("reflows", onReflows);
+ observer.off("resize", onResize);
+ releaseLayoutChangesObserver(tabActor);
+}
+
+function noEventsAreSentWhenThereAreNoReflowsAndLoopTimeouts() {
+ do_print("Checking that if no reflows were detected and the event batching " +
+ "loop expires, then no reflows event is sent");
+
+ let tabActor = new MockTabActor();
+ let observer = getLayoutChangesObserver(tabActor);
+
+ let reflowsEvents = [];
+ let onReflows = (event, reflows) => reflowsEvents.push(reflows);
+ observer.on("reflows", onReflows);
+
+ do_print("Faking timeout expiration and checking for reflows");
+ observer.eventLoopTimer();
+ do_check_eq(reflowsEvents.length, 0);
+
+ observer.off("reflows", onReflows);
+ releaseLayoutChangesObserver(tabActor);
+}
+
+function observerIsAlreadyStarted() {
+ do_print("Checking that the observer is already started when getting it");
+
+ let tabActor = new MockTabActor();
+ let observer = getLayoutChangesObserver(tabActor);
+ do_check_true(observer.isObserving);
+
+ observer.stop();
+ do_check_false(observer.isObserving);
+
+ observer.start();
+ do_check_true(observer.isObserving);
+
+ releaseLayoutChangesObserver(tabActor);
+}
+
+function destroyStopsObserving() {
+ do_print("Checking that the destroying the observer stops it");
+
+ let tabActor = new MockTabActor();
+ let observer = getLayoutChangesObserver(tabActor);
+ do_check_true(observer.isObserving);
+
+ observer.destroy();
+ do_check_false(observer.isObserving);
+
+ releaseLayoutChangesObserver(tabActor);
+}
+
+function stoppingAndStartingSeveralTimesWorksCorrectly() {
+ do_print("Checking that the stopping and starting several times the observer" +
+ " works correctly");
+
+ let tabActor = new MockTabActor();
+ let observer = getLayoutChangesObserver(tabActor);
+
+ do_check_true(observer.isObserving);
+ observer.start();
+ observer.start();
+ observer.start();
+ do_check_true(observer.isObserving);
+
+ observer.stop();
+ do_check_false(observer.isObserving);
+
+ observer.stop();
+ observer.stop();
+ do_check_false(observer.isObserving);
+
+ releaseLayoutChangesObserver(tabActor);
+}
+
+function reflowsArentStackedWhenStopped() {
+ do_print("Checking that when stopped, reflows aren't stacked in the observer");
+
+ let tabActor = new MockTabActor();
+ let observer = getLayoutChangesObserver(tabActor);
+
+ do_print("Stoping the observer");
+ observer.stop();
+
+ do_print("Faking reflows");
+ tabActor.window.docShell.observer.reflow();
+ tabActor.window.docShell.observer.reflow();
+ tabActor.window.docShell.observer.reflow();
+
+ do_print("Checking that reflows aren't recorded");
+ do_check_eq(observer.reflows.length, 0);
+
+ do_print("Starting the observer and faking more reflows");
+ observer.start();
+ tabActor.window.docShell.observer.reflow();
+ tabActor.window.docShell.observer.reflow();
+ tabActor.window.docShell.observer.reflow();
+
+ do_print("Checking that reflows are recorded");
+ do_check_eq(observer.reflows.length, 3);
+
+ releaseLayoutChangesObserver(tabActor);
+}
+
+function stackedReflowsAreResetOnStop() {
+ do_print("Checking that stacked reflows are reset on stop");
+
+ let tabActor = new MockTabActor();
+ let observer = getLayoutChangesObserver(tabActor);
+
+ tabActor.window.docShell.observer.reflow();
+ do_check_eq(observer.reflows.length, 1);
+
+ observer.stop();
+ do_check_eq(observer.reflows.length, 0);
+
+ tabActor.window.docShell.observer.reflow();
+ do_check_eq(observer.reflows.length, 0);
+
+ observer.start();
+ do_check_eq(observer.reflows.length, 0);
+
+ tabActor.window.docShell.observer.reflow();
+ do_check_eq(observer.reflows.length, 1);
+
+ releaseLayoutChangesObserver(tabActor);
+}