ngx-speculoos helps you write simpler, cleaner unit tests for your Angular components, based on the page object pattern. It also provides utilities to make writing Angular unit tests easier.
The library simply wraps the standard Angular ComponentFixture, and you should thus be able to understand and start using ngx-speculoos in just a few minutes if you already know how to write Angular unit tests.
If you've ever written tests like the following:
it('should display French cities when selecting the country France', () => {
const countrySelect = fixture.nativeElement.querySelector('#country'); // countrySelect is of type any
countrySelect.selectedIndex = 12; // what is at index 12?
countrySelect.dispatchEvent(new Event('change')); // why do I need to do that?
fixture.detectChanges();
const city = fixture.nativeElement.querySelector('#city'); // city is of type any
expect(city).toBeTruthy();
expect(city.options.length).toBe(3);
expect(city.options[0].value).toBe('');
expect(city.options[0].label).toBe('');
expect(city.options[1].value).toBe('PARIS');
expect(city.options[1].label).toBe('Paris');
expect(city.options[2].value).toBe('LYON');
expect(city.options[2].label).toBe('Lyon');
});
it('should hide cities when selecting the empty country option', () => {
const countrySelect = fixture.nativeElement.querySelector('#country'); // I did that previously. What about DRY?
countrySelect.selectedIndex = 0;
countrySelect.dispatchEvent(new Event('change')); // why do I need to do that?
fixture.detectChanges(); // why do I need to do that?
expect(fixture.nativeElement.querySelector('#city')).toBeFalsy(); // I did that previously. What about DRY?
});
ngx-speculoos allows writing the above tests in a simpler, cleaner way:
class MyComponentTester extends ComponentTester<MyComponent> {
constructor() {
super(MyComponent);
}
get country() {
return this.select('#country'); // returns a TestSelect object, not any. Similar methods exist for inputs, buttons, etc.
}
get city() {
return this.select('#city'); // returns a TestSelect object, not any
}
}
[...]
it('should display French cities when selecting the country France', () => {
tester.country.selectLabel('France'); // no dispatchEvent, no detectChanges needed
expect(tester.city.optionValues).toEqual(['', 'PARIS', 'LYON']);
expect(tester.city.optionLabels).toEqual(['', 'Paris', 'Lyon']);
});
it('should hide cities when selecting empty country option', () => {
tester.country.selectIndex(0); // no repetition of the selector, no dispatchEvent, no detectChanges needed
expect(tester.city).toBeFalsy(); // no repetition of the selector
});
If you use Jasmine, we also have custom matchers for frequent expectations:
beforeEach(() => jasmine.addMatchers(speculoosMatchers));
it('should contain a pre-populated form', () => {
expect(tester.informationMessage).toContainText('Please check that everything is correct');
expect(tester.country).toHaveSelectedValue('FR');
expect(tester.city).toHaveSelectedLabel('Paris');
expect(tester.name).toHaveValue('Doe');
expect(tester.newsletter).toBeChecked();
});
The complete matcher list includes:
toHaveClass(className: string)
toHaveValue(value: string)
toHaveText(textContent: string)
toContainText(textContent: string)
toBeChecked()
toHaveSelectedIndex(index: number)
toHaveSelectedValue(value: string)
toHaveSelectedLabel(label: string)
toBeVisible()
Using the CLI: ng add ngx-speculoos
Using npm: npm install --save-dev ngx-speculoos
Using yarn: yarn add --dev ngx-speculoos
MyComponentTester
class (in your my-component.spec.ts
file, typically) extending
ComponentTester<MyComponent>
, as shown above.element
, elements
, input
, select
, textarea
, button
).
See the API documentation for detailstest.ts
file.Please, provide feedback by filing issues, or by submitting pull requests, to the Github Project.
You can look at a minimal complete example in the demo project.