/** * Helpers for password manager mochitest-plain tests. */ /* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */ const { LoginTestUtils } = SpecialPowers.Cu.import( "resource://testing-common/LoginTestUtils.jsm", {} ); // Setup LoginTestUtils to report assertions to the mochitest harness. LoginTestUtils.setAssertReporter( SpecialPowers.wrapCallback((err, message, stack) => { SimpleTest.record(!err, err ? err.message : message, null, stack); }) ); const { LoginHelper } = SpecialPowers.Cu.import( "resource://gre/modules/LoginHelper.jsm", {} ); const { Services } = SpecialPowers.Cu.import( "resource://gre/modules/Services.jsm", {} ); const { LENGTH: GENERATED_PASSWORD_LENGTH, REGEX: GENERATED_PASSWORD_REGEX, } = LoginTestUtils.generation; const LOGIN_FIELD_UTILS = LoginTestUtils.loginField; const TESTS_DIR = "/tests/toolkit/components/passwordmgr/test/"; /** * Returns the element with the specified |name| attribute. */ function $_(formNum, name) { var form = document.getElementById("form" + formNum); if (!form) { ok(false, "$_ couldn't find requested form " + formNum); return null; } var element = form.children.namedItem(name); if (!element) { ok(false, "$_ couldn't find requested element " + name); return null; } // Note that namedItem is a bit stupid, and will prefer an // |id| attribute over a |name| attribute when looking for // the element. Login Mananger happens to use .namedItem // anyway, but let's rigorously check it here anyway so // that we don't end up with tests that mistakenly pass. if (element.getAttribute("name") != name) { ok(false, "$_ got confused."); return null; } return element; } /** * Recreate a DOM tree using the outerHTML to ensure that any event listeners * and internal state for the elements are removed. */ function recreateTree(element) { // eslint-disable-next-line no-unsanitized/property, no-self-assign element.outerHTML = element.outerHTML; } /** * Check autocomplete popup results to ensure that expected * *labels* are being shown correctly as items in the popup. */ function checkAutoCompleteResults(actualValues, expectedValues, hostname, msg) { if (hostname === null) { checkArrayValues(actualValues, expectedValues, msg); return; } is( typeof hostname, "string", "checkAutoCompleteResults: hostname must be a string" ); isnot( actualValues.length, 0, "There should be items in the autocomplete popup: " + JSON.stringify(actualValues) ); // Check the footer first. let footerResult = actualValues[actualValues.length - 1]; is(footerResult, "View Saved Logins", "the footer text is shown correctly"); if (actualValues.length == 1) { is( expectedValues.length, 0, "If only the footer is present then there should be no expectedValues" ); info("Only the footer is present in the popup"); return; } // Check the rest of the autocomplete item values. checkArrayValues(actualValues.slice(0, -1), expectedValues, msg); } function getIframeBrowsingContext(window, iframeNumber = 0) { let bc = SpecialPowers.wrap(window).windowGlobalChild.browsingContext; return SpecialPowers.unwrap(bc.children[iframeNumber]); } /** * Set input values via setUserInput to emulate user input * and distinguish them from declarative or script-assigned values */ function setUserInputValues(parentNode, selectorValues) { for (let [selector, newValue] of Object.entries(selectorValues)) { info(`setUserInputValues, selector: ${selector}`); try { let field = SpecialPowers.wrap(parentNode.querySelector(selector)); if (field.value == newValue) { // we don't get an input event if the new value == the old field.value += "#"; } field.setUserInput(newValue); } catch (ex) { info(ex.message); info(ex.stack); ok( false, `setUserInputValues: Couldn't set value of field: ${ex.message}` ); } } } /** * @param {Function} [aFilterFn = undefined] Function to filter out irrelevant submissions. * @return {Promise} resolving when a relevant form submission was processed. */ function getSubmitMessage(aFilterFn = undefined) { info("getSubmitMessage"); return new Promise((resolve, reject) => { PWMGR_COMMON_PARENT.addMessageListener( "formSubmissionProcessed", function processed(...args) { if (aFilterFn && !aFilterFn(...args)) { // This submission isn't the one we're waiting for. return; } info("got formSubmissionProcessed"); PWMGR_COMMON_PARENT.removeMessageListener( "formSubmissionProcessed", processed ); resolve(args[0]); } ); }); } /** * Check for expected username/password in form. * @see `checkForm` below for a similar function. */ function checkLoginForm( usernameField, expectedUsername, passwordField, expectedPassword ) { let formID = usernameField.parentNode.id; is( usernameField.value, expectedUsername, "Checking " + formID + " username is: " + expectedUsername ); is( passwordField.value, expectedPassword, "Checking " + formID + " password is: " + expectedPassword ); } function checkLoginFormInChildFrame( iframeBC, usernameFieldId, expectedUsername, passwordFieldId, expectedPassword ) { return SpecialPowers.spawn( iframeBC, [usernameFieldId, expectedUsername, passwordFieldId, expectedPassword], ( usernameFieldIdF, expectedUsernameF, passwordFieldIdF, expectedPasswordF ) => { let usernameField = this.content.document.getElementById( usernameFieldIdF ); let passwordField = this.content.document.getElementById( passwordFieldIdF ); let formID = usernameField.parentNode.id; Assert.equal( usernameField.value, expectedUsernameF, "Checking " + formID + " username is: " + expectedUsernameF ); Assert.equal( passwordField.value, expectedPasswordF, "Checking " + formID + " password is: " + expectedPasswordF ); } ); } /** * Check a form for expected values. If an argument is null, a field's * expected value will be the default value. * *