diff --git a/debugging/async_await.js b/debugging/async_await.js new file mode 100644 index 000000000..8ba3fbb4e --- /dev/null +++ b/debugging/async_await.js @@ -0,0 +1,37 @@ +describe('angularjs homepage', function() { + it('should greet the named user', async function() { + debugger; + await browser.get('http://www.angularjs.org'); + + await element(by.model('yourName')).sendKeys('Julie'); + + var greeting = element(by.binding('yourName')); + + expect(await greeting.getText()).toEqual('Hello Julie!'); + }); + + describe('todo list', function() { + var todoList; + + beforeEach(async function() { + await browser.get('http://www.angularjs.org'); + todoList = element.all(by.repeater('todo in todoList.todos')); + }); + + it('should list todos', async function() { + expect(await todoList.count()).toEqual(2); + expect(await todoList.get(1).getText()).toEqual('build an AngularJS app'); + }); + + it('should add a todo', async function() { + var addTodo = element(by.model('todoList.todoText')); + var addButton = element(by.css('[value="add"]')); + + await addTodo.sendKeys('write a protractor test'); + await addButton.click(); + + expect(await todoList.count()).toEqual(3); + expect(await todoList.get(2).getText()).toEqual('write a protractor test'); + }); + }); +}); diff --git a/debugging/conf.js b/debugging/conf.js new file mode 100644 index 000000000..38588405f --- /dev/null +++ b/debugging/conf.js @@ -0,0 +1,25 @@ +// An example configuration file for debugging test using async/await. +exports.config = { + directConnect: true, + + // Capabilities to be passed to the webdriver instance. + capabilities: { + 'browserName': 'chrome' + }, + + seleniumAddress: 'http://localhost:4444/wd/hub', + + // Framework to use. Jasmine is recommended. + framework: 'jasmine', + + // Spec patterns are relative to the current working directory when + // protractor is called. + specs: ['async_await.js'], + + SELENIUM_PROMISE_MANAGER: false, + + // Options to be passed to Jasmine. + jasmineNodeOpts: { + defaultTimeoutInterval: 30000 + } +}; diff --git a/docs/async-await.md b/docs/async-await.md index e4ecd2e57..d17e0833e 100644 --- a/docs/async-await.md +++ b/docs/async-await.md @@ -1,4 +1,86 @@ `async`/`await` =============== -Please see [our TypeScript examples which use `async`/`await`](/exampleTypescript/asyncAwait/). +**Background** + +- The Web Driver Control Flow is used to synchronize your commands so they reach +the browser in the correct order (see +[/docs/control-flow.md](/docs/control-flow.md) for details). In the future, the +control flow is being removed (see [SeleniumHQ's github issue]( +https://github.com/SeleniumHQ/selenium/issues/2969) for details). Instead of the +control flow, you can synchronize your commands with promise chaining or the +upcoming ES7 feature `async`/`await`. + +- Previously, we have Typescript support for `async`/`await`: Please see [TypeScript examples which use `async`/`await`](/exampleTypescript/asyncAwait/README.md). + +- The latest [Node.js](https://nodejs.org/en/) provides native async/await, + which means we can get stable e2e test without using control flow in javascript test. + + **Note**: To write and run native async/await test, the node.js version should be greater than or equal to 8.0, and Jasmine version should be greater than or equal to 2.7 + +- If we disable control flow and use async/await to write tests, we can get a + better debugging experience by using [chrome + inspector](./debugging.md#disabled-control-flow) + +**How to use native async/await in test** + +We have a simple example to show how to use async/await in test. + +You can find the whole example in +[here](/debugging/async_await.js) + +```javascript +describe('angularjs homepage', function() { + it('should greet the named user', async function() { + await browser.get('http://www.angularjs.org'); + + await element(by.model('yourName')).sendKeys('Julie'); + + var greeting = element(by.binding('yourName')); + + expect(await greeting.getText()).toEqual('Hello Julie!'); + }); +``` + +As you can see, the syntax is almost the same with TypeScript async/await. + +1. We need wrap our asynchronous function with “async”. +1. We can add “await” keyword to each operation that we want our program to + wait for. + + **Note:** Never forget to add “await” keyword in an async function, it + may bring some unexpected problem (e.g. your test might fail silently and + always be reported as passed). +1. Don’t forget to turn off control_flow, you cannot use a mix of `async`/`await` and the control flow: +`async`/`await` causes the control flow to become unreliable (see +[github issue]( https://github.com/SeleniumHQ/selenium/issues/3037)). So if you +`async`/`await` anywhere in a spec, you should use the +`SELENIUM_PROMISE_MANAGER: false` + +```javascript +// An example configuration file for debugging test using async/await. +exports.config = { + directConnect: true, + + // Capabilities to be passed to the webdriver instance. + capabilities: { + 'browserName': 'chrome' + }, + + seleniumAddress: 'http://localhost:4444/wd/hub', + + // Framework to use. Jasmine is recommended. + framework: 'jasmine', + + // Spec patterns are relative to the current working directory when + // protractor is called. + specs: ['async_await.js'], + + SELENIUM_PROMISE_MANAGER: false, + + // Options to be passed to Jasmine. + jasmineNodeOpts: { + defaultTimeoutInterval: 30000 + } +}; +``` diff --git a/docs/breakpoint.png b/docs/breakpoint.png new file mode 100644 index 000000000..704cae7be Binary files /dev/null and b/docs/breakpoint.png differ diff --git a/docs/chromeDevTool.png b/docs/chromeDevTool.png new file mode 100644 index 000000000..79c4b3993 Binary files /dev/null and b/docs/chromeDevTool.png differ diff --git a/docs/debugging.md b/docs/debugging.md index f0030c591..2e8b60e55 100644 --- a/docs/debugging.md +++ b/docs/debugging.md @@ -7,8 +7,7 @@ state of the application they're testing. WebDriver tests in particular can be difficult to debug because of long error messages and the separation between the browser and the process running the test. -Types of Failure ----------------- +## Types of Failure Protractor comes with examples of failing tests ([failure_spec.js](https://github.com/angular/protractor/blob/master/debugging/failure_spec.js)). To run, start up the test application and a Selenium Server, and run the command below. Then look at all the stack traces. @@ -28,15 +27,125 @@ This test suite shows various types of failure: like. -Timeouts --------- +## Timeouts There are several ways that Protractor can time out. See the [Timeouts](/docs/timeouts.md) reference for full documentation. +## Disabled Control Flow -Pausing to Debug ----------------- + +The latest [Node.js](https://nodejs.org/en/) provides native async/await, which +means we can get stable e2e test easily without using control flow. Furthermore, +if we write our test by using async/await[(how to?)](./async-await.md), we can +use chrome development tool and chrome inspector together to debug the new +tests, which will give a nicer debugging experience. + +**Debuging tests in chrome inspector** + +We can debug both javascript and TypeScript async/await tests in chrome +inspector and the debugging process is almost the same. + +We have a simple example to show how to debug async/await in test. You can find +the whole example in +[here](../debugging/async_await.js) + +- Add “debugger” keyword to the test case that we want to debug. + + ```javascript + it('should greet the named user', async function() { + debugger; + await browser.get('http://www.angularjs.org'); + + await element(by.model('yourName')).sendKeys('Julie'); + + var greeting = element(by.binding('yourName')); + + expect(await greeting.getText()).toEqual('Hello Julie!'); + }); + ``` + +- Start test process with a new argument "inspect-brk", which will enable + inspector agent, listen on default address and port (127.0.0.1:9229) and + break before user code starts + + Use + + ``` + node --inspect-brk bin/protractor + ``` + +- Open chrome inspector: Enter "chrome://inspect/#devices" in browser, find + the current running target and click “Inspect” + + ![screenshot](./inspector.png) + +- The test will start and pause at the beginning. + + ![screenshot](./firstBreak.png) + +- We can click F8 (resume script execution), and the test will pause at the + first line that has our “debugger” keyword. We can then add breakpoints and + debug tests. + + ![screenshot](./breakpoint.png) + +- We can also open chrome development tool on the webdriver controlled browser + to check the html elements and do some queries while the test execution is + pausing. + + ![screenshot](./chromeDevTool.png) + +- Known Issues + +1. If we resume test execution after a long time pause, it will jump to next + test case even we have some other breaking points in current test case since + current test case has already been timeout. You can set + jasmine.DEFAULT_TIMEOUT_INTERVAL to an arbitrary high value so that your + test doesn't time out. + +2. If we step into protractor lib code which was written in Typescript, we + cannot see the TypeScript code. In general, you can add breakpoints to each + line that you want it to pause, and use F8 (resume script execution) to + debug(To avoid step into protractor lib). + + +**Setting Up VSCode for Debugging** + +VS Code has built-in [debugging](https://code.visualstudio.com/docs/editor/debugging) support for the Node.js runtime and can debug JavaScript, TypeScript, and any other language that gets transpiled to JavaScript. + +To set up VSCode for Protractor, follow the below steps: + +1. Click on the Debugging icon in the View Bar on the side of VS Code. +2. Click on the Configure gear icon on the Debug view top bar and choose nodejs environment. +3. It will generate a `launch.json` file under your workspace's `.vscode` folder. +4. Setup your launch.json file by configuring below two commands: +``` + "program": "${workspaceRoot}/node_modules/protractor/bin/protractor", + "args": ["${workspaceRoot}/protractorConfig.js"], +``` +5. Save your launch.json, put some breakpoints and start debugging. + +**Setting Up WebStorm for Debugging** + +To set up WebStorm for Protractor, do the following: + +1. Open the Run/Debug Configurations dialog +2. Add new Node.js configuration. +3. On the Configuration tab set: + - **Node Interpreter**: path to node executable + - **Working directory**: your project base path + - **JavaScript file**: path to Protractor cli.js file (e.g. *node_modules\protractor\built\cli.js*) + - **Application parameters**: path to your Protractor configuration file (e.g. + *protractorConfig.js*) +4. Click OK, place some breakpoints, and start debugging. + + +## Enabled Control Flow + +**Note:** Protractor debugger and element explorer cannot be used for Node.js 8+ + +**Pausing to Debug** Protractor supports two methods for pausing to debug - `browser.pause()` and `browser.debugger()`. You probably want to use `browser.pause()`, unless you @@ -149,53 +258,7 @@ used from the browser's console. > window.clientSideScripts.findInputs('username', document.getElementById('#myEl')); ``` -**Debugging with the control flow disabled** - -If you've set the `SELENIUM_PROMISE_MANAGER` config value to false to [disable the control flow](https://github.com/angular/protractor/blob/master/docs/control-flow.md), -the above methods will not work. Instead, you can now use native `debugger` statements to pause your code. However, you -will need to start your tests using Node's `--inspect-brk` option: - -``` -node --inspect-brk node_modules/.bin/protractor -``` - -You will then be able to use the Chrome devtools at chrome://inspect to connect to the tests. - - -Setting Up VSCode for Debugging -------------------------------- -VS Code has built-in [debugging](https://code.visualstudio.com/docs/editor/debugging) support for the Node.js runtime and can debug JavaScript, TypeScript, and any other language that gets transpiled to JavaScript. - -To set up VSCode for Protractor, follow the below steps: - -1. Click on the Debugging icon in the View Bar on the side of VS Code. -2. Click on the Configure gear icon on the Debug view top bar and choose nodejs environment. -3. It will generate a `launch.json` file under your workspace's `.vscode` folder. -4. Setup your launch.json file by configuring below two commands: -``` - "program": "${workspaceRoot}/node_modules/protractor/bin/protractor", - "args": ["${workspaceRoot}/protractorConfig.js"], -``` -5. Save your launch.json, put some breakpoints and start debugging. - -Setting Up WebStorm for Debugging ---------------------------------- - -To set up WebStorm for Protractor, do the following: - -1. Open the Run/Debug Configurations dialog -2. Add new Node.js configuration. -3. On the Configuration tab set: - - **Node Interpreter**: path to node executable - - **Working directory**: your project base path - - **JavaScript file**: path to Protractor cli.js file (e.g. *node_modules\protractor\built\cli.js*) - - **Application parameters**: path to your Protractor configuration file (e.g. - *protractorConfig.js*) -4. Click OK, place some breakpoints, and start debugging. - - -Testing Out Protractor Interactively ------------------------------------- +**Testing Out Protractor Interactively** When debugging or first writing test suites, you may find it helpful to try out Protractor commands without starting up the entire test suite. You can @@ -222,8 +285,8 @@ matching a locator. Element explorer will start chrome by default. However, you can specify another browser, change browser settings, or specify any other config that you -normally would with your protractor test. To do this, pass configs to -protractor like you normally would, +normally would with your protractor test. To do this, pass configs to +protractor like you normally would, but with the `--elementExplorer` flag set: protractor [configFile] [options] --elementExplorer @@ -232,16 +295,15 @@ For example, to connect to ChromeDriver directly, use protractor --directConnect --elementExplorer -Element explore will ignore your specs, not set up your framework (e.g. jasmine, +Element explore will ignore your specs, not set up your framework (e.g. jasmine, mocha, cucumber), and only allow you to pass in 1 capability, but will honor every other parameter in your config. -Note `baseUrl` is used here as the initial page, i.e. element explorer will try +Note `baseUrl` is used here as the initial page, i.e. element explorer will try to navigate to `baseUrl` automatically on start. -Taking Screenshots ------------------- +## Taking Screenshots WebDriver can snap a screenshot with `browser.takeScreenshot()`. This can be a good way to help debug tests, especially for tests that run on a continuous integration diff --git a/docs/firstBreak.png b/docs/firstBreak.png new file mode 100644 index 000000000..42535319d Binary files /dev/null and b/docs/firstBreak.png differ diff --git a/docs/inspector.png b/docs/inspector.png new file mode 100644 index 000000000..2022f7f4c Binary files /dev/null and b/docs/inspector.png differ diff --git a/exampleTypescript/asyncAwait/README.md b/exampleTypescript/asyncAwait/README.md index 690d6e661..f21bb1a1d 100644 --- a/exampleTypescript/asyncAwait/README.md +++ b/exampleTypescript/asyncAwait/README.md @@ -1,34 +1,17 @@ -`async`/`await` and the Web Driver Control Flow -=============================================== - -The Web Driver Control Flow is used to synchronize your commands so they reach -the browser in the correct order (see -[/docs/control-flow.md](/docs/control-flow.md) for details). In the future, the -control flow is being removed (see [SeleniumHQ's github issue]( -https://github.com/SeleniumHQ/selenium/issues/2969) for details). Instead of the -control flow, you can synchronize your commands with promise chaining or the -upcoming ES7 feature `async`/`await`. - -However, you cannot use a mix of `async`/`await` and the control flow: -`async`/`await` causes the control flow to become unreliable (see -[github issue]( https://github.com/SeleniumHQ/selenium/issues/3037)). So if you -`async`/`await` anywhere in a spec, you should use the -`SELENIUM_PROMISE_MANAGER: false` [config option](/lib/config.js#L644). - Compiling `async`/`await` syntax ================================ `async`/`await` syntax is currently accessible via typescript if you compile -using `--target ES2015` or above. This example +using `--target ES2015` or above. Debugging with `async`/`await` ============================== Disabling the promise manager will break Protractor's debugging and -`browser.pause()`. However, because your tests won't be using the promise +`browser.pause()`. However, because your tests won't be using the promise manager, you can debug them using standard Node debugging tools. For -example, you can use the Chrome inspector to debug the test in this -directory with `npm run debug`. You should see something like +example, you can use the Chrome inspector to debug the test in this +directory (protractor/exampleTypescript) with `npm run debug`. You should see something like ``` Debugger listening on port 9229. @@ -38,10 +21,13 @@ To start debugging, open the following URL in Chrome: ``` Open that URL in chrome, and you'll get an inspector that can be -used to debug your test. Instead of browser.pause, you can add `debugger` -to create a breakpoint. Note that sourcemaps don't work in the inspector +used to debug your test. Instead of browser.pause, you can add `debugger` +to create a breakpoint. Note that sourcemaps don't work in the inspector in Node v6, but do work in Node v7 (due to https://github.com/nodejs/node/issues/8369). +More detail about how to use [chrome + inspector](../../docs/debugging.md#disabled-control-flow) + More Examples ============= diff --git a/exampleTypescript/package.json b/exampleTypescript/package.json index 906f5a721..27dc77123 100644 --- a/exampleTypescript/package.json +++ b/exampleTypescript/package.json @@ -8,7 +8,7 @@ "tsc": "tsc", "pretest": "npm run tsc", "test": "protractor tmp/conf.js", - "debug": "node --inspect --debug-brk ./node_modules/.bin/protractor asyncAwait/conf.js" + "debug": "node --inspect-brk ./node_modules/.bin/protractor asyncAwait/conf.js" }, "dependencies": { "@types/jasmine": "2.5.41",