Skip to content

Commit ddb2960

Browse files
mmalerbajelbourn
authored andcommitted
docs(cdk/testing): add documentation for harness environment authors (#17754)
1 parent 3766065 commit ddb2960

File tree

2 files changed

+82
-2
lines changed

2 files changed

+82
-2
lines changed

src/cdk/testing/test-element.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export interface TestElement {
6363
/** Blur the element. */
6464
blur(): Promise<void>;
6565

66-
/** Clear the element's input (for input elements only). */
66+
/** Clear the element's input (for input and textarea elements only). */
6767
clear(): Promise<void>;
6868

6969
/**

src/cdk/testing/test-harnesses.md

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,4 +549,84 @@ may need to explicitly wait for tasks outside `NgZone`, as this does not happen
549549

550550
### API for harness environment authors
551551

552-
TODO(mmalerba): Fill in docs for harness environment authors
552+
Harness environment authors are developers who want to add support for using component harnesses in
553+
additional testing environments. Out-of-the-box, Angular CDK's component harnesses can be used in
554+
Protractor E2E tests and Karma unit tests. Developers can support additional environments by
555+
creating custom implementations of `TestElement` and `HarnessEnvironment`.
556+
557+
#### Creating a `TestElement` implementation for the environment
558+
559+
The first step in adding support for a new testing environment is to create a `TestElement`
560+
implementation. The `TestElement` interface serves as an environment-agnostic representation of a
561+
DOM element; it lets harnesses interact with DOM elements regardless of the underlying environment.
562+
Because some environments don't support interacting with DOM elements synchronously
563+
(e.g. webdriver), all of `TestElement` methods are asynchronous, returning a `Promise` with the
564+
result of the operation.
565+
566+
| Method | Description |
567+
| ------ | ----------- |
568+
| `blur(): Promise<void>` | Blurs the element. |
569+
| `clear(): Promise<void>` | Clears the text from an element (only applies for `<input>` and `<textarea>`). |
570+
| `click(relativeX?: number, relativeY?: number): Promise<void>` | Clicks an element at a point relative to it's top-left corner. |
571+
| `focus(): Promise<void>` | Focuses the element. |
572+
| `getCssValue(property: string): Promise<string>` | Gets the computed CSS value of the given property for the element. |
573+
| `hover(): Promise<void>` | Hovers the mouse over the element. |
574+
| `sendKeys(...keys: (string \| TestKey)[]): Promise<void>` | Sends a sequence of key events to the element. |
575+
| `sendKeys(modifiers: ModifierKeys, ...keys: (string \| TestKey)[]): Promise<void>` | Sends a sequence of key events to the element, while holding a set of modifier keys. |
576+
| `text(): Promise<string>` | Gets the text content of the element. |
577+
| `getAttribute(name: string): Promise<string \| null>` | Gets the value of the given HTML attribute for the element. |
578+
| `hasClass(name: string): Promise<boolean>` | Checks whether the element has the given class. |
579+
| `getDimensions(): Promise<ElementDimensions>` | Gets the dimensions of the element. |
580+
| `getProperty(name: string): Promise<any>` | Gets the value of the given property for the element. |
581+
| `matchesSelector(selector: string): Promise<boolean>` | Checks whether the given selector matches the element. |
582+
583+
The `TestElement` interface consists largely of methods that resemble methods
584+
available on `HTMLElement`; similar methods exist in most test environments, which makes
585+
implementing the methods fairly straightforward. However, one important difference to note when
586+
implementing the `sendKeys` method, is that the key codes in the `TestKey`
587+
enum likely differ from the key codes used in the test environment. Environment authors should
588+
maintain a mapping from `TestKey` codes to the codes used in the particular testing environment.
589+
590+
The
591+
[`UnitTestElement`](https://github.com/angular/components/blob/master/src/cdk/testing/testbed/unit-test-element.ts#L57)
592+
and
593+
[`ProtractorElement`](https://github.com/angular/components/blob/master/src/cdk/testing/protractor/protractor-element.ts#L67)
594+
implementations in Angular CDK serve as good examples of implementations of this interface.
595+
596+
#### Creating a `HarnessEnvironemnt` implementation for the environment
597+
598+
Test authors use `HarnessEnvironemnt` to create component harness instances for use in tests.
599+
600+
`HarnessEnvironment` is an abstract class that must be extended to create a concrete subclass for
601+
the new environment. When supporting a new test environment, you must create a `HarnessEnvironment`
602+
subclass that add concrete implementations for all abstract members.
603+
604+
You will notice that `HarnessEnvironment` has a generic type parameter: `HarnessEnvironment<E>`.
605+
This parameter, `E`, represents the raw element type of the environment. For example, this parameter
606+
is `Element` for unit test environments.
607+
608+
The following are the abstract methods that must be implemented:
609+
610+
| Method | Description |
611+
| ------ | ----------- |
612+
| `abstract getDocumentRoot(): E` | Gets the root element for the environment (e.g. `document.body`). |
613+
| `abstract createTestElement(element: E): TestElement` | Creates a `TestElement` for the given raw element. |
614+
| `abstract createEnvironment(element: E): HarnessEnvironment` | Creates a `HarnessEnvironment` rooted at the given raw element. |
615+
| `abstract getAllRawElements(selector: string): Promise<E[]>` | Gets all of the raw elements under the root element of the environment matching the given selector. |
616+
| `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). |
617+
| `abstract waitForTasksOutsideAngular(): Promise<void>` | Gets a `Promise` that resolves when the parent zone of `NgZone` is stable. |
618+
619+
In addition to implementing the missing methods, this class should provide a way for test authors to
620+
get `ComponentHarness` instances. The recommended approach is to have a protected constructor and
621+
provide a static method called `loader` that returns a `HarnessLoader` instance. This allows test
622+
authors to write code like: `SomeHarnessEnvironment.loader().getHarness(...)`. Depending on the
623+
needs of the particular environment, the class may provide several different static methods or
624+
require arguments to be passed. (e.g. the `loader` method on `TestbedHarnessEnvironment` takes a
625+
`ComponentFixture`, and the class provides additional static methods called `documentRootLoader` and
626+
`harnessForFixture`).
627+
628+
The
629+
[`TestbedHarnessEnvironment`](https://github.com/angular/components/blob/master/src/cdk/testing/testbed/testbed-harness-environment.ts#L20)
630+
and
631+
[`ProtractorHarnessEnvironment`](https://github.com/angular/components/blob/master/src/cdk/testing/protractor/protractor-harness-environment.ts#L16)
632+
implementations in Angular CDK serve as good examples of implementations of this interface.

0 commit comments

Comments
 (0)