|
| 1 | +# Adding harness support for additional testing environments |
| 2 | + |
| 3 | +## Before you start |
| 4 | + |
| 5 | +Tip: This guide assumes you've already read the [component harnesses overview guide](guide/testing/component-harnesses-overview). Read that first if you're new to using component harnesses. |
| 6 | + |
| 7 | +### When does adding support for a test environment make sense? |
| 8 | + |
| 9 | +To use component harnesses in the following environments, you can use Angular CDK's two built-in environments: |
| 10 | +- Unit tests |
| 11 | +- WebDriver end-to-end tests |
| 12 | + |
| 13 | +To use a supported testing environment, read the [Creating harnesses for your components guide](guide/testing/creating-component-harnesses). |
| 14 | + |
| 15 | +Otherwise, to add support for other environments, you need to define how to interact with a DOM element and how DOM interactions work in your environment. Continue reading to learn more. |
| 16 | + |
| 17 | +### CDK Installation |
| 18 | + |
| 19 | +The [Component Dev Kit (CDK)](https://material.angular.io/cdk/categories) is a set of behavior primitives for building components. To use the component harnesses, first install `@angular/cdk` from npm. You can do this from your terminal using the Angular CLI: |
| 20 | + |
| 21 | +<docs-code language="shell"> |
| 22 | + ng add @angular/cdk |
| 23 | +</docs-code> |
| 24 | + |
| 25 | +## Creating a `TestElement` implementation |
| 26 | + |
| 27 | +Every test environment must define a `TestElement` implementation. The `TestElement` interface serves as an environment-agnostic representation of a DOM element. It enables harnesses to interact with DOM elements regardless of the underlying environment. Because some environments don't support interacting with DOM elements synchronously (e.g. WebDriver), all `TestElement` methods are asynchronous, returning a `Promise` with the result of the operation. |
| 28 | + |
| 29 | +`TestElement` offers a number of methods to interact with the underlying DOM such as `blur()`, `click()`, `getAttribute()`, and more. See the [TestElement API reference page](https://material.angular.io/cdk/test-harnesses/api#TestElement) for the full list of methods. |
| 30 | + |
| 31 | +The `TestElement` interface consists largely of methods that resemble methods available on `HTMLElement`. Similar methods exist in most test environments, which makes implementing the methods fairly straightforward. However, one important difference to note when implementing the `sendKeys` method, is that the key codes in the `TestKey` enum likely differ from the key codes used in the test environment. Environment authors should maintain a mapping from `TestKey` codes to the codes used in the particular testing environment. |
| 32 | + |
| 33 | +The [UnitTestElement](https://github.com/angular/components/blob/main/src/cdk/testing/testbed/unit-test-element.ts#L33) and [SeleniumWebDriverElement](https://github.com/angular/components/blob/main/src/cdk/testing/selenium-webdriver/selenium-webdriver-keys.ts#L16) implementations in Angular CDK serve as good examples of implementations of this interface. |
| 34 | + |
| 35 | +## Creating a `HarnessEnvironment` implementation |
| 36 | +Test authors use `HarnessEnvironment` to create component harness instances for use in tests. `HarnessEnvironment` is an abstract class that must be extended to create a concrete subclass for the new environment. When supporting a new test environment, create a `HarnessEnvironment` subclass that adds concrete implementations for all abstract members. |
| 37 | + |
| 38 | +`HarnessEnvironment` has a generic type parameter: `HarnessEnvironment<E>`. This parameter, `E`, represents the raw element type of the environment. For example, this parameter is Element for unit test environments. |
| 39 | + |
| 40 | +The following are the abstract methods that must be implemented: |
| 41 | + |
| 42 | +| Method | Description | |
| 43 | +|:--- | :--- | |
| 44 | +| `abstract getDocumentRoot(): E` | Gets the root element for the environment (e.g. `document.body`). | |
| 45 | +| `abstract createTestElement(element: E): TestElement` | Creates a `TestElement` for the given raw element. | |
| 46 | +| `abstract createEnvironment(element: E): HarnessEnvironment` | Creates a `HarnessEnvironment` rooted at the given raw element. | |
| 47 | +| `abstract getAllRawElements(selector: string): Promise<E[]>` | Gets all of the raw elements under the root element of the environment matching the given selector. | |
| 48 | +| `abstract forceStabilize(): Promise<void>` | Gets a `Promise` that resolves when the `NgZone` is stable. Additionally, if applicable, tells `NgZone` to stabilize (e.g. calling `flush()` in a `fakeAsync` test). | |
| 49 | +| `abstract waitForTasksOutsideAngular(): Promise<void>` | Gets a `Promise` that resolves when the parent zone of `NgZone` is stable. | |
| 50 | + |
| 51 | +In addition to implementing the missing methods, this class should provide a way for test authors to get `ComponentHarness` instances. You should define a protected constructor and provide a static method called `loader` that returns a `HarnessLoader` instance. This allows test authors to write code like: `SomeHarnessEnvironment.loader().getHarness(...)`. Depending on the needs of the particular environment, the class may provide several different static methods or require arguments to be passed. (e.g. the `loader` method on `TestbedHarnessEnvironment` takes a `ComponentFixture`, and the class provides additional static methods called `documentRootLoader` and `harnessForFixture`). |
| 52 | + |
| 53 | +The [`TestbedHarnessEnvironment`](https://github.com/angular/components/blob/main/src/cdk/testing/testbed/testbed-harness-environment.ts#L89) and [SeleniumWebDriverHarnessEnvironment](https://github.com/angular/components/blob/main/src/cdk/testing/selenium-webdriver/selenium-web-driver-harness-environment.ts#L71) implementations in Angular CDK serve as good examples of implementations of this interface. |
| 54 | + |
| 55 | +## Handling auto change detection |
| 56 | +In order to support the `manualChangeDetection` and parallel APIs, your environment should install a handler for the auto change detection status. |
| 57 | + |
| 58 | +When your environment wants to start handling the auto change detection status it can call `handleAutoChangeDetectionStatus(handler)`. The handler function will receive a `AutoChangeDetectionStatus` which has two properties `isDisabled` and `onDetectChangesNow()`. See the [AutoChangeDetectionStatus API reference page](https://material.angular.io/cdk/test-harnesses/api#AutoChangeDetectionStatus) for more information. |
| 59 | +If your environment wants to stop handling auto change detection status it can call `stopHandlingAutoChangeDetectionStatus()`. |
0 commit comments