DEV Community

Cover image for Spectacular prerelease v0.5 supports standalone feature tests and uses partial Ivy compilation

Spectacular prerelease v0.5 supports standalone feature tests and uses partial Ivy compilation

Cover art by DALL-E.

In July 2022, we published the version 0.5.0 of Spectacular, the integration testing library for Angular.

Note that for prerelase versions, that is versions <1.0.0, minor version increases indicate breaking changes, for example releasing version 0.4.0 introduces at least one breaking change compared to version 0.3.0. Patch version increases for prerelease versions indicate bugfixes, refactors, performance improvements, and/or feature additions.

Let's discuss the features and breaking changes of these versions.

Partial Ivy compilation

Spectacular version 0.1.0-0.4.0 are compiled as View Engine-compatible bundles requiring the Angular Compatibility Compiler (NGCC) to be consumed.

As of version 0.5.0, Spectacular is partially Ivy compiled as is the recommended practice for Angular libraries targeting Angular <=12.2.0. Starting with the future Angular version that removes NGCC, View Engine-compatible libraries stop working.

The partial compilation is a performance improvement as most of the library is compiled before being published to a package registry. However, component templates are still required to be compiled in the context of an application.

To use partial Ivy compilation, Spectacular version 0.5.0 requires at least Angular 12.

Standalone feature test provider

With the introduction of standalone Angular components and other standalone Angular APIs starting in Angular version 14 where standalone APIs are considered in developer preview, we start exposing standalone APIs in Spectacular.

Spectacular's first standalone API is a standalone provider factory for Spectacular's Feature testing API.

Spectacular version 0.5.0 introduces provideSpectacularFeatureTest that configures the feature-aware navigation services SpectacularFeatureLocation and SpectacularFeatureRouter for standalone Angular features.

Feature testing a standalone Angular feature using Angular Testing Library and Spectacular:

import {
  provideSpectacularFeatureTest,
  SpectacularAppComponent,
  SpectacularFeatureLocation,
  SpectacularFeatureRouter,
} from '@ngworker/spectacular';
import { render, screen } from '@testing-library/angular';
import { Matcher } from '@testing-library/dom';
import userEvent from '@testing-library/user-event';
import { crisisCenterPath } from '@tour-of-heroes/crisis-center';

const findCrisisCenterHomeGreeting = () =>
  screen.findByText(/welcome to the crisis center/i);
const findCrisisLink = (name: Exclude<Matcher, number>) =>
  screen.findByRole('link', {
    name,
  });
const findNameControl = () => screen.findByPlaceholderText(/name/i);
const findSaveButton = () =>
  screen.findByRole('button', { name: /save/i });
const findSelectedCrisis = (name: Matcher) =>
  screen.findByText(name, {
    selector: '.selected a',
  });
const setup = async () => {
  const user = userEvent.setup();
  const {
    fixture: {
      debugElement: { injector },
    },
  } = await render(SpectacularAppComponent, {
    providers: [
      provideSpectacularFeatureTest({
        featurePath: crisisCenterPath,
      }),
    ],
  });

  return {
    location: injector.get(SpectacularFeatureLocation),
    router: injector.get(SpectacularFeatureRouter),
    user,
  };
};

it('Edit crisis from crisis detail', async () => {
  const { location, router, user } = await setup();
  const crisisId = 2;
  await router.navigate(['~', crisisId]);

  await user.clear(await findNameControl());
  await user.type(
    await findNameControl(),
    'The global temperature is rising'
  );
  await user.click(await findSaveButton());

  expect(
    await findSelectedCrisis(/the global temperature is rising/i)
  ).toBeInTheDocument();
  expect(location.path()).toBe(`~/;id=${crisisId};foo=foo`);
});

it('Edit crisis from crisis center home', async () => {
  const { router, user } = await setup();
  await router.navigateByUrl('~/');

  await user.click(
    await findCrisisLink(/procrastinators meeting delayed again/i)
  );

  await user.clear(await findNameControl());
  await user.type(await findNameControl(), 'Coral reefs are dying');
  await user.click(await findSaveButton());

  expect(await findCrisisCenterHomeGreeting()).toBeInTheDocument();
  expect(
    await findSelectedCrisis(/coral reefs are dying/i)
  ).toBeInTheDocument();
});

Enter fullscreen mode Exit fullscreen mode

Comparing the previous example to the following code snippet, the difference is in the setup SIFERS where we use provideSpectacularFeatureTest instead of SpectacularFeatureTestingModule to adjust the options configuring Angular Testing Library's render function.

Feature test setup SIFERS for an Angular feature module using Angular Testing Library and Spectacular:

import {
  SpectacularAppComponent,
  SpectacularFeatureLocation,
  SpectacularFeatureRouter,
  SpectacularFeatureTestingModule,
} from '@ngworker/spectacular';
import { render } from '@testing-library/angular';
import userEvent from '@testing-library/user-event';
import {
  CrisisCenterModule,
  crisisCenterPath,
} from '@tour-of-heroes/crisis-center';

const setup = async () => {
  const user = userEvent.setup();
  const {
    fixture: {
      debugElement: { injector },
    },
  } = await render(SpectacularAppComponent, {
    imports: [
      SpectacularFeatureTestingModule.withFeature({
        featureModule: CrisisCenterModule,
        featurePath: crisisCenterPath,
      }),
    ],
  });

  return {
    location: injector.get(SpectacularFeatureLocation),
    router: injector.get(SpectacularFeatureRouter),
    user,
  };
};
Enter fullscreen mode Exit fullscreen mode

See the source code for Spectacular for more examples using the feature testing API or visit our documentation.

Top comments (0)