Stencil version:
@stencil/[email protected]
I'm submitting a:
[X ] bug report
[ ] feature request
[ ] support request => Please do not submit support requests here, use one of these channels: https://forum.ionicframework.com/ or https://stencil-worldwide.slack.com
Current behavior:
Event emitters are not initialised during tests if the class is just instantiated and not rendered.
Expected behavior:
Should be able to test components that emit events even without rendering.
Steps to reproduce:
Emit an event in the code component. In testing, initialize the component and trigger the action that will emit the event.
The following error will appear:
TypeError: Cannot read property 'emit' of undefined
Related code:
Add the following to stencil-component-starter project and run tests
// my-component.ts
@Event() myEvent: EventEmitter<boolean>
doSomething() {
this.myEvent.emit(true)
}
// my-component.spec.ts
it('should emit on method call', () => {
let comp = new MyComponent();
comp.doSomething();
});
Other information:
Thanks for opening an issue with us! Would you mind sharing what your whole test looks like?
I am experiencing this as well using @stencil/[email protected]. Here's a trimmed down version of the component and spec I'm working with:
my-component.tsx:
import { Component, Event, EventEmitter } from '@stencil/core';
@Component({
tag: 'my-component'
})
export class MyComponent {
@Event() toggle: EventEmitter;
opened: boolean = false;
handleToggle() {
this.opened = !this.opened;
this.toggle.emit();
}
render() {
return (
<div>my component</div>
);
}
}
my-component.spec.tsx:
import { MyComponent } from './my-component';
describe('my-component', () => {
describe('handleToggle()', () => {
it('toggles `opened`', () => {
const cmp = new MyComponent();
cmp.handleToggle();
expect(cmp.opened).toEqual(true);
});
it('emits toggle event', () => {
const cmp = new MyComponent();
const spy = jest.spyOn(cmp.toggle, 'emit');
cmp.handleToggle();
expect(spy).toHaveBeenCalled();
});
});
});
Results:
FAIL src/my-component/my-component.spec.tsx
● my-component › handleToggle() › toggles `opened`
TypeError: Cannot read property 'emit' of undefined
● my-component › handleToggle() › emits toggle event
Cannot spyOn on a primitive value; undefined given
@jgw96 the test above is the whole code and test. You can just add my code to stencil-component-starter. (just need to fix the relevant imports). There is not even a need to expect, as the test will fail because of the error.
Related to #618 ?
@bitflower I don't think so. This is 0.6.18 thing.
I'm experiencing this issue as well. Is there a way to mock or provide the EventEmitter to the component when instantiated (vs rendered)?
@Tallyb The event emitter is actually really easy to mock. This will at least get you around the issue:
it('should test something', () => {
// mock the event emitter
component.someEventEmitter = {
emit: () => {}
};
const spy = jest.spyOn(component.someEventEmitter, 'emit');
component.methodThatCallsEventEmitter();
expect(spy).toHaveBeenCalled();
});
Hope this helps!
@hartjus - helps indeed. thanks for this solution.
I'm experiencing this issue after cloning the stencil-app-starter and copy pasting some event code from the documentation (updated to 0.17.1)
import { Component, Event, EventEmitter } from '@stencil/core';
@Component({
tag: 'app-root',
styleUrl: 'app-root.css',
shadow: true
})
export class AppRoot {
@Event() todoCompleted: EventEmitter;
todoCompletedHandler() {
this.todoCompleted.emit();
}
render() {
return (
<div>
<header>
<h1>Stencil App Starter</h1>
</header>
<main>
<button onClick={this.todoCompletedHandler}></button>
</main>
</div>
);
}
}
edit works when binding the method like this:
render() {
return (
<div>
<header>
<h1>Stencil App Starter</h1>
</header>
<main>
<div>
<button onClick={this.playTriggerHandler.bind(this)}></button>
<video-player></video-player>
</div>
</main>
</div>
);
}
}
You could also do it like using an arrow function.
todoCompleteHandler = () => {
this.todoCompleted.emit();
}
@Tallyb The event emitter is actually really easy to mock. This will at least get you around the issue:
it('should test something', () => { // mock the event emitter component.someEventEmitter = { emit: () => {} }; const spy = jest.spyOn(component.someEventEmitter, 'emit'); component.methodThatCallsEventEmitter(); expect(spy).toHaveBeenCalled(); });Hope this helps!
This was very helpful, thank you!!
I did find that typescript was complaining if I just mocked it with emit: () => {} but I solved that by changing it to this: emit: (data?: any) => new CustomEvent(data), in case that helps anyone else...
Most helpful comment
@Tallyb The event emitter is actually really easy to mock. This will at least get you around the issue:
Hope this helps!