Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions adev/src/content/guide/testing/components-scenarios.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,21 @@ const fixture = TestBed.createComponent(ValueDisplay, {
});
```

### Applying host directives

To apply host directives to a component created via `TestBed.createComponent()`, use the `directives` option. You can pass directive types directly, or provide bindings for their inputs.

```ts
import {inputBinding} from '@angular/core';

const fixture = TestBed.createComponent(MyComponent, {
directives: [
HighlightDirective,
{type: TooltipDirective, bindings: [inputBinding('tooltipText', tooltipSignal)]},
],
});
```

### Change an input value with `dispatchEvent()`

To simulate user input, find the input element and set its `value` property.
Expand Down
1 change: 1 addition & 0 deletions goldens/public-api/core/testing/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export interface TestBedStatic extends TestBed {
// @public
export interface TestComponentOptions {
bindings?: Binding[];
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[];
inferTagName?: boolean;
}

Expand Down
59 changes: 59 additions & 0 deletions packages/core/test/test_bed_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,65 @@ describe('TestBed', () => {
});
});

describe('directives', () => {
it('should be able to apply directives to the component', () => {
const logs: string[] = [];

@Directive({
host: {'class': 'dir-class', 'attr-one': 'one'},
})
class Dir {
constructor() {
logs.push('Dir');
}
}

@Component({template: '', host: {'class': 'host-class'}})
class TestComp {
constructor() {
logs.push('TestComp');
}
}

const fixture = TestBed.createComponent(TestComp, {
directives: [Dir],
});
fixture.detectChanges();

expect(logs).toEqual(['TestComp', 'Dir']);
expect(fixture.nativeElement.classList.contains('host-class')).toBe(true);
expect(fixture.nativeElement.classList.contains('dir-class')).toBe(true);
expect(fixture.nativeElement.getAttribute('attr-one')).toBe('one');
});

it('should be able to apply directives with bindings', () => {
@Directive({})
class Dir {
@Input() dirInput = '';
}

@Component({template: 'Value: {{value}}'})
class TestComp {
@Input() value = '';
}

const dirValue = signal('dir-initial');
const fixture = TestBed.createComponent(TestComp, {
bindings: [inputBinding('value', () => 'host-value')],
directives: [{type: Dir, bindings: [inputBinding('dirInput', dirValue)]}],
});
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('Value: host-value');
const dirInstance = fixture.debugElement.injector.get(Dir);
expect(dirInstance.dirInput).toBe('dir-initial');

dirValue.set('dir-updated');
fixture.detectChanges();
expect(dirInstance.dirInput).toBe('dir-updated');
});
});

it('should allow overriding a provider defined via ModuleWithProviders (using TestBed.overrideProvider)', () => {
const serviceOverride = {
get() {
Expand Down
6 changes: 5 additions & 1 deletion packages/core/testing/src/test_bed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
ComponentRef,
ɵDeferBlockBehavior as DeferBlockBehavior,
Directive,
DirectiveWithBindings,
EnvironmentInjector,
ɵflushModuleScopingQueueAsMuchAsPossible as flushModuleScopingQueueAsMuchAsPossible,
ɵgetAsyncClassMetadataFn as getAsyncClassMetadataFn,
Expand Down Expand Up @@ -76,6 +77,9 @@ export interface TestComponentOptions {
/** Bindings to apply to the test component. */
bindings?: Binding[];

/** Directives to apply to the test component. */
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[];

/**
* Whether to infer the tag name of the test component from its selector.
* Otherwise `div` will be used as its tag name.
Expand Down Expand Up @@ -711,7 +715,7 @@ export class TestBedImpl implements TestBed {
[],
`#${rootElId}`,
this.testModuleRef,
undefined,
options?.directives,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't set this up intentionally, because TestBed.createComponent is meant for testing the component as it would be used in an app, not using arbitrary directives on it.

options?.bindings,
) as ComponentRef<T>;
return this.runInInjectionContext(() => new ComponentFixture(componentRef));
Expand Down
Loading