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 d56bc37..cea1c58 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,81 @@ # support-admin-app -Support application
+Support application Internal application used to administer specific support tasks related to the Topcoder platform. -User Management -Submission Management -Tag Management +## Software Requirements +- node.js v6+ +- npm v3+ +- Google Chrome browser version >= 55.0.2883.0 + +## 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 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 +``` + +## 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..93fc2e9 --- /dev/null +++ b/circle.yml @@ -0,0 +1,24 @@ +##Set Node Js Version (Could be set to needed version) +machine: + node: + version: 6.8.1 + +# Add some environment variables + environment: + CIRCLE_ENV: dev + +## Customize dependencies +dependencies: + 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/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..5822935 100755 --- a/package.json +++ b/package.json @@ -3,10 +3,10 @@ "version": "2.3.0", "dependencies": {}, "devDependencies": { - "bower": "~1.4.1", - "browser-sync": "~1.7.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,16 +14,16 @@ "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-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 +31,23 @@ "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":"~2.9.4", "jshint-stylish": "~1.0.0", - "karma-jasmine": "~0.3.1", - "karma-phantomjs-launcher": "~0.1.4", - "main-bower-files": "~2.4.0", - "protractor": "~1.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": ">=0.10.0" + }, + "scripts": { + "postinstall": "bower install --config.interactive=false", + "build": "gulp build", + "start": "gulp serve", + "test": "gulp protractor" } } diff --git a/protractor.conf.js b/protractor.conf.js index 0f43a9e..b2c93a1 100755 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -4,11 +4,9 @@ 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' },