summaryrefslogtreecommitdiffstats
path: root/toolkit/components/places/tests/PlacesTestUtils.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/places/tests/PlacesTestUtils.jsm')
-rw-r--r--toolkit/components/places/tests/PlacesTestUtils.jsm163
1 files changed, 163 insertions, 0 deletions
diff --git a/toolkit/components/places/tests/PlacesTestUtils.jsm b/toolkit/components/places/tests/PlacesTestUtils.jsm
new file mode 100644
index 000000000..36e425cae
--- /dev/null
+++ b/toolkit/components/places/tests/PlacesTestUtils.jsm
@@ -0,0 +1,163 @@
+"use strict";
+
+this.EXPORTED_SYMBOLS = [
+ "PlacesTestUtils",
+];
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.importGlobalProperties(["URL"]);
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+ "resource://gre/modules/Task.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+ "resource://gre/modules/PlacesUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+ "resource://gre/modules/NetUtil.jsm");
+
+this.PlacesTestUtils = Object.freeze({
+ /**
+ * Asynchronously adds visits to a page.
+ *
+ * @param aPlaceInfo
+ * Can be an nsIURI, in such a case a single LINK visit will be added.
+ * Otherwise can be an object describing the visit to add, or an array
+ * of these objects:
+ * { uri: nsIURI of the page,
+ * [optional] transition: one of the TRANSITION_* from nsINavHistoryService,
+ * [optional] title: title of the page,
+ * [optional] visitDate: visit date, either in microseconds from the epoch or as a date object
+ * [optional] referrer: nsIURI of the referrer for this visit
+ * }
+ *
+ * @return {Promise}
+ * @resolves When all visits have been added successfully.
+ * @rejects JavaScript exception.
+ */
+ addVisits: Task.async(function* (placeInfo) {
+ let places = [];
+ let infos = [];
+
+ if (placeInfo instanceof Ci.nsIURI ||
+ placeInfo instanceof URL ||
+ typeof placeInfo == "string") {
+ places.push({ uri: placeInfo });
+ }
+ else if (Array.isArray(placeInfo)) {
+ places = places.concat(placeInfo);
+ } else if (typeof placeInfo == "object" && placeInfo.uri) {
+ places.push(placeInfo)
+ } else {
+ throw new Error("Unsupported type passed to addVisits");
+ }
+
+ // Create a PageInfo for each entry.
+ for (let place of places) {
+ let info = {url: place.uri};
+ info.title = (typeof place.title === "string") ? place.title : "test visit for " + info.url.spec ;
+ if (typeof place.referrer == "string") {
+ place.referrer = NetUtil.newURI(place.referrer);
+ } else if (place.referrer && place.referrer instanceof URL) {
+ place.referrer = NetUtil.newURI(place.referrer.href);
+ }
+ let visitDate = place.visitDate;
+ if (visitDate) {
+ if (!(visitDate instanceof Date)) {
+ visitDate = PlacesUtils.toDate(visitDate);
+ }
+ } else {
+ visitDate = new Date();
+ }
+ info.visits = [{
+ transition: place.transition,
+ date: visitDate,
+ referrer: place.referrer
+ }];
+ infos.push(info);
+ }
+ return PlacesUtils.history.insertMany(infos);
+ }),
+
+ /**
+ * Clear all history.
+ *
+ * @return {Promise}
+ * @resolves When history was cleared successfully.
+ * @rejects JavaScript exception.
+ */
+ clearHistory() {
+ let expirationFinished = new Promise(resolve => {
+ Services.obs.addObserver(function observe(subj, topic, data) {
+ Services.obs.removeObserver(observe, topic);
+ resolve();
+ }, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
+ });
+
+ return Promise.all([expirationFinished, PlacesUtils.history.clear()]);
+ },
+
+ /**
+ * Waits for all pending async statements on the default connection.
+ *
+ * @return {Promise}
+ * @resolves When all pending async statements finished.
+ * @rejects Never.
+ *
+ * @note The result is achieved by asynchronously executing a query requiring
+ * a write lock. Since all statements on the same connection are
+ * serialized, the end of this write operation means that all writes are
+ * complete. Note that WAL makes so that writers don't block readers, but
+ * this is a problem only across different connections.
+ */
+ promiseAsyncUpdates() {
+ return PlacesUtils.withConnectionWrapper("promiseAsyncUpdates", Task.async(function* (db) {
+ try {
+ yield db.executeCached("BEGIN EXCLUSIVE");
+ yield db.executeCached("COMMIT");
+ } catch (ex) {
+ // If we fail to start a transaction, it's because there is already one.
+ // In such a case we should not try to commit the existing transaction.
+ }
+ }));
+ },
+
+ /**
+ * Asynchronously checks if an address is found in the database.
+ * @param aURI
+ * nsIURI or address to look for.
+ *
+ * @return {Promise}
+ * @resolves Returns true if the page is found.
+ * @rejects JavaScript exception.
+ */
+ isPageInDB: Task.async(function* (aURI) {
+ let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
+ let db = yield PlacesUtils.promiseDBConnection();
+ let rows = yield db.executeCached(
+ "SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url",
+ { url });
+ return rows.length > 0;
+ }),
+
+ /**
+ * Asynchronously checks how many visits exist for a specified page.
+ * @param aURI
+ * nsIURI or address to look for.
+ *
+ * @return {Promise}
+ * @resolves Returns the number of visits found.
+ * @rejects JavaScript exception.
+ */
+ visitsInDB: Task.async(function* (aURI) {
+ let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
+ let db = yield PlacesUtils.promiseDBConnection();
+ let rows = yield db.executeCached(
+ `SELECT count(*) FROM moz_historyvisits v
+ JOIN moz_places h ON h.id = v.place_id
+ WHERE url_hash = hash(:url) AND url = :url`,
+ { url });
+ return rows[0].getResultByIndex(0);
+ })
+});