diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..eb25b5f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +language: node_js +node_js: + - 5.2.0 +sudo: required +cache: + directories: + - node_modules +before_install: + - sudo apt-get update + - sudo apt-get install -y libappindicator1 fonts-liberation + - wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb + - sudo dpkg -i google-chrome*.deb + - wget http://chromedriver.storage.googleapis.com/2.21/chromedriver_linux64.zip + - unzip chromedriver_linux64 + - sudo mv chromedriver /usr/bin +before_script: + - export CHROME_BIN=/usr/bin/google-chrome + - "export DISPLAY=:99.0" + - "sh -e /etc/init.d/xvfb start" + - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16" + - sleep 5 # give xvfb some time to start + - gulp bundle + - node index.js & + - sleep 5 +script: node_modules/mocha/bin/mocha test/unit --compilers js:babel-core/register && node_modules/nightwatch/bin/nightwatch --test test/acceptance/index.js --config test/acceptance/nightwatch.json -e chrome diff --git a/gulpfile.babel.js b/gulpfile.babel.js index 19130e2..ef76abd 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -25,7 +25,7 @@ gulp.task('start', function() { nodemon({ script: 'index.js', ext: 'css js mustache', - ignore: ['src/public/main.js'], + ignore: ['src/public/main.js', 'test'], env: { 'NODE_ENV': 'development' }, @@ -34,9 +34,31 @@ gulp.task('start', function() { }); gulp.task('test', function() { - let test = spawn( - 'mocha', - ['test', '--compilers', 'js:babel-core/register'], + let unitTest = spawn( + 'node_modules/mocha/bin/mocha', + ['test/unit', '--compilers', 'js:babel-core/register'], {stdio: 'inherit'} ); + + unitTest.on('exit', function() { + + // Start app + let app = spawn('node', ['index.js']); + + app.stdout.on('data', function(data) { + console.log(String(data)); + }); + + let acceptanceTest = spawn( + 'node_modules/nightwatch/bin/nightwatch', + ['--test', 'test/acceptance/index.js', '--config', 'test/acceptance/nightwatch.json'], + {stdio: 'inherit'} + ); + + acceptanceTest.on('exit', function() { + // Kill app Node process when tests are done + app.kill(); + }); + }); + }); diff --git a/package.json b/package.json index f9b9512..452e0b0 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "browserify": "^13.0.0", "compression": "^1.6.0", "express": "^4.13.3", + "forever": "^0.15.1", "gulp": "^3.9.0", "gulp-uglify": "^1.5.1", "mustache-express": "^1.2.2", @@ -27,10 +28,17 @@ "gulp-nodemon": "^2.0.6", "jscs": "^2.10.1", "jshint": "^2.9.1", + "mocha": "^2.4.5", "mocha-jscs": "^4.2.0", "mocha-jshint": "^2.3.1", + "nightwatch": "^0.8.16", "vinyl-buffer": "^1.0.0", - "vinyl-source-stream": "^1.1.0" + "vinyl-source-stream": "^1.1.0", + "zombie": "^4.2.1" + }, + "scripts": { + "dev": "gulp start", + "test": "gulp test" }, "author": "Daniel Seripap", "license": "MIT" diff --git a/test/acceptance/app.js b/test/acceptance/app.js new file mode 100644 index 0000000..0e63b92 --- /dev/null +++ b/test/acceptance/app.js @@ -0,0 +1,90 @@ +describe('Darkwire', () => { + + describe('starting a room', () => { + + let browser; + + before((client, done) => { + browser = client + .url('http://localhost:3000/', () => { + done(); + }); + }); + + after((client, done) => { + browser.end(() => { + done(); + }); + }); + + afterEach((client, done) => { + done(); + }); + + beforeEach((client, done) => { + done(); + }); + + it('should show welcome modal', () => { + browser + .waitForElementVisible('#first-modal', 5000) + .assert.containsText('#first-modal .modal-title', 'Welcome to darkwire.io'); + }); + + it('should have correct header', () => { + browser.expect.element('#first-modal .modal-title').text.to.equal('Welcome to darkwire.io'); + }); + + describe('opening a second window', () => { + + before((client, done) => { + browser.url((result) => { + let urlSplit = result.value.split('/'); + let roomId = urlSplit[urlSplit.length - 1]; + let url = 'http://localhost:3000/' + roomId; + browser.execute(() => { + window.open('http://localhost:3000/', '_blank'); + }, [], () => { + browser.window_handles((result) => { + browser.switchWindow(result.value[1], () => { + browser.execute((id) => { + window.open('http://localhost:3000/' + id, '_self'); + }, [roomId], () => { + done(); + }); + }); + }); + }); + }); + }); + + it('should not show welcome modal', () => { + browser.assert.hidden('#first-modal'); + }); + + describe('sending messages', () => { + + before((client, done) => { + browser.waitForElementPresent('ul.users li:nth-child(2)', 5000, () => { + browser.setValue('input.inputMessage', ['Hello world', browser.Keys.RETURN], () => { + done(); + }); + }); + }); + + it('should work', () => { + browser.window_handles((result) => { + browser.switchWindow(result.value[0], () => { + browser.waitForElementPresent('span.messageBody', 5000, () => { + browser.assert.containsText('span.messageBody', 'Hello world'); + }); + }); + }); + }); + + }); + + }); + + }); +}); diff --git a/test/acceptance/bin/selenium-server-standalone-2.52.0.jar b/test/acceptance/bin/selenium-server-standalone-2.52.0.jar new file mode 100644 index 0000000..f62a462 Binary files /dev/null and b/test/acceptance/bin/selenium-server-standalone-2.52.0.jar differ diff --git a/test/acceptance/index.js b/test/acceptance/index.js new file mode 100644 index 0000000..36104ea --- /dev/null +++ b/test/acceptance/index.js @@ -0,0 +1,2 @@ +require('babel-register')(); +require('./app.js'); diff --git a/test/acceptance/nightwatch.json b/test/acceptance/nightwatch.json new file mode 100644 index 0000000..852be86 --- /dev/null +++ b/test/acceptance/nightwatch.json @@ -0,0 +1,49 @@ +{ + "src_folders" : ["test"], + "output_folder" : "reports", + "custom_commands_path" : "", + "custom_assertions_path" : "", + "page_objects_path" : "", + "globals_path" : "", + "test_runner" : "mocha", + "selenium" : { + "start_process" : true, + "server_path" : "test/acceptance/bin/selenium-server-standalone-2.52.0.jar", + "log_path" : false, + "host" : "127.0.0.1", + "port" : 4444, + "cli_args" : { + "webdriver.chrome.driver" : "/usr/bin/chromedriver", + "webdriver.ie.driver" : "" + } + }, + + "test_settings" : { + "default" : { + "launch_url" : "http://localhost", + "selenium_port" : 4444, + "selenium_host" : "localhost", + "silent": true, + "screenshots" : { + "enabled" : false, + "path" : "" + }, + "desiredCapabilities": { + "browserName": "firefox", + "javascriptEnabled": true, + "acceptSslCerts": true + } + }, + + "chrome" : { + "desiredCapabilities": { + "browserName": "chrome", + "javascriptEnabled": true, + "acceptSslCerts": true, + "chromeOptions" : { + "args" : ["-e", "--no-sandbox"] + } + } + } + } +} diff --git a/test/app.js b/test/app.js deleted file mode 100644 index 3da3280..0000000 --- a/test/app.js +++ /dev/null @@ -1,5 +0,0 @@ -import mochaJSCS from 'mocha-jscs'; -import mochaJSHint from 'mocha-jshint'; - -mochaJSCS(); -mochaJSHint(); diff --git a/test/helpers.js b/test/helpers.js new file mode 100644 index 0000000..e752691 --- /dev/null +++ b/test/helpers.js @@ -0,0 +1,50 @@ +var helpers = { + polyfillCrypto: () => { + window.crypto = { + subtle: { + generateKey: () => { + return new Promise((resolve, reject) => { + resolve({}); + }); + }, + exportKey: () => { + return new Promise((resolve, reject) => { + resolve([{}]); + }); + }, + importKey: () => { + return new Promise((resolve, reject) => { + resolve([{}]); + }); + }, + encrypt: () => { + return {}; + }, + decrypt: (opts, key, data) => { + if (opts.name === 'AES-CBC') { + // This means it's decrypted a message + return new Promise((resolve, reject) => { + // "Hello world" as an array buffer + resolve(new Uint8Array([72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100])); + }); + } else { + return new Promise((resolve, reject) => { + resolve({}); + }); + } + }, + sign: () => { + return {}; + }, + verify: () => { + return true; + } + }, + getRandomValues: () => { + return [1,2,3,4]; + } + }; + } +}; + +module.exports = helpers; diff --git a/test/unit/app.js b/test/unit/app.js new file mode 100644 index 0000000..afbd0cf --- /dev/null +++ b/test/unit/app.js @@ -0,0 +1,80 @@ +import helpers from './helpers'; +import app from '../index'; +import mochaJSCS from 'mocha-jscs'; +import mochaJSHint from 'mocha-jshint'; + +const Browser = require('zombie'); +Browser.localhost('localhost', 3000); + +mochaJSCS(); +mochaJSHint(); + +describe('Visiting /', () => { + + const browser = new Browser(); + + before((done) => { + browser.on('active', () => { + // browser.evaluate needs a string, so this regex just extracts the body of the function as a string + browser.evaluate(helpers.polyfillCrypto.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1]); + }); + + browser.visit('/', done); + }); + + it('should be successful', () => { + browser.assert.success(); + }); + + it('should show welcome modal', () => { + browser.assert.evaluate('$("#first-modal:visible").length', 1); + browser.assert.text('#first-modal h4.modal-title', 'Welcome to darkwire.io'); + }); + + describe('closing the initial modal', () => { + + before((done) => { + browser.pressButton('#first-modal .modal-footer button', done); + }); + + it('should close the modal and show the main chat page', () => { + browser.assert.evaluate('$("#first-modal:hidden").length', 1); + }); + + describe('opening another tab', () => { + + before((done) => { + let roomIdSplit = browser.url.split('/'); + let roomId = roomIdSplit[roomIdSplit.length - 1]; + browser.open(); + browser.tabs.current = 1; + browser.visit(`/${roomId}`, done); + }); + + it('should be successful', () => { + browser.assert.success(); + }); + + it('should not show welcome modal', () => { + browser.assert.evaluate('$("#first-modal.fade.in").length', 0); + }); + + describe('sending message', () => { + + before((done) => { + browser.fill('.inputMessage', 'Hello world'); + browser.click('span#send-message-btn', done); + }); + + it('should send message', () => { + browser.tabs.current = 0; + browser.assert.text('body', /Hello world/); + }); + + }); + + }); + + }); + +});