소스 검색

Refactor Settings and Password Generator pages

pull/483/head
Guillaume Vincent 5 년 전
부모
커밋
665d68f044
13개의 변경된 파일257개의 추가작업 그리고 387개의 파일을 삭제
  1. +1
    -0
      package.json
  2. +11
    -12
      packages/lesspass-pure/cypress/integration/passwordGeneration.spec.js
  3. +1
    -0
      packages/lesspass-pure/cypress/support/index.js
  4. +0
    -4
      packages/lesspass-pure/src/LessPass.vue
  5. +1
    -0
      packages/lesspass-pure/src/components/InputSite.vue
  6. +1
    -0
      packages/lesspass-pure/src/components/MasterPassword.vue
  7. +1
    -1
      packages/lesspass-pure/src/components/Menu.vue
  8. +118
    -76
      packages/lesspass-pure/src/components/Options.vue
  9. +0
    -9
      packages/lesspass-pure/src/store/getters.js
  10. +0
    -56
      packages/lesspass-pure/src/store/getters.test.js
  11. +53
    -69
      packages/lesspass-pure/src/views/PasswordGenerator.vue
  12. +38
    -29
      packages/lesspass-pure/src/views/Settings.vue
  13. +32
    -131
      yarn.lock

+ 1
- 0
package.json 파일 보기

@@ -31,6 +31,7 @@
"cross-env": "^5.2.0",
"css-loader": "^3.0.0",
"cypress": "^3.4.0",
"cypress-plugin-tab": "^1.0.3",
"express": "^4.17.1",
"file-loader": "^4.0.0",
"html-webpack-plugin": "^3.2.0",


+ 11
- 12
packages/lesspass-pure/cypress/integration/passwordGeneration.spec.js 파일 보기

@@ -1,9 +1,6 @@
describe("Password Generation", function() {
it("can't decrease counter under 0", function() {
cy.visit("/");
cy.get(".showOptions__btn")
.first()
.click();
cy.get("#decreaseCounter__btn").click();
cy.get("#decreaseCounter__btn").click();
cy.get("#passwordCounter").should("have.value", "1");
@@ -28,9 +25,6 @@ describe("Password Generation", function() {
cy.get("#fingerprint .fa-plane").should("be.visible");
cy.get("#generatePassword__btn").click();
cy.get("#generated-password").should("have.value", "hjV@\\5ULp3bIs,6B");
cy.get(".showOptions__btn")
.first()
.click();
cy.get("#decreaseLength__btn").click();
cy.get("#passwordLength").should("have.value", "15");
cy.get("#increaseLength__btn").click();
@@ -75,9 +69,6 @@ describe("Password Generation", function() {
});
it("should have a min length of 5 and max length of 35", function() {
cy.visit("/");
cy.get(".showOptions__btn")
.first()
.click();
cy.get("#passwordLength")
.clear()
.type("35");
@@ -94,9 +85,6 @@ describe("Password Generation", function() {
cy.get("#siteField").type("site");
cy.get("#login").type("login");
cy.get("#passwordField").type("test");
cy.get(".showOptions__btn")
.first()
.click();
cy.get("#passwordCounter")
.clear()
.type("10");
@@ -140,4 +128,15 @@ describe("Password Generation", function() {
cy.get("#copyPasswordButton").should("not.be.visible");
cy.get("#generatePassword__btn").should("be.visible");
});
it("should generate password with 2 tabs and enter", function() {
cy.visit("/");
cy.get("#siteField")
.type("lesspass.com")
.tab()
.type("test@lesspass.com")
.tab()
.type("test@lesspass.com")
.type("{enter}");
cy.get("#generated-password").should("have.value", "hjV@\\5ULp3bIs,6B");
});
});

+ 1
- 0
packages/lesspass-pure/cypress/support/index.js 파일 보기

@@ -1 +1,2 @@
import "cypress-plugin-tab";
import "./commands";

+ 0
- 4
packages/lesspass-pure/src/LessPass.vue 파일 보기

@@ -58,10 +58,6 @@ button,
.right-addon input {
padding-right: 30px;
}

.border-blue {
border-color: #007bff !important;
}
</style>
<template>
<div id="lesspass" class="card">


+ 1
- 0
packages/lesspass-pure/src/components/InputSite.vue 파일 보기

@@ -17,6 +17,7 @@
name="siteField"
ref="siteField"
class="form-control awesomplete"
tabindex="0"
autocorrect="off"
autocapitalize="none"
v-bind:placeholder="label"


+ 1
- 0
packages/lesspass-pure/src/components/MasterPassword.vue 파일 보기

@@ -25,6 +25,7 @@
ref="passwordField"
type="password"
class="form-control"
tabindex="0"
autocorrect="off"
autocapitalize="none"
v-bind:value="value"


+ 1
- 1
packages/lesspass-pure/src/components/Menu.vue 파일 보기

@@ -32,7 +32,7 @@
<router-link class="white-link pl-3" :to="{ name: 'passwords'}" v-if="isAuthenticated">
<i class="fa fa-lg fa-key"></i>
</router-link>
<router-link class="white-link pl-3" :to="{ name: 'options'}" v-if="false">
<router-link class="white-link pl-3" :to="{ name: 'settings'}">
<i class="fa fa-lg fa-cog"></i>
</router-link>
<button class="white-link btn btn-link p-0 m-0 pl-3" type="button" v-if="isAuthenticated"


+ 118
- 76
packages/lesspass-pure/src/components/Options.vue 파일 보기

@@ -1,13 +1,13 @@
<style>
#options input[type="number"] {
-moz-appearance: textfield;
}
#options input[type="number"] {
-moz-appearance: textfield;
}

#options input[type="number"]::-webkit-outer-spin-button,
#options input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
#options input[type="number"]::-webkit-outer-spin-button,
#options input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
</style>
<template>
<div id="options">
@@ -15,41 +15,49 @@
<div class="col-12">
<div class="row">
<div class="col">
<label for="types">{{ $t('Advanced 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"
v-bind:class="{'btn-primary':password.lowercase===true, 'btn-secondary':password.lowercase===false}"
v-on:click="password.lowercase=!password.lowercase">
a-z
</button>
<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>
<div class="col-3">
<button id="uppercase__btn"
type="button" class="btn btn-block btn-sm px-0"
v-bind:class="{'btn-primary':password.uppercase===true, 'btn-secondary':password.uppercase===false}"
v-on:click="password.uppercase=!password.uppercase">
A-Z
</button>
<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>
<div class="col-3">
<button id="numbers__btn"
type="button" class="btn btn-block btn-sm px-0"
v-bind:class="{'btn-primary':password.numbers===true,'btn-secondary':password.numbers===false}"
v-on:click="password.numbers=!password.numbers">
0-9
</button>
<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>
<div class="col-3">
<button id="symbols__btn"
type="button" class="btn btn-block btn-sm px-0"
v-bind:class="{'btn-primary':password.symbols===true,'btn-secondary':password.symbols===false}"
v-on:click="password.symbols=!password.symbols">
%!@
</button>
<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>
</div>
</div>
@@ -58,48 +66,83 @@
<div class="col-5 col-sm-4">
<label for="passwordLength">{{ $t('Length') }}</label>
<div class="input-group input-group-sm">
<span class="input-group-btn" v-on:click="password.length=decrement(password.length, {min: 5, max: 35})">
<button id="decreaseLength__btn" class="btn btn-primary btn-sm border-blue px-2" type="button">
<small><i class="fa fa-minus"></i></small>
<span class="input-group-btn">
<button
id="decreaseLength__btn"
class="btn btn-primary btn-sm px-2"
tabindex="1"
type="button"
v-on:click="options.length=decrement(options.length, {min: 5, max: 35})"
>
<small>
<i class="fa fa-minus"></i>
</small>
</button>
</span>
<input id="passwordLength"
class="form-control form-control-sm"
type="number"
min="5"
max="35"
v-model.number="password.length">
<span class="input-group-btn"
v-on:click="password.length=increment(password.length, {min: 5, max: 35})">
<button id="increaseLength__btn" class="btn btn-primary btn-sm border-blue px-2" type="button">
<small><i class="fa fa-plus"></i></small>
<input
id="passwordLength"
class="form-control form-control-sm"
tabindex="1"
type="number"
min="5"
max="35"
v-model.number="options.length"
/>
<span class="input-group-btn">
<button
id="increaseLength__btn"
class="btn btn-primary btn-sm px-2"
tabindex="1"
type="button"
v-on:click="options.length=increment(options.length, {min: 5, max: 35})"
>
<small>
<i class="fa fa-plus"></i>
</small>
</button>
</span>
</div>
</div>
<div class="col-5 col-sm-4">
<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 password.')"
data-balloon-pos="up">
{{$t('Counter')}}
</label>
<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.')"
data-balloon-pos="up"
>{{$t('Counter')}}</label>
<div class="input-group input-group-sm">
<span id="decreaseCounter__btn" class="input-group-btn"
v-on:click="password.counter=decrement(password.counter, {min: 1})">
<button class="btn btn-primary btn-sm border-blue px-2" type="button">
<small><i class="fa fa-minus"></i></small>
<span class="input-group-btn">
<button
id="decreaseCounter__btn"
class="btn btn-primary btn-sm px-2"
tabindex="1"
type="button"
v-on:click="options.counter=decrement(options.counter, {min: 1})"
>
<small>
<i class="fa fa-minus"></i>
</small>
</button>
</span>
<input id="passwordCounter"
class="form-control form-control-sm"
type="number"
min="1"
v-model.number="password.counter">
<span id="increaseCounter__btn" class="input-group-btn"
v-on:click="password.counter=increment(password.counter, {min: 1})">
<button class="btn btn-primary btn-sm border-blue px-2" type="button">
<small><i class="fa fa-plus"></i></small>
<input
id="passwordCounter"
class="form-control form-control-sm"
tabindex="1"
type="number"
min="1"
v-model.number="options.counter"
/>
<span class="input-group-btn">
<button
id="increaseCounter__btn"
class="btn btn-primary btn-sm px-2"
tabindex="1"
type="button"
v-on:click="options.counter=increment(options.counter, {min: 1})"
>
<small>
<i class="fa fa-plus"></i>
</small>
</button>
</span>
</div>
@@ -109,17 +152,16 @@
</template>

<script type="text/ecmascript-6">
import {increment, decrement} from "../services/form-validator";
import { mapState} from 'vuex';
import { increment, decrement } from "../services/form-validator";

export default {
name: 'options',
computed: {
...mapState(['password']),
},
methods: {
decrement,
increment
}
export default {
name: "options",
props: {
options: Object
},
methods: {
decrement,
increment
}
};
</script>

+ 0
- 9
packages/lesspass-pure/src/store/getters.js 파일 보기

@@ -10,12 +10,3 @@ export const passwordURL = state => {
const encodedPasswordProfile = encodeURIComponent(base64PasswordProfile);
return `${state.baseURL}/#/?passwordProfileEncoded=${encodedPasswordProfile}`;
};

export const isDefaultProfile = state => {
for (let key in defaultOptions) {
if (state.defaultPassword[key] !== state.password[key]) {
return false;
}
}
return true;
};

+ 0
- 56
packages/lesspass-pure/src/store/getters.test.js 파일 보기

@@ -34,62 +34,6 @@ test("passwordURL encode uri component", () => {
);
});

test("isDefaultProfile", () => {
const state = {
password: {
login: "test@example.org",
site: "example.org",
uppercase: true,
lowercase: true,
numbers: true,
symbols: true,
length: 16,
counter: 1,
version: 2
},
defaultPassword: {
login: "",
site: "",
uppercase: true,
lowercase: true,
numbers: true,
symbols: true,
length: 16,
counter: 1,
version: 2
}
};
expect(getters.isDefaultProfile(state)).toBe(true);
});

test("isDefaultProfile false", () => {
const state = {
password: {
login: "test@example.org",
site: "example.org",
uppercase: true,
lowercase: true,
numbers: true,
symbols: false,
length: 32,
counter: 1,
version: 1
},
defaultPassword: {
login: "",
site: "",
uppercase: true,
lowercase: true,
numbers: true,
symbols: true,
length: 16,
counter: 1,
version: 2
}
};
expect(getters.isDefaultProfile(state)).toBe(false);
});

test("isAuthenticated", () => {
const state = {
authenticated: true


+ 53
- 69
packages/lesspass-pure/src/views/PasswordGenerator.vue 파일 보기

@@ -34,6 +34,7 @@ div.awesomplete > ul {
name="login"
ref="login"
class="form-control"
tabindex="0"
autocomplete="off"
autocorrect="off"
autocapitalize="none"
@@ -50,75 +51,59 @@ div.awesomplete > ul {
v-bind:label="$t('Master Password')"
></master-password>
</div>
<div class="form-group" v-bind:class="{ 'mb-0': !showOptions }">
<div v-if="!passwordGenerated">
<button
id="generatePassword__btn"
type="submit"
class="btn btn-primary border-blue"
>{{ $t('Generate') }}</button>
<button
type="button"
class="btn btn-secondary pull-right showOptions__btn"
v-show="!passwordGenerated"
v-on:click="showOptions =! showOptions"
>
<i class="fa fa-sliders"></i>
</button>
</div>
<div class="btn-group" v-show="passwordGenerated">
<div class="input-group">
<span class="input-group-btn">
<button
id="copyPasswordButton"
class="btn btn-primary border-blue"
type="button"
v-on:click="copyPassword()"
>
<i class="fa fa-clipboard"></i>
</button>
</span>
<input
id="generated-password"
type="password"
class="form-control"
tabindex="-1"
ref="passwordGenerated"
v-bind:value="passwordGenerated"
/>
<span class="input-group-btn">
<button
id="revealGeneratedPassword"
type="button"
class="btn btn-secondary"
v-on:click="togglePasswordType($refs.passwordGenerated)"
>
<i class="fa fa-eye"></i>
</button>
</span>
<span class="input-group-btn">
<button
id="sharePasswordProfileButton"
type="button"
class="btn btn-secondary"
v-on:click="sharePasswordProfile()"
>
<i class="fa fa-share-alt pointer"></i>
</button>
</span>
<span class="input-group-btn">
<button
type="button"
class="btn btn-secondary showOptions__btn"
v-on:click="showOptions =! showOptions"
>
<i class="fa fa-sliders"></i>
</button>
</span>
</div>
<options v-bind:options="password"></options>
<div class="form-group mt-4 mb-0">
<button
id="generatePassword__btn"
type="submit"
tabindex="0"
class="btn btn-primary btn-block"
v-if="!passwordGenerated"
>{{ $t('Generate') }}</button>
<div class="input-group" v-show="passwordGenerated">
<span class="input-group-btn">
<button
id="copyPasswordButton"
class="btn btn-primary"
tabindex="0"
type="button"
v-on:click="copyPassword()"
>
<i class="fa fa-clipboard"></i>
</button>
</span>
<input
id="generated-password"
type="password"
class="form-control"
tabindex="-1"
ref="passwordGenerated"
v-bind:value="passwordGenerated"
/>
<span class="input-group-btn">
<button
id="revealGeneratedPassword"
type="button"
class="btn btn-secondary"
tabindex="0"
v-on:click="togglePasswordType($refs.passwordGenerated)"
>
<i class="fa fa-eye"></i>
</button>
</span>
<span class="input-group-btn">
<button
id="sharePasswordProfileButton"
type="button"
class="btn btn-secondary"
tabindex="0"
v-on:click="sharePasswordProfile()"
>
<i class="fa fa-share-alt pointer"></i>
</button>
</span>
</div>
</div>
<options v-if="showOptions || !isDefaultProfile"></options>
</form>
</template>
<script type="text/ecmascript-6">
@@ -143,7 +128,7 @@ export default {
},
computed: {
...mapState(["password", "passwords"]),
...mapGetters(["passwordURL", "isDefaultProfile"])
...mapGetters(["passwordURL"])
},
beforeMount() {
this.$store.dispatch("getPasswords").then(() => {
@@ -162,7 +147,6 @@ export default {
},
data() {
return {
showOptions: false,
masterPassword: "",
passwordGenerated: "",
cleanTimeout: null


+ 38
- 29
packages/lesspass-pure/src/views/Settings.vue 파일 보기

@@ -1,38 +1,47 @@
<template>
<form id="lesspass-options-form" novalidate v-on:submit.prevent="saveAndExit">
<div class="form-group">
<label for="login" class="sr-only">{{ $t('Default login') }}</label>
<div class="inner-addon left-addon">
<i class="fa fa-user"></i>
<input
id="login"
type="text"
name="login"
ref="login"
class="form-control"
autocomplete="off"
autocorrect="off"
autocapitalize="none"
v-bind:placeholder="$t('Default login')"
v-model="defaultPassword.login"
/>
<div>
<h5>{{$t('Options by default')}}</h5>
<form id="lesspass-options-form" novalidate v-on:submit.prevent="saveAndExit">
<div class="form-group">
<label for="login">{{ $t('Login') }}</label>
<div class="inner-addon left-addon">
<i class="fa fa-user"></i>
<input
id="login"
type="text"
name="login"
ref="login"
class="form-control"
autocomplete="off"
autocorrect="off"
autocapitalize="none"
v-bind:placeholder="$t('Login')"
v-model="defaultPassword.login"
/>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary">{{$t('Save')}}</button>
</form>
<options v-bind:options="defaultPassword"></options>
<button type="submit" class="btn btn-primary btn-block mt-4">{{$t('Save')}}</button>
</form>
</div>
</template>

<script>
import { mapState } from "vuex";
import Options from "../components/Options.vue";
import { mapState } from "vuex";

export default {
computed: mapState(["defaultPassword"]),
methods: {
saveAndExit() {
this.$store.dispatch('saveDefaultOptions', this.defaultPassword)
.then(this.$store.dispatch('resetPassword'))
.then(() => this.$router.push({name: 'home'}));
}
export default {
computed: mapState(["defaultPassword"]),
components: {
Options
},
methods: {
saveAndExit() {
this.$store
.dispatch("saveDefaultOptions", this.defaultPassword)
.then(this.$store.dispatch("resetPassword"))
.then(() => this.$router.push({ name: "home" }));
}
}
};
</script>

+ 32
- 131
yarn.lock 파일 보기

@@ -232,7 +232,7 @@
esutils "^2.0.2"
js-tokens "^4.0.0"

"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.6.0", "@babel/parser@^7.6.3", "@babel/parser@^7.6.4":
"@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.6.0", "@babel/parser@^7.6.3", "@babel/parser@^7.6.4":
version "7.6.4"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.4.tgz#cb9b36a7482110282d5cb6dd424ec9262b473d81"
integrity sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==
@@ -739,7 +739,7 @@
"@babel/parser" "^7.6.0"
"@babel/types" "^7.6.0"

"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.5", "@babel/traverse@^7.6.2", "@babel/traverse@^7.6.3":
"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.5", "@babel/traverse@^7.6.2", "@babel/traverse@^7.6.3":
version "7.6.3"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.6.3.tgz#66d7dba146b086703c0fb10dd588b7364cec47f9"
integrity sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==
@@ -1547,17 +1547,6 @@
babel-loader "^8.0.6"
webpack "^4.0.0"

"@vue/cli-plugin-eslint@^4.0.4":
version "4.0.4"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.0.4.tgz#7afddfdaf3bfdafdc826ed0c87edf41e67de3208"
integrity sha512-YpQQLxHxzEVvTqs5zs4KSwqjGF6ILJ0OwD+4mPjCQL0Ms742RzzHb2MTLR1GF3bwcComwgNDNmmzzQERFiWajA==
dependencies:
"@vue/cli-shared-utils" "^4.0.4"
eslint-loader "^2.1.2"
globby "^9.2.0"
webpack "^4.0.0"
yorkie "^2.0.0"

"@vue/cli-plugin-router@^4.0.4":
version "4.0.4"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-router/-/cli-plugin-router-4.0.4.tgz#e1010d3844519baad517f6050541711f6157942a"
@@ -1898,7 +1887,7 @@ acorn@^5.5.0, acorn@^5.5.3:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==

acorn@^6.0.1, acorn@^6.0.2, acorn@^6.0.7, acorn@^6.1.1, acorn@^6.2.1:
acorn@^6.0.1, acorn@^6.0.7, acorn@^6.1.1, acorn@^6.2.1:
version "6.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e"
integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==
@@ -2050,6 +2039,14 @@ ajv@^5.1.0:
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"

ally.js@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/ally.js/-/ally.js-1.4.1.tgz#9fb7e6ba58efac4ee9131cb29aa9ee3b540bcf1e"
integrity sha1-n7fmuljvrE7pExyymqnuO1QLzx4=
dependencies:
css.escape "^1.5.0"
platform "1.3.3"

alphanum-sort@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
@@ -2551,18 +2548,6 @@ babel-core@7.0.0-bridge.0:
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece"
integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==

babel-eslint@^10.0.3:
version "10.0.3"
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a"
integrity sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/parser" "^7.0.0"
"@babel/traverse" "^7.0.0"
"@babel/types" "^7.0.0"
eslint-visitor-keys "^1.0.0"
resolve "^1.12.0"

babel-jest@^24.9.0:
version "24.9.0"
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54"
@@ -4325,6 +4310,11 @@ css-what@2.1, css-what@^2.1.2:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==

css.escape@^1.5.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=

css@^2.1.0:
version "2.2.4"
resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
@@ -4454,6 +4444,13 @@ cyclist@^1.0.1:
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=

cypress-plugin-tab@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/cypress-plugin-tab/-/cypress-plugin-tab-1.0.3.tgz#8a64a4c189c5d180dc0a97175097c0cc937c1213"
integrity sha512-uiadGT+IzoGxKCqYxQXyc5OhHkv4JAT5LWwNTs8R6pBJkBUUC/uZR2ys4jqhqX6mD/ZucAt5p+U9KMr8Ae4fQA==
dependencies:
ally.js "^1.4.1"

cypress@^3.4.0:
version "3.4.1"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.4.1.tgz#ca2e4e9864679da686c6a6189603efd409664c30"
@@ -5363,17 +5360,6 @@ escope@^3.6.0:
esrecurse "^4.1.0"
estraverse "^4.1.1"

eslint-loader@^2.1.2:
version "2.2.1"
resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.2.1.tgz#28b9c12da54057af0845e2a6112701a2f6bf8337"
integrity sha512-RLgV9hoCVsMLvOxCuNjdqOrUqIj9oJg8hF44vzJaYqsAHuY9G2YAeN3joQ9nxP0p5Th9iFSIpKo+SD8KISxXRg==
dependencies:
loader-fs-cache "^1.0.0"
loader-utils "^1.0.2"
object-assign "^4.0.1"
object-hash "^1.1.4"
rimraf "^2.6.1"

eslint-plugin-no-unsafe-innerhtml@1.0.16:
version "1.0.16"
resolved "https://registry.yarnpkg.com/eslint-plugin-no-unsafe-innerhtml/-/eslint-plugin-no-unsafe-innerhtml-1.0.16.tgz#7d02878c8e9bf7916b88836d5ac122b42f151932"
@@ -5381,14 +5367,7 @@ eslint-plugin-no-unsafe-innerhtml@1.0.16:
dependencies:
eslint "^3.7.1"

eslint-plugin-vue@^5.0.0:
version "5.2.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-5.2.3.tgz#3ee7597d823b5478804b2feba9863b1b74273961"
integrity sha512-mGwMqbbJf0+VvpGR5Lllq0PMxvTdrZ/ZPjmhkacrCHbubJeJOt+T6E3HUzAifa2Mxi7RSdJfC9HFpOeSYVMMIw==
dependencies:
vue-eslint-parser "^5.0.0"

eslint-scope@^4.0.0, eslint-scope@^4.0.3:
eslint-scope@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==
@@ -5408,7 +5387,7 @@ eslint-visitor-keys@1.1.0, eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==

eslint@5.16.0, eslint@^5.16.0:
eslint@5.16.0:
version "5.16.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea"
integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==
@@ -5508,15 +5487,6 @@ espree@^3.4.0:
acorn "^5.5.0"
acorn-jsx "^3.0.0"

espree@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/espree/-/espree-4.1.0.tgz#728d5451e0fd156c04384a7ad89ed51ff54eb25f"
integrity sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==
dependencies:
acorn "^6.0.2"
acorn-jsx "^5.0.0"
eslint-visitor-keys "^1.0.0"

espree@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a"
@@ -5667,19 +5637,6 @@ execa@^0.7.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"

execa@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da"
integrity sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=
dependencies:
cross-spawn "^5.0.1"
get-stream "^3.0.0"
is-stream "^1.1.0"
npm-run-path "^2.0.0"
p-finally "^1.0.0"
signal-exit "^3.0.0"
strip-eof "^1.0.0"

execa@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
@@ -6090,15 +6047,6 @@ find-babel-config@^1.1.0:
json5 "^0.5.1"
path-exists "^3.0.0"

find-cache-dir@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9"
integrity sha1-yN765XyKUqinhPnjHFfHQumToLk=
dependencies:
commondir "^1.0.1"
mkdirp "^0.5.1"
pkg-dir "^1.0.0"

find-cache-dir@^2.0.0, find-cache-dir@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
@@ -7604,7 +7552,7 @@ is-callable@^1.1.3, is-callable@^1.1.4:
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==

is-ci@1.2.1, is-ci@^1.0.10:
is-ci@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c"
integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==
@@ -9078,14 +9026,6 @@ load-json-file@^4.0.0:
pify "^3.0.0"
strip-bom "^3.0.0"

loader-fs-cache@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz#54cedf6b727e1779fd8f01205f05f6e88706f086"
integrity sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw==
dependencies:
find-cache-dir "^0.1.1"
mkdirp "0.5.1"

loader-runner@^2.3.1, loader-runner@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
@@ -10116,11 +10056,6 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"

normalize-path@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379"
integrity sha1-MtDkcvkf80VwHBWoMRAY07CpA3k=

normalize-path@^2.0.0, normalize-path@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
@@ -10260,11 +10195,6 @@ object-copy@^0.1.0:
define-property "^0.2.5"
kind-of "^3.0.3"

object-hash@^1.1.4:
version "1.3.1"
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df"
integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==

object-inspect@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b"
@@ -11003,13 +10933,6 @@ pixelmatch@^4.0.0, pixelmatch@^4.0.2:
dependencies:
pngjs "^3.0.0"

pkg-dir@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
integrity sha1-ektQio1bstYp1EcFb/TpyTFM89Q=
dependencies:
find-up "^1.0.0"

pkg-dir@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
@@ -11031,6 +10954,11 @@ pkg-up@^2.0.0:
dependencies:
find-up "^2.1.0"

platform@1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.3.tgz#646c77011899870b6a0903e75e997e8e51da7461"
integrity sha1-ZGx3ARiZhwtqCQPnXpl+jlHadGE=

pluralize@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45"
@@ -12211,7 +12139,7 @@ resolve@1.1.7:
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=

resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1:
resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1:
version "1.12.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6"
integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==
@@ -13343,11 +13271,6 @@ strip-indent@^1.0.1:
dependencies:
get-stdin "^4.0.1"

strip-indent@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=

strip-json-comments@2.0.1, strip-json-comments@^2.0.0, strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@@ -14355,18 +14278,6 @@ void-elements@^2.0.0:
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=

vue-eslint-parser@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz#00f4e4da94ec974b821a26ff0ed0f7a78402b8a1"
integrity sha512-JlHVZwBBTNVvzmifwjpZYn0oPWH2SgWv5dojlZBsrhablDu95VFD+hriB1rQGwbD+bms6g+rAFhQHk6+NyiS6g==
dependencies:
debug "^4.1.0"
eslint-scope "^4.0.0"
eslint-visitor-keys "^1.0.0"
espree "^4.1.0"
esquery "^1.0.1"
lodash "^4.17.11"

vue-hot-reload-api@^2.3.0:
version "2.3.4"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
@@ -15149,16 +15060,6 @@ yeast@0.1.2:
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk=

yorkie@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/yorkie/-/yorkie-2.0.0.tgz#92411912d435214e12c51c2ae1093e54b6bb83d9"
integrity sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw==
dependencies:
execa "^0.8.0"
is-ci "^1.0.10"
normalize-path "^1.0.0"
strip-indent "^2.0.0"

zip-dir@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/zip-dir/-/zip-dir-1.0.2.tgz#253f907aead62a21acd8721d8b88032b2411c051"


불러오는 중...
취소
저장