mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-01 13:57:32 +00:00
Bug 1553209 - Merge the modal-input custom element into the login-item custom element. r=MattN,fluent-reviewers,flod
The separation didn't work out right and some work started getting duplicated between login-item and modal-input. Differential Revision: https://phabricator.services.mozilla.com/D34466 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
f81c678374
commit
d51be76b63
@ -38,13 +38,13 @@ login-item =
|
||||
.copy-username-button = Copy
|
||||
.delete-button = Delete
|
||||
.edit-button = Edit
|
||||
.modal-input-reveal-checkbox-hide = Hide password
|
||||
.modal-input-reveal-checkbox-show = Show password
|
||||
.new-login-title = Create New Login
|
||||
.open-site-button = Launch
|
||||
.origin-label = Website Address
|
||||
.origin-placeholder = https://www.example.com
|
||||
.password-hide-title = Hide password
|
||||
.password-label = Password
|
||||
.password-show-title = Show password
|
||||
.save-changes-button = Save Changes
|
||||
.time-created = Created: { DATETIME($timeCreated, day: "numeric", month: "long", year: "numeric") }
|
||||
.time-changed = Last modified: { DATETIME($timeChanged, day: "numeric", month: "long", year: "numeric") }
|
||||
|
@ -15,7 +15,6 @@
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/login-list.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/login-list-item.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/menu-button.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/modal-input.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/aboutLogins.js"></script>
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
|
||||
<link rel="stylesheet" href="chrome://browser/content/aboutlogins/aboutLogins.css">
|
||||
@ -48,13 +47,13 @@
|
||||
copied-username-button,
|
||||
delete-button,
|
||||
edit-button,
|
||||
modal-input-reveal-checkbox-hide,
|
||||
modal-input-reveal-checkbox-show,
|
||||
new-login-title,
|
||||
open-site-button,
|
||||
origin-label,
|
||||
origin-placeholder,
|
||||
password-hide-title,
|
||||
password-label,
|
||||
password-show-title,
|
||||
save-changes-button,
|
||||
time-created,
|
||||
time-changed,
|
||||
@ -97,25 +96,27 @@
|
||||
<button class="delete-button alternate-button"></button>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<label>
|
||||
<label class="detail-cell">
|
||||
<span class="origin-label field-label"></span>
|
||||
<span class="origin-saved-value"></span>
|
||||
<input type="url" name="origin" required/>
|
||||
</label>
|
||||
<button class="open-site-button"></button>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<label>
|
||||
<label class="detail-cell">
|
||||
<span class="username-label field-label"></span>
|
||||
<modal-input name="username"/>
|
||||
<input type="text" name="username"/>
|
||||
</label>
|
||||
<copy-to-clipboard-button class="copy-username-button"
|
||||
data-telemetry-object="username"></copy-to-clipboard-button>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<label>
|
||||
<label class="detail-cell">
|
||||
<span class="password-label field-label"></span>
|
||||
<modal-input type="password" name="password" required />
|
||||
<div class="reveal-password-wrapper">
|
||||
<input type="password" name="password" required/>
|
||||
<input type="checkbox" class="reveal-password-checkbox"/>
|
||||
</div>
|
||||
</label>
|
||||
<copy-to-clipboard-button class="copy-password-button"
|
||||
data-telemetry-object="password"></copy-to-clipboard-button>
|
||||
@ -143,14 +144,6 @@
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<template id="modal-input-template">
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
|
||||
<link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/modal-input.css">
|
||||
<span class="locked-value"></span>
|
||||
<input type="text" class="unlocked-value"/>
|
||||
<input type="checkbox" class="reveal-checkbox"/>
|
||||
</template>
|
||||
|
||||
<template id="copy-to-clipboard-button-template">
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
|
||||
<link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/copy-to-clipboard-button.css">
|
||||
|
@ -4,6 +4,10 @@
|
||||
|
||||
:host {
|
||||
padding: 18px;
|
||||
|
||||
--reveal-checkbox-opacity: .8;
|
||||
--reveal-checkbox-opacity-hover: .6;
|
||||
--reveal-checkbox-opacity-active: 1;
|
||||
}
|
||||
|
||||
:host([editing]) .edit-button,
|
||||
@ -12,12 +16,20 @@
|
||||
:host([isNewLogin]) copy-to-clipboard-button,
|
||||
:host([isNewLogin]) .open-site-button,
|
||||
:host([isNewLogin]) .meta-info,
|
||||
:host(:not([isNewLogin])) input[name="origin"],
|
||||
:host(:not([editing])) .save-changes-button,
|
||||
:host(:not([editing])) .cancel-button {
|
||||
:host(:not([editing])) .cancel-button,
|
||||
:host(:not([editing])) .save-changes-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:host(:not([editing])) input[type="password"],
|
||||
:host(:not([editing])) input[type="text"],
|
||||
:host(:not([editing])) input[type="url"] {
|
||||
all: unset;
|
||||
display: inline-block;
|
||||
width: -moz-available;
|
||||
background-color: transparent !important; /* override common.inc.css */
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--in-content-box-border-color);
|
||||
@ -53,12 +65,17 @@
|
||||
padding-inline-start: 32px; /* 8px on each side, and 16px for icon width */
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
.detail-row,
|
||||
.reveal-password-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.detail-row > label {
|
||||
.detail-cell {
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
@ -84,3 +101,37 @@
|
||||
/* Show a non-full length border but constrain the text to this width */
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.reveal-password-checkbox {
|
||||
/* !important is needed to override common.css styling for checkboxes */
|
||||
background-color: transparent !important;
|
||||
border-width: 0 !important;
|
||||
background-image: url("chrome://browser/content/aboutlogins/icons/show-password.svg") !important;
|
||||
margin-inline-start: 8px !important;
|
||||
cursor: pointer;
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor !important;
|
||||
opacity: var(--reveal-checkbox-opacity);
|
||||
}
|
||||
|
||||
.reveal-password-checkbox:hover {
|
||||
opacity: var(--reveal-checkbox-opacity-hover);
|
||||
}
|
||||
|
||||
.reveal-password-checkbox:hover:active {
|
||||
opacity: var(--reveal-checkbox-opacity-active);
|
||||
}
|
||||
|
||||
.reveal-password-checkbox:checked {
|
||||
background-image: url("chrome://browser/content/aboutlogins/icons/hide-password.svg") !important;
|
||||
}
|
||||
|
||||
@supports -moz-bool-pref("browser.in-content.dark-mode") {
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:host {
|
||||
--reveal-checkbox-opacity: .8;
|
||||
--reveal-checkbox-opacity-hover: 1;
|
||||
--reveal-checkbox-opacity-active: .6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,10 +39,13 @@ export default class LoginItem extends ReflectedFluentElement {
|
||||
let originInput = this.shadowRoot.querySelector("input[name='origin']");
|
||||
originInput.addEventListener("blur", this);
|
||||
|
||||
let revealCheckbox = this.shadowRoot.querySelector(".reveal-password-checkbox");
|
||||
revealCheckbox.addEventListener("click", this);
|
||||
|
||||
let copyUsernameButton = this.shadowRoot.querySelector(".copy-username-button");
|
||||
let copyPasswordButton = this.shadowRoot.querySelector(".copy-password-button");
|
||||
copyUsernameButton.relatedInput = this.shadowRoot.querySelector("modal-input[name='username']");
|
||||
copyPasswordButton.relatedInput = this.shadowRoot.querySelector("modal-input[name='password']");
|
||||
copyUsernameButton.relatedInput = this.shadowRoot.querySelector("input[name='username']");
|
||||
copyPasswordButton.relatedInput = this.shadowRoot.querySelector("input[name='password']");
|
||||
|
||||
this.render();
|
||||
}
|
||||
@ -56,13 +59,13 @@ export default class LoginItem extends ReflectedFluentElement {
|
||||
"copy-username-button",
|
||||
"delete-button",
|
||||
"edit-button",
|
||||
"modal-input-reveal-checkbox-hide",
|
||||
"modal-input-reveal-checkbox-show",
|
||||
"new-login-title",
|
||||
"open-site-button",
|
||||
"origin-label",
|
||||
"origin-placeholder",
|
||||
"password-hide-title",
|
||||
"password-label",
|
||||
"password-show-title",
|
||||
"save-changes-button",
|
||||
"time-created",
|
||||
"time-changed",
|
||||
@ -92,16 +95,6 @@ export default class LoginItem extends ReflectedFluentElement {
|
||||
copyUsernameButton.setAttribute(newAttrName, this.getAttribute(attrName));
|
||||
break;
|
||||
}
|
||||
case "modal-input-reveal-checkbox-hide": {
|
||||
this.shadowRoot.querySelector("modal-input[name='password']")
|
||||
.setAttribute("reveal-checkbox-hide", this.getAttribute(attrName));
|
||||
break;
|
||||
}
|
||||
case "modal-input-reveal-checkbox-show": {
|
||||
this.shadowRoot.querySelector("modal-input[name='password']")
|
||||
.setAttribute("reveal-checkbox-show", this.getAttribute(attrName));
|
||||
break;
|
||||
}
|
||||
case "new-login-title": {
|
||||
let title = this.shadowRoot.querySelector(".title");
|
||||
title.setAttribute(attrName, this.getAttribute(attrName));
|
||||
@ -115,8 +108,13 @@ export default class LoginItem extends ReflectedFluentElement {
|
||||
originInput.setAttribute("placeholder", this.getAttribute(attrName));
|
||||
break;
|
||||
}
|
||||
case "password-hide-title":
|
||||
case "password-show-title": {
|
||||
this.updatePasswordRevealState();
|
||||
break;
|
||||
}
|
||||
case "username-placeholder": {
|
||||
let usernameInput = this.shadowRoot.querySelector("modal-input[name='username']");
|
||||
let usernameInput = this.shadowRoot.querySelector("input[name='username']");
|
||||
usernameInput.setAttribute("placeholder", this.getAttribute(attrName));
|
||||
break;
|
||||
}
|
||||
@ -136,10 +134,10 @@ export default class LoginItem extends ReflectedFluentElement {
|
||||
|
||||
let title = this.shadowRoot.querySelector(".title");
|
||||
title.textContent = this._login.title || title.getAttribute("new-login-title");
|
||||
this.shadowRoot.querySelector(".origin-saved-value").textContent = this._login.origin || "";
|
||||
this.shadowRoot.querySelector("input[name='origin']").defaultValue = this._login.origin || "";
|
||||
this.shadowRoot.querySelector("modal-input[name='username']").setAttribute("value", this._login.username || "");
|
||||
this.shadowRoot.querySelector("modal-input[name='password']").setAttribute("value", this._login.password || "");
|
||||
this.shadowRoot.querySelector("input[name='username']").defaultValue = this._login.username || "";
|
||||
this.shadowRoot.querySelector("input[name='password']").defaultValue = this._login.password || "";
|
||||
this.updatePasswordRevealState();
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
@ -162,8 +160,14 @@ export default class LoginItem extends ReflectedFluentElement {
|
||||
}
|
||||
case "click": {
|
||||
if (event.target.classList.contains("cancel-button")) {
|
||||
this.toggleEditing();
|
||||
this.render();
|
||||
if (this._login.guid) {
|
||||
this.setLogin(this._login);
|
||||
} else {
|
||||
// TODO, should select the first login if it exists
|
||||
// or show the no-logins view otherwise
|
||||
this.toggleEditing();
|
||||
this.render();
|
||||
}
|
||||
|
||||
recordTelemetryEvent({
|
||||
object: this._login.guid ? "existing_login" : "new_login",
|
||||
@ -195,6 +199,14 @@ export default class LoginItem extends ReflectedFluentElement {
|
||||
recordTelemetryEvent({object: "existing_login", method: "open_site"});
|
||||
return;
|
||||
}
|
||||
if (event.target.classList.contains("reveal-password-checkbox")) {
|
||||
this.updatePasswordRevealState();
|
||||
|
||||
let revealCheckbox = this.shadowRoot.querySelector(".reveal-password-checkbox");
|
||||
let method = revealCheckbox.checked ? "show" : "hide";
|
||||
recordTelemetryEvent({object: "password", method});
|
||||
return;
|
||||
}
|
||||
if (event.target.classList.contains("save-changes-button")) {
|
||||
if (!this._isFormValid({reportErrors: true})) {
|
||||
return;
|
||||
@ -228,9 +240,22 @@ export default class LoginItem extends ReflectedFluentElement {
|
||||
let originInput =
|
||||
this.resetValidation(this.shadowRoot.querySelector("input[name='origin']"), login.origin);
|
||||
originInput.addEventListener("blur", this);
|
||||
let usernameInput =
|
||||
this.resetValidation(this.shadowRoot.querySelector("input[name='username']"), login.username);
|
||||
let passwordInput =
|
||||
this.resetValidation(this.shadowRoot.querySelector("input[name='password']"), login.password);
|
||||
|
||||
let copyUsernameButton = this.shadowRoot.querySelector(".copy-username-button");
|
||||
let copyPasswordButton = this.shadowRoot.querySelector(".copy-password-button");
|
||||
copyUsernameButton.relatedInput = usernameInput;
|
||||
copyPasswordButton.relatedInput = passwordInput;
|
||||
|
||||
this.toggleAttribute("isNewLogin", !login.guid);
|
||||
this.toggleEditing(!login.guid);
|
||||
|
||||
let revealCheckbox = this.shadowRoot.querySelector(".reveal-password-checkbox");
|
||||
revealCheckbox.checked = false;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
@ -272,27 +297,55 @@ export default class LoginItem extends ReflectedFluentElement {
|
||||
this.removeAttribute("isNewLogin");
|
||||
}
|
||||
|
||||
if (shouldEdit) {
|
||||
this.shadowRoot.querySelector("input[name='password']").style.removeProperty("width");
|
||||
} else {
|
||||
// Need to set a shorter width than -moz-available so the reveal checkbox
|
||||
// will still appear next to the password.
|
||||
this.shadowRoot.querySelector("input[name='password']").style.width =
|
||||
(this._login.password || "").length + "ch";
|
||||
}
|
||||
|
||||
this.shadowRoot.querySelector(".delete-button").disabled = this.hasAttribute("isNewLogin");
|
||||
this.shadowRoot.querySelector(".edit-button").disabled = shouldEdit;
|
||||
this.shadowRoot.querySelectorAll("modal-input")
|
||||
.forEach(el => el.toggleAttribute("editing", shouldEdit));
|
||||
this.shadowRoot.querySelector("input[name='origin']").readOnly = !this.hasAttribute("isNewLogin");
|
||||
this.shadowRoot.querySelector("input[name='username']").readOnly = !shouldEdit;
|
||||
this.shadowRoot.querySelector("input[name='password']").readOnly = !shouldEdit;
|
||||
this.toggleAttribute("editing", shouldEdit);
|
||||
}
|
||||
|
||||
resetValidation(formElement, value) {
|
||||
let wasRequired = formElement.hasAttribute("required");
|
||||
let wasRequired = formElement.required;
|
||||
let newFormElement = document.createElement(formElement.localName);
|
||||
newFormElement.defaultValue = value || "";
|
||||
if (value) {
|
||||
newFormElement.defaultValue = value;
|
||||
}
|
||||
newFormElement.className = formElement.className;
|
||||
newFormElement.placeholder = formElement.placeholder;
|
||||
newFormElement.setAttribute("name", formElement.getAttribute("name"));
|
||||
newFormElement.setAttribute("type", formElement.getAttribute("type"));
|
||||
if (wasRequired) {
|
||||
newFormElement.setAttribute("required", "");
|
||||
newFormElement.required = true;
|
||||
}
|
||||
formElement.replaceWith(newFormElement);
|
||||
return newFormElement;
|
||||
}
|
||||
|
||||
updatePasswordRevealState() {
|
||||
let revealCheckbox = this.shadowRoot.querySelector(".reveal-password-checkbox");
|
||||
let labelAttr = revealCheckbox.checked ? "password-show-title"
|
||||
: "password-hide-title";
|
||||
revealCheckbox.setAttribute("aria-label", this.getAttribute(labelAttr));
|
||||
revealCheckbox.setAttribute("title", this.getAttribute(labelAttr));
|
||||
|
||||
let passwordInput = this.shadowRoot.querySelector("input[name='password']");
|
||||
if (revealCheckbox.checked) {
|
||||
passwordInput.setAttribute("type", "text");
|
||||
return;
|
||||
}
|
||||
passwordInput.setAttribute("type", "password");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the edit/new-login form has valid values present for their
|
||||
* respective required fields.
|
||||
@ -301,7 +354,7 @@ export default class LoginItem extends ReflectedFluentElement {
|
||||
* to the user.
|
||||
*/
|
||||
_isFormValid({reportErrors} = {}) {
|
||||
let fields = [this.shadowRoot.querySelector("modal-input[name='password']")];
|
||||
let fields = [this.shadowRoot.querySelector("input[name='password']")];
|
||||
if (this.hasAttribute("isNewLogin")) {
|
||||
fields.push(this.shadowRoot.querySelector("input[name='origin']"));
|
||||
}
|
||||
@ -320,10 +373,9 @@ export default class LoginItem extends ReflectedFluentElement {
|
||||
|
||||
_loginFromForm() {
|
||||
return {
|
||||
username: this.shadowRoot.querySelector("modal-input[name='username']").value.trim(),
|
||||
password: this.shadowRoot.querySelector("modal-input[name='password']").value.trim(),
|
||||
origin: this.hasAttribute("isNewLogin") ? this.shadowRoot.querySelector("input[name='origin']").value.trim()
|
||||
: this.shadowRoot.querySelector(".origin-saved-value").textContent,
|
||||
username: this.shadowRoot.querySelector("input[name='username']").value.trim(),
|
||||
password: this.shadowRoot.querySelector("input[name='password']").value.trim(),
|
||||
origin: this.shadowRoot.querySelector("input[name='origin']").value.trim(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,55 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
:host {
|
||||
--reveal-button-opacity: .8;
|
||||
--reveal-button-opacity-hover: .6;
|
||||
--reveal-button-opacity-active: 1;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:host([editing]) .locked-value,
|
||||
:host(:not([editing])) .unlocked-value {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:host(:not([type="password"])) .reveal-checkbox {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.reveal-checkbox {
|
||||
/* !important is needed to override common.css styling for checkboxes */
|
||||
background-color: transparent !important;
|
||||
border-width: 0 !important;
|
||||
background-image: url("chrome://browser/content/aboutlogins/icons/show-password.svg") !important;
|
||||
margin-inline-start: 8px !important;
|
||||
cursor: pointer;
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor !important;
|
||||
opacity: var(--reveal-button-opacity);
|
||||
}
|
||||
|
||||
.reveal-checkbox:hover {
|
||||
opacity: var(--reveal-button-opacity-hover);
|
||||
}
|
||||
|
||||
.reveal-checkbox:hover:active {
|
||||
opacity: var(--reveal-button-opacity-active);
|
||||
}
|
||||
|
||||
.reveal-checkbox:checked {
|
||||
background-image: url("chrome://browser/content/aboutlogins/icons/hide-password.svg") !important;
|
||||
}
|
||||
|
||||
@supports -moz-bool-pref("browser.in-content.dark-mode") {
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:host {
|
||||
--reveal-button-opacity: .8;
|
||||
--reveal-button-opacity-hover: 1;
|
||||
--reveal-button-opacity-active: .6;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,187 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
import {recordTelemetryEvent} from "chrome://browser/content/aboutlogins/aboutLoginsUtils.js";
|
||||
import ReflectedFluentElement from "chrome://browser/content/aboutlogins/components/reflected-fluent-element.js";
|
||||
|
||||
export default class ModalInput extends ReflectedFluentElement {
|
||||
static get LOCKED_PASSWORD_DISPLAY() {
|
||||
return "••••••••";
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (this.shadowRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
let modalInputTemplate = document.querySelector("#modal-input-template");
|
||||
this.attachShadow({mode: "open"})
|
||||
.appendChild(modalInputTemplate.content.cloneNode(true));
|
||||
|
||||
if (this.hasAttribute("value")) {
|
||||
this.value = this.getAttribute("value");
|
||||
}
|
||||
|
||||
let unlockedValue = this.shadowRoot.querySelector(".unlocked-value");
|
||||
if (this.getAttribute("type") == "password") {
|
||||
unlockedValue.setAttribute("type", "password");
|
||||
}
|
||||
|
||||
if (this.getAttribute("placeholder")) {
|
||||
unlockedValue.setAttribute("placeholder", this.getAttribute("placeholder"));
|
||||
}
|
||||
|
||||
if (this.hasAttribute("required")) {
|
||||
unlockedValue.setAttribute("required", "");
|
||||
}
|
||||
|
||||
this.shadowRoot.querySelector(".reveal-checkbox").addEventListener("click", this);
|
||||
}
|
||||
|
||||
static get reflectedFluentIDs() {
|
||||
return ["reveal-checkbox-hide", "reveal-checkbox-show"];
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
return [
|
||||
"editing",
|
||||
"placeholder",
|
||||
"required",
|
||||
"type",
|
||||
"value",
|
||||
].concat(ModalInput.reflectedFluentIDs);
|
||||
}
|
||||
|
||||
handleSpecialCaseFluentString(attrName) {
|
||||
if (attrName != "reveal-checkbox-hide" &&
|
||||
attrName != "reveal-checkbox-show") {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.updateRevealCheckboxTitle();
|
||||
return true;
|
||||
}
|
||||
|
||||
attributeChangedCallback(attr, oldValue, newValue) {
|
||||
super.attributeChangedCallback(attr, oldValue, newValue);
|
||||
|
||||
if (!this.shadowRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
let lockedValue = this.shadowRoot.querySelector(".locked-value");
|
||||
let unlockedValue = this.shadowRoot.querySelector(".unlocked-value");
|
||||
|
||||
switch (attr) {
|
||||
case "editing": {
|
||||
let isEditing = newValue !== null;
|
||||
if (!isEditing) {
|
||||
this.setAttribute("value", unlockedValue.defaultValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "placeholder": {
|
||||
unlockedValue.setAttribute("placeholder", newValue);
|
||||
break;
|
||||
}
|
||||
case "required": {
|
||||
unlockedValue.toggleAttribute("required", this.hasAttribute("required"));
|
||||
break;
|
||||
}
|
||||
case "type": {
|
||||
if (newValue == "password") {
|
||||
lockedValue.textContent = this.constructor.LOCKED_PASSWORD_DISPLAY;
|
||||
unlockedValue.setAttribute("type", "password");
|
||||
} else {
|
||||
lockedValue.textContent = this.getAttribute("value");
|
||||
unlockedValue.setAttribute("type", "text");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "value": {
|
||||
this.value = newValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "click": {
|
||||
let revealCheckbox = event.target;
|
||||
let lockedValue = this.shadowRoot.querySelector(".locked-value");
|
||||
let unlockedValue = this.shadowRoot.querySelector(".unlocked-value");
|
||||
if (revealCheckbox.checked) {
|
||||
lockedValue.textContent = this.value;
|
||||
unlockedValue.setAttribute("type", "text");
|
||||
|
||||
recordTelemetryEvent({object: "password", method: "show"});
|
||||
} else {
|
||||
lockedValue.textContent = this.constructor.LOCKED_PASSWORD_DISPLAY;
|
||||
unlockedValue.setAttribute("type", "password");
|
||||
|
||||
recordTelemetryEvent({object: "password", method: "hide"});
|
||||
}
|
||||
this.updateRevealCheckboxTitle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.hasAttribute("editing") ? this.shadowRoot.querySelector(".unlocked-value").value.trim()
|
||||
: this.getAttribute("value") || "";
|
||||
}
|
||||
|
||||
set value(val) {
|
||||
if (this.getAttribute("value") != val) {
|
||||
this.setAttribute("value", val);
|
||||
return;
|
||||
}
|
||||
let unlockedValue = this.shadowRoot.querySelector(".unlocked-value");
|
||||
let wasRequired = unlockedValue.hasAttribute("required");
|
||||
let newUnlockedValue = document.createElement(unlockedValue.localName);
|
||||
newUnlockedValue.defaultValue = val || "";
|
||||
newUnlockedValue.className = unlockedValue.className;
|
||||
newUnlockedValue.setAttribute("type", unlockedValue.getAttribute("type"));
|
||||
if (wasRequired) {
|
||||
newUnlockedValue.setAttribute("required", "");
|
||||
}
|
||||
unlockedValue.replaceWith(newUnlockedValue);
|
||||
unlockedValue = newUnlockedValue;
|
||||
let lockedValue = this.shadowRoot.querySelector(".locked-value");
|
||||
if (this.getAttribute("type") == "password" && val && val.length) {
|
||||
lockedValue.textContent = this.constructor.LOCKED_PASSWORD_DISPLAY;
|
||||
} else {
|
||||
lockedValue.textContent = val || "";
|
||||
}
|
||||
let revealCheckbox = this.shadowRoot.querySelector(".reveal-checkbox");
|
||||
revealCheckbox.checked = false;
|
||||
}
|
||||
|
||||
checkValidity() {
|
||||
if (!this.hasAttribute("required")) {
|
||||
return true;
|
||||
}
|
||||
let unlockedValue = this.shadowRoot.querySelector(".unlocked-value");
|
||||
return unlockedValue.checkValidity();
|
||||
}
|
||||
|
||||
reportValidity() {
|
||||
if (!this.hasAttribute("required")) {
|
||||
return true;
|
||||
}
|
||||
let unlockedValue = this.shadowRoot.querySelector(".unlocked-value");
|
||||
return unlockedValue.reportValidity();
|
||||
}
|
||||
|
||||
updateRevealCheckboxTitle() {
|
||||
let revealCheckbox = this.shadowRoot.querySelector(".reveal-checkbox");
|
||||
let labelAttr = revealCheckbox.checked ? "reveal-checkbox-hide"
|
||||
: "reveal-checkbox-show";
|
||||
revealCheckbox.setAttribute("aria-label", this.getAttribute(labelAttr));
|
||||
revealCheckbox.setAttribute("title", this.getAttribute(labelAttr));
|
||||
}
|
||||
}
|
||||
customElements.define("modal-input", ModalInput);
|
@ -15,8 +15,6 @@ browser.jar:
|
||||
content/browser/aboutlogins/components/login-list-item.js (content/components/login-list-item.js)
|
||||
content/browser/aboutlogins/components/menu-button.css (content/components/menu-button.css)
|
||||
content/browser/aboutlogins/components/menu-button.js (content/components/menu-button.js)
|
||||
content/browser/aboutlogins/components/modal-input.css (content/components/modal-input.css)
|
||||
content/browser/aboutlogins/components/modal-input.js (content/components/modal-input.js)
|
||||
content/browser/aboutlogins/components/reflected-fluent-element.js (content/components/reflected-fluent-element.js)
|
||||
content/browser/aboutlogins/icons/delete.svg (content/icons/delete.svg)
|
||||
content/browser/aboutlogins/icons/edit.svg (content/icons/edit.svg)
|
||||
|
@ -35,6 +35,7 @@ add_task(async function test() {
|
||||
expectedValue: testCase[0],
|
||||
copyButtonSelector: testCase[1],
|
||||
};
|
||||
info("waiting for " + testObj.expectedValue + " to be placed on clipboard");
|
||||
await SimpleTest.promiseClipboardChange(testObj.expectedValue, async () => {
|
||||
await ContentTask.spawn(browser, testObj, async function(aTestObj) {
|
||||
let loginItem = content.document.querySelector("login-item");
|
||||
@ -44,7 +45,7 @@ add_task(async function test() {
|
||||
innerButton.click();
|
||||
});
|
||||
});
|
||||
ok(true, "Username is on clipboard now");
|
||||
ok(true, testObj.expectedValue + " is on clipboard now");
|
||||
|
||||
await ContentTask.spawn(browser, testObj, async function(aTestObj) {
|
||||
let loginItem = content.document.querySelector("login-item");
|
||||
|
@ -33,10 +33,8 @@ add_task(async function test_create_login() {
|
||||
let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
|
||||
|
||||
let originInput = loginItem.shadowRoot.querySelector("input[name='origin']");
|
||||
let usernameInput = loginItem.shadowRoot.querySelector("modal-input[name='username']")
|
||||
.shadowRoot.querySelector(".unlocked-value");
|
||||
let passwordInput = loginItem.shadowRoot.querySelector("modal-input[name='password']")
|
||||
.shadowRoot.querySelector(".unlocked-value");
|
||||
let usernameInput = loginItem.shadowRoot.querySelector("input[name='username']");
|
||||
let passwordInput = loginItem.shadowRoot.querySelector("input[name='password']");
|
||||
|
||||
originInput.value = aOriginTuple[0];
|
||||
usernameInput.value = "testuser1";
|
||||
@ -74,10 +72,8 @@ add_task(async function test_create_login() {
|
||||
let editButton = loginItem.shadowRoot.querySelector(".edit-button");
|
||||
editButton.click();
|
||||
|
||||
let usernameInput = loginItem.shadowRoot.querySelector("modal-input[name='username']")
|
||||
.shadowRoot.querySelector(".unlocked-value");
|
||||
let passwordInput = loginItem.shadowRoot.querySelector("modal-input[name='password']")
|
||||
.shadowRoot.querySelector(".unlocked-value");
|
||||
let usernameInput = loginItem.shadowRoot.querySelector("input[name='username']");
|
||||
let passwordInput = loginItem.shadowRoot.querySelector("input[name='password']");
|
||||
usernameInput.value = "testuser2";
|
||||
passwordInput.value = "testpass2";
|
||||
|
||||
|
@ -44,8 +44,8 @@ add_task(async function test_login_item() {
|
||||
}, "Waiting for login item to get populated");
|
||||
ok(loginItemPopulated, "The login item should get populated");
|
||||
|
||||
let usernameInput = loginItem.shadowRoot.querySelector("modal-input[name='username']");
|
||||
let passwordInput = loginItem.shadowRoot.querySelector("modal-input[name='password']");
|
||||
let usernameInput = loginItem.shadowRoot.querySelector("input[name='username']");
|
||||
let passwordInput = loginItem.shadowRoot.querySelector("input[name='password']");
|
||||
|
||||
let editButton = loginItem.shadowRoot.querySelector(".edit-button");
|
||||
editButton.click();
|
||||
@ -56,7 +56,8 @@ add_task(async function test_login_item() {
|
||||
|
||||
let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button");
|
||||
cancelButton.click();
|
||||
await Promise.resolve();
|
||||
usernameInput = loginItem.shadowRoot.querySelector("input[name='username']");
|
||||
passwordInput = loginItem.shadowRoot.querySelector("input[name='password']");
|
||||
is(usernameInput.value, login.username, "Username change should be reverted");
|
||||
is(passwordInput.value, login.password, "Password change should be reverted");
|
||||
|
||||
@ -71,6 +72,8 @@ add_task(async function test_login_item() {
|
||||
let saveChangesButton = loginItem.shadowRoot.querySelector(".save-changes-button");
|
||||
saveChangesButton.click();
|
||||
|
||||
usernameInput = loginItem.shadowRoot.querySelector("input[name='username']");
|
||||
passwordInput = loginItem.shadowRoot.querySelector("input[name='password']");
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
loginListItem = Cu.waiveXrays(loginList.shadowRoot.querySelector("login-list-item"));
|
||||
return loginListItem._login.username == usernameInput.value &&
|
||||
|
@ -16,5 +16,4 @@ support-files =
|
||||
[test_login_item.html]
|
||||
[test_login_list.html]
|
||||
[test_menu_button.html]
|
||||
[test_modal_input.html]
|
||||
[test_reflected_fluent_element.html]
|
||||
|
@ -9,7 +9,6 @@ Test the login-item component
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="module" src="login-item.js"></script>
|
||||
<script type="module" src="modal-input.js"></script>
|
||||
<script src="aboutlogins_common.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
|
||||
@ -64,9 +63,9 @@ add_task(async function setup() {
|
||||
|
||||
add_task(async function test_empty_item() {
|
||||
ok(gLoginItem, "loginItem exists");
|
||||
is(gLoginItem.shadowRoot.querySelector(".origin-saved-value").textContent, "", "origin should be blank");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='username']").value, "", "username should be blank");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='password']").value, "", "password should be blank");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='origin']").value, "", "origin should be blank");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, "", "username should be blank");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, "", "password should be blank");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, "", "time-created should be blank when undefined");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, "", "time-changed should be blank when undefined");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, "", "time-used should be blank when undefined");
|
||||
@ -78,13 +77,9 @@ add_task(async function test_set_login() {
|
||||
|
||||
ok(!gLoginItem.hasAttribute("editing"), "loginItem should not be in 'edit' mode");
|
||||
ok(!gLoginItem.hasAttribute("isNewLogin"), "loginItem should not be in 'isNewLogin' mode");
|
||||
let savedOrigin = gLoginItem.shadowRoot.querySelector(".origin-saved-value");
|
||||
is(getComputedStyle(savedOrigin).display, "inline", ".origin-saved-value should be visible for non-editing existing logins");
|
||||
let originInput = gLoginItem.shadowRoot.querySelector("input[name='origin']");
|
||||
is(getComputedStyle(originInput).display, "none", "input[name='origin'] should be hidden for non-editing existing logins");
|
||||
is(savedOrigin.textContent, TEST_LOGIN_1.origin, "origin should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='username']").value, TEST_LOGIN_1.username, "username should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='password']").value, TEST_LOGIN_1.password, "password should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='origin']").value, TEST_LOGIN_1.origin, "origin should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, TEST_LOGIN_1.password, "password should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, TEST_LOGIN_1.timeCreated, "time-created should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, TEST_LOGIN_1.timePasswordChanged, "time-changed should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, TEST_LOGIN_1.timeLastUsed, "time-used should be populated");
|
||||
@ -98,21 +93,17 @@ add_task(async function test_edit_login() {
|
||||
ok(gLoginItem.hasAttribute("editing"), "loginItem should be in 'edit' mode");
|
||||
ok(isHidden(gLoginItem.shadowRoot.querySelector(".edit-button")), "edit button should be hidden in 'edit' mode");
|
||||
ok(!gLoginItem.hasAttribute("isNewLogin"), "loginItem should not be in 'isNewLogin' mode");
|
||||
let savedOrigin = gLoginItem.shadowRoot.querySelector(".origin-saved-value");
|
||||
is(getComputedStyle(savedOrigin).display, "inline", ".origin-saved-value should be visible for editing existing logins");
|
||||
let deleteButton = gLoginItem.shadowRoot.querySelector(".delete-button");
|
||||
ok(!deleteButton.disabled, "Delete button should be enabled when editing a login");
|
||||
let originInput = gLoginItem.shadowRoot.querySelector("input[name='origin']");
|
||||
is(getComputedStyle(originInput).display, "none", "input[name='origin'] should be hidden for editing existing logins");
|
||||
is(savedOrigin.textContent, TEST_LOGIN_1.origin, "origin should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='username']").value, TEST_LOGIN_1.username, "username should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='password']").value, TEST_LOGIN_1.password, "password should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='origin']").value, TEST_LOGIN_1.origin, "origin should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, TEST_LOGIN_1.password, "password should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, TEST_LOGIN_1.timeCreated, "time-created should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, TEST_LOGIN_1.timePasswordChanged, "time-changed should be populated");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, TEST_LOGIN_1.timeLastUsed, "time-used should be populated");
|
||||
|
||||
gLoginItem.shadowRoot.querySelector("modal-input[name='username']").value = "newUsername";
|
||||
gLoginItem.shadowRoot.querySelector("modal-input[name='password']").value = "newPassword";
|
||||
gLoginItem.shadowRoot.querySelector("input[name='username']").value = "newUsername";
|
||||
gLoginItem.shadowRoot.querySelector("input[name='password']").value = "newPassword";
|
||||
|
||||
let updateEventDispatched = false;
|
||||
document.addEventListener("AboutLoginsUpdateLogin", event => {
|
||||
@ -145,8 +136,7 @@ add_task(async function test_edit_login_cancel() {
|
||||
|
||||
add_task(async function test_reveal_password_change_selected_login() {
|
||||
gLoginItem.setLogin(TEST_LOGIN_1);
|
||||
let passwordInput = gLoginItem.shadowRoot.querySelector('modal-input[name="password"]');
|
||||
let revealCheckbox = passwordInput.shadowRoot.querySelector(".reveal-checkbox");
|
||||
let revealCheckbox = gLoginItem.shadowRoot.querySelector(".reveal-password-checkbox");
|
||||
|
||||
ok(!revealCheckbox.checked, "reveal-checkbox should not be checked by default");
|
||||
revealCheckbox.click();
|
||||
@ -163,14 +153,11 @@ add_task(async function test_set_login_empty() {
|
||||
ok(gLoginItem.hasAttribute("editing"), "loginItem should be in 'edit' mode");
|
||||
ok(isHidden(gLoginItem.shadowRoot.querySelector(".edit-button")), "edit button should be hidden in 'edit' mode");
|
||||
ok(gLoginItem.hasAttribute("isNewLogin"), "loginItem should be in 'isNewLogin' mode");
|
||||
let savedOrigin = gLoginItem.shadowRoot.querySelector(".origin-saved-value");
|
||||
is(getComputedStyle(savedOrigin).display, "none", ".origin-saved-value should be hidden for new logins");
|
||||
let originInput = gLoginItem.shadowRoot.querySelector("input[name='origin']");
|
||||
let deleteButton = gLoginItem.shadowRoot.querySelector(".delete-button");
|
||||
ok(deleteButton.disabled, "Delete button should be disabled when creating a login");
|
||||
is(getComputedStyle(originInput).display, "inline-block", "input[name='origin'] should be visible for new logins");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='username']").value, "", "username should be empty");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='password']").value, "", "password should be empty");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='origin']").value, "", "origin should be empty");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, "", "username should be empty");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, "", "password should be empty");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, "", "time-created should be blank when undefined");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, "", "time-changed should be blank when undefined");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, "", "time-used should be blank when undefined");
|
||||
@ -181,6 +168,7 @@ add_task(async function test_set_login_empty() {
|
||||
}, {once: true});
|
||||
gLoginItem.shadowRoot.querySelector(".save-changes-button").click();
|
||||
ok(!createEventDispatched, "Clicking the .save-changes-button shouldn't dispatch the event when fields are invalid");
|
||||
let originInput = gLoginItem.shadowRoot.querySelector("input[name='origin']");
|
||||
ok(originInput.matches(":invalid"), "origin value is required");
|
||||
is(originInput.value, "", "origin input should be blank at start");
|
||||
|
||||
@ -200,8 +188,8 @@ add_task(async function test_set_login_empty() {
|
||||
synthesizeKey("VK_TAB", { shiftKey: true });
|
||||
}
|
||||
|
||||
gLoginItem.shadowRoot.querySelector("modal-input[name='username']").value = "user1";
|
||||
gLoginItem.shadowRoot.querySelector("modal-input[name='password']").value = "pass1";
|
||||
gLoginItem.shadowRoot.querySelector("input[name='username']").value = "user1";
|
||||
gLoginItem.shadowRoot.querySelector("input[name='password']").value = "pass1";
|
||||
|
||||
document.addEventListener("AboutLoginsCreateLogin", event => {
|
||||
is(event.detail.guid, undefined, "event should not include guid");
|
||||
@ -220,9 +208,9 @@ add_task(async function test_different_login_modified() {
|
||||
gLoginItem.loginModified(otherLogin);
|
||||
await asyncElementRendered();
|
||||
|
||||
is(gLoginItem.shadowRoot.querySelector(".origin-saved-value").textContent, TEST_LOGIN_1.origin, "origin should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='username']").value, TEST_LOGIN_1.username, "username should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='password']").value, TEST_LOGIN_1.password, "password should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='origin']").value, TEST_LOGIN_1.origin, "origin should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, TEST_LOGIN_1.password, "password should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, TEST_LOGIN_1.timeCreated, "time-created should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, TEST_LOGIN_1.timePasswordChanged, "time-changed should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, TEST_LOGIN_1.timeLastUsed, "time-used should be unchanged");
|
||||
@ -234,9 +222,9 @@ add_task(async function test_different_login_removed() {
|
||||
gLoginItem.loginRemoved(otherLogin);
|
||||
await asyncElementRendered();
|
||||
|
||||
is(gLoginItem.shadowRoot.querySelector(".origin-saved-value").textContent, TEST_LOGIN_1.origin, "origin should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='username']").value, TEST_LOGIN_1.username, "username should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='password']").value, TEST_LOGIN_1.password, "password should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='origin']").value, TEST_LOGIN_1.origin, "origin should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, TEST_LOGIN_1.password, "password should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, TEST_LOGIN_1.timeCreated, "time-created should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, TEST_LOGIN_1.timePasswordChanged, "time-changed should be unchanged");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, TEST_LOGIN_1.timeLastUsed, "time-used should be unchanged");
|
||||
@ -248,9 +236,9 @@ add_task(async function test_login_modified() {
|
||||
gLoginItem.loginModified(modifiedLogin);
|
||||
await asyncElementRendered();
|
||||
|
||||
is(gLoginItem.shadowRoot.querySelector(".origin-saved-value").textContent, modifiedLogin.origin, "origin should be updated");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='username']").value, modifiedLogin.username, "username should be updated");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='password']").value, modifiedLogin.password, "password should be updated");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='origin']").value, modifiedLogin.origin, "origin should be updated");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, modifiedLogin.username, "username should be updated");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, modifiedLogin.password, "password should be updated");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, modifiedLogin.timeCreated, "time-created should be updated");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, modifiedLogin.timePasswordChanged, "time-changed should be updated");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, modifiedLogin.timeLastUsed, "time-used should be updated");
|
||||
@ -261,9 +249,9 @@ add_task(async function test_login_removed() {
|
||||
gLoginItem.loginRemoved(TEST_LOGIN_1);
|
||||
await asyncElementRendered();
|
||||
|
||||
is(gLoginItem.shadowRoot.querySelector(".origin-saved-value").textContent, "", "origin should be cleared");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='username']").value, "", "username should be cleared");
|
||||
is(gLoginItem.shadowRoot.querySelector("modal-input[name='password']").value, "", "password should be cleared");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='origin']").value, "", "origin should be cleared");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, "", "username should be cleared");
|
||||
is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, "", "password should be cleared");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, "", "time-created should be blank when undefined");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, "", "time-changed should be blank when undefined");
|
||||
is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, "", "time-used should be blank when undefined");
|
||||
|
@ -1,100 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test the modal-input component
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the modal-input component</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="module" src="modal-input.js"></script>
|
||||
<script src="aboutlogins_common.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display">
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
<iframe id="templateFrame" src="aboutLogins.html"
|
||||
sandbox="allow-same-origin"></iframe>
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script>
|
||||
/** Test the modal-input component **/
|
||||
|
||||
let gModalInput;
|
||||
const TEST_INPUT_VALUE = "fakeValue";
|
||||
add_task(async function setup() {
|
||||
let templateFrame = document.getElementById("templateFrame");
|
||||
let displayEl = document.getElementById("display");
|
||||
importDependencies(templateFrame, displayEl);
|
||||
|
||||
gModalInput = document.createElement("modal-input");
|
||||
gModalInput.setAttribute("value", TEST_INPUT_VALUE);
|
||||
displayEl.appendChild(gModalInput);
|
||||
});
|
||||
|
||||
add_task(async function test_initial_state() {
|
||||
ok(gModalInput, "modalInput exists");
|
||||
is(gModalInput.shadowRoot.querySelector(".locked-value").textContent, TEST_INPUT_VALUE, "Values are set initially");
|
||||
is(gModalInput.shadowRoot.querySelector(".unlocked-value").defaultValue, TEST_INPUT_VALUE, "Values are set initially");
|
||||
is(getComputedStyle(gModalInput.shadowRoot.querySelector(".locked-value")).display, "block", ".locked-value is visible by default");
|
||||
is(getComputedStyle(gModalInput.shadowRoot.querySelector(".unlocked-value")).display, "none", ".unlocked-value is hidden by default");
|
||||
});
|
||||
|
||||
add_task(async function test_editing_set_unset() {
|
||||
let lockedValue = gModalInput.shadowRoot.querySelector(".locked-value");
|
||||
let unlockedValue = gModalInput.shadowRoot.querySelector(".unlocked-value");
|
||||
gModalInput.setAttribute("editing", "");
|
||||
is(getComputedStyle(lockedValue).display, "none", ".locked-value is hidden when editing");
|
||||
is(getComputedStyle(unlockedValue).display, "block", ".unlocked-value is visible when editing");
|
||||
|
||||
const NEW_VALUE = "editedValue";
|
||||
SpecialPowers.wrap(unlockedValue).setUserInput(NEW_VALUE);
|
||||
gModalInput.removeAttribute("editing");
|
||||
unlockedValue = gModalInput.shadowRoot.querySelector(".unlocked-value");
|
||||
|
||||
is(lockedValue.textContent, TEST_INPUT_VALUE, "Values are restored to value prior to edit");
|
||||
is(unlockedValue.value, TEST_INPUT_VALUE, "Edited value is cleared after 'edit' mode is left");
|
||||
is(unlockedValue.defaultValue, TEST_INPUT_VALUE, "Default value is maintained");
|
||||
is(gModalInput.getAttribute("value"), TEST_INPUT_VALUE, "The value attribute on the host element is restored");
|
||||
is(getComputedStyle(lockedValue).display, "block", ".locked-value is visible when not editing");
|
||||
unlockedValue = gModalInput.shadowRoot.querySelector(".unlocked-value");
|
||||
is(getComputedStyle(unlockedValue).display, "none", ".unlocked-value is hidden when not editing");
|
||||
});
|
||||
|
||||
add_task(async function test_password() {
|
||||
gModalInput.setAttribute("type", "password");
|
||||
let lockedValue = gModalInput.shadowRoot.querySelector(".locked-value");
|
||||
let unlockedValue = gModalInput.shadowRoot.querySelector(".unlocked-value");
|
||||
|
||||
is(lockedValue.textContent, gModalInput.constructor.LOCKED_PASSWORD_DISPLAY,
|
||||
"type=password should display masked characters when locked");
|
||||
is(unlockedValue.defaultValue, gModalInput.getAttribute("value"), "type=password should have actual value in .unlocked-value");
|
||||
is(unlockedValue.getAttribute("type"), "password", "input[type=password] should be used for .unlocked-value with type=password");
|
||||
|
||||
gModalInput.removeAttribute("value");
|
||||
is(lockedValue.textContent, "",
|
||||
"type=password should display nothing when locked without a value (.locked-value)");
|
||||
unlockedValue = gModalInput.shadowRoot.querySelector(".unlocked-value");
|
||||
is(unlockedValue.value, "",
|
||||
"type=password should display nothing when locked without a value (.unlocked-value)");
|
||||
});
|
||||
|
||||
add_task(async function test_required() {
|
||||
let unlockedValue = gModalInput.shadowRoot.querySelector(".unlocked-value");
|
||||
ok(unlockedValue.checkValidity(), "The modal-input should be valid when before it is required");
|
||||
|
||||
gModalInput.setAttribute("required", "");
|
||||
ok(!unlockedValue.checkValidity(), "Setting 'required' on the modal-input should make the unlocked-value required");
|
||||
|
||||
unlockedValue = gModalInput.shadowRoot.querySelector(".unlocked-value");
|
||||
unlockedValue.value = "foo";
|
||||
ok(unlockedValue.checkValidity(), "Setting a value on the required modal-input should pass validation");
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user