├── .circleci └── config.yml ├── .editorconfig ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmignore ├── CHANGELOG ├── Gruntfile.js ├── README.md ├── assertions ├── elementsCount.js ├── elementsPresent.js ├── elementsVisible.js └── templateName.js ├── commands ├── clickAndWaitUntilMobified.js ├── get.js ├── getMobifyEvaluatedData.js ├── htmlCapture.js ├── log.js ├── preview.js ├── trigger.js ├── triggerClick.js ├── triggerTouch.js ├── waitForAjaxCompleted.js ├── waitForAnimation.js ├── waitForCondition.js ├── waitForContextsReady.js ├── waitForUrl.js ├── waitForUrlToContain.js └── waitUntilMobified.js ├── package.json ├── selenium └── install.js └── tests ├── mocks.json ├── nightwatch.js ├── node_modules └── mockserver.js ├── nodeunit.json ├── output └── .gitignore ├── run_tests.js ├── src ├── testElementsCountAssertion.js ├── testElementsPresentAssertion.js ├── testElementsVisibleAssertion.js ├── testGet.js ├── testGetMobifyEvaluatedData.js ├── testTemplateName.js ├── testTrigger.js ├── testWaitForAjaxCompleted.js ├── testWaitForAnimation.js ├── testWaitForCondition.js ├── testWaitForContextsReady.js ├── testWaitForUrl.js └── testWaitUntilMobified.js └── tests └── system └── site.json /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | references: 3 | container_config: &container_config 4 | docker: 5 | - image: node:8.9.4 6 | working_directory: /home/ubuntu/nightwatch-commands/ 7 | 8 | repo_cache_key: &repo_cache_key 9 | nightwatch-commands-repo-v1-{{ .Branch }}-{{ .Revision }} 10 | 11 | npm_cache_key: &npm_cache_key 12 | nightwatch-commands-npm-v1-{{ .Branch }}-{{ .Revision }} 13 | 14 | restore_repo: &restore_repo 15 | restore_cache: 16 | keys: 17 | - *repo_cache_key 18 | 19 | restore_node_modules: &restore_node_modules 20 | restore_cache: 21 | keys: 22 | - *npm_cache_key 23 | 24 | jobs: 25 | build: 26 | <<: *container_config 27 | steps: 28 | - *restore_repo 29 | - checkout 30 | - save_cache: 31 | key: *repo_cache_key 32 | paths: 33 | - . 34 | - *restore_node_modules 35 | - run: 36 | name: Install dependencies 37 | command: npm install 38 | - save_cache: 39 | key: *npm_cache_key 40 | paths: 41 | - /home/ubuntu/nightwatch-commands/node_modules 42 | 43 | unit-test: 44 | <<: *container_config 45 | steps: 46 | - *restore_repo 47 | - *restore_node_modules 48 | - run: 49 | name: Run unit tests 50 | command: npm test 51 | working_directory: /home/ubuntu/nightwatch-commands/ 52 | 53 | workflows: 54 | version: 2 55 | build_and_test: 56 | jobs: 57 | - build 58 | - unit-test: 59 | requires: 60 | - build 61 | 62 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 4 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to nightwatch-commands 2 | 3 | ## Bugs, Feature Requests 4 | Submit an [issue](https://github.com/mobify/nightwatch-commands/issues). 5 | 6 | ## Pull Requests 7 | 8 | 1. Make a new branch from `develop`. 9 | 1. Please ensure that you have [EditorConfig](http://editorconfig.org/) installed to ensure code consistency. 10 | 1. Do your work. 11 | 1. Update the README. 12 | 1. Run `grunt lint` and fix any issues. 13 | 1. Open a PR against `develop`. 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Summary 2 | 3 | ### Expected behaviour 4 | 5 | ### Actual behaviour 6 | 7 | ### Steps to reproduce 8 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Status: **Ready for Review** or **Open for Visibility** 2 | Owner: 3 | Reviewers: @ellenmobify @jasecode 4 | 5 | ## Changes 6 | - (change1) 7 | - (change2) 8 | 9 | ## Todos: 10 | - [ ] Passed linting check (run `grunt lint`) 11 | - [ ] Updated README 12 | - [ ] Updated CHANGELOG 13 | 14 | ### Feedback: 15 | _none so far_ 16 | 17 | ## How To Test 18 | - Checkout this branch 19 | - `npm install` 20 | - `npm link` 21 | - `grunt lint` 22 | - (notes) 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.project 2 | *.DS_Store 3 | node_modules 4 | *.idea 5 | npm-debug.log 6 | *.orig 7 | /selenium/drivers 8 | /selenium/selenium-server.jar 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobify/nightwatch-commands/46919dda3422b5e0db48f596fc6da47e3d5cf2a1/.npmignore -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 3.0.0 2 | - A **breaking change** to the `.preview()` command to make it more explicit. `.preview(url, bundle, callback)` essentially replaces the role of `site.js`. 3 | 2.1.0 4 | - Adds a `triggerClick` command that uses JavaScript's click. 5 | - Renames `navigate` to `clickAndWaitUntilMobified` to avoid colliding with a core Nightwatch command with the same name. Resolves https://github.com/mobify/nightwatch-commands/issues/74 6 | - Warnings related to a missing `site.json` should no longer be output if not using the `.preview()` command. Resolves https://github.com/mobify/nightwatch-commands/issues/73 7 | 2.0.1 8 | -Update [selenium-download](https://github.com/groupon/selenium-download) to 2.0.10 9 | - selenium chromedriver 2.29 10 | - selenium standalone server 3.4.0 11 | 2.0.0 12 | - Use [selenium-download](https://github.com/groupon/selenium-download) to manage downloading of Selenium server and Chromedriver. 13 | - BREAKING CHANGES 14 | - This requires node 4+ and Chrome 54 or higher. 15 | 1.8.0 16 | - Use [selenium-download](https://github.com/groupon/selenium-download) to manage downloading of Selenium server and Chromedriver. Compatible with node 0.12.x. 17 | 1.7.0 18 | - Introduces 'waitForContextsReady' command. 19 | 1.6.3 20 | - Fixes a bug in `waitForAjaxCompleted` in cases where `$` is not bound to jQuery. 21 | 1.6.2 22 | - Update `preview` command to accept `site.js` files under `/system/site.js` 23 | 1.6.1 24 | - Update seleniumVersion to 2.52.0 25 | - Update chromeDriver version to 2.21 26 | - Update README to include use section 27 | 1.6.0 28 | - Update `preview` command to accept an optional `site.js`. 29 | - README now contains the latest documentation 30 | 1.5.0 31 | - Follow company practices for tools and gitflow 32 | - Have CircleCI lint our source code (TODO: tests to be fixed later) 33 | - Update JS linting to current standards (grunt-eslint ^17.0.0 & mobify-code-style ^2.3.6) 34 | - Fine tune linting targets to capture as much source code as feasible 35 | - Introduces the command `waitForUrlToContain` 36 | 1.4.0 37 | - Introduces the command `waitForUrl` 38 | 1.3.2 39 | - Fixes a bug introduced in 1.3.1 with the refactoring of the preview command. The bug affected tests that used a key other than before/beforeEach to refer to the set up steps of a test, and in which preview was called more than once. 40 | 1.3.1 41 | - Make site.json optional 42 | 1.3.0 43 | - Update Selenium and Chrome Drivers (2.48.2 & 2.20) 44 | 1.2.0 45 | - htmlCapture command to get/update fixtures 46 | 1.1.1 47 | - Downgrade Chromedriver to 2.12 48 | 1.1.0 49 | - Bumps Selenium server to 2.45 50 | - Bumps Chromedriver to 2.15 51 | 1.0.0 52 | - Reformatting and initial v1 release 53 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | var targets = [ 3 | '*.js', 4 | '**/*.js', 5 | '!node_modules/**', 6 | '!tests/node_modules/**', 7 | '!selenium/**' 8 | ]; 9 | 10 | grunt.loadNpmTasks('grunt-eslint'); 11 | 12 | grunt.initConfig({ 13 | pkg: grunt.file.readJSON('package.json'), 14 | eslint: { 15 | dev: { 16 | src: targets, 17 | options: { 18 | reset: true, 19 | config: require.resolve('mobify-code-style/javascript/.eslintrc') 20 | } 21 | }, 22 | prod: { 23 | src: targets, 24 | options: { 25 | reset: true, 26 | config: require.resolve('mobify-code-style/javascript/.eslintrc-prod') 27 | } 28 | } 29 | } 30 | }); 31 | 32 | grunt.registerTask('test', function() { 33 | var callback = this.async(); 34 | 35 | grunt.util.spawn({ 36 | cmd: 'node', 37 | args: ['./tests/run_tests.js'], 38 | opts: {stdio: 'inherit'} 39 | }, 40 | function() { 41 | callback(); 42 | }); 43 | }); 44 | 45 | grunt.registerTask('lint', ['eslint:prod']); 46 | }; 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nightwatch-commands 2 | =================== 3 | 4 | [![NPM version](https://badge.fury.io/js/nightwatch-commands.svg)](http://badge.fury.io/js/nightwatch-commands) 5 | 6 | A set of Mobify specific custom commands for Nightwatch.js 7 | 8 | ### Download 9 | To begin, clone this repository. `cd` into your chosen folder, and run the following: 10 | `npm install` 11 | 12 | Selenium server and Chromedriver will also be downloaded into `/selenium`. 13 | 14 | Currently Using https://www.npmjs.com/package/selenium-download version: 2.0.10 15 | - selenium chromedriver 2.29 16 | - selenium standalone server 3.4.0 17 | 18 | ### Linting 19 | JavaScript in this tool is linted with [ESLint](http://eslint.org/) according to our code [syntax and style standards](https://github.com/mobify/mobify-code-style) here at Mobify. 20 | 21 | Linting may be run with the `grunt lint` command. Code is also linted automatically on [CircleCI](https://circleci.com/). 22 | 23 | ### Use 24 | In order for your project to be able to access these commands and assertions you need to include them in your projects nightwatch.js `settings.json` file. 25 | 26 | It should look something like this: 27 | 28 | ``` 29 | "custom_commands_path": "./node_modules/nightwatch-commands/commands", 30 | "custom_assertions_path": "./node_modules/nightwatch-commands/assertions" 31 | ``` 32 | 33 | ## Assertions 34 | 35 | #### elementsCount(selector, expected, message, callback) 36 | 37 | The `elementsCount` assertion checks if the given selector is present the number of times that is expected for that selector to appear. 38 | 39 | Parameter Name | Parameter Type | Description 40 | ------------- | -------------- | ----------- 41 | selector | String | The CSS/Xpath selector to locate the element. 42 | expected | Number | The expected number of times for the attribute to appear. 43 | message | String | _optional_ The message to output. 44 | callback | Function | _optional_ A function to call after the current command finishes execution. 45 | 46 | ``` 47 | this.demoTest = function (browser) { 48 | browser.assert.elementsCount('#x-root', 1); 49 | } 50 | ``` 51 | 52 | #### elementsPresent(selector, message, callback) 53 | 54 | The `elementsPresent` assertion checks if the given selectors are present. It returns a list of the missing selectors. 55 | 56 | Parameter Name | Parameter Type | Description 57 | ------------- | -------------- | ----------- 58 | selector | String | The CSS/Xpath selector to locate the element. 59 | message | String | _optional_ The message to output. 60 | callback | Function | _optional_ A function to call after the current command finishes execution. 61 | 62 | ``` 63 | this.demoTest = function (browser) { 64 | browser.assert.elementsPresent('#x-root', '#x-header', '#x-footer'); 65 | }; 66 | ``` 67 | 68 | #### elementsVisible(selector, message, callback) 69 | 70 | The `elementsVisible` assertion checks if one or more selectors are visible. It returns a list of one or more selectors that are not visible on the page. 71 | 72 | Parameter Name | Parameter Type | Description 73 | ------------- | -------------- | ----------- 74 | selectors | String | The CSS/Xpath selector to locate the element. 75 | callback | Function | _optional_ A function to call after the current command finishes execution. 76 | 77 | ``` 78 | this.demoTest = function (browser) { 79 | browser.assert.elementsVisible('#x-root', '#x-head'); 80 | }; 81 | ``` 82 | 83 | #### templateName(expected, message, callback) 84 | 85 | The `templateName` assertion checks if the given template name is correct. 86 | 87 | Parameter Name | Parameter Type | Description 88 | ------------- | -------------- | ----------- 89 | expected | String | The expected value of the attribute to check. 90 | message | String | _optional_ A log message to display in the output. If the parameter is not specified, a default message is displayed. 91 | callback | Function | _optional_ A function to call after the current command finishes execution. 92 | 93 | ``` 94 | this.demoTest = function (client) { 95 | browser.assert.templateName('home'); 96 | }; 97 | ``` 98 | 99 | ## Commands 100 | 101 | #### get(url, callback) 102 | 103 | The `get` command combines the `url` and `waitUntilMobified` functions. This command uses the `browser.execute` function to run code within the client browser. It then waits for the `Mobify`or `Adaptive` object to be present on the page 104 | 105 | Parameter Name | Parameter Type | Description 106 | ------------- | -------------- | ----------- 107 | url | String | The URL to load 108 | callback | Function | _optional_ A function to call after the current command finishes execution. 109 | 110 | ``` 111 | this.demoTest = function (browser) { 112 | browser.get('http://www.test.com'); 113 | }; 114 | ``` 115 | 116 | #### getMobifyEvaluatedData(callback) 117 | 118 | The `getMobifyEvaluatedData` command uses the `waitForCondition` method to retrieve the `Mobify.evaluatedData` from the client browser. 119 | 120 | Input 121 | 122 | Parameter Name | Parameter Type | Description 123 | ------------- | -------------- | ----------- 124 | callback | Function | _optional_ A function to call after the current command finishes execution. 125 | 126 | Output 127 | 128 | Parameter Name | Description 129 | -------------- | ----------- 130 | Object | Returns the client object after `waitUntilMobified` executes on it with the specified parameters. 131 | 132 | ``` 133 | this.demoTest = function (browser) { 134 | browser.getMobifyEvaluatedData(); 135 | }; 136 | ``` 137 | 138 | #### htmlCapture(message, callback) 139 | 140 | `htmlCapture` is a helper command that saves HTML to `tests/system/integration/fixtures`. This command was designed to collect and save HTML files for use in integration tests. This command is intended to be run using a Nightwatch environment with a desktop user agent, however, it can still be used to capture any HTML as required. 141 | 142 | Parameter Name | Parameter Type | Description 143 | ------------- | -------------- | ----------- 144 | fileName | String | camelCased alphanumeric string ending with ".html", representing the name of the fixture 145 | callback | Function | _optional_ A function to call after the current command finishes execution. 146 | 147 | ``` 148 | this.demoTest = function (browser) { 149 | browser.htmlCapture('cart.html'); 150 | }; 151 | ``` 152 | 153 | #### log(message, callback) 154 | 155 | The `log` command prints a message to the console. Use this command to output messages in a test. 156 | 157 | Parameter Name | Parameter Type | Description 158 | ------------- | -------------- | ----------- 159 | message | String | The message to log on the console. 160 | callback | Function | _optional_ A function to call after the current command finishes execution. 161 | 162 | ``` 163 | this.demoTest = function (browser) { 164 | browser.log('Testing submitting form'); 165 | }; 166 | ``` 167 | 168 | #### clickAndWaitUntilMobified(selector, callback) 169 | 170 | The `clickAndWaitUntilMobified` command initiates a `click` command on the supplied selector link, navigates to the URL, and then it initiates the `waitUntilMobified` function before it continues the chain of tests. 171 | 172 | Parameter Name | Parameter Type | Description 173 | ------------- | -------------- | ----------- 174 | selector | String | The CSS selector to click on to navigate to the new URL. 175 | callback | Function | _optional_ A function to call after the current command finishes execution. 176 | 177 | ``` 178 | this.demoTest = function (browser) { 179 | browser.clickAndWaitUntilMobified('.myLink'); 180 | }; 181 | ``` 182 | 183 | #### preview(url, bundle, callback) 184 | 185 | The `preview` command uses http://preview.mobify.com to open a website specified by `url` to preview a given bundle specified by `bundle`. 186 | 187 | Parameter Name | Parameter Type | Description 188 | ------------- | -------------- | ----------- 189 | url | String | The URL to preview, equivalent to the Site URL field. 190 | bundle | String | _optional_ The bundle URL, equivalent to the Bundle Location field. Default is `https://localhost:8443/loader.js` 191 | callback | Function | _optional_ A function to call after the current command finishes execution. 192 | 193 | ``` 194 | this.demoTest = function (client) { 195 | browser.preview('https://www.merlinspotions.com', 'https://localhost:8443/loader.js', ); 196 | }; 197 | ``` 198 | 199 | #### trigger(selector, type, callback) 200 | 201 | The `trigger` command simulates a specified event type on the supplied DOM element specified by the selector parameter. 202 | 203 | Parameter Name | Parameter Type | Description 204 | ------------- | -------------- | ----------- 205 | selector | String | The CSS/Xpath selector to locate the element. 206 | type | String | The specified event type, for example `click` in the enabled JSON Wire Protocols. 207 | callback | Function | _optional_ A function to call after the current command finishes execution. 208 | 209 | ``` 210 | this.demoTest = function (browser) { 211 | browser.trigger('.myLink', 'click'); 212 | }; 213 | ``` 214 | 215 | #### triggerClick(selector, callback) 216 | 217 | The `triggerClick` command uses Javascript's click function on a given selector. Use this when the regular Selenium `.click` does not work. 218 | 219 | Parameter Name | Parameter Type | Description 220 | ------------- | -------------- | ----------- 221 | selector | String | The CSS/Xpath selector to locate the element. 222 | callback | Function | _optional_ A function to call after the current command finishes execution. 223 | 224 | ``` 225 | this.demoTest = function (browser) { 226 | browser.triggerClick('.myLink'); 227 | }; 228 | ``` 229 | 230 | #### triggerTouch(selector, type, callback) 231 | 232 | The `triggerTouch` command simulates a specified touch type event on the supplied DOM element. Use this command when Selenium's `click` does not register. 233 | 234 | Parameter Name | Parameter Type | Description 235 | ------------- | -------------- | ----------- 236 | selector | String | The CSS/Xpath selector to locate the element. 237 | type | String | The specified event type, for example `click` in the enabled JSON Wire Protocols. 238 | callback | Function | _optional_ A function to call after the current command finishes execution. 239 | 240 | ``` 241 | this.demoTest = function (browser) { 242 | browser.triggerTouch('.myLink', 'click'); 243 | }; 244 | ``` 245 | 246 | #### waitForAjaxCompleted(callback) 247 | 248 | The `waitForAjaxCompleted` command uses the `waitForCondition` function to execute code within the client browser. The command checks the value of `jQuery.active` to ensure that the number of active connections is 0. 249 | 250 | Parameter Name | Parameter Type | Description 251 | ------------- | -------------- | ----------- 252 | callback | Function | _optional_ A function to call after the current command finishes execution. 253 | 254 | ``` 255 | this.demoTest = function (browser) { 256 | browser.waitForAjaxCompleted(); 257 | }; 258 | ``` 259 | 260 | #### waitForAnimation(milliSeconds, callback) 261 | 262 | The `waitForAnimation` command suspends the test for the given time in milliseconds while it waits for animation to complete. 263 | 264 | Parameter Name | Parameter Type | Description 265 | ------------- | -------------- | ----------- 266 | milliSeconds | Number | The number of millliseconds to wait. 267 | callback | Function | _optional_ A function to call after the current command finishes execution. 268 | 269 | ``` 270 | this.demoTest = function (browser) { 271 | browser.waitForAnimation(); 272 | }; 273 | ``` 274 | 275 | #### waitForCondition(condition, milliSeconds, timeout, message, callback) 276 | 277 | The `waitForCondition` command receives a condition to check for, waits for a maximum time before timing out, and polls at a specified time interval. The condition returns either as a success or a timeout. 278 | 279 | Parameter Name | Parameter Type | Description 280 | ------------- | -------------- | ----------- 281 | condition | Function | The condition to check against. 282 | milliSeconds | Number | _optional_ The number of milliseconds to poll before timeout. 283 | timeout | Number | _optional_ The number of milliseconds between each poll. 284 | message | String | _optional_ The message to output. 285 | callback | Function | _optional_ A function to call after the current command finishes execution. 286 | 287 | ``` 288 | this.demoTest = function (browser) { 289 | return browser.waitForCondition('return $.active;', 8000, function(result) { 290 | if (typeof callback === 'function') { 291 | callback.call(browser, result); 292 | } 293 | }); 294 | }; 295 | ``` 296 | 297 | #### waitForContextsReady(expectedContextsCount, milliseconds, timeout, messages, callback) 298 | 299 | The `waitForContextsReady` command receives a number of expected contexts to check for, waits for a maximum time before timing out, and polls at a specified time interval. Used with Appium when testing hybrid mobile web apps. See http://nightwatchjs.org/api/contexts.html 300 | 301 | Parameter Name | Parameter Type | Description 302 | ------------- | -------------- | ----------- 303 | expectedContextsCount | Number | The condition to check against. 304 | milliSeconds | Number | _optional_ The number of milliseconds to poll before timeout. 305 | timeout | Number | _optional_ The number of milliseconds between each poll. 306 | message | String | _optional_ The message to output. 307 | callback | Function | _optional_ A function to call after the current command finishes execution. 308 | 309 | ``` 310 | this.demoTest = function (browser) { 311 | browser.waitForContextsReady(3, 8000); 312 | // At this point, it is safe to switch the context to either the NATIVE 313 | // context or any of the WEBVIEW_* contexts. 314 | }; 315 | ``` 316 | 317 | #### waitForUrl(url, milliseconds, timeout, messages, callback) 318 | 319 | The `waitForUrl` command waits until the page URL is equal to the specified `url`. 320 | 321 | Parameter Name | Parameter Type | Description 322 | ------------- | -------------- | ----------- 323 | url | String | Expected URL 324 | milliSeconds | Number | _optional_ The number of milliseconds to poll before timeout. 325 | timeout | Number | _optional_ The number of milliseconds between each poll. 326 | message | String | _optional_ The message to output. 327 | callback | Function | _optional_ A function to call after the current command finishes execution. 328 | 329 | ``` 330 | this.demoTest = function (browser) { 331 | browser.waitForUrl('http://www.google.ca/'); 332 | }; 333 | ``` 334 | 335 | #### waitForUrlToContain(url, milliseconds, timeout, messages, callback) 336 | 337 | The `waitForUrlToContain` command waits until the page URL contains the specified `url`. 338 | 339 | Parameter Name | Parameter Type | Description 340 | ------------- | -------------- | ----------- 341 | url | String | A partial URL to match against. 342 | milliSeconds | Number | _optional_ The number of milliseconds to poll before timeout. 343 | timeout | Number | _optional_ The number of milliseconds between each poll. 344 | message | String | _optional_ The message to output. 345 | callback | Function | _optional_ A function to call after the current command finishes execution. 346 | 347 | ``` 348 | this.demoTest = function (browser) { 349 | browser.waitForUrlToContain('google'); 350 | }; 351 | ``` 352 | 353 | #### waitUntilMobified(milliSeconds, callback) 354 | 355 | The `waitUntilMobified` command will use the `waitForCondition` command to poll for the Mobify or Adaptive object on the page to ensure that the adaptation is complete. Use this command to browse to a page or if the page reloads. 356 | 357 | Parameter Name | Parameter Type | Description 358 | ------------- | -------------- | ----------- 359 | milliSeconds | Number | _optional_ The number of milliseconds to poll before timeout. 360 | If not specified, the default timeout is 10,000 milliseconds. 361 | callback | Function | _optional_ A function to call after the current command finishes execution. 362 | 363 | ``` 364 | this.demoTest = function (browser) { 365 | browser.waitUntilMobified(); 366 | }; 367 | ``` 368 | -------------------------------------------------------------------------------- /assertions/elementsCount.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if the given selector is present the expected number of times. 3 | * 4 | * ``` 5 | * this.demoTest = function (client) { 6 | * browser.assert.elementsCount('#x-root', 1); 7 | * }; 8 | * ``` 9 | * 10 | * @method attributeEquals 11 | * @param {string} selector The selector (CSS / Xpath) used to locate the element. 12 | * @param {string} attribute The attribute name 13 | * @param {string} expected The expected value of the attribute to check. 14 | * @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default. 15 | * @api assertions 16 | */ 17 | 18 | var util = require('util'); 19 | exports.assertion = function(selector, expected, msg) { 20 | 21 | var MSG_ELEMENT_NOT_FOUND = 'Testing if the count for the element <%s> is %s. '; 22 | 23 | this.message = msg || util.format('Testing if the count for the element <%s> is %s.', selector, expected); 24 | 25 | this.expected = function() { 26 | return expected; 27 | }; 28 | 29 | this.pass = function(value) { 30 | return value === expected; 31 | }; 32 | 33 | this.failure = function(result) { 34 | var failed = result === false || result && result.status === -1; 35 | if (failed) { 36 | this.message = msg || util.format(MSG_ELEMENT_NOT_FOUND, selector, expected); 37 | } 38 | return failed; 39 | }; 40 | 41 | this.value = function(result) { 42 | return result.value.length; 43 | }; 44 | 45 | this.command = function(callback) { 46 | return this.api.elements('css selector', selector, callback); 47 | }; 48 | 49 | }; 50 | -------------------------------------------------------------------------------- /assertions/elementsPresent.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if the given selectors are present and returns a list of missing selectors. 3 | * 4 | * ``` 5 | * this.demoTest = function (client) { 6 | * browser.assert.elementsPresent('#x-root', '#x-head'); 7 | * }; 8 | * ``` 9 | * 10 | * @method attributeEquals 11 | * @param {string} selectors The selectors (CSS / Xpath) used to locate the elements, separated by commas. 12 | * @param {string} expected The expected value of the attribute to check. 13 | * @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default. 14 | * @api assertions 15 | */ 16 | 17 | var async = require('async'), 18 | util = require('util'), 19 | events = require('events'); 20 | 21 | var Assertion = function() { 22 | events.EventEmitter.call(this); 23 | this.cb = null; 24 | this.abortOnFailure = true; 25 | this.selector = null; 26 | }; 27 | 28 | util.inherits(Assertion, events.EventEmitter); 29 | 30 | Assertion.prototype.command = function(selectors, callback) { 31 | var args = Array.prototype.slice.call(arguments, 0); 32 | var lastArgument = args[args.length - 1]; 33 | 34 | if (typeof (lastArgument) === 'function') { 35 | callback = args.pop(); 36 | } else { 37 | callback = function() { 38 | }; 39 | } 40 | 41 | this.cb = callback; 42 | this.selectors = args.slice(0); 43 | this.checkElements(); 44 | 45 | return this; 46 | }; 47 | 48 | Assertion.prototype.checkElements = function() { 49 | var self = this; 50 | var missing = []; 51 | var found = []; 52 | var selectors = this.selectors; 53 | 54 | var checkElement = function(selector, cb) { 55 | self.api.element.call(self, self.client.locateStrategy, selector, function(result) { 56 | var value; 57 | 58 | if (result.status === 0) { 59 | value = result.value.ELEMENT; 60 | } 61 | 62 | if (value) { 63 | found.push(selector); 64 | } else { 65 | missing.push(selector); 66 | } 67 | 68 | cb(); 69 | }); 70 | }; 71 | 72 | var returnResults = function(err) { 73 | var result = missing.length; 74 | var msg, passed; 75 | 76 | if (result === 0) { 77 | msg = util.format('Page contained %s expected element%s.', found.length, found.length > 1 ? 's' : ''); 78 | passed = true; 79 | } else { 80 | var missingMsg = missing.map(function(el) { 81 | return '<' + el + '>'; 82 | }); 83 | msg = util.format('Page missing the following elements: %s.', missingMsg.join(', ')); 84 | passed = false; 85 | } 86 | 87 | self.client.assertion(passed, result, 0, msg, false); 88 | self.cb(result); 89 | self.emit('complete'); 90 | }; 91 | 92 | async.each(selectors, checkElement, returnResults); 93 | }; 94 | 95 | module.exports = Assertion; 96 | -------------------------------------------------------------------------------- /assertions/elementsVisible.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if the given selectors are visible and returns a list of selectors that are not visible on the page. 3 | * 4 | * ``` 5 | * this.demoTest = function (client) { 6 | * browser.assert.elementsVisible('#x-root', '#x-head'); 7 | * }; 8 | * ``` 9 | * 10 | * @method attributeEquals 11 | * @param {string} selectors The selectors (CSS / Xpath) used to locate the elements, separated by commas. 12 | * @param {string} expected The expected value of the attribute to check. 13 | * @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default. 14 | * @api assertions 15 | */ 16 | 17 | var async = require('async'), 18 | util = require('util'), 19 | events = require('events'); 20 | 21 | var Assertion = function() { 22 | events.EventEmitter.call(this); 23 | this.cb = null; 24 | this.abortOnFailure = true; 25 | this.selector = null; 26 | }; 27 | 28 | util.inherits(Assertion, events.EventEmitter); 29 | 30 | Assertion.prototype.command = function(selectors, callback) { 31 | var args = Array.prototype.slice.call(arguments, 0); 32 | var lastArgument = args[args.length - 1]; 33 | 34 | if (typeof (lastArgument) === 'function') { 35 | callback = args.pop(); 36 | } else { 37 | callback = function() {}; 38 | } 39 | 40 | this.cb = callback; 41 | this.selectors = args.slice(0); 42 | this.checkElements(); 43 | 44 | return this; 45 | }; 46 | 47 | Assertion.prototype.checkElements = function() { 48 | var self = this; 49 | var missing = []; 50 | var found = []; 51 | var selectors = this.selectors; 52 | 53 | var checkElement = function(selector, cb) { 54 | self.api.isVisible.call(self, selector, function(result) { 55 | var value; 56 | 57 | if (result.status === 0) { 58 | value = result.value; 59 | } 60 | 61 | if (value) { 62 | found.push(selector); 63 | } else { 64 | missing.push(selector); 65 | } 66 | 67 | cb(); 68 | }); 69 | }; 70 | 71 | var returnResults = function(err) { 72 | var result = missing.length; 73 | var msg, passed; 74 | 75 | if (result === 0) { 76 | msg = util.format('Page contained %s visible element%s.', found.length, found.length > 1 ? 's' : ''); 77 | passed = true; 78 | } else { 79 | var missingMsg = missing.map(function(el) { 80 | return '<' + el + '>'; 81 | }); 82 | msg = util.format('The following elements were not visible on the page: %s', missingMsg.join(', ')); 83 | passed = false; 84 | } 85 | 86 | self.client.assertion(passed, result, 0, msg, false); 87 | self.cb(result); 88 | self.emit('complete'); 89 | }; 90 | 91 | async.each(selectors, checkElement, returnResults); 92 | }; 93 | 94 | module.exports = Assertion; 95 | -------------------------------------------------------------------------------- /assertions/templateName.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if the given template name is correct. 3 | * 4 | * ``` 5 | * this.demoTest = function (client) { 6 | * browser.assert.templateName('home'); 7 | * }; 8 | * ``` 9 | * 10 | * @method attributeEquals 11 | * @param {string} selector The selector (CSS / Xpath) used to locate the element. 12 | * @param {string} attribute The attribute name 13 | * @param {string} expected The expected value of the attribute to check. 14 | * @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default. 15 | * @api assertions 16 | */ 17 | 18 | var util = require('util'), 19 | events = require('events'); 20 | 21 | var Assertion = function() { 22 | events.EventEmitter.call(this); 23 | this.cb = null; 24 | this.abortOnFailure = true; 25 | this.expected = null; 26 | }; 27 | 28 | util.inherits(Assertion, events.EventEmitter); 29 | 30 | Assertion.prototype.command = function(expected, msg) { 31 | this.expected = expected; 32 | this.checkTemplateName(); 33 | this.msg = msg; 34 | 35 | return this; 36 | }; 37 | 38 | Assertion.prototype.checkTemplateName = function() { 39 | var self = this; 40 | var expected = this.expected; 41 | var msg = this.msg; 42 | 43 | this.api.getMobifyEvaluatedData(function(result) { 44 | if (result) { 45 | var actual = result.templateName || result.content.templateName; 46 | var passed = actual === expected; 47 | msg = msg || ('Testing if the actual page template <' + actual + '> equals the expected <' + expected + '>.'); 48 | self.client.assertion(passed, result, expected, msg, self.abortOnFailure); 49 | self.emit('complete'); 50 | } else { 51 | self.checkTemplateName(); 52 | } 53 | }); 54 | }; 55 | 56 | module.exports = Assertion; 57 | -------------------------------------------------------------------------------- /commands/clickAndWaitUntilMobified.js: -------------------------------------------------------------------------------- 1 | exports.command = function(selector, callback) { 2 | var client = this; 3 | 4 | // Clicks a link, navigates to the URL and then waits for the page 5 | // to be mobified before continuing the chain. 6 | // 7 | client 8 | .click(selector) 9 | .waitUntilMobified(8000, function(result) { 10 | if (typeof callback === 'function') { 11 | callback.call(client, result); 12 | } 13 | }); 14 | 15 | return this; 16 | }; 17 | -------------------------------------------------------------------------------- /commands/get.js: -------------------------------------------------------------------------------- 1 | // Uses the browser.execute to run code within the client browser, 2 | // access the Mobify object and test the template. 3 | 4 | exports.command = function(url, callback) { 5 | var client = this; 6 | 7 | return client.url(url) 8 | .waitUntilMobified(10000, function(result) { 9 | if (typeof callback === 'function') { 10 | callback.call(client, result); 11 | } 12 | }); 13 | 14 | }; 15 | -------------------------------------------------------------------------------- /commands/getMobifyEvaluatedData.js: -------------------------------------------------------------------------------- 1 | exports.command = function(callback) { 2 | var client = this; 3 | 4 | // Uses waitForCondition to retrieve the Mobify.evaluatedData 5 | // from the client browser 6 | // 7 | return client.waitForCondition(function() { 8 | var framework = Mobify || Adaptive; 9 | return framework ? framework.evaluatedData : null; 10 | }, 8000, function(result) { 11 | if (typeof callback === 'function') { 12 | callback.call(client, result); 13 | } 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /commands/htmlCapture.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs'); 3 | var mkdirp = require('mkdirp'); 4 | 5 | var mkdirpSync = function(path) { 6 | try { 7 | mkdirp.sync(path); 8 | } catch (e) { 9 | if ( e.code !== 'EEXIST' ) throw e; 10 | } 11 | }; 12 | 13 | exports.command = function(fileName, callback) { 14 | if (fileName && typeof fileName !== 'string') { 15 | throw new Error('htmlCapture expects first parameter to be string; ' + typeof (fileName) + ' given'); 16 | } 17 | 18 | var htmlCheck = /^\w+\.html$/; 19 | 20 | if (!htmlCheck.test(fileName)) { 21 | throw new Error('htmlCapture expects first parameter to be camelCased alphanumeric string ending with ".html"'); 22 | } 23 | 24 | var client = this; 25 | var filePath = 'tests/fixtures/'; 26 | var fileLocation = filePath + fileName; 27 | var messages = { 28 | success: 'Wrote HTML fixture to ' + fileLocation + ' after ', 29 | failure: 'Failed to write HTML fixture after ' 30 | }; 31 | 32 | client.source(function(result) { 33 | // Source will be stored in result.value. IE: 34 | // console.log(result.value); 35 | 36 | // If it doesn't exist, create a path to "tests/fixtures/". 37 | mkdirpSync(filePath); 38 | 39 | // Now save result.value to "../../fixtures/contact.html" in order to update the fixture. 40 | fs.writeFile(fileLocation, result.value, function(error) { 41 | if (error) { 42 | console.error('Write error: ' + error.message); 43 | } else { 44 | console.log('Successful write to ' + fileLocation); 45 | } 46 | }); 47 | }); 48 | }; 49 | -------------------------------------------------------------------------------- /commands/log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Logs a message to the console, allowing for adding messages to test output 3 | * 4 | * ``` 5 | * this.demoTest = function (browser) { 6 | * browser 7 | * .log('Testing submitting form') 8 | * .click('#weirdSelectorThatMakesItNotClearWeAreSubmitting'); 9 | * }; 10 | * ``` 11 | * 12 | * @method log 13 | * @param {string} message The message to log to the console. 14 | * @param {function} [callback] Optional callback function to be called when the command finishes. 15 | * @api commands 16 | */ 17 | var chalk = require('chalk'); 18 | var infoSymbol = String.fromCharCode('9432'); 19 | 20 | var log = function(message, callback) { 21 | var browser = this; 22 | 23 | browser.perform(function() { 24 | console.log(chalk.blue.bold(infoSymbol) + ' ' + message); 25 | }); 26 | 27 | if (typeof callback === 'function') { 28 | callback.call(this); 29 | } 30 | 31 | return this; 32 | }; 33 | 34 | exports.command = log; 35 | -------------------------------------------------------------------------------- /commands/preview.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Preview will use preview.mobify.com to open a website and allow you to preview 3 | * a given bundle. The site URL and bundle URL should be passed in. Upon completion, waitUntilMobified is called, to be sure that the 4 | * adaptation is complete. 5 | * 6 | * Usage: 7 | * ``` 8 | * this.demoTest = function (client) { 9 | * browser.preview('https://www.merlinspotions.com', 'https://localhost:8443/loader.js'); 10 | * }; 11 | * ``` 12 | * 13 | * @method preview 14 | * @param {string} [url] Corresponds to the Site URL field on https://preview.mobify.com 15 | * @param {string} [bundle] Corresponds to the Bundle Location field on https://preview.mobify.com 16 | * @param {function} [callback] Optional callback function to be called when the command finishes. 17 | * @api commands 18 | */ 19 | 20 | var qs = require('querystring'); 21 | 22 | exports.command = function(url, bundle, callback) { 23 | var browser = this; 24 | 25 | if (arguments.length < 2) { 26 | throw new Error('Usage: browser.preview(url, bundle, callback)'); 27 | } 28 | 29 | if (typeof bundle === 'function') { 30 | callback = bundle; 31 | bundle = null; 32 | } 33 | 34 | var bundleUrl = bundle || 'https://localhost:8443/loader.js'; 35 | 36 | var params = qs.stringify({'url': url, 'site_folder': bundleUrl}); 37 | 38 | return browser.url('https://preview.mobify.com?' + params) 39 | .waitForElementPresent('#authorize', 10000, function() { 40 | this.click('#authorize', function() { 41 | browser.waitUntilMobified(10000, function(result) { 42 | if (typeof callback === 'function') { 43 | callback.call(browser, result); 44 | } 45 | }); 46 | }); 47 | }); 48 | }; 49 | -------------------------------------------------------------------------------- /commands/trigger.js: -------------------------------------------------------------------------------- 1 | exports.command = function(selector, type, callback) { 2 | var client = this; 3 | 4 | var lastArgument = Array.prototype.slice.call(arguments, 0).pop(); 5 | if (typeof (lastArgument) === 'function') { 6 | callback = lastArgument; 7 | } 8 | 9 | client.execute(function(sel, t) { 10 | var event = new MouseEvent(t || 'click', { 11 | 'view': window, 12 | 'bubbles': true, 13 | 'cancelable': true 14 | }); 15 | var el = document.querySelector(sel); 16 | el.dispatchEvent(event); 17 | return true; 18 | }, [selector, type], function(result) { 19 | if (typeof callback === 'function') { 20 | callback.call(client, result.value); 21 | } 22 | }); 23 | 24 | return this; 25 | }; 26 | -------------------------------------------------------------------------------- /commands/triggerClick.js: -------------------------------------------------------------------------------- 1 | exports.command = function(selector, callback) { 2 | var client = this; 3 | /* 4 | / Use Javascript's click when Selenium's does not register. 5 | */ 6 | client.execute('document.querySelector("' + selector + '").click();', function(result) { 7 | if (typeof callback === 'function') { 8 | callback.call(client, result); 9 | } 10 | }); 11 | 12 | return this; 13 | }; 14 | -------------------------------------------------------------------------------- /commands/triggerTouch.js: -------------------------------------------------------------------------------- 1 | exports.command = function(selector, type, callback) { 2 | var client = this; 3 | 4 | // Selenium's click doesn't always register 5 | // We should expand this to bind and trigger more events 6 | // 7 | client.execute(function(sel, t) { 8 | var event = new MouseEvent(t || 'click', { 9 | 'view': window, 10 | 'bubbles': true, 11 | 'cancelable': true 12 | }); 13 | var el = document.querySelector(sel); 14 | el.dispatchEvent(event); 15 | return true; 16 | }, [selector, type], function(result) { 17 | if (typeof callback === 'function') { 18 | callback.call(client, result); 19 | } 20 | }); 21 | 22 | return this; 23 | }; 24 | -------------------------------------------------------------------------------- /commands/waitForAjaxCompleted.js: -------------------------------------------------------------------------------- 1 | exports.command = function(callback) { 2 | var client = this; 3 | 4 | // Uses waitForCondition to run code within the client browser 5 | // 6 | return client.waitForCondition('return jQuery.active;', 8000, function(result) { 7 | if (typeof callback === 'function') { 8 | callback.call(client, result); 9 | } 10 | }); 11 | 12 | }; 13 | -------------------------------------------------------------------------------- /commands/waitForAnimation.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var events = require('events'); 3 | 4 | /** 5 | * Suspends the test for the given time in milliseconds while waiting for animation to complete. 6 | * 7 | * ``` 8 | * this.demoTest = function (browser) { 9 | * browser.waitForAnimation(); 10 | * }; 11 | * ``` 12 | * 13 | * @method waitForAnimation 14 | * @param {number} ms The number of milliseconds to wait. 15 | * @param {function} [callback] Optional callback function to be called when the command finishes. 16 | * @api commands 17 | */ 18 | 19 | var WaitForAnimation = function() { 20 | events.EventEmitter.call(this); 21 | }; 22 | 23 | util.inherits(WaitForAnimation, events.EventEmitter); 24 | 25 | WaitForAnimation.prototype.command = function(ms, cb) { 26 | var self = this; 27 | 28 | ms = ms || 1000; 29 | 30 | setTimeout(function() { 31 | // if we have a callback, call it right before the complete event 32 | if (cb) { 33 | cb.call(self.client.api); 34 | } 35 | 36 | self.emit('complete'); 37 | }, ms); 38 | 39 | return this; 40 | }; 41 | 42 | module.exports = WaitForAnimation; 43 | -------------------------------------------------------------------------------- /commands/waitForCondition.js: -------------------------------------------------------------------------------- 1 | var util = require('util'), 2 | events = require('events'); 3 | 4 | var CommandAction = function() { 5 | events.EventEmitter.call(this); 6 | this.startTimer = null; 7 | this.cb = null; 8 | this.ms = null; 9 | this.selector = null; 10 | this.protocol = require('nightwatch/lib/api/protocol.js')(this.client); 11 | }; 12 | 13 | util.inherits(CommandAction, events.EventEmitter); 14 | 15 | CommandAction.prototype.command = function(condition, milliseconds, timeout, messages, callback) { 16 | 17 | if (milliseconds && typeof milliseconds !== 'number') { 18 | throw new Error('waitForCondition expects second parameter to be number; ' + typeof (milliseconds) + ' given'); 19 | } 20 | 21 | var lastArgument = Array.prototype.slice.call(arguments, 0).pop(); 22 | if (typeof (lastArgument) === 'function') { 23 | callback = lastArgument; 24 | } 25 | 26 | if (!messages || typeof messages !== 'object') { 27 | messages = { 28 | success: 'Condition was satisfied after ', 29 | timeout: 'Timed out while waiting for condition after ' 30 | }; 31 | } 32 | 33 | timeout = timeout && typeof (timeout) !== 'function' && typeof (timeout) !== 'object' ? timeout : 0; 34 | 35 | this.startTimer = new Date().getTime(); 36 | this.cb = callback || function() { 37 | }; 38 | this.ms = milliseconds || 1000; 39 | this.timeout = timeout; 40 | this.condition = condition; 41 | this.messages = messages; 42 | this.check(); 43 | return this; 44 | }; 45 | 46 | CommandAction.prototype.check = function() { 47 | var self = this; 48 | 49 | this.protocol.execute.call(this.client, this.condition, function(result) { 50 | var now = new Date().getTime(); 51 | 52 | if (result.status === 0 && result.value !== 'undefined') { 53 | setTimeout(function() { 54 | var msg = self.messages.success + (now - self.startTimer) + ' milliseconds.'; 55 | self.cb.call(self.client.api, result.value); 56 | self.client.assertion(true, !!result.value, false, msg, true); 57 | return self.emit('complete'); 58 | }, self.timeout); 59 | } else if (now - self.startTimer < self.ms) { 60 | setTimeout(function() { 61 | self.check(); 62 | }, 500); 63 | } else { 64 | var msg = self.messages.timeout + self.ms + ' milliseconds.'; 65 | self.cb.call(self.client.api, false); 66 | self.client.assertion(false, false, false, msg, true); 67 | return self.emit('complete'); 68 | } 69 | }); 70 | }; 71 | 72 | module.exports = CommandAction; 73 | -------------------------------------------------------------------------------- /commands/waitForContextsReady.js: -------------------------------------------------------------------------------- 1 | var util = require('util'), 2 | events = require('events'); 3 | 4 | var CommandAction = function() { 5 | events.EventEmitter.call(this); 6 | this.startTimer = null; 7 | this.cb = null; 8 | this.ms = null; 9 | this.selector = null; 10 | this.protocol = require('nightwatch/lib/api/protocol.js')(this.client); 11 | }; 12 | 13 | util.inherits(CommandAction, events.EventEmitter); 14 | 15 | CommandAction.prototype.command = function(expectedContextsCount, milliseconds, timeout, messages, callback) { 16 | 17 | if (milliseconds && typeof milliseconds !== 'number') { 18 | throw new Error('waitForContextsReady expects second parameter to be number; ' + typeof (milliseconds) + ' given'); 19 | } 20 | 21 | var lastArgument = Array.prototype.slice.call(arguments, 0).pop(); 22 | if (typeof (lastArgument) === 'function') { 23 | callback = lastArgument; 24 | } 25 | 26 | if (!messages || typeof messages !== 'object') { 27 | messages = { 28 | success: 'All contexts found after ', 29 | timeout: 'Timed out while waiting for contexts after ' 30 | }; 31 | } 32 | 33 | timeout = timeout && typeof (timeout) !== 'function' && typeof (timeout) !== 'object' ? timeout : 0; 34 | 35 | this.startTimer = new Date().getTime(); 36 | this.cb = callback || function() {}; 37 | this.ms = milliseconds || 1000; 38 | this.timeout = timeout; 39 | this.messages = messages; 40 | this.check(expectedContextsCount); 41 | return this; 42 | }; 43 | 44 | CommandAction.prototype.check = function(expectedContextsCount) { 45 | var self = this; 46 | 47 | this.protocol.contexts(function(result) { 48 | var now = new Date().getTime(); 49 | var contextsCount = result.value ? result.value.length : -1; 50 | 51 | if (expectedContextsCount === contextsCount) { 52 | setTimeout(function() { 53 | var msg = self.messages.success + (now - self.startTimer) + ' milliseconds.'; 54 | self.cb.call(self.client.api, result.value); 55 | self.client.assertion(true, !!result.value, false, msg, true); 56 | return self.emit('complete'); 57 | }, self.timeout); 58 | } else if (now - self.startTimer < self.ms) { 59 | setTimeout(function() { 60 | self.check(expectedContextsCount); 61 | }, 500); 62 | } else { 63 | var msg = self.messages.timeout + self.ms + ' milliseconds.'; 64 | self.cb.call(self.client.api, false); 65 | self.client.assertion(false, false, false, msg, true); 66 | return self.emit('complete'); 67 | } 68 | }); 69 | }; 70 | 71 | module.exports = CommandAction; 72 | -------------------------------------------------------------------------------- /commands/waitForUrl.js: -------------------------------------------------------------------------------- 1 | var util = require('util'), 2 | events = require('events'); 3 | 4 | var WaitForUrl = function() { 5 | events.EventEmitter.call(this); 6 | this.startTimer = null; 7 | this.cb = null; 8 | this.ms = null; 9 | this.selector = null; 10 | this.protocol = require('nightwatch/lib/api/protocol.js')(this.client); 11 | }; 12 | 13 | util.inherits(WaitForUrl, events.EventEmitter); 14 | 15 | /** 16 | * Waiting for url expected 17 | * @param {[type]} url [url expected] 18 | * @param {[type]} milliseconds [time for close assert] 19 | * @param {[type]} timeout [time verify] 20 | * @param {[type]} messages [message output] 21 | * @param {Function} callback [callback] 22 | * @return {[type]} [client] 23 | */ 24 | WaitForUrl.prototype.command = function(url, milliseconds, timeout, messages, callback) { 25 | 26 | if (milliseconds && typeof milliseconds !== 'number') { 27 | throw new Error('waitForCondition expects second parameter to be number; ' + typeof (milliseconds) + ' given'); 28 | } 29 | 30 | var lastArgument = Array.prototype.slice.call(arguments, 0).pop(); 31 | if (typeof (lastArgument) === 'function') { 32 | callback = lastArgument; 33 | } 34 | 35 | if (!messages || typeof messages !== 'object') { 36 | messages = { 37 | success: 'Url expected after ', 38 | timeout: 'Timed out while waiting for url after ' 39 | }; 40 | } 41 | 42 | timeout = timeout && typeof (timeout) !== 'function' && typeof (timeout) !== 'object' ? timeout : 0; 43 | 44 | this.startTimer = new Date().getTime(); 45 | this.cb = callback || function() { 46 | }; 47 | this.ms = milliseconds || 1000; 48 | this.timeout = timeout; 49 | this.url = url; 50 | this.messages = messages; 51 | this.check(); 52 | return this; 53 | }; 54 | 55 | WaitForUrl.prototype.check = function() { 56 | var self = this; 57 | 58 | this.protocol.url(function(result) { 59 | var now = new Date().getTime(); 60 | 61 | if (result.status === 0 && result.value === self.url) { 62 | setTimeout(function() { 63 | var msg = self.messages.success + (now - self.startTimer) + ' milliseconds.'; 64 | self.cb.call(self.client.api, result.value); 65 | self.client.assertion(true, !!result.value, false, msg, true); 66 | return self.emit('complete'); 67 | }, self.timeout); 68 | } else if (now - self.startTimer < self.ms) { 69 | setTimeout(function() { 70 | self.check(); 71 | }, 500); 72 | } else { 73 | var msg = self.messages.timeout + self.ms + ' milliseconds.'; 74 | self.cb.call(self.client.api, false); 75 | self.client.assertion(false, false, false, msg, true); 76 | return self.emit('complete'); 77 | } 78 | }); 79 | }; 80 | 81 | module.exports = WaitForUrl; 82 | -------------------------------------------------------------------------------- /commands/waitForUrlToContain.js: -------------------------------------------------------------------------------- 1 | var util = require('util'), 2 | events = require('events'); 3 | 4 | var WaitForUrlToContain = function() { 5 | events.EventEmitter.call(this); 6 | this.startTimer = null; 7 | this.cb = null; 8 | this.ms = null; 9 | this.selector = null; 10 | this.protocol = require('nightwatch/lib/api/protocol.js')(this.client); 11 | }; 12 | 13 | util.inherits(WaitForUrlToContain, events.EventEmitter); 14 | 15 | /** 16 | * Waiting for url expected 17 | * @param {string} url [url expected to contain] 18 | * @param {number} milliseconds [total time until command times out] 19 | * @param {number} timeout [time to wait before command starts polling the URL] 20 | * @param {Object} messages [message output] 21 | * @param {Function} callback [callback] 22 | * @return {[type]} [client] 23 | */ 24 | WaitForUrlToContain.prototype.command = function(url, milliseconds, timeout, messages, callback) { 25 | 26 | if (milliseconds && typeof milliseconds !== 'number') { 27 | throw new Error('waitForCondition expects second parameter to be number; ' + typeof (milliseconds) + ' given'); 28 | } 29 | 30 | var lastArgument = Array.prototype.slice.call(arguments, 0).pop(); 31 | if (typeof (lastArgument) === 'function') { 32 | callback = lastArgument; 33 | } 34 | 35 | if (!messages || typeof messages !== 'object') { 36 | messages = { 37 | success: 'Url expected after ', 38 | timeout: 'Timed out while waiting for url after ' 39 | }; 40 | } 41 | 42 | timeout = timeout && typeof (timeout) !== 'function' && typeof (timeout) !== 'object' ? timeout : 0; 43 | 44 | this.startTimer = new Date().getTime(); 45 | this.cb = callback || function() { 46 | }; 47 | this.ms = milliseconds || 10000; 48 | this.timeout = timeout; 49 | this.url = url; 50 | this.messages = messages; 51 | this.check(); 52 | return this; 53 | }; 54 | 55 | WaitForUrlToContain.prototype.check = function() { 56 | var self = this; 57 | 58 | this.protocol.url(function(result) { 59 | var now = new Date().getTime(); 60 | 61 | if (result.status === 0 && (result.value.indexOf(self.url) > -1)) { 62 | setTimeout(function() { 63 | var msg = self.messages.success + (now - self.startTimer) + ' milliseconds.'; 64 | self.cb.call(self.client.api, result.value); 65 | self.client.assertion(true, !!result.value, false, msg, true); 66 | return self.emit('complete'); 67 | }, self.timeout); 68 | } else if (now - self.startTimer < self.ms) { 69 | setTimeout(function() { 70 | self.check(); 71 | }, 500); 72 | } else { 73 | var msg = self.messages.timeout + self.ms + ' milliseconds.'; 74 | self.cb.call(self.client.api, false); 75 | self.client.assertion(false, false, false, msg, true); 76 | return self.emit('complete'); 77 | } 78 | }); 79 | }; 80 | 81 | module.exports = WaitForUrlToContain; 82 | -------------------------------------------------------------------------------- /commands/waitUntilMobified.js: -------------------------------------------------------------------------------- 1 | exports.command = function(milliseconds, callback) { 2 | if (milliseconds && typeof milliseconds !== 'number') { 3 | throw new Error('waitUntilMobified expects first parameter to be number; ' + typeof (milliseconds) + ' given'); 4 | } 5 | 6 | var client = this; 7 | var messages = { 8 | success: 'Page was Mobified after ', 9 | failure: 'Timed out after ' 10 | }; 11 | 12 | /** Uses waitForCondition to check for either the Mobify or 13 | * Adaptive object in the client browser 14 | */ 15 | client.waitForCondition(function() { 16 | var framework = Mobify || Adaptive; 17 | return !!framework; 18 | }, milliseconds, 2000, messages, function(result) { 19 | if (typeof callback === 'function') { 20 | callback.call(client, result); 21 | } 22 | }); 23 | 24 | return this; 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nightwatch-commands", 3 | "version": "3.0.0", 4 | "description": "A set of Mobify specific custom commands for Nightwatch.js", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/mobify/nightwatch-commands" 8 | }, 9 | "author": "Mobify", 10 | "bugs": { 11 | "url": "https://github.com/mobify/nightwatch-commands/issues" 12 | }, 13 | "devDependencies": { 14 | "nightwatch": "0.9.8", 15 | "nodeunit": "0.8.4", 16 | "grunt": "0.4.5", 17 | "grunt-cli": "0.1.13", 18 | "grunt-eslint": "17.0.0", 19 | "mobify-code-style": "2.6.0" 20 | }, 21 | "scripts": { 22 | "install": "node selenium/install.js", 23 | "test": "./node_modules/.bin/grunt lint; ./node_modules/.bin/grunt test" 24 | }, 25 | "homepage": "https://github.com/mobify/nightwatch-commands", 26 | "dependencies": { 27 | "async": "0.2.10", 28 | "chalk": "0.4.0", 29 | "mkdirp": "0.5.1", 30 | "selenium-download": "2.0.10" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /selenium/install.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var mkdirp = require('mkdirp'); 3 | var path = require('path'); 4 | var selenium = require('selenium-download'); 5 | 6 | // Download Selenium server and Chromedriver 7 | selenium.ensure(__dirname, function(error) { 8 | if (error) { 9 | console.error(error.stack); 10 | } else { 11 | // Rename so that we don't need to update paths in project configs 12 | var oldSeleniumPath = path.join(__dirname, 'selenium.jar'); 13 | var newSeleniumPath = path.join(__dirname, 'selenium-server.jar'); 14 | 15 | fs.rename(oldSeleniumPath, newSeleniumPath, function(error) { 16 | if (error) console.log(error); 17 | return; 18 | }); 19 | 20 | mkdirp(path.join(__dirname, 'drivers')); 21 | var oldChromedriverPath = path.join(__dirname, 'chromedriver'); 22 | var newChromedriverPath = path.join(__dirname, 'drivers', 'chromedriver'); 23 | 24 | fs.rename(oldChromedriverPath, newChromedriverPath, function(error) { 25 | if (error) console.log(error); 26 | return; 27 | }); 28 | } 29 | }); 30 | -------------------------------------------------------------------------------- /tests/mocks.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 10195, 3 | "mocks": [{ 4 | "url": "/wd/hub/session", 5 | "postdata": "", 6 | "response": "{\"status\": 0, \"sessionId\": \"1352110219202\", \"value\": { \"javascriptEnabled\": true, \"browserName\": \"firefox\", \"version\": \"TEST\", \"platform\": \"TEST\"}, \"state\": null}", 7 | "responseHeaders": {}, 8 | "statusCode": 201, 9 | "method": "POST" 10 | }, { 11 | "url" : "/wd/hub/session/1352110219202", 12 | "postdata" : "", 13 | "response" : "{\"status\":0,\"value\":true}" 14 | }, { 15 | "url" : "/wd/hub/session/1352110219202", 16 | "postdata" : "", 17 | "response" : "{\"status\":0,\"value\":false}" 18 | }, { 19 | "url" : "/wd/hub/session/1352110219202", 20 | "method": "DELETE", 21 | "postdata" : "", 22 | "response" : "{\"status\":0,\"value\":false}" 23 | }, { 24 | "url" : "/wd/hub/session/1352110219202/url", 25 | "method": "GET", 26 | "postdata" : "" 27 | }, { 28 | "url" : "/wd/hub/session/1352110219202/url", 29 | "method": "POST", 30 | "postdata" : "{\"url\":\"http://localhost\"}" 31 | }, { 32 | "url" : "/wd/hub/session/1352110219202/url", 33 | "method": "GET", 34 | "postdata" : "", 35 | "response" : "{\"value\":\"http://localhost/\",\"status\":0}" 36 | }, { 37 | "url" : "/wd/hub/session/null/url", 38 | "method": "GET", 39 | "postdata" : "" 40 | }, { 41 | "url" : "/wd/hub/session/1352110219202/execute", 42 | "postdata" : "{\"script\":\"return true;\",\"args\":[]}", 43 | "response" : "{\"status\":0,\"value\":true}" 44 | }, { 45 | "url" : "/wd/hub/session/1352110219202/execute", 46 | "postdata" : "{\"script\":\"return false\",\"args\":[]}", 47 | "response" : "{\"status\":0,\"value\":false}" 48 | }, { 49 | "url" : "/wd/hub/session/1352110219202/execute", 50 | "postdata" : "{\"script\":\"return Mobify.evaluatedData;\",\"args\":[]}", 51 | "response" : "{\"status\":0,\"value\":true}" 52 | }, { 53 | "url" : "/wd/hub/session/null/execute", 54 | "method": "POST", 55 | "postdata": "{\"script\":\"var passedArgs = Array.prototype.slice.call(arguments,0); return function () {\\n var framework = Mobify || Adaptive;\\n return !!framework;\\n }.apply(window, passedArgs);\",\"args\":[]}", 56 | "response" : "{\"status\":0,\"value\":true}" 57 | }, { 58 | "url" : "/wd/hub/session/1352110219202/execute", 59 | "method": "POST", 60 | "postdata": "{\"script\":\"var passedArgs = Array.prototype.slice.call(arguments,0); return function () {\\n var framework = Mobify || Adaptive;\\n return !!framework;\\n }.apply(window, passedArgs);\",\"args\":[]}", 61 | "response" : "{\"status\":0,\"value\":true}" 62 | }, { 63 | "url" : "/wd/hub/session/1352110219202/execute", 64 | "method": "POST", 65 | "postdata": "{\"script\":\"var passedArgs = Array.prototype.slice.call(arguments,0); return function (){ var framework = Mobify || Adaptive; return !!framework; }.apply(window, passedArgs);\",\"args\":[]}", 66 | "response" : "{\"status\":0,\"value\":true}" 67 | }, { 68 | "url" : "/wd/hub/session/1352110219202/execute", 69 | "postdata": "{\"script\":\"var passedArgs = Array.prototype.slice.call(arguments,0); return function () {\\n var framework = Mobify || Adaptive;\\n return framework ? framework.evaluatedData : null;\\n }.apply(window, passedArgs);\",\"args\":[]}", 70 | "response" : "{\"status\":0,\"value\":true}" 71 | }, { 72 | "url" : "/wd/hub/session/1352110219202/execute", 73 | "postdata" : "{\"script\":\"var passedArgs = Array.prototype.slice.call(arguments,0); return function (sel, t) {\\n var event = new MouseEvent(t || 'click', {\\n 'view': window,\\n 'bubbles': true,\\n 'cancelable': true\\n });\\n var el = document.querySelector(sel);\\n el.dispatchEvent(event);\\n return true;\\n }.apply(window, passedArgs);\",\"args\":[\".clickMe\",\"click\"]}", 74 | "response" : "{\"status\":0,\"value\":true}" 75 | }, { 76 | "url" : "/wd/hub/session/1352110219202/execute", 77 | "postdata" : "{\"script\":\"var passedArgs = Array.prototype.slice.call(arguments,0); return function (sel, t) {\\n var event = new MouseEvent(t || 'click', {\\n 'view': window,\\n 'bubbles': true,\\n 'cancelable': true\\n });\\n var el = document.querySelector(sel);\\n el.dispatchEvent(event);\\n return true;\\n }.apply(window, passedArgs);\",\"args\":[\".clickMe\",null]}", 78 | "response" : "{\"status\":0,\"value\":true}" 79 | }, { 80 | "url" : "/wd/hub/session/1352110219202/execute", 81 | "postdata" : "{\"script\":\"return $.active;\",\"args\":[]}", 82 | "response" : "{\"status\":0,\"value\":0}" 83 | }, { 84 | "url" : "/wd/hub/session/1352110219202/contexts", 85 | "method": "GET", 86 | "postdata" : "", 87 | "response" : "{\"status\":0,\"value\":[\"NATIVE\", \"WEBVIEW_1\", \"WEBVIEW_2\"]}" 88 | }] 89 | } 90 | -------------------------------------------------------------------------------- /tests/nightwatch.js: -------------------------------------------------------------------------------- 1 | var nightwatch = require('../node_modules/nightwatch'); 2 | 3 | module.exports = { 4 | init: function(options, callback) { 5 | var opts = { 6 | seleniumPort: 10195, 7 | silent: true, 8 | output: false, 9 | globals: { 10 | myGlobal: 'test' 11 | }, 12 | desiredCapabilities: { 13 | browserName: 'chrome' 14 | }, 15 | /* eslint-disable camelcase */ 16 | custom_commands_path: '../commands', 17 | custom_assertions_path: '../assertions' 18 | /* eslint-enable camelcase */ 19 | }; 20 | 21 | if (options) { 22 | for (var prop in options) { 23 | if (options.hasOwnProperty(prop)) { 24 | opts[prop] = options[prop]; 25 | } 26 | } 27 | } 28 | 29 | return nightwatch.client(opts).on('selenium:session_create', function() { 30 | if (callback) { 31 | callback(); 32 | } 33 | }).once('error', function() { 34 | if (callback) { 35 | callback(); 36 | } 37 | process.exit(); 38 | }).start(); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /tests/node_modules/mockserver.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var mocks = null; 3 | 4 | function mockServer(options, callback) { 5 | // The default options: 6 | var defaultOptions = { 7 | postdata: '', 8 | weakURLVerification: false, 9 | responseHeaders: {}, 10 | responseType: 'application/json' 11 | }; 12 | 13 | callback = callback || function() {}; 14 | 15 | for (var k in defaultOptions) { 16 | options[k] = (options[k] || defaultOptions[k]); 17 | } 18 | 19 | if (Array.isArray(options.mocks)) { 20 | mocks = options.mocks; 21 | } 22 | // Prepare the next request: 23 | function nextInLine(req, postdata) { 24 | var mockFound = false; 25 | 26 | mocks.forEach(function(item) { 27 | item.postdata = item.postdata || ''; 28 | item.method = item.method || 'POST'; 29 | item.statusCode = item.statusCode || 200; 30 | 31 | if ((item.url == req.url) && (item.method == req.method) && (item.postdata == postdata || !item.postdata)) { 32 | item.responseHeaders = item.responseHeaders || {}; 33 | item.response = item.response || ''; 34 | mockFound = item; 35 | } 36 | }); 37 | return mockFound; 38 | } 39 | 40 | var s = http.createServer(function(req, res) { 41 | var postdata = ''; 42 | var headers; 43 | req.setEncoding('utf8'); 44 | req.on('data', function(chunk) { 45 | postdata += chunk; 46 | }); 47 | req.on('end', function() { 48 | var item = nextInLine(req, postdata); 49 | var responsedata = ''; 50 | if (item) { 51 | headers = item.responseHeaders; 52 | headers['Content-Type'] = options.responseType; 53 | headers['Content-Length'] = item.response.length; 54 | res.writeHead(Number(item.statusCode), headers); 55 | responsedata = item.response; 56 | } else { 57 | // Keep this for debugging 58 | // console.log('MOCK NOT FOUND'); 59 | // console.log('REQUEST URL: ', req.url); 60 | // console.log('REQUEST METHOD: ', req.method); 61 | // console.log('REQUEST POST DATA: ', postdata); 62 | headers = {}; 63 | headers['Content-Type'] = options.responseType; 64 | headers['Content-Length'] = 0; 65 | res.writeHead(200, 'OK', headers); 66 | } 67 | 68 | if (typeof options.finishedCallback == 'function') { 69 | options.finishedCallback(req, res); 70 | } 71 | 72 | res.end(responsedata); 73 | }); 74 | }); 75 | 76 | s.listen(options.port, callback); 77 | 78 | return s; 79 | } 80 | 81 | module.exports = { 82 | init: function(callback) { 83 | var i; 84 | var mockoptions = require('../mocks.json'); 85 | try { 86 | i = new mockServer(mockoptions, callback); 87 | } catch (err) { 88 | console.log(err); 89 | } 90 | return i; 91 | }, 92 | addMock: function(mock) { 93 | if (Array.isArray(mocks)) { 94 | mocks.push(mock); 95 | return true; 96 | } 97 | return false; 98 | } 99 | }; 100 | -------------------------------------------------------------------------------- /tests/nodeunit.json: -------------------------------------------------------------------------------- 1 | { 2 | "output" : "output", 3 | "error_prefix": "\u001B[31m", 4 | "error_suffix": "\u001B[39m", 5 | "ok_prefix": "\u001B[32m", 6 | "ok_suffix": "\u001B[39m", 7 | "bold_prefix": "\u001B[1m", 8 | "bold_suffix": "\u001B[22m", 9 | "assertion_prefix": "\u001B[35m", 10 | "assertion_suffix": "\u001B[39m" 11 | } 12 | -------------------------------------------------------------------------------- /tests/output/.gitignore: -------------------------------------------------------------------------------- 1 | *.xml 2 | -------------------------------------------------------------------------------- /tests/run_tests.js: -------------------------------------------------------------------------------- 1 | try { 2 | var args = process.argv, reporterType = 'default'; 3 | var reporters = require('nodeunit').reporters; 4 | 5 | if (args.length === 3) { 6 | reporterType = args[2]; 7 | 8 | if (!(reporterType in reporters)) { 9 | console.error('Invalid reporterType specified', reporterType); 10 | process.exit(); 11 | } 12 | } 13 | 14 | var reporter = reporters[reporterType]; 15 | var options = require('./nodeunit.json'); 16 | } catch (e) { 17 | console.log(e); 18 | console.log('Cannot find nodeunit module.'); 19 | process.exit(); 20 | } 21 | 22 | process.chdir(__dirname); 23 | 24 | try { 25 | var server = require('mockserver').init(); 26 | server.on('listening', function() { 27 | reporter.run(['src'], options, function(err) { 28 | setTimeout(function() { 29 | server.close(); 30 | }, 100); 31 | 32 | if (err) { 33 | process.exit(1); 34 | } 35 | }); 36 | }); 37 | //reporter.run(['src/commands'], options); 38 | } catch (err) { 39 | console.log(err); 40 | process.exit(); 41 | } 42 | -------------------------------------------------------------------------------- /tests/src/testElementsCountAssertion.js: -------------------------------------------------------------------------------- 1 | var Api = require('../../node_modules/nightwatch/lib/core/api.js'); 2 | 3 | module.exports = { 4 | setUp: function(callback) { 5 | callback(); 6 | }, 7 | 8 | 'Elements Count items found message is correct': function(test) { 9 | var assertionFn = require('../../assertions/elementsCount.js'); 10 | 11 | var client = { 12 | options: {}, 13 | api: { 14 | elements: function(selectorStrategy, selector, callback) { 15 | test.equals(selectorStrategy, 'css selector'); 16 | test.equals(selector, '.some-parent .with-children'); 17 | callback({ 18 | status: 0, 19 | value: [ 20 | { ELEMENT: '6' }, 21 | { ELEMENT: '7' }, 22 | { ELEMENT: '8' }, 23 | { ELEMENT: '9' }, 24 | { ELEMENT: '10' }, 25 | { ELEMENT: '11' } 26 | ] 27 | }); 28 | } 29 | }, 30 | assertion: function(passed, result, expected, msg, abortOnFailure) { 31 | test.equals(passed, true); 32 | test.equals(result, 6); 33 | test.equals(expected, 6); 34 | test.equals(msg, 'Testing if the count for the element <.some-parent .with-children> is 6.'); 35 | test.equals(abortOnFailure, true); 36 | test.done(); 37 | } 38 | }; 39 | 40 | Api.init(client); 41 | var m = Api.createAssertion('elementsCount', assertionFn, true, client); 42 | m._commandFn('.some-parent .with-children', 6); 43 | }, 44 | 45 | 'Elements Count no items found messaging is correct': function(test) { 46 | var assertionFn = require('../../assertions/elementsCount.js'); 47 | var client = { 48 | options: {}, 49 | api: { 50 | elements: function(selectorStrategy, selector, callback) { 51 | test.equals(selectorStrategy, 'css selector'); 52 | test.equals(selector, '.notfound'); 53 | callback({ 54 | status: -1, 55 | value: [] 56 | }); 57 | } 58 | }, 59 | assertion: function(passed, result, expected, msg, abortOnFailure) { 60 | test.equals(passed, false); 61 | test.equals(result, null); 62 | test.equals(expected, 0); 63 | test.equals(msg, 'Testing if the count for the element <.notfound> is 0. '); 64 | test.equals(abortOnFailure, true); 65 | test.done(); 66 | } 67 | }; 68 | 69 | Api.init(client); 70 | var m = Api.createAssertion('elementsCount', assertionFn, true, client); 71 | m._commandFn('.notfound', 0); 72 | }, 73 | 74 | tearDown: function(callback) { 75 | callback(); 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /tests/src/testElementsPresentAssertion.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | setUp: function(callback) { 3 | callback(); 4 | }, 5 | 6 | 'Elements Present found messaging is correct for one element' : function(test) { 7 | var Assertion = require('../../assertions/elementsPresent.js'); 8 | 9 | var client = { 10 | options: {}, 11 | api: { 12 | element: function(using, selector, callback) { 13 | callback({ 14 | status: 0, 15 | value: { 16 | ELEMENT: 'body' 17 | } 18 | }); 19 | } 20 | }, 21 | assertion: function(passed, result, expected, msg, abortOnFailure) { 22 | test.equals(passed, true); 23 | test.equals(result, 0); 24 | test.equals(expected, 0); 25 | test.equals(msg, 'Page contained 1 expected element.'); 26 | test.equals(abortOnFailure, false); 27 | Assertion = null; 28 | test.done(); 29 | } 30 | }; 31 | 32 | var m = new Assertion(); 33 | m.abortOnFailure = true; 34 | m.client = client; 35 | m.api = client.api; 36 | m.command('body'); 37 | }, 38 | 39 | 'Elements Present found messaging is correct for multiple elements' : function(test) { 40 | var Assertion = require('../../assertions/elementsPresent.js'); 41 | 42 | var client = { 43 | options: {}, 44 | api: { 45 | element: function(using, selector, callback) { 46 | callback({ 47 | status: 0, 48 | value : { 49 | ELEMENT: 'body', 50 | ELEMENT: '.element' 51 | } 52 | }); 53 | } 54 | }, 55 | assertion : function(passed, result, expected, msg, abortOnFailure) { 56 | test.equals(passed, true); 57 | test.equals(result, 0); 58 | test.equals(expected, 0); 59 | test.equals(msg, 'Page contained 2 expected elements.'); 60 | test.equals(abortOnFailure, false); 61 | delete Assertion; 62 | test.done(); 63 | } 64 | }; 65 | 66 | var m = new Assertion(); 67 | m.abortOnFailure = true; 68 | m.client = client; 69 | m.api = client.api; 70 | m.command('body', '.element'); 71 | }, 72 | 73 | 'Elements Present not found messaging is correct': function(test) { 74 | var Assertion = require('../../assertions/elementsPresent.js'); 75 | var client = { 76 | options: {}, 77 | api: { 78 | element: function(using, selector, callback) { 79 | callback({ 80 | status: -1, 81 | value: { 82 | ELEMENT: '' 83 | } 84 | }); 85 | } 86 | }, 87 | assertion: function(passed, result, expected, msg, abortOnFailure) { 88 | test.equals(passed, false); 89 | test.equals(result, 1); 90 | test.equals(expected, 0); 91 | test.equals(msg, 'Page missing the following elements: <.notfound>.'); 92 | test.equals(abortOnFailure, false); 93 | Assertion = null; 94 | test.done(); 95 | } 96 | }; 97 | 98 | var m = new Assertion(); 99 | m.abortOnFailure = true; 100 | m.client = client; 101 | m.api = client.api; 102 | 103 | m.command('.notfound'); 104 | }, 105 | 106 | tearDown: function(callback) { 107 | callback(); 108 | } 109 | }; 110 | -------------------------------------------------------------------------------- /tests/src/testElementsVisibleAssertion.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | setUp: function(callback) { 4 | callback(); 5 | }, 6 | 7 | 'Elements Present found messaging is correct for one element' : function(test) { 8 | var Assertion = require('../../assertions/elementsVisible.js'); 9 | 10 | var client = { 11 | options: {}, 12 | api: { 13 | isVisible: function(selector, callback) { 14 | callback({ 15 | status: 0, 16 | value : true 17 | }); 18 | } 19 | }, 20 | assertion : function(passed, result, expected, msg, abortOnFailure) { 21 | test.equals(passed, true); 22 | test.equals(result, 0); 23 | test.equals(expected, 0); 24 | test.equals(msg, 'Page contained 1 visible element.'); 25 | test.equals(abortOnFailure, false); 26 | delete Assertion; 27 | test.done(); 28 | } 29 | }; 30 | 31 | var m = new Assertion(); 32 | m.abortOnFailure = true; 33 | m.client = client; 34 | m.api = client.api; 35 | m.command('body'); 36 | }, 37 | 38 | 'Elements Present found messaging is correct for multiple elements' : function(test) { 39 | var Assertion = require('../../assertions/elementsVisible.js'); 40 | 41 | var client = { 42 | options: {}, 43 | api: { 44 | isVisible: function(selector, callback) { 45 | callback({ 46 | status: 0, 47 | value : true 48 | }); 49 | } 50 | }, 51 | assertion : function(passed, result, expected, msg, abortOnFailure) { 52 | test.equals(passed, true); 53 | test.equals(result, 0); 54 | test.equals(expected, 0); 55 | test.equals(msg, 'Page contained 2 visible elements.'); 56 | test.equals(abortOnFailure, false); 57 | delete Assertion; 58 | test.done(); 59 | } 60 | }; 61 | 62 | var m = new Assertion(); 63 | m.abortOnFailure = true; 64 | m.client = client; 65 | m.api = client.api; 66 | m.command('body', '.element'); 67 | }, 68 | 69 | 'Elements Present not found messaging is correct' : function(test) { 70 | var Assertion = require('../../assertions/elementsVisible.js'); 71 | var client = { 72 | options: {}, 73 | api: { 74 | isVisible : function(selector, callback) { 75 | callback({ 76 | status: -1, 77 | value : false 78 | }); 79 | } 80 | }, 81 | assertion : function(passed, result, expected, msg, abortOnFailure) { 82 | test.equals(passed, false); 83 | test.equals(result, 1); 84 | test.equals(expected, 0); 85 | test.equals(msg, 'The following elements were not visible on the page: <.notfound>'); 86 | test.equals(abortOnFailure, false); 87 | delete Assertion; 88 | test.done(); 89 | } 90 | }; 91 | 92 | var m = new Assertion(); 93 | m.abortOnFailure = true; 94 | m.client = client; 95 | m.api = client.api; 96 | 97 | m.command('.notfound'); 98 | }, 99 | 100 | tearDown : function(callback) { 101 | callback(); 102 | } 103 | }; 104 | -------------------------------------------------------------------------------- /tests/src/testGet.js: -------------------------------------------------------------------------------- 1 | var MockServer = require('mockserver'); 2 | 3 | module.exports = { 4 | setUp: function(callback) { 5 | this.client = require('../nightwatch.js').init(); 6 | 7 | callback(); 8 | }, 9 | 10 | testSuccess: function(test) { 11 | var client = this.client.api; 12 | 13 | client.get('http://localhost', function callback(result) { 14 | test.equal(true, result); 15 | test.done(); 16 | }); 17 | }, 18 | 19 | tearDown: function(callback) { 20 | this.client = null; 21 | // clean up 22 | callback(); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /tests/src/testGetMobifyEvaluatedData.js: -------------------------------------------------------------------------------- 1 | var MockServer = require('mockserver'); 2 | 3 | module.exports = { 4 | setUp: function(callback) { 5 | this.client = require('../nightwatch.js').init(); 6 | MockServer.addMock({ 7 | url: '/wd/hub/session/1352110219202/execute', 8 | method: 'POST', 9 | postdata: '{"script":"var passedArgs = Array.prototype.slice.call(arguments,0); return function (){ var framework = Mobify || Adaptive; return framework ? framework.evaluatedData : null; }.apply(window, passedArgs);","args":[]}', 10 | response: JSON.stringify({ 11 | sessionId: '1352110219202', 12 | status: 0, 13 | value: true 14 | }) 15 | }); 16 | 17 | callback(); 18 | }, 19 | 20 | testSuccess: function(test) { 21 | var client = this.client.api; 22 | client.getMobifyEvaluatedData(function callback(result) { 23 | test.equal(true, result); 24 | test.done(); 25 | }); 26 | }, 27 | 28 | tearDown: function(callback) { 29 | this.client = null; 30 | // clean up 31 | callback(); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /tests/src/testTemplateName.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | setUp: function(callback) { 3 | callback(); 4 | }, 5 | 6 | 'Mobify template name is correct': function(test) { 7 | var Assertion = require('../../assertions/templateName.js'); 8 | var client = { 9 | api: { 10 | getMobifyEvaluatedData: function(callback) { 11 | callback({ 12 | content: { 13 | templateName: 'home' 14 | } 15 | }); 16 | } 17 | }, 18 | assertion: function(passed, result, expected, msg, abortOnFailure) { 19 | test.equals(passed, true); 20 | test.equals(result.content.templateName, 'home'); 21 | test.equals(expected, 'home'); 22 | test.equals(msg, 'Testing if the actual page template equals the expected .'); 23 | test.equals(abortOnFailure, true); 24 | Assertion = null; 25 | test.done(); 26 | } 27 | }; 28 | 29 | var m = new Assertion(); 30 | m.abortOnFailure = true; 31 | m.client = client; 32 | m.api = client.api; 33 | 34 | m.command('home'); 35 | }, 36 | 37 | 'Mobify template name is incorrect': function(test) { 38 | var Assertion = require('../../assertions/templateName.js'); 39 | var client = { 40 | api: { 41 | getMobifyEvaluatedData: function(callback) { 42 | callback({ 43 | content: { 44 | templateName: 'main' 45 | } 46 | }); 47 | } 48 | }, 49 | assertion: function(passed, result, expected, msg, abortOnFailure) { 50 | test.equals(passed, false); 51 | test.equals(result.content.templateName, 'main'); 52 | test.equals(expected, 'home'); 53 | test.equals(msg, 'Testing if the actual page template
equals the expected .'); 54 | test.equals(abortOnFailure, true); 55 | Assertion = null; 56 | test.done(); 57 | } 58 | }; 59 | 60 | var m = new Assertion(); 61 | m.abortOnFailure = true; 62 | m.client = client; 63 | m.api = client.api; 64 | 65 | m.command('home'); 66 | }, 67 | 68 | tearDown: function(callback) { 69 | callback(); 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /tests/src/testTrigger.js: -------------------------------------------------------------------------------- 1 | var MockServer = require('mockserver'); 2 | 3 | module.exports = { 4 | setUp: function(callback) { 5 | this.client = require('../nightwatch.js').init(); 6 | 7 | callback(); 8 | }, 9 | 10 | testSuccessClick: function(test) { 11 | var client = this.client.api; 12 | client.trigger('.clickMe', 'click', function callback(result) { 13 | test.equal(true, result); 14 | test.done(); 15 | }); 16 | }, 17 | 18 | testSuccessDefaultEvent: function(test) { 19 | var client = this.client.api; 20 | client.trigger('.clickMe', function callback(result) { 21 | test.equal(true, result); 22 | test.done(); 23 | }); 24 | }, 25 | 26 | tearDown: function(callback) { 27 | this.client = null; 28 | // clean up 29 | callback(); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /tests/src/testWaitForAjaxCompleted.js: -------------------------------------------------------------------------------- 1 | var MockServer = require('mockserver'); 2 | 3 | module.exports = { 4 | setUp: function(callback) { 5 | this.client = require('../nightwatch.js').init(); 6 | 7 | callback(); 8 | }, 9 | 10 | testSuccess: function(test) { 11 | var client = this.client.api; 12 | client.waitForAjaxCompleted(function callback(result) { 13 | test.equal(0, result); 14 | test.done(); 15 | }); 16 | }, 17 | 18 | tearDown: function(callback) { 19 | this.client = null; 20 | // clean up 21 | callback(); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /tests/src/testWaitForAnimation.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | setUp: function(callback) { 3 | this.client = require('../nightwatch.js').init(); 4 | 5 | callback(); 6 | }, 7 | 8 | testCommand: function(test) { 9 | this.client.api.waitForAnimation(200, function() { 10 | test.done(); 11 | }); 12 | }, 13 | 14 | tearDown: function(callback) { 15 | this.client = null; 16 | 17 | callback(); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /tests/src/testWaitForCondition.js: -------------------------------------------------------------------------------- 1 | var MockServer = require('mockserver'); 2 | 3 | module.exports = { 4 | setUp: function(callback) { 5 | this.client = require('../nightwatch.js').init(); 6 | 7 | callback(); 8 | }, 9 | 10 | testSuccess: function(test) { 11 | var client = this.client.api; 12 | client.waitForCondition('return true;', 100, 0, function callback(result) { 13 | test.equal(result, true); 14 | test.done(); 15 | }); 16 | }, 17 | 18 | testFailure: function(test) { 19 | var client = this.client.api; 20 | client.waitForCondition('return false', 600, 0, function callback(result) { 21 | test.equal(result, false); 22 | test.done(); 23 | }); 24 | }, 25 | 26 | tearDown: function(callback) { 27 | this.client = null; 28 | // clean up 29 | callback(); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /tests/src/testWaitForContextsReady.js: -------------------------------------------------------------------------------- 1 | var MockServer = require('mockserver'); 2 | 3 | module.exports = { 4 | setUp: function(callback) { 5 | this.client = require('../nightwatch.js').init(); 6 | 7 | callback(); 8 | }, 9 | 10 | testSuccess: function(test) { 11 | var client = this.client.api; 12 | var expectedContextsCount = 3; 13 | client.waitForContextsReady(expectedContextsCount, 1000, 0, function callback(result) { 14 | test.notEqual(result, undefined); 15 | test.equal(result.length, expectedContextsCount); 16 | test.done(); 17 | }); 18 | }, 19 | 20 | testFailure: function(test) { 21 | var client = this.client.api; 22 | client.waitForContextsReady(5, 1000, 0, function callback(result) { 23 | test.equal(result, false); 24 | test.done(); 25 | }); 26 | }, 27 | 28 | tearDown: function(callback) { 29 | this.client = null; 30 | // clean up 31 | callback(); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /tests/src/testWaitForUrl.js: -------------------------------------------------------------------------------- 1 | var MockServer = require('mockserver'); 2 | 3 | module.exports = { 4 | setUp: function(callback) { 5 | this.client = require('../nightwatch.js').init(); 6 | 7 | callback(); 8 | }, 9 | 10 | testSuccess: function(test) { 11 | var client = this.client.api; 12 | client.waitForUrl('http://localhost/', 100, 0, function callback(result) { 13 | test.equal(result, 'http://localhost/'); 14 | test.done(); 15 | }); 16 | }, 17 | 18 | testFailure: function(test) { 19 | var client = this.client.api; 20 | client.waitForUrl('http://localhost2/', 600, 0, function callback(result) { 21 | test.equal(result, false); 22 | test.done(); 23 | }); 24 | }, 25 | 26 | tearDown: function(callback) { 27 | this.client = null; 28 | // clean up 29 | callback(); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /tests/src/testWaitUntilMobified.js: -------------------------------------------------------------------------------- 1 | var MockServer = require('mockserver'); 2 | 3 | module.exports = { 4 | setUp: function(callback) { 5 | this.client = require('../nightwatch.js').init(); 6 | 7 | callback(); 8 | }, 9 | 10 | testSuccess: function(test) { 11 | var client = this.client.api; 12 | client.waitUntilMobified(100, function callback(result) { 13 | test.equal(true, result); 14 | test.done(); 15 | }); 16 | }, 17 | 18 | tearDown: function(callback) { 19 | this.client = null; 20 | // clean up 21 | callback(); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /tests/tests/system/site.json: -------------------------------------------------------------------------------- 1 | { 2 | "activeProfile": "default", 3 | "profiles": { 4 | "default": { 5 | "bundleUrl": "", 6 | "siteUrl": "" 7 | } 8 | } 9 | } --------------------------------------------------------------------------------