-
-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): support of HostDirectives #5117
- Loading branch information
Showing
17 changed files
with
1,127 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
--- | ||
title: How to test a host directive in Angular application | ||
description: Covering an Angular host directive with tests | ||
sidebar_label: Host Directive | ||
--- | ||
|
||
Let's imagine we have a component with a host directive which adds the `name` attribute. | ||
|
||
The code of the directive: | ||
|
||
```ts | ||
@Directive({ | ||
selector: 'host', | ||
standalone: true, | ||
}) | ||
class HostDirective { | ||
@HostBinding('attr.name') @Input() input?: string; | ||
} | ||
``` | ||
|
||
The code of the component: | ||
|
||
```ts | ||
@Component({ | ||
selector: 'target', | ||
hostDirectives: [ | ||
{ | ||
directive: HostDirective, | ||
inputs: ['input'], | ||
}, | ||
], | ||
template: 'target', | ||
}) | ||
class TargetComponent { | ||
// tons of logic we want to ignore | ||
} | ||
``` | ||
|
||
The component can be heavy, and, in an ideal test, the logic of the component should be ignored, | ||
so the focus would stay on the directive and how it behaves. | ||
|
||
[`MockBuilder`](../api/MockBuilder.md) knows how to mock the component | ||
and how to keep one or some of its host directives as they are. | ||
|
||
In order to do so, the host directive should be kept, and its component should be mocked: | ||
|
||
```ts | ||
beforeEach(() => MockBuilder(HostDirective, TargetComponent)); | ||
``` | ||
|
||
Profit! | ||
|
||
To access the directive in a test, [`ngMocks.findInstnace`](../api/ngMocks/findInstance.md) can be used. | ||
|
||
```ts | ||
it('keeps host directives', () => { | ||
const fixture = MockRender(TargetComponent, { input: 'test' }); | ||
|
||
const directive = ngMocks.findInstance(HostDirective); | ||
expect(directive.input).toEqual('test'); | ||
expect(ngMocks.formatHtml(fixture)).toContain(' name="test"'); | ||
}); | ||
``` | ||
|
||
## Live example | ||
|
||
- [Try it on CodeSandbox](https://codesandbox.io/s/github/help-me-mom/ng-mocks-sandbox/tree/tests?file=/src/examples/TestHostDirective/test.spec.ts&initialpath=%3Fspec%3DTestHostDirective) | ||
- [Try it on StackBlitz](https://stackblitz.com/github/help-me-mom/ng-mocks-sandbox/tree/tests?file=src/examples/TestHostDirective/test.spec.ts&initialpath=%3Fspec%3DTestHostDirective) | ||
|
||
```ts title="/~https://github.com/help-me-mom/ng-mocks/blob/master/examples/TestHostDirective/test.spec.ts" | ||
import { | ||
Component, | ||
Directive, | ||
HostBinding, | ||
Input, | ||
} from '@angular/core'; | ||
|
||
import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; | ||
|
||
@Directive({ | ||
selector: 'host', | ||
standalone: true, | ||
}) | ||
class HostDirective { | ||
@HostBinding('attr.name') @Input() input?: string; | ||
|
||
public hostTestHostDirective() {} | ||
} | ||
|
||
@Component({ | ||
selector: 'target', | ||
hostDirectives: [ | ||
{ | ||
directive: HostDirective, | ||
inputs: ['input'], | ||
}, | ||
], | ||
template: 'target', | ||
}) | ||
class TargetComponent { | ||
public targetTestHostDirective() {} | ||
} | ||
|
||
describe('TestHostDirective', () => { | ||
beforeEach(() => MockBuilder(HostDirective, TargetComponent)); | ||
|
||
it('keeps host directives', () => { | ||
const fixture = MockRender(TargetComponent, { input: 'test' }); | ||
|
||
const directive = ngMocks.findInstance(HostDirective); | ||
expect(directive.input).toEqual('test'); | ||
expect(ngMocks.formatHtml(fixture)).toContain(' name="test"'); | ||
}); | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
--- | ||
title: How to mock a host directive | ||
description: Mocking an Angular host directive | ||
sidebar_label: Host Directive | ||
--- | ||
|
||
It can happen that a component hast a host directive which should be mocked in a test. | ||
|
||
There are several ways how `ng-mocks` can mock host directives: | ||
|
||
- [`MockBuilder`](../../api/MockBuilder.md#shallow-flag) and its [`shallow`](../../api/MockBuilder.md#shallow-flag) flag | ||
- [`MockBuilder`](../../api/MockBuilder.md) constructor | ||
- `TestBed` | ||
|
||
## `shallow` flag | ||
|
||
It's the easiest and recommended way which covers all host directives automatically, so there is no need to specify all of them. | ||
|
||
To mock all host directives, simply provide [`shallow`](../../api/MockBuilder.md#shallow-flag) flag in [`MockBuilder.mock`](../../api/MockBuilder.md#mock): | ||
|
||
```ts | ||
beforeEach(() => | ||
MockBuilder().mock(TargetComponent, { shallow: true }), | ||
); | ||
``` | ||
|
||
Now, all host directives and their dependencies will be mocks. | ||
|
||
## `MockBuilder` | ||
|
||
[`MockBuilder`](../../api/MockBuilder.md) is useful, when only one or some host directives should be mocked. | ||
|
||
To do so, the host directives should be specified as the second parameter of [`MockBuilder`](../../api/MockBuilder.md): | ||
|
||
```ts | ||
beforeEach(() => MockBuilder(TargetComponent, HostDirective)); | ||
``` | ||
|
||
That's it, now `TargetComponent` will have a mock of `HostDirective`. | ||
|
||
## TestBed | ||
|
||
If you use `TestBed`, you should mock the desired host directive with [`MockDirective`](../../api/MockDirective.md) | ||
and import / declare its component. | ||
|
||
For example, if the name of the component is `TargetComponent` and its host directive is called `HostDirective`, | ||
then `TestBed` can be defined like that: | ||
|
||
```ts | ||
beforeEach(() => | ||
TestBed.configureTestingModule({ | ||
imports: [MockDirective(HostDirective)], // mocking the host directive | ||
declarations: [TargetComponent], // declaring the component under test | ||
}).compileComponents(), | ||
); | ||
``` | ||
|
||
Profit! Under the hood `TargetComponent` will be redefined to use a mock of `HostDirective`. | ||
|
||
## Live example | ||
|
||
- [Try it on CodeSandbox](https://codesandbox.io/s/github/help-me-mom/ng-mocks-sandbox/tree/tests?file=/src/examples/MockHostDirective/test.spec.ts&initialpath=%3Fspec%3DMockHostDirective) | ||
- [Try it on StackBlitz](https://stackblitz.com/github/help-me-mom/ng-mocks-sandbox/tree/tests?file=src/examples/MockHostDirective/test.spec.ts&initialpath=%3Fspec%3DMockHostDirective) | ||
|
||
```ts title="/~https://github.com/help-me-mom/ng-mocks/blob/master/examples/MockHostDirective/test.spec.ts" | ||
import { | ||
Component, | ||
Directive, | ||
EventEmitter, | ||
Input, | ||
Output, | ||
} from '@angular/core'; | ||
import { TestBed } from '@angular/core/testing'; | ||
|
||
import { | ||
MockBuilder, | ||
MockDirective, | ||
MockRender, | ||
ngMocks, | ||
} from 'ng-mocks'; | ||
|
||
@Directive({ | ||
selector: 'host', | ||
standalone: true, | ||
}) | ||
class HostDirective { | ||
@Input() input?: string; | ||
@Output() output = new EventEmitter<void>(); | ||
|
||
public hostMockHostDirective() {} | ||
} | ||
|
||
@Component({ | ||
selector: 'target', | ||
hostDirectives: [ | ||
{ | ||
directive: HostDirective, | ||
inputs: ['input'], | ||
outputs: ['output'], | ||
}, | ||
], | ||
template: 'target', | ||
}) | ||
class TargetComponent { | ||
public targetMockHostDirective() {} | ||
} | ||
|
||
describe('MockHostDirective', () => { | ||
describe('TestBed', () => { | ||
beforeEach(() => | ||
TestBed.configureTestingModule({ | ||
imports: [MockDirective(HostDirective)], | ||
declarations: [TargetComponent], | ||
}), | ||
); | ||
|
||
it('mocks host directives', () => { | ||
const fixture = TestBed.createComponent(TargetComponent); | ||
|
||
const directive = ngMocks.findInstance(fixture, HostDirective); | ||
expect(directive).toBeDefined(); | ||
}); | ||
}); | ||
|
||
describe('MockBuilder', () => { | ||
beforeEach(() => MockBuilder(TargetComponent, HostDirective)); | ||
|
||
it('mocks host directives', () => { | ||
MockRender(TargetComponent, { input: 'test' }); | ||
|
||
const directive = ngMocks.findInstance(HostDirective); | ||
expect(directive.input).toEqual('test'); | ||
}); | ||
}); | ||
|
||
describe('MockBuilder:shallow', () => { | ||
beforeEach(() => | ||
MockBuilder().mock(TargetComponent, { shallow: true }), | ||
); | ||
|
||
it('mocks host directives', () => { | ||
MockRender(TargetComponent, { input: 'test' }); | ||
|
||
const directive = ngMocks.findInstance(HostDirective); | ||
expect(directive.input).toEqual('test'); | ||
}); | ||
}); | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { | ||
Component, | ||
Directive, | ||
EventEmitter, | ||
Input, | ||
Output, | ||
VERSION, | ||
} from '@angular/core'; | ||
import { TestBed } from '@angular/core/testing'; | ||
|
||
import { | ||
MockBuilder, | ||
MockDirective, | ||
MockRender, | ||
ngMocks, | ||
} from 'ng-mocks'; | ||
|
||
@Directive( | ||
{ | ||
selector: 'host', | ||
standalone: true, | ||
} as never /* TODO: remove after upgrade to a14 */, | ||
) | ||
class HostDirective { | ||
@Input() input?: string; | ||
@Output() output = new EventEmitter<void>(); | ||
|
||
public hostMockHostDirective() {} | ||
} | ||
|
||
@Component( | ||
{ | ||
selector: 'target', | ||
hostDirectives: [ | ||
{ | ||
directive: HostDirective, | ||
inputs: ['input'], | ||
outputs: ['output'], | ||
}, | ||
], | ||
template: 'target', | ||
} as never /* TODO: remove after upgrade to a15 */, | ||
) | ||
class TargetComponent { | ||
public targetMockHostDirective() {} | ||
} | ||
|
||
describe('MockHostDirective', () => { | ||
if (Number.parseInt(VERSION.major, 10) < 15) { | ||
it('needs a15+', () => { | ||
expect(true).toBeTruthy(); | ||
}); | ||
|
||
return; | ||
} | ||
|
||
describe('TestBed', () => { | ||
beforeEach(() => | ||
TestBed.configureTestingModule({ | ||
imports: [MockDirective(HostDirective)], | ||
declarations: [TargetComponent], | ||
}).compileComponents(), | ||
); | ||
|
||
it('mocks host directives', () => { | ||
const fixture = TestBed.createComponent(TargetComponent); | ||
|
||
const directive = ngMocks.findInstance(fixture, HostDirective); | ||
expect(directive).toBeDefined(); | ||
}); | ||
}); | ||
|
||
describe('MockBuilder', () => { | ||
beforeEach(() => MockBuilder(TargetComponent, HostDirective)); | ||
|
||
it('mocks host directives', () => { | ||
MockRender(TargetComponent, { input: 'test' }); | ||
|
||
const directive = ngMocks.findInstance(HostDirective); | ||
expect(directive.input).toEqual('test'); | ||
}); | ||
}); | ||
|
||
describe('MockBuilder:shallow', () => { | ||
beforeEach(() => | ||
MockBuilder().mock(TargetComponent, { shallow: true }), | ||
); | ||
|
||
it('mocks host directives', () => { | ||
MockRender(TargetComponent, { input: 'test' }); | ||
|
||
const directive = ngMocks.findInstance(HostDirective); | ||
expect(directive.input).toEqual('test'); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.