Skip to content

Commit

Permalink
POC: zoneless support via await until condition in harnesses
Browse files Browse the repository at this point in the history
  • Loading branch information
mmalerba committed Jul 1, 2024
1 parent 35ce605 commit c2a5cd8
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 17 deletions.
34 changes: 26 additions & 8 deletions src/cdk/testing/component-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ export type LocatorFnResult<T extends (HarnessQuery<any> | string)[]> = {
[I in keyof T]: T[I] extends new (...args: any[]) => infer C // Map `ComponentHarnessConstructor<C>` to `C`.
? C
: // Map `HarnessPredicate<C>` to `C`.
T[I] extends {harnessType: new (...args: any[]) => infer C}
? C
: // Map `string` to `TestElement`.
T[I] extends string
? TestElement
: // Map everything else to `never` (should not happen due to the type constraint on `T`).
never;
T[I] extends {harnessType: new (...args: any[]) => infer C}
? C
: // Map `string` to `TestElement`.
T[I] extends string
? TestElement
: // Map everything else to `never` (should not happen due to the type constraint on `T`).
never;
}[number];

/**
Expand Down Expand Up @@ -263,6 +263,13 @@ export interface LocatorFactory {
* authors to wait for async tasks outside of the Angular zone.
*/
waitForTasksOutsideAngular(): Promise<void>;

/**
* Waits for the given condition
*/
until(condition: () => boolean | Promise<boolean>): Promise<void>;

sleep(ms: number): Promise<void>;
}

/**
Expand Down Expand Up @@ -399,6 +406,14 @@ export abstract class ComponentHarness {
protected async waitForTasksOutsideAngular() {
return this.locatorFactory.waitForTasksOutsideAngular();
}

protected async sleep(ms: number) {
return this.locatorFactory.sleep(ms);
}

protected async until(condition: () => boolean | Promise<boolean>) {
return this.locatorFactory.until(condition);
}
}

/**
Expand Down Expand Up @@ -471,7 +486,10 @@ export class HarnessPredicate<T extends ComponentHarness> {
private _descriptions: string[] = [];
private _ancestor: string;

constructor(public harnessType: ComponentHarnessConstructor<T>, options: BaseHarnessFilters) {
constructor(
public harnessType: ComponentHarnessConstructor<T>,
options: BaseHarnessFilters,
) {
this._addBaseOptions(options);
}

Expand Down
13 changes: 12 additions & 1 deletion src/cdk/testing/harness-environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {parallel} from './change-detection';
import {manualChangeDetection, parallel} from './change-detection';
import {
AsyncFactoryFn,
ComponentHarness,
Expand Down Expand Up @@ -160,6 +160,17 @@ export abstract class HarnessEnvironment<E> implements HarnessLoader, LocatorFac
// Part of LocatorFactory interface, subclasses will implement.
abstract waitForTasksOutsideAngular(): Promise<void>;

// Part of LocatorFactory interface, subclasses will implement.
abstract sleep(ms: number): Promise<void>;

async until(condition: () => boolean | Promise<boolean>) {
await manualChangeDetection(async () => {
while (!(await condition())) {
await this.sleep(0);
}
});
}

/** Gets the root element for the document. */
protected abstract getDocumentRoot(): E;

Expand Down
10 changes: 9 additions & 1 deletion src/cdk/testing/testbed/testbed-harness-environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
stopHandlingAutoChangeDetectionStatus,
TestElement,
} from '@angular/cdk/testing';
import {ComponentFixture, flush} from '@angular/core/testing';
import {ComponentFixture, flush, tick} from '@angular/core/testing';
import {Observable} from 'rxjs';
import {takeWhile} from 'rxjs/operators';
import {TaskState, TaskStateZoneInterceptor} from './task-state-zone-interceptor';
Expand Down Expand Up @@ -197,6 +197,14 @@ export class TestbedHarnessEnvironment extends HarnessEnvironment<Element> {
await this._taskState?.pipe(takeWhile(state => !state.stable)).toPromise();
}

async sleep(ms: number) {
if (isInFakeAsyncZone()) {
tick(ms); // TODO: Doesn't work for zoneless + fakeAsync.
} else {
await new Promise(resolve => setTimeout(resolve, ms));
}
}

/** Gets the root element for the document. */
protected getDocumentRoot(): Element {
return document.body;
Expand Down
6 changes: 6 additions & 0 deletions src/cdk/testing/tests/harnesses/main-component-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,16 @@ export class MainComponentHarness extends ComponentHarness {

private _testTools = this.locatorFor(SubComponentHarness);

async isInitialized() {
await this.sleep(1000);
}

async increaseCounter(times: number) {
const button = await this.button();
for (let i = 0; i < times; i++) {
const oldCount = await (await this.asyncCounter()).text();
await button.click();
await this.until(async () => oldCount !== (await (await this.asyncCounter()).text()));
}
}

Expand Down
11 changes: 4 additions & 7 deletions src/cdk/testing/tests/testbed.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {_supportsShadowDom} from '@angular/cdk/platform';
import {HarnessLoader, manualChangeDetection, parallel} from '@angular/cdk/testing';
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
import {waitForAsync, ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing';
import {provideZoneChangeDetection} from '@angular/core';
import {ComponentFixture, TestBed, fakeAsync, waitForAsync} from '@angular/core/testing';
import {querySelectorAll as piercingQuerySelectorAll} from 'kagekiri';
import {crossEnvironmentSpecs} from './cross-environment.spec';
import {FakeOverlayHarness} from './harnesses/fake-overlay-harness';
Expand All @@ -13,9 +12,6 @@ describe('TestbedHarnessEnvironment', () => {
let fixture: ComponentFixture<{}>;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [provideZoneChangeDetection()],
});
fixture = TestBed.createComponent(TestMainComponent);
});

Expand Down Expand Up @@ -83,12 +79,13 @@ describe('TestbedHarnessEnvironment', () => {
expect(element.id).toContain('root');
});

it('should wait for async operation to complete in fakeAsync test', fakeAsync(async () => {
fit('should wait for async operation to complete in fakeAsync test', async () => {
await harness.isInitialized();
const asyncCounter = await harness.asyncCounter();
expect(await asyncCounter.text()).toBe('5');
await harness.increaseCounter(3);
expect(await asyncCounter.text()).toBe('8');
}));
});
});

describe('change detection behavior', () => {
Expand Down

0 comments on commit c2a5cd8

Please sign in to comment.