From 9a23663c74ee51004bfcacc7b61a450ccd69aaab Mon Sep 17 00:00:00 2001 From: Nick Bogdanov Date: Wed, 8 Jul 2015 15:46:09 +0300 Subject: [PATCH] Intern-based functional tests This commit makes it possible to execute UI functional tests either based on Casper or on Intern/Selenium Change-Id: I21d342093f142a1bbe7b8ae4f2c96fad1c125a0b --- gulpfile.js | 12 +- package.json | 2 +- static/tests/functional/pages/cluster.js | 103 ++++++++++++++ static/tests/functional/pages/clusters.js | 78 +++++++++++ static/tests/functional/pages/common.js | 132 ++++++++++++++++++ static/tests/functional/pages/login.js | 84 +++++++++++ static/tests/functional/pages/welcome.js | 47 +++++++ static/tests/functional/test_cluster_page.js | 129 +++++++++++++++++ static/tests/functional/test_clusters_page.js | 60 ++++++++ static/tests/functional/test_login_logout.js | 59 ++++++++ static/tests/functional/test_welcome_page.js | 49 +++++++ static/tests/helpers.js | 47 +++++++ static/tests/intern.js | 5 +- 13 files changed, 802 insertions(+), 5 deletions(-) create mode 100644 static/tests/functional/pages/cluster.js create mode 100644 static/tests/functional/pages/clusters.js create mode 100644 static/tests/functional/pages/common.js create mode 100644 static/tests/functional/pages/login.js create mode 100644 static/tests/functional/pages/welcome.js create mode 100644 static/tests/functional/test_cluster_page.js create mode 100644 static/tests/functional/test_clusters_page.js create mode 100644 static/tests/functional/test_login_logout.js create mode 100644 static/tests/functional/test_welcome_page.js create mode 100644 static/tests/helpers.js diff --git a/gulpfile.js b/gulpfile.js index 64c259e41..9ccacf570 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -158,7 +158,7 @@ function runIntern(params) { return function() { var baseDir = 'static'; var runner = './node_modules/.bin/intern-runner'; - var browser = params.browser || argv.browser || 'phantomjs'; + var browser = params.browser || argv.browser || 'firefox'; var options = [['config', 'tests/intern-' + browser + '.js']]; var suiteOptions = []; ['suites', 'functionalSuites'].forEach(function(suiteType) { @@ -181,7 +181,8 @@ function runIntern(params) { }; } -gulp.task('intern:unit', runIntern({suites: argv.suites || 'static/tests/unit/**/*.js'})); +gulp.task('intern:unit', runIntern({suites: argv.suites || 'static/tests/unit/**/*.js', browser: 'phantomjs'})); +gulp.task('intern:functional', runIntern({functionalSuites: argv.suites || 'static/tests/functional/**/test_*.js'})); gulp.task('unit-tests', function(cb) { runSequence('selenium', 'intern:unit', function(err) { @@ -190,6 +191,13 @@ gulp.task('unit-tests', function(cb) { }); }); +gulp.task('functional-tests', function(cb) { + runSequence('selenium', 'intern:functional', function(err) { + shutdownSelenium(); + cb(err); + }); +}); + gulp.task('jison', function() { return gulp.src('static/expression/parser.jison') .pipe(jison({moduleType: 'js'})) diff --git a/package.json b/package.json index cedc6621f..a91ee4c78 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,6 @@ "rimraf": "~2.2.8", "run-sequence": "~1.0.2", "selenium-standalone": "~4.4.0", - "uglify-js": "~2.4.16" + "uglify-js": "~2.4.21" } } diff --git a/static/tests/functional/pages/cluster.js b/static/tests/functional/pages/cluster.js new file mode 100644 index 000000000..485202485 --- /dev/null +++ b/static/tests/functional/pages/cluster.js @@ -0,0 +1,103 @@ +/* + * Copyright 2015 Mirantis, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +define(['underscore', + '../../helpers'], function(_, Helpers) { + 'use strict'; + function ClusterPage(remote) { + this.remote = remote; + } + + ClusterPage.prototype = { + constructor: ClusterPage, + goToTab: function(tabName) { + var that = this; + return this.remote + .then(function() { + return Helpers.clickLinkByText( + that.remote, + '.tabs-box .tabs a', + tabName); + }); + }, + removeCluster: function() { + var that = this; + return this.remote + .then( + function() { + return this.parent + .setFindTimeout(2000) + .then(function() { + return that.goToTab('Actions'); + }) + .findByCssSelector('button.delete-environment-btn') + .click() + .end() + .setFindTimeout(2000) + .findByCssSelector('div.modal-content') + .findByCssSelector('button.remove-cluster-btn') + .click() + .end() + .setFindTimeout(2000) + .waitForDeletedByCssSelector('div.modal-content'); + } + ); + }, + checkNodeRoles: function(assignRoles) { + return this.remote + .setFindTimeout(2000) + .findAllByCssSelector('div.role-panel label') + .then(function(roles) { + return roles.reduce( + function(result, role) { + return role + .getVisibleText() + .then(function(label) { + var index = assignRoles.indexOf(label.substr(1)); + if (index >= 0) { + role.click(); + assignRoles.splice(index, 1); + return assignRoles.length == 0; + } + }); + }, + false + ); + }); + }, + checkNodes: function(amount) { + var that = this; + return this.remote + .setFindTimeout(2000) + .then(function() { + return _.range(amount).reduce( + function(result, index) { + return that.remote + .setFindTimeout(1000) + .findAllByCssSelector('.node.discover > label') + .then(function(nodes) { + return nodes[index].click(); + }) + .catch(function() { + throw new Error('Failed to add ' + amount + ' nodes to the cluster'); + }); + }, + true); + }); + } + }; + return ClusterPage; +}); diff --git a/static/tests/functional/pages/clusters.js b/static/tests/functional/pages/clusters.js new file mode 100644 index 000000000..ec7408a35 --- /dev/null +++ b/static/tests/functional/pages/clusters.js @@ -0,0 +1,78 @@ +/* + * Copyright 2015 Mirantis, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +define([], function() { + 'use strict'; + function ClustersPage(remote) { + this.remote = remote; + } + + ClustersPage.prototype = { + constructor: ClustersPage, + createCluster: function(clusterName) { + return this.remote + .setFindTimeout(1000) + .findByClassName('create-cluster') + .click() + .end() + .setFindTimeout(2000) + .findByCssSelector('div.modal-content') + .findByName('name') + .clearValue() + .type(clusterName) + .pressKeys('\uE007') + .pressKeys('\uE007') + .pressKeys('\uE007') + .pressKeys('\uE007') + .pressKeys('\uE007') + .pressKeys('\uE007') + .pressKeys('\uE007') + .end() + .setFindTimeout(4000) + .waitForDeletedByCssSelector('div.modal-content') + .end(); + }, + clusterSelector: '.clusterbox div.name', + goToEnvironment: function(clusterName) { + var that = this; + return this.remote + .setFindTimeout(5000) + .findAllByCssSelector(that.clusterSelector) + .then(function(divs) { + return divs.reduce( + function(matchFound, element) { + return element.getVisibleText().then( + function(name) { + if (name === clusterName) { + element.click(); + return true; + } + return matchFound; + } + )}, + false + ); + }) + .then(function(result) { + if (!result) { + throw new Error('Cluster ' + clusterName + ' not found'); + } + return true; + }); + } + }; + return ClustersPage; +}); diff --git a/static/tests/functional/pages/common.js b/static/tests/functional/pages/common.js new file mode 100644 index 000000000..50ea89df2 --- /dev/null +++ b/static/tests/functional/pages/common.js @@ -0,0 +1,132 @@ +define([ + 'intern/node_modules/dojo/node!fs', + '../../helpers', + 'tests/functional/pages/login', + 'tests/functional/pages/welcome', + 'tests/functional/pages/cluster', + 'tests/functional/pages/clusters' +], + function(fs, Helpers, LoginPage, WelcomePage, ClusterPage, ClustersPage) { + 'use strict'; + function CommonMethods(remote) { + this.remote = remote; + this.loginPage = new LoginPage(remote); + this.welcomePage = new WelcomePage(remote); + this.clusterPage = new ClusterPage(remote); + this.clustersPage = new ClustersPage(remote); + } + + CommonMethods.prototype = { + constructor: CommonMethods, + takeScreenshot: function(filename, error) { + return this.remote + .takeScreenshot() + .then(function(buffer) { + if (!filename) { + filename = new Date().toTimeString(); + } + fs.writeFileSync('/tmp/' + filename + '.png', buffer); + if (error) { + throw error; + } + }); + }, + getOut: function() { + var that = this; + return this.remote + .then(function() { + return that.welcomePage.skip(); + }) + .then(function() { + return that.loginPage.logout(); + }); + }, + getIn: function() { + var that = this; + return this.remote + .then(function() { + return that.loginPage.logout(); + }) + .then(function() { + return that.loginPage.login(); + }) + .waitForDeletedByClassName('login-btn') + .then(function() { + return that.welcomePage.skip(); + }); + }, + clickLink: function(text) { + return this.remote + .setFindTimeout(1000) + .findByLinkText(text) + .click() + .end(); + }, + waitForModal: function() { + return this.remote + .setTimeout(2000) + .findByCssSelector('div.modal-content') + .end(); + }, + waitForModalToClose: function() { + return this.remote + .setTimeout(2000) + .waitForDeletedByCssSelector('div.modal-content') + .end(); + }, + goToEnvironment: function(clusterName) { + var that = this; + return this.remote + .then(function() { + return that.clickLink('Environments'); + }) + .then(function() { + return that.clustersPage.goToEnvironment(clusterName); + }); + }, + createCluster: function(clusterName) { + var that = this; + return this.remote + .then(function() { + return that.clickLink('Environments'); + }) + .then(function() { + return that.clustersPage.createCluster(clusterName); + }); + }, + removeCluster: function(clusterName, suppressErrors) { + var that = this; + return this.remote + .then(function() { + return that.clickLink('Environments'); + }) + .then(function() { + return that.clustersPage.goToEnvironment(clusterName); + }) + .then(function() { + return that.clusterPage.removeCluster(); + }) + .catch(function() { + if (!suppressErrors) throw new Error('Unable to delete cluster ' + clusterName); + }); + }, + doesClusterExist: function(clusterName) { + var that = this; + return this.remote + .setFindTimeout(2000) + .then(function() { + return that.clickLink('Environments'); + }) + .findAllByCssSelector(that.clustersPage.clusterSelector) + .then(function(divs) { + return divs.reduce(function(matchFound, element) { + return element.getVisibleText().then( + function(name) { + return (name === clusterName) || matchFound; + } + )}, false); + }); + } + }; + return CommonMethods; +}); diff --git a/static/tests/functional/pages/login.js b/static/tests/functional/pages/login.js new file mode 100644 index 000000000..10dff7241 --- /dev/null +++ b/static/tests/functional/pages/login.js @@ -0,0 +1,84 @@ +/* + * Copyright 2015 Mirantis, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +define([ + '../../helpers' +], function(Helpers) { + 'use strict'; + + function LoginPage(remote) { + this.remote = remote; + } + + LoginPage.prototype = { + constructor: LoginPage, + login: function(username, password) { + username = username || Helpers.username; + password = password || Helpers.password; + var that = this; + + return this.remote + .setWindowSize(1280, 1024) + .setTimeout('page load', 20000) + .getCurrentUrl() + .then(function(url) { + if (url !== Helpers.serverUrl + '/#login') { + return that.logout(); + } + }) + .setFindTimeout(10000) + .findByName('username') + .clearValue() + .type(username) + .end() + .findByName('password') + .clearValue() + .type(password) + .end() + .findByClassName('login-btn') + .click() + .end(); + }, + logout: function() { + return this.remote + .getCurrentUrl() + .then(function(url) { + if (url.indexOf(Helpers.serverUrl) !== 0) { + return this.parent + .get(Helpers.serverUrl + '/#logout') + .setFindTimeout(10000) + .findByClassName('login-btn') + .then(function() { + return true; + }); + } + }) + .setFindTimeout(1000) + .findByCssSelector('li.user-icon') + .click() + .end() + .findByCssSelector('.user-popover button.btn-logout') + .click() + .end() + .findByCssSelector('.login-btn') + .then( + function() {return true}, + function() {return true} + ); + } + }; + return LoginPage; +}); diff --git a/static/tests/functional/pages/welcome.js b/static/tests/functional/pages/welcome.js new file mode 100644 index 000000000..5fd249396 --- /dev/null +++ b/static/tests/functional/pages/welcome.js @@ -0,0 +1,47 @@ +/* + * Copyright 2015 Mirantis, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +define(['../../helpers'], function(Helpers) { + 'use strict'; + function WelcomePage(remote) { + this.remote = remote; + } + + WelcomePage.prototype = { + constructor: WelcomePage, + skip: function(strictCheck) { + return this.remote + .getCurrentUrl() + .then(function(url) { + if (url == Helpers.serverUrl + '/#welcome') { + return this.parent + .setFindTimeout(2000) + .findByCssSelector('.welcome-button-box button') + .click() + .end() + .waitForDeletedByCssSelector('.welcome-button-box button') + .then( + function() {return true}, + function() {return !strictCheck} + ); + } else { + return true; + } + }); + } + }; + return WelcomePage; +}); diff --git a/static/tests/functional/test_cluster_page.js b/static/tests/functional/test_cluster_page.js new file mode 100644 index 000000000..cb5b8346d --- /dev/null +++ b/static/tests/functional/test_cluster_page.js @@ -0,0 +1,129 @@ +/* + * Copyright 2015 Mirantis, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +define([ + 'underscore', + 'intern!object', + 'intern/chai!assert', + 'tests/functional/pages/common', + 'tests/functional/pages/cluster' +], function(_, registerSuite, assert, Common, ClusterPage) { + 'use strict'; + + registerSuite(function() { + var common, + clusterPage, + clusterName; + + return { + name: 'Clusters page', + setup: function() { + common = new Common(this.remote); + clusterPage = new ClusterPage(this.remote); + clusterName = 'Test Cluster #' + Math.round(99999 * Math.random()); + }, + beforeEach: function() { + return this.remote + .then(function() { + return common.getIn(); + }) + .then(function() { + return common.createCluster(clusterName); + }); + }, + afterEach: function() { + return this.remote + .then(function() { + return common.removeCluster(clusterName, true); + }); + }, + 'Remove Cluster': function() { + return this.remote + .then(function() { + return common.doesClusterExist(clusterName); + }) + .then(function(result) { + assert.ok(result, 'Cluster exists'); + }) + .then(function() { + return common.removeCluster(clusterName); + }) + .then(function() { + return common.doesClusterExist(clusterName); + }) + .then(function(result) { + assert.notOk(result, 'Cluster removed successfully'); + }); + }, + 'Add Cluster Nodes': function() { + var nodesAmount = 3, + that = this, + applyButton; + return this.remote + .then(function() { + return common.goToEnvironment(clusterName); + }) + .setFindTimeout(5000) + .findByCssSelector('button.btn-add-nodes') + .click() + .end() + .findByCssSelector('button.btn-apply') + .then(function(button) { + applyButton = button; + return applyButton.isEnabled().then(function(isEnabled) { + assert.isFalse(isEnabled, 'Apply button is disabled until both roles and nodes chosen'); + return true; + }); + }) + .end() + .findByCssSelector('div.role-panel') + .end() + .then(function() { + return clusterPage.checkNodeRoles(['Controller', 'Storage - Cinder']); + }) + .then(function() { + return applyButton.isEnabled().then(function(isEnabled) { + assert.isFalse(isEnabled, 'Apply button is disabled until both roles and nodes chosen'); + return true; + }); + }) + .then(function() { + return clusterPage.checkNodes(nodesAmount); + }) + .then(function() { + applyButton.click(); + }) + .setFindTimeout(2000) + .findByCssSelector('button.btn-add-nodes') + .end() + + .then(function() { + return _.range(1, 1 + nodesAmount).reduce( + function(nodesFound, index) { + return that.remote + .setFindTimeout(1000) + .findByCssSelector('div.node:nth-child(' + index + ')') + .catch(function() { + throw new Error('Unable to find ' + index + ' node in cluster'); + }); + }, + 0 + ); + }); + } + }; + }); +}); diff --git a/static/tests/functional/test_clusters_page.js b/static/tests/functional/test_clusters_page.js new file mode 100644 index 000000000..8d32f4464 --- /dev/null +++ b/static/tests/functional/test_clusters_page.js @@ -0,0 +1,60 @@ +/* + * Copyright 2015 Mirantis, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +define([ + 'intern!object', + 'intern/chai!assert', + 'tests/functional/pages/common' +], function(registerSuite, assert, Common) { + 'use strict'; + + registerSuite(function() { + var common, + clusterName; + + return { + name: 'Clusters page', + setup: function() { + common = new Common(this.remote); + clusterName = 'Test Cluster #' + Math.round(99999 * Math.random()); + }, + beforeEach: function() { + return this.remote + .then(function() { + return common.getIn(); + }) + .then(function() { + return common.createCluster(clusterName); + }); + }, + afterEach: function() { + return this.remote + .then(function() { + return common.removeCluster(clusterName, true); + }); + }, + 'Create Cluster': function() { + return this.remote + .then(function() { + return common.doesClusterExist(clusterName); + }) + .then(function(result) { + assert.ok(result, 'Newly created cluster name found in the list'); + }); + } + }; + }); +}); diff --git a/static/tests/functional/test_login_logout.js b/static/tests/functional/test_login_logout.js new file mode 100644 index 000000000..607bcbd41 --- /dev/null +++ b/static/tests/functional/test_login_logout.js @@ -0,0 +1,59 @@ +/* + * Copyright 2014 Mirantis, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +define([ + 'intern!object', + 'intern/chai!assert', + 'tests/functional/pages/login', + 'tests/functional/pages/common' +], function(registerSuite, assert, LoginPage, Common) { + 'use strict'; + + registerSuite(function() { + var loginPage, common; + return { + name: 'Login page', + setup: function() { + loginPage = new LoginPage(this.remote); + common = new Common(this.remote); + }, + beforeEach: function() { + this.remote + .then(function() { + return common.getOut(); + }); + }, + 'Login with incorrect credentials': function() { + return this.remote + .then(function() { + return loginPage.login('login', '*****'); + }) + .findByCssSelector('div.login-fields-box p.text-danger') + .isDisplayed() + .then(function(errorShown) { + assert.ok(errorShown, 'Error message is expected to be displayed'); + }); + }, + 'Login with proper credentials': function() { + return this.remote + .then(function() { + return loginPage.login(); + }) + .waitForDeletedByClassName('login-btn'); + } + }; + }); +}); diff --git a/static/tests/functional/test_welcome_page.js b/static/tests/functional/test_welcome_page.js new file mode 100644 index 000000000..582f2976a --- /dev/null +++ b/static/tests/functional/test_welcome_page.js @@ -0,0 +1,49 @@ +/* + * Copyright 2015 Mirantis, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +define([ + 'intern!object', + 'intern/chai!assert', + 'tests/functional/pages/login', + 'tests/functional/pages/welcome' +], function(registerSuite, assert, LoginPage, WelcomePage) { + 'use strict'; + + registerSuite(function() { + var loginPage, + welcomePage; + + return { + name: 'Welcome page', + setup: function() { + loginPage = new LoginPage(this.remote); + welcomePage = new WelcomePage(this.remote); + }, + 'Skip welcome page': function() { + return this.remote + .then(function() { + return loginPage.login(); + }) + .then(function() { + return welcomePage.skip(true); + }) + .then(function(result) { + assert.ok(result, 'Start using fuel button is present'); + }); + } + }; + }); +}); diff --git a/static/tests/helpers.js b/static/tests/helpers.js new file mode 100644 index 000000000..1e426feb2 --- /dev/null +++ b/static/tests/helpers.js @@ -0,0 +1,47 @@ +/* + * Copyright 2014 Mirantis, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +define(['underscore'], function(_) { + 'use strict'; + + var serverHost = '127.0.0.1', + serverPort = process.env.SERVER_PORT || 5544, + serverUrl = 'http://' + serverHost + ':' + serverPort, + username = 'admin', + password = 'admin'; + + return { + username: username, + password: password, + serverUrl: serverUrl, + clickLinkByText: function(remote, cssSelector, linkText) { + return remote + .setFindTimeout(1000) + .findAllByCssSelector(cssSelector) + .then(function(links) { + return links.reduce(function(matchFound, link) { + return link.getVisibleText().then(function(text) { + if (_.trim(text) == linkText) { + link.click(); + return true; + } + return matchFound; + }); + }, false); + }); + } + }; +}); diff --git a/static/tests/intern.js b/static/tests/intern.js index fec6d28e3..c0a309299 100644 --- a/static/tests/intern.js +++ b/static/tests/intern.js @@ -31,8 +31,9 @@ define(['config'], function(config) { 'host-node': 'requirejs', 'host-browser': '/vendor/bower/requirejs/require.js' }, - // A regular expression matching URLs to files that should not be included in code coverage analysis + grep: /^/, excludeInstrumentation: /^/, - loader: config + loader: config, + reporters: ['console'] }; });