diff --git a/apps/example-app/src/app/examples/08-directive.spec.ts b/apps/example-app/src/app/examples/08-directive.spec.ts
index c36822a3..5df9413a 100644
--- a/apps/example-app/src/app/examples/08-directive.spec.ts
+++ b/apps/example-app/src/app/examples/08-directive.spec.ts
@@ -3,8 +3,8 @@ import { render, screen, fireEvent } from '@testing-library/angular';
import { SpoilerDirective } from './08-directive';
test('it is possible to test directives', async () => {
- await render(SpoilerDirective, {
- template: '
',
+ await render('', {
+ declarations: [SpoilerDirective],
});
const directive = screen.getByTestId('dir');
@@ -25,8 +25,8 @@ test('it is possible to test directives with props', async () => {
const hidden = 'SPOILER ALERT';
const visible = 'There is nothing to see here ...';
- await render(SpoilerDirective, {
- template: '',
+ await render('', {
+ declarations: [SpoilerDirective],
componentProperties: {
hidden,
visible,
@@ -49,8 +49,8 @@ test('it is possible to test directives with props in template', async () => {
const hidden = 'SPOILER ALERT';
const visible = 'There is nothing to see here ...';
- await render(SpoilerDirective, {
- template: ``,
+ await render(``, {
+ declarations: [SpoilerDirective],
});
expect(screen.queryByText(visible)).not.toBeInTheDocument();
diff --git a/projects/testing-library/src/lib/models.ts b/projects/testing-library/src/lib/models.ts
index 6248ef92..b1984477 100644
--- a/projects/testing-library/src/lib/models.ts
+++ b/projects/testing-library/src/lib/models.ts
@@ -250,6 +250,9 @@ export interface RenderComponentOptions
extends RenderComponentOptions {
@@ -262,6 +265,8 @@ export interface RenderDirectiveOptions`
* })
+ *
+ * @deprecated Use `render(template, { declarations: [SomeDirective] })` instead.
*/
template: string;
/**
@@ -282,6 +287,27 @@ export interface RenderDirectiveOptions;
}
+// eslint-disable-next-line @typescript-eslint/ban-types
+export interface RenderTemplateOptions
+ extends RenderComponentOptions {
+ /**
+ * @description
+ * An Angular component to wrap the component in.
+ * The template will be overridden with the `template` option.
+ *
+ * @default
+ * `WrapperComponent`, an empty component that strips the `ng-version` attribute
+ *
+ * @example
+ * const component = await render(SpoilerDirective, {
+ * template: ``
+ * wrapper: CustomWrapperComponent
+ * })
+ */
+ wrapper?: Type;
+ componentProperties?: Partial;
+}
+
export interface Config extends Pick, 'excludeComponentDeclaration'> {
/**
* DOM Testing Library config
diff --git a/projects/testing-library/src/lib/testing-library.ts b/projects/testing-library/src/lib/testing-library.ts
index 13f96f06..fe3a22ca 100644
--- a/projects/testing-library/src/lib/testing-library.ts
+++ b/projects/testing-library/src/lib/testing-library.ts
@@ -22,7 +22,7 @@ import {
waitForOptions as dtlWaitForOptions,
configure as dtlConfigure,
} from '@testing-library/dom';
-import { RenderComponentOptions, RenderDirectiveOptions, RenderResult } from './models';
+import { RenderComponentOptions, RenderDirectiveOptions, RenderTemplateOptions, RenderResult } from './models';
import { getConfig } from './config';
const mountedFixtures = new Set>();
@@ -32,14 +32,24 @@ export async function render(
component: Type,
renderOptions?: RenderComponentOptions,
): Promise>;
+/**
+ * @deprecated Use `render(template, { declarations: [DirectiveType] })` instead.
+ */
export async function render(
component: Type,
renderOptions?: RenderDirectiveOptions,
): Promise>;
+export async function render(
+ template: string,
+ renderOptions?: RenderTemplateOptions,
+): Promise>;
export async function render(
- sut: Type,
- renderOptions: RenderComponentOptions | RenderDirectiveOptions = {},
+ sut: Type | string,
+ renderOptions:
+ | RenderComponentOptions
+ | RenderDirectiveOptions
+ | RenderTemplateOptions = {},
): Promise> {
const { dom: domConfig, ...globalConfig } = getConfig();
const {
@@ -69,7 +79,12 @@ export async function render(
});
TestBed.configureTestingModule({
- declarations: addAutoDeclarations(sut, { declarations, excludeComponentDeclaration, template, wrapper }),
+ declarations: addAutoDeclarations(sut, {
+ declarations,
+ excludeComponentDeclaration,
+ template,
+ wrapper,
+ }),
imports: addAutoImports({
imports: imports.concat(defaultImports),
routes,
@@ -176,7 +191,7 @@ export async function render(
detectChanges,
navigate,
rerender,
- debugElement: fixture.debugElement.query(By.directive(sut)),
+ debugElement: typeof sut === 'string' ? fixture.debugElement : fixture.debugElement.query(By.directive(sut)),
container: fixture.nativeElement,
debug: (element = fixture.nativeElement, maxLength, options) =>
Array.isArray(element)
@@ -193,14 +208,18 @@ async function createComponent(component: Type): Promise(
- component: Type,
+ sut: Type | string,
{ template, wrapper }: Pick, 'template' | 'wrapper'>,
): Promise> {
+ if (typeof sut === 'string') {
+ TestBed.overrideTemplate(wrapper, sut);
+ return createComponent(wrapper);
+ }
if (template) {
TestBed.overrideTemplate(wrapper, template);
return createComponent(wrapper);
}
- return createComponent(component);
+ return createComponent(sut);
}
function setComponentProperties(
@@ -248,7 +267,7 @@ function getChangesObj(oldProps: Partial | null, newProps: Par
}
function addAutoDeclarations(
- component: Type,
+ sut: Type | string,
{
declarations,
excludeComponentDeclaration,
@@ -256,9 +275,13 @@ function addAutoDeclarations(
wrapper,
}: Pick, 'declarations' | 'excludeComponentDeclaration' | 'template' | 'wrapper'>,
) {
+ if (typeof sut === 'string') {
+ return [...declarations, wrapper];
+ }
+
const wrappers = () => (template ? [wrapper] : []);
- const components = () => (excludeComponentDeclaration ? [] : [component]);
+ const components = () => (excludeComponentDeclaration ? [] : [sut]);
return [...declarations, ...wrappers(), ...components()];
}
diff --git a/projects/testing-library/tests/directive.spec.ts b/projects/testing-library/tests/render-template.spec.ts
similarity index 61%
rename from projects/testing-library/tests/directive.spec.ts
rename to projects/testing-library/tests/render-template.spec.ts
index 08db0eb3..1bc4e30d 100644
--- a/projects/testing-library/tests/directive.spec.ts
+++ b/projects/testing-library/tests/render-template.spec.ts
@@ -1,8 +1,11 @@
+/* eslint-disable testing-library/no-container */
+/* eslint-disable testing-library/render-result-naming-convention */
import { Directive, HostListener, ElementRef, Input, Output, EventEmitter, Component } from '@angular/core';
import { render, fireEvent } from '../src/public_api';
@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
selector: '[onOff]',
})
export class OnOffDirective {
@@ -21,6 +24,7 @@ export class OnOffDirective {
}
@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
selector: '[update]',
})
export class UpdateInputDirective {
@@ -32,27 +36,53 @@ export class UpdateInputDirective {
constructor(private el: ElementRef) {}
}
+@Component({
+ // eslint-disable-next-line @angular-eslint/component-selector
+ selector: 'greeting',
+ template: 'Hello {{ name }}!',
+})
+export class GreetingComponent {
+ @Input() name = 'World';
+}
+
test('the directive renders', async () => {
- const component = await render(OnOffDirective, {
- template: '',
+ const component = await render('', {
+ declarations: [OnOffDirective],
});
expect(component.container.querySelector('[onoff]')).toBeInTheDocument();
});
-test('uses the default props', async () => {
+test('the component renders', async () => {
+ const component = await render('', {
+ declarations: [GreetingComponent],
+ });
+
+ expect(component.container.querySelector('greeting')).toBeInTheDocument();
+ expect(component.getByText('Hello Angular!'));
+});
+
+test('the directive renders (compatibility with the deprecated signature)', async () => {
const component = await render(OnOffDirective, {
template: '',
});
+ expect(component.container.querySelector('[onoff]')).toBeInTheDocument();
+});
+
+test.only('uses the default props', async () => {
+ const component = await render('', {
+ declarations: [OnOffDirective],
+ });
+
fireEvent.click(component.getByText('init'));
fireEvent.click(component.getByText('on'));
fireEvent.click(component.getByText('off'));
});
test('overrides input properties', async () => {
- const component = await render(OnOffDirective, {
- template: '',
+ const component = await render('', {
+ declarations: [OnOffDirective],
});
fireEvent.click(component.getByText('init'));
@@ -62,8 +92,8 @@ test('overrides input properties', async () => {
test('overrides input properties via a wrapper', async () => {
// `bar` will be set as a property on the wrapper component, the property will be used to pass to the directive
- const component = await render(OnOffDirective, {
- template: '',
+ const component = await render('', {
+ declarations: [OnOffDirective],
componentProperties: {
bar: 'hello',
},
@@ -77,8 +107,8 @@ test('overrides input properties via a wrapper', async () => {
test('overrides output properties', async () => {
const clicked = jest.fn();
- const component = await render(OnOffDirective, {
- template: '',
+ const component = await render('', {
+ declarations: [OnOffDirective],
componentProperties: {
clicked,
},
@@ -93,8 +123,8 @@ test('overrides output properties', async () => {
describe('removeAngularAttributes', () => {
test('should remove angular attributes', async () => {
- await render(OnOffDirective, {
- template: '',
+ await render('', {
+ declarations: [OnOffDirective],
removeAngularAttributes: true,
});
@@ -103,8 +133,8 @@ describe('removeAngularAttributes', () => {
});
test('is disabled by default', async () => {
- await render(OnOffDirective, {
- template: '',
+ await render('', {
+ declarations: [OnOffDirective],
});
expect(document.querySelector('[ng-version]')).not.toBeNull();
@@ -113,8 +143,8 @@ describe('removeAngularAttributes', () => {
});
test('updates properties and invokes change detection', async () => {
- const component = await render(UpdateInputDirective, {
- template: '',
+ const component = await render('', {
+ declarations: [UpdateInputDirective],
componentProperties: {
value: 'value1',
},