diff --git a/doc/article/en-US/end-to-end-testing.md b/doc/article/en-US/end-to-end-testing.md deleted file mode 100644 index 2834967..0000000 --- a/doc/article/en-US/end-to-end-testing.md +++ /dev/null @@ -1,306 +0,0 @@ ---- -name: End to End Testing -description: Testing front-end applications has become an important task for today's developers. Not only do you want to ensure that your application's internal code operates correctly, but you also want to validate the in-browser behavior. This article will show you how to achieve this by combining Aurelia and Protractor. -author: Vildan Softic (https://github.com/zewa666) ---- -## What is E2E testing? - -End-To-End (E2E) testing is all about testing your application against specific _scenarios_. From a requirements-engineering-perspective you'd call those _User Stories_. Essentially, these stories describe a series of actions a user performs to achieve a certain goal. So by assembling a bunch of these - referred to as a _test suite_ - you are able to verify that your web app acts as expected. The key to success, of course, lies in the amount and quality of tests written. - -With E2E testing you are not interacting with the app's code per se, but with the app's interface. This is different than unit tests, which take care of isolated parts of the application - called _units_ - by verifying them through the removal or mocking of other parts. It's important to note that one method of testing does not replace the other, so don't take this article as an excuse to skip unit testing. - -## How are E2E tests different? - -One of the key differences when working with E2E tests is that all of your work is located in the browser, which naturally leads to writing a lot of _asynchronous code_. It doesn't matter whether you request a DOM Element, send some fake keystrokes or trigger a click, each of these actions needs to be automatically translated to understandable instructions and sent to the browser under test. So working with `Promises` becomes a major enabler when keeping track of deferred executions and responses. - -Another important aspect already noted is the necessity to translate programmatic actions into browser understandable ones. Needless to say, variations exist between the different browsers... - -When performing E2E tests you also need to take into consideration that you are actually testing a real web page. So, compared to unit tests, you will additionally need your web page up and accessible by the browser during test execution. Keep in mind that E2E tests, sometimes also referred to as _integration tests_, test your app's system as a whole. Thus, modifications which get persisted to databases or local storage will stay that way and may produce side effects for your next test run. - -Last but not least, there is a much higher test code maintenance cost, compared to unit tests. The reason is that now, not only one component is tested exclusively, but rather the whole system at once. Imagine trying to fill out an input element with the id `txtFirstname`, just to realize the next day your tests fail because your fellow front-end designer decided to change the name to `txtFirstName`. This makes it clear that you must treat your test code like general application logic and give it all the love it deserves. - -## Protractor - -Although the previous section may sound depressing, there is hope for developers in [Protractor](http://angular.github.io/protractor/#/), an End-To-End testing framework. Under the hood it's actually a Node.js application, which supports a wide variety of assertion/test libraries like [Jasmine](http://jasmine.github.io/), [Mocha](http://mochajs.org/) or [Cucumber](https://github.com/cucumber/cucumber-js). - ->Info: Jasmine ->The remainer of the article will use the BDD Testing Framework Jasmine. A nice tutorial on how to use Jasmine can be [found here](http://code.tutsplus.com/tutorials/testing-your-javascript-with-jasmine--net-21229). - -Protractor acts as a wrapper on top of the WebDriverJS-API, which is used to communicate with a Selenium Server, either local (standalone) or remote. The picture below depicts the whole process. Protractor communicates via the WebDriverJS-API (the way to talk to Selenium's WebDriver API) which is used to translate user interactions into browser understandable instructions. - -![Protractor Diagram](img/protractor.png) - -Now the interesting thing is that instead of manually testing your application in each of the major browsers, automated Protractor tests can run on multiple browsers at the same time, saving you valuable time and money. Support is wide-spread and even includes headless browsers like [PhantomJS](http://phantomjs.org/). - -Besides that, being a wrapper, it offers additional convenience features, not present in the vanilla WebDriverJS-API. One feature, perhaps the most important, is that it allows you to write asynchronous tests in a synchronous style. This means that Protractor will automatically execute the next task, the moment the previous pending tasks finish. - -## A Basic Example - -To get a basic idea of how this works, take a look at the following example. - - - - describe('aurelia homepage', () => { - it('should load page', () => { - browser.get('http://www.aurelia.io'); - expect(browser.getTitle()).toEqual('Home | Aurelia'); - }); - }); - - - -As you can see, the test utilizes Jasmine for BDD style testing which is placed in a separate JavaScript file and defines a scenario/suite by using a `describe` block. Each test then gets handled by a separate `it` function. In this one we'd like to verify that after loading the Aurelia Homepage, the title equals our expected page title. The first line will issue a general `browser` method `get` which loads the given URL. This function now returns a promise, to which you'd normally append a `then` function, which gets called after the promise successfully resolves. In this test case though, we don't need to care about that, because Protractor will execute the expectation only after the previous line has successfully resolved. Protractor also adapts the Jasmine expectations to work in an async way, so by the time matchers like `toEqual` are called, the previous expectation is already resolved. - -But sometimes you need to wait for a certain action to happen in the future. Again we can leverage the general browser object and utilize it's `sleep` method. - - - - describe('aurelia homepage', () => { - it('should navigate to a different subpage', () => { - // load page - // navigate to different subpage - // wait for X to happen after 2 seconds - browser.sleep(2000) - expect(WHATEVER).toEqual(SOMETHING); - }); - }); - - - -## Accessing DOM Elements - -Great! So we know how to load a page. But how do we find DOM Elements and see whether they are rendered properly? Protractor provides the global object `element`, an `ElementFinder`, which offers a *locator factory* `by` used to define a way to search for elements. Let's take a look at the following example. - - - - describe('aurelia homepage', () => { - beforeEach(() => { - browser.get('http://www.aurelia.io'); - }); - - it('should have proper header text set', () => { - expect(element(by.tagName('h2')).getText()).toBe('EXPECTED HEADER'); - }); - - it('should find an about section', () => { - expect(element(by.id('about')).isPresent()).toBe(true); - }); - }); - - - -The first test is looking for an `

` tag by utilizing the `tagName` *locator*. The second test looks for an element with the ID `about` and expects it to be rendered on the page. Here we use the `isPresent` method, provided by the `ElementFinder`. - -You may have noticed the method `beforeEach` at the top of the describe block. This is a setup method, which will get called before each test in the current describe block. To perform _tear down_ operations, you'd simply define a function `afterEach`, which gets called after each test. - ->Info: Protractor Locators ->You can find a full list of _locators_ [here](http://angular.github.io/protractor/#/locators). Just keep in mind that everything specific to AngularJS, like `binding` or `model` won't work with Aurelia Apps. We have our own helpers for that. - -## Interacting with Forms - -Now we know how to work with general elements, but what about inputs? Wouldn't it be nice to fake data entries in order to verify the logic of a form? To do so, let's look at the next example. Our test will navigate to the Google homepage, search for a specific keyword, trigger the search and expect to see an element containing the given value. - - - - describe('google homepage', () => { - beforeEach(() => { - browser.get('http://www.google.com'); - }); - - it('should load page', () => { - element(by.name('q')).sendKeys('Aurelia'); - element(by.name('btnG')).click(); - - browser.sleep(2000); - expect(element(by.css('h3 a')).getText()).toContain('Aurelia'); - }); - }); - - - -First we navigate to the page using `browser.get` and look for an input with the name `q`. The `sendKeys` method now simulates the keystrokes for the keyword _Aurelia_. Afterwards we perform a search by clicking the button named `btnG`. Now we need to wait for Google to perform the search and render the result. We therefore leverage the `browser.sleep` method to give it some time. Finally we look for a link containing the word _Aurelia_. - -## Protractor and Aurelia - -In order to work with Protractor, there is a little configuration that is necessary. This is done in a configuration file, e.g. protractor.conf.js, which sets up the basic information for Protractor so it can find our test files, start the standalone Selenium server and wire up the `JasmineOptions` for the console output. The Aurelia [Skeleton Navigation App](https://github.com/aurelia/skeleton-navigation) thankfully already shares a pre-configured setup. Let's take a look at it. - - - - exports.config = { - directConnect: true, - - // Capabilities to be passed to the webdriver instance. - capabilities: { - 'browserName': 'chrome' - }, - - //seleniumAddress: 'http://0.0.0.0:4444', - specs: ['test/e2e/dist/*.js'], - - plugins: [{ - path: 'aurelia.protractor.js' - }], - - - // Options to be passed to Jasmine-node. - jasmineNodeOpts: { - showColors: true, - defaultTimeoutInterval: 30000 - } - }; - - - -The first setting tells Protractor to directly connect to the Browser leveraging its WebDriver, in this case, Chrome, defined by the `capabilities` property. By doing so, Protractor won't need a Selenium Server and will talk directly to the mentioned Browser. - -The method `plugins` points to the Aurelia specific implementation of custom matchers and helpers and sets those up before Protractor starts. One of them is the valueBind custom locator, which searches for Aurelia value bindings.The option `seleniumAddress` now may be omitted since we are using `directConnect`. It typically would point to a remotely running Selenium instance, helpful in remote testing scenerios. - -The property `specs` takes the path to our spec files. Since Aurelia is built from ground up with full support for ES6, we encourage developers to write their tests using ES6 features. - -Since we'd like to start tests only when Aurelia is fully loaded, we leverage another plugin method called `loadAndWaitForAureliaPage`. - - - - beforeEach(() => { - browser.loadAndWaitForAureliaPage('http://localhost:9000'); - }); - - - -This helper uses a Protractor feature to listen for a DOM event fired by Aurelia after initial view composition. By placing this in a `beforeEach` section, we ensure that none of the tests will be started before the async script successfully finishes. - -## Testing the Aurelia Skeleton Navigation App - -Besides having the configuration file set up, the Skeleton Navigation App also defines a set of demo tests to help you get started with testing your own page. First you'd need to download the App directly from our [Github-Repo](https://github.com/aurelia/skeleton-navigation) and follow the installation instructions. Afterwards, in order to start E2E testing, simply open up a console and run the following command to start up the built-in web server: - - - - gulp watch - - - -After that, open another console and hit the following command to start up the E2E test run: - - - - gulp e2e - - - -You will find the demo spec in the folder `test/e2e/src/`. - -## Page Objects - -To conclude this article we're going to quickly look at how to structure tests. We organize our test methods using a pattern called _Page Objects_ (POs). What this means is that you try to group information about _how_ you access parts of the application into a separate class. This makes it simple to access specific elements multiple times. Now instead of repeating the `element.by.xxx` code over and over across multiple tests, we unify the access, making it easier to maintain and modify. - -Since Aurelia promotes the use of ES6, our page objects are simple ES6 classes, exposing functionality through methods. These methods contain the logic for how to interact with Protractor. The following example shows our main Skeleton PO, which takes care of general application information like the page title and page navigation. - - - - export class PageObjectSkeleton { - getCurrentPageTitle() { - return browser.getTitle(); - } - - navigateTo(href) { - element(by.css('a[href="' + href + '"]')).click(); - return browser.waitForRouterComplete(); - } - } - - - -One notable thing is the usage of the Aurelia specific function `waitForRouterComplete`. It will wait for a custom DOM event dispatched by Aurelia's router to determine, when the page has been fully loaded and displayed. - -The second PO is all about the Welcome page. - - - - export class PageObjectWelcome { - getGreeting() { - return element(by.tagName('h2')).getText(); - } - - setFirstname(value) { - return element(by.valueBind('firstName')).clear().sendKeys(value); - } - - setLastname(value) { - return element(by.valueBind('lastName')).clear().sendKeys(value); - } - - getFullname() { - return element(by.css('.help-block')).getText(); - } - - pressSubmitButton() { - return element(by.css('button[type="submit"]')).click(); - } - - openAlertDialog() { - return browser.wait(() => { - this.pressSubmitButton(); - - return browser.switchTo().alert().then( - function(alert) { alert.accept(); return true; }, - function() { return false; } - ); - }); - } - } - - - -## Test Specification - -The previously defined page objects can now be imported into our test specification by leveraging the ES6 `import` syntax. Using `beforeEach` we can instantiate the POs, navigate to the Web app and wait for the previously mentioned `aurelia-composed` event to start testing. - -Our page object methods, in combination with Jasmine's BDD style assertions, make each test become an easy to read English sentence. - - - - import {PageObjectWelcome} from './welcome.po.js'; - import {PageObjectSkeleton} from './skeleton.po.js'; - - describe('aurelia skeleton app', () => { - let poWelcome; - let poSkeleton; - - beforeEach(() => { - poSkeleton = new PageObjectSkeleton(); - poWelcome = new PageObjectWelcome(); - - browser.loadAndWaitForAureliaPage('http://localhost:9000'); - }); - - it('should load the page and display the initial page title', () => { - expect(poSkeleton.getCurrentPageTitle()).toBe('Welcome | Aurelia'); - }); - - it('should display greeting', () => { - expect(poWelcome.getGreeting()).toBe('Welcome to the Aurelia Navigation App!'); - }); - - it('should automatically write down the fullname', () => { - poWelcome.setFirstname('Rob'); - poWelcome.setLastname('Eisenberg'); - - browser.sleep(200); // wait for async bindings to complete - expect(poWelcome.getFullname()).toBe('ROB EISENBERG'); - }); - - it('should show alert message when clicking submit button', () => { - expect(poWelcome.openAlertDialog()).toBe(true); - }); - - it('should navigate to users page', () => { - poSkeleton.navigateTo('#/users'); - expect(poSkeleton.getCurrentPageTitle()).toBe('Github Users | Aurelia'); - }); - }); - - - -## Summary - -We hope you enjoyed this introduction to E2E Testing with Protractor and the Aurelia Framework. Time to start writing some tests! diff --git a/doc/article/en-US/img/protractor.png b/doc/article/en-US/img/protractor.png deleted file mode 100644 index f3bc94e..0000000 Binary files a/doc/article/en-US/img/protractor.png and /dev/null differ diff --git a/doc/article/en-US/testing-components.md b/doc/article/en-US/testing-components.md deleted file mode 100644 index 787661b..0000000 --- a/doc/article/en-US/testing-components.md +++ /dev/null @@ -1,431 +0,0 @@ ---- -name: Testing Components -description: An overview of how to unit test Custom Elements and Custom Attributes. -author: Martin Gustafsson (http://github.com/martingust) ---- -## Introduction - -With the Component Tester you can easily stage a custom element or custom attribute in isolation inside a mini Aurelia application, assert how it responds to data-binding and assert its behavior throughout the component's lifecycle (bind, attached etc). - -## Getting Started - -If you are using JSPM: - -```Shell -jspm install aurelia-testing -``` - -If you are using NPM: - -```Shell -npm install aurelia-testing -``` - -Once you've got the library installed, you can use it in a unit test. In the following examples we will be using Jasmine, but any testing framework would work. - -## Testing a Custom Element - -Let's start with a simple custom element that we want to test: - - - - - - - - - - import {bindable} from 'aurelia-framework'; - - export class MyComponent { - @bindable firstName; - } - - - -In order to test that the component renders expected HTML, based on what the view is bound to, we can write following test: - - - - import {StageComponent} from 'aurelia-testing'; - import {bootstrap} from 'aurelia-bootstrapper'; - - describe('MyComponent', () => { - let component; - - beforeEach(() => { - component = StageComponent - .withResources('my-component') - .inView('') - .boundTo({ firstName: 'Bob' }); - }); - - it('should render first name', done => { - component.create(bootstrap).then(() => { - const nameElement = document.querySelector('.firstName'); - expect(nameElement.innerHTML).toBe('Bob'); - done(); - }).catch(e => { console.log(e.toString()) }); - }); - - afterEach(() => { - component.dispose(); - }); - }); - - - -Running the test should result in the following html should be rendered `
Bob
` and the test should pass. But let's take a step back and see what is going on here. First, we import `StageComponent` from `aurelia-testing`: - - - - import {StageComponent} from 'aurelia-testing'; - - - -`StageComponent` is just a convenience factory that creates a new instance of the `ComponentTester` class. `ComponentTester` is the actual class doing all the work. Next we use the `StageComponent` factory to stage our component: - - - - component = StageComponent - .withResources('src/my-component') - .inView('') - .boundTo({ firstName: 'Bob' }); - - - -`StageComponent` comes with one property, `withResources`, that lets you start off the staging with a fluent API. `withResources` lets you specify which resource or resources for Aurelia to register. It takes either a string for registering one single resource or an Array of strings for registering multiple resources. `inView` lets you provide the html markup to be run. This is just a standard Aurelia view where you can do all the data binding you are used to in a full-blown Aurelia application. `boundTo` lets you provide a test `viewModel` with the data that the view will get bound to. In this example, the staging of the component is done in Jasmine's `beforeEach` method in order to reuse the same setup for multiple tests. - -> Info -> If you are using `karma` and your configuration already has a path for `'*': 'src/*'` set you may not need to use `src/`, and just `my-component`. - -Next, we come to the actual test where we call `create` on the `ComponentTester`. Create will kick everything off and bootstrap the mini Aurelia application, configure it with `standardConfiguration` (we will take a look later at how you can run with your own configuration), register provided resources as global resources, start the application and finally render your component so you can assert the expected behavior. In this case, we want to make sure our `firstName` property gets rendered correctly in the HTML by selecting the `div` tag via it's class name. We use `document.querySelector('.firstName');` to grab that and then check that its innerHTML is `Bob`. Next we call Jasmine's `done` function to tell Jasmine that the test is complete. Calling `done` is needed since the `create` method is asynchronous and returns a Promise. - -Finally, we call `dispose` on our `ComponentTester` instance. This will clean up the DOM so our next test will start out with a clean document. That's pretty much all there is to it. Easy right? Imagine doing the same assert with stand alone unit tests that run outside of Aurelia. It would be pretty difficult, especially for a more complex component. - - -## Manually handling lifecycle - -When testing a component sometimes you want to have tests run at certain points of the lifecycle. To do this we can tell the component we created that we will manually handle the lifecycle methods - - - - - import {StageComponent} from 'aurelia-testing'; - import {bootstrap} from 'aurelia-bootstrapper'; - - describe('MyComponent', () => { - let component; - - beforeEach(() => { - component = StageComponent - .withResources('src/my-component') - .inView('') - .boundTo({ firstName: 'Bob' }); - }); - - it('can manually handle lifecycle', done => { - let nameElement; - - component.manuallyHandleLifecycle().create(bootstrap) - .then(() => { - nameElement = document.querySelector('.name'); - expect(nameElement.innerHTML).toBe(' '); - }) - .then(() => component.bind()) - .then(() => { - expect(nameElement.innerHTML).toBe('Foo bind'); - }) - .then(() => component.attached()) - .then(() => { - expect(nameElement.innerHTML).toBe('Foo attached'); - }) - .then(() => component.detached()) - .then(() => component.unbind()) - .then(() => { - expect(component.viewModel.name).toBe(null); - }) - .then(() => component.bind({ name: 'Bar' })) - .then(() => { - expect(nameElement.innerHTML).toBe('Bar bind'); - }) - .then(() => component.attached()) - .then(() => { - expect(nameElement.innerHTML).toBe('Bar attached'); - }) - .then(done); - }); - - afterEach(() => { - component.dispose(); - }); - - }); - - - -As you see, the test helper lets you easily push components through their lifecycle, testing various aspects of it at each point along the way. - -## Testing a Custom Attribute - -Testing a Custom Attribute is not much different than testing a Custom Element. Let's look at how it's done by starting with a simple example custom attribute that lets you change the background color of the element it is placed on: - - - - export class MyAttributeCustomAttribute { - static inject = [Element]; - constructor(element) { - this.element = element; - } - - valueChanged(newValue){ - this.element.style.backgroundColor = newValue; - } - } - - - -Now, let's assert that the element actually gets the background color it is bound to: - - - - import {StageComponent} from 'aurelia-testing'; - import {bootstrap} from 'aurelia-bootstrapper'; - - describe('MyAttribute', () => { - let component; - - beforeEach(() => { - component = StageComponent - .withResources('src/my-attribute') - .inView('
Bob
') - .boundTo({ color: 'blue' }); - }); - - it('should set the background color to provided color', done => { - component.create(bootstrap).then(() => { - expect(component.element.style.backgroundColor).toBe('blue'); - done(); - }).catch(e => console.log(e.toString())); - }); - - afterEach(() => { - component.dispose(); - }); - }); -
-
- -As you can see, everything follows the same pattern we had for our custom element test. One exception is that we take advantage of the `element` property which gets provided by the `ComponentTester` instance. The `element` property is the actual HTML element that gets rendered. This can also be used when testing custom elements. - -## Testing custom component with a real view-model - -If you want to test a custom component with a real view-model, mocking -out all dependencies, you can do this as well. A common scenario is -to test the view/view-model, mocking out service calls to the backend. - -If the view model has a dependency on a class called Service for all backend communication: - - - - export class MockService { - firstName; - - getFirstName() { return Promise.resolve(this.firstName); - } - - describe('MyComponent', () => { - let component; - let service = new MockService(); - - beforeEach(() => { - service.firstName = undefined; - - component = StageComponent - .withResources('src/component') - .inView(''); - - component.bootstrap(aurelia => { - aurelia.use.standardConfiguration(); - - aurelia.container.registerInstance(Service, service); - }); - }); - - it('should render first name', done => { - service.firstName = 'Bob'; - - component.create(bootstrap).then(() => { - const nameElement = document.querySelector('.first-name'); - expect(nameElement.innerHTML).toBe('Bob'); - - done(); - }); - }); - - afterEach(() => { - component.dispose(); - }); - }); - - - -## Using a Real Parent View-model - -If you want to test using a custom element inside of a real parent view-model this can be done just as easily. This can be really helpful when needing to test the state of a parent that is affected by the child custom element or attribute - - - - - import {StageComponent} from 'aurelia-testing'; - import {bootstrap} from 'aurelia-bootstrapper'; - import {MyComponent} from 'src/my-component'; - - describe('MyAttribute', () => { - let component; - let viewModel; - - beforeEach(() => { - viewModel = new MyComponent(); - component = StageComponent - .withResources('src/my-attribute') - .inView('
Bob
') - .boundTo(viewModel); - }); - //... - }); -
-
- -Using this you can also use the `ref` custom attribute to get access to things and check their state in the view-model. - -Or if your view-model has dependencies to load through DI - - - - - import {StageComponent} from 'aurelia-testing'; - import {bootstrap} from 'aurelia-bootstrapper'; - import {MyComponent} from 'src/my-component'; - import {Container} from 'aurelia-dependency-injection'; - import {MyService} from 'src/my-service'; - - describe('MyAttribute', () => { - let component; - let container; - let viewModel; - let myService; - - beforeEach(() => { - container = new Container(); - myService = container.get(MyService); - viewModel = container.get(MyComponent); - component = StageComponent - .withResources('src/my-attribute') - .inView('
Bob
') - .boundTo(viewModel); - }); - //... - }); -
-
- -Now the service dependency for `MyComponent` will be resolved through DI automatically. - -## Improving Readability with Multi-line Strings - -You can improve the readability of your complex views by using template literals in your tests - - - - - import {StageComponent} from 'aurelia-testing'; - import {bootstrap} from 'aurelia-bootstrapper'; - - describe('MyAttribute', () => { - let component; - - beforeEach(() => { - let view = ` -
-
-
Bob
-
-
- `; - component = StageComponent - .withResources('src/my-attribute') - .inView(view) - .boundTo(viewModel); - }); - //... - }); -
-
- -## Helpful Properties and Functions - -The `ComponentTester` exposes a set of properties that can be handy when doing asserts or to stage a component in a specific way. Here's a list of what is available: - -* `element` - The HTML element that gets rendered. -* `viewModel` - The view-model for the component. -* `configure` - The `ComponentTester`'s configure method can be overwritten in order to set it up with custom configuration or get a reference to the `container` instance. -* `dispose` - Cleans up the DOM after a test has run. -* `bind` - Manually handles `bind`. -* `unbind` - Manually handles `unbind`. -* `attached` - Manually handles `attached`. -* `detached` - Manually handles `detached`. -* `waitForElement` and `waitForElements` - Waits until one or several elements are present / absent. See below. - -## Testing complex components - -In some cases, the tested element is not rendered yet when the `component.create()` promise is resolved, and therefore when the actual test starts. For these situations, `aurelia-testing` and `ComponentTester` expose helper methods and functions to wait for tested elements to be present in the page. - -### Waiting for element(s) - -If you want to wait for elements that can be looked up in the DOM using a query passed to `querySelector` or `querySelectorAll`, you can use one of the following: - -* `ComponentTester.waitForElement` or `ComponentTester.waitForElements`: to wait for one or several HTML element(s) within the tested component. The query is carried out using `querySelector` and `querySelectorAll`, respectively. -* `waitForDocumentElement` or `waitForDocumentElements` (imported from `aurelia-testing`): to wait for one or several HTML element(s) within the document, not restricted to the descendants of the tested component. This is especially useful if you want to wait for elements created by third-party libraries such as context menus, date pickers, etc. - -All these methods and functions take 2 arguments: - -* `selector` (mandatory): is a selector string to look up the wanted element(s). It must be compatible with `querySelector` and `querySelectorAll` -* `options` is an object that can have the following properties: - - `present`: `true` to test for presence, `false` for absence (defaults to `true`) - - `interval`: the polling interval (defaults to 50ms) - - `timeout`: the timeout (defaults to 5s) - -They all return a `Promise` that resolves to an `Element` (`waitForElement`) or a `NodeList` (`waitForElements`). The `Promise` is rejected in the event of a timeout. The returned `Promise` can be used to execute some testing code only once a given element has been detected to be present or absent, either because the component was slow to be fully rendered or because the test relies on asynchronous actions such as events or animations. - -### Waiting for matches to complex queries ... or anything else - -If your query is complex (with non-trivial jQuery lookups for example), or you want to wait for the result of a callback to be something else than `null`, you can use the higher-level `waitFor` function imported from `aurelia-testing`. - -`waitFor(getter, options)` works exactly the same way as the previously described methods and functions, but takes a callback (`getter`) as the first argument instead of a selector string. `waitFor` internally calls `getter` with no arguments at regular intervals times until the returned value is anything else than `null`, an empty `NodeList` or jQuery set. The returned `Promise` will resolve to the result of `getter()`. - -### Examples - - - - component.waitForElement('.firstName').then((nameElement) => { - expect(nameElement.innerHTML).toBe('Bob'); - done(); - }); - - - - - - import {waitFor} from 'aurelia-testing'; - - waitFor(() => $('.firstName')).then((nameElement) => { - expect(nameElement.html()).toBe('Bob'); - done(); - }); - - - - - - component.waitForElement('.firstName', {present: false, timeout: 2000}).then(done); - - diff --git a/doc/img/protractor.png b/doc/img/protractor.png deleted file mode 100644 index f3bc94e..0000000 Binary files a/doc/img/protractor.png and /dev/null differ