Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* Any copyright is dedicated to the Public Domain.
// Tests for `History.update` as implemented in History.sys.mjs
"use strict";
add_task(async function test_error_cases() {
Assert.throws(
() => PlacesUtils.history.update("not an object"),
/Error: PageInfo: Input should be a valid object/,
"passing a string as pageInfo should throw an Error"
);
Assert.throws(
() => PlacesUtils.history.update(null),
/Error: PageInfo: Input should be/,
"passing a null as pageInfo should throw an Error"
);
Assert.throws(
() =>
PlacesUtils.history.update({
description: "Test description",
}),
/Error: PageInfo: The following properties were expected: url, guid/,
"not included a url or a guid should throw"
);
Assert.throws(
() => PlacesUtils.history.update({ url: "not a valid url string" }),
/Error: PageInfo: Invalid value for property/,
"passing an invalid url should throw an Error"
);
Assert.throws(
() =>
PlacesUtils.history.update({
description: 123,
}),
/Error: PageInfo: Invalid value for property/,
"passing a non-string description in pageInfo should throw an Error"
);
Assert.throws(
() =>
PlacesUtils.history.update({
guid: "invalid guid",
description: "Test description",
}),
/Error: PageInfo: Invalid value for property/,
"passing a invalid guid in pageInfo should throw an Error"
);
Assert.throws(
() =>
PlacesUtils.history.update({
previewImageURL: "not a valid url string",
}),
/Error: PageInfo: Invalid value for property/,
"passing an invlid preview image url in pageInfo should throw an Error"
);
Assert.throws(
() => {
let imageName = "a-very-long-string".repeat(10000);
PlacesUtils.history.update({
previewImageURL,
});
},
/Error: PageInfo: Invalid value for property/,
"passing an oversized previewImageURL in pageInfo should throw an Error"
);
Assert.throws(
/TypeError: pageInfo object must at least/,
"passing a pageInfo with neither description, previewImageURL, nor annotations should throw a TypeError"
);
Assert.throws(
() =>
PlacesUtils.history.update({
annotations: "asd",
}),
/Error: PageInfo: Invalid value for property/,
"passing a pageInfo with incorrect annotations type should throw an Error"
);
Assert.throws(
() =>
PlacesUtils.history.update({
annotations: new Map(),
}),
/Error: PageInfo: Invalid value for property/,
"passing a pageInfo with an empty annotations type should throw an Error"
);
Assert.throws(
() =>
PlacesUtils.history.update({
annotations: new Map([[1234, "value"]]),
}),
/Error: PageInfo: Invalid value for property/,
"passing a pageInfo with an invalid key type should throw an Error"
);
Assert.throws(
() =>
PlacesUtils.history.update({
annotations: new Map([["test", ["myarray"]]]),
}),
/Error: PageInfo: Invalid value for property/,
"passing a pageInfo with an invalid key type should throw an Error"
);
Assert.throws(
() =>
PlacesUtils.history.update({
annotations: new Map([["test", { anno: "value" }]]),
}),
/Error: PageInfo: Invalid value for property/,
"passing a pageInfo with an invalid key type should throw an Error"
);
});
add_task(async function test_description_change_saved() {
await PlacesUtils.history.clear();
await PlacesTestUtils.addVisits(TEST_URL);
Assert.ok(await PlacesTestUtils.isPageInDB(TEST_URL));
let description = "Test description";
await PlacesUtils.history.update({ url: TEST_URL, description });
let descriptionInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"description",
{ url: TEST_URL }
);
Assert.equal(
description,
descriptionInDB,
"description should be updated via URL as expected"
);
description = "";
await PlacesUtils.history.update({ url: TEST_URL, description });
descriptionInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"description",
{ url: TEST_URL }
);
Assert.strictEqual(
null,
descriptionInDB,
"an empty description should set it to null in the database"
);
let guid = await PlacesTestUtils.getDatabaseValue("moz_places", "guid", {
url: TEST_URL,
});
description = "Test description";
await PlacesUtils.history.update({ url: TEST_URL, guid, description });
descriptionInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"description",
{ url: TEST_URL }
);
Assert.equal(
description,
descriptionInDB,
"description should be updated via GUID as expected"
);
description = "Test descipriton".repeat(1000);
await PlacesUtils.history.update({ url: TEST_URL, description });
descriptionInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"description",
{ url: TEST_URL }
);
Assert.ok(
!!descriptionInDB.length < description.length,
"a long description should be truncated"
);
description = null;
await PlacesUtils.history.update({ url: TEST_URL, description });
descriptionInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"description",
{ url: TEST_URL }
);
Assert.strictEqual(
description,
descriptionInDB,
"a null description should set it to null in the database"
);
});
add_task(async function test_siteName_change_saved() {
await PlacesUtils.history.clear();
await PlacesTestUtils.addVisits(TEST_URL);
Assert.ok(await PlacesTestUtils.isPageInDB(TEST_URL));
let siteName = "Test site name";
await PlacesUtils.history.update({ url: TEST_URL, siteName });
let siteNameInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"site_name",
{ url: TEST_URL }
);
Assert.equal(
siteName,
siteNameInDB,
"siteName should be updated via URL as expected"
);
siteName = "";
await PlacesUtils.history.update({ url: TEST_URL, siteName });
siteNameInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"site_name",
{ url: TEST_URL }
);
Assert.strictEqual(
null,
siteNameInDB,
"an empty siteName should set it to null in the database"
);
let guid = await PlacesTestUtils.getDatabaseValue("moz_places", "guid", {
url: TEST_URL,
});
siteName = "Test site name";
await PlacesUtils.history.update({ url: TEST_URL, guid, siteName });
siteNameInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"site_name",
{ url: TEST_URL }
);
Assert.equal(
siteName,
siteNameInDB,
"siteName should be updated via GUID as expected"
);
siteName = "Test site name".repeat(1000);
await PlacesUtils.history.update({ url: TEST_URL, siteName });
siteNameInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"site_name",
{
url: TEST_URL,
}
);
Assert.ok(
!!siteNameInDB.length < siteName.length,
"a long siteName should be truncated"
);
siteName = null;
await PlacesUtils.history.update({ url: TEST_URL, siteName });
siteNameInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"site_name",
{
url: TEST_URL,
}
);
Assert.strictEqual(
siteName,
siteNameInDB,
"a null siteName should set it to null in the database"
);
});
add_task(async function test_previewImageURL_change_saved() {
await PlacesUtils.history.clear();
await PlacesTestUtils.addVisits(TEST_URL);
Assert.ok(await PlacesTestUtils.isPageInDB(TEST_URL));
let previewImageURL = IMAGE_URL;
await PlacesUtils.history.update({ url: TEST_URL, previewImageURL });
let previewImageURLInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"preview_image_url",
{
url: TEST_URL,
}
);
Assert.equal(
previewImageURL,
previewImageURLInDB,
"previewImageURL should be updated via URL as expected"
);
previewImageURL = null;
await PlacesUtils.history.update({ url: TEST_URL, previewImageURL });
previewImageURLInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"preview_image_url",
{
url: TEST_URL,
}
);
Assert.strictEqual(
null,
previewImageURLInDB,
"a null previewImageURL should set it to null in the database"
);
let guid = await PlacesTestUtils.getDatabaseValue("moz_places", "guid", {
url: TEST_URL,
});
previewImageURL = IMAGE_URL;
await PlacesUtils.history.update({ guid, previewImageURL });
previewImageURLInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"preview_image_url",
{
url: TEST_URL,
}
);
Assert.equal(
previewImageURL,
previewImageURLInDB,
"previewImageURL should be updated via GUID as expected"
);
previewImageURL = "";
await PlacesUtils.history.update({ url: TEST_URL, previewImageURL });
previewImageURLInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"preview_image_url",
{
url: TEST_URL,
}
);
Assert.strictEqual(
null,
previewImageURLInDB,
"an empty previewImageURL should set it to null in the database"
);
});
add_task(async function test_change_description_and_preview_saved() {
await PlacesUtils.history.clear();
await PlacesTestUtils.addVisits(TEST_URL);
Assert.ok(await PlacesTestUtils.isPageInDB(TEST_URL));
let description = "Test description";
await PlacesUtils.history.update({
url: TEST_URL,
description,
previewImageURL,
});
let descriptionInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"description",
{
url: TEST_URL,
}
);
let previewImageURLInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"preview_image_url",
{
url: TEST_URL,
}
);
Assert.equal(
description,
descriptionInDB,
"description should be updated via URL as expected"
);
Assert.equal(
previewImageURL,
previewImageURLInDB,
"previewImageURL should be updated via URL as expected"
);
// Update description should not touch other fields
description = null;
await PlacesUtils.history.update({ url: TEST_URL, description });
descriptionInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"description",
{
url: TEST_URL,
}
);
previewImageURLInDB = await PlacesTestUtils.getDatabaseValue(
"moz_places",
"preview_image_url",
{
url: TEST_URL,
}
);
Assert.strictEqual(
description,
descriptionInDB,
"description should be updated via URL as expected"
);
Assert.equal(
previewImageURL,
previewImageURLInDB,
"previewImageURL should not be updated"
);
});
/**
* Gets annotation information from the database for the specified URL and
* annotation name.
*
* @param {String} pageUrl The URL to search for.
* @param {String} annoName The name of the annotation to search for.
* @return {Array} An array of objects containing the annotations found.
*/
async function getAnnotationInfoFromDB(pageUrl, annoName) {
let db = await PlacesUtils.promiseDBConnection();
let rows = await db.execute(
`
SELECT a.content, a.flags, a.expiration, a.type FROM moz_anno_attributes n
JOIN moz_annos a ON n.id = a.anno_attribute_id
JOIN moz_places h ON h.id = a.place_id
WHERE h.url_hash = hash(:pageUrl) AND h.url = :pageUrl
AND n.name = :annoName
`,
{ annoName, pageUrl }
);
let result = rows.map(row => {
return {
content: row.getResultByName("content"),
flags: row.getResultByName("flags"),
expiration: row.getResultByName("expiration"),
type: row.getResultByName("type"),
};
});
return result;
}
add_task(async function test_simple_change_annotations() {
await PlacesUtils.history.clear();
await PlacesTestUtils.addVisits(TEST_URL);
Assert.ok(
await PlacesTestUtils.isPageInDB(TEST_URL),
"Should have inserted the page into the database."
);
await PlacesUtils.history.update({
url: TEST_URL,
annotations: new Map([["test/annotation", "testContent"]]),
});
let pageInfo = await PlacesUtils.history.fetch(TEST_URL, {
includeAnnotations: true,
});
Assert.equal(
pageInfo.annotations.size,
1,
"Should have one annotation for the page"
);
Assert.equal(
pageInfo.annotations.get("test/annotation"),
"testContent",
"Should have the correct annotation"
);
let annotationInfo = await getAnnotationInfoFromDB(
TEST_URL,
"test/annotation"
);
Assert.deepEqual(
{
content: "testContent",
flags: 0,
type: PlacesUtils.history.ANNOTATION_TYPE_STRING,
expiration: PlacesUtils.history.ANNOTATION_EXPIRE_NEVER,
},
annotationInfo[0],
"Should have stored the correct annotation data in the db"
);
await PlacesUtils.history.update({
url: TEST_URL,
annotations: new Map([["test/annotation2", "testAnno"]]),
});
pageInfo = await PlacesUtils.history.fetch(TEST_URL, {
includeAnnotations: true,
});
Assert.equal(
pageInfo.annotations.size,
2,
"Should have two annotations for the page"
);
Assert.equal(
pageInfo.annotations.get("test/annotation"),
"testContent",
"Should have the correct value for the first annotation"
);
Assert.equal(
pageInfo.annotations.get("test/annotation2"),
"testAnno",
"Should have the correct value for the second annotation"
);
await PlacesUtils.history.update({
url: TEST_URL,
annotations: new Map([["test/annotation", 1234]]),
});
pageInfo = await PlacesUtils.history.fetch(TEST_URL, {
includeAnnotations: true,
});
Assert.equal(
pageInfo.annotations.size,
2,
"Should still have two annotations for the page"
);
Assert.equal(
pageInfo.annotations.get("test/annotation"),
1234,
"Should have the updated the first annotation value"
);
Assert.equal(
pageInfo.annotations.get("test/annotation2"),
"testAnno",
"Should have kept the value for the second annotation"
);
annotationInfo = await getAnnotationInfoFromDB(TEST_URL, "test/annotation");
Assert.deepEqual(
{
content: 1234,
flags: 0,
type: PlacesUtils.history.ANNOTATION_TYPE_INT64,
expiration: PlacesUtils.history.ANNOTATION_EXPIRE_NEVER,
},
annotationInfo[0],
"Should have updated the annotation data in the db"
);
await PlacesUtils.history.update({
url: TEST_URL,
annotations: new Map([["test/annotation", null]]),
});
pageInfo = await PlacesUtils.history.fetch(TEST_URL, {
includeAnnotations: true,
});
Assert.equal(
pageInfo.annotations.size,
1,
"Should have removed only the first annotation"
);
Assert.strictEqual(
pageInfo.annotations.get("test/annotation"),
undefined,
"Should have removed only the first annotation"
);
Assert.equal(
pageInfo.annotations.get("test/annotation2"),
"testAnno",
"Should have kept the value for the second annotation"
);
await PlacesUtils.history.update({
url: TEST_URL,
annotations: new Map([["test/annotation2", null]]),
});
pageInfo = await PlacesUtils.history.fetch(TEST_URL, {
includeAnnotations: true,
});
Assert.equal(pageInfo.annotations.size, 0, "Should have no annotations left");
let db = await PlacesUtils.promiseDBConnection();
let rows = await db.execute(`
SELECT * FROM moz_annos
`);
Assert.equal(rows.length, 0, "Should be no annotations left in the db");
});
add_task(async function test_change_multiple_annotations() {
await PlacesUtils.history.clear();
await PlacesTestUtils.addVisits(TEST_URL);
Assert.ok(
await PlacesTestUtils.isPageInDB(TEST_URL),
"Should have inserted the page into the database."
);
await PlacesUtils.history.update({
url: TEST_URL,
annotations: new Map([
["test/annotation", "testContent"],
["test/annotation2", "testAnno"],
]),
});
let pageInfo = await PlacesUtils.history.fetch(TEST_URL, {
includeAnnotations: true,
});
Assert.equal(
pageInfo.annotations.size,
2,
"Should have inserted the two annotations for the page."
);
Assert.equal(
pageInfo.annotations.get("test/annotation"),
"testContent",
"Should have the correct value for the first annotation"
);
Assert.equal(
pageInfo.annotations.get("test/annotation2"),
"testAnno",
"Should have the correct value for the second annotation"
);
await PlacesUtils.history.update({
url: TEST_URL,
annotations: new Map([
["test/annotation", 123456],
["test/annotation2", 135246],
]),
});
pageInfo = await PlacesUtils.history.fetch(TEST_URL, {
includeAnnotations: true,
});
Assert.equal(
pageInfo.annotations.size,
2,
"Should have two annotations for the page"
);
Assert.equal(
pageInfo.annotations.get("test/annotation"),
123456,
"Should have the correct value for the first annotation"
);
Assert.equal(
pageInfo.annotations.get("test/annotation2"),
135246,
"Should have the correct value for the second annotation"
);
await PlacesUtils.history.update({
url: TEST_URL,
annotations: new Map([
["test/annotation", null],
["test/annotation2", null],
]),
});
pageInfo = await PlacesUtils.history.fetch(TEST_URL, {
includeAnnotations: true,
});
Assert.equal(pageInfo.annotations.size, 0, "Should have no annotations left");
});
add_task(async function test_annotations_nonexisting_page() {
info("Adding annotations to a non existing page should be silent");
await PlacesUtils.history.update({
annotations: new Map([["test/annotation", null]]),
});
});
add_task(async function test_annotations_nonexisting_page() {
info("Adding annotations to a non existing page should be silent");
await PlacesUtils.history.update({
annotations: new Map([["test/annotation", null]]),
});
});