From 0e38b5bdc3a782623d34025ef3cb4ce38412ab70 Mon Sep 17 00:00:00 2001 From: Pascal Date: Sat, 25 Mar 2017 12:56:16 +0100 Subject: [PATCH 1/3] E2E Tests and deployment documentation --- README.md | 74 +++++++++++++++++++++++++++++++++++++++++-- circle.yml | 13 ++++++++ e2e/dashboard.spec.js | 22 +++++++++++++ e2e/login.po.js | 22 +++++++++++++ e2e/login.spec.js | 48 ++++++++++++++++++++++++++++ e2e/main.po.js | 12 +++++-- e2e/main.spec.js | 21 ------------ gulp/server.js | 2 +- package.json | 19 +++++++---- protractor.conf.js | 4 +-- 10 files changed, 201 insertions(+), 36 deletions(-) create mode 100644 circle.yml create mode 100644 e2e/dashboard.spec.js create mode 100644 e2e/login.po.js create mode 100644 e2e/login.spec.js delete mode 100755 e2e/main.spec.js diff --git a/README.md b/README.md index 7f15a6a..65a0a9e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,75 @@ # support-admin-app -Support application
+Support application Internal application used to administer specific support tasks related to the Topcoder platform. -. + +## Software Requirements + +- node.js v6+ +- npm v3+ + +## Installation + +To install npm and bower dependencies run: + +> npm install + +Bower is set to run as a npm postinstall script. + +## Configuration + +The configuration is provided in `config.json` in the base directory. +It contains four environments (`local`, `dev`, `qa`, `prod`) which are controlled by the BUILD_ENV environment variable, +it defaults to the `dev` environment if BUILD_ENV is empty. + +The following configuration parameters are available: + +| Name | Description | +|--------------------------|---------------------------------| +| ES_PROJECT_API_URL | URL of the ES project API | +| API_URL | URL of the topcoder API | +| WORK_API_URL | URL of the topcoder work API | +| ADMIN_TOOL_URL | URL of the admin tool API | +| API_VERSION_PATH | Version of the API | +| AUTH0_CLIENT_ID | Client ID for Auth0 | +| AUTH0_DOMAIN | Domain for Auth0 authentication | +| AUTH0_TOKEN_NAME | Auth0 token name | +| AUTH0_REFRESH_TOKEN_NAME | Auth0 refresh token name | + +## Start the Application + +Simply execute the following command to start the app in development mode (with browsersync) + +> npm start + +To build the application to be hosted on a real webserver run: + +> npm run build + +## Execute E2E Tests + +> npm test + +## Fallback instruction in case the npm scripts fail + +### Install global dependencies + +> npm install -g gulp@3.8.10 bower + +### Install project dependencies + +> npm install + +> bower install + +### Start the Application + +> gulp serve + +### Build the Application + +> gulp build + +### Execute E2E Tests + +> gulp protractor diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..45d3737 --- /dev/null +++ b/circle.yml @@ -0,0 +1,13 @@ +machine: + node: + version: 6.9.5 + environment: + # Fix issue with selenium-server in containers. + # See http://github.com/SeleniumHQ/docker-selenium/issues/87 + DBUS_SESSION_BUS_ADDRESS: /dev/null + +dependencies: + post: + # install latest google chrome + - curl -L -o google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb + - sudo dpkg -i google-chrome.deb diff --git a/e2e/dashboard.spec.js b/e2e/dashboard.spec.js new file mode 100644 index 0000000..112452a --- /dev/null +++ b/e2e/dashboard.spec.js @@ -0,0 +1,22 @@ +'use strict'; + +describe('The dashboard view', function () { + var page; + var mainPage; + + beforeEach(function () { + browser.get('http://localhost:3000/'); + page = require('./login.po'); + mainPage = require('./main.po'); + page.usernameInput.sendKeys('amy_admin'); + page.passwordInput.sendKeys('topcoder1'); + page.loginButton.click(); + }); + + it('should find members', function() { + mainPage.searchHandleInput.sendKeys('sah2ed'); + mainPage.searchButton.click(); + expect(mainPage.users.count()).toBe(1); + expect(mainPage.users.get(0).element(by.cssContainingText('td', 'sah2ed')).getText()).toBe('sah2ed'); + }); +}); diff --git a/e2e/login.po.js b/e2e/login.po.js new file mode 100644 index 0000000..14077e3 --- /dev/null +++ b/e2e/login.po.js @@ -0,0 +1,22 @@ +/** + * This file uses the Page Object pattern to define the main page for tests + * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ + */ + +'use strict'; + +var LoginPage = function() { + /*this.jumbEl = element(by.css('.jumbotron')); + this.h1El = this.jumbEl.element(by.css('h1')); + this.imgEl = this.jumbEl.element(by.css('img')); + this.thumbnailEls = element(by.css('body')).all(by.repeater('awesomeThing in awesomeThings'));*/ + this.loginForm = element(by.css('form')); + this.loginHeader = this.loginForm.element(by.css('h3')); + const inputs = this.loginForm.all(by.css('input')); + this.usernameInput = inputs.get(0); + this.passwordInput = inputs.get(1); + this.loginButton = this.loginForm.element(by.css('button')); + this.alerts = element(by.css('body')).all(by.repeater('alert in alerts')); +}; + +module.exports = new LoginPage(); diff --git a/e2e/login.spec.js b/e2e/login.spec.js new file mode 100644 index 0000000..2e46633 --- /dev/null +++ b/e2e/login.spec.js @@ -0,0 +1,48 @@ +'use strict'; + +describe('The login view', function () { + var page; + var mainPage; + + beforeEach(function () { + browser.get('http://localhost:3000/'); + page = require('./login.po'); + mainPage = require('./main.po'); + }); + + it('should display the correct form heading', function() { + expect(page.loginHeader.getText()).toBe('ADMIN APP LOGIN'); + //expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/); + //expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); + }); + + it('login should fail for wrong credentials', function () { + //expect(page.thumbnailEls.count()).toBeGreaterThan(5); + page.usernameInput.sendKeys('wrong'); + page.passwordInput.sendKeys('wrong'); + page.loginButton.click(); + expect(page.alerts.count()).toBe(1); + expect(page.alerts.get(0).getText()).toBe('Wrong username or password.'); + }); + + it('login should succeed for correct credentials', function () { + //expect(page.thumbnailEls.count()).toBeGreaterThan(5); + page.usernameInput.sendKeys('amy_admin'); + page.passwordInput.sendKeys('topcoder1'); + page.loginButton.click(); + expect(mainPage.isUserLoggedIn.isDisplayed()).toBeTruthy(); + expect(mainPage.loggedInUser.getText()).toBe('amy_admin'); + }); + + it('logout should work after login', function () { + //expect(page.thumbnailEls.count()).toBeGreaterThan(5); + page.usernameInput.sendKeys('amy_admin'); + page.passwordInput.sendKeys('topcoder1'); + page.loginButton.click(); + expect(mainPage.isUserLoggedIn.isDisplayed()).toBeTruthy(); + expect(mainPage.loggedInUser.getText()).toBe('amy_admin'); + mainPage.logout.click(); + expect(page.loginHeader.getText()).toBe('ADMIN APP LOGIN'); + }); + +}); diff --git a/e2e/main.po.js b/e2e/main.po.js index 6b88871..aff343f 100755 --- a/e2e/main.po.js +++ b/e2e/main.po.js @@ -5,11 +5,17 @@ 'use strict'; -var MainPage = function() { - this.jumbEl = element(by.css('.jumbotron')); +var MainPage = function() { + /*this.jumbEl = element(by.css('.jumbotron')); this.h1El = this.jumbEl.element(by.css('h1')); this.imgEl = this.jumbEl.element(by.css('img')); - this.thumbnailEls = element(by.css('body')).all(by.repeater('awesomeThing in awesomeThings')); + this.thumbnailEls = element(by.css('body')).all(by.repeater('awesomeThing in awesomeThings'));*/ + this.isUserLoggedIn = element(by.css('li[ng-show="authorized()"]')); + this.loggedInUser = this.isUserLoggedIn.element(by.css('strong')); + this.searchHandleInput = element(by.css('#search-condition-handle')); + this.searchButton = element(by.css('button[ng-click="search()"]')); + this.users = element(by.css('body')).all(by.repeater('user in users')); + this.logout = element(by.css('a[ng-click="logout()"]')); }; module.exports = new MainPage(); diff --git a/e2e/main.spec.js b/e2e/main.spec.js deleted file mode 100755 index da89d22..0000000 --- a/e2e/main.spec.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -describe('The main view', function () { - var page; - - beforeEach(function () { - browser.get('http://localhost:3000/index.html'); - page = require('./main.po'); - }); - - it('should include jumbotron with correct data', function() { - expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); - expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/); - expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); - }); - - it('list more than 5 awesome things', function () { - expect(page.thumbnailEls.count()).toBeGreaterThan(5); - }); - -}); diff --git a/gulp/server.js b/gulp/server.js index 5740a54..f5f4f92 100755 --- a/gulp/server.js +++ b/gulp/server.js @@ -49,7 +49,7 @@ gulp.task('serve:dist', ['build'], function () { browserSyncInit(paths.dist); }); -gulp.task('serve:e2e', ['inject'], function () { +gulp.task('serve:e2e', ['inject', 'ng-config'], function () { browserSyncInit([paths.tmp + '/serve', paths.src], null, []); }); diff --git a/package.json b/package.json index 5628322..4d9caf5 100755 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "dependencies": {}, "devDependencies": { "bower": "~1.4.1", - "browser-sync": "~1.7.1", + "browser-sync": "~2.18.8", "chalk": "~0.5.1", "del": "~0.1.3", "gulp": "~3.8.10", @@ -23,7 +23,8 @@ "gulp-load-plugins": "~0.7.1", "gulp-minify-html": "~0.1.7", "gulp-ng-annotate": "~1.0.0", - "gulp-protractor": "~0.0.11", + "gulp-ng-config": "~1.2.1", + "gulp-protractor": "~4.0.0", "gulp-rename": "~1.2.0", "gulp-replace": "~0.5.0", "gulp-rev": "~2.0.1", @@ -31,18 +32,24 @@ "gulp-size": "~1.1.0", "gulp-uglify": "~1.0.1", "gulp-useref": "~1.0.2", - "gulp-ng-config": "~1.2.1", "http-proxy": "~1.7.0", + "jasmine-core": "^2.5.2", "jshint-stylish": "~1.0.0", + "karma": "^0.12", "karma-jasmine": "~0.3.1", - "karma-phantomjs-launcher": "~0.1.4", + "karma-phantomjs-launcher": "~1.0.4", "main-bower-files": "~2.4.0", - "protractor": "~1.4.0", "require-dir": "~0.1.0", "uglify-save-license": "~0.4.1", "wiredep": "~2.2.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.0.0" + }, + "scripts": { + "postinstall": "bower install", + "build": "gulp build", + "start": "gulp serve", + "test": "gulp protractor" } } diff --git a/protractor.conf.js b/protractor.conf.js index 0f43a9e..6272896 100755 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -1,7 +1,5 @@ 'use strict'; -var paths = require('./.yo-rc.json')['generator-gulp-angular'].props.paths; - // An example configuration file. exports.config = { // The address of a running selenium server. @@ -15,7 +13,7 @@ exports.config = { // Spec patterns are relative to the current working directly when // protractor is called. - specs: [paths.e2e + '/**/*.js'], + specs: ['e2e/**/*.js'], // Options to be passed to Jasmine-node. jasmineNodeOpts: { From bca23685eb20fd46ca50fd1c4fe07c21824d333f Mon Sep 17 00:00:00 2001 From: sah2ed Date: Tue, 28 Mar 2017 18:42:09 +0100 Subject: [PATCH 2/3] Merge of selected changes from Sharathkumar92 into pfilippi's submission. --- .yo-rc.json | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 30 ++++++++++++-------- circle.yml | 29 +++++++++++++------ package.json | 26 ++++++++--------- 4 files changed, 130 insertions(+), 35 deletions(-) create mode 100644 .yo-rc.json diff --git a/.yo-rc.json b/.yo-rc.json new file mode 100644 index 0000000..2196edc --- /dev/null +++ b/.yo-rc.json @@ -0,0 +1,80 @@ +{ + "generator-gulp-angular": { + "version": "1.0.0", + "props": { + "angularVersion": "~1.4.2", + "angularModules": [ + { + "key": "animate", + "module": "ngAnimate" + }, + { + "key": "cookies", + "module": "ngCookies" + }, + { + "key": "touch", + "module": "ngTouch" + }, + { + "key": "sanitize", + "module": "ngSanitize" + }, + { + "key": "messages", + "module": "ngMessages" + }, + { + "key": "aria", + "module": "ngAria" + } + ], + "jQuery": { + "key": "jqLite" + }, + "resource": { + "key": "$http", + "module": null + }, + "router": { + "key": "angular-route", + "module": "ngRoute" + }, + "ui": { + "key": "angular-material", + "module": "ngMaterial" + }, + "cssPreprocessor": { + "key": "node-sass", + "extension": "scss" + }, + "jsPreprocessor": { + "key": "noJsPrepro", + "extension": "js", + "srcExtension": "js" + }, + "htmlPreprocessor": { + "key": "noHtmlPrepro", + "extension": "html" + }, + "bootstrapComponents": { + "name": null, + "version": null, + "key": null, + "module": null + }, + "foundationComponents": { + "name": null, + "version": null, + "key": null, + "module": null + }, + "paths": { + "src": "src", + "dist": "dist", + "e2e": "e2e", + "tmp": ".tmp" + } + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index 65a0a9e..cea1c58 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ Internal application used to administer specific support tasks related to the To - node.js v6+ - npm v3+ +- Google Chrome browser version >= 55.0.2883.0 ## Installation @@ -39,37 +40,42 @@ The following configuration parameters are available: ## Start the Application Simply execute the following command to start the app in development mode (with browsersync) - -> npm start +``` +npm install +npm start +``` +Application will be hosted and running at http://locahost:3000 To build the application to be hosted on a real webserver run: - -> npm run build +``` +npm run build +``` ## Execute E2E Tests -> npm test +```npm test``` ## Fallback instruction in case the npm scripts fail ### Install global dependencies -> npm install -g gulp@3.8.10 bower +```npm install -g gulp@3.8.10 bower``` ### Install project dependencies -> npm install - -> bower install +``` +npm install +bower install +``` ### Start the Application -> gulp serve +```gulp serve``` ### Build the Application -> gulp build +```gulp build``` ### Execute E2E Tests -> gulp protractor +```gulp protractor``` diff --git a/circle.yml b/circle.yml index 45d3737..93fc2e9 100644 --- a/circle.yml +++ b/circle.yml @@ -1,13 +1,24 @@ +##Set Node Js Version (Could be set to needed version) machine: node: - version: 6.9.5 + version: 6.8.1 + +# Add some environment variables environment: - # Fix issue with selenium-server in containers. - # See http://github.com/SeleniumHQ/docker-selenium/issues/87 - DBUS_SESSION_BUS_ADDRESS: /dev/null - + CIRCLE_ENV: dev + +## Customize dependencies dependencies: - post: - # install latest google chrome - - curl -L -o google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb - - sudo dpkg -i google-chrome.deb + pre: + - curl -s https://raw.githubusercontent.com/chronogolf/circleci-google-chrome/master/use_chrome_stable_version.sh | bash + + override: + - npm install + +compile: + override: + - npm run build + +test: + override: + - npm run test diff --git a/package.json b/package.json index 4d9caf5..5822935 100755 --- a/package.json +++ b/package.json @@ -3,10 +3,10 @@ "version": "2.3.0", "dependencies": {}, "devDependencies": { - "bower": "~1.4.1", + "bower": "~1.8.0", "browser-sync": "~2.18.8", "chalk": "~0.5.1", - "del": "~0.1.3", + "del": "~2.2.2", "gulp": "~3.8.10", "gulp-angular-filesort": "~1.0.4", "gulp-angular-templatecache": "~1.4.2", @@ -14,14 +14,13 @@ "gulp-awspublish": "^3.0.1", "gulp-consolidate": "~0.1.2", "gulp-csso": "~0.2.9", - "gulp-filter": "~1.0.2", + "gulp-filter": "~2.0.2", "gulp-flatten": "~0.0.4", "gulp-inject": "~1.0.2", - "gulp-jshint": "~1.9.0", - "gulp-karma": "~0.0.4", + "gulp-jshint": "~2.0.4", "gulp-less": "~1.3.6", - "gulp-load-plugins": "~0.7.1", - "gulp-minify-html": "~0.1.7", + "gulp-load-plugins": "~1.5.0", + "gulp-minify-html": "~1.0.6", "gulp-ng-annotate": "~1.0.0", "gulp-ng-config": "~1.2.1", "gulp-protractor": "~4.0.0", @@ -34,20 +33,19 @@ "gulp-useref": "~1.0.2", "http-proxy": "~1.7.0", "jasmine-core": "^2.5.2", + "jshint":"~2.9.4", "jshint-stylish": "~1.0.0", - "karma": "^0.12", - "karma-jasmine": "~0.3.1", - "karma-phantomjs-launcher": "~1.0.4", - "main-bower-files": "~2.4.0", + "main-bower-files": "~2.13.1", + "protractor": "~5.1.1", "require-dir": "~0.1.0", "uglify-save-license": "~0.4.1", - "wiredep": "~2.2.0" + "wiredep": "~4.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=0.10.0" }, "scripts": { - "postinstall": "bower install", + "postinstall": "bower install --config.interactive=false", "build": "gulp build", "start": "gulp serve", "test": "gulp protractor" From b970c1d440eb38719286346f1770f4ddff53394d Mon Sep 17 00:00:00 2001 From: sah2ed Date: Tue, 28 Mar 2017 18:58:28 +0100 Subject: [PATCH 3/3] Merge of selected changes from Sharathkumar92 into pfilippi's submission. --- protractor.conf.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protractor.conf.js b/protractor.conf.js index 6272896..b2c93a1 100755 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -1,19 +1,19 @@ 'use strict'; +var paths = require('./.yo-rc.json')['generator-gulp-angular'].props.paths; + // An example configuration file. exports.config = { - // The address of a running selenium server. - //seleniumAddress: 'http://localhost:4444/wd/hub', - //seleniumServerJar: deprecated, this should be set on node_modules/protractor/config.json // Capabilities to be passed to the webdriver instance. + capabilities: { 'browserName': 'chrome' }, // Spec patterns are relative to the current working directly when // protractor is called. - specs: ['e2e/**/*.js'], + specs: [paths.e2e + '/**/*.js'], // Options to be passed to Jasmine-node. jasmineNodeOpts: {