diff --git a/projects/testing-library/src/lib/models.ts b/projects/testing-library/src/lib/models.ts index 7799c3c..26646e9 100644 --- a/projects/testing-library/src/lib/models.ts +++ b/projects/testing-library/src/lib/models.ts @@ -186,6 +186,23 @@ export interface RenderComponentOptions | any[])[]; /** * @description * Queries to bind. Overrides the default set from DOM Testing Library unless merged. diff --git a/projects/testing-library/src/lib/testing-library.ts b/projects/testing-library/src/lib/testing-library.ts index 9677123..c4b8d86 100644 --- a/projects/testing-library/src/lib/testing-library.ts +++ b/projects/testing-library/src/lib/testing-library.ts @@ -55,6 +55,7 @@ export async function render( wrapper = WrapperComponent as Type, componentProperties = {}, componentProviders = [], + ɵcomponentImports: componentImports, excludeComponentDeclaration = false, routes = [], removeAngularAttributes = false, @@ -83,6 +84,7 @@ export async function render( providers: [...providers], schemas: [...schemas], }); + overrideComponentImports(sut, componentImports); await TestBed.compileComponents(); @@ -128,23 +130,23 @@ export async function render( const [path, params] = (basePath + href).split('?'); const queryParams = params ? params.split('&').reduce((qp, q) => { - const [key, value] = q.split('='); - const currentValue = qp[key]; - if (typeof currentValue === 'undefined') { - qp[key] = value; - } else if (Array.isArray(currentValue)) { - qp[key] = [...currentValue, value]; - } else { - qp[key] = [currentValue, value]; - } - return qp; - }, {} as Record) + const [key, value] = q.split('='); + const currentValue = qp[key]; + if (typeof currentValue === 'undefined') { + qp[key] = value; + } else if (Array.isArray(currentValue)) { + qp[key] = [...currentValue, value]; + } else { + qp[key] = [currentValue, value]; + } + return qp; + }, {} as Record) : undefined; const navigateOptions: NavigationExtras | undefined = queryParams ? { - queryParams, - } + queryParams, + } : undefined; const doNavigate = () => { @@ -264,6 +266,18 @@ function setComponentProperties( return fixture; } +function overrideComponentImports(sut: Type | string, imports: (Type | any[])[] | undefined) { + if (imports) { + if (typeof sut === 'function' && ɵisStandalone(sut)) { + TestBed.overrideComponent(sut, { set: { imports } }); + } else { + throw new Error( + `Error while rendering ${sut}: Cannot specify componentImports on a template or non-standalone component.`, + ); + } + } +} + function hasOnChangesHook(componentInstance: SutType): componentInstance is SutType & OnChanges { return ( 'ngOnChanges' in componentInstance && typeof (componentInstance as SutType & OnChanges).ngOnChanges === 'function' @@ -397,7 +411,7 @@ if (typeof process === 'undefined' || !process.env?.ATL_SKIP_AUTO_CLEANUP) { } @Component({ selector: 'atl-wrapper-component', template: '' }) -class WrapperComponent {} +class WrapperComponent { } /** * Wrap findBy queries to poke the Angular change detection cycle diff --git a/projects/testing-library/tests/render.spec.ts b/projects/testing-library/tests/render.spec.ts index 33e8d80..1faec70 100644 --- a/projects/testing-library/tests/render.spec.ts +++ b/projects/testing-library/tests/render.spec.ts @@ -19,7 +19,7 @@ import { render, fireEvent, screen } from '../src/public_api'; `, }) -class FixtureComponent {} +class FixtureComponent { } test('creates queries and events', async () => { const view = await render(FixtureComponent); @@ -48,6 +48,51 @@ describe('standalone', () => { }); }); +describe('standalone with child', () => { + @Component({ + selector: 'child-fixture', + template: `A child fixture`, + standalone: true, + }) + class ChildFixture { } + + @Component({ + selector: 'child-fixture', + template: `A mock child fixture`, + standalone: true, + }) + class MockChildFixture { } + + @Component({ + selector: 'parent-fixture', + template: `

Parent fixture

+
`, + standalone: true, + imports: [ChildFixture], + }) + class ParentFixture { } + + it('renders the standalone component with child', async () => { + await render(ParentFixture); + expect(screen.getByText('Parent fixture')); + expect(screen.getByText('A child fixture')); + }); + + it('renders the standalone component with child', async () => { + await render(ParentFixture, { ɵcomponentImports: [MockChildFixture] }); + expect(screen.getByText('Parent fixture')); + expect(screen.getByText('A mock child fixture')); + }); + + it('rejects render of template with componentImports set', () => { + const result = render(`
`, { + imports: [ParentFixture], + ɵcomponentImports: [MockChildFixture], + }); + return expect(result).rejects.toMatchObject({ message: /Error while rendering/ }); + }); +}); + describe('removeAngularAttributes', () => { it('should remove angular attribute', async () => { await render(FixtureComponent, { @@ -72,7 +117,7 @@ describe('animationModule', () => { @NgModule({ declarations: [FixtureComponent], }) - class FixtureModule {} + class FixtureModule { } describe('excludeComponentDeclaration', () => { it('does not throw if component is declared in an imported module', async () => { await render(FixtureComponent, {