From 9980741406531b12aafad2aae8ec1b411fbf6a93 Mon Sep 17 00:00:00 2001 From: leedr Date: Wed, 2 Sep 2015 10:39:34 -0500 Subject: [PATCH 01/20] Intial functional test automation files --- test/functional/settingsDefaults.js | 90 +++++++++++++++++++++++++++ test/intern.js | 94 +++++++++++++++++++++++++++++ test/support/pages/SettingsPage.js | 49 +++++++++++++++ 3 files changed, 233 insertions(+) create mode 100644 test/functional/settingsDefaults.js create mode 100644 test/intern.js create mode 100644 test/support/pages/SettingsPage.js diff --git a/test/functional/settingsDefaults.js b/test/functional/settingsDefaults.js new file mode 100644 index 000000000000..3ea0e930f489 --- /dev/null +++ b/test/functional/settingsDefaults.js @@ -0,0 +1,90 @@ +// Kibana is loading. Give me a moment here. I'm loading a whole bunch of code. Don't worry, all this good stuff will be cached up for next time! +//http://localhost:5601/app/kibana#/settings/indices/?_g=%28refreshInterval:%28display:Off,pause:!f,value:0%29,time:%28from:now-15m,mode:quick,to:now%29%29 +//http://localhost:5601/app/kibana#/settings/indices/?_g=(refreshInterval:(display:Off,pause:!f,value:0),time:(from:now-15m,mode:quick,to:now)) +// long timeout if ElasticSearch isn't running... + + +define([ + 'intern!object', + 'intern/chai!assert', + 'intern/dojo/node!fs', + '../support/pages/SettingsPage' +], function (registerSuite, assert, fs, SettingsPage) { + + registerSuite(function () { + var settingsPage; + var url = 'http://localhost:5601'; + return { + // on setup, we create an settingsPage instance + // that we will use for all the tests + setup: function () { + // curl -XDELETE http://localhost:9200/.kibana + settingsPage = new SettingsPage(this.remote); + }, + + /* + ** Test the default state of checboxes and the 2 text input fields + */ + 'testSettingsInitialState': function () { + return this.remote + .get(url) + .then(function () { + return settingsPage + .getTimeBasedEventsCheckbox() + .isSelected() + .then(function (selected) { + assert.strictEqual(selected, true, 'Expected the Index contains time-based events to be checked'); + return settingsPage.getNameIsPatternCheckbox() + .isSelected() + .then(function (nameIsPatternSelected) { + assert.strictEqual(nameIsPatternSelected, false, 'Expected the Name Is Pattern checkbox to not be checked'); + return settingsPage.getIndexPatternField() + .getAttribute('value') + .then(function (pattern) { + // not getting the value + // assert.strictEqual(pattern, 'logstash-*', 'Expected + // pattern logstash-*'); + return settingsPage.getTimeFieldNameField() + .getVisibleText() + .then(function (timeField) { + assert.strictEqual(timeField, '@timestamp', 'Expected Time-field name @timestamp'); + }); + }); + }); + }); + }); + }, + + /* + ** Test that unchecking the Time-based Events checkbox hides the Name is pattern checkbox + */ + 'testSettingsCheckboxHide': function () { + return this.remote + .get('http://localhost:5601') + .then(function () { + return settingsPage + .getTimeBasedEventsCheckbox() + .then(function (selected) { + // uncheck the 'time-based events' checkbox + return selected.click(); + }) + .then(function () { + return settingsPage.getNameIsPatternCheckbox(); + }) + .then(function (selected1) { + assert.strictEqual( + true, false, 'Did not expect to find the Name is Pattern checkbox when the TimeBasedEvents checkbox is unchecked' + ); + }) + .catch(function (reason) { + // We expect to find an element not found exception. Check the reason string for the message. + assert.strictEqual( + reason.toString().indexOf('Unable to locate element') > 0, true, 'Expected to not find Name Is Pattern checkbox' + ); + return; + }); + }); + } + }; + }); +}); diff --git a/test/intern.js b/test/intern.js new file mode 100644 index 000000000000..ef75068060fa --- /dev/null +++ b/test/intern.js @@ -0,0 +1,94 @@ +// Learn more about configuring this file at . +// These default settings work OK for most people. The options that *must* be changed below are the +// packages, suites, excludeInstrumentation, and (if you want functional tests) functionalSuites +define({ + + // The port on which the instrumenting proxy will listen + proxyPort: 9000, + + // A fully qualified URL to the Intern proxy + proxyUrl: 'http://localhost:9000/', + + + // Default desired capabilities for all environments. Individual capabilities can be overridden by any of the + // specified browser environments in the `environments` array below as well. See + // for links to the different capabilities options for + // different services. + // + // Note that the `build` capability will be filled in with the current commit ID or build tag from the CI + // environment automatically + capabilities: { + 'selenium-version': '2.45.0', + 'idle-timeout': 30 + + //'browserstack.selenium_version': '2.45.0' + }, + + // Browsers to run integration testing against. Note that version numbers must be strings if used with Sauce + // OnDemand. Options that will be permutated are browserName, version, platform, and platformVersion; any other + // capabilities options specified for an environment will be copied as-is + environments: [ + /*{ browserName: 'internet explorer', version: '11', platform: 'WIN8' }, + { browserName: 'internet explorer', version: '10', platform: 'WIN8' }, + { browserName: 'internet explorer', version: '9', platform: 'WINDOWS' }, + { browserName: 'firefox', version: '37', platform: [ 'WINDOWS', 'MAC' ] }, + { browserName: 'chrome', version: '39', platform: [ 'WINDOWS', 'MAC' ] }, + { browserName: 'safari', version: '8', platform: 'MAC' } */ + // { + // browserName: 'chrome' + // }, + { + browserName: 'firefox' + } + ], + + // Maximum number of simultaneous integration tests that should be executed on the remote WebDriver service + maxConcurrency: 1, + + // Whether or not to start Sauce Connect before running tests + useSauceConnect: false, + + + // Name of the tunnel class to use for WebDriver tests. + // See for built-in options + //tunnel: 'BrowserStackTunnel', + + // Connection information for the remote WebDriver service. If using Sauce Labs, keep your username and password + // in the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables unless you are sure you will NEVER be + // publishing this configuration file somewhere + webdriver: { + host: 'localhost', + port: 4444 + }, + + + // Configuration options for the module loader; any AMD configuration options supported by the AMD loader in use + // can be used here. + // If you want to use a different loader than the default loader, see + // for instruction + loaderOptions: { + // Packages that should be registered with the loader in each testing environment + // packages: [ { name: 'myPackage', location: '.' } ] + + packages: [{ + name: 'intern-selftest', + location: '.' + }], + map: { + 'intern-selftest': { + dojo: 'intern-selftest/node_modules/dojo' + } + } + + }, + + // Non-functional test suite(s) to run in each browser + suites: [ /* 'tests/unit/hello' 'myPackage/tests/foo', 'myPackage/tests/bar' */ ], + + // Functional test suite(s) to execute against each browser once non-functional tests are completed + // functionalSuites: ['test/functional/settingsDefaults' /* 'myPackage/tests/functional' */ ], + functionalSuites: ['test/functional/testHeaderNav' /* 'myPackage/tests/functional' */ ], + + // A regular expression matching URLs to files that should not be included in code coverage analysis + excludeInstrumentation: /^(?:tests|node_modules)\// +}); diff --git a/test/support/pages/SettingsPage.js b/test/support/pages/SettingsPage.js new file mode 100644 index 000000000000..e59077235645 --- /dev/null +++ b/test/support/pages/SettingsPage.js @@ -0,0 +1,49 @@ +// in test/support/pages/SettingsPage.js +define(function (require) { + // the page object is created as a constructor + // so we can provide the remote Command object + // at runtime + function SettingsPage(remote) { + this.remote = remote; + } + + SettingsPage.prototype = { + constructor: SettingsPage, + + getTimeBasedEventsCheckbox: function () { + return this.remote + .setFindTimeout(5000) + .findByXpath('//input[@ng-model=\'index.isTimeBased\']'); + }, + + getNameIsPatternCheckbox: function () { + return this.remote + .setFindTimeout(2000) + .findByXpath('//input[@ng-model=\'index.nameIsPattern\']'); + }, + + getIndexPatternField: function () { + return this.remote + .setFindTimeout(5000) + //findElement(By.name("name")).getAttribute("value") (from Selenium IDE -> Java) + .findByName('name'); + }, + + getTimeFieldNameField: function () { + return this.remote + .findByXpath( + '//body[@id=\'kibana-body\']/div[2]/div/kbn-settings-app/div/div/kbn-settings-indices/div[2]/div/div[2]/form/div[3]/select' + ); + }, + + getCreateButton: function () { + return this.remote + .findByCssSelector('.btn'); + } + + + // …additional page interaction tasks… + }; + + return SettingsPage; +}); From 7b8374b7c8b91ca737e83d01fbd2cb58b06e876d Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Wed, 2 Sep 2015 14:07:20 -0500 Subject: [PATCH 02/20] Add intern task to grunt and associated npm script --- Gruntfile.js | 2 +- package.json | 2 ++ tasks/config/intern.js | 13 +++++++++++++ tasks/test.js | 4 ++++ test/intern.js | 2 +- 5 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tasks/config/intern.js diff --git a/Gruntfile.js b/Gruntfile.js index 128cac6e172c..e6a6ca0ef4f6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -63,7 +63,7 @@ module.exports = function (grunt) { init: true, config: config, loadGruntTasks: { - pattern: ['grunt-*', '@*/grunt-*', 'gruntify-*', '@*/gruntify-*'] + pattern: ['grunt-*', '@*/grunt-*', 'gruntify-*', '@*/gruntify-*', 'intern'] } }); diff --git a/package.json b/package.json index b2862197b95b..d58bc2aa6127 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "test:dev": "grunt test:dev", "test:quick": "grunt test:quick", "test:browser": "grunt test:browser", + "test:ui": "grunt test:ui", "test:server": "grunt test:server", "test:coverage": "grunt test:coverage", "build": "grunt build", @@ -144,6 +145,7 @@ "gruntify-eslint": "^1.0.0", "html-entities": "^1.1.1", "husky": "^0.8.1", + "intern": "^3.0.1", "istanbul-instrumenter-loader": "^0.1.3", "karma": "^0.13.3", "karma-chrome-launcher": "^0.2.0", diff --git a/tasks/config/intern.js b/tasks/config/intern.js new file mode 100644 index 000000000000..d1a698b6b959 --- /dev/null +++ b/tasks/config/intern.js @@ -0,0 +1,13 @@ +var path = require('path'); + + +module.exports = function (grunt) { + return { + options: { + runType: 'runner', + config: 'test/intern', + reporters: ['Console'] + }, + dev: {} + }; +}; diff --git a/tasks/test.js b/tasks/test.js index 9874dd97fc71..9b5fbbe84c3e 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -14,6 +14,10 @@ module.exports = function (grunt) { 'karma:dev' ]); + grunt.registerTask('test:ui', [ + 'intern:dev' + ]); + grunt.registerTask('test', function (subTask) { if (subTask) grunt.fail.fatal(`invalid task "test:${subTask}"`); diff --git a/test/intern.js b/test/intern.js index ef75068060fa..db34a70522c2 100644 --- a/test/intern.js +++ b/test/intern.js @@ -87,7 +87,7 @@ define({ // Functional test suite(s) to execute against each browser once non-functional tests are completed // functionalSuites: ['test/functional/settingsDefaults' /* 'myPackage/tests/functional' */ ], - functionalSuites: ['test/functional/testHeaderNav' /* 'myPackage/tests/functional' */ ], + functionalSuites: ['test/functional/settingsDefaults.js' /* 'myPackage/tests/functional' */ ], // A regular expression matching URLs to files that should not be included in code coverage analysis excludeInstrumentation: /^(?:tests|node_modules)\// From 0d53f5ee13a4ac2edffcad4457790070c7f11b93 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Wed, 2 Sep 2015 14:28:35 -0500 Subject: [PATCH 03/20] [functional testing] Start new instance of es --- tasks/config/esvm.js | 11 ++++++++++- tasks/test.js | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/tasks/config/esvm.js b/tasks/config/esvm.js index 60275aa9ebc5..410683f4ccd7 100644 --- a/tasks/config/esvm.js +++ b/tasks/config/esvm.js @@ -22,6 +22,15 @@ module.exports = function (grunt) { } } }, - dev: {} + dev: {}, + ui: { + options: { + config: { + http: { + port: 9209 + } + } + } + } }; }; diff --git a/tasks/test.js b/tasks/test.js index 9b5fbbe84c3e..12ca211f956c 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -15,6 +15,7 @@ module.exports = function (grunt) { ]); grunt.registerTask('test:ui', [ + 'esvm:ui', 'intern:dev' ]); From 76d60aec9e811e3afc11bd024a9d1b550f7eac45 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Wed, 2 Sep 2015 14:56:07 -0500 Subject: [PATCH 04/20] [functional testing] Start new instance of kibana --- tasks/config/esvm.js | 2 +- tasks/config/run.js | 15 +++++++++++++++ tasks/test.js | 1 + test/functional/settingsDefaults.js | 4 ++-- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/tasks/config/esvm.js b/tasks/config/esvm.js index 410683f4ccd7..817dd3743d6b 100644 --- a/tasks/config/esvm.js +++ b/tasks/config/esvm.js @@ -27,7 +27,7 @@ module.exports = function (grunt) { options: { config: { http: { - port: 9209 + port: 9220 } } } diff --git a/tasks/config/run.js b/tasks/config/run.js index 8ecdb130a248..7441c54e74fc 100644 --- a/tasks/config/run.js +++ b/tasks/config/run.js @@ -21,6 +21,21 @@ module.exports = function (grunt) { ] }, + testUIServer: { + options: { + wait: false, + ready: /Server running/, + quiet: false, + failOnError: false + }, + cmd: /^win/.test(platform) ? '.\\bin\\kibana.bat' : './bin/kibana', + args: [ + '--server.port=5620', + '--elasticsearch.url=http://localhost:9220', + '--logging.json=false' + ] + }, + testCoverageServer: { options: { wait: false, diff --git a/tasks/test.js b/tasks/test.js index 12ca211f956c..ba1270b7279a 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -16,6 +16,7 @@ module.exports = function (grunt) { grunt.registerTask('test:ui', [ 'esvm:ui', + 'run:testUIServer', 'intern:dev' ]); diff --git a/test/functional/settingsDefaults.js b/test/functional/settingsDefaults.js index 3ea0e930f489..767fe6a95d9e 100644 --- a/test/functional/settingsDefaults.js +++ b/test/functional/settingsDefaults.js @@ -13,7 +13,7 @@ define([ registerSuite(function () { var settingsPage; - var url = 'http://localhost:5601'; + var url = 'http://localhost:5620'; return { // on setup, we create an settingsPage instance // that we will use for all the tests @@ -60,7 +60,7 @@ define([ */ 'testSettingsCheckboxHide': function () { return this.remote - .get('http://localhost:5601') + .get(url) .then(function () { return settingsPage .getTimeBasedEventsCheckbox() From 83a582cdbe5016f9649fa241f4e3795edde9bf6c Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Thu, 3 Sep 2015 13:39:26 -0500 Subject: [PATCH 05/20] [functional testing] Load fixtures --- tasks/config/loadFixtures.js | 10 ++++++++++ tasks/loadFixtures.js | 27 +++++++++++++++++++++++++++ tasks/test.js | 1 + 3 files changed, 38 insertions(+) create mode 100644 tasks/config/loadFixtures.js create mode 100644 tasks/loadFixtures.js diff --git a/tasks/config/loadFixtures.js b/tasks/config/loadFixtures.js new file mode 100644 index 000000000000..2e786998bd58 --- /dev/null +++ b/tasks/config/loadFixtures.js @@ -0,0 +1,10 @@ +var path = require('path'); + + +module.exports = function (grunt) { + return { + options: { + server: 'http://localhost:9220', + } + }; +}; diff --git a/tasks/loadFixtures.js b/tasks/loadFixtures.js new file mode 100644 index 000000000000..a524f83881ee --- /dev/null +++ b/tasks/loadFixtures.js @@ -0,0 +1,27 @@ +var _ = require('lodash'); +var request = require('request'); +var fs = require('fs'); +var path = require('path'); +var colors = require('ansicolors'); + +module.exports = function (grunt) { + grunt.registerTask('loadFixtures', 'Loads fixtures into elasticsearch', function () { + const config = this.options(); + const done = this.async(); + const fixturesPath = path.join(grunt.config.get('root'), 'test/fixtures'); + + fs.readdir(fixturesPath, function (err, files) { + if (err) grunt.fail.warn(err); + + let doneProcessing = 0; + files.forEach(function (file) { + fs.createReadStream(path.join(fixturesPath, file)) + .pipe(request.post(`${config.server}/_bulk`, function (err, res, body) { + if (err || res.statusCode !== 200) grunt.fail.warn(err || body); + grunt.log.write(`[${colors.green('success')}] ${file}`); + if (++doneProcessing === files.length) done(); + })); + }); + }); + }); +}; diff --git a/tasks/test.js b/tasks/test.js index ba1270b7279a..e3a1746354df 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -16,6 +16,7 @@ module.exports = function (grunt) { grunt.registerTask('test:ui', [ 'esvm:ui', + 'loadFixtures', 'run:testUIServer', 'intern:dev' ]); From 71dd15ca2267a0a734e3839dd1e2bad893f312a7 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Fri, 4 Sep 2015 14:11:28 -0500 Subject: [PATCH 06/20] [functional testing] Load webdriver --- tasks/loadFixtures.js | 7 ++-- tasks/startSelenium.js | 84 ++++++++++++++++++++++++++++++++++++++++++ tasks/test.js | 1 + 3 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 tasks/startSelenium.js diff --git a/tasks/loadFixtures.js b/tasks/loadFixtures.js index a524f83881ee..bc64f49bf8b0 100644 --- a/tasks/loadFixtures.js +++ b/tasks/loadFixtures.js @@ -6,16 +6,17 @@ var colors = require('ansicolors'); module.exports = function (grunt) { grunt.registerTask('loadFixtures', 'Loads fixtures into elasticsearch', function () { + const FIXTURES_PATH = path.join(grunt.config.get('root'), 'test/fixtures'); + const config = this.options(); const done = this.async(); - const fixturesPath = path.join(grunt.config.get('root'), 'test/fixtures'); - fs.readdir(fixturesPath, function (err, files) { + fs.readdir(FIXTURES_PATH, function (err, files) { if (err) grunt.fail.warn(err); let doneProcessing = 0; files.forEach(function (file) { - fs.createReadStream(path.join(fixturesPath, file)) + fs.createReadStream(path.join(FIXTURES_PATH, file)) .pipe(request.post(`${config.server}/_bulk`, function (err, res, body) { if (err || res.statusCode !== 200) grunt.fail.warn(err || body); grunt.log.write(`[${colors.green('success')}] ${file}`); diff --git a/tasks/startSelenium.js b/tasks/startSelenium.js new file mode 100644 index 000000000000..b8a1ce640a9c --- /dev/null +++ b/tasks/startSelenium.js @@ -0,0 +1,84 @@ +var _ = require('lodash'); +var request = require('request'); +var fs = require('fs'); +var path = require('path'); +var colors = require('ansicolors'); +var crypto = require('crypto'); +var {spawn} = require('child_process'); + +module.exports = function (grunt) { + grunt.registerTask('startSelenium', 'Start an instance of selenium standalone', function () { + const done = this.async(); + const config = _.defaults(this.options(), { + seleniumStandalone: { + filename: 'selenium-server-standalone-2.47.1.jar', + server: 'https://selenium-release.storage.googleapis.com/2.47/', + md5: 'e6cb10b8f0f353c6ca4a8f62fb5cb472' + } + }); + + const SELENIUM_DIRECTORY = path.join(grunt.config.get('root'), 'selenium'); + const SELENIUM_FILE_PATH = path.join(SELENIUM_DIRECTORY, config.seleniumStandalone.filename); + const SELENIUM_DOWNLOAD_URL = config.seleniumStandalone.server + config.seleniumStandalone.filename; + const SELENIUM_HASH = config.seleniumStandalone.md5; + + function waitForReadiness(seleniumProcess, ready) { + seleniumProcess.stderr.on('data', function (log) { + if (~log.toString('utf8').indexOf('Selenium Server is up and running')) { + grunt.log.writeln('Selenium standalone started on port 4444'); + done(); + } + }); + + process.on('exit', function () { + seleniumProcess.kill(); + }); + } + + function validateDownload(path, expectedHash, success) { + grunt.log.write('Validating hash...'); + fs.readFile(path, function (err, data) { + if (err) grunt.fail.warn(err); + const calculatedHash = crypto.createHash('md5').update(data).digest('hex'); + if (calculatedHash !== expectedHash) return grunt.fail.warn('Selenium download has an invalid hash'); + grunt.log.writeln('done'); + success(); + }); + } + + function spawnSelenium() { + var seleniumProcess = spawn('java', ['-jar', SELENIUM_FILE_PATH]); + waitForReadiness(seleniumProcess); + } + + function downloadSelenium(success) { + grunt.log.write(`Downloading ${SELENIUM_DOWNLOAD_URL}...`); + request.get(SELENIUM_DOWNLOAD_URL) + .pipe(fs.createWriteStream(SELENIUM_FILE_PATH)) + .on('finish', function () { + grunt.log.writeln('done'); + validateDownload(SELENIUM_FILE_PATH, SELENIUM_HASH, success); + }) + .on('error', function (err) { + grunt.fail.warn(err); + }); + } + + + function start() { + fs.mkdir(SELENIUM_DIRECTORY, function (err) { + if (err && err.code !== 'EEXIST') grunt.fail.warn(err); + fs.exists(SELENIUM_FILE_PATH, function (exists) { + if (exists) { + spawnSelenium(); + } else { + downloadSelenium(spawnSelenium); + } + }); + }); + } + + start(); + + }); +}; diff --git a/tasks/test.js b/tasks/test.js index e3a1746354df..8046867865df 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -17,6 +17,7 @@ module.exports = function (grunt) { grunt.registerTask('test:ui', [ 'esvm:ui', 'loadFixtures', + 'startSelenium', 'run:testUIServer', 'intern:dev' ]); From d7db845b585013b88174b844d8be951316e672e5 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 8 Sep 2015 11:45:29 -0500 Subject: [PATCH 07/20] [functional testing] Add minimal smoke test, clean up config --- test/functional/apps.js | 20 ++++++++++ test/intern.js | 81 +---------------------------------------- 2 files changed, 22 insertions(+), 79 deletions(-) create mode 100644 test/functional/apps.js diff --git a/test/functional/apps.js b/test/functional/apps.js new file mode 100644 index 000000000000..c8015221a16b --- /dev/null +++ b/test/functional/apps.js @@ -0,0 +1,20 @@ +define([ + 'intern!object', + 'intern/dojo/node!expect.js' +], function (registerSuite, expect) { + registerSuite(function () { + var url = 'http://localhost:5620/apps'; + return { + 'apps': function () { + return this.remote + .get(url) + .setFindTimeout(60000) + .findByCssSelector('a[href="/app/kibana"]') + .getVisibleText() + .then(function (text) { + expect(text).to.eql('Kibana\nthe kibana you know and love'); + }); + } + }; + }); +}); diff --git a/test/intern.js b/test/intern.js index db34a70522c2..9bbae79a7578 100644 --- a/test/intern.js +++ b/test/intern.js @@ -1,94 +1,17 @@ -// Learn more about configuring this file at . -// These default settings work OK for most people. The options that *must* be changed below are the -// packages, suites, excludeInstrumentation, and (if you want functional tests) functionalSuites define({ - - // The port on which the instrumenting proxy will listen - proxyPort: 9000, - - // A fully qualified URL to the Intern proxy - proxyUrl: 'http://localhost:9000/', - - - // Default desired capabilities for all environments. Individual capabilities can be overridden by any of the - // specified browser environments in the `environments` array below as well. See - // for links to the different capabilities options for - // different services. - // - // Note that the `build` capability will be filled in with the current commit ID or build tag from the CI - // environment automatically capabilities: { - 'selenium-version': '2.45.0', + 'selenium-version': '2.47.1', 'idle-timeout': 30 - - //'browserstack.selenium_version': '2.45.0' }, - - // Browsers to run integration testing against. Note that version numbers must be strings if used with Sauce - // OnDemand. Options that will be permutated are browserName, version, platform, and platformVersion; any other - // capabilities options specified for an environment will be copied as-is environments: [ - /*{ browserName: 'internet explorer', version: '11', platform: 'WIN8' }, - { browserName: 'internet explorer', version: '10', platform: 'WIN8' }, - { browserName: 'internet explorer', version: '9', platform: 'WINDOWS' }, - { browserName: 'firefox', version: '37', platform: [ 'WINDOWS', 'MAC' ] }, - { browserName: 'chrome', version: '39', platform: [ 'WINDOWS', 'MAC' ] }, - { browserName: 'safari', version: '8', platform: 'MAC' } */ - // { - // browserName: 'chrome' - // }, { browserName: 'firefox' } ], - - // Maximum number of simultaneous integration tests that should be executed on the remote WebDriver service - maxConcurrency: 1, - - // Whether or not to start Sauce Connect before running tests - useSauceConnect: false, - - - // Name of the tunnel class to use for WebDriver tests. - // See for built-in options - //tunnel: 'BrowserStackTunnel', - - // Connection information for the remote WebDriver service. If using Sauce Labs, keep your username and password - // in the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables unless you are sure you will NEVER be - // publishing this configuration file somewhere webdriver: { host: 'localhost', port: 4444 }, - - - // Configuration options for the module loader; any AMD configuration options supported by the AMD loader in use - // can be used here. - // If you want to use a different loader than the default loader, see - // for instruction - loaderOptions: { - // Packages that should be registered with the loader in each testing environment - // packages: [ { name: 'myPackage', location: '.' } ] - - packages: [{ - name: 'intern-selftest', - location: '.' - }], - map: { - 'intern-selftest': { - dojo: 'intern-selftest/node_modules/dojo' - } - } - - }, - - // Non-functional test suite(s) to run in each browser - suites: [ /* 'tests/unit/hello' 'myPackage/tests/foo', 'myPackage/tests/bar' */ ], - - // Functional test suite(s) to execute against each browser once non-functional tests are completed - // functionalSuites: ['test/functional/settingsDefaults' /* 'myPackage/tests/functional' */ ], - functionalSuites: ['test/functional/settingsDefaults.js' /* 'myPackage/tests/functional' */ ], - - // A regular expression matching URLs to files that should not be included in code coverage analysis + functionalSuites: ['test/functional/apps.js'], excludeInstrumentation: /^(?:tests|node_modules)\// }); From 32d199efce1621dc1a6601a0ee7e99c5b20ec9d7 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 8 Sep 2015 13:24:16 -0500 Subject: [PATCH 08/20] [functional testing] Add fixture --- tasks/loadFixtures.js | 2 +- test/fixtures/test.json | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/test.json diff --git a/tasks/loadFixtures.js b/tasks/loadFixtures.js index bc64f49bf8b0..8698c498efd4 100644 --- a/tasks/loadFixtures.js +++ b/tasks/loadFixtures.js @@ -19,7 +19,7 @@ module.exports = function (grunt) { fs.createReadStream(path.join(FIXTURES_PATH, file)) .pipe(request.post(`${config.server}/_bulk`, function (err, res, body) { if (err || res.statusCode !== 200) grunt.fail.warn(err || body); - grunt.log.write(`[${colors.green('success')}] ${file}`); + grunt.log.writeln(`[${colors.green('success')}] ${file}`); if (++doneProcessing === files.length) done(); })); }); diff --git a/test/fixtures/test.json b/test/fixtures/test.json new file mode 100644 index 000000000000..134d798a687a --- /dev/null +++ b/test/fixtures/test.json @@ -0,0 +1,6 @@ +{ "index" : { "_index" : "test", "_type" : "type1", "_id" : "1" } } +{ "field1" : "value1" } +{ "index" : { "_index" : "test", "_type" : "type1", "_id" : "2" } } +{ "field1" : "value2" } +{ "index" : { "_index" : "test", "_type" : "type1", "_id" : "3" } } +{ "field1" : "value3" } From e67b9616583e15c51da1c99c8ef3783456160464 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 8 Sep 2015 13:27:50 -0500 Subject: [PATCH 09/20] [functional testing] Add selenium to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 78ce49b03b17..e7ab50b2a859 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ disabledPlugins webpackstats.json config/kibana.dev.yml coverage +selenium From d6c9019be5c40c0eb0c0657992e6daf65a10ca43 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Wed, 9 Sep 2015 10:12:45 -0500 Subject: [PATCH 10/20] [functional testing] Cleanup, run from npm test --- tasks/loadFixtures.js | 10 ++++++++-- tasks/startSelenium.js | 10 +++++----- tasks/test.js | 1 + 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/tasks/loadFixtures.js b/tasks/loadFixtures.js index 8698c498efd4..5659edc2ae4e 100644 --- a/tasks/loadFixtures.js +++ b/tasks/loadFixtures.js @@ -18,8 +18,14 @@ module.exports = function (grunt) { files.forEach(function (file) { fs.createReadStream(path.join(FIXTURES_PATH, file)) .pipe(request.post(`${config.server}/_bulk`, function (err, res, body) { - if (err || res.statusCode !== 200) grunt.fail.warn(err || body); - grunt.log.writeln(`[${colors.green('success')}] ${file}`); + var status; + if (err || res.statusCode !== 200) { + grunt.fail.warn(err || body); + status = colors.red('error'); + } else { + status = colors.green('success'); + } + grunt.log.writeln(`[${status}] ${file}`); if (++doneProcessing === files.length) done(); })); }); diff --git a/tasks/startSelenium.js b/tasks/startSelenium.js index b8a1ce640a9c..cdeedfea07d7 100644 --- a/tasks/startSelenium.js +++ b/tasks/startSelenium.js @@ -22,7 +22,7 @@ module.exports = function (grunt) { const SELENIUM_DOWNLOAD_URL = config.seleniumStandalone.server + config.seleniumStandalone.filename; const SELENIUM_HASH = config.seleniumStandalone.md5; - function waitForReadiness(seleniumProcess, ready) { + function waitForReadiness(seleniumProcess) { seleniumProcess.stderr.on('data', function (log) { if (~log.toString('utf8').indexOf('Selenium Server is up and running')) { grunt.log.writeln('Selenium standalone started on port 4444'); @@ -30,9 +30,6 @@ module.exports = function (grunt) { } }); - process.on('exit', function () { - seleniumProcess.kill(); - }); } function validateDownload(path, expectedHash, success) { @@ -48,6 +45,9 @@ module.exports = function (grunt) { function spawnSelenium() { var seleniumProcess = spawn('java', ['-jar', SELENIUM_FILE_PATH]); + process.on('exit', function () { + seleniumProcess.kill(); + }); waitForReadiness(seleniumProcess); } @@ -67,7 +67,7 @@ module.exports = function (grunt) { function start() { fs.mkdir(SELENIUM_DIRECTORY, function (err) { - if (err && err.code !== 'EEXIST') grunt.fail.warn(err); + if (err && err.code !== 'EEXIST') return grunt.fail.warn(err); fs.exists(SELENIUM_FILE_PATH, function (exists) { if (exists) { spawnSelenium(); diff --git a/tasks/test.js b/tasks/test.js index 8046867865df..fbb9bac784a5 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -6,6 +6,7 @@ module.exports = function (grunt) { grunt.registerTask('test:quick', [ 'test:server', + 'test:ui', 'test:browser' ]); From c064533ee88f9638f4a7337f1f99b275031045da Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Fri, 11 Sep 2015 09:54:42 -0500 Subject: [PATCH 11/20] [functional testing] Simplify, starting with one test --- test/functional/settingsDefaults.js | 90 ----------------------------- test/support/pages/SettingsPage.js | 49 ---------------- 2 files changed, 139 deletions(-) delete mode 100644 test/functional/settingsDefaults.js delete mode 100644 test/support/pages/SettingsPage.js diff --git a/test/functional/settingsDefaults.js b/test/functional/settingsDefaults.js deleted file mode 100644 index 767fe6a95d9e..000000000000 --- a/test/functional/settingsDefaults.js +++ /dev/null @@ -1,90 +0,0 @@ -// Kibana is loading. Give me a moment here. I'm loading a whole bunch of code. Don't worry, all this good stuff will be cached up for next time! -//http://localhost:5601/app/kibana#/settings/indices/?_g=%28refreshInterval:%28display:Off,pause:!f,value:0%29,time:%28from:now-15m,mode:quick,to:now%29%29 -//http://localhost:5601/app/kibana#/settings/indices/?_g=(refreshInterval:(display:Off,pause:!f,value:0),time:(from:now-15m,mode:quick,to:now)) -// long timeout if ElasticSearch isn't running... - - -define([ - 'intern!object', - 'intern/chai!assert', - 'intern/dojo/node!fs', - '../support/pages/SettingsPage' -], function (registerSuite, assert, fs, SettingsPage) { - - registerSuite(function () { - var settingsPage; - var url = 'http://localhost:5620'; - return { - // on setup, we create an settingsPage instance - // that we will use for all the tests - setup: function () { - // curl -XDELETE http://localhost:9200/.kibana - settingsPage = new SettingsPage(this.remote); - }, - - /* - ** Test the default state of checboxes and the 2 text input fields - */ - 'testSettingsInitialState': function () { - return this.remote - .get(url) - .then(function () { - return settingsPage - .getTimeBasedEventsCheckbox() - .isSelected() - .then(function (selected) { - assert.strictEqual(selected, true, 'Expected the Index contains time-based events to be checked'); - return settingsPage.getNameIsPatternCheckbox() - .isSelected() - .then(function (nameIsPatternSelected) { - assert.strictEqual(nameIsPatternSelected, false, 'Expected the Name Is Pattern checkbox to not be checked'); - return settingsPage.getIndexPatternField() - .getAttribute('value') - .then(function (pattern) { - // not getting the value - // assert.strictEqual(pattern, 'logstash-*', 'Expected - // pattern logstash-*'); - return settingsPage.getTimeFieldNameField() - .getVisibleText() - .then(function (timeField) { - assert.strictEqual(timeField, '@timestamp', 'Expected Time-field name @timestamp'); - }); - }); - }); - }); - }); - }, - - /* - ** Test that unchecking the Time-based Events checkbox hides the Name is pattern checkbox - */ - 'testSettingsCheckboxHide': function () { - return this.remote - .get(url) - .then(function () { - return settingsPage - .getTimeBasedEventsCheckbox() - .then(function (selected) { - // uncheck the 'time-based events' checkbox - return selected.click(); - }) - .then(function () { - return settingsPage.getNameIsPatternCheckbox(); - }) - .then(function (selected1) { - assert.strictEqual( - true, false, 'Did not expect to find the Name is Pattern checkbox when the TimeBasedEvents checkbox is unchecked' - ); - }) - .catch(function (reason) { - // We expect to find an element not found exception. Check the reason string for the message. - assert.strictEqual( - reason.toString().indexOf('Unable to locate element') > 0, true, 'Expected to not find Name Is Pattern checkbox' - ); - return; - }); - }); - } - }; - }); -}); diff --git a/test/support/pages/SettingsPage.js b/test/support/pages/SettingsPage.js deleted file mode 100644 index e59077235645..000000000000 --- a/test/support/pages/SettingsPage.js +++ /dev/null @@ -1,49 +0,0 @@ -// in test/support/pages/SettingsPage.js -define(function (require) { - // the page object is created as a constructor - // so we can provide the remote Command object - // at runtime - function SettingsPage(remote) { - this.remote = remote; - } - - SettingsPage.prototype = { - constructor: SettingsPage, - - getTimeBasedEventsCheckbox: function () { - return this.remote - .setFindTimeout(5000) - .findByXpath('//input[@ng-model=\'index.isTimeBased\']'); - }, - - getNameIsPatternCheckbox: function () { - return this.remote - .setFindTimeout(2000) - .findByXpath('//input[@ng-model=\'index.nameIsPattern\']'); - }, - - getIndexPatternField: function () { - return this.remote - .setFindTimeout(5000) - //findElement(By.name("name")).getAttribute("value") (from Selenium IDE -> Java) - .findByName('name'); - }, - - getTimeFieldNameField: function () { - return this.remote - .findByXpath( - '//body[@id=\'kibana-body\']/div[2]/div/kbn-settings-app/div/div/kbn-settings-indices/div[2]/div/div[2]/form/div[3]/select' - ); - }, - - getCreateButton: function () { - return this.remote - .findByCssSelector('.btn'); - } - - - // …additional page interaction tasks… - }; - - return SettingsPage; -}); From 49c856712c336981b81eda466c0e3ee079268fc6 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Fri, 11 Sep 2015 14:08:35 -0500 Subject: [PATCH 12/20] [functional testing] Add dev mode --- package.json | 2 ++ tasks/startSelenium.js | 4 ++-- tasks/test.js | 11 +++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d58bc2aa6127..8c712d75396b 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,8 @@ "test:quick": "grunt test:quick", "test:browser": "grunt test:browser", "test:ui": "grunt test:ui", + "test:ui:server": "grunt test:ui:server", + "test:ui:runner": "grunt test:ui:runner", "test:server": "grunt test:server", "test:coverage": "grunt test:coverage", "build": "grunt build", diff --git a/tasks/startSelenium.js b/tasks/startSelenium.js index cdeedfea07d7..27e8a6c71fda 100644 --- a/tasks/startSelenium.js +++ b/tasks/startSelenium.js @@ -7,7 +7,7 @@ var crypto = require('crypto'); var {spawn} = require('child_process'); module.exports = function (grunt) { - grunt.registerTask('startSelenium', 'Start an instance of selenium standalone', function () { + grunt.registerTask('startSelenium', 'Start an instance of selenium standalone', function (keepalive) { const done = this.async(); const config = _.defaults(this.options(), { seleniumStandalone: { @@ -26,7 +26,7 @@ module.exports = function (grunt) { seleniumProcess.stderr.on('data', function (log) { if (~log.toString('utf8').indexOf('Selenium Server is up and running')) { grunt.log.writeln('Selenium standalone started on port 4444'); - done(); + if (keepalive !== 'keepalive') done(); } }); diff --git a/tasks/test.js b/tasks/test.js index fbb9bac784a5..69840ded60ba 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -23,6 +23,17 @@ module.exports = function (grunt) { 'intern:dev' ]); + grunt.registerTask('test:ui:server', [ + 'esvm:ui', + 'loadFixtures', + 'run:testUIServer', + 'startSelenium:keepalive' + ]); + + grunt.registerTask('test:ui:runner', [ + 'intern:dev' + ]); + grunt.registerTask('test', function (subTask) { if (subTask) grunt.fail.fatal(`invalid task "test:${subTask}"`); From 6d44d2e9ef079f4389342f5ad2d28306c6ecee78 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 14 Sep 2015 09:17:41 -0500 Subject: [PATCH 13/20] [functional testing] Move task defaults to configs --- tasks/config/loadFixtures.js | 1 + tasks/config/startSelenium.js | 15 +++++++++++++++ tasks/loadFixtures.js | 6 ++---- tasks/startSelenium.js | 18 +++++------------- 4 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 tasks/config/startSelenium.js diff --git a/tasks/config/loadFixtures.js b/tasks/config/loadFixtures.js index 2e786998bd58..15eebbdfc43b 100644 --- a/tasks/config/loadFixtures.js +++ b/tasks/config/loadFixtures.js @@ -5,6 +5,7 @@ module.exports = function (grunt) { return { options: { server: 'http://localhost:9220', + dataDir: path.join(grunt.config.get('root'), 'test/fixtures') } }; }; diff --git a/tasks/config/startSelenium.js b/tasks/config/startSelenium.js new file mode 100644 index 000000000000..c70610b1e553 --- /dev/null +++ b/tasks/config/startSelenium.js @@ -0,0 +1,15 @@ +var path = require('path'); + + +module.exports = function (grunt) { + return { + options: { + selenium: { + filename: 'selenium-server-standalone-2.47.1.jar', + server: 'https://selenium-release.storage.googleapis.com/2.47/', + md5: 'e6cb10b8f0f353c6ca4a8f62fb5cb472', + directory: path.join(grunt.config.get('root'), 'selenium') + } + } + }; +}; diff --git a/tasks/loadFixtures.js b/tasks/loadFixtures.js index 5659edc2ae4e..2b0c6e5da5e9 100644 --- a/tasks/loadFixtures.js +++ b/tasks/loadFixtures.js @@ -6,17 +6,15 @@ var colors = require('ansicolors'); module.exports = function (grunt) { grunt.registerTask('loadFixtures', 'Loads fixtures into elasticsearch', function () { - const FIXTURES_PATH = path.join(grunt.config.get('root'), 'test/fixtures'); - const config = this.options(); const done = this.async(); - fs.readdir(FIXTURES_PATH, function (err, files) { + fs.readdir(config.dataDir, function (err, files) { if (err) grunt.fail.warn(err); let doneProcessing = 0; files.forEach(function (file) { - fs.createReadStream(path.join(FIXTURES_PATH, file)) + fs.createReadStream(path.join(config.dataDir, file)) .pipe(request.post(`${config.server}/_bulk`, function (err, res, body) { var status; if (err || res.statusCode !== 200) { diff --git a/tasks/startSelenium.js b/tasks/startSelenium.js index 27e8a6c71fda..ac1d8a83cac0 100644 --- a/tasks/startSelenium.js +++ b/tasks/startSelenium.js @@ -9,18 +9,10 @@ var {spawn} = require('child_process'); module.exports = function (grunt) { grunt.registerTask('startSelenium', 'Start an instance of selenium standalone', function (keepalive) { const done = this.async(); - const config = _.defaults(this.options(), { - seleniumStandalone: { - filename: 'selenium-server-standalone-2.47.1.jar', - server: 'https://selenium-release.storage.googleapis.com/2.47/', - md5: 'e6cb10b8f0f353c6ca4a8f62fb5cb472' - } - }); + const config = this.options(); - const SELENIUM_DIRECTORY = path.join(grunt.config.get('root'), 'selenium'); - const SELENIUM_FILE_PATH = path.join(SELENIUM_DIRECTORY, config.seleniumStandalone.filename); - const SELENIUM_DOWNLOAD_URL = config.seleniumStandalone.server + config.seleniumStandalone.filename; - const SELENIUM_HASH = config.seleniumStandalone.md5; + const SELENIUM_FILE_PATH = path.join(config.selenium.directory, config.selenium.filename); + const SELENIUM_DOWNLOAD_URL = config.selenium.server + config.selenium.filename; function waitForReadiness(seleniumProcess) { seleniumProcess.stderr.on('data', function (log) { @@ -57,7 +49,7 @@ module.exports = function (grunt) { .pipe(fs.createWriteStream(SELENIUM_FILE_PATH)) .on('finish', function () { grunt.log.writeln('done'); - validateDownload(SELENIUM_FILE_PATH, SELENIUM_HASH, success); + validateDownload(SELENIUM_FILE_PATH, config.selenium.md5, success); }) .on('error', function (err) { grunt.fail.warn(err); @@ -66,7 +58,7 @@ module.exports = function (grunt) { function start() { - fs.mkdir(SELENIUM_DIRECTORY, function (err) { + fs.mkdir(config.selenium.directory, function (err) { if (err && err.code !== 'EEXIST') return grunt.fail.warn(err); fs.exists(SELENIUM_FILE_PATH, function (exists) { if (exists) { From 5ae159d6b70d6f046ee6e1c83161d29a725736c6 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 14 Sep 2015 10:18:31 -0500 Subject: [PATCH 14/20] [functional testing] Use sync methods --- tasks/startSelenium.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tasks/startSelenium.js b/tasks/startSelenium.js index ac1d8a83cac0..807756361b12 100644 --- a/tasks/startSelenium.js +++ b/tasks/startSelenium.js @@ -56,18 +56,19 @@ module.exports = function (grunt) { }); } - function start() { - fs.mkdir(config.selenium.directory, function (err) { - if (err && err.code !== 'EEXIST') return grunt.fail.warn(err); - fs.exists(SELENIUM_FILE_PATH, function (exists) { - if (exists) { - spawnSelenium(); - } else { - downloadSelenium(spawnSelenium); - } - }); - }); + try { + fs.mkdirSync(config.selenium.directory); + + } catch (err) { + if (err && err.code !== 'EEXIST') grunt.fail.warn(err); + } + + if (fs.existsSync(SELENIUM_FILE_PATH)) { + spawnSelenium(); + } else { + downloadSelenium(spawnSelenium); + } } start(); From a47725ab91d8c160515dcd3181eada17cd407e76 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 14 Sep 2015 14:09:50 -0500 Subject: [PATCH 15/20] [functional testing] Use wreck --- tasks/loadFixtures.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tasks/loadFixtures.js b/tasks/loadFixtures.js index 2b0c6e5da5e9..aacf5b81c447 100644 --- a/tasks/loadFixtures.js +++ b/tasks/loadFixtures.js @@ -1,5 +1,5 @@ var _ = require('lodash'); -var request = require('request'); +var wreck = require('wreck'); var fs = require('fs'); var path = require('path'); var colors = require('ansicolors'); @@ -14,18 +14,20 @@ module.exports = function (grunt) { let doneProcessing = 0; files.forEach(function (file) { - fs.createReadStream(path.join(config.dataDir, file)) - .pipe(request.post(`${config.server}/_bulk`, function (err, res, body) { + wreck.post(`${config.server}/_bulk`, { + payload: fs.createReadStream(path.join(config.dataDir, file)), + json: true + }, function (err, res, payload) { var status; if (err || res.statusCode !== 200) { - grunt.fail.warn(err || body); + grunt.fail.warn(err || payload); status = colors.red('error'); } else { status = colors.green('success'); } grunt.log.writeln(`[${status}] ${file}`); if (++doneProcessing === files.length) done(); - })); + }); }); }); }); From ca81d4b7774720c0b9c3a2860693c43d122b1eb7 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 15 Sep 2015 16:26:04 -0500 Subject: [PATCH 16/20] [functional testing] Use grunt-run with selenium --- package.json | 2 +- .../{startSelenium.js => downloadSelenium.js} | 0 tasks/config/run.js | 28 ++++++++++++++ .../{startSelenium.js => downloadSelenium.js} | 37 +++++-------------- tasks/loadFixtures.js | 35 ++++++++---------- tasks/test.js | 6 ++- 6 files changed, 59 insertions(+), 49 deletions(-) rename tasks/config/{startSelenium.js => downloadSelenium.js} (100%) rename tasks/{startSelenium.js => downloadSelenium.js} (64%) diff --git a/package.json b/package.json index 020f1e29ed72..5255e421764e 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "grunt-contrib-copy": "0.8.1", "grunt-esvm": "1.1.5", "grunt-karma": "0.12.0", - "grunt-run": "0.4.0", + "grunt-run": "0.5.0", "grunt-s3": "0.2.0-alpha.3", "grunt-simple-mocha": "0.4.0", "gruntify-eslint": "1.0.1", diff --git a/tasks/config/startSelenium.js b/tasks/config/downloadSelenium.js similarity index 100% rename from tasks/config/startSelenium.js rename to tasks/config/downloadSelenium.js diff --git a/tasks/config/run.js b/tasks/config/run.js index 7441c54e74fc..473550c7b2cf 100644 --- a/tasks/config/run.js +++ b/tasks/config/run.js @@ -74,6 +74,34 @@ module.exports = function (grunt) { ] }, + seleniumServer: { + options: { + wait: false, + ready: /Selenium Server is up and running/, + quiet: true, + failOnError: false + }, + cmd: 'java', + args: [ + '-jar', + 'selenium/selenium-server-standalone-2.47.1.jar' + ] + }, + + devSeleniumServer: { + options: { + wait: false, + ready: /Selenium Server is up and running/, + quiet: false, + failOnError: false + }, + cmd: 'java', + args: [ + '-jar', + 'selenium/selenium-server-standalone-2.47.1.jar' + ] + }, + optimizeBuild: { options: { wait: false, diff --git a/tasks/startSelenium.js b/tasks/downloadSelenium.js similarity index 64% rename from tasks/startSelenium.js rename to tasks/downloadSelenium.js index 807756361b12..dfc5ac3f1419 100644 --- a/tasks/startSelenium.js +++ b/tasks/downloadSelenium.js @@ -7,67 +7,50 @@ var crypto = require('crypto'); var {spawn} = require('child_process'); module.exports = function (grunt) { - grunt.registerTask('startSelenium', 'Start an instance of selenium standalone', function (keepalive) { + grunt.registerTask('downloadSelenium', 'Download selenium standalone', function (keepalive) { const done = this.async(); const config = this.options(); const SELENIUM_FILE_PATH = path.join(config.selenium.directory, config.selenium.filename); const SELENIUM_DOWNLOAD_URL = config.selenium.server + config.selenium.filename; - function waitForReadiness(seleniumProcess) { - seleniumProcess.stderr.on('data', function (log) { - if (~log.toString('utf8').indexOf('Selenium Server is up and running')) { - grunt.log.writeln('Selenium standalone started on port 4444'); - if (keepalive !== 'keepalive') done(); - } - }); - - } - function validateDownload(path, expectedHash, success) { grunt.log.write('Validating hash...'); - fs.readFile(path, function (err, data) { + fs.readFile(path, function checkHash(err, data) { if (err) grunt.fail.warn(err); + const calculatedHash = crypto.createHash('md5').update(data).digest('hex'); if (calculatedHash !== expectedHash) return grunt.fail.warn('Selenium download has an invalid hash'); + grunt.log.writeln('done'); success(); }); } - function spawnSelenium() { - var seleniumProcess = spawn('java', ['-jar', SELENIUM_FILE_PATH]); - process.on('exit', function () { - seleniumProcess.kill(); - }); - waitForReadiness(seleniumProcess); - } - function downloadSelenium(success) { grunt.log.write(`Downloading ${SELENIUM_DOWNLOAD_URL}...`); request.get(SELENIUM_DOWNLOAD_URL) .pipe(fs.createWriteStream(SELENIUM_FILE_PATH)) - .on('finish', function () { + .on('error', function downloadError(err) { + grunt.fail.warn(err); + }) + .on('finish', function downloadFinish() { grunt.log.writeln('done'); validateDownload(SELENIUM_FILE_PATH, config.selenium.md5, success); - }) - .on('error', function (err) { - grunt.fail.warn(err); }); } function start() { try { fs.mkdirSync(config.selenium.directory); - } catch (err) { if (err && err.code !== 'EEXIST') grunt.fail.warn(err); } if (fs.existsSync(SELENIUM_FILE_PATH)) { - spawnSelenium(); + done(); } else { - downloadSelenium(spawnSelenium); + downloadSelenium(done); } } diff --git a/tasks/loadFixtures.js b/tasks/loadFixtures.js index aacf5b81c447..b92ba4cf82f0 100644 --- a/tasks/loadFixtures.js +++ b/tasks/loadFixtures.js @@ -8,26 +8,23 @@ module.exports = function (grunt) { grunt.registerTask('loadFixtures', 'Loads fixtures into elasticsearch', function () { const config = this.options(); const done = this.async(); + const files = fs.readdirSync(config.dataDir); + let doneProcessing = 0; - fs.readdir(config.dataDir, function (err, files) { - if (err) grunt.fail.warn(err); - - let doneProcessing = 0; - files.forEach(function (file) { - wreck.post(`${config.server}/_bulk`, { - payload: fs.createReadStream(path.join(config.dataDir, file)), - json: true - }, function (err, res, payload) { - var status; - if (err || res.statusCode !== 200) { - grunt.fail.warn(err || payload); - status = colors.red('error'); - } else { - status = colors.green('success'); - } - grunt.log.writeln(`[${status}] ${file}`); - if (++doneProcessing === files.length) done(); - }); + files.forEach(function (file) { + wreck.post(`${config.server}/_bulk`, { + payload: fs.createReadStream(path.join(config.dataDir, file)), + json: true + }, function bulkResponse(err, res, payload) { + var status; + if (err || res.statusCode !== 200) { + grunt.fail.warn(err || payload); + status = colors.red('error'); + } else { + status = colors.green('success'); + } + grunt.log.writeln(`[${status}] ${file}`); + if (++doneProcessing === files.length) done(); }); }); }); diff --git a/tasks/test.js b/tasks/test.js index 69840ded60ba..a8e6e3fdabfc 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -18,8 +18,9 @@ module.exports = function (grunt) { grunt.registerTask('test:ui', [ 'esvm:ui', 'loadFixtures', - 'startSelenium', 'run:testUIServer', + 'downloadSelenium', + 'run:seleniumServer', 'intern:dev' ]); @@ -27,7 +28,8 @@ module.exports = function (grunt) { 'esvm:ui', 'loadFixtures', 'run:testUIServer', - 'startSelenium:keepalive' + 'downloadSelenium', + 'run:devSeleniumServer:keepalive' ]); grunt.registerTask('test:ui:runner', [ From f611c44af41587a3cd6e2ce6b281973d20175a8d Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Thu, 17 Sep 2015 09:56:02 -0500 Subject: [PATCH 17/20] [functional testing] Cleanup before going to next task --- tasks/test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tasks/test.js b/tasks/test.js index a8e6e3fdabfc..26964286857c 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -21,7 +21,10 @@ module.exports = function (grunt) { 'run:testUIServer', 'downloadSelenium', 'run:seleniumServer', - 'intern:dev' + 'intern:dev', + 'esvm_shutdown:ui', + 'stop:seleniumServer', + 'stop:testUIServer' ]); grunt.registerTask('test:ui:server', [ From 15f6275bd3721cacf084b1c54fecf9aa566eb59f Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Thu, 17 Sep 2015 11:56:36 -0500 Subject: [PATCH 18/20] [functional testing] Always check selenium hash --- tasks/downloadSelenium.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/downloadSelenium.js b/tasks/downloadSelenium.js index dfc5ac3f1419..62840493ee2a 100644 --- a/tasks/downloadSelenium.js +++ b/tasks/downloadSelenium.js @@ -48,7 +48,7 @@ module.exports = function (grunt) { } if (fs.existsSync(SELENIUM_FILE_PATH)) { - done(); + validateDownload(SELENIUM_FILE_PATH, config.selenium.md5, done); } else { downloadSelenium(done); } From 4ae294937d66bd15f564372a6a6fe88bf0a1e24f Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Thu, 17 Sep 2015 15:29:24 -0500 Subject: [PATCH 19/20] [functional testing] Use CommonJS style syntax in test --- test/functional/apps.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/functional/apps.js b/test/functional/apps.js index c8015221a16b..04569c512b2c 100644 --- a/test/functional/apps.js +++ b/test/functional/apps.js @@ -1,7 +1,7 @@ -define([ - 'intern!object', - 'intern/dojo/node!expect.js' -], function (registerSuite, expect) { +define(function (require) { + var registerSuite = require('intern!object'); + var expect = require('intern/dojo/node!expect.js'); + registerSuite(function () { var url = 'http://localhost:5620/apps'; return { From 251bf525eb8a348b93359682e9104d45ed987723 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Thu, 17 Sep 2015 16:38:04 -0500 Subject: [PATCH 20/20] [functional testing] Test for kibana ready on status page --- test/functional/{apps.js => status.js} | 9 +++++---- test/intern.js | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) rename test/functional/{apps.js => status.js} (60%) diff --git a/test/functional/apps.js b/test/functional/status.js similarity index 60% rename from test/functional/apps.js rename to test/functional/status.js index 04569c512b2c..514de74c0676 100644 --- a/test/functional/apps.js +++ b/test/functional/status.js @@ -3,16 +3,17 @@ define(function (require) { var expect = require('intern/dojo/node!expect.js'); registerSuite(function () { - var url = 'http://localhost:5620/apps'; + var url = 'http://localhost:5620/status'; return { - 'apps': function () { + 'status': function () { return this.remote .get(url) + .refresh() .setFindTimeout(60000) - .findByCssSelector('a[href="/app/kibana"]') + .findByCssSelector('.plugin_status_breakdown') .getVisibleText() .then(function (text) { - expect(text).to.eql('Kibana\nthe kibana you know and love'); + expect(text.indexOf('plugin:kibana Ready')).to.be.above(-1); }); } }; diff --git a/test/intern.js b/test/intern.js index 9bbae79a7578..371c758cd152 100644 --- a/test/intern.js +++ b/test/intern.js @@ -12,6 +12,6 @@ define({ host: 'localhost', port: 4444 }, - functionalSuites: ['test/functional/apps.js'], + functionalSuites: ['test/functional/status.js'], excludeInstrumentation: /^(?:tests|node_modules)\// });