Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

"use strict";
/*
This test will ideally test the following cases
(each with and without a callback associated with it)
Case A: Tests which should remove pages (Positives)
Case A 1: Page has multiple visits both in/out of timeframe, all get deleted
Case A 2: Page has single uri, removed by host
Case A 3: Page has random subhost, with same host, removed by wildcard
Case A 4: Page is localhost and localhost:port, removed by host
Case A 5: Page is a `file://` type address, removed by empty host
Cases A 1,2,3 will be tried with and without bookmarks added (which prevent page deletion)
Case B: Tests in which no pages are removed (Inverses)
Case B 1 (inverse): Page has no visits in timeframe, and nothing is deleted
Case B 2: Page has single uri, not removed since hostname is different
Case B 3: Page has multiple subhosts, not removed since wildcard doesn't match
Case C: Combinations tests
Case C 1: Single hostname, multiple visits, at least one in timeframe and hostname
Case C 2: Random subhosts, multiple visits, at least one in timeframe and hostname-wildcard
*/
add_task(async function test_removeByFilter() {
// Cleanup
await PlacesUtils.history.clear();
await PlacesUtils.bookmarks.eraseEverything();
// Adding a witness URI
let witnessURI = NetUtil.newURI(
Math.random()
);
await PlacesTestUtils.addVisits(witnessURI);
Assert.ok(
await PlacesTestUtils.isPageInDB(witnessURI),
"Witness URI is in database"
);
let removeByFilterTester = async function (
visits,
filter,
checkBeforeRemove,
checkAfterRemove,
useCallback,
bookmarkedUri
) {
// Add visits for URIs
await PlacesTestUtils.addVisits(visits);
if (
bookmarkedUri !== null &&
visits.map(v => v.uri).includes(bookmarkedUri)
) {
await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: bookmarkedUri,
title: "test bookmark",
});
}
await checkBeforeRemove();
// Take care of any observers (due to bookmarks)
let { placesEventListener, promiseObserved } =
getObserverPromise(bookmarkedUri);
if (placesEventListener) {
PlacesObservers.addListener(
["page-title-changed", "history-cleared", "page-removed"],
placesEventListener
);
}
// Perfom delete operation on database
let removed = false;
if (useCallback) {
// The amount of callbacks will be the unique URIs to remove from the database
let netCallbacksRequired = new Set(visits.map(v => v.uri)).size;
removed = await PlacesUtils.history.removeByFilter(filter, pageInfo => {
Assert.ok(
PlacesUtils.validatePageInfo(pageInfo, false),
"pageInfo should follow a basic format"
);
Assert.ok(
netCallbacksRequired > 0,
"Callback called as many times as required"
);
netCallbacksRequired--;
});
} else {
removed = await PlacesUtils.history.removeByFilter(filter);
}
await checkAfterRemove();
await promiseObserved;
if (placesEventListener) {
await PlacesUtils.bookmarks.eraseEverything();
PlacesObservers.removeListener(
["page-title-changed", "history-cleared", "page-removed"],
placesEventListener
);
}
Assert.ok(
await PlacesTestUtils.isPageInDB(witnessURI),
"Witness URI is still in database"
);
return removed;
};
const remoteUriList = [
Math.random(),
Math.random(),
Math.random(),
];
const localhostUriList = [
"http://localhost:4500/" + Math.random(),
"http://localhost/" + Math.random(),
];
const fileUriList = ["file:///home/user/files" + Math.random()];
const title = "Title " + Math.random();
let sameHostVisits = [
{
uri: remoteUriList[0],
title,
visitDate: new Date(2005, 1, 1) * 1000,
},
{
uri: remoteUriList[0],
title,
visitDate: new Date(2005, 3, 3) * 1000,
},
{
uri: remoteUriList[0],
title,
visitDate: new Date(2007, 1, 1) * 1000,
},
];
let randomHostVisits = [
{
uri: remoteUriList[0],
title,
visitDate: new Date(2005, 1, 1) * 1000,
},
{
uri: remoteUriList[1],
title,
visitDate: new Date(2005, 3, 3) * 1000,
},
{
uri: remoteUriList[2],
title,
visitDate: new Date(2007, 1, 1) * 1000,
},
];
let localhostVisits = [
{
uri: localhostUriList[0],
title,
},
{
uri: localhostUriList[1],
title,
},
];
let fileVisits = [
{
uri: fileUriList[0],
title,
},
];
let assertInDB = async function (aUri) {
Assert.ok(await PlacesTestUtils.isPageInDB(aUri));
};
let assertNotInDB = async function (aUri) {
Assert.ok(!(await PlacesTestUtils.isPageInDB(aUri)));
};
for (let callbackUse of [true, false]) {
// Case A Positives
for (let bookmarkUse of [true, false]) {
let bookmarkedUri = () => undefined;
let checkableArray = arr => arr;
let checkClosure = assertNotInDB;
if (bookmarkUse) {
bookmarkedUri = arr => arr[0];
checkableArray = arr => arr.slice(1);
checkClosure = function () {};
}
// Case A 1: Dates
await removeByFilterTester(
sameHostVisits,
{ beginDate: new Date(2004, 1, 1), endDate: new Date(2006, 1, 1) },
() => assertInDB(remoteUriList[0]),
() => checkClosure(remoteUriList[0]),
callbackUse,
bookmarkedUri(remoteUriList)
);
// Case A 2: Single Sub-host
await removeByFilterTester(
sameHostVisits,
{ host: "mozilla.org" },
() => assertInDB(remoteUriList[0]),
() => checkClosure(remoteUriList[0]),
callbackUse,
bookmarkedUri(remoteUriList)
);
// Case A 3: Multiple subhost
await removeByFilterTester(
randomHostVisits,
{ host: ".mozilla.org" },
async () => {
for (let uri of remoteUriList) {
await assertInDB(uri);
}
},
async () => {
for (let uri of checkableArray(remoteUriList)) {
await checkClosure(uri);
}
},
callbackUse,
bookmarkedUri(remoteUriList)
);
}
// Case A 4: Localhost
await removeByFilterTester(
localhostVisits,
{ host: "localhost" },
async () => {
for (let uri of localhostUriList) {
await assertInDB(uri);
}
},
async () => {
for (let uri of localhostUriList) {
await assertNotInDB(uri);
}
},
callbackUse
);
// Case A 5: Local Files
await removeByFilterTester(
fileVisits,
{ host: "." },
async () => {
for (let uri of fileUriList) {
await assertInDB(uri);
}
},
async () => {
for (let uri of fileUriList) {
await assertNotInDB(uri);
}
},
callbackUse
);
// Case B: Tests which do not remove anything (inverses)
// Case B 1: Date
await removeByFilterTester(
sameHostVisits,
{ beginDate: new Date(2001, 1, 1), endDate: new Date(2002, 1, 1) },
() => assertInDB(remoteUriList[0]),
() => assertInDB(remoteUriList[0]),
callbackUse
);
// Case B 2 : Single subhost
await removeByFilterTester(
sameHostVisits,
{ host: "notthere.org" },
() => assertInDB(remoteUriList[0]),
() => assertInDB(remoteUriList[0]),
callbackUse
);
// Case B 3 : Multiple subhosts
await removeByFilterTester(
randomHostVisits,
{ host: ".notthere.org" },
async () => {
for (let uri of remoteUriList) {
await assertInDB(uri);
}
},
async () => {
for (let uri of remoteUriList) {
await assertInDB(uri);
}
},
callbackUse
);
// Case B 4 : invalid local subhost
await removeByFilterTester(
randomHostVisits,
{ host: ".org" },
async () => {
for (let uri of remoteUriList) {
await assertInDB(uri);
}
},
async () => {
for (let uri of remoteUriList) {
await assertInDB(uri);
}
},
callbackUse
);
// Case C: Combination Cases
// Case C 1: single subhost
await removeByFilterTester(
sameHostVisits,
{
host: "mozilla.org",
beginDate: new Date(2004, 1, 1),
endDate: new Date(2006, 1, 1),
},
() => assertInDB(remoteUriList[0]),
() => assertNotInDB(remoteUriList[0]),
callbackUse
);
// Case C 2: multiple subhost
await removeByFilterTester(
randomHostVisits,
{
host: ".mozilla.org",
beginDate: new Date(2005, 1, 1),
endDate: new Date(2017, 1, 1),
},
async () => {
for (let uri of remoteUriList) {
await assertInDB(uri);
}
},
async () => {
for (let uri of remoteUriList) {
await assertNotInDB(uri);
}
},
callbackUse
);
}
});
// Test various error cases
add_task(async function test_error_cases() {
Assert.throws(
() => PlacesUtils.history.removeByFilter(),
/TypeError: Expected a filter/
);
Assert.throws(
() => PlacesUtils.history.removeByFilter("obviously, not a filter"),
/TypeError: Expected a filter/
);
Assert.throws(
() => PlacesUtils.history.removeByFilter({}),
/TypeError: Expected a non-empty filter/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({ beginDate: "now" }),
/TypeError: Expected a valid Date/
);
Assert.throws(
() => PlacesUtils.history.removeByFilter({ beginDate: Date.now() }),
/TypeError: Expected a valid Date/
);
Assert.throws(
() => PlacesUtils.history.removeByFilter({ beginDate: new Date(NaN) }),
/TypeError: Expected a valid Date/
);
Assert.throws(
() =>
PlacesUtils.history.removeByFilter(
{ beginDate: new Date() },
"obviously, not a callback"
),
/TypeError: Invalid function/
);
Assert.throws(
() =>
PlacesUtils.history.removeByFilter({
beginDate: new Date(1000),
endDate: new Date(0),
}),
/TypeError: `beginDate` should be at least as old/
);
Assert.throws(
() => PlacesUtils.history.removeByFilter({ host: "#" }),
/TypeError: Expected well formed hostname string for/
);
Assert.throws(
() => PlacesUtils.history.removeByFilter({ host: "www..org" }),
/TypeError: Expected well formed hostname string for/
);
Assert.throws(
() => PlacesUtils.history.removeByFilter({ host: {} }),
/TypeError: `host` should be a string/
);
Assert.throws(
() => PlacesUtils.history.removeByFilter({ host: "*.mozilla.org" }),
/TypeError: Expected well formed hostname string for/
);
Assert.throws(
() => PlacesUtils.history.removeByFilter({ host: "*" }),
/TypeError: Expected well formed hostname string for/
);
Assert.throws(
() => PlacesUtils.history.removeByFilter({ host: "local.host." }),
/TypeError: Expected well formed hostname string for/
);
Assert.throws(
() => PlacesUtils.history.removeByFilter({ host: "(local files)" }),
/TypeError: Expected well formed hostname string for/
);
Assert.throws(
() => PlacesUtils.history.removeByFilter({ host: "" }),
/TypeError: Expected a non-empty filter/
);
});
add_task(async function test_chunking() {
await PlacesUtils.history.clear();
await PlacesUtils.bookmarks.eraseEverything();
info("Insert many visited pages");
let pages = [];
for (let i = 1; i <= 1500; i++) {
let visits = [
{
date: new Date(Date.now() - (86400 + i) * 1000),
transition: PlacesUtils.history.TRANSITIONS.TYPED,
},
];
pages.push(
{
title: `Page ${i}`,
visits,
},
{
title: `Subdomain ${i}`,
visits,
}
);
}
await PlacesUtils.history.insertMany(pages);
info("Remove all visited pages");
await PlacesUtils.history.removeByFilter({
host: ".example.com",
});
});
// Helper functions
function getObserverPromise(bookmarkedUri) {
if (!bookmarkedUri) {
return { promiseObserved: Promise.resolve() };
}
let placesEventListener;
let promiseObserved = new Promise((resolve, reject) => {
placesEventListener = events => {
for (const event of events) {
switch (event.type) {
case "page-title-changed": {
reject(new Error("Unexpected page-title-changed event happens"));
break;
}
case "history-cleared": {
reject(new Error("Unexpected history-cleared event happens"));
break;
}
case "page-removed": {
if (event.isRemovedFromStore) {
Assert.notEqual(
event.url,
bookmarkedUri,
"Bookmarked URI should not be deleted"
);
} else {
Assert.equal(
event.isPartialVisistsRemoval,
false,
"Observing page-removed deletes all visits"
);
Assert.equal(
event.url,
bookmarkedUri,
"Bookmarked URI should have all visits removed but not the page itself"
);
}
resolve();
break;
}
}
}
};
});
return { placesEventListener, promiseObserved };
}