Skip to content

Commit 7443b10

Browse files
committed
build registration form
1 parent 4ed561e commit 7443b10

File tree

8 files changed

+217
-24
lines changed

8 files changed

+217
-24
lines changed

front-end/package-lock.json

Lines changed: 17 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

front-end/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"test": "npm run test:unit && npm run test:e2e"
1212
},
1313
"dependencies": {
14+
"axios": "^0.18.0",
1415
"bootstrap": "^4.1.3",
1516
"jquery": "^3.3.1",
1617
"popper.js": "^1.14.4",
@@ -25,9 +26,10 @@
2526
"@vue/cli-plugin-unit-jest": "^3.0.1",
2627
"@vue/cli-service": "^3.0.1",
2728
"@vue/eslint-config-standard": "^3.0.1",
28-
"@vue/test-utils": "^1.0.0-beta.20",
29+
"@vue/test-utils": "^1.0.0-beta.24",
2930
"babel-core": "7.0.0-bridge.0",
3031
"babel-jest": "^23.0.1",
32+
"moxios": "^0.4.0",
3133
"node-sass": "^4.9.0",
3234
"sass-loader": "^7.0.1",
3335
"vue-template-compiler": "^2.5.17"

front-end/src/main.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@ import Vue from 'vue'
22
import App from './App.vue'
33
import router from './router'
44
import store from './store'
5+
import axios from 'axios'
6+
7+
// Bootstrap axios
8+
axios.defaults.baseURL = '/api'
9+
axios.defaults.headers.common.Accept = 'application/json'
10+
axios.interceptors.response.use(
11+
response => response,
12+
(error) => {
13+
return Promise.reject(error)
14+
}
15+
)
516

617
Vue.config.productionTip = false
718

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export default {
2+
register (detail) {
3+
return new Promise((resolve, reject) => {
4+
detail.emailAddress === 'sunny@local'
5+
? resolve({result: 'success'})
6+
: reject(new Error('User already exist'))
7+
})
8+
}
9+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import axios from 'axios'
2+
3+
export default {
4+
/**
5+
* Register a new user
6+
* @param {Object} detail registration detail
7+
*/
8+
register (detail) {
9+
return new Promise((resolve, reject) => {
10+
axios.post('/registrations', detail).then(({data}) => {
11+
resolve(data)
12+
}).catch((error) => {
13+
reject(error)
14+
})
15+
})
16+
}
17+
}

front-end/src/views/RegisterPage.vue

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@
66
<img class="logo" src="/static/images/logo.png">
77
<div class="tagline">Open source task management tool</div>
88
</div>
9-
<form>
9+
<form @submit.prevent="submitForm">
10+
<div v-show="errorMessage" class="alert alert-danger failed">{{ errorMessage }}</div>
1011
<div class="form-group">
1112
<label for="username">Username</label>
12-
<input type="text" class="form-control" id="username">
13+
<input type="text" class="form-control" id="username" v-model="form.username">
1314
</div>
1415
<div class="form-group">
1516
<label for="emailAddress">Email address</label>
16-
<input type="email" class="form-control" id="emailAddress">
17+
<input type="email" class="form-control" id="emailAddress" v-model="form.emailAddress">
1718
</div>
1819
<div class="form-group">
1920
<label for="password">Password</label>
20-
<input type="password" class="form-control" id="password">
21+
<input type="password" class="form-control" id="password" v-model="form.password">
2122
</div>
2223
<button type="submit" class="btn btn-primary btn-block">Create account</button>
2324
<p class="accept-terms text-muted">By clicking “Create account”, you agree to our <a href="#">terms of service</a> and <a href="#">privacy policy</a>.</p>
@@ -38,8 +39,29 @@
3839
</template>
3940

4041
<script>
42+
import registrationService from '@/services/registration'
43+
4144
export default {
42-
name: "RegisterPage"
45+
name: 'RegisterPage',
46+
data: function () {
47+
return {
48+
form: {
49+
username: '',
50+
emailAddress: '',
51+
password: ''
52+
},
53+
errorMessage: ''
54+
}
55+
},
56+
methods: {
57+
submitForm () {
58+
registrationService.register(this.form).then(() => {
59+
this.$router.push({name: 'LoginPage'})
60+
}).catch((error) => {
61+
this.errorMessage = 'Failed to register user. ' + error.message
62+
})
63+
}
64+
}
4365
}
4466
</script>
4567

@@ -61,13 +83,15 @@ export default {
6183
line-height: 180%;
6284
color: #666;
6385
}
64-
.logo {
86+
87+
.logo {
6588
max-width: 150px;
6689
margin: 0 auto;
6790
}
6891
}
6992
7093
.register-form {
94+
7195
.form-group label {
7296
font-weight: bold;
7397
color: #555;
@@ -94,4 +118,5 @@ export default {
94118
color: #666;
95119
}
96120
}
121+
97122
</style>
Lines changed: 87 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,94 @@
1-
import Vue from 'vue'
1+
import { mount, createLocalVue } from '@vue/test-utils'
22
import RegisterPage from '@/views/RegisterPage'
3+
import VueRouter from 'vue-router'
4+
5+
// Adding Vue Router to the test so that
6+
// we can access vm.$router
7+
const localVue = createLocalVue()
8+
localVue.use(VueRouter)
9+
const router = new VueRouter()
10+
11+
// Mock dependency registratioService
12+
jest.mock('@/services/registration')
313

414
describe('RegisterPage.vue', () => {
5-
it('should render correct contents', () => {
6-
const Constructor = Vue.extend(RegisterPage)
7-
const vm = new Constructor().$mount()
8-
expect(vm.$el.querySelector('.logo').getAttribute('src'))
15+
let wrapper
16+
let fieldUsername
17+
let fieldEmailAddress
18+
let fieldPassword
19+
let buttonSubmit
20+
21+
beforeEach(() => {
22+
wrapper = mount(RegisterPage, {
23+
localVue,
24+
router
25+
})
26+
fieldUsername = wrapper.find('#username')
27+
fieldEmailAddress = wrapper.find('#emailAddress')
28+
fieldPassword = wrapper.find('#password')
29+
buttonSubmit = wrapper.find('form button[type="submit"]')
30+
})
31+
32+
afterAll(() => {
33+
jest.restoreAllMocks()
34+
})
35+
36+
it('should render registration form', () => {
37+
expect(wrapper.find('.logo').attributes().src)
938
.toEqual('/static/images/logo.png')
10-
expect(vm.$el.querySelector('.tagline').textContent)
39+
expect(wrapper.find('.tagline').text())
1140
.toEqual('Open source task management tool')
12-
expect(vm.$el.querySelector('#username').value).toEqual('')
13-
expect(vm.$el.querySelector('#emailAddress').value).toEqual('')
14-
expect(vm.$el.querySelector('#password').value).toEqual('')
15-
expect(vm.$el.querySelector('form button[type="submit"]').textContent)
16-
.toEqual('Create account')
41+
expect(fieldUsername.element.value).toEqual('')
42+
expect(fieldEmailAddress.element.value).toEqual('')
43+
expect(fieldPassword.element.value).toEqual('')
44+
expect(buttonSubmit.text()).toEqual('Create account')
45+
})
46+
47+
it('should contain data model with initial values', () => {
48+
expect(wrapper.vm.form.username).toEqual('')
49+
expect(wrapper.vm.form.emailAddress).toEqual('')
50+
expect(wrapper.vm.form.password).toEqual('')
51+
})
52+
53+
it('should have form inputs bound with data model', () => {
54+
const username = 'sunny'
55+
const emailAddress = 'sunny@local'
56+
const password = 'VueJsRocks!'
57+
58+
wrapper.vm.form.username = username
59+
wrapper.vm.form.emailAddress = emailAddress
60+
wrapper.vm.form.password = password
61+
expect(fieldUsername.element.value).toEqual(username)
62+
expect(fieldEmailAddress.element.value).toEqual(emailAddress)
63+
expect(fieldPassword.element.value).toEqual(password)
64+
})
65+
66+
it('should have form submit event handler `submitForm`', () => {
67+
const stub = jest.fn()
68+
wrapper.setMethods({submitForm: stub})
69+
buttonSubmit.trigger('submit')
70+
expect(stub).toBeCalled()
71+
})
72+
73+
it('should register when it is a new user', () => {
74+
const stub = jest.fn()
75+
wrapper.vm.$router.push = stub
76+
wrapper.vm.form.username = 'sunny'
77+
wrapper.vm.form.emailAddress = 'sunny@local'
78+
wrapper.vm.form.password = 'Jest!'
79+
wrapper.vm.submitForm()
80+
wrapper.vm.$nextTick(() => {
81+
expect(stub).toHaveBeenCalledWith({name: 'LoginPage'})
82+
})
83+
})
84+
85+
it('should fail it is not a new user', () => {
86+
// In the mock, only sunny@local is new user
87+
wrapper.vm.form.emailAddress = 'ted@local'
88+
expect(wrapper.find('.failed').isVisible()).toBe(false)
89+
wrapper.vm.submitForm()
90+
wrapper.vm.$nextTick(null, () => {
91+
expect(wrapper.find('.failed').isVisible()).toBe(true)
92+
})
1793
})
1894
})
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import moxios from 'moxios'
2+
import registrationService from '@/services/registration'
3+
4+
describe('services/registration', () => {
5+
beforeEach(() => {
6+
moxios.install()
7+
})
8+
9+
afterEach(() => {
10+
moxios.uninstall()
11+
})
12+
13+
it('should pass the response to caller when request succeeded', () => {
14+
expect.assertions(2)
15+
moxios.wait(() => {
16+
let request = moxios.requests.mostRecent()
17+
expect(request).toBeTruthy()
18+
request.respondWith({
19+
status: 200,
20+
response: {result: 'success'}
21+
})
22+
})
23+
return registrationService.register().then(data => {
24+
expect(data.result).toEqual('success')
25+
})
26+
})
27+
28+
it('should propagate the error to caller when request failed', () => {
29+
expect.assertions(2)
30+
moxios.wait(() => {
31+
let request = moxios.requests.mostRecent()
32+
expect(request).toBeTruthy()
33+
request.reject({
34+
status: 400,
35+
response: {message: 'Bad request'}
36+
})
37+
})
38+
return registrationService.register().catch(error => {
39+
expect(error.response.message).toEqual('Bad request')
40+
})
41+
})
42+
})

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy