@@ -42,5 +42,6 @@ jobs: | |||
run: | | |||
git config --global user.name 'botsito' | |||
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 |
@@ -1,6 +1,6 @@ | |||
{ | |||
"name": "lesspass-pure", | |||
"version": "9.2.0", | |||
"version": "9.3.1", | |||
"description": "LessPass web component", | |||
"license": "GPL-3.0", | |||
"author": "Guillaume Vincent <guillaume@oslab.fr>", | |||
@@ -12,7 +12,9 @@ | |||
} | |||
</style> | |||
<template> | |||
<div v-bind:style="avatarStyle" class="passwordProfile__avatar">{{firstLetter}}</div> | |||
<div v-bind:style="avatarStyle" class="passwordProfile__avatar"> | |||
{{ firstLetter }} | |||
</div> | |||
</template> | |||
<script> | |||
export default { | |||
@@ -82,7 +82,7 @@ export default { | |||
} | |||
}, | |||
watch: { | |||
site: function (newValue) { | |||
site: function(newValue) { | |||
const suggestions = getSuggestions(newValue).map(suggestion => { | |||
return { site: suggestion, suggestion: true, login: "" }; | |||
}); | |||
@@ -1,44 +1,76 @@ | |||
<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> | |||
<template> | |||
<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="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 class="col-8 text-right"> | |||
<span v-if="saved && isAuthenticated"> | |||
<small><i class="fa fa-lg fa-check pl-3"></i> saved</small> | |||
</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> | |||
</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> | |||
</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> | |||
</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> | |||
</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> | |||
</router-link> | |||
</div> | |||
@@ -47,32 +79,29 @@ | |||
</div> | |||
</template> | |||
<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> |
@@ -34,11 +34,15 @@ | |||
<div v-if="message.text"> | |||
<div | |||
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="col-12"> | |||
<small>{{message.text}}</small> | |||
<small>{{ message.text }}</small> | |||
<span class="close-notification" v-on:click="hideMessage"> | |||
<i class="fa fa-close"></i> | |||
</span> | |||
@@ -24,9 +24,9 @@ | |||
<div class="passwordProfile__info" v-on:click="setPassword()"> | |||
<avatar v-bind:name="password.site"></avatar> | |||
<div class="passwordProfile__meta"> | |||
<b>{{password.site}}</b> | |||
<b>{{ password.site }}</b> | |||
<br /> | |||
{{password.login}} | |||
{{ password.login }} | |||
</div> | |||
</div> | |||
<div class="passwordProfile__actions"> | |||
@@ -1,10 +1,20 @@ | |||
<template> | |||
<div style="display: none;"> | |||
<label for="username"> | |||
<input type="text" id="username" name="username" autocomplete="username"> | |||
<input | |||
type="text" | |||
id="username" | |||
name="username" | |||
autocomplete="username" | |||
/> | |||
</label> | |||
<label for="password"> | |||
<input type="password" id="password" name="password" autocomplete="current-password"> | |||
<input | |||
type="password" | |||
id="password" | |||
name="password" | |||
autocomplete="current-password" | |||
/> | |||
</label> | |||
</div> | |||
</template> |
@@ -3,36 +3,60 @@ import * as urlParser from "./url-parser"; | |||
test("cleanUrl", () => { | |||
expect("lesspass.com").toBe(urlParser.cleanUrl("https://lesspass.com/#!/")); | |||
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("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("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("chrome://extensions/")); | |||
}); | |||
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( | |||
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.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", () => { | |||
@@ -90,7 +90,10 @@ test("SET_PASSWORDS", () => { | |||
test("DELETE_PASSWORD", () => { | |||
const DELETE_PASSWORD = mutations[types.DELETE_PASSWORD]; | |||
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); | |||
DELETE_PASSWORD(state, { id: "1" }); | |||
@@ -100,7 +103,10 @@ test("DELETE_PASSWORD", () => { | |||
test("DELETE_PASSWORD replace state.password with state.defaultPassword", () => { | |||
const DELETE_PASSWORD = mutations[types.DELETE_PASSWORD]; | |||
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 }, | |||
defaultPassword: { length: 16 } | |||
}; | |||
@@ -75,7 +75,7 @@ export default { | |||
this.$router.push({ name: "home" }).catch(() => {}); | |||
}, | |||
changePassword: async function() { | |||
if (!this.email) { | |||
if (!this.email) { | |||
message.error(this.$t("EmailRequiredError", "Email is required")); | |||
return; | |||
} | |||
@@ -112,7 +112,9 @@ export default { | |||
var siteMatch = password.site.match(new RegExp(this.searchQuery, "i")); | |||
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( | |||
this.pagination.currentPage * this.pagination.perPage - | |||
this.pagination.perPage, | |||