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("#generated-password").should("have.value", "hjV@\\5ULp3bIs,6B"); | |||
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").click(); | |||
cy.get(".fa-save").should("not.be.visible"); | |||
cy.get(".fa-save").should('not.exist'); | |||
}); | |||
it("can log in and log out", function() { | |||
cy.visit("/"); | |||
@@ -34,9 +36,11 @@ describe("Connected Mode", function() { | |||
cy.get("#signInButton").click(); | |||
cy.get("#siteField").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(".fa-key").should("not.be.visible"); | |||
cy.get(".fa-key").should('not.exist'); | |||
}); | |||
it("reset password page", function() { | |||
cy.visit("/"); | |||
@@ -61,7 +65,9 @@ describe("Connected Mode", function() { | |||
.click(); | |||
cy.get("#siteField").should("have.value", "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(); | |||
}); | |||
}); |
@@ -6,7 +6,6 @@ describe("LessPass", function() { | |||
it("should focus site field", function() { | |||
cy.visit("/"); | |||
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.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("#passwordField").type("test@lesspass.com"); | |||
cy.wait(500); | |||
@@ -98,7 +100,9 @@ describe("Password Generation", function() { | |||
it("should generate password when hit enter nrt_266", function() { | |||
cy.visit("/"); | |||
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("#passwordField") | |||
.type("test@lesspass.com") | |||
@@ -124,13 +128,15 @@ describe("Password Generation", function() { | |||
it("should clear password generated when master password change", function() { | |||
cy.visit("/"); | |||
cy.wait(500); | |||
cy.get("#siteField").type("example.org").tab(); | |||
cy.get("#siteField") | |||
.type("example.org") | |||
.tab(); | |||
cy.get("#login").type("user"); | |||
cy.get("#passwordField").type("password"); | |||
cy.get("#generatePassword__btn").should("be.visible"); | |||
cy.get("#generatePassword__btn").click(); | |||
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("#copyPasswordButton").should("not.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"); | |||
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() { | |||
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"); | |||
getCounter().should("have.value", "2"); | |||
} | |||
@@ -31,10 +45,10 @@ describe("Settings", function() { | |||
cy.visit("/#/settings"); | |||
cy.wait(500); | |||
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"); | |||
getCounter().should("have.value", "1"); | |||
}); | |||
@@ -43,7 +57,9 @@ describe("Settings", function() { | |||
cy.visit("/#/settings"); | |||
cy.wait(500); | |||
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", () => { | |||
@@ -62,5 +78,5 @@ describe("Settings", function() { | |||
cy.visit("/#/settings"); | |||
cy.wait(500); | |||
checkSettingsEdited(); | |||
}) | |||
}); | |||
}); |
@@ -58,9 +58,12 @@ | |||
"@vue/test-utils": "^1.1.3", | |||
"axios-mock-adapter": "^1.19.0", | |||
"babel-core": "7.0.0-bridge.0", | |||
"cypress": "^6.8.0", | |||
"cypress-plugin-tab": "^1.0.5", | |||
"jest": "^26.6.3", | |||
"jquery": "^3.6.0", | |||
"popper.js": "^1.16.1", | |||
"start-server-and-test": "^1.12.1", | |||
"vue-jest": "^3.0.7", | |||
"vue-polyglot-utils": "^0.1.1", | |||
"vue-template-compiler": "^2.6.12", | |||
@@ -15,56 +15,72 @@ | |||
<div class="col-12"> | |||
<div class="row"> | |||
<div class="col"> | |||
<label for="types">{{ $t('Options') }}</label> | |||
<label for="types">{{ $t("Options") }}</label> | |||
</div> | |||
</div> | |||
<div id="types" class="row"> | |||
<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 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 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 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 class="form-group row mb-0"> | |||
<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"> | |||
<span class="input-group-btn"> | |||
<button | |||
@@ -72,7 +88,9 @@ | |||
class="btn btn-primary btn-sm px-2" | |||
tabindex="1" | |||
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> | |||
<i class="fa fa-minus"></i> | |||
@@ -94,7 +112,9 @@ | |||
class="btn btn-primary btn-sm px-2" | |||
tabindex="1" | |||
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> | |||
<i class="fa fa-plus"></i> | |||
@@ -107,9 +127,15 @@ | |||
<label | |||
for="passwordCounter" | |||
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" | |||
>{{$t('Counter')}}</label> | |||
>{{ $t("Counter") }}</label | |||
> | |||
<div class="input-group input-group-sm"> | |||
<span class="input-group-btn"> | |||
<button | |||
@@ -117,7 +143,9 @@ | |||
class="btn btn-primary btn-sm px-2" | |||
tabindex="1" | |||
type="button" | |||
v-on:click="options.counter=decrement(options.counter, {min: 1})" | |||
v-on:click=" | |||
options.counter = decrement(options.counter, { min: 1 }) | |||
" | |||
> | |||
<small> | |||
<i class="fa fa-minus"></i> | |||
@@ -138,7 +166,9 @@ | |||
class="btn btn-primary btn-sm px-2" | |||
tabindex="1" | |||
type="button" | |||
v-on:click="options.counter=increment(options.counter, {min: 1})" | |||
v-on:click=" | |||
options.counter = increment(options.counter, { min: 1 }) | |||
" | |||
> | |||
<small> | |||
<i class="fa fa-plus"></i> | |||
@@ -12,7 +12,11 @@ div.awesomplete > ul { | |||
} | |||
</style> | |||
<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"> | |||
<input-site | |||
ref="site" | |||
@@ -25,7 +29,7 @@ div.awesomplete > ul { | |||
</div> | |||
<remove-auto-complete></remove-auto-complete> | |||
<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"> | |||
<i class="fa fa-user"></i> | |||
<input | |||
@@ -59,7 +63,9 @@ div.awesomplete > ul { | |||
tabindex="0" | |||
class="btn btn-primary btn-block" | |||
v-if="!passwordGenerated" | |||
>{{ $t('Generate') }}</button> | |||
> | |||
{{ $t("Generate") }} | |||
</button> | |||
<div class="input-group" v-show="passwordGenerated"> | |||
<span class="input-group-btn"> | |||
<button | |||
@@ -198,6 +204,19 @@ export default { | |||
); | |||
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; | |||
if (length > 35) { | |||
message.warning( | |||
@@ -209,10 +228,10 @@ export default { | |||
} | |||
this.cleanErrors(); | |||
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, | |||
counter: this.password.counter, | |||
version: this.password.version | |||