From f076b482e9a8bdfbfab6bd61b25a39f38d1ecd1f Mon Sep 17 00:00:00 2001 From: David Baldwynn Date: Tue, 21 Nov 2017 14:05:58 -0800 Subject: [PATCH 1/3] moved to node v7 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1602d47e..0b3e4910 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ "url": "https://github.com/tellform/tellform.git" }, "engines": { - "node": "6.x.x", - "npm": "3.x.x" + "node": "7.10.1", + "npm": "4.2.0" }, "scripts": { "addcontrib": "all-contributors add", From df83c6bb931016f187a67825b774fa7b2d7d5d86 Mon Sep 17 00:00:00 2001 From: David Baldwynn Date: Tue, 21 Nov 2017 15:29:47 -0800 Subject: [PATCH 2/3] fixed create_admin script --- app/models/user.server.model.js | 2 +- config/env/all.js | 7 +++-- scripts/create_admin.js | 50 ++++++++++++++++++++------------- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/app/models/user.server.model.js b/app/models/user.server.model.js index fd80602d..ab0a7cc6 100755 --- a/app/models/user.server.model.js +++ b/app/models/user.server.model.js @@ -108,7 +108,7 @@ UserSchema.virtual('password').get(function () { /** * Create instance method for hashing a password */ -UserSchema.methods.hashPassword = function(password) { +UserSchema.statics.hashPassword = UserSchema.methods.hashPassword = function(password) { var encoding = 'base64'; var iterations = 10000; var keylen = 128; diff --git a/config/env/all.js b/config/env/all.js index 583f366f..eba2c9f0 100755 --- a/config/env/all.js +++ b/config/env/all.js @@ -16,10 +16,11 @@ module.exports = { }, - admin:{ + admin: { email: process.env.ADMIN_EMAIL || 'admin@admin.com', - username: process.env.ADMIN_USERNAME || 'root', - password: process.env.ADMIN_PASSWORD || 'root', + username: process.env.ADMIN_USERNAME || 'admin', + password: process.env.ADMIN_PASSWORD || 'admin', + roles: ['user', 'admin'] }, redisUrl: process.env.REDIS_URL || 'redis://127.0.0.1:6379', diff --git a/scripts/create_admin.js b/scripts/create_admin.js index 6d5ee5c6..ea3b1469 100644 --- a/scripts/create_admin.js +++ b/scripts/create_admin.js @@ -3,36 +3,46 @@ var config = require('../config/config'), chalk = require('chalk'); exports.run = function(app, db, cb) { + console.log(chalk.green('Creating the Admin Account')); var User = mongoose.model('User'); - var email = 'admin@admin.com' || config.admin.email; + var username = config.admin.username; + + console.log('username: ' + config.admin.username); + console.log('password: ' + config.admin.password); - var newUser = new User({ + var newUserObj = { firstName: 'Admin', lastName: 'Account', - email: email, - username: 'root' || config.admin.username, - password: 'root' || config.admin.password, - provider: 'local', - roles: ['admin', 'user'] - }); + email: config.admin.email, + username: username, + roles: config.admin.roles + }; - User.findOne({email: email}, function (err, user) { + var options = { + upsert: true, + new: true, + setDefaultsOnInsert: true + } + + User.findOneAndUpdate({username: username}, newUserObj, options, function (err, currUser1) { if (err) { - cb(err); + return cb(err); } - if(!user){ - newUser.save(function (userErr) { - if (userErr) { - return cb(userErr); - } - console.log(chalk.green('Successfully created Admin Account')); - - cb(); - }); + if(!currUser1){ + return cb(new Error('Couldn\'t create admin account')) } else { - cb('User already exists!'); + + currUser1.password = config.admin.password; + currUser1.save(function(err, currUser2){ + if (err) { + return cb(err); + } + + console.log(chalk.green('Successfully created/updated Admin Account')); + return cb(); + }); } }); } From 4f82a552277b1f759c76ab24831965371d3fb5c2 Mon Sep 17 00:00:00 2001 From: David Baldwynn Date: Tue, 21 Nov 2017 15:35:06 -0800 Subject: [PATCH 3/3] added selenium tests --- selenium/.editorconfig | 17 + selenium/.gitignore | 9 + selenium/.vscode/launch.json | 27 ++ selenium/README.md | 28 ++ selenium/commons/commons.md | 1 + selenium/config.json | 23 ++ selenium/hosts | 0 selenium/install.sh | 4 + selenium/package.json | 25 ++ selenium/run.bat | 7 + selenium/run.sh | 5 + selenium/test/loginAndChangeProfile.js | 411 +++++++++++++++++++++++ selenium/test/loginAndCreateForm.spec.js | 363 ++++++++++++++++++++ 13 files changed, 920 insertions(+) create mode 100644 selenium/.editorconfig create mode 100644 selenium/.gitignore create mode 100644 selenium/.vscode/launch.json create mode 100644 selenium/README.md create mode 100644 selenium/commons/commons.md create mode 100644 selenium/config.json create mode 100644 selenium/hosts create mode 100644 selenium/install.sh create mode 100644 selenium/package.json create mode 100644 selenium/run.bat create mode 100755 selenium/run.sh create mode 100644 selenium/test/loginAndChangeProfile.js create mode 100644 selenium/test/loginAndCreateForm.spec.js diff --git a/selenium/.editorconfig b/selenium/.editorconfig new file mode 100644 index 00000000..5a13b97d --- /dev/null +++ b/selenium/.editorconfig @@ -0,0 +1,17 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +# Apply for all files +[*] + +charset = utf-8 + +indent_style = space +indent_size = 4 + +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/selenium/.gitignore b/selenium/.gitignore new file mode 100644 index 00000000..704c683c --- /dev/null +++ b/selenium/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +.idea +node_modules +npm-debug.log +uirecorder.log +reports +screenshots/**/*.png +screenshots/**/*.html +screenshots/**/*.json diff --git a/selenium/.vscode/launch.json b/selenium/.vscode/launch.json new file mode 100644 index 00000000..ec025318 --- /dev/null +++ b/selenium/.vscode/launch.json @@ -0,0 +1,27 @@ +{ + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug UIRecorder Local", + "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", + "cwd": "${workspaceRoot}", + "args": ["--reporter", "mochawesome-uirecorder", "${file}"], + "env": { + "webdriver": "127.0.0.1" + } + }, + { + "type": "node", + "request": "launch", + "name": "Debug UIRecorder Default", + "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", + "cwd": "${workspaceRoot}", + "args": ["--reporter", "mochawesome-uirecorder", "${file}"] + } + ] +} \ No newline at end of file diff --git a/selenium/README.md b/selenium/README.md new file mode 100644 index 00000000..62ab2ba6 --- /dev/null +++ b/selenium/README.md @@ -0,0 +1,28 @@ +UI Recorder test sample project +================ + +It's a UI Recorder test sample project. + +Save your test code here. + +Get more info: [http://uirecorder.com/](http://uirecorder.com/) + +How to run test case? +================ + +1. npm install +2. source run.sh ( Linux|Mac ) or run.bat ( Windows ) + +How to dock jenkins? +================ + +1. Add commands + + source ./install.sh + source ./run.sh + +2. Add reports + + > JUnit: reports/index.xml + + > HTML: reports/ diff --git a/selenium/commons/commons.md b/selenium/commons/commons.md new file mode 100644 index 00000000..730f82ee --- /dev/null +++ b/selenium/commons/commons.md @@ -0,0 +1 @@ +Please save common test case here. \ No newline at end of file diff --git a/selenium/config.json b/selenium/config.json new file mode 100644 index 00000000..f382f84d --- /dev/null +++ b/selenium/config.json @@ -0,0 +1,23 @@ +{ + "webdriver": { + "host": "127.0.0.1", + "port": "4444", + "browsers": "chrome" + }, + "vars": { + "LoginUsername": "admin", + "LoginPassword": "admin", + "ShortTextTitle": "SeleniumShortText", + "Profile_NewFirstName": "SeleniumUser_FirstName", + "Profile_NewLastName": "SeleniumUser_LastName", + "Profile_OldFirstName": "Admin", + "Profile_OldLastName": "Account", + "Profile_NewInvalidEmail": "SeleniumInvalidEmail" + }, + "recorder": { + "pathAttrs": "data-id,data-name,type,data-type,role,data-role,data-value", + "attrValueBlack": "", + "classValueBlack": "", + "hideBeforeExpect": "" + } +} \ No newline at end of file diff --git a/selenium/hosts b/selenium/hosts new file mode 100644 index 00000000..e69de29b diff --git a/selenium/install.sh b/selenium/install.sh new file mode 100644 index 00000000..67759d80 --- /dev/null +++ b/selenium/install.sh @@ -0,0 +1,4 @@ +ls ~/nvm || git clone https://github.com/creationix/nvm.git ~/nvm +source ~/nvm/nvm.sh +nvm install v7.10.0 +npm install diff --git a/selenium/package.json b/selenium/package.json new file mode 100644 index 00000000..03727c90 --- /dev/null +++ b/selenium/package.json @@ -0,0 +1,25 @@ +{ + "name": "uirecorderTest", + "version": "1.0.0", + "description": "", + "main": "", + "dependencies": { + "chai": "3.5.0", + "jwebdriver": "2.2.4", + "mocha": "3.1.2", + "mocha-parallel-tests": "1.2.4", + "mochawesome-uirecorder": "1.5.22", + "resemblejs-node": "1.0.0", + "selenium-standalone": "6.x.x" + }, + "devDependencies": { + }, + "scripts": { + "installdriver": "./node_modules/.bin/selenium-standalone install --drivers.firefox.baseURL=http://npm.taobao.org/mirrors/geckodriver --baseURL=http://npm.taobao.org/mirrors/selenium --drivers.chrome.baseURL=http://npm.taobao.org/mirrors/chromedriver --drivers.ie.baseURL=http://npm.taobao.org/mirrors/selenium", + "server": "./node_modules/.bin/selenium-standalone start", + "test": "./node_modules/.bin/mocha \"*/**/*.spec.js\" --reporter mochawesome-uirecorder --bail", + "singletest": "./node_modules/.bin/mocha --reporter mochawesome-uirecorder --bail", + "paralleltest": "./node_modules/.bin/mocha-parallel-tests \"*/**/*.spec.js\" --reporter mochawesome-uirecorder --max-parallel 5 --bail" + }, + "author": "" +} diff --git a/selenium/run.bat b/selenium/run.bat new file mode 100644 index 00000000..da1ec242 --- /dev/null +++ b/selenium/run.bat @@ -0,0 +1,7 @@ +@echo off + +if "%1" neq "" ( + npm run singletest %1 %2 +) else ( + npm run paralleltest +) diff --git a/selenium/run.sh b/selenium/run.sh new file mode 100755 index 00000000..6f479e13 --- /dev/null +++ b/selenium/run.sh @@ -0,0 +1,5 @@ +if [ "$1" = "" ]; then + npm run paralleltest +else + npm run singletest $1 $2 +fi diff --git a/selenium/test/loginAndChangeProfile.js b/selenium/test/loginAndChangeProfile.js new file mode 100644 index 00000000..83e3fda8 --- /dev/null +++ b/selenium/test/loginAndChangeProfile.js @@ -0,0 +1,411 @@ +const fs = require('fs'); +const path = require('path'); +const chai = require("chai"); +const should = chai.should(); +const JWebDriver = require('jwebdriver'); +chai.use(JWebDriver.chaiSupportChainPromise); +const resemble = require('resemblejs-node'); +resemble.outputSettings({ + errorType: 'flatDifferenceIntensity' +}); + +const rootPath = getRootPath(); + +module.exports = function(){ + + let driver, testVars; + + before(function(){ + let self = this; + driver = self.driver; + testVars = self.testVars; + }); + + it('url: http://localhost:5000', async function(){ + await driver.url(_(`http://localhost:5000`)); + }); + + it('waitBody: ', async function(){ + await driver.sleep(500).wait('body', 30000).html().then(function(code){ + isPageError(code).should.be.false; + }); + }); + + it('insertVar: username ( #username, {{LoginUsername}} )', async function(){ + await driver.sleep(300).wait('#username', 30000) + .val(_(`{{LoginUsername}}`)); + }); + + it('insertVar: password ( #password, {{LoginUsername}} )', async function(){ + await driver.sleep(300).wait('#password', 30000) + .val(_(`{{LoginUsername}}`)); + }); + + it('expect: displayed, .btn-signup, equal, true', async function(){ + await driver.sleep(300).wait('.btn-signup', 30000) + .displayed() + .should.not.be.a('error') + .should.equal(_(true)); + }); + + it('click: Sign in ( button, 174, 18, 0 )', async function(){ + await driver.sleep(300).wait('button', 30000) + .sleep(300).mouseMove(174, 18).click(0); + }); + + it('mouseDown: My Settings ( a > span.ng-binding, 41.375, 12, 0 )', async function(){ + await driver.sleep(300).wait('a > span.ng-binding', 30000) + .sleep(300).mouseMove(41.375, 12).mouseDown(0); + }); + + it('expect: displayed, div.new-button, equal, true', async function(){ + await driver.sleep(300).wait('div.new-button', 30000) + .displayed() + .should.not.be.a('error') + .should.equal(_(true)); + }); + + it('expect: displayed, a.dropdown-toggle, equal, true', async function(){ + await driver.sleep(300).wait('a.dropdown-toggle', 30000) + .displayed() + .should.not.be.a('error') + .should.equal(_(true)); + }); + + it('click: My Settings ( a.dropdown-toggle, 95, 29, 0 )', async function(){ + await driver.sleep(300).wait('a.dropdown-toggle', 30000) + .sleep(300).mouseMove(95, 29).click(0); + }); + + it('× expect: display, li:nth-child(1) > a.ng-binding, equal, true', async function(){ + await driver.sleep(300).wait('li:nth-child(1) > a.ng-binding', 30000) + .displayed() + .should.not.be.a('error') + .should.equal(_(true)); + }); + + it('× expect: display, li:nth-child(3) > a.ng-binding, equal, true', async function(){ + await driver.sleep(300).wait('li:nth-child(3) > a.ng-binding', 30000) + .displayed() + .should.not.be.a('error') + .should.equal(_(true)); + }); + + it('mouseDown: Edit Profile ( li:nth-child(1) > a.ng-binding, 54.59375, 14, 0 )', async function(){ + await driver.sleep(300).wait('li:nth-child(1) > a.ng-binding', 30000) + .sleep(300).mouseMove(54.59375, 14).mouseDown(0); + }); + + it('click: Edit Profile ( li:nth-child(1) > a.ng-binding, 52, 13, 0 )', async function(){ + await driver.sleep(300).wait('li:nth-child(1) > a.ng-binding', 30000) + .sleep(300).mouseMove(52, 13).click(0); + }); + + it('waitBody: ', async function(){ + await driver.sleep(500).wait('body', 30000).html().then(function(code){ + isPageError(code).should.be.false; + }); + }); + + it('× insertVar: firstName ( #firstName, {{Profile_NewFirstName}} )', async function(){ + await driver.sleep(300).wait('#firstName', 30000) + .val(_(`{{Profile_NewFirstName}}`)); + }); + + it('× insertVar: lastName ( #lastName, {{Profile_NewLastName}} )', async function(){ + await driver.sleep(300).wait('#lastName', 30000) + .val(_(`{{Profile_NewLastName}}`)); + }); + + it('× click: Save Changes ( button.btn-signup, 95, 10, 0 )', async function(){ + await driver.sleep(300).wait('button.btn-signup', 30000) + .sleep(300).mouseMove(95, 10).click(0); + }); + + it('× mouseUp: Profile saved succes... ( section.row, 333, 78, 0 )', async function(){ + await driver.sleep(300).wait('section.row', 30000) + .sleep(300).mouseMove(333, 78).mouseMove(333, 78).mouseUp(0); + }); + + it('× expect: displayed, div.text-success, equal, true', async function(){ + await driver.sleep(300).wait('div.text-success', 30000) + .displayed() + .should.not.be.a('error') + .should.equal(_(true)); + }); + + it('× expect: displayed, .text-danger, notEqual, true', async function(){ + await driver.sleep(300).wait('.text-danger', 300) + .displayed() + .should.not.be.a('error') + .should.not.equal(_(true)); + }); + + /* + ** Revert back to expected names + */ + + it('× insertVar: firstName ( #firstName, {{Profile_OldFirstName}} )', async function(){ + await driver.sleep(300).wait('#firstName', 30000) + .val(_(`{{Profile_OldFirstName}}`)); + }); + + it('× insertVar: lastName ( #lastName, {{Profile_OldLastName}} )', async function(){ + await driver.sleep(300).wait('#lastName', 30000) + .val(_(`{{Profile_OldLastName}}`)); + }); + + it('× click: Save Changes ( button.btn-signup, 95, 10, 0 )', async function(){ + await driver.sleep(300).wait('button.btn-signup', 30000) + .sleep(300).mouseMove(95, 10).click(0); + }); + + it('× expect: displayed, .text-danger, notEqual, true', async function(){ + await driver.sleep(300).wait('.text-danger', 300) + .displayed() + .should.not.be.a('error') + .should.not.equal(_(true)); + }); + + + //Check that we can't save an invalid email + it('× insertVar: email ( #email, {{Profile_NewInvalidEmail}} )', async function(){ + await driver.sleep(300).wait('#email', 30000) + .val(_(`{{Profile_NewInvalidEmail}}`)); + }); + + it('× click: Save Changes ( button.btn-signup, 90, 16, 0 )', async function(){ + await driver.sleep(300).wait('button.btn-signup', 30000) + .sleep(300).mouseMove(90, 16).click(0); + }); + + it('url: http://localhost:5000/#!/settings/profile', async function(){ + await driver.url(_(`http://localhost:5000/#!/settings/profile`)); + }); + + it('url: http://localhost:5000/#!/settings/profile', async function(){ + await driver.url(_(`http://localhost:5000/#!/settings/profile`)); + }); + + it('waitBody: ', async function(){ + await driver.sleep(500).wait('body', 30000).html().then(function(code){ + isPageError(code).should.be.false; + }); + }); + + it('scrollTo: 0, 114', async function(){ + await driver.scrollTo(0, 114); + }); + + it('click: Email ( div:nth-child(8) > div.field-title, 352, 0, 0 )', async function(){ + await driver.sleep(300).wait('div:nth-child(8) > div.field-title', 30000) + .sleep(300).mouseMove(352, 0).click(0); + }); + + it('click: Email ( //h4[text()="Email"], 323, 5, 0 )', async function(){ + await driver.sleep(300).wait('//h4[text()="Email"]', 30000) + .sleep(300).mouseMove(323, 5).click(0); + }); + + it('expect: text, #email, notEqual, {{Profile_NewInvalidEmail}}', async function(){ + await driver.sleep(300).wait('#email', 300) + .text() + .should.not.be.a('error') + .should.not.equal(_(`{{Profile_NewInvalidEmail}}`)); + }); + + + /* + ** Logout + */ + it('click: Signout ( //a[text()="Signout"], 31, 31, 0 )', async function(){ + await driver.sleep(300).wait('//a[text()="Signout"]', 30000) + .sleep(300).mouseMove(31, 31).click(0); + }); + + it('expect: displayed, button.btn-signup, equal, true', async function(){ + await driver.sleep(300).wait('button.btn-signup', 30000) + .displayed() + .should.not.be.a('error') + .should.equal(_(true)); + }); + + function _(str){ + if(typeof str === 'string'){ + return str.replace(/\{\{(.+?)\}\}/g, function(all, key){ + return testVars[key] || ''; + }); + } + else{ + return str; + } + } + +}; + +if(module.parent && /mocha\.js/.test(module.parent.id)){ + runThisSpec(); +} + +function runThisSpec(){ + // read config + let webdriver = process.env['webdriver'] || ''; + let proxy = process.env['wdproxy'] || ''; + let config = require(rootPath + '/config.json'); + let webdriverConfig = Object.assign({},config.webdriver); + let host = webdriverConfig.host; + let port = webdriverConfig.port || 4444; + let match = webdriver.match(/([^\:]+)(?:\:(\d+))?/); + if(match){ + host = match[1] || host; + port = match[2] || port; + } + let testVars = config.vars; + let browsers = webdriverConfig.browsers; + browsers = browsers.replace(/^\s+|\s+$/g, ''); + delete webdriverConfig.host; + delete webdriverConfig.port; + delete webdriverConfig.browsers; + + // read hosts + let hostsPath = rootPath + '/hosts'; + let hosts = ''; + if(fs.existsSync(hostsPath)){ + hosts = fs.readFileSync(hostsPath).toString(); + } + let specName = path.relative(rootPath, __filename).replace(/\\/g,'/').replace(/\.js$/,''); + + browsers.split(/\s*,\s*/).forEach(function(browserName){ + let caseName = specName + ' : ' + browserName; + + let browserInfo = browserName.split(' '); + browserName = browserInfo[0]; + let browserVersion = browserInfo[1]; + + describe(caseName, function(){ + + this.timeout(600000); + this.slow(1000); + + let driver; + before(function(){ + let self = this; + let driver = new JWebDriver({ + 'host': host, + 'port': port + }); + let sessionConfig = Object.assign({}, webdriverConfig, { + 'browserName': browserName, + 'version': browserVersion, + 'ie.ensureCleanSession': true, + 'chromeOptions': { + 'args': ['--enable-automation'] + } + }); + if(proxy){ + sessionConfig.proxy = { + 'proxyType': 'manual', + 'httpProxy': proxy, + 'sslProxy': proxy + } + } + else if(hosts){ + sessionConfig.hosts = hosts; + } + self.driver = driver.session(sessionConfig).maximize().config({ + pageloadTimeout: 30000, // page onload timeout + scriptTimeout: 5000, // sync script timeout + asyncScriptTimeout: 10000 // async script timeout + }); + self.testVars = testVars; + let casePath = path.dirname(caseName); + self.screenshotPath = rootPath + '/screenshots/' + casePath; + self.diffbasePath = rootPath + '/diffbase/' + casePath; + self.caseName = caseName.replace(/.*\//g, '').replace(/\s*[:\.\:\-\s]\s*/g, '_'); + mkdirs(self.screenshotPath); + mkdirs(self.diffbasePath); + self.stepId = 0; + return self.driver; + }); + + module.exports(); + + beforeEach(function(){ + let self = this; + self.stepId ++; + if(self.skipAll){ + self.skip(); + } + }); + + afterEach(async function(){ + let self = this; + let currentTest = self.currentTest; + let title = currentTest.title; + if(currentTest.state === 'failed' && /^(url|waitBody|switchWindow|switchFrame):/.test(title)){ + self.skipAll = true; + } + if(!/^(closeWindow):/.test(title)){ + let filepath = self.screenshotPath + '/' + self.caseName + '_' + self.stepId; + let driver = self.driver; + try{ + // catch error when get alert msg + await driver.getScreenshot(filepath + '.png'); + let url = await driver.url(); + let html = await driver.source(); + html = '\n' + html; + fs.writeFileSync(filepath + '.html', html); + let cookies = await driver.cookies(); + fs.writeFileSync(filepath + '.cookie', JSON.stringify(cookies)); + } + catch(e){} + } + }); + + after(function(){ + return this.driver.close(); + }); + + }); + }); +} + +function getRootPath(){ + let rootPath = path.resolve(__dirname); + while(rootPath){ + if(fs.existsSync(rootPath + '/config.json')){ + break; + } + rootPath = rootPath.substring(0, rootPath.lastIndexOf(path.sep)); + } + return rootPath; +} + +function mkdirs(dirname){ + if(fs.existsSync(dirname)){ + return true; + }else{ + if(mkdirs(path.dirname(dirname))){ + fs.mkdirSync(dirname); + return true; + } + } +} + +function callSpec(name){ + try{ + require(rootPath + '/' + name)(); + } + catch(e){ + console.log(e) + process.exit(1); + } +} + +function isPageError(code){ + return code == '' || / jscontent="errorCode" jstcache="\d+"|diagnoseConnectionAndRefresh|dnserror_unavailable_header|id="reportCertificateErrorRetry"|400 Bad Request|403 Forbidden|404 Not Found|500 Internal Server Error|502 Bad Gateway|503 Service Temporarily Unavailable|504 Gateway Time-out/i.test(code); +} + +function catchError(error){ + +} diff --git a/selenium/test/loginAndCreateForm.spec.js b/selenium/test/loginAndCreateForm.spec.js new file mode 100644 index 00000000..db307227 --- /dev/null +++ b/selenium/test/loginAndCreateForm.spec.js @@ -0,0 +1,363 @@ +const fs = require('fs'); +const path = require('path'); +const chai = require("chai"); +const should = chai.should(); +const JWebDriver = require('jwebdriver'); +chai.use(JWebDriver.chaiSupportChainPromise); +const resemble = require('resemblejs-node'); +resemble.outputSettings({ + errorType: 'flatDifferenceIntensity' +}); + +const rootPath = getRootPath(); + +module.exports = function(){ + + let driver, testVars; + + before(function(){ + let self = this; + driver = self.driver; + testVars = self.testVars; + }); + + it('url: http://localhost:5000', async function(){ + await driver.url(_(`http://localhost:5000`)); + }); + + it('waitBody: ', async function(){ + await driver.sleep(500).wait('body', 30000).html().then(function(code){ + isPageError(code).should.be.false; + }); + }); + + it('insertVar: username ( #username, {{LoginUsername}} )', async function(){ + await driver.sleep(300).wait('#username', 30000) + .val(_(`{{LoginUsername}}`)); + }); + + it('insertVar: password ( #password, {{LoginPassword}} )', async function(){ + await driver.sleep(300).wait('#password', 30000) + .val(_(`{{LoginPassword}}`)); + }); + + it('mouseUp: Sign in ( button, 375, 45, 0 )', async function(){ + await driver.sleep(300).wait('button', 30000) + .sleep(300).mouseMove(375, 45).mouseMove(375, 45).mouseUp(0); + }); + + it('expect: displayed, button.btn.btn-signup, equal, true', async function(){ + await driver.sleep(300).wait('button.btn.btn-signup', 30000) + .displayed() + .should.not.be.a('error') + .should.equal(_(true)); + }); + + it('click: Sign in ( button, 217, 21, 0 )', async function(){ + await driver.sleep(300).wait('button', 30000) + .sleep(300).mouseMove(217, 21).click(0); + }); + + it('expect: displayed, div.new-button, equal, true', async function(){ + await driver.sleep(500).wait('div.new-button', 30000) + .displayed() + .should.not.be.a('error') + .should.equal(_(true)); + }); + + it('expect: displayed, section.navbar.navbar-inverse, equal, true', async function(){ + await driver.sleep(300).wait('section.navbar.navbar-inverse', 30000) + .displayed() + .should.not.be.a('error') + .should.equal(_(true)); + }); + + it('click: h4.fa, 76, 96, 0', async function(){ + await driver.sleep(300).wait('h4.fa', 30000) + .sleep(300).mouseMove(76, 96).click(0); + }); + + it('sleep: 100', async function(){ + await driver.sleep(100); + }); + + it('expect: displayed, form[name="forms.createForm"], equal, true', async function(){ + await driver.sleep(300).wait('form[name="forms.createForm"]', 30000) + .displayed() + .should.not.be.a('error') + .should.equal(_(true)); + }); + + it('× click: title ( input[name="title"], 115, 12, 0 )', async function(){ + await driver.sleep(300).wait('input[name="title"]', 30000) + .sleep(300).mouseMove(115, 12).click(0); + }); + + it('× expect: attr, .form-item.create-new.new-form button.btn,disabled, equal, true', async function(){ + await driver.sleep(300).wait('.form-item.create-new.new-form button.btn', 30000) + .attr('disabled') + .should.not.be.a('error') + .should.equal(_(`true`)); + }); + + it('sendKeys: aeoaoe', async function(){ + await driver.sendKeys('aeoaoe'); + }); + + it('× expect: attr, .form-item.create-new.new-form button.btn,disabled, equal, undefined', async function(){ + await driver.sleep(300).wait('.form-item.create-new.new-form button.btn', 30000) + .attr('disabled') + .should.not.be.a('error') + .should.equal(_(null)); + }); + + it('× click: Create this TellForm ( button, 57, 16, 0 )', async function(){ + await driver.sleep(300).wait('button', 30000) + .sleep(300).mouseMove(57, 16).click(0); + }); + + it('× expect: displayed, i.status-light.status-light-on, equal, true', async function(){ + await driver.sleep(300).wait('i.status-light.status-light-on', 30000) + .displayed() + .should.not.be.a('error') + .should.equal(_(true)); + }); + + it('× expect: displayed, .btn.btn-danger > .fa.fa-trash-o, equal, true', async function(){ + await driver.sleep(300).wait('.btn.btn-danger > .fa.fa-trash-o', 30000) + .displayed() + .should.not.be.a('error') + .should.equal(_(true)); + }); + + it('× click: Create ( //a[text()="Create"], 163, 25, 2 )', async function(){ + await driver.sleep(300).wait('//a[text()="Create"]', 30000) + .sleep(300).mouseMove(163, 25).click(2); + }); + + it('× mouseUp: aeoaoe ( section.admin-form, 467, 53, 0 )', async function(){ + await driver.sleep(300).wait('section.admin-form', 30000) + .sleep(300).mouseMove(467, 53).mouseMove(467, 53).mouseUp(0); + }); + + it('× click: Short Text ( div:nth-child(1) > div.panel-default > div.panel-heading > span.ng-binding, 39, 4, 0 )', async function(){ + await driver.sleep(300).wait('div:nth-child(1) > div.panel-default > div.panel-heading > span.ng-binding', 30000) + .sleep(300).mouseMove(39, 4).click(0); + }); + + it('× insertVar: title ( input[name="title"], {{ShortTextTitle}} )', async function(){ + await driver.sleep(300).wait('input[name="title"]', 30000) + .val(_(`{{ShortTextTitle}}`)); + }); + + it('mouseUp: 1 SeleniumShortText... ( body, 740, 97, 0 )', async function(){ + await driver.sleep(300).wait('body', 30000) + .sleep(300).mouseMove(740, 97).mouseMove(740, 97).mouseUp(0); + }); + + it('× expect: text, field-directive .field-title h3, equal, 1 {{ShortTextTitle}}', async function(){ + await driver.sleep(300).wait('field-directive .field-title h3', 30000) + .text() + .should.not.be.a('error') + .should.equal(_(`1 {{ShortTextTitle}}`)); + }); + + it('× click: Save ( button.btn-signup, 33, 23, 0 )', async function(){ + await driver.sleep(300).wait('button.btn-signup', 30000) + .sleep(300).mouseMove(33, 23).click(0); + }); + + it('× click: Delete Form ( span.hidden-sm, 55, 8, 0 )', async function(){ + await driver.sleep(300).wait('span.hidden-sm', 30000) + .sleep(300).mouseMove(55, 8).click(0); + }); + + it('× dblClick: aeoaoe ( //strong[text()="aeoaoe"], 26, 4, 0 )', async function(){ + await driver.sleep(300).wait('//strong[text()="aeoaoe"]', 30000) + .sleep(300).mouseMove(26, 4).click(0).click(0); + }); + + it('× click: input.input-block, 399, 3, 0', async function(){ + await driver.sleep(300).wait('input.input-block', 30000) + .sleep(300).mouseMove(399, 3).click(0); + }); + + function _(str){ + if(typeof str === 'string'){ + return str.replace(/\{\{(.+?)\}\}/g, function(all, key){ + return testVars[key] || ''; + }); + } + else{ + return str; + } + } + +}; + +if(module.parent && /mocha\.js/.test(module.parent.id)){ + runThisSpec(); +} + +function runThisSpec(){ + // read config + let webdriver = process.env['webdriver'] || ''; + let proxy = process.env['wdproxy'] || ''; + let config = require(rootPath + '/config.json'); + let webdriverConfig = Object.assign({},config.webdriver); + let host = webdriverConfig.host; + let port = webdriverConfig.port || 4444; + let match = webdriver.match(/([^\:]+)(?:\:(\d+))?/); + if(match){ + host = match[1] || host; + port = match[2] || port; + } + let testVars = config.vars; + let browsers = webdriverConfig.browsers; + browsers = browsers.replace(/^\s+|\s+$/g, ''); + delete webdriverConfig.host; + delete webdriverConfig.port; + delete webdriverConfig.browsers; + + // read hosts + let hostsPath = rootPath + '/hosts'; + let hosts = ''; + if(fs.existsSync(hostsPath)){ + hosts = fs.readFileSync(hostsPath).toString(); + } + let specName = path.relative(rootPath, __filename).replace(/\\/g,'/').replace(/\.js$/,''); + + browsers.split(/\s*,\s*/).forEach(function(browserName){ + let caseName = specName + ' : ' + browserName; + + let browserInfo = browserName.split(' '); + browserName = browserInfo[0]; + let browserVersion = browserInfo[1]; + + describe(caseName, function(){ + + this.timeout(600000); + this.slow(1000); + + let driver; + before(function(){ + let self = this; + let driver = new JWebDriver({ + 'host': host, + 'port': port + }); + let sessionConfig = Object.assign({}, webdriverConfig, { + 'browserName': browserName, + 'version': browserVersion, + 'ie.ensureCleanSession': true, + 'chromeOptions': { + 'args': ['--enable-automation'] + } + }); + if(proxy){ + sessionConfig.proxy = { + 'proxyType': 'manual', + 'httpProxy': proxy, + 'sslProxy': proxy + } + } + else if(hosts){ + sessionConfig.hosts = hosts; + } + self.driver = driver.session(sessionConfig).maximize().config({ + pageloadTimeout: 30000, // page onload timeout + scriptTimeout: 5000, // sync script timeout + asyncScriptTimeout: 10000 // async script timeout + }); + self.testVars = testVars; + let casePath = path.dirname(caseName); + self.screenshotPath = rootPath + '/screenshots/' + casePath; + self.diffbasePath = rootPath + '/diffbase/' + casePath; + self.caseName = caseName.replace(/.*\//g, '').replace(/\s*[:\.\:\-\s]\s*/g, '_'); + mkdirs(self.screenshotPath); + mkdirs(self.diffbasePath); + self.stepId = 0; + return self.driver; + }); + + module.exports(); + + beforeEach(function(){ + let self = this; + self.stepId ++; + if(self.skipAll){ + self.skip(); + } + }); + + afterEach(async function(){ + let self = this; + let currentTest = self.currentTest; + let title = currentTest.title; + if(currentTest.state === 'failed' && /^(url|waitBody|switchWindow|switchFrame):/.test(title)){ + self.skipAll = true; + } + if(!/^(closeWindow):/.test(title)){ + let filepath = self.screenshotPath + '/' + self.caseName + '_' + self.stepId; + let driver = self.driver; + try{ + // catch error when get alert msg + await driver.getScreenshot(filepath + '.png'); + let url = await driver.url(); + let html = await driver.source(); + html = '\n' + html; + fs.writeFileSync(filepath + '.html', html); + let cookies = await driver.cookies(); + fs.writeFileSync(filepath + '.cookie', JSON.stringify(cookies)); + } + catch(e){} + } + }); + + after(function(){ + return this.driver.close(); + }); + + }); + }); +} + +function getRootPath(){ + let rootPath = path.resolve(__dirname); + while(rootPath){ + if(fs.existsSync(rootPath + '/config.json')){ + break; + } + rootPath = rootPath.substring(0, rootPath.lastIndexOf(path.sep)); + } + return rootPath; +} + +function mkdirs(dirname){ + if(fs.existsSync(dirname)){ + return true; + }else{ + if(mkdirs(path.dirname(dirname))){ + fs.mkdirSync(dirname); + return true; + } + } +} + +function callSpec(name){ + try{ + require(rootPath + '/' + name)(); + } + catch(e){ + console.log(e) + process.exit(1); + } +} + +function isPageError(code){ + return code == '' || / jscontent="errorCode" jstcache="\d+"|diagnoseConnectionAndRefresh|dnserror_unavailable_header|id="reportCertificateErrorRetry"|400 Bad Request|403 Forbidden|404 Not Found|500 Internal Server Error|502 Bad Gateway|503 Service Temporarily Unavailable|504 Gateway Time-out/i.test(code); +} + +function catchError(error){ + +}