Fixes #587 Option are not good in term of accessibility, so use checkbox instead. Fix test using Cypress 6pull/599/head
@@ -17,10 +17,12 @@ describe("Connected Mode", function() { | |||||
cy.get("#generatePassword__btn").click(); | cy.get("#generatePassword__btn").click(); | ||||
cy.get("#generated-password").should("have.value", "hjV@\\5ULp3bIs,6B"); | cy.get("#generated-password").should("have.value", "hjV@\\5ULp3bIs,6B"); | ||||
cy.get(".fa-save").should("be.visible"); | cy.get(".fa-save").should("be.visible"); | ||||
cy.get(".fa-user").first().click(); | |||||
cy.get(".fa-user") | |||||
.first() | |||||
.click(); | |||||
cy.get("#signOutButton").should("be.visible"); | cy.get("#signOutButton").should("be.visible"); | ||||
cy.get("#signOutButton").click(); | cy.get("#signOutButton").click(); | ||||
cy.get(".fa-save").should("not.be.visible"); | |||||
cy.get(".fa-save").should('not.exist'); | |||||
}); | }); | ||||
it("can log in and log out", function() { | it("can log in and log out", function() { | ||||
cy.visit("/"); | cy.visit("/"); | ||||
@@ -34,9 +36,11 @@ describe("Connected Mode", function() { | |||||
cy.get("#signInButton").click(); | cy.get("#signInButton").click(); | ||||
cy.get("#siteField").should("be.visible"); | cy.get("#siteField").should("be.visible"); | ||||
cy.get(".fa-key").should("be.visible"); | cy.get(".fa-key").should("be.visible"); | ||||
cy.get(".fa-user").first().click(); | |||||
cy.get(".fa-user") | |||||
.first() | |||||
.click(); | |||||
cy.get("#signOutButton").click(); | cy.get("#signOutButton").click(); | ||||
cy.get(".fa-key").should("not.be.visible"); | |||||
cy.get(".fa-key").should('not.exist'); | |||||
}); | }); | ||||
it("reset password page", function() { | it("reset password page", function() { | ||||
cy.visit("/"); | cy.visit("/"); | ||||
@@ -61,7 +65,9 @@ describe("Connected Mode", function() { | |||||
.click(); | .click(); | ||||
cy.get("#siteField").should("have.value", "example.org"); | cy.get("#siteField").should("have.value", "example.org"); | ||||
cy.get("#login").should("have.value", "contact@example.org"); | cy.get("#login").should("have.value", "contact@example.org"); | ||||
cy.get(".fa-user").first().click(); | |||||
cy.get(".fa-user") | |||||
.first() | |||||
.click(); | |||||
cy.get("#signOutButton").click(); | cy.get("#signOutButton").click(); | ||||
}); | }); | ||||
}); | }); |
@@ -6,7 +6,6 @@ describe("LessPass", function() { | |||||
it("should focus site field", function() { | it("should focus site field", function() { | ||||
cy.visit("/"); | cy.visit("/"); | ||||
cy.wait(500); | cy.wait(500); | ||||
cy.focused() | |||||
.should('have.id', 'siteField') | |||||
cy.focused().should("have.id", "siteField"); | |||||
}); | }); | ||||
}); | }); |
@@ -18,7 +18,9 @@ describe("Password Generation", function() { | |||||
cy.visit("/"); | cy.visit("/"); | ||||
cy.wait(500); | cy.wait(500); | ||||
cy.get("#siteField").type("lesspass.com").tab(); | |||||
cy.get("#siteField") | |||||
.type("lesspass.com") | |||||
.tab(); | |||||
cy.get("#login").type("test@lesspass.com"); | cy.get("#login").type("test@lesspass.com"); | ||||
cy.get("#passwordField").type("test@lesspass.com"); | cy.get("#passwordField").type("test@lesspass.com"); | ||||
cy.wait(500); | cy.wait(500); | ||||
@@ -98,7 +100,9 @@ describe("Password Generation", function() { | |||||
it("should generate password when hit enter nrt_266", function() { | it("should generate password when hit enter nrt_266", function() { | ||||
cy.visit("/"); | cy.visit("/"); | ||||
cy.wait(500); | cy.wait(500); | ||||
cy.get("#siteField").type("lesspass.com").tab(); | |||||
cy.get("#siteField") | |||||
.type("lesspass.com") | |||||
.tab(); | |||||
cy.get("#login").type("test@lesspass.com"); | cy.get("#login").type("test@lesspass.com"); | ||||
cy.get("#passwordField") | cy.get("#passwordField") | ||||
.type("test@lesspass.com") | .type("test@lesspass.com") | ||||
@@ -124,13 +128,15 @@ describe("Password Generation", function() { | |||||
it("should clear password generated when master password change", function() { | it("should clear password generated when master password change", function() { | ||||
cy.visit("/"); | cy.visit("/"); | ||||
cy.wait(500); | cy.wait(500); | ||||
cy.get("#siteField").type("example.org").tab(); | |||||
cy.get("#siteField") | |||||
.type("example.org") | |||||
.tab(); | |||||
cy.get("#login").type("user"); | cy.get("#login").type("user"); | ||||
cy.get("#passwordField").type("password"); | cy.get("#passwordField").type("password"); | ||||
cy.get("#generatePassword__btn").should("be.visible"); | cy.get("#generatePassword__btn").should("be.visible"); | ||||
cy.get("#generatePassword__btn").click(); | cy.get("#generatePassword__btn").click(); | ||||
cy.get("#copyPasswordButton").should("be.visible"); | cy.get("#copyPasswordButton").should("be.visible"); | ||||
cy.get("#generatePassword__btn").should("not.be.visible"); | |||||
cy.get("#generatePassword__btn").should("not.exist"); | |||||
cy.get("#passwordField").type("password"); | cy.get("#passwordField").type("password"); | ||||
cy.get("#copyPasswordButton").should("not.be.visible"); | cy.get("#copyPasswordButton").should("not.be.visible"); | ||||
cy.get("#generatePassword__btn").should("be.visible"); | cy.get("#generatePassword__btn").should("be.visible"); | ||||
@@ -7,21 +7,35 @@ const getLength = () => cy.get("#options #passwordLength"); | |||||
const getCounter = () => cy.get("#options #passwordCounter"); | const getCounter = () => cy.get("#options #passwordCounter"); | ||||
function editSettings() { | function editSettings() { | ||||
getLogin().clear().type("New login"); | |||||
getLowercase().click().should("have.class", "btn-secondary"); | |||||
getUppercase().click().should("have.class", "btn-secondary"); | |||||
getNumbers().click().should("have.class", "btn-secondary"); | |||||
getSymbols().click().should("have.class", "btn-secondary"); | |||||
getLength().clear().type("5"); | |||||
getCounter().clear().type("2"); | |||||
getLogin() | |||||
.clear() | |||||
.type("New login"); | |||||
getLowercase() | |||||
.click() | |||||
.should("not.be.checked"); | |||||
getUppercase() | |||||
.click() | |||||
.should("not.be.checked"); | |||||
getNumbers() | |||||
.click() | |||||
.should("not.be.checked"); | |||||
getSymbols() | |||||
.click() | |||||
.should("not.be.checked"); | |||||
getLength() | |||||
.clear() | |||||
.type("5"); | |||||
getCounter() | |||||
.clear() | |||||
.type("2"); | |||||
} | } | ||||
function checkSettingsEdited() { | function checkSettingsEdited() { | ||||
getLogin().should("have.value", "New login"); | getLogin().should("have.value", "New login"); | ||||
getLowercase().should("have.class", "btn-secondary"); | |||||
getUppercase().should("have.class", "btn-secondary"); | |||||
getNumbers().should("have.class", "btn-secondary"); | |||||
getSymbols().should("have.class", "btn-secondary"); | |||||
getLowercase().should("not.be.checked"); | |||||
getUppercase().should("not.be.checked"); | |||||
getNumbers().should("not.be.checked"); | |||||
getSymbols().should("not.be.checked"); | |||||
getLength().should("have.value", "5"); | getLength().should("have.value", "5"); | ||||
getCounter().should("have.value", "2"); | getCounter().should("have.value", "2"); | ||||
} | } | ||||
@@ -31,10 +45,10 @@ describe("Settings", function() { | |||||
cy.visit("/#/settings"); | cy.visit("/#/settings"); | ||||
cy.wait(500); | cy.wait(500); | ||||
getLogin().should("have.value", ""); | getLogin().should("have.value", ""); | ||||
getLowercase().should("have.class", "btn-primary"); | |||||
getUppercase().should("have.class", "btn-primary"); | |||||
getNumbers().should("have.class", "btn-primary"); | |||||
getSymbols().should("have.class", "btn-primary"); | |||||
getLowercase().should("be.checked"); | |||||
getUppercase().should("be.checked"); | |||||
getNumbers().should("be.checked"); | |||||
getSymbols().should("be.checked"); | |||||
getLength().should("have.value", "16"); | getLength().should("have.value", "16"); | ||||
getCounter().should("have.value", "1"); | getCounter().should("have.value", "1"); | ||||
}); | }); | ||||
@@ -43,7 +57,9 @@ describe("Settings", function() { | |||||
cy.visit("/#/settings"); | cy.visit("/#/settings"); | ||||
cy.wait(500); | cy.wait(500); | ||||
cy.get("#btn-submit-settings").click(); | cy.get("#btn-submit-settings").click(); | ||||
cy.location("pathname").should("be", "/"); | |||||
cy.location().should(location => { | |||||
expect(location.pathname).to.eq("/"); | |||||
}); | |||||
}); | }); | ||||
it("should pass on the settings to the password generator page after save", () => { | it("should pass on the settings to the password generator page after save", () => { | ||||
@@ -62,5 +78,5 @@ describe("Settings", function() { | |||||
cy.visit("/#/settings"); | cy.visit("/#/settings"); | ||||
cy.wait(500); | cy.wait(500); | ||||
checkSettingsEdited(); | checkSettingsEdited(); | ||||
}) | |||||
}); | |||||
}); | }); |
@@ -58,9 +58,12 @@ | |||||
"@vue/test-utils": "^1.1.3", | "@vue/test-utils": "^1.1.3", | ||||
"axios-mock-adapter": "^1.19.0", | "axios-mock-adapter": "^1.19.0", | ||||
"babel-core": "7.0.0-bridge.0", | "babel-core": "7.0.0-bridge.0", | ||||
"cypress": "^6.8.0", | |||||
"cypress-plugin-tab": "^1.0.5", | |||||
"jest": "^26.6.3", | "jest": "^26.6.3", | ||||
"jquery": "^3.6.0", | "jquery": "^3.6.0", | ||||
"popper.js": "^1.16.1", | "popper.js": "^1.16.1", | ||||
"start-server-and-test": "^1.12.1", | |||||
"vue-jest": "^3.0.7", | "vue-jest": "^3.0.7", | ||||
"vue-polyglot-utils": "^0.1.1", | "vue-polyglot-utils": "^0.1.1", | ||||
"vue-template-compiler": "^2.6.12", | "vue-template-compiler": "^2.6.12", | ||||
@@ -15,56 +15,72 @@ | |||||
<div class="col-12"> | <div class="col-12"> | ||||
<div class="row"> | <div class="row"> | ||||
<div class="col"> | <div class="col"> | ||||
<label for="types">{{ $t('Options') }}</label> | |||||
<label for="types">{{ $t("Options") }}</label> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<div id="types" class="row"> | <div id="types" class="row"> | ||||
<div class="col-3"> | <div class="col-3"> | ||||
<button | |||||
id="lowercase__btn" | |||||
type="button" | |||||
class="btn btn-block btn-sm px-0" | |||||
tabindex="1" | |||||
v-bind:class="{'btn-primary':options.lowercase===true, 'btn-secondary':options.lowercase===false}" | |||||
v-on:click="options.lowercase=!options.lowercase" | |||||
>a-z</button> | |||||
<div class="form-check"> | |||||
<input | |||||
id="lowercase__btn" | |||||
type="checkbox" | |||||
tabindex="1" | |||||
class="form-check-input" | |||||
v-model="options.lowercase" | |||||
/> | |||||
<label class="form-check-label" for="lowercase__btn"> | |||||
a-z | |||||
</label> | |||||
</div> | |||||
</div> | </div> | ||||
<div class="col-3"> | <div class="col-3"> | ||||
<button | |||||
id="uppercase__btn" | |||||
type="button" | |||||
class="btn btn-block btn-sm px-0" | |||||
tabindex="1" | |||||
v-bind:class="{'btn-primary':options.uppercase===true, 'btn-secondary':options.uppercase===false}" | |||||
v-on:click="options.uppercase=!options.uppercase" | |||||
>A-Z</button> | |||||
<div class="form-check"> | |||||
<input | |||||
id="uppercase__btn" | |||||
type="checkbox" | |||||
tabindex="1" | |||||
class="form-check-input" | |||||
v-model="options.uppercase" | |||||
/> | |||||
<label class="form-check-label" for="uppercase__btn"> | |||||
A-Z | |||||
</label> | |||||
</div> | |||||
</div> | </div> | ||||
<div class="col-3"> | <div class="col-3"> | ||||
<button | |||||
id="numbers__btn" | |||||
type="button" | |||||
class="btn btn-block btn-sm px-0" | |||||
tabindex="1" | |||||
v-bind:class="{'btn-primary':options.numbers===true,'btn-secondary':options.numbers===false}" | |||||
v-on:click="options.numbers=!options.numbers" | |||||
>0-9</button> | |||||
<div class="form-check"> | |||||
<input | |||||
id="numbers__btn" | |||||
type="checkbox" | |||||
tabindex="1" | |||||
class="form-check-input" | |||||
v-model="options.numbers" | |||||
/> | |||||
<label class="form-check-label" for="numbers__btn"> | |||||
0-9 | |||||
</label> | |||||
</div> | |||||
</div> | </div> | ||||
<div class="col-3"> | <div class="col-3"> | ||||
<button | |||||
id="symbols__btn" | |||||
type="button" | |||||
class="btn btn-block btn-sm px-0" | |||||
tabindex="1" | |||||
v-bind:class="{'btn-primary':options.symbols===true,'btn-secondary':options.symbols===false}" | |||||
v-on:click="options.symbols=!options.symbols" | |||||
>%!@</button> | |||||
<div class="form-check"> | |||||
<input | |||||
id="symbols__btn" | |||||
type="checkbox" | |||||
tabindex="1" | |||||
class="form-check-input" | |||||
v-model="options.symbols" | |||||
/> | |||||
<label class="form-check-label" for="symbols__btn"> | |||||
%!@ | |||||
</label> | |||||
</div> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="form-group row mb-0"> | <div class="form-group row mb-0"> | ||||
<div class="col-5 col-sm-4"> | <div class="col-5 col-sm-4"> | ||||
<label for="passwordLength">{{ $t('Length') }}</label> | |||||
<label for="passwordLength">{{ $t("Length") }}</label> | |||||
<div class="input-group input-group-sm"> | <div class="input-group input-group-sm"> | ||||
<span class="input-group-btn"> | <span class="input-group-btn"> | ||||
<button | <button | ||||
@@ -72,7 +88,9 @@ | |||||
class="btn btn-primary btn-sm px-2" | class="btn btn-primary btn-sm px-2" | ||||
tabindex="1" | tabindex="1" | ||||
type="button" | type="button" | ||||
v-on:click="options.length=decrement(options.length, {min: 5, max: 35})" | |||||
v-on:click=" | |||||
options.length = decrement(options.length, { min: 5, max: 35 }) | |||||
" | |||||
> | > | ||||
<small> | <small> | ||||
<i class="fa fa-minus"></i> | <i class="fa fa-minus"></i> | ||||
@@ -94,7 +112,9 @@ | |||||
class="btn btn-primary btn-sm px-2" | class="btn btn-primary btn-sm px-2" | ||||
tabindex="1" | tabindex="1" | ||||
type="button" | type="button" | ||||
v-on:click="options.length=increment(options.length, {min: 5, max: 35})" | |||||
v-on:click=" | |||||
options.length = increment(options.length, { min: 5, max: 35 }) | |||||
" | |||||
> | > | ||||
<small> | <small> | ||||
<i class="fa fa-plus"></i> | <i class="fa fa-plus"></i> | ||||
@@ -107,9 +127,15 @@ | |||||
<label | <label | ||||
for="passwordCounter" | for="passwordCounter" | ||||
data-balloon-length="large" | data-balloon-length="large" | ||||
v-bind:data-balloon="$t('CounterFieldHelp', 'Increment this value to change the generated password without changing your master options.')" | |||||
v-bind:data-balloon=" | |||||
$t( | |||||
'CounterFieldHelp', | |||||
'Increment this value to change the generated password without changing your master options.' | |||||
) | |||||
" | |||||
data-balloon-pos="up" | data-balloon-pos="up" | ||||
>{{$t('Counter')}}</label> | |||||
>{{ $t("Counter") }}</label | |||||
> | |||||
<div class="input-group input-group-sm"> | <div class="input-group input-group-sm"> | ||||
<span class="input-group-btn"> | <span class="input-group-btn"> | ||||
<button | <button | ||||
@@ -117,7 +143,9 @@ | |||||
class="btn btn-primary btn-sm px-2" | class="btn btn-primary btn-sm px-2" | ||||
tabindex="1" | tabindex="1" | ||||
type="button" | type="button" | ||||
v-on:click="options.counter=decrement(options.counter, {min: 1})" | |||||
v-on:click=" | |||||
options.counter = decrement(options.counter, { min: 1 }) | |||||
" | |||||
> | > | ||||
<small> | <small> | ||||
<i class="fa fa-minus"></i> | <i class="fa fa-minus"></i> | ||||
@@ -138,7 +166,9 @@ | |||||
class="btn btn-primary btn-sm px-2" | class="btn btn-primary btn-sm px-2" | ||||
tabindex="1" | tabindex="1" | ||||
type="button" | type="button" | ||||
v-on:click="options.counter=increment(options.counter, {min: 1})" | |||||
v-on:click=" | |||||
options.counter = increment(options.counter, { min: 1 }) | |||||
" | |||||
> | > | ||||
<small> | <small> | ||||
<i class="fa fa-plus"></i> | <i class="fa fa-plus"></i> | ||||
@@ -12,7 +12,11 @@ div.awesomplete > ul { | |||||
} | } | ||||
</style> | </style> | ||||
<template> | <template> | ||||
<form id="password-generator" v-on:submit.prevent="generatePassword" novalidate> | |||||
<form | |||||
id="password-generator" | |||||
v-on:submit.prevent="generatePassword" | |||||
novalidate | |||||
> | |||||
<div class="form-group"> | <div class="form-group"> | ||||
<input-site | <input-site | ||||
ref="site" | ref="site" | ||||
@@ -25,7 +29,7 @@ div.awesomplete > ul { | |||||
</div> | </div> | ||||
<remove-auto-complete></remove-auto-complete> | <remove-auto-complete></remove-auto-complete> | ||||
<div class="form-group"> | <div class="form-group"> | ||||
<label for="login" class="sr-only">{{ $t('Username') }}</label> | |||||
<label for="login" class="sr-only">{{ $t("Username") }}</label> | |||||
<div class="inner-addon left-addon"> | <div class="inner-addon left-addon"> | ||||
<i class="fa fa-user"></i> | <i class="fa fa-user"></i> | ||||
<input | <input | ||||
@@ -59,7 +63,9 @@ div.awesomplete > ul { | |||||
tabindex="0" | tabindex="0" | ||||
class="btn btn-primary btn-block" | class="btn btn-primary btn-block" | ||||
v-if="!passwordGenerated" | v-if="!passwordGenerated" | ||||
>{{ $t('Generate') }}</button> | |||||
> | |||||
{{ $t("Generate") }} | |||||
</button> | |||||
<div class="input-group" v-show="passwordGenerated"> | <div class="input-group" v-show="passwordGenerated"> | ||||
<span class="input-group-btn"> | <span class="input-group-btn"> | ||||
<button | <button | ||||
@@ -198,6 +204,19 @@ export default { | |||||
); | ); | ||||
return; | return; | ||||
} | } | ||||
const lowercase = this.password.lowercase; | |||||
const uppercase = this.password.uppercase; | |||||
const numbers = this.password.numbers; | |||||
const symbols = this.password.symbols; | |||||
if (!lowercase && !uppercase && !numbers && !symbols) { | |||||
message.error( | |||||
this.$t( | |||||
"AtLeastOneOptionShouldBeSelected", | |||||
"You must select at least one option among lowercase, uppercase, numbers or symbols." | |||||
) | |||||
); | |||||
return; | |||||
} | |||||
const length = this.password.length; | const length = this.password.length; | ||||
if (length > 35) { | if (length > 35) { | ||||
message.warning( | message.warning( | ||||
@@ -209,10 +228,10 @@ export default { | |||||
} | } | ||||
this.cleanErrors(); | this.cleanErrors(); | ||||
const passwordProfile = { | const passwordProfile = { | ||||
lowercase: this.password.lowercase, | |||||
uppercase: this.password.uppercase, | |||||
numbers: this.password.numbers, | |||||
symbols: this.password.symbols, | |||||
lowercase, | |||||
uppercase, | |||||
numbers, | |||||
symbols, | |||||
length: this.password.length, | length: this.password.length, | ||||
counter: this.password.counter, | counter: this.password.counter, | ||||
version: this.password.version | version: this.password.version | ||||