@s-libs/ng-jasmine
    Preparing search index...

    Class ComponentContext<T>

    Provides the foundation for an opinionated pattern for component tests.

    • Includes all features from AngularContext
    • Automatically creates your component at the beginning of run().
    • Sets up Angular to call ngOnChanges() like it would in production. This is not the case if you use the standard TestBed.createComponent() directly.
    • Wraps your component in a parent that you can easily style however you like.
    • Lets you use component harnesses in the fakeAsync zone, which is normally a challenge.
    • Causes async APP_INITIALIZERs to complete before instantiating the component. Two caveats:
      • this requires all work in your initializers to complete with a call to tick()
      • this requires delaying app initialization until inside the fakeAsync zone, i.e. with the callback to #run. If you have async initializers, you must be careful not to do things that finalize the app setup before then, such as #inject.

    A very simple example:

    @Component({ template: 'Hello, {{name()}}!' })
    class GreeterComponent {
    readonly name = input.required<string>();
    }

    it('greets you by name', () => {
    const ctx = new ComponentContext(GreeterComponent);
    ctx.assignInputs({ name: 'World' });
    ctx.run(() => {
    expect(ctx.fixture.nativeElement.textContent).toBe('Hello, World!');
    });
    });

    A full example, with routing and a component harness. This is the full code for a tiny Angular app:

     /////////////////
    // app-context.ts

    // To re-use your context setup, make a subclass of ComponentContext to import into any spec
    class AppContext extends ComponentContext<AppComponent> {
    constructor() {
    // Import `appConfig` from `app.config.ts`
    super(AppComponent, appConfig);
    }
    }

    ////////////////////////
    // app.component.spec.ts

    describe('AppComponent', () => {
    let ctx: AppContext;
    beforeEach(() => {
    ctx = new AppContext();
    });

    it('can navigate to the first page', () => {
    ctx.run(async () => {
    const app = await ctx.getHarness(AppComponentHarness);
    await app.navigateToFirstPage();
    expect(ctx.fixture.nativeElement.textContent).toContain(
    'First works!',
    );
    });
    });
    });

    ///////////////////////////
    // app.component.harness.ts

    // A simple component harness to demonstrate its integration with component contexts
    class AppComponentHarness extends ComponentHarness {
    static hostSelector = 'app-root';

    #getFirstPageLink = this.locatorFor('a');

    async navigateToFirstPage(): Promise<void> {
    const link = await this.#getFirstPageLink();
    await link.click();
    }
    }

    /////////////////////
    // first.component.ts

    // A minimal component for demonstration purposes
    @Component({ template: '<p>First works!</p>' })
    class FirstComponent {}

    ///////////////////
    // app.component.ts

    // A minimal app component with routing for demonstration purposes
    @Component({
    selector: 'app-root',
    imports: [RouterOutlet, RouterLink],
    template: `
    <a routerLink="/first-page">First Page</a>
    <router-outlet />
    `,
    })
    class AppComponent {}

    ////////////////////////
    // app.routes.ts

    const routes: Routes = [{ path: 'first-page', component: FirstComponent }];

    ////////////////
    // app.config.ts

    const appConfig: ApplicationConfig = { providers: [provideRouter(routes)] };

    Type Parameters

    • T

    Hierarchy (View Summary)

    Index

    Constructors

    Properties

    fixture: ComponentFixture<unknown>

    The ComponentFixture for a synthetic wrapper around your component. Available with the callback to run().

    startTime: Date = ...

    Set this before calling run() to mock the time at which the test starts.

    Methods

    • Assign css styles to the div wrapping your component. Can be called before or during run(). Accepts an object with the same structure as the ngStyle directive.

      ctx.assignWrapperStyles({
      width: '400px',
      height: '600px',
      margin: '20px auto',
      border: '1px solid',
      });

      Parameters

      • styles: Record<string, any>

      Returns void

    • Runs test in a fakeAsync zone. It can use async/await, but be sure anything you await is already due to execute (e.g. if a timeout is due in 3 seconds, call .tick(3000) before awaiting its result).

      Also runs the following in this order, all within the same zone:

      1. this.init()
      2. test()
      3. this.verifyPostTestConditions()
      4. this.cleanUp()

      Parameters

      • test: () => void | Promise<void>

      Returns void