Skip to content

Queries

The Spectator API includes convenient methods for querying the DOM as part of a test: query, queryAll, queryLast , queryHost and queryHostAll. All query methods are polymorphic and allow you to query using any of the following techniques.

Pass a string selector (in the same style as you would when using jQuery or document.querySelector) to query for elements that match that path in the DOM. This method for querying is equivalent to Angular’s By.css predicate. Note that native HTML elements will be returned. For example:

// Returns a single HTMLElement
spectator.query('div > ul.nav li:first-child');
// Returns an array of all matching HTMLElements
spectator.queryAll('div > ul.nav li');
// Query from the document context
spectator.query('div', { root: true });
spectator.query('app-child', { read: ChildServiceService });

Pass a type (such as a component, directive or provider class) to query for instances of that type in the DOM. This is equivalent to Angular’s By.directive predicate. You can optionally pass in a second parameter to read a specific injection token from the matching elements’ injectors. For example:

// Returns a single instance of MyComponent (if present)
spectator.query(MyComponent);
// Returns the instance of `SomeService` found in the instance of `MyComponent` that exists in the DOM (if present)
spectator.query(MyComponent, { read: SomeService });
spectator.query(MyComponent, { read: ElementRef });
host.queryLast(ChildComponent);
host.queryAll(ChildComponent);

Spectator allows you to query for elements using selectors inspired by dom-testing-library. The available selectors are:

spectator.query(byPlaceholder('Please enter your email address'));
spectator.query(byValue('By value'));
spectator.query(byTitle('By title'));
spectator.query(byAltText('By alt text'));
spectator.query(byLabel('By label'));
spectator.query(byText('By text'));
spectator.query(byText('By text', {selector: '#some .selector'}));
spectator.query(byTextContent('By text content', {selector: '#some .selector'}));
spectator.query(byRole('checkbox', { checked: true }));
spectator.query(byTestId('someTestId'))

The difference between byText and byTextContent is that the former doesn’t match text inside a nested elements.

For example, in this following HTML byText('foobar', {selector: 'div'}) won’t match the following div, but byTextContent will:

<div>
<span>foo</span>
<span>bar</span>
</div>

These DOM selectors are directly supported by our Custom Matchers:

expect(byTextContent('By text content', {selector: '#some .selector'})).toExist();
expect(byPlaceholder('Please enter your email address')).toHaveValue('my-value');
// ... See the Custom Matchers section for a full list of possible matchers

Configure @testing-library/dom to use byTestId with a different testIdAttribute

import { configure } from '@testing-library/dom';
configure({ testIdAttribute: 'data-test' });

Spectator allows you to query for nested elements within a parent element. This is useful when you have multiple instances of the same component on the page and you want to query for children within a specific one. The parent selector is a string selector or DOMSelector that is used to find the parent element. The parent selector is passed as the second parameter to the query methods. For example:

spectator.query(ChildComponent, { parentSelector: '#parent-component-1' });
spectator.queryAll(ChildComponent, { parentSelector: '#parent-component-1' });
spectator.query(ChildComponent, { parentSelector: byTestId('parent-component-1') });
spectator.queryAll(ChildComponent, { parentSelector: byTestId('parent-component-1') });

Spectator allows you to test <select></select> elements easily, and supports multi select.

Example:

it('should set the correct options on multi select', () => {
const select = spectator.query('#test-multi-select') as HTMLSelectElement;
spectator.selectOption(select, ['1', '2']);
expect(select).toHaveSelectedOptions(['1', '2']);
});
it('should set the correct option on standard select', () => {
const select = spectator.query('#test-single-select') as HTMLSelectElement;
spectator.selectOption(select, '1');
expect(select).toHaveSelectedOptions('1');
});

It also allows you to check if your change event handler is acting correctly for each item selected. You can disable this if you need to pre set choices without dispatching the change event.

API:

spectator.selectOption(selectElement: HTMLSelectElement, options: string | string[] | HTMLOptionElement | HTMLOptionElement[], config: { emitEvents: boolean } = { emitEvents: true });

Example:

it('should dispatch correct number of change events', () => {
const onChangeSpy = spyOn(spectator.component, 'handleChange');
const select = spectator.query('#test-onchange-select') as HTMLSelectElement;
spectator.selectOption(select, ['1', '2'], { emitEvents: true});
expect(select).toHaveSelectedOptions(['1', '2']);
expect(onChangeSpy).toHaveBeenCalledTimes(2);
});
it('should not dispatch correct number of change events', () => {
const onChangeSpy = spyOn(spectator.component, 'handleChange');
const select = spectator.query('#test-onchange-select') as HTMLSelectElement;
spectator.selectOption(select, ['1', '2'], { emitEvents: false});
expect(select).toHaveSelectedOptions(['1', '2']);
expect(onChangeSpy).not.toHaveBeenCalledTimes(2);
});

You can also pass HTMLOptionElement(s) as arguments to selectOption and the toHaveSelectedOptions matcher. This is particularly useful when you are using [ngValue] binding on the <option>:

it('should set the correct option on single select when passing the element', () => {
const select = spectator.query('#test-single-select-element') as HTMLSelectElement;
spectator.selectOption(select, spectator.query(byText('Two')) as HTMLOptionElement);
expect(select).toHaveSelectedOptions(spectator.query(byText('Two')) as HTMLOptionElement);
});