@@ -42,5 +42,6 @@ jobs: | |||||
run: | | run: | | ||||
git config --global user.name 'botsito' | git config --global user.name 'botsito' | ||||
git config --global user.email 'botsito@lesspass.com' | git config --global user.email 'botsito@lesspass.com' | ||||
git commit -am "Auto build for lesspass-pure" | |||||
git add . | |||||
git commit -m "Auto build for lesspass-pure" | |||||
git push | git push |
@@ -1,6 +1,6 @@ | |||||
{ | { | ||||
"name": "lesspass-pure", | "name": "lesspass-pure", | ||||
"version": "9.2.0", | |||||
"version": "9.3.1", | |||||
"description": "LessPass web component", | "description": "LessPass web component", | ||||
"license": "GPL-3.0", | "license": "GPL-3.0", | ||||
"author": "Guillaume Vincent <guillaume@oslab.fr>", | "author": "Guillaume Vincent <guillaume@oslab.fr>", | ||||
@@ -12,7 +12,9 @@ | |||||
} | } | ||||
</style> | </style> | ||||
<template> | <template> | ||||
<div v-bind:style="avatarStyle" class="passwordProfile__avatar">{{firstLetter}}</div> | |||||
<div v-bind:style="avatarStyle" class="passwordProfile__avatar"> | |||||
{{ firstLetter }} | |||||
</div> | |||||
</template> | </template> | ||||
<script> | <script> | ||||
export default { | export default { | ||||
@@ -82,7 +82,7 @@ export default { | |||||
} | } | ||||
}, | }, | ||||
watch: { | watch: { | ||||
site: function (newValue) { | |||||
site: function(newValue) { | |||||
const suggestions = getSuggestions(newValue).map(suggestion => { | const suggestions = getSuggestions(newValue).map(suggestion => { | ||||
return { site: suggestion, suggestion: true, login: "" }; | return { site: suggestion, suggestion: true, login: "" }; | ||||
}); | }); | ||||
@@ -1,44 +1,76 @@ | |||||
<style> | <style> | ||||
#menu .white-link, #menu .text-white { | |||||
color: inherit; | |||||
} | |||||
#menu .white-link, | |||||
#menu .text-white { | |||||
color: inherit; | |||||
} | |||||
#menu .white-link:hover, #menu .white-link:focus, #menu .white-link:active { | |||||
text-decoration: none; | |||||
color: inherit; | |||||
} | |||||
#menu .white-link:hover, | |||||
#menu .white-link:focus, | |||||
#menu .white-link:active { | |||||
text-decoration: none; | |||||
color: inherit; | |||||
} | |||||
.card-inverse { | |||||
background-color: #333; | |||||
border-color: #333; | |||||
} | |||||
.card-inverse { | |||||
background-color: #333; | |||||
border-color: #333; | |||||
} | |||||
</style> | </style> | ||||
<template> | <template> | ||||
<div id="menu"> | <div id="menu"> | ||||
<div class="card-header" v-bind:class="{ 'text-white bg-dark': isGuest}"> | |||||
<div class="card-header" v-bind:class="{ 'text-white bg-dark': isGuest }"> | |||||
<div class="row"> | <div class="row"> | ||||
<div class="col-4"> | <div class="col-4"> | ||||
<span id="title" v-on:click="fullReload()" class="white-link pointer">LessPass</span> | |||||
<span id="title" v-on:click="fullReload()" class="white-link pointer" | |||||
>LessPass</span | |||||
> | |||||
</div> | </div> | ||||
<div class="col-8 text-right"> | <div class="col-8 text-right"> | ||||
<span v-if="saved && isAuthenticated"> | <span v-if="saved && isAuthenticated"> | ||||
<small><i class="fa fa-lg fa-check pl-3"></i> saved</small> | <small><i class="fa fa-lg fa-check pl-3"></i> saved</small> | ||||
</span> | </span> | ||||
<span class="white-link" | |||||
v-on:click="saveOrUpdatePassword()" | |||||
v-if="!saved && isAuthenticated && $store.state.password.site !== '' && $store.state.route.path === '/'" :title="$t('Save')"> | |||||
<span | |||||
class="white-link" | |||||
v-on:click="saveOrUpdatePassword()" | |||||
v-if=" | |||||
!saved && | |||||
isAuthenticated && | |||||
$store.state.password.site !== '' && | |||||
$store.state.route.path === '/' | |||||
" | |||||
:title="$t('Save')" | |||||
> | |||||
<i class="fa fa-lg fa-save pointer"></i> | <i class="fa fa-lg fa-save pointer"></i> | ||||
</span> | </span> | ||||
<router-link class="white-link pl-3" :to="{ name: 'passwords'}" v-if="isAuthenticated" :title="$t('Saved passwords')"> | |||||
<router-link | |||||
class="white-link pl-3" | |||||
:to="{ name: 'passwords' }" | |||||
v-if="isAuthenticated" | |||||
:title="$t('Saved passwords')" | |||||
> | |||||
<i class="fa fa-lg fa-key"></i> | <i class="fa fa-lg fa-key"></i> | ||||
</router-link> | </router-link> | ||||
<router-link class="white-link pl-3" :to="{ name: 'settings'}" :title="$t('Settings')"> | |||||
<router-link | |||||
class="white-link pl-3" | |||||
:to="{ name: 'settings' }" | |||||
:title="$t('Settings')" | |||||
> | |||||
<i class="fa fa-lg fa-cog"></i> | <i class="fa fa-lg fa-cog"></i> | ||||
</router-link> | </router-link> | ||||
<router-link class="white-link pl-3" :to="{ name: 'myaccount'}" v-if="isAuthenticated" :title="$t('My Account')"> | |||||
<router-link | |||||
class="white-link pl-3" | |||||
:to="{ name: 'myaccount' }" | |||||
v-if="isAuthenticated" | |||||
:title="$t('My Account')" | |||||
> | |||||
<i class="fa fa-lg fa-user pointer"></i> | <i class="fa fa-lg fa-user pointer"></i> | ||||
</router-link> | </router-link> | ||||
<router-link class="white-link pl-3" :to="{ name: 'login'}" v-if="isGuest" :title="$t('Sign In')"> | |||||
<router-link | |||||
class="white-link pl-3" | |||||
:to="{ name: 'login' }" | |||||
v-if="isGuest" | |||||
:title="$t('Sign In')" | |||||
> | |||||
<i class="fa fa-lg fa-sign-in pointer"></i> | <i class="fa fa-lg fa-sign-in pointer"></i> | ||||
</router-link> | </router-link> | ||||
</div> | </div> | ||||
@@ -47,32 +79,29 @@ | |||||
</div> | </div> | ||||
</template> | </template> | ||||
<script> | <script> | ||||
import {mapGetters} from 'vuex'; | |||||
import { mapGetters } from "vuex"; | |||||
export default { | |||||
data() { | |||||
return { | |||||
saved: false | |||||
} | |||||
}, | |||||
methods: { | |||||
fullReload() { | |||||
this.$store.dispatch('resetPassword'); | |||||
this.$router.push({name: 'home'}).catch(e => {}); | |||||
}, | |||||
saveOrUpdatePassword() { | |||||
this.$store.dispatch('saveOrUpdatePassword'); | |||||
this.saved = true; | |||||
setTimeout(() => { | |||||
this.saved = false; | |||||
}, 3000); | |||||
} | |||||
export default { | |||||
data() { | |||||
return { | |||||
saved: false | |||||
}; | |||||
}, | |||||
methods: { | |||||
fullReload() { | |||||
this.$store.dispatch("resetPassword"); | |||||
this.$router.push({ name: "home" }).catch(e => {}); | |||||
}, | }, | ||||
computed: { | |||||
...mapGetters([ | |||||
'isAuthenticated', | |||||
'isGuest' | |||||
]) | |||||
saveOrUpdatePassword() { | |||||
this.$store.dispatch("saveOrUpdatePassword"); | |||||
this.saved = true; | |||||
setTimeout(() => { | |||||
this.saved = false; | |||||
}, 3000); | |||||
} | } | ||||
}, | |||||
computed: { | |||||
...mapGetters(["isAuthenticated", "isGuest"]) | |||||
} | } | ||||
}; | |||||
</script> | </script> |
@@ -34,11 +34,15 @@ | |||||
<div v-if="message.text"> | <div v-if="message.text"> | ||||
<div | <div | ||||
class="card-header text-white" | class="card-header text-white" | ||||
v-bind:class="{ 'bg-warning': message.status==='warning', 'bg-danger': message.status==='error', 'bg-success': message.status==='success' }" | |||||
v-bind:class="{ | |||||
'bg-warning': message.status === 'warning', | |||||
'bg-danger': message.status === 'error', | |||||
'bg-success': message.status === 'success' | |||||
}" | |||||
> | > | ||||
<div class="row"> | <div class="row"> | ||||
<div class="col-12"> | <div class="col-12"> | ||||
<small>{{message.text}}</small> | |||||
<small>{{ message.text }}</small> | |||||
<span class="close-notification" v-on:click="hideMessage"> | <span class="close-notification" v-on:click="hideMessage"> | ||||
<i class="fa fa-close"></i> | <i class="fa fa-close"></i> | ||||
</span> | </span> | ||||
@@ -24,9 +24,9 @@ | |||||
<div class="passwordProfile__info" v-on:click="setPassword()"> | <div class="passwordProfile__info" v-on:click="setPassword()"> | ||||
<avatar v-bind:name="password.site"></avatar> | <avatar v-bind:name="password.site"></avatar> | ||||
<div class="passwordProfile__meta"> | <div class="passwordProfile__meta"> | ||||
<b>{{password.site}}</b> | |||||
<b>{{ password.site }}</b> | |||||
<br /> | <br /> | ||||
{{password.login}} | |||||
{{ password.login }} | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="passwordProfile__actions"> | <div class="passwordProfile__actions"> | ||||
@@ -1,10 +1,20 @@ | |||||
<template> | <template> | ||||
<div style="display: none;"> | <div style="display: none;"> | ||||
<label for="username"> | <label for="username"> | ||||
<input type="text" id="username" name="username" autocomplete="username"> | |||||
<input | |||||
type="text" | |||||
id="username" | |||||
name="username" | |||||
autocomplete="username" | |||||
/> | |||||
</label> | </label> | ||||
<label for="password"> | <label for="password"> | ||||
<input type="password" id="password" name="password" autocomplete="current-password"> | |||||
<input | |||||
type="password" | |||||
id="password" | |||||
name="password" | |||||
autocomplete="current-password" | |||||
/> | |||||
</label> | </label> | ||||
</div> | </div> | ||||
</template> | </template> |
@@ -3,36 +3,60 @@ import * as urlParser from "./url-parser"; | |||||
test("cleanUrl", () => { | test("cleanUrl", () => { | ||||
expect("lesspass.com").toBe(urlParser.cleanUrl("https://lesspass.com/#!/")); | expect("lesspass.com").toBe(urlParser.cleanUrl("https://lesspass.com/#!/")); | ||||
expect("lesspass.com").toBe(urlParser.cleanUrl("https://lesspass.com/api/")); | expect("lesspass.com").toBe(urlParser.cleanUrl("https://lesspass.com/api/")); | ||||
expect("api.lesspass.com").toBe(urlParser.cleanUrl("https://api.lesspass.com/")); | |||||
expect("api.lesspass.com").toBe( | |||||
urlParser.cleanUrl("https://api.lesspass.com/") | |||||
); | |||||
expect("lesspass.com").toBe(urlParser.cleanUrl("http://lesspass.com")); | expect("lesspass.com").toBe(urlParser.cleanUrl("http://lesspass.com")); | ||||
expect("stackoverflow.com").toBe(urlParser.cleanUrl( | |||||
"http://stackoverflow.com/questions/3689423/google-chrome-plugin-how-to-get-domain-from-url-tab-url" | |||||
)); | |||||
expect("v4-alpha.getbootstrap.com").toBe(urlParser.cleanUrl("http://v4-alpha.getbootstrap.com/components/buttons/")); | |||||
expect("accounts.google.com").toBe(urlParser.cleanUrl( | |||||
"https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue=https://mail.google.com/mail/&ss=1&scc=1<mpl=default<mplcache=2&emr=1&osid=1#identifier" | |||||
)); | |||||
expect("www.netflix.com").toBe(urlParser.cleanUrl("https://www.netflix.com/browse")); | |||||
expect("stackoverflow.com").toBe( | |||||
urlParser.cleanUrl( | |||||
"http://stackoverflow.com/questions/3689423/google-chrome-plugin-how-to-get-domain-from-url-tab-url" | |||||
) | |||||
); | |||||
expect("v4-alpha.getbootstrap.com").toBe( | |||||
urlParser.cleanUrl("http://v4-alpha.getbootstrap.com/components/buttons/") | |||||
); | |||||
expect("accounts.google.com").toBe( | |||||
urlParser.cleanUrl( | |||||
"https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue=https://mail.google.com/mail/&ss=1&scc=1<mpl=default<mplcache=2&emr=1&osid=1#identifier" | |||||
) | |||||
); | |||||
expect("www.netflix.com").toBe( | |||||
urlParser.cleanUrl("https://www.netflix.com/browse") | |||||
); | |||||
expect("www.bbc.co.uk").toBe(urlParser.cleanUrl("https://www.bbc.co.uk")); | expect("www.bbc.co.uk").toBe(urlParser.cleanUrl("https://www.bbc.co.uk")); | ||||
expect("192.168.1.1:10443").toBe(urlParser.cleanUrl("https://192.168.1.1:10443/webapp/")); | |||||
expect("192.168.1.1:10443").toBe( | |||||
urlParser.cleanUrl("https://192.168.1.1:10443/webapp/") | |||||
); | |||||
expect("").toBe(urlParser.cleanUrl(undefined)); | expect("").toBe(urlParser.cleanUrl(undefined)); | ||||
expect("").toBe(urlParser.cleanUrl(undefined)); | expect("").toBe(urlParser.cleanUrl(undefined)); | ||||
expect("").toBe(urlParser.cleanUrl("chrome://extensions/")); | expect("").toBe(urlParser.cleanUrl("chrome://extensions/")); | ||||
}); | }); | ||||
test("getSuggestions", () => { | test("getSuggestions", () => { | ||||
expect(["bbc", "bbc.com", "www.bbc.com"]).toEqual(urlParser.getSuggestions("https://www.bbc.com")); | |||||
expect(["bbc", "bbc.com", "www.bbc.com"]).toEqual( | |||||
urlParser.getSuggestions("https://www.bbc.com") | |||||
); | |||||
expect(["example", "example.org", "www.example.org"]).toEqual( | expect(["example", "example.org", "www.example.org"]).toEqual( | ||||
urlParser.getSuggestions("https://www.example.org/api/?offset=100&limit=10") | urlParser.getSuggestions("https://www.example.org/api/?offset=100&limit=10") | ||||
); | ); | ||||
expect(["example", "example.org"]).toEqual(urlParser.getSuggestions("https://example.org")); | |||||
expect(["example", "example.org"]).toEqual(urlParser.getSuggestions("example.org")); | |||||
expect([]).toEqual(urlParser.getSuggestions("https://192.168.1.1:10443/webapp/")); | |||||
expect(["example", "example.org"]).toEqual( | |||||
urlParser.getSuggestions("https://example.org") | |||||
); | |||||
expect(["example", "example.org"]).toEqual( | |||||
urlParser.getSuggestions("example.org") | |||||
); | |||||
expect([]).toEqual( | |||||
urlParser.getSuggestions("https://192.168.1.1:10443/webapp/") | |||||
); | |||||
expect([]).toEqual(urlParser.getSuggestions("example")); | expect([]).toEqual(urlParser.getSuggestions("example")); | ||||
expect([]).toEqual(urlParser.getSuggestions("example.")); | expect([]).toEqual(urlParser.getSuggestions("example.")); | ||||
expect([]).toEqual(urlParser.getSuggestions("example.o")); | expect([]).toEqual(urlParser.getSuggestions("example.o")); | ||||
expect(urlParser.getSuggestions("http://example.org")).toEqual(urlParser.getSuggestions("https://example.org")); | |||||
expect(["example", "example.org"]).toEqual(urlParser.getSuggestions("EXAMPLE.org")); | |||||
expect(urlParser.getSuggestions("http://example.org")).toEqual( | |||||
urlParser.getSuggestions("https://example.org") | |||||
); | |||||
expect(["example", "example.org"]).toEqual( | |||||
urlParser.getSuggestions("EXAMPLE.org") | |||||
); | |||||
}); | }); | ||||
test("getSite", () => { | test("getSite", () => { | ||||
@@ -90,7 +90,10 @@ test("SET_PASSWORDS", () => { | |||||
test("DELETE_PASSWORD", () => { | test("DELETE_PASSWORD", () => { | ||||
const DELETE_PASSWORD = mutations[types.DELETE_PASSWORD]; | const DELETE_PASSWORD = mutations[types.DELETE_PASSWORD]; | ||||
const state = { | const state = { | ||||
passwords: [{ id: "1", site: "site1" }, { id: "2", site: "site2" }] | |||||
passwords: [ | |||||
{ id: "1", site: "site1" }, | |||||
{ id: "2", site: "site2" } | |||||
] | |||||
}; | }; | ||||
expect(state.passwords.length).toBe(2); | expect(state.passwords.length).toBe(2); | ||||
DELETE_PASSWORD(state, { id: "1" }); | DELETE_PASSWORD(state, { id: "1" }); | ||||
@@ -100,7 +103,10 @@ test("DELETE_PASSWORD", () => { | |||||
test("DELETE_PASSWORD replace state.password with state.defaultPassword", () => { | test("DELETE_PASSWORD replace state.password with state.defaultPassword", () => { | ||||
const DELETE_PASSWORD = mutations[types.DELETE_PASSWORD]; | const DELETE_PASSWORD = mutations[types.DELETE_PASSWORD]; | ||||
const state = { | const state = { | ||||
passwords: [{ id: "1", length: 30 }, { id: "2", length: 16 }], | |||||
passwords: [ | |||||
{ id: "1", length: 30 }, | |||||
{ id: "2", length: 16 } | |||||
], | |||||
password: { id: "1", length: 30 }, | password: { id: "1", length: 30 }, | ||||
defaultPassword: { length: 16 } | defaultPassword: { length: 16 } | ||||
}; | }; | ||||
@@ -75,7 +75,7 @@ export default { | |||||
this.$router.push({ name: "home" }).catch(() => {}); | this.$router.push({ name: "home" }).catch(() => {}); | ||||
}, | }, | ||||
changePassword: async function() { | changePassword: async function() { | ||||
if (!this.email) { | |||||
if (!this.email) { | |||||
message.error(this.$t("EmailRequiredError", "Email is required")); | message.error(this.$t("EmailRequiredError", "Email is required")); | ||||
return; | return; | ||||
} | } | ||||
@@ -112,7 +112,9 @@ export default { | |||||
var siteMatch = password.site.match(new RegExp(this.searchQuery, "i")); | var siteMatch = password.site.match(new RegExp(this.searchQuery, "i")); | ||||
return loginMatch || siteMatch; | return loginMatch || siteMatch; | ||||
}); | }); | ||||
this.pagination.pageCount = Math.ceil(passwords.length / this.pagination.perPage); | |||||
this.pagination.pageCount = Math.ceil( | |||||
passwords.length / this.pagination.perPage | |||||
); | |||||
return passwords.slice( | return passwords.slice( | ||||
this.pagination.currentPage * this.pagination.perPage - | this.pagination.currentPage * this.pagination.perPage - | ||||
this.pagination.perPage, | this.pagination.perPage, | ||||