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();
});
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,
};
};
See the source code for Spectacular for more examples using the feature testing API or visit our documentation.
Top comments (0)