I created a component, whose action uses store service. How can I stub this action from integration test?
// app/components/invoice-form.js
export default Ember.Component.extend({
actions: {
loadPartners() {
let self = this;
this.get('store').findAll('partner').then(function(partners) {
self.set('partners', partners);
});
}
}
});
In this component's template I pass this action as closure to child component:
{{button-confirmation text="Are you sure?" onConfirm=(action "loadPartners")}}
In my integration test, I render the component as usual
this.render(hbs`{{invoice-form}}`);
Action loadPartners is not passed as argument to component helper. Its just static component's action.
So the question is how to stub action loadPartners from integration test?
In an integration test, you shouldn't change the inner part of components. Instead of it, you should change implementations of your component's dependencies.
So in this case, you should stub store. See how to stub store.
Ref from Guide
Related
I have a component, say MyComponent. I have injected a service into this component, say MyService.
When 'MyComponent' is used inside another component(say ParentComponent), it passes some arguments to 'MyComponent' through the template. 'MyService' which is injected in 'MyComponent' needs this template arguments. How can I pass it to the service while injecting the service?
Right now I have written a special function inside the service which will be called in the init() function of 'MyComponent'. I don't know if that is the right way.
Here is a rough code example:
parent-component/template.hbs:
// some html
{{my-component myOptions=parentOptions}}
// some html
my-component/component.js:
export default Component.extend({
// some js
myService: inject.service(),
init() {
this._super(...arguments);
get(this, 'myService').passDownMyArguments(get(this, 'myOptions'));
},
// some js again
})
I want to know if I can pass 'myOptions' to the service when it is injected rather than calling another function within the service.
I guess you can not.
As you may see from the component lifecycle https://guides.emberjs.com/v2.6.0/components/the-component-lifecycle/,
component will receive parameters from template after initialization only,
so you can pass parameters at the next lifecycle hooks like this:
...
myService: inject.service(),
didReceiveAttrs() {
this.get('myService').myFunction(this.get('myOptions'));
}
No, you can't pass values to a service when you inject it. What you are doing is the best way to handle things given your setup. But a question: should your service be up in the containing component instead?
How to test a method inside a service that returns a store in Ember Unit Test using qunit
export default Ember.Service.extend({
store: Ember.inject.service(),
setSomeProps() {
this.get('store').find('somemodel', id)
.then((someData) => {
this.set('someProp', someDate.get('name'));
});
}
});
The setSomeProps is a method inside my service, I am fairly new to ember and cannot get my head around ember unit test. Whats the best way to write a unit test for this function
You can mock the store service in your unit test of store-caller-service.
You should use wait function for asynchronous test behaviour which is described here.
Take a look at this twiddle example
How would one go about calling a function from an imported action module? Here is my component
class Task extends Component {
handleClick(e) {
e.preventDefault();
Actions.returnTask(this.props.id);
}
render() {...}
}
and tests that look like this:
jest.dontMock('./Task');
jest.dontMock('./Actions');
describe('Tasks', function() {
let renderedComponent;
let mockReturnTask;
let TaskActions;
beforeEach(function() {
renderedComponent = TestUtils.renderIntoDocument(
<Task task={testTask} key={1} />
);
Actions = require('./Actions');
mockReturnTask = jest.genMockFn();
Actions.returnTask = mockReturnTask;
});
it('should call a returnTask action on Actions', function() {
renderedComponent.handleClick(event);
expect(mockReturnTask).toBeCalled();
});
});
When running my test it tells me that the function was not called. If I do expect(Actions.returnTask).toBeCalled(); I get an error message that toBeCalledWith() should be used on a mock function error. How do I mock a function on the external Actions and check that it is called in my tests? I feel like my method above should be working.
Your example doesn't include the require of Task but I'm pretty sure of what happened here. Jest users must be aware of what require they do, and when.
Chronologically:
jest is set to never mock Actions and Task
Task component is required, jest requires it for real
Actions is required because it is required in the Task component. jest requires it for real.
Task component is instantiated
Actions.returnTask is monkey-patched but it's not the function the component is bind with, it's a new function who exist only in the test.
To make your component using a jest mocked function, you have to require and monkey-patch Actions before requiring Tasks
Be aware that you don't have to unmocked Actions if you mock a function of Actions right after. It's exactly what the jest auto-mocking killing feature is made for : remove jest.dontMock('./Actions'); and all references to your Actions module. The test should be working.
I've got openModal action defined on application route. I'm trying to call this action from within a component.
If I use syntax for action bubbling:
{{my-component openModal="openModal"}}
then everything works as expected and I can trigger this action using this.sendAction("openModal").
However, I'm not sure how to get the same result using the new closure syntax:
{{my-component openModal=(action "openModal")}}
In this case, Ember complains that there's no action openModal defined on the controller. Do I have to define this action on every controller that uses my-component? Is there a way to somehow use target option to tell Ember that this action is defined on a route? Is it ok to mix bubbling and closure syntax in a single component?
I'm using Ember 2.0 beta 1.
Until routable components are introduced somewhere in Ember 2.1 or 2.2 or 2.3 or 2.4 or 2.5 or 2.6 or 2.7, it is impossible to pass a closure action from a route.
For now, you can only pass closure actions from a controller and on to child components.
UPD: Miko Paderes hints that an addon is available: https://github.com/dockyard/ember-route-action-helper
Try this:
{{my-component openModal=(action send "openModal")}}
...which makes use of the send method on the controller.
I can't say I fully understand it given that the second argument to send is the context but it is still passing additional arguments to my action in the route correctly. I'm currently using version 2.4.5.
You can send closure actions to the route by proxying them through the controller. This works with any Ember version that supports closure actions. The only caveat is that the action name must differ from controller and route.
For instance, given this template:
{{foo-component myAction=(action 'foo')}}
You would need a foo action on your controller, which could proxy to the route:
proxyFooTo: 'fooBar',
actions: {
foo(context) {
this.send('proxyFooTo', context);
}
}
Then on the route
actions: {
fooBar(context) { ... handle fooBar ... }
}
It is also possible to avoid creating a controller, or if there is one already avoid adding more logic in it, by specifying the target property
http://emberjs.jsbin.com/fiwokenoyu/1/edit?html,js,output
in route
js
setupController(controller,model){
this._super(...arguments);
controller.set('theRoute', this);
},
actions:{
openModal(data){
/*...*/
}
}
in template
hbs
{{my-component openModalAction=(action 'openModal' target=theRoute)}}
Try the Ember add-on called ember-route-action-helper,
https://github.com/dockyard/ember-route-action-helper
You can just replace route-action with action at the time "routable- components" become available.
{{todo-list todos=model addTodo=(route-action "addTodo")}}
So the code above has a similar effect as
Inside the route,
setupController(controller,model){
this._super(...arguments);
controller.set('theRoute', this);
},
actions:{
addTodo(data){
/*...*/
}
}
Inside the HBS,
{{todo-list addTodo=(action 'addTodo' target=theRoute)}}
How do I run a function after an Ember View is inserted into the DOM?
Here's my use-case: I'd like to use jQuery UI sortable to allow sorting.
You need to override didInsertElement as it's "Called when the element of the view has been inserted into the DOM. Override this function to do any set up that requires an element in the document body."
Inside the didInsertElement callback, you can use this.$() to get a jQuery object for the view's element.
Reference: https://github.com/emberjs/ember.js/blob/master/packages/ember-views/lib/views/view.js
You can also use afterRender method
didInsertElement: function () {
Ember.run.scheduleOnce('afterRender', this, function () {
//Put your code here what you want to do after render of a view
});
}
Ember 2.x: View is deprecated, use component instead
You have to understand the component's lifecycle to know when does certain things happen.
As components are rendered, re-rendered and finally removed, Ember provides lifecycle hooks that allow you to run code at specific times in a component's life.
https://guides.emberjs.com/v2.6.0/components/the-component-lifecycle/
Generally, didInsertElement is a great place to integrate with 3rd-party libraries.
This hook guarantees two (2) things,
The component's element has been both created and inserted into the DOM.
The component's element is accessible via the component's $() method.
In you need JavaScript to run whenever the attributes change
Run your code inside didRender hook.
Once again, please read the lifecycle documentation above for more information
Starting with Ember 3.13, you can use components that inherit from Glimmer, and this example below shows what that could look like:
import Component from '#glimmer/component';
import { action } from '#ember/object';
/* global jQuery */
export default class MyOctaneComponent extends Component {
#action configureSorting(element) {
jQuery(element).sortable();
}
}
<div {{did-insert this.configureSorting}}>
<span>1</span>
<span>2</span>
<span>3</span>
</div>
These view style components don't have lifecycle hooks directly, instead, you can use render-modifiers to attach a function. Unofficial introduction to modifiers can be found here
The benefit of this is that, it's clearer what the responsibilities of the template are and become.
Here is a runnable codesandbox if you want to play around with this:
https://codesandbox.io/s/octane-starter-ftt8s
You need to fire whatever you want in the didInsertElement callback in your View:
MyEmberApp.PostsIndexView = Ember.View.extend({
didInsertElement: function(){
// 'this' refers to the view's template element.
this.$('table.has-datatable').DataTable();
}
});