diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/components/passwordmgr/test/browser/browser_basicAuth_multiTab.js | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/toolkit/components/passwordmgr/test/browser/browser_basicAuth_multiTab.js b/toolkit/components/passwordmgr/test/browser/browser_basicAuth_multiTab.js new file mode 100644 index 0000000000..68f21d0ea4 --- /dev/null +++ b/toolkit/components/passwordmgr/test/browser/browser_basicAuth_multiTab.js @@ -0,0 +1,158 @@ +/* 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/. */ + +"use strict"; + +/** + * Tests that can show multiple auth prompts in different tabs and handle them + * correctly. + */ + +const ORIGIN1 = "https://example.com"; +const ORIGIN2 = "https://example.org"; + +const AUTH_PATH = + "/browser/toolkit/components/passwordmgr/test/browser/authenticate.sjs"; + +/** + * Opens a tab and navigates to the auth test path. + * @param {String} origin - Origin to open with test path. + * @param {Object} authOptions - Authentication options to pass to server and + * test for. + * @param {String} authOptions.user - Expected username. + * @param {String} authOptions.pass - Expected password. + * @param {String} authOptions.realm - Realm to return on auth request. + * @returns {Object} - An object containing passed origin and authOptions, + * opened tab, a promise which resolves once the tab loads, a promise which + * resolves once the prompt has been opened. + */ +async function openTabWithAuthPrompt(origin, authOptions) { + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "https://example.com" + ); + + let promptPromise = PromptTestUtils.waitForPrompt(tab.linkedBrowser, { + modalType: Services.prompt.MODAL_TYPE_TAB, + promptType: "promptUserAndPass", + }); + let url = new URL(origin + AUTH_PATH); + Object.entries(authOptions).forEach(([key, value]) => + url.searchParams.append(key, value) + ); + let loadPromise = BrowserTestUtils.browserLoaded( + tab.linkedBrowser, + false, + url.toString() + ); + info("Loading " + url.toString()); + BrowserTestUtils.loadURIString(tab.linkedBrowser, url.toString()); + return { origin, tab, authOptions, loadPromise, promptPromise }; +} + +/** + * Waits for tab to load and tests for expected auth state. + * @param {boolean} expectAuthed - true: auth success, false otherwise. + * @param {Object} tabInfo - Information about the tab as generated by + * openTabWithAuthPrompt. + */ +async function testTabAuthed(expectAuthed, { tab, loadPromise, authOptions }) { + // Wait for tab to load after auth. + await loadPromise; + Assert.ok(true, "Tab loads after auth"); + + // Fetch auth results from body (set by authenticate.sjs). + let { loginResult, user, pass } = await SpecialPowers.spawn( + tab.linkedBrowser, + [], + () => { + let result = {}; + result.loginResult = content.document.getElementById("ok").innerText; + result.user = content.document.getElementById("user").innerText; + result.pass = content.document.getElementById("pass").innerText; + return result; + } + ); + + Assert.equal( + loginResult == "PASS", + expectAuthed, + "Site has expected auth state" + ); + Assert.equal(user, expectAuthed ? authOptions.user : "", "Sent correct user"); + Assert.equal( + pass, + expectAuthed ? authOptions.pass : "", + "Sent correct password" + ); +} + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + // This test relies on tab auth prompts. + set: [["prompts.modalType.httpAuth", Services.prompt.MODAL_TYPE_TAB]], + }); +}); + +add_task(async function test() { + let tabA = await openTabWithAuthPrompt(ORIGIN1, { + user: "userA", + pass: "passA", + realm: "realmA", + }); + // Tab B and C share realm and credentials. + // However, since the auth happens in separate tabs we should get two prompts. + let tabB = await openTabWithAuthPrompt(ORIGIN2, { + user: "userB", + pass: "passB", + realm: "realmB", + }); + let tabC = await openTabWithAuthPrompt(ORIGIN2, { + user: "userB", + pass: "passB", + realm: "realmB", + }); + let tabs = [tabA, tabB, tabC]; + + info(`Opening ${tabs.length} tabs with auth prompts`); + let prompts = await Promise.all(tabs.map(tab => tab.promptPromise)); + + Assert.equal(prompts.length, tabs.length, "Should have one prompt per tab"); + + for (let i = 0; i < prompts.length; i++) { + let titleEl = prompts[i].ui.prompt.document.querySelector("#titleText"); + Assert.equal( + titleEl.textContent, + new URL(tabs[i].origin).host, + "Prompt matches the tab's host" + ); + } + + // Interact with the prompts. This is deliberately done out of order + // (no FIFO, LIFO). + let [promptA, promptB, promptC] = prompts; + + // Accept prompt B with correct login details. + await PromptTestUtils.handlePrompt(promptB, { + loginInput: tabB.authOptions.user, + passwordInput: tabB.authOptions.pass, + }); + await testTabAuthed(true, tabB); + + // Accept prompt A with correct login details + await PromptTestUtils.handlePrompt(promptA, { + loginInput: tabA.authOptions.user, + passwordInput: tabA.authOptions.pass, + }); + await testTabAuthed(true, tabA); + + // Cancel prompt C + await PromptTestUtils.handlePrompt(promptC, { + buttonNumClick: 1, + }); + await testTabAuthed(false, tabC); + + // Cleanup tabs + tabs.forEach(({ tab }) => BrowserTestUtils.removeTab(tab)); +}); |