Skip to content

Commit

Permalink
Merge pull request #186 from vijayee/master
Browse files Browse the repository at this point in the history
Sign Up View
  • Loading branch information
taoeffect authored Feb 20, 2017
2 parents 7d1db62 + 37fdb05 commit 46f3ed8
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 56 deletions.
1 change: 1 addition & 0 deletions frontend/simple/js/backend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
import {HapiBackend} from './hapi'

export default new HapiBackend()

1 change: 1 addition & 0 deletions frontend/simple/js/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@ export function saveSettings (state: Object): Promise<*> {
export function loadSettings (): Promise<Object> {
return appSettings.getItem('testUser')
}

8 changes: 7 additions & 1 deletion frontend/simple/js/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,18 @@ const state = {
currentGroupId: null,
contracts: {}, // contracts we've successfully subscribed to
whitelist: [], // contracts we're expecting to subscribe to
loggedIn: true// TODO: properly handle this
loggedIn: false// TODO: properly handle this
}

// Mutations must be synchronous! Never call these directly!
// http://vuex.vuejs.org/en/mutations.html
const mutations = {
login (state, user) {
state.loggedIn = user
},
logout (state) {
state.loggedIn = false
},
applyAction (state, {action, contractId}) {
action.apply(store, contractId, Vue)
},
Expand Down
2 changes: 1 addition & 1 deletion frontend/simple/views/CreateGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export default {
memberRemovalPercentage: this.memberRemovalPercentage,
incomeProvided: this.incomeProvided,
contributionPrivacy: this.contributionPrivacy,
founderHashKey: 'alsdfjlskdfjaslkfjsldkfj'
founderHashKey: this.$store.state.loggedIn
})
let hash = entry.toHash()
await backend.subscribe(hash)
Expand Down
89 changes: 63 additions & 26 deletions frontend/simple/views/NavBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
<div class="nav-center">
<!-- TODO: use v-for to dynamically generate these? -->
<!--TODO figure out what needs to be done with active classe after upgrade "{activeClass: 'is-active', path: '/new-group'}"-->
<router-link class="nav-item" active-class ="is-active" to="new-group"><i18n>Start a group</i18n></router-link>
<router-link class="nav-item" active-class ="is-active" to="signup"><i18n>New User</i18n></router-link>
<router-link class="nav-item" active-class ="is-active" to="new-group" id="CreateGroup"><i18n>Start a group</i18n></router-link>
<router-link class="nav-item" active-class ="is-active" to="signup" v-show="!$store.state.loggedIn"><i18n>New User</i18n></router-link>
<router-link class="nav-item" active-class ="is-active" to="user"><i18n>Profile</i18n></router-link>
<router-link class="nav-item" active-class ="is-active" to="user-group"><i18n>Group</i18n></router-link>
<router-link class="nav-item" active-class ="is-active" to="pay-group"><i18n>Pay Group</i18n></router-link>
Expand All @@ -39,44 +39,47 @@
them. see: http://bulma.io/documentation/components/nav/ -->
<div class="nav-right">
<span class="nav-item is-tab control">
<router-link class="button is-success" to="/new-user"><i18n>Sign Up</i18n></router-link>
<a href="#" class="button"
v-bind:class="loggedIn ? 'is-danger' : 'is-primary'"
<router-link v-show="!$store.state.loggedIn" class="button is-success" to="/new-user"><i18n>Sign Up</i18n></router-link>
<a href="#" id="LoginBtn" class="button"
v-bind:class="$store.state.loggedIn ? 'is-danger' : 'is-primary'"
@click.prevent="toggleModal"
>
{{ loggedIn ? 'Sign Out' : 'Log In' }}
{{ $store.state.loggedIn ? 'Sign Out' : 'Log In' }}
</a>
</span>
</div>
</div>
</nav>
<div class="modal" ref="modal">
<div class="modal-background"></div>
<div class="modal" ref="modal" id="LoginModal">
<div class="modal-background" v-on:click="toggleModal"></div>
<div class="modal-content" style="width: 300px">
<div class="card is-rounded">
<div class="card-content">
<h1 class="title">
<i18n>Log In</i18n>
</h1>
<p class="control has-icon">
<input class="input" type="email" placeholder="Email">
<i class="fa fa-envelope"></i>
<input class="input" id="LoginName" name="name" v-model="name" v-validate data-vv-rules="required|regex:^\S+$" placeholder="username" required>
<span class="icon">
<i class="fa fa-user"></i>
</span>
<span v-show="errors.has('name')" class="help is-danger"><i18n>Username cannot contain spaces</i18n></span>
</p>
<p class="control has-icon">
<input class="input" type="password" placeholder="Password">
<i class="fa fa-lock"></i>
</p>
<p class="control">
<label class="checkbox">
<input type="checkbox">
<i18n>Remember me</i18n>
</label>
<input class="input" id="LoginPassword" name="password" v-model="password" v-validate data-vv-rules="required|min:7" placeholder="password" type="password" required>
<span v-show="errors.has('password')" class="help is-danger">Password must be at least 7 characters</span>
<span class="icon is-small">
<i class="fa fa-lock"></i>
</span>
</p>
<span class="help is-danger" id="LoginResponse" v-show="response">{{response}}</span>
<p class="control">
<button class="button is-primary is-medium is-fullwidth"
@click="loginOrLogout"
<button id="LoginButton" class="button is-primary is-medium is-fullwidth"
@click="login" :disabled="errors.any() || !fields.passed()"
>
<i class="fa fa-user"></i>
<span class="icon is-medium">
<i class="fa fa-user"></i>
</span>
<i18n>Login</i18n>
</button>
</p>
Expand All @@ -89,20 +92,54 @@
</template>

<script>
import Vue from 'vue'
import {HapiNamespace} from '../js/backend/hapi'
var namespace = new HapiNamespace()
export default {
name: 'NavBar',
created: function () {
Vue.events.$on('login', this.toggleModal)
},
methods: {
loginOrLogout () {
if (!this.loggedIn) this.toggleModal()
login: async function () {
try {
// TODO Insert cryptography here
let response = await namespace.lookup(this.name)
let identity = response.body
console.log(`Retrieved identity ${identity}`)
this.$store.commit('login', this.name)
this.toggleModal()
} catch (ex) {
this.response = 'Invalid username or password'
console.log('login failed')
}
},
logout: function () {
this.$store.commit('logout')
},
toggleModal () {
// https://github.com/oneuijs/You-Dont-Need-jQuery#css--style
this.$refs.modal.classList.toggle('is-active')
if (!this.$refs.modal.classList.contains('is-active') && this.$store.state.loggedIn) {
this.logout()
this.$router.push({path: '/'})
} else {
document.getElementById('LoginName').focus()
this.response = null
this.name = null
this.password = null
this.errors.clear()
// https://github.com/oneuijs/You-Dont-Need-jQuery#css--style
this.$refs.modal.classList.toggle('is-active')
if (this.$route.query.next) {
this.$router.push({path: this.$route.query.next})
}
}
}
},
data () {
return {
loggedIn: false
name: null,
password: null,
response: null
}
}
}
Expand Down
80 changes: 56 additions & 24 deletions frontend/simple/views/SignUp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,27 @@
<div class="box centered" style="max-width:400px">
<h2 class="subtitle">Sign Up</h2>

<a v-on:click="forwardToLogin" style="margin-left: 65%">Have an account?</a>


<p class="control has-icon">
<input class="input" name="name" v-validate data-vv-rules="required|regex:^\S+$" placeholder="username" required>
<input id="name" class="input" name="name" v-model="name" v-on:keypress="checkName" v-validate data-vv-rules="required|regex:^\S+$" placeholder="username" required>
<span class="icon is-small">
<i class="fa fa-user"></i>
</span>
<span v-show="errors.has('name')" class="help is-danger">Username cannot contain spaces</span>
<span v-if="errors.has('name')" id="badUsername" class="help is-danger">Username cannot contain spaces</span>
<span v-show="nameAvailable != null && !errors.has('name') " class="help" v-bind:class="[nameAvailable ? 'is-success' : 'is-danger']">{{ nameAvailable ? 'name is available' : 'name is unavailable' }}</span>
</p>
<p class="control has-icon">
<input class="input" name="email" v-validate data-vv-rules="required|email" type="email" placeholder="email" required>
<input class="input" id= "email" name="email" v-model="email" v-validate data-vv-rules="required|email" type="email" placeholder="email" required>
<span class="icon is-small">
<i class="fa fa-envelope"></i>
</span>
<span v-show="errors.has('email')" class="help is-danger">Not an email</span>
<span v-if="errors.has('email')" id="badEmail" class="help is-danger">Not an email</span>
</p>
<p class="control has-icon">
<input class="input" name="password" v-validate data-vv-rules="required|min:7" placeholder="password" type="password" required>
<span v-show="errors.has('password')" class="help is-danger">Password must be at least 7 characters</span>
<input class="input" id="password" name="password" v-model="password" v-validate data-vv-rules="required|min:7" placeholder="password" type="password" required>
<span v-if="errors.has('password')" id="badPassword" class="help is-danger">Password must be at least 7 characters</span>
<span class="icon is-small">
<i class="fa fa-lock"></i>
</span>
Expand Down Expand Up @@ -65,35 +69,63 @@
</style>

<script>
var serialize = require('form-serialize')
var request = require('superagent')
import Vue from 'vue'
import _ from 'lodash'
import * as Events from '../../../shared/events'
import {HapiNamespace} from '../js/backend/hapi'
var namespace = new HapiNamespace()
// TODO: fix all this
export default {
name: 'SignUp',
methods: {
submit: function () {
console.log(this.errors)
this.response = ''
request.post(`${process.env.API_URL}/user/`)
.send(serialize(this.$refs.form, {hash: true}))
.end((err, res) => {
this.error = !!err || !res.ok
console.log('this.error', this.error)
this.response = this.error ? res.body.message : (res.text === '' ? 'success' : res.text)
if (!this.error && this.$route.query.next) {
this.login()
this.$router.push({path: this.$route.query.next})
submit: async function () {
try {
let user = new Events.IdentityContract({
attributes: [
{name: 'name', value: this.name},
{name: 'email', value: this.email}
]
})
await namespace.register(this.name, user.toHash())
// TODO Just add cryptographic magic
this.response = 'success'
if (this.$route.query.next) {
setTimeout(() => {
this.$router.push({path: this.$route.query.next})
}, 1000)
}
})
}
this.$store.commit('login', this.name)
this.error = false
} catch (ex) {
console.log(ex)
this.response = ex.toString()
this.error = true
}
},
forwardToLogin: function () {
Vue.events.$emit('login')
},
checkName: _.debounce(async function () {
if (this.name && !this.errors.has('name')) {
try {
await namespace.lookup(this.name)
this.nameAvailable = false
} catch (ex) {
this.nameAvailable = true
}
return this.nameAvailable
}
}, 700, {maxWait: 2000})
},
data () {
return {
error: false,
response: '',
formData: {},
untouched: []
nameAvailable: null,
name: null,
password: null,
email: null
}
}
}
Expand Down
66 changes: 62 additions & 4 deletions test/frontend.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe('Frontend', function () {
})

it('Should Traverse Log', async function () {
this.timeout(10000)
this.timeout(4000)
await n.goto(page('event-log'))
let prior = await n.evaluate(() => document.getElementById('LogPosition').innerText)
let initial = await n.wait('textarea[name="payload"]')
Expand All @@ -88,11 +88,69 @@ describe('Frontend', function () {
})
})

describe('Sign up Test', function () {
let randInt = (min, max) => Math.floor(Math.random() * (max - min)) + min
let username = `testuser${randInt(0, 10000000)}${randInt(10000000, 20000000)}`
it('Should register User', async function () {
this.timeout(4000)
await n.goto(page('signup'))
let signedup = await n.insert('#name', username)
.insert('#email', `[email protected]`)
.insert('#password', 'testtest')
.click('button[type="submit"]')
.wait(() => !!document.getElementById('serverMsg').innerText)
.evaluate(() => document.getElementById('serverMsg').innerText)
should(signedup).equal('success')
/*
await n.insert('#name')
.insert('#email')
.insert('#password')
.wait(() => !document.getElementById('name').innerText &&
!document.getElementById('email').innerText &&
!document.getElementById('password').innerText)
*/
})
it('Test Logout and Login', async function () {
this.timeout(4000)
let response = await n.click('#LoginBtn')
.wait(() => document.getElementById('LoginBtn').classList.contains('is-primary'))
.click('#LoginBtn')
.wait(() => document.getElementById('LoginModal').classList.contains('is-active'))
.insert('#LoginName', username)
.insert('#LoginPassword', 'testtest')
.click('#LoginButton')
.wait(() => !!document.getElementById('LoginResponse'))
.evaluate(() => document.getElementById('LoginResponse').innerText)
should(response).not.equal('Invalid username or password')
})
/* There appears to be a bug in nightmare that causes the insert and type commands to enter old data into the field if the
insert or type commands or used more than once on the same field. This concatenates the old values to the new.
This occurs regardless of whether you clear the field or not. Until its fixed skip this validation test
*/
it.skip('Test Validation', async function () {
this.timeout(4000)
await n.goto(page('signup'))
let badUsername = 't e s t'
let badEmail = `@fail`
let badPassword = `789`// six is so afraid
let denied = await n.insert('#name', badUsername)
.insert('#email', badEmail)
.insert('#password', badPassword)
.evaluate(() => document.querySelector('button[type="submit"]').disabled)
should(denied).equal(true)
let usernameMsg = await n.evaluate(() => !!document.getElementById('badUsername'))
should(usernameMsg).equal(true)
let emailMsg = await n.evaluate(() => !!document.getElementById('badEmail'))
should(emailMsg).equal(true)
let passwordMsg = await n.evaluate(() => !!document.getElementById('badPassword'))
should(passwordMsg).equal(true)
})
})

describe('Group Creation Test', function () {
it('Should create a group', async function () {
this.timeout(10000)
await n.goto(page('new-group'))
.should.finally.containEql({ code: 200, url: page('new-group') })
this.timeout(4000)
await n.click('#CreateGroup')
let created = await n.insert('input[name="groupName"]', 'Test Group')
.insert('textarea[name="sharedValues"]', 'Testing this software')
.insert('input[name="groupName"]', 'Test Group')
Expand Down

0 comments on commit 46f3ed8

Please sign in to comment.