├── .babelrc
├── .editorconfig
├── .gitignore
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── src
├── App.spec.js
├── App.vue
├── features
│ └── auth
│ │ ├── main.vue
│ │ ├── sign-in.spec.js
│ │ ├── sign-in.vue
│ │ ├── sign-up.vue
│ │ └── styles.css
├── main.js
└── plugins
│ └── event-bus.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", { "modules": false }],
4 | "stage-3"
5 | ],
6 | "env": {
7 | "test": {
8 | "presets": [
9 | ["env", { "targets": { "node": "current" }}]
10 | ]
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log
5 | yarn-error.log
6 |
7 | # Editor directories and files
8 | .idea
9 | *.suo
10 | *.ntvs*
11 | *.njsproj
12 | *.sln
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # login
2 |
3 | > A Vue.js project
4 |
5 | ## Build Setup
6 |
7 | ``` bash
8 | # install dependencies
9 | npm install
10 |
11 | # serve with hot reload at localhost:8080
12 | npm run dev
13 |
14 | # build for production with minification
15 | npm run build
16 | ```
17 |
18 | For detailed explanation on how things work, consult the [docs for vue-loader](http://vuejs.github.io/vue-loader).
19 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | login
6 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "login",
3 | "description": "A Vue.js project",
4 | "version": "1.0.0",
5 | "author": "Fabio Vedovelli ",
6 | "license": "MIT",
7 | "private": true,
8 | "scripts": {
9 | "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
10 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
11 | "test": "jest"
12 | },
13 | "dependencies": {
14 | "vue": "^2.5.11",
15 | "vuelidate": "^0.6.1"
16 | },
17 | "browserslist": [
18 | "> 1%",
19 | "last 2 versions",
20 | "not ie <= 8"
21 | ],
22 | "devDependencies": {
23 | "@vue/test-utils": "^1.0.0-beta.12",
24 | "babel-core": "^6.26.0",
25 | "babel-loader": "^7.1.2",
26 | "babel-preset-env": "^1.6.0",
27 | "babel-preset-stage-3": "^6.24.1",
28 | "cross-env": "^5.0.5",
29 | "css-loader": "^0.28.7",
30 | "file-loader": "^1.1.4",
31 | "jest": "^22.4.2",
32 | "vue-jest": "^2.1.0",
33 | "vue-loader": "^13.0.5",
34 | "vue-template-compiler": "^2.4.4",
35 | "webpack": "^3.6.0",
36 | "webpack-dev-server": "^2.9.1"
37 | },
38 | "jest": {
39 | "moduleNameMapper": {
40 | "^@/(.*)$": "/src/$1"
41 | },
42 | "moduleFileExtensions": [
43 | "js",
44 | "json",
45 | "vue"
46 | ],
47 | "transform": {
48 | ".*\\.(vue)$": "/node_modules/vue-jest",
49 | "^.+\\.js$": "/node_modules/babel-jest"
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/App.spec.js:
--------------------------------------------------------------------------------
1 |
2 | import { shallow } from '@vue/test-utils'
3 | import App from './App'
4 |
5 | describe('App', () => {
6 | test('é uma instância do Vue', () => {
7 | const wrapper = shallow(App)
8 | expect(wrapper.isVueInstance()).toBeTruthy()
9 | })
10 | })
11 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/features/auth/main.vue:
--------------------------------------------------------------------------------
1 |
2 |
30 |
31 |
32 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/src/features/auth/sign-in.spec.js:
--------------------------------------------------------------------------------
1 |
2 | import Vue from 'vue'
3 | import Vuelidate from 'vuelidate'
4 | import EventBus from '@/plugins/event-bus'
5 | import { shallow } from '@vue/test-utils'
6 |
7 | Vue.use(EventBus)
8 | Vue.use(Vuelidate)
9 |
10 | import Signin from './sign-in'
11 |
12 | describe('Signin', () => {
13 | let wrapper
14 |
15 | beforeAll(() => {
16 | wrapper = shallow(Signin)
17 | })
18 |
19 | test('Verifica o estado da propriedade username no validation model quando input é preenchido', () => {
20 | setData(wrapper)
21 | const field = wrapper.find('#sign-in-user')
22 | field.trigger('input')
23 | expect(wrapper.vm.$v.username.$dirty).toBeTruthy()
24 | resetData(wrapper)
25 | })
26 |
27 | test('Todas as propriedades e ferramentas voltam ao estado inicial quando reset() for executado', () => {
28 | const { username, email, password } = wrapper.vm.$data
29 |
30 | setData(wrapper)
31 | resetData(wrapper)
32 |
33 | expect(username).toBe('')
34 | expect(email).toBe('')
35 | expect(password).toBe('')
36 | expect(wrapper.vm.$v.$invalid).toBeTruthy()
37 | })
38 |
39 | test('As propriedades corretas estão presentes no estado do componente', () => {
40 | const expected = ['username', 'email', 'password', 'keepSignedIn']
41 | const received = Object.keys(wrapper.vm.$data)
42 | expect(expected).toEqual(received)
43 | })
44 |
45 | test('O evento do-sign-in é disparado quando o vm.submit() é executado', () => {
46 | setData(wrapper)
47 | wrapper.vm.submit()
48 | const data = wrapper.emitted('do-sign-in')[0][0]
49 | expect(data).toEqual({
50 | email: 'vedovelli@gmail.com',
51 | username: 'vedovelli',
52 | password: 123456,
53 | keepSignedIn: true
54 | })
55 | })
56 |
57 | test('Se o estado do validation model é $invalid == false quando todos os campos obrigatórios forem preenchidos', () => {
58 | setData(wrapper)
59 | expect(wrapper.vm.$v.$invalid).toBeFalsy()
60 | })
61 |
62 | test('Se o estado do validation model é inicialmente $invalid == true', () => {
63 | resetData(wrapper)
64 | expect(wrapper.vm.$v.$invalid).toBeTruthy()
65 | })
66 |
67 | test('Componente é uma instância do Vue', () => {
68 | expect(wrapper.isVueInstance()).toBeTruthy()
69 | })
70 |
71 | const setData = wrapper => wrapper.setData({
72 | username: 'vedovelli',
73 | email: 'vedovelli@gmail.com',
74 | password: 123456
75 | })
76 |
77 | const resetData = wrapper => wrapper.setData({
78 | username: '',
79 | email: '',
80 | password: ''
81 | })
82 | })
83 |
--------------------------------------------------------------------------------
/src/features/auth/sign-in.vue:
--------------------------------------------------------------------------------
1 |
2 |
43 |
44 |
45 |
84 |
85 |
--------------------------------------------------------------------------------
/src/features/auth/sign-up.vue:
--------------------------------------------------------------------------------
1 |
2 |
52 |
53 |
54 |
79 |
80 |
--------------------------------------------------------------------------------
/src/features/auth/styles.css:
--------------------------------------------------------------------------------
1 | .login-wrap{
2 | width:100%;
3 | margin:auto;
4 | max-width:525px;
5 | min-height:670px;
6 | position:relative;
7 | background:url(https://raw.githubusercontent.com/khadkamhn/day-01-login-form/master/img/bg.jpg) no-repeat center;
8 | box-shadow:0 12px 15px 0 rgba(0,0,0,.24),0 17px 50px 0 rgba(0,0,0,.19);
9 | }
10 | .login-html{
11 | width:100%;
12 | height:100%;
13 | position:absolute;
14 | padding:90px 70px 50px 70px;
15 | background:rgba(40,57,101,.9);
16 | }
17 | .login-html .sign-in-htm,
18 | .login-html .sign-up-htm{
19 | top:0;
20 | left:0;
21 | right:0;
22 | bottom:0;
23 | position:absolute;
24 | transform:rotateY(180deg);
25 | backface-visibility:hidden;
26 | transition:all .4s linear;
27 | }
28 | .login-html .sign-in,
29 | .login-html .sign-up,
30 | .login-form .group .check{
31 | display:none;
32 | }
33 | .login-html .tab,
34 | .login-form .group .label,
35 | .login-form .group .button{
36 | text-transform:uppercase;
37 | }
38 | .login-html .tab{
39 | font-size:22px;
40 | margin-right:15px;
41 | padding-bottom:5px;
42 | margin:0 15px 10px 0;
43 | display:inline-block;
44 | border-bottom:2px solid transparent;
45 | }
46 | .login-html .sign-in:checked + .tab,
47 | .login-html .sign-up:checked + .tab{
48 | color:#fff;
49 | border-color:#1161ee;
50 | }
51 | .login-form{
52 | min-height:345px;
53 | position:relative;
54 | perspective:1000px;
55 | transform-style:preserve-3d;
56 | }
57 | .login-form .group{
58 | margin-bottom:15px;
59 | }
60 |
61 | .login-form .group input[type="text"].invalid,
62 | .login-form .group input[type="password"].invalid {
63 | border: red 1px solid;
64 | }
65 |
66 | .login-form .group label.invalid {
67 | color: red;
68 | }
69 |
70 | .login-form .group .label,
71 | .login-form .group .input,
72 | .login-form .group .button{
73 | width:100%;
74 | color:#fff;
75 | display:block;
76 | }
77 | .login-form .group .input,
78 | .login-form .group .button{
79 | border:none;
80 | padding:15px 20px;
81 | border-radius:25px;
82 | background:rgba(255,255,255,.1);
83 | }
84 | .login-form .group input[data-type="password"]{
85 | text-security:circle;
86 | -webkit-text-security:circle;
87 | }
88 | .login-form .group .label{
89 | color:#aaa;
90 | font-size:12px;
91 | }
92 | .login-form .group .button{
93 | background:#1161ee;
94 | }
95 | .login-form .group label .icon{
96 | width:15px;
97 | height:15px;
98 | border-radius:2px;
99 | position:relative;
100 | display:inline-block;
101 | background:rgba(255,255,255,.1);
102 | }
103 | .login-form .group label .icon:before,
104 | .login-form .group label .icon:after{
105 | content:'';
106 | width:10px;
107 | height:2px;
108 | background:#fff;
109 | position:absolute;
110 | transition:all .2s ease-in-out 0s;
111 | }
112 | .login-form .group label .icon:before{
113 | left:3px;
114 | width:5px;
115 | bottom:6px;
116 | transform:scale(0) rotate(0);
117 | }
118 | .login-form .group label .icon:after{
119 | top:6px;
120 | right:0;
121 | transform:scale(0) rotate(0);
122 | }
123 | .login-form .group .check:checked + label{
124 | color:#fff;
125 | }
126 | .login-form .group .check:checked + label .icon{
127 | background:#1161ee;
128 | }
129 | .login-form .group .check:checked + label .icon:before{
130 | transform:scale(1) rotate(45deg);
131 | }
132 | .login-form .group .check:checked + label .icon:after{
133 | transform:scale(1) rotate(-45deg);
134 | }
135 | .login-html .sign-in:checked + .tab + .sign-up + .tab + .login-form .sign-in-htm{
136 | transform:rotate(0);
137 | }
138 | .login-html .sign-up:checked + .tab + .login-form .sign-up-htm{
139 | transform:rotate(0);
140 | }
141 |
142 | .hr{
143 | height:2px;
144 | margin:60px 0 50px 0;
145 | background:rgba(255,255,255,.2);
146 | }
147 | .foot-lnk{
148 | text-align:center;
149 | }
150 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import EventBus from './plugins/event-bus'
4 | import Vuelidate from 'vuelidate'
5 |
6 | Vue.use(EventBus)
7 | Vue.use(Vuelidate)
8 |
9 | new Vue({
10 | el: '#app',
11 | render: h => h(App)
12 | })
13 |
--------------------------------------------------------------------------------
/src/plugins/event-bus.js:
--------------------------------------------------------------------------------
1 |
2 | import Vue from 'Vue'
3 |
4 | const bus = new Vue()
5 |
6 | export default function install(Vue) {
7 | Object.defineProperties(Vue.prototype, {
8 | $bus: {
9 | get() {
10 | return bus
11 | }
12 | }
13 | })
14 | }
15 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var webpack = require('webpack')
3 |
4 | module.exports = {
5 | entry: './src/main.js',
6 | output: {
7 | path: path.resolve(__dirname, './dist'),
8 | publicPath: '/dist/',
9 | filename: 'build.js'
10 | },
11 | module: {
12 | rules: [
13 | {
14 | test: /\.css$/,
15 | use: [
16 | 'vue-style-loader',
17 | 'css-loader'
18 | ],
19 | }, {
20 | test: /\.vue$/,
21 | loader: 'vue-loader',
22 | options: {
23 | loaders: {
24 | }
25 | // other vue-loader options go here
26 | }
27 | },
28 | {
29 | test: /\.js$/,
30 | loader: 'babel-loader',
31 | exclude: /node_modules/
32 | },
33 | {
34 | test: /\.(png|jpg|gif|svg)$/,
35 | loader: 'file-loader',
36 | options: {
37 | name: '[name].[ext]?[hash]'
38 | }
39 | }
40 | ]
41 | },
42 | resolve: {
43 | alias: {
44 | 'vue$': 'vue/dist/vue.esm.js'
45 | },
46 | extensions: ['*', '.js', '.vue', '.json']
47 | },
48 | devServer: {
49 | historyApiFallback: true,
50 | noInfo: true,
51 | overlay: true
52 | },
53 | performance: {
54 | hints: false
55 | },
56 | devtool: '#eval-source-map'
57 | }
58 |
59 | if (process.env.NODE_ENV === 'production') {
60 | module.exports.devtool = '#source-map'
61 | // http://vue-loader.vuejs.org/en/workflow/production.html
62 | module.exports.plugins = (module.exports.plugins || []).concat([
63 | new webpack.DefinePlugin({
64 | 'process.env': {
65 | NODE_ENV: '"production"'
66 | }
67 | }),
68 | new webpack.optimize.UglifyJsPlugin({
69 | sourceMap: true,
70 | compress: {
71 | warnings: false
72 | }
73 | }),
74 | new webpack.LoaderOptionsPlugin({
75 | minimize: true
76 | })
77 | ])
78 | }
79 |
--------------------------------------------------------------------------------