diff --git a/src/test/get-expected-files.js b/src/test/get-expected-files.js index c33ec6e2c..4944e40f2 100644 --- a/src/test/get-expected-files.js +++ b/src/test/get-expected-files.js @@ -83,6 +83,7 @@ export function app(options) { 'server/config/environment/test.js', 'server/config/environment/shared.js', 'server/views/404.' + markup, + 'e2e/.eslintrc', 'e2e/main/main.po.js', 'e2e/main/main.spec.js', 'e2e/components/navbar/navbar.po.js', diff --git a/templates/app/.editorconfig b/templates/app/.editorconfig index c2cdfb8ad..8a80734f0 100644 --- a/templates/app/.editorconfig +++ b/templates/app/.editorconfig @@ -9,7 +9,7 @@ root = true # Change these settings to your own preference indent_style = space -indent_size = 2 +indent_size = 4 # We recommend you to keep these unchanged end_of_line = lf diff --git a/templates/app/.eslintrc b/templates/app/.eslintrc index b757332c7..a317f9c6b 100644 --- a/templates/app/.eslintrc +++ b/templates/app/.eslintrc @@ -149,7 +149,7 @@ "id-blacklist": 0, //blacklist certain identifiers to prevent them being used "id-length": 0, //this option enforces minimum and maximum identifier lengths (variable names, property names etc.) "id-match": 0, //require identifiers to match the provided regular expression - "indent": ["error", 2], //specify tab or space width for your code + "indent": ["warn", 4], //specify tab or space width for your code "jsx-quotes": 0, //specify whether double or single quotes should be used in JSX attributes "key-spacing": 2, //enforce spacing between keys and values in object literal properties "keyword-spacing": [2, { diff --git a/templates/app/_.babelrc b/templates/app/_.babelrc index 31128679b..0e13da5e9 100644 --- a/templates/app/_.babelrc +++ b/templates/app/_.babelrc @@ -1,15 +1,15 @@ { "presets": [ - "es2015", - "es2016", - "es2017", - "stage-0" + ["babel-preset-env", { + "targets": { + "node": "6.2" + } + }] ], "plugins": [ <%_ if(filters.flow) { -%> "transform-flow-comments", <%_ } -%> - "angular2-annotations", "transform-runtime", "transform-decorators-legacy" ] diff --git a/templates/app/_package.json b/templates/app/_package.json index 3d0636925..377a3fbca 100644 --- a/templates/app/_package.json +++ b/templates/app/_package.json @@ -118,6 +118,7 @@ "babel-plugin-transform-class-properties": "^6.6.0", "babel-plugin-transform-runtime": "^6.6.0", "babel-plugin-istanbul": "^4.1.4", + "babel-preset-env": "^1.6.1", "babel-preset-es2015": "^6.6.0", "eslint": "^2.12.0", "del": "^3.0.0", @@ -181,6 +182,7 @@ <%# END WEBPACK %> "through2": "^2.0.1", "open": "~0.0.4", + "protractor": "^5.3.0", "istanbul": "1.1.0-alpha.1", "chai": "^4.1.2", "sinon": "^3.2.1", @@ -217,6 +219,7 @@ "lint:server": "eslint ./server/**/*.js -c ./server/.eslintrc --ignore-pattern *.spec.js --ignore-pattern *.integration.js", "test": "gulp test", "test:client": "karma start ./karma.conf.js --single-run", + "test:e2e": "gulp webpack:dev && protractor ./protractor.conf.js", <%_ if(filters.flow) { -%> "flow": "flow", <%_ } -%> diff --git a/templates/app/client/app/account(auth)/login/login.component.js b/templates/app/client/app/account(auth)/login/login.component.js index e9b3402a6..83af3f85a 100644 --- a/templates/app/client/app/account(auth)/login/login.component.js +++ b/templates/app/client/app/account(auth)/login/login.component.js @@ -1,3 +1,4 @@ +// @flow import { Component } from '@angular/core'; <%_ if(filters.uirouter) { -%> import { StateService } from 'ui-router-ng2';<% } %> @@ -5,7 +6,6 @@ import { StateService } from 'ui-router-ng2';<% } %> import { Router } from '@angular/router';<% } %> import { AuthService } from '../../../components/auth/auth.service'; -// @flow <%_ if(filters.flow) { -%> type User = { name: string; @@ -48,8 +48,8 @@ export class LoginComponent { this.StateService = _StateService_;<% } %> } - login() { - this.submitted = true; + login(form) { + if(form.invalid) return; return this.AuthService.login({ email: this.user.email, @@ -63,7 +63,7 @@ export class LoginComponent { this.StateService.go('main');<% } %> }) .catch(err => { - this.errors.login = err.message; + this.errors.login = err.json().message; }); } } diff --git a/templates/app/client/app/account(auth)/login/login.html b/templates/app/client/app/account(auth)/login/login.html index 8c977d61a..66b27b162 100644 --- a/templates/app/client/app/account(auth)/login/login.html +++ b/templates/app/client/app/account(auth)/login/login.html @@ -6,7 +6,7 @@

Login

Admin account is admin@example.com / admin

-
+
diff --git a/templates/app/client/app/account(auth)/signup/signup.component.js b/templates/app/client/app/account(auth)/signup/signup.component.js index 80a8e0fc4..e6b9dbe69 100644 --- a/templates/app/client/app/account(auth)/signup/signup.component.js +++ b/templates/app/client/app/account(auth)/signup/signup.component.js @@ -5,14 +5,14 @@ import { StateService } from 'ui-router-ng2';<% } %> <%_ if(filters.ngroute) { -%> import { Router } from '@angular/router';<% } %> import { AuthService } from '../../../components/auth/auth.service'; +import template from './signup.html'; <%_ if(filters.flow) { -%> type User = { name: string; email: string; password: string; -};<% } %> -<%_ if(filters.ts) { -%> +};<% } %><%_ if(filters.ts) { -%> interface User { name: string; email: string; @@ -21,7 +21,7 @@ interface User { @Component({ selector: 'signup', - template: require('./signup.<%=templateExt%>') + template, }) export class SignupComponent { user: User = { @@ -61,19 +61,27 @@ export class SignupComponent { <% if(filters.ngroute) { %>this.Router.navigateByUrl('/home');<% } -%> <% if(filters.uirouter) { %>this.StateService.go('main');<% } -%> }) - .catch(err => { - err = err.data; - this.errors = {};<% if(filters.mongooseModels) { %> + .catch(err => {<% if(filters.mongooseModels) { %> + this.errors = err.errors; + // Update validity of form fields that match the mongoose errors - err.errors.forEach((error, field) => { + Object.entries(err.errors).forEach(([field, error]) => { this.errors[field] = error.message; + + if(field === 'email' && error.kind === 'user defined') { + form.form.controls[field].setErrors({inUse: true}); + } });<% } %><% if(filters.sequelizeModels) { %> + this.errors = {}; + // Update validity of form fields that match the sequelize errors if(err.name) { err.fields.forEach(field => { this.errors[field] = err.message; }); }<% } %> + + this.submitted = false; }); } } diff --git a/templates/app/client/app/account(auth)/signup/signup.html b/templates/app/client/app/account(auth)/signup/signup.html index 1833c0e7a..274436825 100644 --- a/templates/app/client/app/account(auth)/signup/signup.html +++ b/templates/app/client/app/account(auth)/signup/signup.html @@ -4,7 +4,7 @@

Sign up

- +
Sign up required mongoose-error #email="ngModel"> -

+

Please enter a valid email address.

-

- {{ errors.email }} +

+ This email address is already in use.

diff --git a/templates/app/client/app/admin(auth)/admin.module.js b/templates/app/client/app/admin(auth)/admin.module.js index 6fa9336ac..1d610a156 100644 --- a/templates/app/client/app/admin(auth)/admin.module.js +++ b/templates/app/client/app/admin(auth)/admin.module.js @@ -3,6 +3,7 @@ import { BrowserModule } from '@angular/platform-browser';<% if(filters.uirouter import { UIRouterModule } from 'ui-router-ng2';<% } %><% if(filters.ngroute) { %> import { RouterModule, Routes } from '@angular/router';<% } %> import { AuthGuard } from '../../components/auth/auth-guard.service'; +import { AuthModule } from '../../components/auth/auth.module'; import { AdminComponent } from './admin.component'; <%_ if(filters.uirouter) { -%> @@ -16,6 +17,7 @@ const adminRoutes: Routes = [{ @NgModule({ imports: [ + AuthModule, BrowserModule, <%_ if(filters.ngroute) { _%> RouterModule.forChild(adminRoutes),<% } %> diff --git a/templates/app/client/components/auth(auth)/auth.module.js b/templates/app/client/components/auth(auth)/auth.module.js index 95c005351..348400c6a 100644 --- a/templates/app/client/components/auth(auth)/auth.module.js +++ b/templates/app/client/components/auth(auth)/auth.module.js @@ -3,11 +3,13 @@ import { NgModule } from '@angular/core'; import { AuthService } from './auth.service'; import { UserService } from './user.service'; +import { AuthGuard } from '../../components/auth/auth-guard.service'; @NgModule({ providers: [ AuthService, - UserService + UserService, + AuthGuard, ] }) export class AuthModule {} diff --git a/templates/app/client/components/auth(auth)/auth.service.js b/templates/app/client/components/auth(auth)/auth.service.js index 93c8c2463..8b14f933c 100644 --- a/templates/app/client/components/auth(auth)/auth.service.js +++ b/templates/app/client/components/auth(auth)/auth.service.js @@ -122,7 +122,8 @@ export class AuthService { }) .catch(err => { this.logout(); - return safeCb(callback)(err); + safeCb(callback)(err); + return Promise.reject(err); }); } diff --git a/templates/app/client/components/auth(auth)/user.service.js b/templates/app/client/components/auth(auth)/user.service.js index 98ff90458..68c2f59c6 100644 --- a/templates/app/client/components/auth(auth)/user.service.js +++ b/templates/app/client/components/auth(auth)/user.service.js @@ -1,4 +1,4 @@ -'use strict'; +// @flow import { Injectable } from '@angular/core'; import { Response } from '@angular/http'; import { AuthHttp } from 'angular2-jwt'; @@ -7,7 +7,6 @@ import 'rxjs/add/operator/map'; import 'rxjs/add/operator/catch'; import 'rxjs/add/operator/toPromise'; -// @flow type UserType = { // TODO: use Mongoose model id?: string; @@ -17,7 +16,7 @@ type UserType = { } function handleError(err) { - return Observable.throw(err.json().error || 'Server error'); + return Observable.throw(err.json() || 'Server error'); } @Injectable() diff --git a/templates/app/client/components/navbar/navbar.component.js b/templates/app/client/components/navbar/navbar.component.js index f4a4ba8e7..381b4835c 100644 --- a/templates/app/client/components/navbar/navbar.component.js +++ b/templates/app/client/components/navbar/navbar.component.js @@ -5,10 +5,11 @@ import { StateService } from 'ui-router-ng2';<% } %> <%_ if (filters.ngroute) { -%> import { Router } from '@angular/router';<% } %> import { AuthService } from '../auth/auth.service';<% } %> +import template from './navbar.html'; @Component({ selector: 'navbar', - template: require('./navbar.<%=templateExt%>') + template, }) export class NavbarComponent { isCollapsed = true; @@ -53,9 +54,10 @@ export class NavbarComponent { } logout() { - let promise = this.AuthService.logout();<% if(filters.uirouter) { %> - this.StateService.go('login');<% } %><% if(filters.ngroute) { %> - this.Router.navigateByUrl('/home');<% } %> - return promise; + return this.AuthService.logout().then(() => {<% if(filters.uirouter) { %> + this.StateService.go('login');<% } %><% if(filters.ngroute) { %> + this.Router.navigateByUrl('/home');<% } %> + this.reset(); + }); }<% } -%> } diff --git a/templates/app/client/components/navbar/navbar.html b/templates/app/client/components/navbar/navbar.html index 05302297b..3a0e2448f 100644 --- a/templates/app/client/components/navbar/navbar.html +++ b/templates/app/client/components/navbar/navbar.html @@ -26,7 +26,7 @@
  • uiSrefActive="active"<% } else { %>routerLinkActive="active"<% } %>>uiSref="login"<% } else { %>routerLink="/login"<% } %>>Login
  • uiSrefActive="active"<% } else { %>routerLinkActive="active"<% } %>>uiSref="settings"<% } else { %>routerLink="/settings"<% } %>>
  • -
  • Logout
  • +
  • Log out
  • <% } %>
    diff --git a/templates/app/e2e/.eslintrc b/templates/app/e2e/.eslintrc new file mode 100644 index 000000000..1c977d989 --- /dev/null +++ b/templates/app/e2e/.eslintrc @@ -0,0 +1,13 @@ +{ + "parser": "babel-eslint", + "env": { + "es6": true, + "node": true,<% if(filters.mocha) { %> + "mocha": true,<% } else { %> + "jasmine": true,<% } %> + "protractor": true + }, + "globals": { + "expect": true + } +} diff --git a/templates/app/e2e/account(auth)/login/login.po.js b/templates/app/e2e/account(auth)/login/login.po.js index a9829c4cb..762a0f7a0 100644 --- a/templates/app/e2e/account(auth)/login/login.po.js +++ b/templates/app/e2e/account(auth)/login/login.po.js @@ -3,26 +3,27 @@ * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ */ -'use strict'; +import {OauthButtons} from '../../components/oauth-buttons/oauth-buttons.po'; -var LoginPage = function() { - var form = this.form = element(by.css('.form')); - form.email = form.element(by.model('vm.user.email')); - form.password = form.element(by.model('vm.user.password')); - form.submit = form.element(by.css('.btn-login'));<% if (filters.oauth) { %> - form.oauthButtons = require('../../components/oauth-buttons/oauth-buttons.po').oauthButtons;<% } %> +export class LoginPage { + constructor() { + this.form = element(by.css('.form')); + const form = this.form; - this.login = function(data) { - for (var prop in data) { - var formElem = form[prop]; - if (data.hasOwnProperty(prop) && formElem && typeof formElem.sendKeys === 'function') { - formElem.sendKeys(data[prop]); - } + form.email = form.element(by.name('email')); + form.password = form.element(by.name('password')); + form.submit = form.element(by.css('.btn-login')); + form.oauthButtons = (new OauthButtons()).oauthButtons; } - return form.submit.click(); - }; -}; - -module.exports = new LoginPage(); + login(data) { + for(let prop in data) { + let formElem = this.form[prop]; + if(data.hasOwnProperty(prop) && formElem && typeof formElem.sendKeys === 'function') { + formElem.sendKeys(data[prop]); + } + } + return this.form.submit.click(); + } +} diff --git a/templates/app/e2e/account(auth)/login/login.spec(jasmine).js b/templates/app/e2e/account(auth)/login/login.spec(jasmine).js index 780ed57d0..bd8fa3dc0 100644 --- a/templates/app/e2e/account(auth)/login/login.spec(jasmine).js +++ b/templates/app/e2e/account(auth)/login/login.spec(jasmine).js @@ -1,86 +1,73 @@ -'use strict'; - -var config = browser.params;<% if (filters.mongooseModels) { %> -var UserModel = require(config.serverConfig.root + '/server/api/user/user.model').default;<% } %><% if (filters.sequelizeModels) { %> -var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> +const config = browser.params;<% if (filters.mongooseModels) { %> +import UserModel from '../../../server/api/user/user.model';<% } %><% if (filters.sequelizeModels) { %> +import {User as UserModel} from '../../../server/sqldb';<% } %> +import {LoginPage} from './login.po'; +import {NavbarComponent} from '../../components/navbar/navbar.po'; describe('Login View', function() { - var page; + let page; + + const loadPage = () => { + return browser.get(`${config.baseUrl}/login`).then(() => { + page = new LoginPage(); + }); + }; + + const testUser = { + name: 'Test User', + email: 'test@example.com', + password: 'test' + }; - var loadPage = function() { - browser.get(config.baseUrl + '/login'); - page = require('./login.po'); - }; + beforeEach(async function() { + await UserModel + <% if (filters.mongooseModels) { %>.remove();<% } + if (filters.sequelizeModels) { %>.destroy({ where: {} });<% } %> - var testUser = { - name: 'Test User', - email: 'test@example.com', - password: 'test' - }; + await UserModel.create(testUser); - beforeEach(function(done) { - <% if (filters.mongooseModels) { %>UserModel.remove()<% } - if (filters.sequelizeModels) { %>UserModel.destroy({ where: {} })<% } %> - .then(function() { - return UserModel.create(testUser) - .then(loadPage); - }) - // .then(loadPage) - .finally(function() { - browser.wait(function() { - //console.log('waiting for angular...'); - return browser.executeScript('return !!window.angular'); + await loadPage(); + }); - }, 5000).then(done); - }); - }); + it('should include login form with correct inputs and submit button', function() { + expect(page.form.email.getAttribute('type')).toBe('email'); + expect(page.form.email.getAttribute('name')).toBe('email'); + expect(page.form.password.getAttribute('type')).toBe('password'); + expect(page.form.password.getAttribute('name')).toBe('password'); + expect(page.form.submit.getAttribute('type')).toBe('submit'); + expect(page.form.submit.getText()).toBe('Login'); + });<% if (filters.oauth) { %> - it('should include login form with correct inputs and submit button', function() { - expect(page.form.email.getAttribute('type')).toBe('email'); - expect(page.form.email.getAttribute('name')).toBe('email'); - expect(page.form.password.getAttribute('type')).toBe('password'); - expect(page.form.password.getAttribute('name')).toBe('password'); - expect(page.form.submit.getAttribute('type')).toBe('submit'); - expect(page.form.submit.getText()).toBe('Login'); - });<% if (filters.oauth) { %> + it('should include oauth buttons with correct classes applied', function() {<% if (filters.facebookAuth) { %> + expect(page.form.oauthButtons.facebook.getText()).toBe('Connect with Facebook');<% } if (filters.googleAuth) { %> + expect(page.form.oauthButtons.google.getText()).toBe('Connect with Google+');<% } if (filters.twitterAuth) { %> + expect(page.form.oauthButtons.twitter.getText()).toBe('Connect with Twitter');<% } %> + });<% } %> - it('should include oauth buttons with correct classes applied', function() {<% if (filters.facebookAuth) { %> - expect(page.form.oauthButtons.facebook.getText()).toBe('Connect with Facebook'); - expect(page.form.oauthButtons.facebook.getAttribute('class')).toMatch('btn-block');<% } if (filters.googleAuth) { %> - expect(page.form.oauthButtons.google.getText()).toBe('Connect with Google+'); - expect(page.form.oauthButtons.google.getAttribute('class')).toMatch('btn-block');<% } if (filters.twitterAuth) { %> - expect(page.form.oauthButtons.twitter.getText()).toBe('Connect with Twitter'); - expect(page.form.oauthButtons.twitter.getAttribute('class')).toMatch('btn-block');<% } %> - });<% } %> + describe('with local auth', function() { + it('should login a user and redirect to "/home"', async function() { + await page.login(testUser); - describe('with local auth', function() { + let navbar = new NavbarComponent(); - it('should login a user and redirecting to "/"', function() { - return page.login(testUser).then(() => { - var navbar = require('../../components/navbar/navbar.po'); + browser.ignoreSynchronization = false; + await browser.wait(() => browser.getCurrentUrl(), 5000, 'URL didn\'t change after 5s'); + browser.ignoreSynchronization = true; - return browser.wait( - () => element(by.css('.hero-unit')), - 5000, - `Didn't find .hero-unit after 5s` - ).then(() => { - expect(browser.getCurrentUrl()).toBe(config.baseUrl + '/'); - expect(navbar.navbarAccountGreeting.getText()).toBe('Hello ' + testUser.name); + expect(await browser.getCurrentUrl()).toBe(`${config.baseUrl}/home`); + expect(await navbar.navbarAccountGreeting.getText()).toBe(`Hello ${testUser.name}`); }); - }); - }); - it('should indicate login failures', function() { - page.login({ - email: testUser.email, - password: 'badPassword' - }); + it('should indicate login failures', async function() { + await page.login({ + email: testUser.email, + password: 'badPassword' + }); - expect(browser.getCurrentUrl()).toBe(config.baseUrl + '/login'); + expect(await browser.getCurrentUrl()).toBe(`${config.baseUrl}/login`); - var helpBlock = page.form.element(by.css('.form-group.has-error .help-block.ng-binding')); - expect(helpBlock.getText()).toBe('This password is not correct.'); + let helpBlock = page.form.element(by.css('.form-group.has-error .help-block:not([hidden])')); + expect(await helpBlock.getText()).toBe('This password is not correct.'); + }); }); - - }); }); diff --git a/templates/app/e2e/account(auth)/login/login.spec(mocha).js b/templates/app/e2e/account(auth)/login/login.spec(mocha).js index fd11f4131..9e9778d22 100644 --- a/templates/app/e2e/account(auth)/login/login.spec(mocha).js +++ b/templates/app/e2e/account(auth)/login/login.spec(mocha).js @@ -1,93 +1,87 @@ -'use strict'; - -var config = browser.params;<% if (filters.mongooseModels) { %> -var UserModel = require(config.serverConfig.root + '/server/api/user/user.model').default;<% } %><% if (filters.sequelizeModels) { %> -var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> +const config = browser.params;<% if (filters.mongooseModels) { %> +import UserModel from '../../../server/api/user/user.model';<% } %><% if (filters.sequelizeModels) { %> +import {User as UserModel} from '../../../server/sqldb';<% } %> +import {LoginPage} from './login.po'; +import {NavbarComponent} from '../../components/navbar/navbar.po'; describe('Login View', function() { - var page; - - var loadPage = function() { - let promise = browser.get(config.baseUrl + '/login'); - page = require('./login.po'); - return promise; - }; - - var testUser = { - name: 'Test User', - email: 'test@example.com', - password: 'test' - }; - - before(function() { - return UserModel - <% if (filters.mongooseModels) { %>.remove()<% } - if (filters.sequelizeModels) { %>.destroy({ where: {} })<% } %> - .then(function() { - <% if (filters.mongooseModels) { %>return UserModel.create(testUser);<% } - if (filters.sequelizeModels) { %>return UserModel.create(testUser);<% } %> - }) - .then(loadPage); - }); - - after(function() { - <% if (filters.mongooseModels) { %>return UserModel.remove();<% } - if (filters.sequelizeModels) { %>return UserModel.destroy({ where: {} });<% } %> - }); - - it('should include login form with correct inputs and submit button', function() { - <%= expect() %>page.form.email.getAttribute('type')<%= to() %>.eventually.equal('email'); - <%= expect() %>page.form.email.getAttribute('name')<%= to() %>.eventually.equal('email'); - <%= expect() %>page.form.password.getAttribute('type')<%= to() %>.eventually.equal('password'); - <%= expect() %>page.form.password.getAttribute('name')<%= to() %>.eventually.equal('password'); - <%= expect() %>page.form.submit.getAttribute('type')<%= to() %>.eventually.equal('submit'); - <%= expect() %>page.form.submit.getText()<%= to() %>.eventually.equal('Login'); - });<% if (filters.oauth) { %> - - it('should include oauth buttons with correct classes applied', function() {<% if (filters.facebookAuth) { %> - <%= expect() %>page.form.oauthButtons.facebook.getText()<%= to() %>.eventually.equal('Connect with Facebook'); - <%= expect() %>page.form.oauthButtons.facebook.getAttribute('class')<%= to() %>.eventually.contain('btn-block');<% } if (filters.googleAuth) { %> - <%= expect() %>page.form.oauthButtons.google.getText()<%= to() %>.eventually.equal('Connect with Google+'); - <%= expect() %>page.form.oauthButtons.google.getAttribute('class')<%= to() %>.eventually.contain('btn-block');<% } if (filters.twitterAuth) { %> - <%= expect() %>page.form.oauthButtons.twitter.getText()<%= to() %>.eventually.equal('Connect with Twitter'); - <%= expect() %>page.form.oauthButtons.twitter.getAttribute('class')<%= to() %>.eventually.contain('btn-block');<% } %> - });<% } %> - - describe('with local auth', function() { - - it('should login a user and redirecting to "/"', function() { - return page.login(testUser).then(() => { - var navbar = require('../../components/navbar/navbar.po'); - - return browser.wait( - () => element(by.css('.hero-unit')), - 5000, - `Didn't find .hero-unit after 5s` - ).then(() => { - <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/'); - <%= expect() %>navbar.navbarAccountGreeting.getText()<%= to() %>.eventually.equal('Hello ' + testUser.name); + let page; + + const loadPage = () => { + return browser.get(`${config.baseUrl}/login`).then(() => { + page = new LoginPage(); }); - }); + }; + + const testUser = { + name: 'Test User', + email: 'test@example.com', + password: 'test' + }; + + before(async function() { + await UserModel + <% if (filters.mongooseModels) { %>.remove();<% } + if (filters.sequelizeModels) { %>.destroy({ where: {} });<% } %> + + await UserModel.create(testUser); + + await loadPage(); }); - describe('and invalid credentials', function() { - before(function() { - return loadPage(); - }) + after(function() { + <% if (filters.mongooseModels) { %>return UserModel.remove();<% } + if (filters.sequelizeModels) { %>return UserModel.destroy({ where: {} });<% } %> + }); - it('should indicate login failures', function() { - page.login({ - email: testUser.email, - password: 'badPassword' + it('should include login form with correct inputs and submit button', function() { + <%= expect() %>page.form.email.getAttribute('type')<%= to() %>.eventually.equal('email'); + <%= expect() %>page.form.email.getAttribute('name')<%= to() %>.eventually.equal('email'); + <%= expect() %>page.form.password.getAttribute('type')<%= to() %>.eventually.equal('password'); + <%= expect() %>page.form.password.getAttribute('name')<%= to() %>.eventually.equal('password'); + <%= expect() %>page.form.submit.getAttribute('type')<%= to() %>.eventually.equal('submit'); + <%= expect() %>page.form.submit.getText()<%= to() %>.eventually.equal('Login'); + });<% if (filters.oauth) { %> + + it('should include oauth buttons with correct classes applied', function() {<% if (filters.facebookAuth) { %> + <%= expect() %>page.form.oauthButtons.facebook.getText()<%= to() %>.eventually.equal('Connect with Facebook');<% } if (filters.googleAuth) { %> + <%= expect() %>page.form.oauthButtons.google.getText()<%= to() %>.eventually.equal('Connect with Google+');<% } if (filters.twitterAuth) { %> + <%= expect() %>page.form.oauthButtons.twitter.getText()<%= to() %>.eventually.equal('Connect with Twitter');<% } %> + });<% } %> + + describe('with local auth', function() { + it('should login a user and redirect to "/home"', async function() { + await page.login(testUser); + + let navbar = new NavbarComponent(); + + browser.ignoreSynchronization = false; + await browser.wait(() => browser.getCurrentUrl(), 5000, 'URL didn\'t change after 5s'); + browser.ignoreSynchronization = true; + + <%= expect() %>(await browser.getCurrentUrl())<%= to() %>.equal(`${config.baseUrl}/home`); + <%= expect() %>(await navbar.navbarAccountGreeting.getText())<%= to() %>.equal(`Hello ${testUser.name}`); }); - <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/login'); + describe('and invalid credentials', function() { + before(() => loadPage()); - var helpBlock = page.form.element(by.css('.form-group.has-error .help-block.ng-binding')); - <%= expect() %>helpBlock.getText()<%= to() %>.eventually.equal('This password is not correct.'); - }); + it('should indicate login failures', async function() { + await page.login({ + email: testUser.email, + password: 'badPassword' + }); - }); + <%= expect() %>(await browser.getCurrentUrl())<%= to() %>.equal(`${config.baseUrl}/login`); + + let helpBlock = page.form.element(by.css('.form-group.has-error .help-block:not([hidden])')); - }); + browser.ignoreSynchronization = false; + await browser.wait(() => helpBlock.getText(), 5000, 'Couldn\'t find help text after 5s'); + browser.ignoreSynchronization = true; + + <%= expect() %>(await helpBlock.getText())<%= to() %>.equal('This password is not correct.'); + }); + }); + }); }); diff --git a/templates/app/e2e/account(auth)/logout/logout.spec(jasmine).js b/templates/app/e2e/account(auth)/logout/logout.spec(jasmine).js index f32722410..8e99a0bee 100644 --- a/templates/app/e2e/account(auth)/logout/logout.spec(jasmine).js +++ b/templates/app/e2e/account(auth)/logout/logout.spec(jasmine).js @@ -1,53 +1,51 @@ -'use strict'; - -var config = browser.params;<% if (filters.mongooseModels) { %> -var UserModel = require(config.serverConfig.root + '/server/api/user/user.model').default;<% } %><% if (filters.sequelizeModels) { %> -var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> +const config = browser.params;<% if (filters.mongooseModels) { %> +import UserModel from '../../../server/api/user/user.model';<% } %><% if (filters.sequelizeModels) { %> +import {User as UserModel} from '../../../server/sqldb';<% } %> +import {LoginPage} from '../login/login.po'; +import {NavbarComponent} from '../../components/navbar/navbar.po'; describe('Logout View', function() { - var login = function(user) { - browser.get(config.baseUrl + '/login'); - require('../login/login.po').login(user); - }; - - var testUser = { - name: 'Test User', - email: 'test@example.com', - password: 'test' - }; - - beforeEach(function(done) { - <% if (filters.mongooseModels) { %>UserModel.remove()<% } - if (filters.sequelizeModels) { %>UserModel.destroy({ where: {} })<% } %> - .then(function() { - <% if (filters.mongooseModels) { %>return UserModel.create(testUser);<% } - if (filters.sequelizeModels) { %>return UserModel.create(testUser);<% } %> - }) - .then(function() { - return login(testUser); - }) - .finally(function() { - browser.wait(function() { - return browser.executeScript('return !!window.angular'); - }, 5000).then(done); - }); - }); - - describe('with local auth', function() { - - it('should logout a user and redirecting to "/"', function() { - var navbar = require('../../components/navbar/navbar.po'); - - expect(browser.getCurrentUrl()).toBe(config.baseUrl + '/'); - expect(navbar.navbarAccountGreeting.getText()).toBe('Hello ' + testUser.name); - - browser.get(config.baseUrl + '/logout'); - - navbar = require('../../components/navbar/navbar.po'); - - expect(browser.getCurrentUrl()).toBe(config.baseUrl + '/'); - expect(navbar.navbarAccountGreeting.isDisplayed()).toBe(false); + const login = async (user) => { + await browser.get(`${config.baseUrl}/login`); + + const loginPage = new LoginPage(); + await loginPage.login(user); + }; + + const testUser = { + name: 'Test User', + email: 'test@example.com', + password: 'test' + }; + + beforeEach(async function() { + await UserModel + <% if (filters.mongooseModels) { %>.remove();<% } + if (filters.sequelizeModels) { %>.destroy({ where: {} });<% } %> + + <% if (filters.mongooseModels) { %>await UserModel.create(testUser);<% } + if (filters.sequelizeModels) { %>await UserModel.create(testUser);<% } %> + + await login(testUser); }); - }); + describe('with local auth', function() { + it('should logout a user and redirect to "/home"', async function() { + let navbar = new NavbarComponent(); + + browser.ignoreSynchronization = false; + await browser.wait(() => browser.getCurrentUrl(), 5000, 'URL didn\'t change after 5s'); + browser.ignoreSynchronization = true; + + expect(await browser.getCurrentUrl()).toBe(`${config.baseUrl}/home`); + expect(await navbar.navbarAccountGreeting.getText()).toBe(`Hello ${testUser.name}`); + + await navbar.logout(); + + navbar = new NavbarComponent(); + + expect(await browser.getCurrentUrl()).toBe(`${config.baseUrl}/home`); + expect(await navbar.navbarAccountGreeting.isDisplayed()).toBe(false); + }); + }); }); diff --git a/templates/app/e2e/account(auth)/logout/logout.spec(mocha).js b/templates/app/e2e/account(auth)/logout/logout.spec(mocha).js index d1b170272..cec559af0 100644 --- a/templates/app/e2e/account(auth)/logout/logout.spec(mocha).js +++ b/templates/app/e2e/account(auth)/logout/logout.spec(mocha).js @@ -1,55 +1,56 @@ -'use strict'; - -var config = browser.params;<% if (filters.mongooseModels) { %> -var UserModel = require(config.serverConfig.root + '/server/api/user/user.model').default;<% } %><% if (filters.sequelizeModels) { %> -var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> +const config = browser.params;<% if (filters.mongooseModels) { %> +import UserModel from '../../../server/api/user/user.model';<% } %><% if (filters.sequelizeModels) { %> +import {User as UserModel} from '../../../server/sqldb';<% } %> +import {LoginPage} from '../login/login.po'; +import {NavbarComponent} from '../../components/navbar/navbar.po'; describe('Logout View', function() { - var login = function(user) { - let promise = browser.get(config.baseUrl + '/login'); - require('../login/login.po').login(user); - return promise; - }; - - var testUser = { - name: 'Test User', - email: 'test@example.com', - password: 'test' - }; - - beforeEach(function() { - return UserModel - <% if (filters.mongooseModels) { %>.remove()<% } - if (filters.sequelizeModels) { %>.destroy({ where: {} })<% } %> - .then(function() { - <% if (filters.mongooseModels) { %>return UserModel.create(testUser);<% } - if (filters.sequelizeModels) { %>return UserModel.create(testUser);<% } %> - }) - .then(function() { - return login(testUser); - }); - }); - - after(function() { - <% if (filters.mongooseModels) { %>return UserModel.remove();<% } - if (filters.sequelizeModels) { %>return UserModel.destroy({ where: {} });<% } %> - }) - - describe('with local auth', function() { - - it('should logout a user and redirecting to "/"', function() { - var navbar = require('../../components/navbar/navbar.po'); - - <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/'); - <%= expect() %>navbar.navbarAccountGreeting.getText()<%= to() %>.eventually.equal('Hello ' + testUser.name); - - browser.get(config.baseUrl + '/logout'); - - navbar = require('../../components/navbar/navbar.po'); - - <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/'); - <%= expect() %>navbar.navbarAccountGreeting.isDisplayed()<%= to() %>.eventually.equal(false); + const login = async (user) => { + await browser.get(`${config.baseUrl}/login`); + + const loginPage = new LoginPage(); + await loginPage.login(user); + }; + + const testUser = { + name: 'Test User', + email: 'test@example.com', + password: 'test' + }; + + beforeEach(async function() { + await UserModel + <% if (filters.mongooseModels) { %>.remove();<% } + if (filters.sequelizeModels) { %>.destroy({ where: {} });<% } %> + + <% if (filters.mongooseModels) { %>await UserModel.create(testUser);<% } + if (filters.sequelizeModels) { %>await UserModel.create(testUser);<% } %> + + await login(testUser); + }); + + after(function() { + <% if (filters.mongooseModels) { %>return UserModel.remove();<% } + if (filters.sequelizeModels) { %>return UserModel.destroy({ where: {} });<% } %> }); - }); + describe('with local auth', function() { + it('should logout a user and redirect to "/home"', async function() { + let navbar = new NavbarComponent(); + + browser.ignoreSynchronization = false; + await browser.wait(() => browser.getCurrentUrl(), 5000, 'URL didn\'t change after 5s'); + browser.ignoreSynchronization = true; + + <%= expect() %>(await browser.getCurrentUrl())<%= to() %>.equal(`${config.baseUrl}/home`); + <%= expect() %>(await navbar.navbarAccountGreeting.getText())<%= to() %>.equal(`Hello ${testUser.name}`); + + await navbar.logout(); + + navbar = new NavbarComponent(); + + <%= expect() %>(await browser.getCurrentUrl())<%= to() %>.equal(`${config.baseUrl}/home`); + <%= expect() %>(await navbar.navbarAccountGreeting.isDisplayed())<%= to() %>.equal(false); + }); + }); }); diff --git a/templates/app/e2e/account(auth)/signup/signup.po.js b/templates/app/e2e/account(auth)/signup/signup.po.js index aee1f4194..bd82a5b28 100644 --- a/templates/app/e2e/account(auth)/signup/signup.po.js +++ b/templates/app/e2e/account(auth)/signup/signup.po.js @@ -3,28 +3,28 @@ * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ */ -'use strict'; +import {OauthButtons} from '../../components/oauth-buttons/oauth-buttons.po'; -var SignupPage = function() { - var form = this.form = element(by.css('.form')); - form.name = form.element(by.model('vm.user.name')); - form.email = form.element(by.model('vm.user.email')); - form.password = form.element(by.model('vm.user.password')); - form.confirmPassword = form.element(by.model('vm.user.confirmPassword')); - form.submit = form.element(by.css('.btn-register'));<% if (filters.oauth) { %> - form.oauthButtons = require('../../components/oauth-buttons/oauth-buttons.po').oauthButtons;<% } %> - - this.signup = function(data) { - for (var prop in data) { - var formElem = form[prop]; - if (data.hasOwnProperty(prop) && formElem && typeof formElem.sendKeys === 'function') { - formElem.sendKeys(data[prop]); - } +export class SignupPage { + constructor() { + this.form = element(by.css('.form')); + let form = this.form; + form.name = form.element(by.name('name')); + form.email = form.element(by.name('email')); + form.password = form.element(by.name('password')); + form.confirmPassword = form.element(by.name('confirmPassword')); + form.submit = form.element(by.css('.btn-register')); + form.oauthButtons = (new OauthButtons()).oauthButtons; } - return form.submit.click(); - }; -}; - -module.exports = new SignupPage(); + signup(data) { + for(let prop in data) { + let formElem = this.form[prop]; + if(data.hasOwnProperty(prop) && formElem && typeof formElem.sendKeys === 'function') { + formElem.sendKeys(data[prop]); + } + } + return this.form.submit.click(); + } +} diff --git a/templates/app/e2e/account(auth)/signup/signup.spec(jasmine).js b/templates/app/e2e/account(auth)/signup/signup.spec(jasmine).js index a70f76f19..d17ea34ba 100644 --- a/templates/app/e2e/account(auth)/signup/signup.spec(jasmine).js +++ b/templates/app/e2e/account(auth)/signup/signup.spec(jasmine).js @@ -1,79 +1,77 @@ -'use strict'; - -var config = browser.params;<% if (filters.mongooseModels) { %> -var UserModel = require(config.serverConfig.root + '/server/api/user/user.model').default;<% } %><% if (filters.sequelizeModels) { %> -var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> +const config = browser.params;<% if (filters.mongooseModels) { %> +import UserModel from '../../../server/api/user/user.model';<% } %><% if (filters.sequelizeModels) { %> +import {User as UserModel} from '../../../server/sqldb';<% } %> +import {SignupPage} from './signup.po'; +import {NavbarComponent} from '../../components/navbar/navbar.po'; describe('Signup View', function() { - var page; - - var loadPage = function() { - browser.manage().deleteAllCookies(); - browser.get(config.baseUrl + '/signup'); - page = require('./signup.po'); - }; - - var testUser = { - name: 'Test', - email: 'test@example.com', - password: 'test', - confirmPassword: 'test' - }; - - beforeEach(function(done) { - loadPage(); - browser.wait(function() { - return browser.executeScript('return !!window.angular'); - }, 5000).then(done); - }); - - it('should include signup form with correct inputs and submit button', function() { - expect(page.form.name.getAttribute('type')).toBe('text'); - expect(page.form.name.getAttribute('name')).toBe('name'); - expect(page.form.email.getAttribute('type')).toBe('email'); - expect(page.form.email.getAttribute('name')).toBe('email'); - expect(page.form.password.getAttribute('type')).toBe('password'); - expect(page.form.password.getAttribute('name')).toBe('password'); - expect(page.form.confirmPassword.getAttribute('type')).toBe('password'); - expect(page.form.confirmPassword.getAttribute('name')).toBe('confirmPassword'); - expect(page.form.submit.getAttribute('type')).toBe('submit'); - expect(page.form.submit.getText()).toBe('Sign up'); - });<% if (filters.oauth) { %> - - it('should include oauth buttons with correct classes applied', function() {<% if (filters.facebookAuth) { %> - expect(page.form.oauthButtons.facebook.getText()).toBe('Connect with Facebook'); - expect(page.form.oauthButtons.facebook.getAttribute('class')).toMatch('btn-block');<% } if (filters.googleAuth) { %> - expect(page.form.oauthButtons.google.getText()).toBe('Connect with Google+'); - expect(page.form.oauthButtons.google.getAttribute('class')).toMatch('btn-block');<% } if (filters.twitterAuth) { %> - expect(page.form.oauthButtons.twitter.getText()).toBe('Connect with Twitter'); - expect(page.form.oauthButtons.twitter.getAttribute('class')).toMatch('btn-block');<% } %> - });<% } %> - - describe('with local auth', function() { - - beforeAll(function(done) { - <% if (filters.mongooseModels) { %>UserModel.remove().then(done);<% } - if (filters.sequelizeModels) { %>UserModel.destroy({ where: {} }).then(done);<% } %> + let page; + + const loadPage = () => { + browser.manage().deleteAllCookies(); + return browser.get(`${config.baseUrl}/signup`).then(() => { + page = new SignupPage(); + }); + }; + + const testUser = { + name: 'Test', + email: 'test@example.com', + password: 'test1234', + confirmPassword: 'test1234' + }; + + beforeEach(() => loadPage()); + + it('should include signup form with correct inputs and submit button', function() { + expect(page.form.name.getAttribute('type')).toBe('text'); + expect(page.form.name.getAttribute('name')).toBe('name'); + expect(page.form.email.getAttribute('type')).toBe('email'); + expect(page.form.email.getAttribute('name')).toBe('email'); + expect(page.form.password.getAttribute('type')).toBe('password'); + expect(page.form.password.getAttribute('name')).toBe('password'); + expect(page.form.confirmPassword.getAttribute('type')).toBe('password'); + expect(page.form.confirmPassword.getAttribute('name')).toBe('confirmPassword'); + expect(page.form.submit.getAttribute('type')).toBe('submit'); + expect(page.form.submit.getText()).toBe('Sign up'); + });<% if (filters.oauth) { %> + + it('should include oauth buttons with correct classes applied', function() {<% if (filters.facebookAuth) { %> + expect(page.form.oauthButtons.facebook.getText()).toBe('Connect with Facebook');<% } if (filters.googleAuth) { %> + expect(page.form.oauthButtons.google.getText()).toBe('Connect with Google+');<% } if (filters.twitterAuth) { %> + expect(page.form.oauthButtons.twitter.getText()).toBe('Connect with Twitter');<% } %> + });<% } %> + + describe('with local auth', function() { + beforeAll(() => { + return <% if (filters.mongooseModels) { %>UserModel.remove().then(done);<% } + if (filters.sequelizeModels) { %>UserModel.destroy({ where: {} }).then(done);<% } %> + }); + + it('should signup a new user, log them in, and redirecting to "/"', async function() { + await page.signup(testUser); + + browser.ignoreSynchronization = false; + await browser.wait(() => browser.getCurrentUrl(), 5000, 'URL didn\'t change after 5s'); + browser.ignoreSynchronization = true; + + let navbar = new NavbarComponent(); + + expect(await browser.getCurrentUrl()).toBe(`${config.baseUrl}/home`); + expect(await navbar.navbarAccountGreeting.getText()).toBe('Hello ' + testUser.name); + }); + + it('should indicate signup failures', async function() { + await page.signup(testUser); + + browser.ignoreSynchronization = false; + await browser.wait(() => browser.getCurrentUrl(), 5000, 'URL didn\'t change after 5s'); + browser.ignoreSynchronization = true; + + expect(await browser.getCurrentUrl()).toBe(`${config.baseUrl}/signup`); + + let helpBlock = page.form.element(by.css('.form-group.has-error .help-block:not([hidden])')); + expect(await helpBlock.getText()).toBe('This email address is already in use.'); + }); }); - - it('should signup a new user, log them in, and redirecting to "/"', function() { - page.signup(testUser); - - var navbar = require('../../components/navbar/navbar.po'); - - expect(browser.getCurrentUrl()).toBe(config.baseUrl + '/'); - expect(navbar.navbarAccountGreeting.getText()).toBe('Hello ' + testUser.name); - }); - - it('should indicate signup failures', function() { - page.signup(testUser); - - expect(browser.getCurrentUrl()).toBe(config.baseUrl + '/signup'); - expect(page.form.email.getAttribute('class')).toContain('ng-invalid-mongoose'); - - var helpBlock = page.form.element(by.css('.form-group.has-error .help-block.ng-binding')); - expect(helpBlock.getText()).toBe('The specified email address is already in use.'); - }); - - }); }); diff --git a/templates/app/e2e/account(auth)/signup/signup.spec(mocha).js b/templates/app/e2e/account(auth)/signup/signup.spec(mocha).js index f7debbe47..1ae0b1570 100644 --- a/templates/app/e2e/account(auth)/signup/signup.spec(mocha).js +++ b/templates/app/e2e/account(auth)/signup/signup.spec(mocha).js @@ -1,89 +1,86 @@ -'use strict'; - -var config = browser.params;<% if (filters.mongooseModels) { %> -var UserModel = require(config.serverConfig.root + '/server/api/user/user.model').default;<% } %><% if (filters.sequelizeModels) { %> -var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> +const config = browser.params;<% if (filters.mongooseModels) { %> +import UserModel from '../../../server/api/user/user.model';<% } %><% if (filters.sequelizeModels) { %> +import {User as UserModel} from '../../../server/sqldb';<% } %> +import {SignupPage} from './signup.po'; +import {NavbarComponent} from '../../components/navbar/navbar.po'; describe('Signup View', function() { - var page; - - var loadPage = function() { - browser.manage().deleteAllCookies() - let promise = browser.get(config.baseUrl + '/signup'); - page = require('./signup.po'); - return promise; - }; - - var testUser = { - name: 'Test', - email: 'test@example.com', - password: 'test', - confirmPassword: 'test' - }; - - before(function() { - return loadPage(); - }); - - after(function() { - <% if (filters.mongooseModels) { %>return UserModel.remove();<% } - if (filters.sequelizeModels) { %>return UserModel.destroy({ where: {} });<% } %> - }); - - it('should include signup form with correct inputs and submit button', function() { - <%= expect() %>page.form.name.getAttribute('type')<%= to() %>.eventually.equal('text'); - <%= expect() %>page.form.name.getAttribute('name')<%= to() %>.eventually.equal('name'); - <%= expect() %>page.form.email.getAttribute('type')<%= to() %>.eventually.equal('email'); - <%= expect() %>page.form.email.getAttribute('name')<%= to() %>.eventually.equal('email'); - <%= expect() %>page.form.password.getAttribute('type')<%= to() %>.eventually.equal('password'); - <%= expect() %>page.form.password.getAttribute('name')<%= to() %>.eventually.equal('password'); - <%= expect() %>page.form.confirmPassword.getAttribute('type')<%= to() %>.eventually.equal('password'); - <%= expect() %>page.form.confirmPassword.getAttribute('name')<%= to() %>.eventually.equal('confirmPassword'); - <%= expect() %>page.form.submit.getAttribute('type')<%= to() %>.eventually.equal('submit'); - <%= expect() %>page.form.submit.getText()<%= to() %>.eventually.equal('Sign up'); - });<% if (filters.oauth) { %> - - it('should include oauth buttons with correct classes applied', function() {<% if (filters.facebookAuth) { %> - <%= expect() %>page.form.oauthButtons.facebook.getText()<%= to() %>.eventually.equal('Connect with Facebook'); - <%= expect() %>page.form.oauthButtons.facebook.getAttribute('class')<%= to() %>.eventually.contain('btn-block');<% } if (filters.googleAuth) { %> - <%= expect() %>page.form.oauthButtons.google.getText()<%= to() %>.eventually.equal('Connect with Google+'); - <%= expect() %>page.form.oauthButtons.google.getAttribute('class')<%= to() %>.eventually.contain('btn-block');<% } if (filters.twitterAuth) { %> - <%= expect() %>page.form.oauthButtons.twitter.getText()<%= to() %>.eventually.equal('Connect with Twitter'); - <%= expect() %>page.form.oauthButtons.twitter.getAttribute('class')<%= to() %>.eventually.contain('btn-block');<% } %> - });<% } %> - - describe('with local auth', function() { - - before(function() { - <% if (filters.mongooseModels) { %>return UserModel.remove();<% } - if (filters.sequelizeModels) { %>return UserModel.destroy({ where: {} });<% } %> - }) - - it('should signup a new user, log them in, and redirecting to "/"', function() { - page.signup(testUser); - - var navbar = require('../../components/navbar/navbar.po'); - - <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/'); - <%= expect() %>navbar.navbarAccountGreeting.getText()<%= to() %>.eventually.equal('Hello ' + testUser.name); + let page; + + const loadPage = () => { + browser.manage().deleteAllCookies(); + return browser.get(`${config.baseUrl}/signup`).then(() => { + page = new SignupPage(); + }); + }; + + const testUser = { + name: 'Test', + email: 'test@example.com', + password: 'test1234', + confirmPassword: 'test1234' + }; + + before(() => loadPage()); + + after(() => { + <% if (filters.mongooseModels) { %>return UserModel.remove();<% } + if (filters.sequelizeModels) { %>return UserModel.destroy({ where: {} });<% } %> }); - describe('and invalid credentials', function() { - before(function() { - return loadPage(); - }); - - it('should indicate signup failures', function() { - page.signup(testUser); - - <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/signup'); - <%= expect() %>page.form.email.getAttribute('class')<%= to() %>.eventually.contain('ng-invalid-mongoose'); - - var helpBlock = page.form.element(by.css('.form-group.has-error .help-block.ng-binding')); - <%= expect() %>helpBlock.getText()<%= to() %>.eventually.equal('The specified email address is already in use.'); - }); - + it('should include signup form with correct inputs and submit button', function() { + <%= expect() %>page.form.name.getAttribute('type')<%= to() %>.eventually.equal('text'); + <%= expect() %>page.form.name.getAttribute('name')<%= to() %>.eventually.equal('name'); + <%= expect() %>page.form.email.getAttribute('type')<%= to() %>.eventually.equal('email'); + <%= expect() %>page.form.email.getAttribute('name')<%= to() %>.eventually.equal('email'); + <%= expect() %>page.form.password.getAttribute('type')<%= to() %>.eventually.equal('password'); + <%= expect() %>page.form.password.getAttribute('name')<%= to() %>.eventually.equal('password'); + <%= expect() %>page.form.confirmPassword.getAttribute('type')<%= to() %>.eventually.equal('password'); + <%= expect() %>page.form.confirmPassword.getAttribute('name')<%= to() %>.eventually.equal('confirmPassword'); + <%= expect() %>page.form.submit.getAttribute('type')<%= to() %>.eventually.equal('submit'); + <%= expect() %>page.form.submit.getText()<%= to() %>.eventually.equal('Sign up'); + });<% if (filters.oauth) { %> + + it('should include oauth buttons with correct classes applied', function() {<% if (filters.facebookAuth) { %> + <%= expect() %>page.form.oauthButtons.facebook.getText()<%= to() %>.eventually.equal('Connect with Facebook');<% } if (filters.googleAuth) { %> + <%= expect() %>page.form.oauthButtons.google.getText()<%= to() %>.eventually.equal('Connect with Google+');<% } if (filters.twitterAuth) { %> + <%= expect() %>page.form.oauthButtons.twitter.getText()<%= to() %>.eventually.equal('Connect with Twitter');<% } %> + });<% } %> + + describe('with local auth', function() { + before(() => { + <% if (filters.mongooseModels) { %>return UserModel.remove();<% } + if (filters.sequelizeModels) { %>return UserModel.destroy({ where: {} });<% } %> + }); + + it('should signup a new user, log them in, and redirecting to "/"', async function() { + await page.signup(testUser); + + browser.ignoreSynchronization = false; + await browser.wait(() => browser.getCurrentUrl(), 5000, 'URL didn\'t change after 5s'); + browser.ignoreSynchronization = true; + + let navbar = new NavbarComponent(); + + <%= expect() %>(await browser.getCurrentUrl())<%= to() %>.equal(`${config.baseUrl}/home`); + <%= expect() %>(await navbar.navbarAccountGreeting.getText())<%= to() %>.equal(`Hello ${testUser.name}`); + }); + + describe('and invalid credentials', function() { + before(() => loadPage()); + + it('should indicate signup failures', async function() { + await page.signup(testUser); + + browser.ignoreSynchronization = false; + await browser.wait(() => browser.getCurrentUrl(), 5000, 'URL didn\'t change after 5s'); + browser.ignoreSynchronization = true; + + <%= expect() %>(await browser.getCurrentUrl())<%= to() %>.equal(`${config.baseUrl}/signup`); + + let helpBlock = page.form.element(by.css('.form-group.has-error .help-block:not([hidden])')); + <%= expect() %>(await helpBlock.getText())<%= to() %>.equal('This email address is already in use.'); + }); + }); }); - - }); }); diff --git a/templates/app/e2e/components/navbar/navbar.po.js b/templates/app/e2e/components/navbar/navbar.po.js index 80a48418e..9ff3928df 100644 --- a/templates/app/e2e/components/navbar/navbar.po.js +++ b/templates/app/e2e/components/navbar/navbar.po.js @@ -3,14 +3,18 @@ * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ */ -'use strict'; +export class NavbarComponent { + constructor() { + this.navbar = element(by.css('.navbar')); + this.navbarHeader = this.navbar.element(by.css('.navbar-header')); + this.navbarNav = this.navbar.element(by.css('#navbar-main .nav.navbar-nav:not(.navbar-right)')); + this.navbarAccount = this.navbar.element(by.css('#navbar-main .nav.navbar-nav.navbar-right')); + this.navbarAccountGreeting = this.navbarAccount.element(by.css('.navbar-text')); + this.loginButton = this.navbar.element(by.linkText('Login')); + this.logoutButton = this.navbar.element(by.linkText('Log out')); + } -var NavbarComponent = function() { - this.navbar = element(by.css('.navbar')); - this.navbarHeader = this.navbar.element(by.css('.navbar-header')); - this.navbarNav = this.navbar.element(by.css('#navbar-main .nav.navbar-nav:not(.navbar-right)'));<% if (filters.auth) { %> - this.navbarAccount = this.navbar.element(by.css('#navbar-main .nav.navbar-nav.navbar-right')); - this.navbarAccountGreeting = this.navbarAccount.element(by.binding('getCurrentUser().name'));<% } %> -}; - -module.exports = new NavbarComponent(); + logout() { + return this.logoutButton.click(); + } +} diff --git a/templates/app/e2e/components/oauth-buttons(oauth)/oauth-buttons.po.js b/templates/app/e2e/components/oauth-buttons(oauth)/oauth-buttons.po.js index c25d2b994..da0634f30 100644 --- a/templates/app/e2e/components/oauth-buttons(oauth)/oauth-buttons.po.js +++ b/templates/app/e2e/components/oauth-buttons(oauth)/oauth-buttons.po.js @@ -2,14 +2,13 @@ * This file uses the Page Object pattern to define the main page for tests * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ */ +/* eslint-env protractor, node */ -'use strict'; - -var OauthButtons = function() { - var oauthButtons = this.oauthButtons = element(by.css('oauth-buttons'));<% if (filters.facebookAuth) { %> - oauthButtons.facebook = oauthButtons.element(by.css('.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.btn-facebook'));<% } if (filters.googleAuth) { %> - oauthButtons.google = oauthButtons.element(by.css('.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.btn-google'));<% } if (filters.twitterAuth) { %> - oauthButtons.twitter = oauthButtons.element(by.css('.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.btn-twitter'));<% } %> -}; - -module.exports = new OauthButtons(); +export class OauthButtons { + constructor() { + this.oauthButtons = element(by.css('oauth-buttons'));<% if (filters.facebookAuth) { %> + this.oauthButtons.facebook = this.oauthButtons.element(by.css('.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.btn-facebook'));<% } if (filters.googleAuth) { %> + this.oauthButtons.google = this.oauthButtons.element(by.css('.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.btn-google'));<% } if (filters.twitterAuth) { %> + this.oauthButtons.twitter = this.oauthButtons.element(by.css('.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.btn-twitter'));<% } %> + } +} diff --git a/templates/app/e2e/main/main.po.js b/templates/app/e2e/main/main.po.js index 6718608c7..6c88179b4 100644 --- a/templates/app/e2e/main/main.po.js +++ b/templates/app/e2e/main/main.po.js @@ -3,13 +3,10 @@ * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ */ -'use strict'; - -var MainPage = function() { - this.heroEl = element(by.css('.hero-unit')); - this.h1El = this.heroEl.element(by.css('h1')); - this.imgEl = this.heroEl.element(by.css('img')); -}; - -module.exports = new MainPage(); - +export class MainPage { + constructor() { + this.heroEl = element(by.css('.hero-unit')); + this.h1El = this.heroEl.element(by.css('h1')); + this.imgEl = this.heroEl.element(by.css('img')); + } +} diff --git a/templates/app/e2e/main/main.spec(jasmine).js b/templates/app/e2e/main/main.spec(jasmine).js index 3d56cb5d3..d43806ed1 100644 --- a/templates/app/e2e/main/main.spec(jasmine).js +++ b/templates/app/e2e/main/main.spec(jasmine).js @@ -1,18 +1,18 @@ -'use strict'; - -var config = browser.params; +let config = browser.params; +import {MainPage} from './main.po'; describe('Main View', function() { - var page; + let page; - beforeEach(function() { - browser.get(config.baseUrl + '/'); - page = require('./main.po'); - }); + beforeEach(() => { + return browser.get(`${config.baseUrl}/`).then(() => { + page = new MainPage(); + }); + }); - it('should include jumbotron with correct data', function() { - expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); - expect(page.imgEl.getAttribute('src')).toMatch(/yeoman(\.[a-zA-Z0-9]*)?\.png$/); - expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); - }); + it('should include jumbotron with correct data', function() { + expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); + expect(page.imgEl.getAttribute('src')).toMatch(/yeoman(\.[a-zA-Z0-9]*)?\.png$/); + expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); + }); }); diff --git a/templates/app/e2e/main/main.spec(mocha).js b/templates/app/e2e/main/main.spec(mocha).js index 12b0781db..67e3d2de3 100644 --- a/templates/app/e2e/main/main.spec(mocha).js +++ b/templates/app/e2e/main/main.spec(mocha).js @@ -1,19 +1,18 @@ -'use strict'; - -var config = browser.params; +let config = browser.params; +import {MainPage} from './main.po'; describe('Main View', function() { - var page; + let page; - beforeEach(function() { - let promise = browser.get(config.baseUrl + '/'); - page = require('./main.po'); - return promise; - }); + beforeEach(() => { + return browser.get(`${config.baseUrl}/`).then(() => { + page = new MainPage(); + }); + }); - it('should include jumbotron with correct data', function() { - <%= expect() %>page.h1El.getText()<%= to() %>.eventually.equal('\'Allo, \'Allo!'); - <%= expect() %>page.imgEl.getAttribute('src')<%= to() %>.eventually.match(/yeoman(\.[a-zA-Z0-9]*)?\.png$/); - <%= expect() %>page.imgEl.getAttribute('alt')<%= to() %>.eventually.equal('I\'m Yeoman'); - }); + it('should include jumbotron with correct data', function() { + <%= expect() %>page.h1El.getText()<%= to() %>.eventually.equal('\'Allo, \'Allo!'); + <%= expect() %>page.imgEl.getAttribute('src')<%= to() %>.eventually.match(/yeoman(\.[a-zA-Z0-9]*)?\.png$/); + <%= expect() %>page.imgEl.getAttribute('alt')<%= to() %>.eventually.equal('I\'m Yeoman'); + }); }); diff --git a/templates/app/mocha.conf.js b/templates/app/mocha.conf.js index 76f56625e..e0b771496 100644 --- a/templates/app/mocha.conf.js +++ b/templates/app/mocha.conf.js @@ -3,7 +3,7 @@ // Register the Babel require hook require('babel-core/register'); -var chai = require('chai'); +const chai = require('chai'); // Load Chai assertions global.expect = chai.expect; @@ -16,4 +16,4 @@ global.sinon = require('sinon'); // Initialize Chai plugins chai.use(require('sinon-chai')); chai.use(require('chai-as-promised')); -chai.use(require('chai-things')) +chai.use(require('chai-things')); diff --git a/templates/app/protractor.conf.js b/templates/app/protractor.conf.js index 021a54eaf..ca911b15d 100644 --- a/templates/app/protractor.conf.js +++ b/templates/app/protractor.conf.js @@ -13,10 +13,7 @@ var config = { // with relative paths will be prepended with this. baseUrl: 'http://localhost:' + (process.env.PORT || '<%= Number(devPort) + 1 %>'), - // Credientials for Saucelabs - sauceUser: process.env.SAUCE_USERNAME, - - sauceKey: process.env.SAUCE_ACCESS_KEY, + directConnect: true, // list of files / patterns to load in the browser specs: [ @@ -35,8 +32,9 @@ var config = { capabilities: { 'browserName': 'chrome', 'name': 'Fullstack E2E', - 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, - 'build': process.env.TRAVIS_BUILD_NUMBER + 'chromeOptions': { + 'args': ['show-fps-counter=true'] + }, }, // ----- The test framework ----- diff --git a/templates/app/server/api/user(auth)/index.js b/templates/app/server/api/user(auth)/index.js index fad00f650..fb6ec0323 100644 --- a/templates/app/server/api/user(auth)/index.js +++ b/templates/app/server/api/user(auth)/index.js @@ -4,7 +4,7 @@ import {Router} from 'express'; import * as controller from './user.controller'; import * as auth from '../../auth/auth.service'; -var router = new Router(); +var router = Router(); router.get('/', auth.hasRole('admin'), controller.index); router.delete('/:id', auth.hasRole('admin'), controller.destroy); diff --git a/templates/app/webpack.make.js b/templates/app/webpack.make.js index bd1886f72..3e6e0e962 100644 --- a/templates/app/webpack.make.js +++ b/templates/app/webpack.make.js @@ -122,7 +122,23 @@ module.exports = function makeWebpackConfig(options) { use: [{ loader: 'babel-loader', options: { - plugins: TEST ? ['istanbul'] : [], + presets: [ + ['babel-preset-env', { + // debug: true, + targets: { + browsers: ['last 2 versions', 'not ie < 11'], + }, + modules: false, + }] + ], + plugins: [ + 'transform-flow-comments', + 'angular2-annotations', + 'transform-runtime', + 'transform-decorators-legacy', + 'transform-class-properties', + 'transform-export-extensions', + ].concat(TEST ? ['istanbul'] : []), } }].concat(DEV ? '@angularclass/hmr-loader' : []), include: [