Skip to content

Commit 69e3bd8

Browse files
fix: include custom queries in RenderResult type (#594)
Closes #590
1 parent 73d315a commit 69e3bd8

5 files changed

Lines changed: 86 additions & 29 deletions

File tree

‎projects/testing-library/src/lib/models.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@ export type OutputRefKeysWithCallback<T> = {
3030
};
3131

3232
export type RenderResultQueries<Q extends Queries = typeof queries> = BoundFunctions<Q>;
33-
export interface RenderResult<ComponentType, WrapperType = ComponentType> extends RenderResultQueries {
33+
export type RenderResult<
34+
ComponentType,
35+
WrapperType = ComponentType,
36+
Q extends Queries = typeof queries,
37+
> = RenderResultQueries<Q> & BaseRenderResult<ComponentType, WrapperType>;
38+
39+
interface BaseRenderResult<ComponentType, WrapperType = ComponentType> {
3440
/**
3541
* @description
3642
* The containing DOM node of your rendered Angular Component.

‎projects/testing-library/src/lib/testing-library.ts‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,19 @@ type SubscribedOutput<T> = readonly [key: keyof T, callback: (v: any) => void, s
4444

4545
const mountedFixtures = new Set<ComponentFixture<any>>();
4646

47-
export async function render<ComponentType>(
47+
export async function render<ComponentType, Q extends Queries = typeof dtlQueries>(
4848
component: Type<ComponentType>,
49-
renderOptions?: RenderComponentOptions<ComponentType>,
50-
): Promise<RenderResult<ComponentType, ComponentType>>;
51-
export async function render<WrapperType = WrapperComponent>(
49+
renderOptions?: RenderComponentOptions<ComponentType, Q>,
50+
): Promise<RenderResult<ComponentType, ComponentType, Q>>;
51+
export async function render<WrapperType = WrapperComponent, Q extends Queries = typeof dtlQueries>(
5252
template: string,
53-
renderOptions?: RenderTemplateOptions<WrapperType>,
54-
): Promise<RenderResult<WrapperType>>;
53+
renderOptions?: RenderTemplateOptions<WrapperType, object, Q>,
54+
): Promise<RenderResult<WrapperType, WrapperType, Q>>;
5555

56-
export async function render<SutType, WrapperType = SutType>(
56+
export async function render<SutType, WrapperType = SutType, Q extends Queries = typeof dtlQueries>(
5757
sut: Type<SutType> | string,
58-
renderOptions: RenderComponentOptions<SutType> | RenderTemplateOptions<WrapperType> = {},
59-
): Promise<RenderResult<SutType>> {
58+
renderOptions: RenderComponentOptions<SutType, Q> | RenderTemplateOptions<WrapperType, object, Q> = {},
59+
): Promise<RenderResult<SutType, SutType, Q>> {
6060
const { dom: domConfig, ...globalConfig } = getConfig();
6161
const {
6262
detectChangesOnRender = true,
@@ -87,8 +87,8 @@ export async function render<SutType, WrapperType = SutType>(
8787
configureTestBed = () => {
8888
/* noop*/
8989
},
90-
} = { ...globalConfig, ...renderOptions } as RenderComponentOptions<SutType> &
91-
RenderTemplateOptions<WrapperType> &
90+
} = { ...globalConfig, ...renderOptions } as RenderComponentOptions<SutType, Q> &
91+
RenderTemplateOptions<WrapperType, object, Q> &
9292
Config;
9393

9494
dtlConfigure({
@@ -362,8 +362,8 @@ export async function render<SutType, WrapperType = SutType>(
362362
console.log(dtlPrettyDOM(element, maxLength, options));
363363
}
364364
},
365-
...replaceFindWithFindAndDetectChanges(dtlGetQueriesForElement(fixture.nativeElement, queries)),
366-
};
365+
...replaceFindWithFindAndDetectChanges(dtlGetQueriesForElement<Q>(fixture.nativeElement, queries)),
366+
} as RenderResult<SutType, SutType, Q>;
367367
}
368368

369369
async function createComponent<SutType>(
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Component } from '@angular/core';
2+
import { test, expect } from 'vitest';
3+
import { render, queries, queryHelpers } from '../../public_api';
4+
5+
const myQueryByTestId = queryHelpers.queryByAttribute.bind(null, 'data-test-id');
6+
7+
test('custom queries passed to render are available on the render result', async () => {
8+
const view = await render(FixtureComponent, {
9+
queries: {
10+
...queries,
11+
myQueryByTestId,
12+
},
13+
});
14+
15+
expect(view.myQueryByTestId('my-fixture')).not.toBeNull();
16+
// eslint-disable-next-line testing-library/prefer-screen-queries
17+
expect(view.myQueryByTestId('my-fixture')).toBe(view.getByText('Hello world'));
18+
});
19+
20+
@Component({
21+
selector: 'atl-fixture',
22+
template: `<div data-test-id="my-fixture">Hello world</div>`,
23+
})
24+
class FixtureComponent {}

‎projects/testing-library/zoneless/src/public_api.ts‎

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ import {
1818

1919
export type RenderResultQueries<Q extends Queries = typeof queries> = BoundFunctions<Q>;
2020

21-
export interface RenderResult<ComponentType, WrapperType = ComponentType> extends RenderResultQueries {
21+
export type RenderResult<
22+
ComponentType,
23+
WrapperType = ComponentType,
24+
Q extends Queries = typeof queries,
25+
> = RenderResultQueries<Q> & BaseRenderResult<ComponentType, WrapperType>;
26+
27+
interface BaseRenderResult<ComponentType, WrapperType = ComponentType> {
2228
/**
2329
* @description
2430
* The containing DOM node of your rendered Angular Component.
@@ -206,28 +212,28 @@ export interface RenderTemplateOptions<WrapperType, Properties extends object =
206212
imports?: any[];
207213
}
208214

209-
export async function render<ComponentType>(
215+
export async function render<ComponentType, Q extends Queries = typeof queries>(
210216
component: Type<ComponentType>,
211-
renderOptions?: RenderComponentOptions,
212-
): Promise<RenderResult<ComponentType, ComponentType>>;
213-
export async function render<WrapperType = WrapperComponent>(
217+
renderOptions?: RenderComponentOptions<Q>,
218+
): Promise<RenderResult<ComponentType, ComponentType, Q>>;
219+
export async function render<WrapperType = WrapperComponent, Q extends Queries = typeof queries>(
214220
template: string,
215-
renderOptions?: RenderTemplateOptions<WrapperType>,
216-
): Promise<RenderResult<WrapperType>>;
217-
export async function render<ComponentType, WrapperType = ComponentType>(
221+
renderOptions?: RenderTemplateOptions<WrapperType, object, Q>,
222+
): Promise<RenderResult<WrapperType, WrapperType, Q>>;
223+
export async function render<ComponentType, WrapperType = ComponentType, Q extends Queries = typeof queries>(
218224
componentOrTemplate: Type<ComponentType> | string,
219-
renderOptions: RenderComponentOptions | RenderTemplateOptions<WrapperType> = {},
220-
): Promise<RenderResult<ComponentType, ComponentType | WrapperType>> {
225+
renderOptions: RenderComponentOptions<Q> | RenderTemplateOptions<WrapperType, object, Q> = {},
226+
): Promise<RenderResult<ComponentType, ComponentType | WrapperType, Q>> {
221227
TestBed.configureTestingModule({
222228
declarations: [WrapperComponent],
223229
imports: 'imports' in renderOptions ? renderOptions.imports : [],
224230
providers: renderOptions.providers ?? [],
225231
});
226232

227-
if ('importOverrides' in renderOptions && (renderOptions as RenderComponentOptions).importOverrides?.length) {
233+
if ('importOverrides' in renderOptions && (renderOptions as RenderComponentOptions<Q>).importOverrides?.length) {
228234
const sut = componentOrTemplate as Type<unknown>;
229235
if (typeof sut === 'function' && isStandalone(sut)) {
230-
const overrides = (renderOptions as RenderComponentOptions).importOverrides!;
236+
const overrides = (renderOptions as RenderComponentOptions<Q>).importOverrides!;
231237
TestBed.overrideComponent(sut, {
232238
remove: { imports: overrides.map((o) => o.replace) },
233239
add: { imports: overrides.map((o) => o.with) },
@@ -267,8 +273,8 @@ export async function render<ComponentType, WrapperType = ComponentType>(
267273
console.log(prettyDOM(element, maxLength, options));
268274
}
269275
},
270-
...getQueriesForElement(fixture.nativeElement, renderOptions?.queries),
271-
};
276+
...getQueriesForElement<Q>(fixture.nativeElement, renderOptions?.queries),
277+
} as RenderResult<ComponentType, ComponentType | WrapperType, Q>;
272278
}
273279

274280
async function createComponentFixture<SutType>(

‎projects/testing-library/zoneless/tests/zoneless.spec.ts‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Component, inject, Injectable, model, output, outputBinding, signal, twoWayBinding } from '@angular/core';
22
import { test, expect, vi } from 'vitest';
33
import userEvent from '@testing-library/user-event';
4-
import { render, screen } from '../index';
4+
import { render, screen, queries, queryHelpers } from '../index';
55

66
@Injectable()
77
class CounterService {
@@ -250,3 +250,24 @@ test('can provide custom service providers', async () => {
250250
await user.click(decrementControl);
251251
expect(counterControl).toHaveTextContent('1');
252252
});
253+
254+
@Component({
255+
selector: 'atl-custom-query-fixture',
256+
template: `<div data-test-id="my-fixture">Hello world</div>`,
257+
})
258+
class CustomQueryFixtureComponent {}
259+
260+
test('custom queries passed to render are available on the render result', async () => {
261+
const myQueryByTestId = queryHelpers.queryByAttribute.bind(null, 'data-test-id');
262+
263+
const view = await render(CustomQueryFixtureComponent, {
264+
queries: {
265+
...queries,
266+
myQueryByTestId,
267+
},
268+
});
269+
270+
expect(view.myQueryByTestId('my-fixture')).not.toBeNull();
271+
// eslint-disable-next-line testing-library/prefer-screen-queries
272+
expect(view.myQueryByTestId('my-fixture')).toBe(view.getByText('Hello world'));
273+
});

0 commit comments

Comments
 (0)