summaryrefslogtreecommitdiffstats
path: root/browser/components/places/tests/browser/interactions/head.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/places/tests/browser/interactions/head.js')
-rw-r--r--browser/components/places/tests/browser/interactions/head.js200
1 files changed, 200 insertions, 0 deletions
diff --git a/browser/components/places/tests/browser/interactions/head.js b/browser/components/places/tests/browser/interactions/head.js
new file mode 100644
index 0000000000..91aff9d100
--- /dev/null
+++ b/browser/components/places/tests/browser/interactions/head.js
@@ -0,0 +1,200 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { Interactions } = ChromeUtils.importESModule(
+ "resource:///modules/Interactions.sys.mjs"
+);
+
+const { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
+
+XPCOMUtils.defineLazyPreferenceGetter(
+ this,
+ "pageViewIdleTime",
+ "browser.places.interactions.pageViewIdleTime",
+ 60
+);
+
+add_setup(async function global_setup() {
+ // Disable idle management because it interacts with our code, causing
+ // unexpected intermittent failures, we'll fake idle notifications when
+ // we need to test it.
+ let idleService = Cc["@mozilla.org/widget/useridleservice;1"].getService(
+ Ci.nsIUserIdleService
+ );
+ idleService.removeIdleObserver(Interactions, pageViewIdleTime);
+ registerCleanupFunction(() => {
+ idleService.addIdleObserver(Interactions, pageViewIdleTime);
+ });
+
+ // Clean interactions for each test.
+ await Interactions.reset();
+ registerCleanupFunction(async () => {
+ await Interactions.reset();
+ });
+});
+
+/**
+ * Ensures that a list of interactions have been permanently stored.
+ *
+ * @param {Array} expected list of interactions to be found.
+ * @param {boolean} [dontFlush] Avoid flushing pending data.
+ */
+async function assertDatabaseValues(expected, { dontFlush = false } = {}) {
+ await Interactions.interactionUpdatePromise;
+ if (!dontFlush) {
+ await Interactions.store.flush();
+ }
+
+ let interactions = await PlacesUtils.withConnectionWrapper(
+ "head.js::assertDatabaseValues",
+ async db => {
+ let rows = await db.execute(`
+ SELECT h.url AS url, h2.url as referrer_url, total_view_time, key_presses, typing_time, scrolling_time, scrolling_distance
+ FROM moz_places_metadata m
+ JOIN moz_places h ON h.id = m.place_id
+ LEFT JOIN moz_places h2 ON h2.id = m.referrer_place_id
+ ORDER BY created_at ASC
+ `);
+ return rows.map(r => ({
+ url: r.getResultByName("url"),
+ referrerUrl: r.getResultByName("referrer_url"),
+ keypresses: r.getResultByName("key_presses"),
+ typingTime: r.getResultByName("typing_time"),
+ totalViewTime: r.getResultByName("total_view_time"),
+ scrollingTime: r.getResultByName("scrolling_time"),
+ scrollingDistance: r.getResultByName("scrolling_distance"),
+ }));
+ }
+ );
+ info(
+ `Found ${interactions.length} interactions:\n ${JSON.stringify(
+ interactions
+ )}`
+ );
+ Assert.equal(
+ interactions.length,
+ expected.length,
+ "Found the expected number of entries"
+ );
+ for (let i = 0; i < Math.min(expected.length, interactions.length); i++) {
+ let actual = interactions[i];
+ Assert.equal(
+ actual.url,
+ expected[i].url,
+ "Should have saved the page into the database"
+ );
+
+ if (expected[i].exactTotalViewTime != undefined) {
+ Assert.equal(
+ actual.totalViewTime,
+ expected[i].exactTotalViewTime,
+ "Should have kept the exact time."
+ );
+ } else if (expected[i].totalViewTime != undefined) {
+ Assert.greaterOrEqual(
+ actual.totalViewTime,
+ expected[i].totalViewTime,
+ "Should have stored the interaction time"
+ );
+ }
+
+ if (expected[i].maxViewTime != undefined) {
+ Assert.less(
+ actual.totalViewTime,
+ expected[i].maxViewTime,
+ "Should have recorded an interaction below the maximum expected"
+ );
+ }
+
+ if (expected[i].keypresses != undefined) {
+ Assert.equal(
+ actual.keypresses,
+ expected[i].keypresses,
+ "Should have saved the keypresses into the database"
+ );
+ }
+
+ if (expected[i].exactTypingTime != undefined) {
+ Assert.equal(
+ actual.typingTime,
+ expected[i].exactTypingTime,
+ "Should have stored the exact typing time."
+ );
+ } else if (expected[i].typingTimeIsGreaterThan != undefined) {
+ Assert.greater(
+ actual.typingTime,
+ expected[i].typingTimeIsGreaterThan,
+ "Should have stored at least this amount of typing time."
+ );
+ } else if (expected[i].typingTimeIsLessThan != undefined) {
+ Assert.less(
+ actual.typingTime,
+ expected[i].typingTimeIsLessThan,
+ "Should have stored less than this amount of typing time."
+ );
+ }
+
+ if (expected[i].exactScrollingDistance != undefined) {
+ Assert.equal(
+ actual.scrollingDistance,
+ expected[i].exactScrollingDistance,
+ "Should have scrolled by exactly least this distance"
+ );
+ } else if (expected[i].exactScrollingTime != undefined) {
+ Assert.greater(
+ actual.scrollingTime,
+ expected[i].exactScrollingTime,
+ "Should have scrolled for exactly least this duration"
+ );
+ }
+
+ if (expected[i].scrollingDistanceIsGreaterThan != undefined) {
+ Assert.greater(
+ actual.scrollingDistance,
+ expected[i].scrollingDistanceIsGreaterThan,
+ "Should have scrolled by at least this distance"
+ );
+ } else if (expected[i].scrollingTimeIsGreaterThan != undefined) {
+ Assert.greater(
+ actual.scrollingTime,
+ expected[i].scrollingTimeIsGreaterThan,
+ "Should have scrolled for at least this duration"
+ );
+ }
+ }
+}
+
+/**
+ * Ensures that a list of interactions have been permanently stored.
+ *
+ * @param {string} url The url to query.
+ * @param {string} property The property to extract.
+ */
+async function getDatabaseValue(url, property) {
+ await Interactions.store.flush();
+ const PROP_TRANSLATOR = {
+ totalViewTime: "total_view_time",
+ keypresses: "key_presses",
+ typingTime: "typing_time",
+ };
+ property = PROP_TRANSLATOR[property] || property;
+
+ return PlacesUtils.withConnectionWrapper(
+ "head.js::getDatabaseValue",
+ async db => {
+ let rows = await db.execute(
+ `
+ SELECT * FROM moz_places_metadata m
+ JOIN moz_places h ON h.id = m.place_id
+ WHERE url = :url
+ ORDER BY created_at DESC
+ `,
+ { url }
+ );
+ return rows?.[0].getResultByName(property);
+ }
+ );
+}