ngneat / spectator Goto Github PK
View Code? Open in Web Editor NEW🦊 🚀 A Powerful Tool to Simplify Your Angular Tests
Home Page: https://ngneat.github.io/spectator
License: MIT License
🦊 🚀 A Powerful Tool to Simplify Your Angular Tests
Home Page: https://ngneat.github.io/spectator
License: MIT License
So when i use the spectator API to dispatch a keydown event like this:
spectator.dispatchKeyboardEvent(select, 'keydown', SPACE);
Things do not work correctly. After debugging the code, it turns out that event.metaKey
is true for the dispatched event (running in Chrome). After some looking, i think the bug is here: https://github.com/NetanelBasal/spectator/blob/master/projects/spectator/src/lib/event-objects.ts#L40
When i look at MDN, the API for initKeyEvent
is different from initKeyboardEvent
. The spectator code doesn't seem to take that into account. Also, according to MDN both methods are deprecrated and you should just use the new KeyboardEvent(...)
constructor function.
> ev = document.createEvent('KeyboardEvent')
KeyboardEvent {isTrusted: false, key: "", code: "", location: 0, ctrlKey: false, …}
> ev.initKeyboardEvent('keydown', true, true, window, 0, 0, 0, 0, 0, 13)
undefined
> ev.metaKey
true
Imagine testing a service depending on observable from another service:
@Injectable({
providedIn: 'root'
})
export class ConsumerService {
constructor(private serviceWithObservable: ServiceWithObservable) {
serviceWithObservable.obs$.subscribe(...)
}
}
@Injectable({
providedIn: 'root'
})
export class ServiceWithObservable {
obs$ = new Subject();
}
For testing services we use mocks
array which calls mockProvider
which works great but it creates spies only for methods. Observable property is missing so in the test you need to do something like:
const serviceWithObservable = TestBed.get(ServiceWithObservable) as ServiceWithObservable;
serviceWithObservable.obs$ = new Subject();
- What do you think about adding parameter for properties to mockProvider
?
Eg:
providers: [
mockProvider(ServiceWithObservable, {
obs$: new Subject()
})
]
Could be used with Object.assign
or something to add these properties to the mock.
Just wanted to report that in some situations this error can appear:
Illegal state: Could not load the summary for directive HostComponent.
This happens when you use multiple factories (e.g. createTestComponentFactory
and createHostComponentFactory
) in the same test ("describe") block.
Is this intended? Maybe a better error message should be visible.
You should document how to mock ngrx store in a component/service.
Here is an example of how to mock the store in a service where the service needs data from selectors for a small slice of the store state. I'd love to know if there is a way to simplify this even more...
import { SearchService } from './search.service';
import { createService } from '@netbasal/spectator';
import { of } from 'rxjs';
import { StoreModule, Action } from '@ngrx/store';
import * as fromBook from '@book/state';
function reducer(state = {}, action: Action): {} {
switch (action.type) {
default:
return state;
}
}
describe('SearchService', () => {
const spectator = createService({
service: SearchService,
imports: [
StoreModule.forRoot(reducer),
StoreModule.forFeature('book', fromBook.reducer)
]
});
beforeEach(() => {
// const store = spectator.get(Store) just returns {} ???
// Grab the private instance of the store in the service under test:
const store = spectator.service['store'];
store.dispatch(new fromBook.SetCurrentBookId(42));
store.dispatch(new fromBook.SetCurrentProductId(1));
});
it('can search', () => {
const testData = {};
const spy = spyOn(myObject, 'someMethod').and.returnValue(of(testData));
spectator.service.search('something');
expect(spy).toHaveBeenCalledWith(42, 1, true, true);
});
});
@dirkluijk when you have time.
On work, we have a Module with some Services to help the teams with some stuff related to our architecture (like oauth and so on...). When I use createService, it gives me the this error:
ERROR in ./~/@angular/platform-browser/bundles/platform-browser-animations.umd.js
Module not found: Error: Cannot resolve module '@angular/animations' in C:\Users\davi.carvalho\Documents\ANGL-SPAWEBBGRL\node_modules\@angular\platform-browser\bundles
@ ./~/@angular/platform-browser/bundles/platform-browser-animations.umd.js 7:145-175
ERROR in ./~/@angular/platform-browser/bundles/platform-browser-animations.umd.js
Module not found: Error: Cannot resolve module '@angular/animations/browser' in C:\Users\davi.carvalho\Documents\ANGL-SPAWEBBGRL\node_modules\@angular\platform-browser\bundles
@ ./~/@angular/platform-browser/bundles/platform-browser-animations.umd.js 7:177-215
ERROR in [at-loader] src\guards-module\guards\authMBS.guard.spaa.spec.ts:85:25
Argument of type '"hasValidDataAccess"' is not assignable to parameter of type '"RouteForbidden" | "UrlUserMenu" | "getRolesList" | "canActivate"'.
ERROR in [at-loader] src\guards-module\guards\authMBS.guard.spaa.spec.ts:104:25
Argument of type '"hasValidDataAccess"' is not assignable to parameter of type '"RouteForbidden" | "UrlUserMenu" | "getRolesList" | "canActivate"'.
webpack: Failed to compile.
19 07 2018 11:02:31.900:INFO [karma]: Karma v1.2.0 server started at http://localhost:8090/
19 07 2018 11:02:31.908:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency
19 07 2018 11:02:32.443:INFO [launcher]: Starting browser PhantomJS
19 07 2018 11:02:53.710:INFO [PhantomJS 2.1.1 (Windows 8 0.0.0)]: Connected on socket /#nn-3uL5yD-e4sA_6AAAA with id 40538519
PhantomJS 2.1.1 (Windows 8 0.0.0) ERROR
SyntaxError: Unexpected token 'const'
at karma-test-shim.js:78894
PhantomJS 2.1.1 (Windows 8 0.0.0) ERROR
SyntaxError: Unexpected token 'const'
at karma-test-shim.js:78894
=============================== Coverage summary ===============================
Statements : 100% ( 0/0 )
Branches : 100% ( 0/0 )
Functions : 100% ( 0/0 )
Lines : 100% ( 0/0 )
================================================================================
So it breaks the tests and say that I don't have @angular/animations, since it's just services, we don't have @angular/animations in it.
I'm using version 1.2.1, because we are using Angular 5
Error I'm getting:
Error: Type EditorTestHostComponent is part of the declarations of 2 modules: AtlasDevToolsModule and DynamicTestModule! Please consider moving EditorTestHostComponent to a higher module that imports AtlasDevToolsModule and DynamicTestModule. You can also create a new NgModule that exports and includes EditorTestHostComponent then import that NgModule in AtlasDevToolsModule and DynamicTestModule.
Is it possible to support my host
component coming from another module?
So i wanted to unit test a component that has an event handler for a child component. I don't want to test the child component, so i mocked the child component using MockComponent({ selector: 'foo', output: ['change'] })
.
I looked at the spectator sources and saw that it created an EventEmitter
for every output. Ok so far, but how do i get the mock component instance and emit the event?
const component = spectator.debugElement.query(By.css('foo')).componentInstance;
component.change.emit('foo');
expect(service.update).toHaveBeenCalledWith('foo');
Afaics there is no easier way to do this using spectator? Also, perhaps we should document this? :)
I have a component with a form. However, the typeInElement()
function does not seem to work with the ngModel
directive. I tried solving it with fakeAsync
and tick()
but the form control stays empty, and so does the model.
My component (simplified):
@Component({
selector: 'app-foo',
template: '<form><input type="text" name="foo" class="foo" [(ngModel)]="model.foo" /></form>'
})
class FooComponent {
model = {
foo: ''
};
}
My test code (simplified):
describe('FooComponent', () => {
const createComponent = createTestComponentFactory({
component: FooComponent,
imports: [FormsModule],
});
let spectator: Spectator<FooComponent>;
it('should search when submitting form', fakeAsync(() => {
spectator = createComponent();
spectator.typeInElement('My value', '.foo');
// tick(); // I tried this
// spectator.detectChanges(); // ...and this
expect(spectator.component.model.foo).toBe('My value'); // fails
}));
});
Any help on this?
According to internals.d.ts, the query method has the following types:
/**
*
* @param {Type<T> | string} directiveOrSelector
* @param {{read}} options
* @returns {T}
*/
query<R>(directiveOrSelector: string, options?: {
read;
}): Element;
query<R>(directiveOrSelector: Type<any>, options?: {
read;
}): R;
That means that you have to cast the return value of the query method when passing a selector before you can use it. Can't we change the API so you can do: spectator.query<HTMLElement>('a').click()
?
...without adding the rxjs-compat
module (which is meant to be a temporary workaround).
ERROR in node_modules/@netbasal/spectator/dist/src/internals.d.ts(10,10): error TS2305: Module '".../node_modules/rxjs/Observable"' has no exported member 'Observable'.
node_modules/rxjs/Observable.d.ts(1,15): error TS2307: Cannot find module 'rxjs-compat/Observable'
ng add @netbasal/spectator
command.For a filter endpoint I have to add query params to a GET request.
Before this expectOne('endpoint/to/call', HTTPMethod.GET)
was working great, but now I have added params to the request and suddenly it stops working and logs Expected one matching request for criteria "Match method: GET, URL: 'endpoint/to/call", found none.
. I also see a logging line saying Expected no open requests, found 1: GET endpoint/to/call
The HTTP call before:
return this.http.get<SomeObject>(`endpoint/to/call`);
The HTTP call after:
const params = new HttpParams({
fromString: 'value1=test&value2=testing'
});
return this.http.get<SomeObject>(`endpoint/to/call`, {params: params});
Thanks!
Created new angular-cli version 7 project and did a test drive using Spectator.
Received an error when running ng test:
ERROR in node_modules/@netbasal/spectator/lib/mock.d.ts(1,23): error TS2688: Cannot find type definition file for '@types/jest'.
Installing @types/jest does not help, since I have inference with jasmine types (want to stick to Jasmine)
Any help is appreciated
I've been trying to test a directive with EasyTestWithHost
and it's fantastic for all my cases save one -- I have one test case where I need to set-up a spy which I know should get triggered in the initial change detection cycle (in ngAfterContentInit
in my case).
If I do TestBed.get(token)
before I called this.create()
(in order to spy on a method), I get an error saying overrideComponent
cannot be called after inject
which makes sense. Ideally I'd like to have an option for this.create()
which suppresses the first change detection in the host so that I can call it once I've created my spy.
Looking at the code, this may not be a trivial change as the change detection may be needed for other aspects of the test set-up. I'll have a play if I get time and submit a PR.
Hi,
Some of my co-workers had trouble with life cycle hooks. This may be obvious for experienced Angular devs, but maybe it would be nice to have a dedicated page in the docs Components -> Lifecycle hooks
:
Lifecycle hooks
When using regular
Spectator
(withcreateTestComponentFactory
), lifecycle hooks on the tested component are not triggered. You have trigger them yourself, e.g. by callingspectator.component.ngOnChanges()
. This is because live cycle hooks only get triggered when input bindings are updated by a parent view.To profit from automatic life cycle hooks, you will need the
SpectatorWithHost
.host = createHost(`<hello title="Hello"></hello>`);Please note that, for complex input bindings,
createHost('<my-component></my-component>', true, { someInput: 'value' })
will not work, because the third argument will only update the component instance and not trigger life cycle hooks. Instead, use a custom host:@Component({selector: 'host'}) class HostComponent { myTitle = 'World'; } describe('HelloComponent', () => { let host: SpectatorWithHost<HelloComponent, HostComponent>; const createHost = createHostComponentFactory({ component: HelloComponent, host: HostComponent }); it('does trigger lifecycle hooks when using a host ', () => { host = createHost(`<hello [title]="myTitle"></hello>`); }); });
I hope this can be of any value for the docs ;)
Hi, unfortunately v1.13.1 isn't available on npm: https://www.npmjs.com/package/@netbasal/spectator
I will be needing the patch which supports content projection in mocked components. It exists in the aforementioned release.
As we are still using angular 5, I cannot use the next available version which contains this patch (v2.0.2). Will it be possible to republish v1.13.1? Thanks! We are currently using v1.13.0.
For the meantime, our workaround is to explicitly set the template option in MockComponent to <ng-content></ng-content>
I'm trying to test a component that calls a service when a child component updates using (ngModelChange)="update($event)"
. However the typescript compiler doesn't recognize the ngModelChange
output on the component (there is no property on the component class).
error TS2339: Property 'ngModelChange' does not exist on type 'EditAvatarComponent'.
Caused by:
fit('should update the user when the avatar changes', () => {
const component = spectator.query<EditAvatarComponent>(EditAvatarComponent);
component.ngModelChange.emit({ image: 'foo' });
expect(currentUserService.update).toHaveBeenCalledWith({ avatar: { image: 'foo' } });
});
Any ideas?
Hi,
I just wanted to share this Gist: testing HTTP interceptors with Spectator.
Maybe it's nice to have a special HttpInterceptorFactory
on the roadmap of Spectator. 😎
At first glance it makes perfect sense that createService
doesn't need declarations array however when testing a service depending on RouterModule
there is a need to define routes. And routes must lead to a component.
I could mock Router
and that would solve my problem. However that means also mimicking internal behaviour of Router
.
What do you think about allowing declarations
also for createService
?
Following your Widget Example,
Lets Say I have a Test Component A that depends on Service B, where Service B also depends on Service C. If i add the service B to Mocks, i still get the error saying "No provider for Service C". Isn't is supposed to mock the dependency tree? How can i solve this error?
So jasmine-jquery defines toHaveAttr as: https://github.com/velesin/jasmine-jquery/blob/master/lib/jasmine-jquery.js#L466
For example: expect(spectator.query('span')).toHaveAttr('foo', 'bar');
The current spectator matcher is a lot more verbose:
expect(spectator.query('span')).toHaveAttr({ attr: 'foo', value: 'bar' });
Can we change the API so it matches jasmine-jquery? 😄
Any plans to support angular 6 soon (without needing rxjs-compat)?
Installing the latest version of spectator (2.0.3
) in a project with Angular 7.0.0 gives the following warnings:
warning " > @netbasal/[email protected]" has incorrect peer dependency "@angular/common@^6.0.0".
warning " > @netbasal/[email protected]" has incorrect peer dependency "@angular/core@^6.0.0".
Since Angular 7 was officially released on 2018-10-18, it would be nice if spectator supported it.
Today, when writing unit tests, I wanted to improve the organisation of our testdata. I came up with a simple test fixture generator:
// Example domain model
interface Person {
id: string;
name: string;
dateStarted: number;
activated?: boolean;
department: Department;
}
interface Department {
id: string;
name: string;
}
// Fixture factory creator
function createFixtureFactory<T>(defaultObject: T): (overrideObject?: Partial<T>) => T {
return (overrideObject: Partial<T> = {}) => {
return Object.assign({}, defaultObject, overrideObject);
};
}
// Example fixture factories
class DepartmentFixtures {
static createDefault = createFixtureFactory<Department>({
id: '1',
name: 'foo'
})
}
class PersonFixtures {
static createDefault = createFixtureFactory<Person>({
id: '1',
activated: true,
dateStarted: 1,
name: 'foo',
department: DepartmentFixtures.createDefault()
});
static createCrasyPerson = createFixtureFactory(PersonFixtures.createDefault({
name: 'crasy'
}));
}
// example test
describe('', () => {
const testPerson1 = PersonFixtures.createDefault({
id: '1',
department: DepartmentFixtures.createDefault({
name: 'joe'
})
});
const testPerson2 = PersonFixtures.createDefault({
id: '2',
department: DepartmentFixtures.createDefault({
name: 'joe'
})
});
});
The benefits:
any
objects in unit testsWhat about adding such feature to Spectator? Or should it remain in a separate util library?
ping @NetanelBasal
When testing data service using createHTTPFactory
I see errors in the console saying that the test has no expectations. Looks like expectOne
is not connected to jasmine.
Simple fix would be to add expect(true).toBe(true);
to expectOne
but I am not sure if it's the correct one.
Tried with spectator 3.3.0 but it behaved same with the older version.
We use typescript strict mode in our application but spectator fails to compile with following error:
ERROR in node_modules/@netbasal/spectator/lib/matchers-types.d.ts(13,18): error TS7031: Binding element 'attr' implicitly has an 'any' type.
node_modules/@netbasal/spectator/lib/matchers-types.d.ts(13,24): error TS7031: Binding element 'val' implicitly has an 'any' type.
node_modules/@netbasal/spectator/lib/matchers-types.d.ts(14,21): error TS7006: Parameter 'attr' implicitly has an 'any' type.
node_modules/@netbasal/spectator/lib/matchers-types.d.ts(14,27): error TS7006: Parameter 'val' implicitly has an 'any' type.
node_modules/@netbasal/spectator/lib/matchers-types.d.ts(18,18): error TS7031: Binding element 'prop' implicitly has an 'any' type.
node_modules/@netbasal/spectator/lib/matchers-types.d.ts(18,24): error TS7031: Binding element 'val' implicitly has an 'any' type.
node_modules/@netbasal/spectator/lib/matchers-types.d.ts(19,20): error TS7006: Parameter 'prop' implicitly has an 'any' type.
node_modules/@netbasal/spectator/lib/matchers-types.d.ts(19,26): error TS7006: Parameter 'val' implicitly has an 'any' type.
node_modules/@netbasal/spectator/lib/matchers-types.d.ts(21,20): error TS7006: Parameter 'prop' implicitly has an 'any' type.
node_modules/@netbasal/spectator/lib/matchers-types.d.ts(21,26): error TS7006: Parameter 'val' implicitly has an 'any' type.
node_modules/@netbasal/spectator/lib/matchers-types.d.ts(29,18): error TS7031: Binding element 'data' implicitly has an 'any' type.
node_modules/@netbasal/spectator/lib/matchers-types.d.ts(29,24): error TS7031: Binding element 'val' implicitly has an 'any' type.
node_modules/@netbasal/spectator/lib/matchers-types.d.ts(49,32): error TS7031: Binding element 'selector' implicitly has an 'any' type.
node_modules/@netbasal/spectator/lib/matchers-types.d.ts(49,42): error TS7031: Binding element 'text' implicitly has an 'any' type.
I created a repository to reproduce the issue: https://github.com/MartinNuc/spectator-strict
just run npm install
and npm test
.
I was able to fix the issue by adding types to matchers-types.d.ts
. I will create PR.
I am working on a new feature that I would like to eventually submit as a pull request, but that feature makes some changes to the library's dependencies and I was surprised to find that the dependencies are checked into git. Is this necessary? For one, it makes it hard to see the real changes a pull request is making as they are buried in the noise of minor dependency updates.
This is my code :
//app.component.ts
spectator = createComponent({}, false);
app = spectator.debugElement.componentInstance;
translate = TestBed.get(TranslateService);
});
it('should create the app', async(() => {
spyOn(spectator.component, 'setupLanguage').and.callThrough();
spectator.fixture.detectChanges();
app = spectator.debugElement.componentInstance;
expect(app).toBeTruthy();
expect(spectator.component.setupLanguage).toHaveBeenCalled();
}));```
**//app.component.spec.ts**
```export class AppComponent {
title = 'app';
languageKeys = ['en', 'de', 'fr', 'it'];
constructor(private translate: TranslateService) {
this.setupLanguage();
}
As seen in the DOM selector Jest spec in #61, the DOM selectors have not been exposed in Spectator's Jest API.
We should consider exposing them such that Jest users can access the complete Spectator API through the Jest subpackage.
Or should we only expose the APIs where the implementation differs?
This is an example tested service, which uses Angular's FormBuilder:
import {Injectable} from '@angular/core';
import {FormBuilder} from '@angular/forms';
@Injectable()
export class FormsService {
constructor(
private fb: FormBuilder,
) {}
}
Here's the test case for it, using the 'imports' property
import {ReactiveFormsModule} from "@angular/forms";
import {createService} from "@netbasal/spectator";
import {FormsService} from "./media.service";
describe('FormsService', () => {
const spectator = createService<FormsService>({
imports: [
ReactiveFormsModule
],
service: FormsService,
});
it('should create', () => {
expect(spectator.service).toBeTruthy();
});
});
What I get in an error - it doesn't find the FormBuilder:
Error: StaticInjectorError(DynamicTestModule)[FormsService -> FormBuilder]: StaticInjectorError(Platform: core)[FormsService -> FormBuilder]: NullInjectorError: No provider for FormBuilder!
Adding the following to ZippyComponent
:
<div (click)="toggle()" class="zippy__title" (keyup.enter)="toggle()">
and adding the following test:
it('should toggle the content when pressing "Enter"', () => {
host = createHost(`<zippy title="Zippy title"></zippy>`);
const pressEnter = () => {
host.dispatchKeyboardEvent('.zippy__title', 'keyup', /* Enter key */ 13);
};
pressEnter();
expect(host.query(".zippy__content")).toExist();
pressEnter();
expect(".zippy__content").not.toExist();
});
Will fail, though it's expected to succeed.
The following alternative code in (plain) Angular works (notice the differences in the pressEnter
function):
it('should toggle the content when pressing "Enter"', () => {
host = createHost(`<zippy title="Zippy title"></zippy>`);
const pressEnter = () => {
host.getNativeElement('.zippy__title').dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter' }));
host.detectChanges();
};
pressEnter();
expect(host.query(".zippy__content")).toExist();
pressEnter();
expect(".zippy__content").not.toExist();
});
I have a very simple service that runs a very simple POST.
return this.http.post<SearchRequest>(`/api/2.0/search/userproduct/${userProductId}`, request)
.pipe(
catchError((e: HttpErrorResponse) => {
Logger.error('SearchService', 'search', e.message);
return of(undefined);
})
);
I can easily cause an error to happen by calling TestResponse.error('');
But this doesn't allow me to test that I returned something. How do I write the test that tests for receiving undefined in an error condition? And what if I handled a 500 differently from a 204 or 401/3?
We successfully migrated from Karma/Jasmine to Jest (with Spectator unit tests).
Spectator uses Jasmine for the mocking implementation, this still works fine. However, as Jest has its own mocking API, is it possible to make Spectator use Jest for mocks? This prevents mixing two different API's for mocking.
e.g.
const createComponent = createTestComponentFactory({
component: MyComponent,
mocks: [MyService]
});
let spectator: Spectator<MyComponent>;
beforeEach(() => {
spectator = createComponent();
// spectator.get(MyService).someMethod.andReturn('foo'); // with Jasmine
spectator.get(MyService).someMethod.mockReturnValue('foo'); // with Jest
});
Maybe by checking if Jest is available and automatically use Jest instead of Karma, or by configuring Spectator somehow?
I am willing to contribute for this feature request.
@benelliott your application tests are working in IE? IE stopped working when we upgrade to 2+. 2- is working.
cc @dirkluijk
On https://netbasal.gitbooks.io/spectator/components/providers.html there is an error on the last line:
const queryService = host.query<QueryService>(QueryService, true);
should read:
const queryService = host.get<QueryService>(QueryService, true);
Component:
import {Component, Input} from "@angular/core";
@Component({
selector: "app-test-component",
template: `<div>Test text</div>`
})
export class TestComponent{
constructor() {
}
}
Unit test:
import {createHost, EasyTestWithHost} from 'ngx-easy-test';
import {TestComponent} from './test.component';
describe('TestComponent', () => {
type Context = EasyTestWithHost<TestComponent>;
createHost(TestComponent);
it('should create', function (this: Context) {
this.create(`<app-test-component></app-test-component>`);
expect(this.testedComponent.toBeTruthy()))
});
});
Console output when running npm run test:
ERROR in C:/Users.../node_modules/ngx-easy-test/src/easy-test.d.ts (2,13): All declarations of 'Matchers' must have identical type parameters.
ERROR in c:/users/.../node_modules/@types/jasmine/index.d.ts (26,41): Generic type 'Matchers<T>' requires 1 type argument(s).
ERROR in c:/users/.../node_modules/@types/jasmine/index.d.ts (27,39): Generic type 'Matchers<T>' requires 1 type argument(s).
ERROR in c:/users/.../node_modules/@types/jasmine/index.d.ts (152,24): Generic type 'Matchers<T>' requires 1 type argument(s).
ERROR in c:/users/.../node_modules/@types/jasmine/index.d.ts (302,15): All declarations of 'Matchers' must have identical type parameters.
ERROR in c:/users/.../node_modules/@types/jasmine/index.d.ts (333,14): Generic type 'Matchers<T>' requires 1 type argument(s).
ERROR in c:/users/.../node_modules/@types/jasmine/index.d.ts (429,24): Generic type 'Matchers<T>' requires 1 type argument(s).
ERROR in c:/users/.../node_modules/@types/jasmine/index.d.ts (442,30): Generic type 'Matchers<T>' requires 1 type argument(s).
Windows 10
Node 8.0
Typescript 2.3.4
Angular: 4.3.1
Described here: https://angular.io/guide/entry-components
Components that dynamically load other components by type need this, and it seems like a simple thing to add to spectator by changing config.ts.
Thanks!
I have a use case where a directive makes use of a service.
However, I can not figure out how to set this up with createHostComponentFactory
.
I've used
AppUnlessDirective
from the Angular docs as an example.
const createHost = createHostComponentFactory({
component: AppUnlessDirective,
providers: [AppUnlessService]
});
Giving the following error (because it's a directive and not a component?)
TypeError: Cannot read property 'componentInstance' of null
https://stackblitz.com/edit/spectator-directive-providers?file=app%2Fapp-unless.directive.spec.ts
The code is mentioned to be formatted using Prettier, and uses TSLint. However, the code within it is not conforming to the latter, and the use of Prettier's formatting isn't automatic via a pre-commit hook.
Problem
It's been impossible for a long time (possibly still is) to use Output() on a structural directive in Angular. To do that, one must use a de-sugarized version of a structural directive, i.e. with ng-template. I've been trying to test that case with spectator, since that's what I'm using to test my app, but I can't write the tests correctly with custom host component. I looked into that issue #13 but it didn't help me.
Example code
https://stackblitz.com/github/slawojstanislawski/spectator-structural
Hi,
I want to configure typing autocompletion when using your library but I couldn't find anything in the README. Would you please tell me how to configure it ?
Given the following component:
@Component({
selector: 'my-component',
template: `
<div>
<ng-container [ngTemplateOutlet]="content"></ng-content>
</div>`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
})
export class MyComponent {
@Input() someInput: boolean;
@Input() content: TemplateRef;
}
And the following test code:
let spectator: SpectatorWithHost<MyComponent>;
const createHost = createHostComponentFactory({
component: MyComponent,
shallow: true
});
it('input test', () => {
spectator = createHost(`
<ng-template #content>
<span>Sample content</span>
</ng-template>
<my-component
[someInput]="someInput"
[content]="content">
</my-component>
`, true, { someInput: true })
expect(spectator.component.someInput).toBe(true);
});
The test 'input test'
fails, and someInput
is undefined
.
passing false
as the detectChanges
parameter (to createHost
) results in someInput
being true
, but as soon as you run a changeDetection (via spectator.detectChanges()
to check the DOM, the values reset.
Great library! Looking forward to trying it out.
In your example, you have:
host = createHost(`<zippy title="Zippy title"></zippy>`);
I was curious to know if you've thought of a way to pass complex objects as input parameters instead of just simple primitives. I occasionally create components that require this, usually receiving those objects from another component.
For example, It could be useful to be able to do something like this:
host = createHost(`<zippy title="Zippy title" [options]="options"></zippy>`, { options: ... });
Thanks!
Hi,
While is was testing with your beautiful library I ran into a exception at the mocks
part.
In most of my components I subscribe to an observable, for example this.route.paramMap.subscribe(paramMap => {});
and it will crash with TypeError: Cannot read property 'subscribe' of null
.
So I have created a Gist with minimal setup code as example for other people.
Maybe it's nice to make a way to configure the return value of the mocked functions. I saw the default return value was 'null' but with observables it would be nice to have some way to configure it.
Example (brain dump):
@Component({})
class MyComponent {
constructor(private barService: BarService,
private route: ActivatedRoute) {}
ngOnInit(): void {
// this fails without configuring the mocks:
// ERROR: Cannot read property 'subscribe' of null
this.route.paramMap.pipe(
switchMap(() => this.barService.getFoo())
).subscribe();
// and this fails too...
this.barService.getBar();
}
}
describe('', () => {
// proposal, a way to configure mocks in the factory?
// or just mock all Observables on the mock prototypes by default...
const createComponent = createTestComponentFactory({
component: MyComponent,
mocks: [[BarService, { getFoo: of('lorem'), getBar: 'ipsum' }]]
});
it('', () => {
const spectator = createComponent();
// detectChanges should work without issues
});
})
I would like to use this library with Jest testing framework, however when I follow the instructions provided on this repo https://github.com/thymikee/jest-preset-angular I'm getting errors:
Cannot read property 'hostFixture'/'testedComponent'/'create' of null
.
I'm using toBeEmpty()
to ensure that an element with a sub-element that has either an *ngFor
or an *ngIf
contains no children, but both of those directives add a comment node to the DOM, which causes toBeEmpty()
to return false
.
If you don't like this idea, maybe adding another matcher that has this behavior is a better alternative.
When testing a directive according to https://netbasal.gitbook.io/spectator/directives the host.query
doesn't work because it expects to have debugElement which is not defined for a directive.
Results in TypeError: Cannot read property 'query' of null
with stack trace:
at <Jasmine>
at http://localhost:9877/node_modules/@netbasal/spectator/fesm5/netbasal-spectator.js?:896:1
at SpectatorWithHost.push../node_modules/@netbasal/spectator/fesm5/netbasal-spectator.js.Spectator.query (http://localhost:9877/node_modules/@netbasal/spectator/fesm5/netbasal-spectator.js?:517:1)
Possible solution: use hostDebugElement
when debugElement
is not defined.
What do you think?
Hi!
First of, thanks for an awesome testing framework :)
I'm currently mocking some outputs using MockComponent. There seems to be an issue where all instances of the mock component share the same event emitter. Calling emit on a single instance will therefore call the bound method multiple times, once for each instance.
Here's a test to demonstrate this:
import { Component, EventEmitter, Output } from "@angular/core";
import { createTestComponentFactory, MockComponent } from "@netbasal/spectator";
@Component({
selector: "app-subcomponent-component",
template: ``,
})
class SubcomponentDemoComponent {
@Output()
public emitter = new EventEmitter();
}
@Component({
selector: "app-demo-component",
template: `
<app-subcomponent-demo (emitter)="boundMethod()"></app-subcomponent-demo>
<app-subcomponent-demo (emitter)="boundMethod()"></app-subcomponent-demo>
`,
})
class DemoComponent {
public boundMethod() {}
}
describe("spectator", () => {
const create = createTestComponentFactory({
component: DemoComponent,
shallow: true,
declarations: [
MockComponent({
selector: "app-subcomponent-demo",
identifier: SubcomponentDemoComponent,
outputs: ["emitter"],
}),
],
});
it("should differentiate between instances of event emitters", () => {
const spectator = create({}, false);
spyOn(spectator.component, "boundMethod");
const instances = spectator.queryAll<SubcomponentDemoComponent>(SubcomponentDemoComponent);
instances[0].emitter.emit();
expect(spectator.component.boundMethod).toHaveBeenCalledTimes(1);
});
});
The function, boundMethod should only be called once because only one value is emitted, however, it is called twice.
Could you have a look into this?
Thanks and cheers,
Oliver
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.