summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/extensions/newsblog/test
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/extensions/newsblog/test')
-rw-r--r--comm/mailnews/extensions/newsblog/test/browser/browser.ini20
-rw-r--r--comm/mailnews/extensions/newsblog/test/browser/browser_feedDisplay.js228
-rw-r--r--comm/mailnews/extensions/newsblog/test/browser/data/article.html17
-rw-r--r--comm/mailnews/extensions/newsblog/test/browser/data/rss.xml27
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/head_feeds.js35
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/README.md24
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeditems.json1
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeditems.rdf6
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeds.json1
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeds.rdf17
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeditems.json1
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeditems.rdf6
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeds.json1
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeds.rdf12
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeditems.json1
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeditems.rdf6
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeds.json23
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeds.rdf21
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeditems.json122
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeditems.rdf126
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeds.json46
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeds.rdf32
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/rss2_example.xml25
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/rss2_guid.xml42
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/rss_7_1.rdf66
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/resources/rss_7_1_BORKED.rdf66
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/test_feedparser.js146
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/test_rdfmigration.js61
-rw-r--r--comm/mailnews/extensions/newsblog/test/unit/xpcshell.ini8
29 files changed, 1187 insertions, 0 deletions
diff --git a/comm/mailnews/extensions/newsblog/test/browser/browser.ini b/comm/mailnews/extensions/newsblog/test/browser/browser.ini
new file mode 100644
index 0000000000..1abed389c7
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/browser/browser.ini
@@ -0,0 +1,20 @@
+[DEFAULT]
+prefs =
+ datareporting.policy.dataSubmissionPolicyBypassNotification=true
+ mail.account.account1.server=server1
+ mail.accountmanager.accounts=account1
+ mail.provider.suppress_dialog_on_startup=true
+ mail.server.server1.hostname=Feeds
+ mail.server.server1.name=Feeds
+ mail.server.server1.type=rss
+ mail.server.server1.userName=nobody
+ mail.spellcheck.inline=false
+ mail.spotlight.firstRunDone=true
+ mail.winsearch.firstRunDone=true
+ mailnews.database.global.indexer.enabled=false
+ mailnews.start_page.override_url=about:blank
+ mailnews.start_page.url=about:blank
+subsuite = thunderbird
+support-files = data/**
+
+[browser_feedDisplay.js]
diff --git a/comm/mailnews/extensions/newsblog/test/browser/browser_feedDisplay.js b/comm/mailnews/extensions/newsblog/test/browser/browser_feedDisplay.js
new file mode 100644
index 0000000000..98fa755c46
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/browser/browser_feedDisplay.js
@@ -0,0 +1,228 @@
+/* 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/. */
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+var { mailTestUtils } = ChromeUtils.import(
+ "resource://testing-common/mailnews/MailTestUtils.jsm"
+);
+var { MockRegistrar } = ChromeUtils.importESModule(
+ "resource://testing-common/MockRegistrar.sys.mjs"
+);
+
+add_task(async () => {
+ function folderTreeClick(row, event = {}) {
+ EventUtils.synthesizeMouseAtCenter(
+ folderTree.rows[row].querySelector(".name"),
+ event,
+ about3Pane
+ );
+ }
+ function threadTreeClick(row, event = {}) {
+ EventUtils.synthesizeMouseAtCenter(
+ threadTree.getRowAtIndex(row),
+ event,
+ about3Pane
+ );
+ }
+
+ /** @implements {nsIExternalProtocolService} */
+ let mockExternalProtocolService = {
+ QueryInterface: ChromeUtils.generateQI(["nsIExternalProtocolService"]),
+ _loadedURLs: [],
+ loadURI(uri, windowContext) {
+ this._loadedURLs.push(uri.spec);
+ },
+ isExposedProtocol(scheme) {
+ return true;
+ },
+ urlLoaded(url) {
+ return this._loadedURLs.includes(url);
+ },
+ };
+
+ let mockExternalProtocolServiceCID = MockRegistrar.register(
+ "@mozilla.org/uriloader/external-protocol-service;1",
+ mockExternalProtocolService
+ );
+
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(mockExternalProtocolServiceCID);
+
+ // Some tests that open new windows don't return focus to the main window
+ // in a way that satisfies mochitest, and the test times out.
+ Services.focus.focusedWindow = about3Pane;
+ });
+
+ let tabmail = document.getElementById("tabmail");
+ let about3Pane = tabmail.currentAbout3Pane;
+ let { folderTree, threadTree, messageBrowser } = about3Pane;
+ let menu = about3Pane.document.getElementById("folderPaneContext");
+ let menuItem = about3Pane.document.getElementById(
+ "folderPaneContext-subscribe"
+ );
+ // Not `currentAboutMessage` as that's null right now.
+ let aboutMessage = messageBrowser.contentWindow;
+ let messagePane = aboutMessage.getMessagePaneBrowser();
+
+ let account = MailServices.accounts.getAccount("account1");
+ let rootFolder = account.incomingServer.rootFolder;
+ about3Pane.displayFolder(rootFolder.URI);
+ let index = about3Pane.folderTree.selectedIndex;
+ Assert.equal(index, 0);
+
+ let shownPromise = BrowserTestUtils.waitForEvent(menu, "popupshown");
+ folderTreeClick(index, { type: "contextmenu" });
+ await shownPromise;
+
+ let hiddenPromise = BrowserTestUtils.waitForEvent(menu, "popuphidden");
+ let dialogPromise = BrowserTestUtils.promiseAlertDialog(
+ null,
+ "chrome://messenger-newsblog/content/feed-subscriptions.xhtml",
+ {
+ async callback(dialogWindow) {
+ let dialogDocument = dialogWindow.document;
+
+ let list = dialogDocument.getElementById("rssSubscriptionsList");
+ let locationInput = dialogDocument.getElementById("locationValue");
+ let addFeedButton = dialogDocument.getElementById("addFeed");
+
+ await BrowserTestUtils.waitForEvent(list, "select");
+
+ EventUtils.synthesizeMouseAtCenter(locationInput, {}, dialogWindow);
+ await TestUtils.waitForCondition(() => !addFeedButton.disabled);
+ EventUtils.sendString(
+ "https://example.org/browser/comm/mailnews/extensions/newsblog/test/browser/data/rss.xml",
+ dialogWindow
+ );
+ EventUtils.synthesizeKey("VK_TAB", {}, dialogWindow);
+
+ // There's no good way to know if we're ready to continue.
+ await new Promise(r => dialogWindow.setTimeout(r, 250));
+
+ let hiddenPromise = BrowserTestUtils.waitForAttribute(
+ "hidden",
+ addFeedButton,
+ "true"
+ );
+ EventUtils.synthesizeMouseAtCenter(addFeedButton, {}, dialogWindow);
+ await hiddenPromise;
+
+ EventUtils.synthesizeMouseAtCenter(
+ dialogDocument.querySelector("dialog").getButton("accept"),
+ {},
+ dialogWindow
+ );
+ },
+ }
+ );
+ menu.activateItem(menuItem);
+ await Promise.all([hiddenPromise, dialogPromise]);
+
+ let folder = rootFolder.subFolders.find(f => f.name == "Test Feed");
+ Assert.ok(folder);
+
+ about3Pane.displayFolder(folder.URI);
+ index = folderTree.selectedIndex;
+ Assert.equal(about3Pane.threadTree.view.rowCount, 1);
+
+ // Description mode.
+
+ let loadedPromise = BrowserTestUtils.browserLoaded(messagePane);
+ threadTreeClick(0);
+ await loadedPromise;
+
+ Assert.notEqual(messagePane.currentURI.spec, "about:blank");
+ await SpecialPowers.spawn(messagePane, [], () => {
+ let doc = content.document;
+
+ let p = doc.querySelector("p");
+ Assert.equal(p.textContent, "This is the description.");
+
+ let style = content.getComputedStyle(doc.body);
+ Assert.equal(style.backgroundColor, "rgba(0, 0, 0, 0)");
+
+ let noscript = doc.querySelector("noscript");
+ style = content.getComputedStyle(noscript);
+ Assert.equal(style.display, "inline");
+ });
+
+ Assert.ok(
+ aboutMessage.document.getElementById("expandedtoRow").hidden,
+ "The To field is not visible"
+ );
+ Assert.equal(
+ aboutMessage.document.getElementById("dateLabel").textContent,
+ aboutMessage.document.getElementById("dateLabelSubject").textContent,
+ "The regular date label and the subject date have the same value"
+ );
+ Assert.ok(
+ BrowserTestUtils.is_hidden(
+ aboutMessage.document.getElementById("dateLabel"),
+ "The regular date label is not visible"
+ )
+ );
+ Assert.ok(
+ BrowserTestUtils.is_visible(
+ aboutMessage.document.getElementById("dateLabelSubject")
+ ),
+ "The date label on the subject line is visible"
+ );
+
+ await BrowserTestUtils.synthesizeMouseAtCenter("a", {}, messagePane);
+ Assert.deepEqual(mockExternalProtocolService._loadedURLs, [
+ "https://example.org/link/from/description",
+ ]);
+ mockExternalProtocolService._loadedURLs.length = 0;
+
+ // Web mode.
+
+ loadedPromise = BrowserTestUtils.browserLoaded(
+ messagePane,
+ false,
+ "https://example.org/browser/comm/mailnews/extensions/newsblog/test/browser/data/article.html"
+ );
+ window.FeedMessageHandler.onSelectPref = 0;
+ await loadedPromise;
+
+ await SpecialPowers.spawn(messagePane, [], () => {
+ let doc = content.document;
+
+ let p = doc.querySelector("p");
+ Assert.equal(p.textContent, "This is the article.");
+
+ let style = content.getComputedStyle(doc.body);
+ Assert.equal(style.backgroundColor, "rgb(0, 128, 0)");
+
+ let noscript = doc.querySelector("noscript");
+ style = content.getComputedStyle(noscript);
+ Assert.equal(style.display, "none");
+ });
+ await BrowserTestUtils.synthesizeMouseAtCenter("a", {}, messagePane);
+ Assert.deepEqual(mockExternalProtocolService._loadedURLs, [
+ "https://example.org/link/from/article",
+ ]);
+ mockExternalProtocolService._loadedURLs.length = 0;
+
+ // Clean up.
+
+ shownPromise = BrowserTestUtils.waitForEvent(menu, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(
+ about3Pane.folderTree.selectedRow,
+ { type: "contextmenu" },
+ about3Pane
+ );
+ await shownPromise;
+
+ hiddenPromise = BrowserTestUtils.waitForEvent(menu, "popuphidden");
+ let promptPromise = BrowserTestUtils.promiseAlertDialog("accept");
+ menuItem = about3Pane.document.getElementById("folderPaneContext-remove");
+ menu.activateItem(menuItem);
+ await Promise.all([hiddenPromise, promptPromise]);
+
+ window.FeedMessageHandler.onSelectPref = 1;
+
+ folderTree.selectedIndex = 0;
+});
diff --git a/comm/mailnews/extensions/newsblog/test/browser/data/article.html b/comm/mailnews/extensions/newsblog/test/browser/data/article.html
new file mode 100644
index 0000000000..4c6b780c41
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/browser/data/article.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title></title>
+ </head>
+ <body>
+ <p>This is the article.</p>
+ <p><a href="https://example.org/link/from/article">Here's a link.</a></p>
+ <script>
+ document.body.style.backgroundColor = "green";
+ </script>
+ <noscript>
+ <p>This noscript should not display.</p>
+ </noscript>
+ </body>
+</html>
diff --git a/comm/mailnews/extensions/newsblog/test/browser/data/rss.xml b/comm/mailnews/extensions/newsblog/test/browser/data/rss.xml
new file mode 100644
index 0000000000..ec6aebccc1
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/browser/data/rss.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rss version="2.0">
+ <channel>
+ <title>Test Feed</title>
+ <link>https://example.org/</link>
+ <description></description>
+ <lastBuildDate>Thu, 21 Jan 2021 17:57:54 +0000</lastBuildDate>
+ <language>en-US</language>
+
+ <item>
+ <title>Test Article</title>
+ <link>https://example.org/browser/comm/mailnews/extensions/newsblog/test/browser/data/article.html</link>
+ <pubDate>Wed, 20 Jan 2021 17:00:39 +0000</pubDate>
+
+ <description><![CDATA[
+ <p>This is the description.</p>
+ <p><a href="https://example.org/link/from/description">Here's a link.</a></p>
+ <script>
+ document.body.style.backgroundColor = "red";
+ </script>
+ <noscript>
+ <p>This noscript should display.</p>
+ </noscript>
+ ]]></description>
+ </item>
+ </channel>
+</rss>
diff --git a/comm/mailnews/extensions/newsblog/test/unit/head_feeds.js b/comm/mailnews/extensions/newsblog/test/unit/head_feeds.js
new file mode 100644
index 0000000000..004e1acf68
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/head_feeds.js
@@ -0,0 +1,35 @@
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+var { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+var { mailTestUtils } = ChromeUtils.import(
+ "resource://testing-common/mailnews/MailTestUtils.jsm"
+);
+var { localAccountUtils } = ChromeUtils.import(
+ "resource://testing-common/mailnews/LocalAccountUtils.jsm"
+);
+
+let { FeedParser } = ChromeUtils.import("resource:///modules/FeedParser.jsm");
+let { Feed } = ChromeUtils.import("resource:///modules/Feed.jsm");
+let { FeedUtils } = ChromeUtils.import("resource:///modules/FeedUtils.jsm");
+let { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+
+// Set up local web server to serve up test files.
+// We run it on a random port so that other tests can run concurrently
+// even if they also run a web server.
+let httpServer = new HttpServer();
+httpServer.registerDirectory("/", do_get_file("resources"));
+httpServer.start(-1);
+const SERVER_PORT = httpServer.identity.primaryPort;
+
+// Ensure the profile directory is set up
+do_get_profile();
+
+var gDEPTH = "../../../../../";
+
+registerCleanupFunction(async () => {
+ await httpServer.stop();
+ load(gDEPTH + "mailnews/resources/mailShutdown.js");
+});
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/README.md b/comm/mailnews/extensions/newsblog/test/unit/resources/README.md
new file mode 100644
index 0000000000..df655769b0
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/README.md
@@ -0,0 +1,24 @@
+Data files for unit testing the feeds code.
+
+- `rss_7_1.rdf`
+ Simple RSS1.0 feed example, from:
+ https://www.w3.org/2000/10/rdf-tests/RSS_1.0/rss_7_1.rdf
+
+- `rss_7_1_BORKED.rdf`
+ Sabotaged version of `rss_7_1.rdf` with a bad
+ <items> list, pointing to all sorts of URLs not
+ represented as <item>s in the feed (see Bug 476641).
+
+- `rss2_example.xml`
+ RSS2.0 example from wikipedia, but with
+ Japanese text in the title, with leading/trailing
+ whitespace.
+
+- `rss2_guid.xml`
+ RSS2.0 feed where two items have the same link but different guid.
+ (they should both appear in the feed).
+
+- `feeds-*/`
+ Test cases for migrating legacy .rdf config files to
+ the new json ones. The .rdf files are the old data,
+ and the .json files are the expected results.
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeditems.json b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeditems.json
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeditems.json
@@ -0,0 +1 @@
+{}
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeditems.rdf b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeditems.rdf
new file mode 100644
index 0000000000..81a5a62ec5
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeditems.rdf
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:fz="urn:forumzilla:"
+ xmlns:NC="http://home.netscape.com/NC-rdf#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+</RDF:RDF>
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeds.json b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeds.json
new file mode 100644
index 0000000000..fe51488c70
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeds.json
@@ -0,0 +1 @@
+[]
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeds.rdf b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeds.rdf
new file mode 100644
index 0000000000..f7e6b400e2
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-bad/feeds.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:NS1="http://purl.org/rss/1.0/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:fz="urn:forumzilla:"
+ xmlns:NC="http://home.netscape.com/NC-rdf#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <RDF:Description RDF:about="urn:forumzilla:root">
+ <fz:feeds RDF:resource="rdf:#$cvA6q"/>
+ </RDF:Description>
+ <fz:feed RDF:about="https://example.com/feed/"
+ fz:quickMode="false"
+ dc:title="A feed with no dc:identifier, and thus no url. Should be ditched.">
+ </fz:feed>
+ <RDF:Seq RDF:about="rdf:#$cvA6q">
+ <RDF:li RDF:resource="https://example.com/feed/"/>
+ </RDF:Seq>
+</RDF:RDF>
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeditems.json b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeditems.json
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeditems.json
@@ -0,0 +1 @@
+{}
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeditems.rdf b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeditems.rdf
new file mode 100644
index 0000000000..81a5a62ec5
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeditems.rdf
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:fz="urn:forumzilla:"
+ xmlns:NC="http://home.netscape.com/NC-rdf#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+</RDF:RDF>
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeds.json b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeds.json
new file mode 100644
index 0000000000..fe51488c70
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeds.json
@@ -0,0 +1 @@
+[]
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeds.rdf b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeds.rdf
new file mode 100644
index 0000000000..a5f9c997e8
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-empty/feeds.rdf
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:NS1="http://purl.org/rss/1.0/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:fz="urn:forumzilla:"
+ xmlns:NC="http://home.netscape.com/NC-rdf#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <RDF:Description RDF:about="urn:forumzilla:root">
+ <fz:feeds RDF:resource="rdf:#$cvA6q"/>
+ </RDF:Description>
+ <RDF:Seq RDF:about="rdf:#$cvA6q">
+ </RDF:Seq>
+</RDF:RDF>
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeditems.json b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeditems.json
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeditems.json
@@ -0,0 +1 @@
+{}
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeditems.rdf b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeditems.rdf
new file mode 100644
index 0000000000..81a5a62ec5
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeditems.rdf
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:fz="urn:forumzilla:"
+ xmlns:NC="http://home.netscape.com/NC-rdf#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+</RDF:RDF>
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeds.json b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeds.json
new file mode 100644
index 0000000000..e20f7a2f00
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeds.json
@@ -0,0 +1,23 @@
+[
+ {
+ "title": "Government Digital Service",
+ "url": "https://gds.blog.gov.uk/feed/",
+ "quickMode": false,
+ "options": {
+ "version": 2,
+ "updates": {
+ "enabled": true,
+ "updateMinutes": 100,
+ "updateUnits": "min",
+ "lastUpdateTime": 1568784489107,
+ "lastDownloadTime": null,
+ "updatePeriod": "",
+ "updateFrequency": "",
+ "updateBase": ""
+ },
+ "category": { "enabled": false, "prefixEnabled": false, "prefix": "" }
+ },
+ "destFolder": "mailbox://nobody@Feeds/Government%20Digital%20Service",
+ "link": "https://gds.blog.gov.uk/feed/"
+ }
+]
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeds.rdf b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeds.rdf
new file mode 100644
index 0000000000..3b6b6b2df8
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-missing-timestamp/feeds.rdf
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:NS1="http://purl.org/rss/1.0/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:fz="urn:forumzilla:"
+ xmlns:NC="http://home.netscape.com/NC-rdf#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <RDF:Description RDF:about="urn:forumzilla:root">
+ <fz:feeds RDF:resource="rdf:#$cvA6q"/>
+ </RDF:Description>
+ <fz:feed RDF:about="https://gds.blog.gov.uk/feed/"
+ fz:quickMode="false"
+ dc:title="Government Digital Service"
+ NS1:link="https://gds.blog.gov.uk/feed/"
+ fz:options="{&quot;version&quot;:2,&quot;updates&quot;:{&quot;enabled&quot;:true,&quot;updateMinutes&quot;:100,&quot;updateUnits&quot;:&quot;min&quot;,&quot;lastUpdateTime&quot;:1568784489107,&quot;lastDownloadTime&quot;:null,&quot;updatePeriod&quot;:&quot;&quot;,&quot;updateFrequency&quot;:&quot;&quot;,&quot;updateBase&quot;:&quot;&quot;},&quot;category&quot;:{&quot;enabled&quot;:false,&quot;prefixEnabled&quot;:false,&quot;prefix&quot;:&quot;&quot;}}"
+ dc:identifier="https://gds.blog.gov.uk/feed/">
+ <fz:destFolder RDF:resource="mailbox://nobody@Feeds/Government%20Digital%20Service"/>
+ </fz:feed>
+ <RDF:Seq RDF:about="rdf:#$cvA6q">
+ <RDF:li RDF:resource="https://gds.blog.gov.uk/feed/"/>
+ </RDF:Seq>
+</RDF:RDF>
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeditems.json b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeditems.json
new file mode 100644
index 0000000000..a16c68f0c0
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeditems.json
@@ -0,0 +1,122 @@
+{
+ "https://gds.blog.gov.uk/?p=32978": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784488792,
+ "feedURLs": ["https://gds.blog.gov.uk/feed/"]
+ },
+ "https://gds.blog.gov.uk/?p=32944": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784488971,
+ "feedURLs": ["https://gds.blog.gov.uk/feed/"]
+ },
+ "https://gds.blog.gov.uk/?p=33011": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784488610,
+ "feedURLs": ["https://gds.blog.gov.uk/feed/"]
+ },
+ "https://gds.blog.gov.uk/?p=33020": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784488551,
+ "feedURLs": ["https://gds.blog.gov.uk/feed/"]
+ },
+ "https://civilservice.blog.gov.uk/?p=16464": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784520041,
+ "feedURLs": ["https://civilservice.blog.gov.uk/feed/"]
+ },
+ "https://gds.blog.gov.uk/?p=32951": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784488909,
+ "feedURLs": ["https://gds.blog.gov.uk/feed/"]
+ },
+ "https://gds.blog.gov.uk/?p=32963": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784488851,
+ "feedURLs": ["https://gds.blog.gov.uk/feed/"]
+ },
+ "https://civilservice.blog.gov.uk/?p=16431": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784520152,
+ "feedURLs": ["https://civilservice.blog.gov.uk/feed/"]
+ },
+ "https://civilservice.blog.gov.uk/?p=16477": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784519983,
+ "feedURLs": ["https://civilservice.blog.gov.uk/feed/"]
+ },
+ "https://gds.blog.gov.uk/?p=32939": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784489030,
+ "feedURLs": ["https://gds.blog.gov.uk/feed/"]
+ },
+ "https://civilservice.blog.gov.uk/?p=16453": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784520096,
+ "feedURLs": ["https://civilservice.blog.gov.uk/feed/"]
+ },
+ "https://civilservice.blog.gov.uk/?p=16418": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784520209,
+ "feedURLs": ["https://civilservice.blog.gov.uk/feed/"]
+ },
+ "https://civilservice.blog.gov.uk/?p=16507": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784519869,
+ "feedURLs": ["https://civilservice.blog.gov.uk/feed/"]
+ },
+ "https://civilservice.blog.gov.uk/?p=16490": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784519926,
+ "feedURLs": ["https://civilservice.blog.gov.uk/feed/"]
+ },
+ "https://civilservice.blog.gov.uk/?p=16378": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784520323,
+ "feedURLs": ["https://civilservice.blog.gov.uk/feed/"]
+ },
+ "https://gds.blog.gov.uk/?p=32927": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784489089,
+ "feedURLs": ["https://gds.blog.gov.uk/feed/"]
+ },
+ "https://gds.blog.gov.uk/?p=33001": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784488670,
+ "feedURLs": ["https://gds.blog.gov.uk/feed/"]
+ },
+ "https://civilservice.blog.gov.uk/?p=16393": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784520265,
+ "feedURLs": ["https://civilservice.blog.gov.uk/feed/"]
+ },
+ "https://civilservice.blog.gov.uk/?p=16514": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784519812,
+ "feedURLs": ["https://civilservice.blog.gov.uk/feed/"]
+ },
+ "https://gds.blog.gov.uk/?p=32988": {
+ "stored": true,
+ "valid": true,
+ "lastSeenTime": 1568784488731,
+ "feedURLs": ["https://gds.blog.gov.uk/feed/"]
+ }
+}
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeditems.rdf b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeditems.rdf
new file mode 100644
index 0000000000..f0941b5f6b
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeditems.rdf
@@ -0,0 +1,126 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:fz="urn:forumzilla:"
+ xmlns:NC="http://home.netscape.com/NC-rdf#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fgds.blog.gov.uk%2f%3fp=32978"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784488792"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://gds.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fgds.blog.gov.uk%2f%3fp=32944"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784488971"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://gds.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fgds.blog.gov.uk%2f%3fp=33011"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784488610"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://gds.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fgds.blog.gov.uk%2f%3fp=33020"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784488551"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://gds.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fcivilservice.blog.gov.uk%2f%3fp=16464"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784520041"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://civilservice.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fgds.blog.gov.uk%2f%3fp=32951"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784488909"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://gds.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fgds.blog.gov.uk%2f%3fp=32963"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784488851"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://gds.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fcivilservice.blog.gov.uk%2f%3fp=16431"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784520152"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://civilservice.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fcivilservice.blog.gov.uk%2f%3fp=16477"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784519983"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://civilservice.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fgds.blog.gov.uk%2f%3fp=32939"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784489030"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://gds.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fcivilservice.blog.gov.uk%2f%3fp=16453"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784520096"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://civilservice.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fcivilservice.blog.gov.uk%2f%3fp=16418"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784520209"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://civilservice.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fcivilservice.blog.gov.uk%2f%3fp=16507"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784519869"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://civilservice.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fcivilservice.blog.gov.uk%2f%3fp=16490"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784519926"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://civilservice.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fcivilservice.blog.gov.uk%2f%3fp=16378"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784520323"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://civilservice.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fgds.blog.gov.uk%2f%3fp=32927"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784489089"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://gds.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fgds.blog.gov.uk%2f%3fp=33001"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784488670"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://gds.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fcivilservice.blog.gov.uk%2f%3fp=16393"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784520265"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://civilservice.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fcivilservice.blog.gov.uk%2f%3fp=16514"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784519812"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://civilservice.blog.gov.uk/feed/"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:feeditem:https:%2f%2fgds.blog.gov.uk%2f%3fp=32988"
+ fz:stored="true"
+ fz:last-seen-timestamp="1568784488731"
+ fz:valid="true">
+ <fz:feed RDF:resource="https://gds.blog.gov.uk/feed/"/>
+ </RDF:Description>
+</RDF:RDF>
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeds.json b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeds.json
new file mode 100644
index 0000000000..2ab7d4f780
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeds.json
@@ -0,0 +1,46 @@
+[
+ {
+ "title": "Government Digital Service",
+ "lastModified": "Wed, 11 Sep 2019 15:47:49 GMT",
+ "url": "https://gds.blog.gov.uk/feed/",
+ "quickMode": false,
+ "options": {
+ "version": 2,
+ "updates": {
+ "enabled": true,
+ "updateMinutes": 100,
+ "updateUnits": "min",
+ "lastUpdateTime": 1568784489107,
+ "lastDownloadTime": null,
+ "updatePeriod": "",
+ "updateFrequency": "",
+ "updateBase": ""
+ },
+ "category": { "enabled": false, "prefixEnabled": false, "prefix": "" }
+ },
+ "destFolder": "mailbox://nobody@Feeds/Government%20Digital%20Service",
+ "link": "https://gds.blog.gov.uk/feed/"
+ },
+ {
+ "title": "Civil Service",
+ "lastModified": "Tue, 17 Sep 2019 16:21:00 GMT",
+ "url": "https://civilservice.blog.gov.uk/feed/",
+ "quickMode": false,
+ "options": {
+ "version": 2,
+ "updates": {
+ "enabled": true,
+ "updateMinutes": 100,
+ "updateUnits": "min",
+ "lastUpdateTime": 1568784520338,
+ "lastDownloadTime": null,
+ "updatePeriod": "",
+ "updateFrequency": "",
+ "updateBase": ""
+ },
+ "category": { "enabled": false, "prefixEnabled": false, "prefix": "" }
+ },
+ "destFolder": "mailbox://nobody@Feeds/Civil%20Service",
+ "link": "https://civilservice.blog.gov.uk/feed/"
+ }
+]
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeds.rdf b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeds.rdf
new file mode 100644
index 0000000000..5c2fb72c74
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/feeds-simple/feeds.rdf
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:NS1="http://purl.org/rss/1.0/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:fz="urn:forumzilla:"
+ xmlns:NC="http://home.netscape.com/NC-rdf#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <RDF:Description RDF:about="urn:forumzilla:root">
+ <fz:feeds RDF:resource="rdf:#$cvA6q"/>
+ </RDF:Description>
+ <fz:feed RDF:about="https://gds.blog.gov.uk/feed/"
+ fz:quickMode="false"
+ dc:title="Government Digital Service"
+ NS1:link="https://gds.blog.gov.uk/feed/"
+ dc:lastModified="Wed, 11 Sep 2019 15:47:49 GMT"
+ fz:options="{&quot;version&quot;:2,&quot;updates&quot;:{&quot;enabled&quot;:true,&quot;updateMinutes&quot;:100,&quot;updateUnits&quot;:&quot;min&quot;,&quot;lastUpdateTime&quot;:1568784489107,&quot;lastDownloadTime&quot;:null,&quot;updatePeriod&quot;:&quot;&quot;,&quot;updateFrequency&quot;:&quot;&quot;,&quot;updateBase&quot;:&quot;&quot;},&quot;category&quot;:{&quot;enabled&quot;:false,&quot;prefixEnabled&quot;:false,&quot;prefix&quot;:&quot;&quot;}}"
+ dc:identifier="https://gds.blog.gov.uk/feed/">
+ <fz:destFolder RDF:resource="mailbox://nobody@Feeds/Government%20Digital%20Service"/>
+ </fz:feed>
+ <fz:feed RDF:about="https://civilservice.blog.gov.uk/feed/"
+ fz:quickMode="false"
+ dc:title="Civil Service"
+ NS1:link="https://civilservice.blog.gov.uk/feed/"
+ dc:lastModified="Tue, 17 Sep 2019 16:21:00 GMT"
+ fz:options="{&quot;version&quot;:2,&quot;updates&quot;:{&quot;enabled&quot;:true,&quot;updateMinutes&quot;:100,&quot;updateUnits&quot;:&quot;min&quot;,&quot;lastUpdateTime&quot;:1568784520338,&quot;lastDownloadTime&quot;:null,&quot;updatePeriod&quot;:&quot;&quot;,&quot;updateFrequency&quot;:&quot;&quot;,&quot;updateBase&quot;:&quot;&quot;},&quot;category&quot;:{&quot;enabled&quot;:false,&quot;prefixEnabled&quot;:false,&quot;prefix&quot;:&quot;&quot;}}"
+ dc:identifier="https://civilservice.blog.gov.uk/feed/">
+ <fz:destFolder RDF:resource="mailbox://nobody@Feeds/Civil%20Service"/>
+ </fz:feed>
+ <RDF:Seq RDF:about="rdf:#$cvA6q">
+ <RDF:li RDF:resource="https://gds.blog.gov.uk/feed/"/>
+ <RDF:li RDF:resource="https://civilservice.blog.gov.uk/feed/"/>
+ </RDF:Seq>
+</RDF:RDF>
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/rss2_example.xml b/comm/mailnews/extensions/newsblog/test/unit/resources/rss2_example.xml
new file mode 100644
index 0000000000..0fe6268ec1
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/rss2_example.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<rss version="2.0">
+<channel>
+ <title>
+
+ 本当に簡単なシンジケーションの例
+
+ </title>
+ <description>This is an example of an RSS feed</description>
+ <link>http://www.example.com/main.html</link>
+ <lastBuildDate>Mon, 06 Sep 2010 00:01:00 +0000 </lastBuildDate>
+ <pubDate>Sun, 06 Sep 2009 16:20:00 +0000</pubDate>
+ <ttl>1800</ttl>
+
+ <item>
+ <title>Example entry</title>
+ <description>Here is some text containing an interesting description.</description>
+ <link>http://www.example.com/blog/post/1</link>
+ <guid isPermaLink="false">7bd204c6-1655-4c27-aeee-53f933c5395f</guid>
+ <pubDate>Sun, 06 Sep 2009 16:20:00 +0000</pubDate>
+ </item>
+
+</channel>
+</rss>
+
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/rss2_guid.xml b/comm/mailnews/extensions/newsblog/test/unit/resources/rss2_guid.xml
new file mode 100644
index 0000000000..9f1efea9cc
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/rss2_guid.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<rss version="2.0">
+<channel>
+ <title>GUID test</title>
+ <description>This RSS feed has multiple items with the same link, but with different guids. They should be treated as separate items (see Bug 1656090)</description>
+ <link>http://www.example.com/main.html</link>
+ <lastBuildDate>Mon, 06 Sep 2020 00:01:00 +0000 </lastBuildDate>
+ <pubDate>Sun, 06 Sep 2019 16:20:00 +0000</pubDate>
+ <ttl>1800</ttl>
+
+ <item>
+ <title>Entry One</title>
+ <description>Blah blah blah.</description>
+ <link>http://www.example.com/blog/post/1</link>
+ <guid isPermaLink="false">0524a046-df56-11ea-8bc9-47d63411283f</guid>
+ <pubDate>Sun, 06 Sep 2019 16:20:00 +0000</pubDate>
+ </item>
+ <item>
+ <title>Entry One Again(with same link but different guid!)</title>
+ <description>Blah blah blah.</description>
+ <link>http://www.example.com/blog/post/1</link>
+ <guid isPermaLink="false">0524a35c-df56-11ea-8bca-43f820f3bd93</guid>
+ <pubDate>Sun, 06 Sep 2019 16:20:00 +0000</pubDate>
+ </item>
+ <item>
+ <title>Entry Two</title>
+ <description>Blah blah blah.</description>
+ <link>http://www.example.com/blog/post/2</link>
+ <guid isPermaLink="false">0524a442-df56-11ea-8bcb-035a71f5f71a</guid>
+ <pubDate>Sun, 06 Sep 2019 16:20:00 +0000</pubDate>
+ </item>
+ <item>
+ <title>Entry Three</title>
+ <description>Blah blah blah.</description>
+ <link>http://www.example.com/blog/post/3</link>
+ <guid isPermaLink="false">0524a53c-df56-11ea-8bcc-8f0977d58351</guid>
+ <pubDate>Sun, 06 Sep 2019 16:20:00 +0000</pubDate>
+ </item>
+
+</channel>
+</rss>
+
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/rss_7_1.rdf b/comm/mailnews/extensions/newsblog/test/unit/resources/rss_7_1.rdf
new file mode 100644
index 0000000000..179965e7de
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/rss_7_1.rdf
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+
+<!-- RDF Site Summary (RSS) 1.0
+ http://groups.yahoo.com/group/rss-dev/files/specification.html
+ Section 7
+ -->
+
+<rdf:RDF
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://purl.org/rss/1.0/">
+
+ <channel rdf:about="http://www.xml.com/xml/news.rss">
+ <title>XML.com</title>
+ <link>http://xml.com/pub</link>
+ <description>
+ XML.com features a rich mix of information and services
+ for the XML community.
+ </description>
+
+ <image rdf:resource="http://xml.com/universal/images/xml_tiny.gif" />
+
+ <items>
+ <rdf:Seq>
+ <rdf:li resource="http://xml.com/pub/2000/08/09/xslt/xslt.html" />
+ <rdf:li resource="http://xml.com/pub/2000/08/09/rdfdb/index.html" />
+ </rdf:Seq>
+ </items>
+
+ <textinput rdf:resource="http://search.xml.com" />
+
+ </channel>
+
+ <image rdf:about="http://xml.com/universal/images/xml_tiny.gif">
+ <title>XML.com</title>
+ <link>http://www.xml.com</link>
+ <url>http://xml.com/universal/images/xml_tiny.gif</url>
+ </image>
+
+ <item rdf:about="http://xml.com/pub/2000/08/09/xslt/xslt.html">
+ <title>Processing Inclusions with XSLT</title>
+ <link>http://xml.com/pub/2000/08/09/xslt/xslt.html</link>
+ <description>
+ Processing document inclusions with general XML tools can be
+ problematic. This article proposes a way of preserving inclusion
+ information through SAX-based processing.
+ </description>
+ </item>
+
+ <item rdf:about="http://xml.com/pub/2000/08/09/rdfdb/index.html">
+ <title>Putting RDF to Work</title>
+ <link>http://xml.com/pub/2000/08/09/rdfdb/index.html</link>
+ <description>
+ Tool and API support for the Resource Description Framework
+ is slowly coming of age. Edd Dumbill takes a look at RDFDB,
+ one of the most exciting new RDF toolkits.
+ </description>
+ </item>
+
+ <textinput rdf:about="http://search.xml.com">
+ <title>Search XML.com</title>
+ <description>Search XML.com's XML collection</description>
+ <name>s</name>
+ <link>http://search.xml.com</link>
+ </textinput>
+
+</rdf:RDF>
diff --git a/comm/mailnews/extensions/newsblog/test/unit/resources/rss_7_1_BORKED.rdf b/comm/mailnews/extensions/newsblog/test/unit/resources/rss_7_1_BORKED.rdf
new file mode 100644
index 0000000000..e2c6e0b109
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/resources/rss_7_1_BORKED.rdf
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+
+<!-- RDF Site Summary (RSS) 1.0
+ http://groups.yahoo.com/group/rss-dev/files/specification.html
+ Section 7
+ -->
+
+<rdf:RDF
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://purl.org/rss/1.0/">
+
+ <channel rdf:about="http://www.xml.com/xml/news.rss">
+ <title>XML.com</title>
+ <link>http://xml.com/pub</link>
+ <description>
+ XML.com features a rich mix of information and services
+ for the XML community.
+ </description>
+
+ <image rdf:resource="http://xml.com/universal/images/xml_tiny.gif" />
+
+ <items>
+ <rdf:Seq>
+ <rdf:li resource="http://OBVIOUSLY_WRONG_URL.com/blah/blah.html" />
+ <rdf:li resource="http://ANOTHER_OBVIOUSLY_WRONG_URL.com/foo/bar/wibble.html" />
+ </rdf:Seq>
+ </items>
+
+ <textinput rdf:resource="http://search.xml.com" />
+
+ </channel>
+
+ <image rdf:about="http://xml.com/universal/images/xml_tiny.gif">
+ <title>XML.com</title>
+ <link>http://www.xml.com</link>
+ <url>http://xml.com/universal/images/xml_tiny.gif</url>
+ </image>
+
+ <item rdf:about="http://xml.com/pub/2000/08/09/xslt/xslt.html">
+ <title>Processing Inclusions with XSLT</title>
+ <link>http://xml.com/pub/2000/08/09/xslt/xslt.html</link>
+ <description>
+ Processing document inclusions with general XML tools can be
+ problematic. This article proposes a way of preserving inclusion
+ information through SAX-based processing.
+ </description>
+ </item>
+
+ <item rdf:about="http://xml.com/pub/2000/08/09/rdfdb/index.html">
+ <title>Putting RDF to Work</title>
+ <link>http://xml.com/pub/2000/08/09/rdfdb/index.html</link>
+ <description>
+ Tool and API support for the Resource Description Framework
+ is slowly coming of age. Edd Dumbill takes a look at RDFDB,
+ one of the most exciting new RDF toolkits.
+ </description>
+ </item>
+
+ <textinput rdf:about="http://search.xml.com">
+ <title>Search XML.com</title>
+ <description>Search XML.com's XML collection</description>
+ <name>s</name>
+ <link>http://search.xml.com</link>
+ </textinput>
+
+</rdf:RDF>
diff --git a/comm/mailnews/extensions/newsblog/test/unit/test_feedparser.js b/comm/mailnews/extensions/newsblog/test/unit/test_feedparser.js
new file mode 100644
index 0000000000..6577280356
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/test_feedparser.js
@@ -0,0 +1,146 @@
+// see https://www.w3.org/2000/10/rdf-tests/ for test files (including rss1)
+// - test examples for all feed types
+// - test items in test_download()
+// - test rss1 feed with itunes `new-feed-url` redirect
+// - test rss1 feed with RSS syndication extension tags (updatePeriod et al)
+// - test multiple/missing authors (with fallback to feed title)
+// - test missing dates
+// - test content formatting
+
+// Some RSS1 feeds in the wild:
+// https://www.livejournal.com/stats/latest-rss.bml
+// https://journals.sagepub.com/action/showFeed?ui=0&mi=ehikzz&ai=2b4&jc=acrc&type=etoc&feed=rss
+// https://www.revolutionspodcast.com/index.rdf
+// https://www.tandfonline.com/feed/rss/uasa20
+// http://export.arxiv.org/rss/astro-ph
+// - uses html formatting in <dc:creator>
+
+// Helper to compare feeditems.
+function assertItemsEqual(got, expected) {
+ Assert.equal(got.length, expected.length);
+ for (let i = 0; i < expected.length; i++) {
+ // Only check fields in expected. Means testdata can exclude "description" and other bulky fields.
+ for (let k of Object.keys(expected[i])) {
+ Assert.equal(got[i][k], expected[i][k]);
+ }
+ }
+}
+
+// Test the rss1 feed parser
+add_task(async function test_rss1() {
+ // Boilerplate.
+ let account = FeedUtils.createRssAccount("test_rss1");
+ let rootFolder = account.incomingServer.rootMsgFolder.QueryInterface(
+ Ci.nsIMsgLocalMailFolder
+ );
+ let folder = rootFolder.createLocalSubfolder("folderofeeds");
+
+ // These two files yield the same feed, but the second one has a sabotaged
+ // <items> to simulate badly-encoded feeds seen in the wild.
+ for (let testFile of [
+ "resources/rss_7_1.rdf",
+ "resources/rss_7_1_BORKED.rdf",
+ ]) {
+ dump(`checking ${testFile}\n`);
+ // Would be nicer to use the test http server to fetch the file, but that
+ // would involve XMLHTTPRequest. This is more concise.
+ let doc = await do_parse_document(testFile, "application/xml");
+ let feed = new Feed(
+ "https://www.w3.org/2000/10/rdf-tests/RSS_1.0/rss_7_1.rdf",
+ folder
+ );
+ feed.parseItems = true; // We want items too, not just the feed details.
+ feed.onParseError = function (f) {
+ throw new Error("PARSE ERROR");
+ };
+ let parser = new FeedParser();
+ let items = parser.parseAsRSS1(feed, doc);
+
+ // Check some channel details.
+ Assert.equal(feed.title, "XML.com");
+ Assert.equal(feed.link, "http://xml.com/pub");
+
+ // Check the items (the titles and links at least!).
+ assertItemsEqual(items, [
+ {
+ url: "http://xml.com/pub/2000/08/09/xslt/xslt.html",
+ title: "Processing Inclusions with XSLT",
+ },
+ {
+ url: "http://xml.com/pub/2000/08/09/rdfdb/index.html",
+ title: "Putting RDF to Work",
+ },
+ ]);
+ }
+});
+
+// Test feed downloading.
+// Mainly checking that it doesn't crash and that the right feed parser is used.
+add_task(async function test_download() {
+ // Boilerplate
+ let account = FeedUtils.createRssAccount("test_feed_download");
+ let rootFolder = account.incomingServer.rootMsgFolder.QueryInterface(
+ Ci.nsIMsgLocalMailFolder
+ );
+
+ // load & parse example rss feed
+ // Feed object rejects anything other than http and https, so we're
+ // running a local http server for testing (see head_feeds.js for it).
+ let feedTests = [
+ {
+ url: "http://localhost:" + SERVER_PORT + "/rss_7_1.rdf",
+ feedType: "RSS_1.xRDF",
+ title: "XML.com",
+ expectedItems: 2,
+ },
+ {
+ // Has Japanese title with leading/trailing whitespace.
+ url: "http://localhost:" + SERVER_PORT + "/rss2_example.xml",
+ feedType: "RSS_2.0",
+ title: "本当に簡単なシンジケーションの例",
+ expectedItems: 1,
+ },
+ {
+ // Has two items with same link but different guid (Bug 1656090).
+ url: "http://localhost:" + SERVER_PORT + "/rss2_guid.xml",
+ feedType: "RSS_2.0",
+ title: "GUID test",
+ expectedItems: 4,
+ },
+ // TODO: examples for the other feed types!
+ ];
+
+ let n = 1;
+ for (let test of feedTests) {
+ let folder = rootFolder.createLocalSubfolder("feed" + n);
+ n++;
+ let feed = new Feed(test.url, folder);
+
+ let dl = new Promise(function (resolve, reject) {
+ let cb = {
+ downloaded(f, error, disable) {
+ if (error != FeedUtils.kNewsBlogSuccess) {
+ reject(
+ new Error(`download failed (url=${feed.url} error=${error})`)
+ );
+ return;
+ }
+ // Feed has downloaded - make sure the right type was detected.
+ Assert.equal(feed.mFeedType, test.feedType, "feed type matching");
+ Assert.equal(feed.title, test.title, "title matching");
+ // Make sure we're got the expected number of messages in the folder.
+ let cnt = [...folder.messages].length;
+ Assert.equal(cnt, test.expectedItems, "itemcount matching");
+
+ resolve();
+ },
+ onProgress(f, loaded, total, lengthComputable) {},
+ };
+
+ feed.download(true, cb);
+ });
+
+ // Wait for this feed to complete downloading.
+ await dl;
+ }
+});
diff --git a/comm/mailnews/extensions/newsblog/test/unit/test_rdfmigration.js b/comm/mailnews/extensions/newsblog/test/unit/test_rdfmigration.js
new file mode 100644
index 0000000000..a2bc2cf702
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/test_rdfmigration.js
@@ -0,0 +1,61 @@
+/* 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 { MailMigrator } = ChromeUtils.import(
+ "resource:///modules/MailMigrator.jsm"
+);
+
+/**
+ * Tests migration of old .rdf feed config files to the new .json files.
+ *
+ * @param {String} testDataDir - A directory containing legacy feeds.rdf and
+ * feeditems.rdf files, along with coressponding
+ * .json files containing the expected results
+ * of the migration.
+ * @returns {void}
+ */
+async function migrationTest(testDataDir) {
+ // Set up an RSS account/server.
+ let account = FeedUtils.createRssAccount("rss_migration_test");
+ let rootFolder = account.incomingServer.rootMsgFolder.QueryInterface(
+ Ci.nsIMsgLocalMailFolder
+ );
+ // Note, we don't create any folders to hold downloaded feed items,
+ // that's OK here, because we're only migrating the config files, not
+ // downloading feeds. The migration doesn't check destFolder existence.
+ let rootDir = rootFolder.filePath.path;
+
+ // Install legacy feeds.rdf/feeditems.rdf
+ for (let f of ["feeds.rdf", "feeditems.rdf"]) {
+ await IOUtils.copy(
+ PathUtils.join(testDataDir, f),
+ PathUtils.join(rootDir, f)
+ );
+ }
+
+ // Perform the migration
+ await MailMigrator._migrateRSSServer(account.incomingServer);
+
+ // Check actual results against expectations.
+ for (let f of ["feeds.json", "feeditems.json"]) {
+ let got = await IOUtils.readJSON(PathUtils.join(rootDir, f));
+ let expected = await IOUtils.readJSON(PathUtils.join(testDataDir, f));
+ Assert.deepEqual(got, expected, `match ${testDataDir}/${f}`);
+ }
+
+ // Delete the account and all it's files.
+ MailServices.accounts.removeAccount(account, true);
+}
+
+add_task(async function test_rdfmigration() {
+ let testDataDirs = [
+ "feeds-simple",
+ "feeds-empty",
+ "feeds-missing-timestamp",
+ "feeds-bad",
+ ];
+ for (let d of testDataDirs) {
+ await migrationTest(do_get_file("resources/" + d).path);
+ }
+});
diff --git a/comm/mailnews/extensions/newsblog/test/unit/xpcshell.ini b/comm/mailnews/extensions/newsblog/test/unit/xpcshell.ini
new file mode 100644
index 0000000000..3b7fc2869c
--- /dev/null
+++ b/comm/mailnews/extensions/newsblog/test/unit/xpcshell.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+head = head_feeds.js
+tail =
+support-files = resources/*
+
+[test_feedparser.js]
+[test_rdfmigration.js]
+