diff --git a/docs/content/guide/e2e-testing.ngdoc b/docs/content/guide/e2e-testing.ngdoc index 290d80b4..7874c83d 100644 --- a/docs/content/guide/e2e-testing.ngdoc +++ b/docs/content/guide/e2e-testing.ngdoc @@ -3,319 +3,83 @@ @name E2E Testing @description +# E2E Testing +
-In addition to the above elements, scenarios may also contain helper functions to avoid duplicating
+In addition to the above elements, tests may also contain helper functions to avoid duplicating
code in the `it` blocks.
-Here is an example of a simple scenario:
+Here is an example of a simple test:
```js
-describe('Buzz Client', function() {
-it('should filter results', function() {
- input('user').enter('jacksparrow');
- element(':button').click();
- expect(repeater('ul li').count()).toEqual(10);
- input('filterText').enter('Bees');
- expect(repeater('ul li').count()).toEqual(1);
-});
+describe('TODO list', function() {
+ it('should filter results', function() {
+
+ // Find the element with ng-model="user" and type "jacksparrow" into it
+ element(by.model('user')).sendKeys('jacksparrow');
+
+ // Find the first (and only) button on the page and click it
+ element(by.css(':button')).click();
+
+ // Verify that there are 10 tasks
+ expect(element.all(by.repeater('task in tasks')).count()).toEqual(10);
+
+ // Enter 'groceries' into the element with ng-model="filterText"
+ element(by.model('filterText')).sendKeys('groceries');
+
+ // Verify that now there is only one item in the task list
+ expect(element.all(by.repeater('task in tasks')).count()).toEqual(1);
+ });
});
```
-Note that
-[`input('user')`](https://github.com/angular/angular.js/blob/master/docs/content/guide/dev_guide.e2e-testing.ngdoc#L119)
-finds the `` element with `ng-model="user"` not `name="user"`.
-
-This scenario describes the requirements of a Buzz Client, specifically, that it should be able to
-filter the stream of the user. It starts by entering a value in the input field with ng-model="user", clicking
-the only button on the page, and then it verifies that there are 10 items listed. It then enters
-'Bees' in the input field with ng-model='filterText' and verifies that the list is reduced to a single item.
-
-The API section below lists the available commands and expectations for the Runner.
-
-## API
-Source: https://github.com/angular/angular.js/blob/master/src/ngScenario/dsl.js
-
-### `pause()`
-Pauses the execution of the tests until you call `resume()` in the console (or click the resume
-link in the Runner UI).
-
-### `sleep(seconds)`
-Pauses the execution of the tests for the specified number of `seconds`.
-
-### `browser().navigateTo(url)`
-Loads the `url` into the test frame.
-
-### `browser().navigateTo(url, fn)`
-Loads the URL returned by `fn` into the testing frame. The given `url` is only used for the test
-output. Use this when the destination URL is dynamic (that is, the destination is unknown when you
-write the test).
-
-### `browser().reload()`
-Refreshes the currently loaded page in the test frame.
-
-### `browser().window().href()`
-Returns the window.location.href of the currently loaded page in the test frame.
-
-### `browser().window().path()`
-Returns the window.location.pathname of the currently loaded page in the test frame.
-
-### `browser().window().search()`
-Returns the window.location.search of the currently loaded page in the test frame.
-
-### `browser().window().hash()`
-Returns the window.location.hash (without `#`) of the currently loaded page in the test frame.
-
-### `browser().location().url()`
-Returns the {@link ng.$location $location.url()} of the currently loaded page in
-the test frame.
-
-### `browser().location().path()`
-Returns the {@link ng.$location $location.path()} of the currently loaded page in
-the test frame.
-
-### `browser().location().search()`
-Returns the {@link ng.$location $location.search()} of the currently loaded page
-in the test frame.
-
-### `browser().location().hash()`
-Returns the {@link ng.$location $location.hash()} of the currently loaded page in
-the test frame.
-
-### `expect(future).{matcher}`
-Asserts the value of the given `future` satisfies the `matcher`. All API statements return a
-`future` object, which get a `value` assigned after they are executed. Matchers are defined using
-`angular.scenario.matcher`, and they use the value of futures to run the expectation. For example:
-`expect(browser().location().href()).toEqual('http://www.google.com')`. Available matchers
-are presented further down this document.
-
-### `expect(future).not().{matcher}`
-Asserts the value of the given `future` satisfies the negation of the `matcher`.
-
-### `using(selector, label)`
-Scopes the next DSL element selection.
-
-### `binding(name)`
-Returns the value of the first binding matching the given `name`.
-
-### `input(name).enter(value)`
-Enters the given `value` in the text field with the corresponding ng-model `name`.
-
-### `input(name).check()`
-Checks/unchecks the checkbox with the corresponding ng-model `name`.
-
-### `input(name).select(value)`
-Selects the given `value` in the radio button with the corresponding ng-model `name`.
-
-### `input(name).val()`
-Returns the current value of an input field with the corresponding ng-model `name`.
-
-### `repeater(selector, label).count()`
-Returns the number of rows in the repeater matching the given jQuery `selector`. The `label` is
-used for test output.
-
-### `repeater(selector, label).row(index)`
-Returns an array with the bindings in the row at the given `index` in the repeater matching the
-given jQuery `selector`. The `label` is used for test output.
-
-### `repeater(selector, label).column(binding)`
-Returns an array with the values in the column with the given `binding` in the repeater matching
-the given jQuery `selector`. The `label` is used for test output.
-
-### `select(name).option(value)`
-Picks the option with the given `value` on the select with the given ng-model `name`.
-
-### `select(name).options(value1, value2...)`
-Picks the options with the given `values` on the multi select with the given ng-model `name`.
-
-### `element(selector, label).count()`
-Returns the number of elements that match the given jQuery `selector`. The `label` is used for test
-output.
-
-### `element(selector, label).click()`
-Clicks on the element matching the given jQuery `selector`. The `label` is used for test output.
-
-### `element(selector, label).query(fn)`
-Executes the function `fn(selectedElements, done)`, where selectedElements are the elements that
-match the given jQuery `selector` and `done` is a function that is called at the end of the `fn`
-function. The `label` is used for test output.
-
-### `element(selector, label).{method}()`
-Returns the result of calling `method` on the element matching the given jQuery `selector`, where
-`method` can be any of the following jQuery methods: `val`, `text`, `html`, `height`,
-`innerHeight`, `outerHeight`, `width`, `innerWidth`, `outerWidth`, `position`, `scrollLeft`,
-`scrollTop`, `offset`. The `label` is used for test output.
-
-### `element(selector, label).{method}(value)`
-Executes the `method` passing in `value` on the element matching the given jQuery `selector`, where
-`method` can be any of the following jQuery methods: `val`, `text`, `html`, `height`,
-`innerHeight`, `outerHeight`, `width`, `innerWidth`, `outerWidth`, `position`, `scrollLeft`,
-`scrollTop`, `offset`. The `label` is used for test output.
-
-### `element(selector, label).{method}(key)`
-Returns the result of calling `method` passing in `key` on the element matching the given jQuery
-`selector`, where `method` can be any of the following jQuery methods: `attr`, `prop`, `css`. The
-`label` is used for test output.
-
-### `element(selector, label).{method}(key, value)`
-Executes the `method` passing in `key` and `value` on the element matching the given jQuery
-`selector`, where `method` can be any of the following jQuery methods: `attr`, `prop`, `css`. The
-`label` is used for test output.
-
-## Matchers
-
-Matchers are used in combination with the `expect(...)` function as described above and can
-be negated with `not()`. For instance: `expect(element('h1').text()).not().toEqual('Error')`.
-
-Source: https://github.com/angular/angular.js/blob/master/src/ngScenario/matchers.js
-
-```js
-// value and Object comparison following the rules of angular.equals().
-expect(value).toEqual(value)
-
-// a simpler value comparison using ===
-expect(value).toBe(value)
-
-// checks that the value is defined by checking its type.
-expect(value).toBeDefined()
-
-// the following two matchers are using JavaScript's standard truthiness rules
-expect(value).toBeTruthy()
-expect(value).toBeFalsy()
-
-// verify that the value matches the given regular expression. The regular
-// expression may be passed in form of a string or a regular expression
-// object.
-expect(value).toMatch(expectedRegExp)
-
-// a check for null using ===
-expect(value).toBeNull()
-
-// Array.indexOf(...) is used internally to check whether the element is
-// contained within the array.
-expect(value).toContain(expected)
-
-// number comparison using < and >
-expect(value).toBeLessThan(expected)
-expect(value).toBeGreaterThan(expected)
-```
+This test describes the requirements of a ToDo list, specifically, that it should be able to
+filter the list of items.
## Example
-See the [angular-seed](https://github.com/angular/angular-seed) project for more examples.
-
-### Conditional actions with element(...).query(fn)
-
-E2E testing with angular scenario is highly asynchronous and hides a lot of complexity by
-queueing actions and expectations that can handle futures. From time to time, you might need
-conditional assertions or element selection. Even though you should generally try to avoid this
-(as it is can be sign for unstable tests), you can add conditional behavior with
-`element(...).query(fn)`. The following code listing shows how this function can be used to delete
-added entries (where an entry is some domain object) using the application's web interface.
-
-Imagine the application to be structured into two views:
-
- 1. *Overview view* which lists all the added entries in a table and
- 2. a *detail view* which shows the entries' details and contains a delete button. When clicking the
- delete button, the user is redirected back to the *overview page*.
-
-```js
-beforeEach(function () {
- var deleteEntry = function () {
- browser().navigateTo('/entries');
-
- // we need to select the element as it might be the case that there
- // are no entries (and therefore no rows). When the selector does not
- // result in a match, the test would be marked as a failure.
- element('table tbody').query(function (tbody, done) {
- // ngScenario gives us a jQuery lite wrapped element. We call the
- // `children()` function to retrieve the table body's rows
- var children = tbody.children();
-
- if (children.length > 0) {
- // if there is at least one entry in the table, click on the link to
- // the entry's detail view
- element('table tbody a').click();
- // and, after a route change, click the delete button
- element('.btn-danger').click();
- }
-
- // if there is more than one entry shown in the table, queue another
- // delete action.
- if (children.length > 1) {
- deleteEntry();
- }
-
- // remember to call `done()` so that ngScenario can continue
- // test execution.
- done();
- });
-
- };
-
- // start deleting entries
- deleteEntry();
-});
-```
-
-In order to understand what is happening, we should emphasize that ngScenario calls are not
-immediately executed, but queued (in ngScenario terms, we would be talking about adding
-future actions). If we had only one entry in our table, then the following future actions
-would be queued:
-
-```js
-// delete entry 1
-browser().navigateTo('/entries');
-element('table tbody').query(function (tbody, done) { ... });
-element('table tbody a');
-element('.btn-danger').click();
-```
-
-For two entries, ngScenario would have to work on the following queue:
-
-```js
-// delete entry 1
-browser().navigateTo('/entries');
-element('table tbody').query(function (tbody, done) { ... });
-element('table tbody a');
-element('.btn-danger').click();
-
- // delete entry 2
- // indented to represent "recursion depth"
- browser().navigateTo('/entries');
- element('table tbody').query(function (tbody, done) { ... });
- element('table tbody a');
- element('.btn-danger').click();
-```
+See the [angular-seed](https://github.com/angular/angular-seed) project for more examples, or look
+at the embedded examples in the Angular documentation (For example, [$http](http://docs.angularjs.org/api/ng/service/$http)
+has an end to end test in the example under the `protractor.js` tag).
## Caveats
-`ngScenario` does not work with apps that manually bootstrap using `angular.bootstrap`. You must use the `ng-app` directive.
+Protractor does not work out-of-the-box with apps that manually bootstrap manually using
+`angular.bootstrap`. You must use the `ng-app` directive.