summaryrefslogtreecommitdiffstats
path: root/toolkit/components/reader/test/browser_readerMode.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/reader/test/browser_readerMode.js')
-rw-r--r--toolkit/components/reader/test/browser_readerMode.js394
1 files changed, 394 insertions, 0 deletions
diff --git a/toolkit/components/reader/test/browser_readerMode.js b/toolkit/components/reader/test/browser_readerMode.js
new file mode 100644
index 0000000000..9a1c11329b
--- /dev/null
+++ b/toolkit/components/reader/test/browser_readerMode.js
@@ -0,0 +1,394 @@
+/* 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/. */
+
+/**
+ * Test that the reader mode button appears and works properly on
+ * reader-able content.
+ */
+const TEST_PREFS = [["reader.parse-on-load.enabled", true]];
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+var readerButton = document.getElementById("reader-mode-button");
+
+ChromeUtils.defineESModuleGetters(this, {
+ PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs",
+});
+
+add_task(async function test_reader_button() {
+ registerCleanupFunction(function () {
+ // Reset test prefs.
+ TEST_PREFS.forEach(([name, value]) => {
+ Services.prefs.clearUserPref(name);
+ });
+ while (gBrowser.tabs.length > 1) {
+ gBrowser.removeCurrentTab();
+ }
+ });
+
+ // Set required test prefs.
+ TEST_PREFS.forEach(([name, value]) => {
+ Services.prefs.setBoolPref(name, value);
+ });
+
+ let tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser));
+ is_element_hidden(
+ readerButton,
+ "Reader mode button is not present on a new tab"
+ );
+ ok(
+ !UITour.isInfoOnTarget(window, "readerMode-urlBar"),
+ "Info panel shouldn't appear without the reader mode button"
+ );
+
+ // Point tab to a test page that is reader-able.
+ let url = TEST_PATH + "readerModeArticle.html";
+ // Set up favicon for testing.
+ let favicon =
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAA" +
+ "AAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";
+ info("Adding visit so we can add favicon");
+ await PlacesTestUtils.addVisits(new URL(url));
+ info("Adding favicon");
+ await PlacesTestUtils.addFavicons(new Map([[url, favicon]]));
+ info("Opening tab and waiting for reader mode button to show up");
+
+ await promiseTabLoadEvent(tab, url);
+ await TestUtils.waitForCondition(() => !readerButton.hidden);
+
+ is_element_visible(
+ readerButton,
+ "Reader mode button is present on a reader-able page"
+ );
+
+ // Switch page into reader mode.
+ let promiseTabLoad = promiseTabLoadEvent(tab);
+ readerButton.click();
+ await promiseTabLoad;
+
+ let readerUrl = gBrowser.selectedBrowser.currentURI.spec;
+ ok(
+ readerUrl.startsWith("about:reader"),
+ "about:reader loaded after clicking reader mode button"
+ );
+ is_element_visible(
+ readerButton,
+ "Reader mode button is present on about:reader"
+ );
+ let iconEl = tab.iconImage;
+ await TestUtils.waitForCondition(
+ () => iconEl.getBoundingClientRect().width != 0
+ );
+ is_element_visible(iconEl, "Favicon should be visible");
+ is(iconEl.src, favicon, "Correct favicon should be loaded");
+
+ is(gURLBar.untrimmedValue, url, "gURLBar value is about:reader URL");
+ is(gURLBar.value, url, "gURLBar is displaying original article URL");
+
+ // Check selected value for URL bar
+ await new Promise((resolve, reject) => {
+ waitForClipboard(
+ url,
+ function () {
+ gURLBar.focus();
+ gURLBar.select();
+ goDoCommand("cmd_copy");
+ },
+ resolve,
+ reject
+ );
+ });
+
+ info("Got correct URL when copying");
+
+ // Switch page back out of reader mode.
+ let promisePageShow = BrowserTestUtils.waitForContentEvent(
+ tab.linkedBrowser,
+ "pageshow",
+ false,
+ e => e.target.location.href != "about:blank"
+ );
+ readerButton.click();
+ await promisePageShow;
+ is(
+ gBrowser.selectedBrowser.currentURI.spec,
+ url,
+ "Back to the original page after clicking active reader mode button"
+ );
+ ok(
+ gBrowser.selectedBrowser.canGoForward,
+ "Moved one step back in the session history."
+ );
+
+ let nonReadableUrl = TEST_PATH + "readerModeNonArticle.html";
+
+ // Load a new tab that is NOT reader-able.
+ let newTab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser));
+ await promiseTabLoadEvent(newTab, nonReadableUrl);
+ await TestUtils.waitForCondition(() => readerButton.hidden);
+ is_element_hidden(
+ readerButton,
+ "Reader mode button is not present on a non-reader-able page"
+ );
+
+ // Switch back to the original tab to make sure reader mode button is still visible.
+ gBrowser.removeCurrentTab();
+ await TestUtils.waitForCondition(() => !readerButton.hidden);
+ is_element_visible(
+ readerButton,
+ "Reader mode button is present on a reader-able page"
+ );
+
+ // Load a new tab in reader mode that is NOT reader-able in the reader mode.
+ newTab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
+ let promiseAboutReaderError = BrowserTestUtils.waitForContentEvent(
+ newTab.linkedBrowser,
+ "AboutReaderContentError"
+ );
+ await promiseTabLoadEvent(newTab, "about:reader?url=" + nonReadableUrl);
+ await promiseAboutReaderError;
+ await TestUtils.waitForCondition(() => !readerButton.hidden);
+ is_element_visible(
+ readerButton,
+ "Reader mode button is present on about:reader even in error state"
+ );
+
+ // Switch page back out of reader mode.
+ promisePageShow = BrowserTestUtils.waitForContentEvent(
+ newTab.linkedBrowser,
+ "pageshow",
+ false,
+ e => e.target.location.href != "about:blank"
+ );
+ readerButton.click();
+ await promisePageShow;
+ is(
+ gBrowser.selectedBrowser.currentURI.spec,
+ nonReadableUrl,
+ "Back to the original non-reader-able page after clicking active reader mode button"
+ );
+ await TestUtils.waitForCondition(() => readerButton.hidden);
+ is_element_hidden(
+ readerButton,
+ "Reader mode button is not present on a non-reader-able page"
+ );
+});
+
+add_task(async function test_getOriginalUrl() {
+ let { ReaderMode } = ChromeUtils.importESModule(
+ "resource://gre/modules/ReaderMode.sys.mjs"
+ );
+ let url = "https://foo.com/article.html";
+
+ is(
+ ReaderMode.getOriginalUrl("about:reader?url=" + encodeURIComponent(url)),
+ url,
+ "Found original URL from encoded URL"
+ );
+ is(
+ ReaderMode.getOriginalUrl("about:reader?foobar"),
+ null,
+ "Did not find original URL from malformed reader URL"
+ );
+ is(
+ ReaderMode.getOriginalUrl(url),
+ null,
+ "Did not find original URL from non-reader URL"
+ );
+
+ let badUrl = "https://foo.com/?;$%^^";
+ is(
+ ReaderMode.getOriginalUrl("about:reader?url=" + encodeURIComponent(badUrl)),
+ badUrl,
+ "Found original URL from encoded malformed URL"
+ );
+ is(
+ ReaderMode.getOriginalUrl("about:reader?url=" + badUrl),
+ badUrl,
+ "Found original URL from non-encoded malformed URL"
+ );
+});
+
+add_task(async function test_reader_view_element_attribute_transform() {
+ registerCleanupFunction(function () {
+ while (gBrowser.tabs.length > 1) {
+ gBrowser.removeCurrentTab();
+ }
+ });
+
+ function observeAttribute(element, attributes, triggerFn, checkFn) {
+ return new Promise(resolve => {
+ let observer = new MutationObserver(mutations => {
+ for (let mu of mutations) {
+ if (element.getAttribute(mu.attributeName) !== mu.oldValue) {
+ if (checkFn()) {
+ resolve();
+ observer.disconnect();
+ return;
+ }
+ }
+ }
+ });
+
+ observer.observe(element, {
+ attributes: true,
+ attributeOldValue: true,
+ attributeFilter: Array.isArray(attributes) ? attributes : [attributes],
+ });
+
+ triggerFn();
+ });
+ }
+
+ let menuitem = document.getElementById("menu_readerModeItem");
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+ is(
+ menuitem.hidden,
+ true,
+ "menuitem element should have the hidden attribute"
+ );
+
+ info("Navigate a reader-able page");
+ function waitForNonBlankPage() {
+ return BrowserTestUtils.waitForContentEvent(
+ tab.linkedBrowser,
+ "pageshow",
+ false,
+ e => e.target.location.href != "about:blank"
+ );
+ }
+ let waitForPageshow = waitForNonBlankPage();
+ await observeAttribute(
+ menuitem,
+ "hidden",
+ () => {
+ let url = TEST_PATH + "readerModeArticle.html";
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, url);
+ },
+ () => !menuitem.hidden
+ );
+ is(
+ menuitem.hidden,
+ false,
+ "menuitem's hidden attribute should be false on a reader-able page"
+ );
+ await waitForPageshow;
+
+ info("Navigate a non-reader-able page");
+ waitForPageshow = waitForNonBlankPage();
+ await observeAttribute(
+ menuitem,
+ "hidden",
+ () => {
+ let url = TEST_PATH + "readerModeArticleHiddenNodes.html";
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, url);
+ },
+ () => menuitem.hidden
+ );
+ is(
+ menuitem.hidden,
+ true,
+ "menuitem's hidden attribute should be true on a non-reader-able page"
+ );
+ await waitForPageshow;
+
+ info("Navigate a reader-able page");
+ waitForPageshow = waitForNonBlankPage();
+ await observeAttribute(
+ menuitem,
+ "hidden",
+ () => {
+ let url = TEST_PATH + "readerModeArticle.html";
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, url);
+ },
+ () => !menuitem.hidden
+ );
+ is(
+ menuitem.hidden,
+ false,
+ "menuitem's hidden attribute should be false on a reader-able page"
+ );
+ await waitForPageshow;
+
+ info("Enter Reader Mode");
+ waitForPageshow = waitForNonBlankPage();
+ await observeAttribute(
+ readerButton,
+ "readeractive",
+ () => {
+ readerButton.click();
+ },
+ () => readerButton.getAttribute("readeractive") == "true"
+ );
+ is(
+ readerButton.getAttribute("readeractive"),
+ "true",
+ "readerButton's readeractive attribute should be true when entering reader mode"
+ );
+ await waitForPageshow;
+
+ info("Exit Reader Mode");
+ waitForPageshow = waitForNonBlankPage();
+ await observeAttribute(
+ readerButton,
+ ["readeractive", "hidden"],
+ () => {
+ readerButton.click();
+ },
+ () => {
+ info(
+ `active: ${readerButton.getAttribute("readeractive")}; hidden: ${
+ menuitem.hidden
+ }`
+ );
+ return !readerButton.getAttribute("readeractive") && !menuitem.hidden;
+ }
+ );
+ ok(
+ !readerButton.getAttribute("readeractive"),
+ "readerButton's readeractive attribute should be empty when reader mode is exited"
+ );
+ ok(!menuitem.hidden, "menuitem should not be hidden.");
+ await waitForPageshow;
+
+ info("Navigate a non-reader-able page");
+ waitForPageshow = waitForNonBlankPage();
+ await observeAttribute(
+ menuitem,
+ "hidden",
+ () => {
+ let url = TEST_PATH + "readerModeArticleHiddenNodes.html";
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, url);
+ },
+ () => menuitem.hidden
+ );
+ is(
+ menuitem.hidden,
+ true,
+ "menuitem's hidden attribute should be true on a non-reader-able page"
+ );
+ await waitForPageshow;
+});
+
+add_task(async function test_reader_mode_lang() {
+ let url = TEST_PATH + "readerModeArticle.html";
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, url);
+
+ await promiseTabLoadEvent(tab, url);
+ await TestUtils.waitForCondition(() => !readerButton.hidden);
+
+ // Switch page into reader mode.
+ let promiseTabLoad = promiseTabLoadEvent(tab);
+ readerButton.click();
+ await promiseTabLoad;
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
+ let container = content.document.querySelector(".container");
+ is(container.lang, "en");
+ });
+});