polymer unit test mocking dependencies - unit-testing

Im just starting on polymer. Im trying to unit test a custom element that has dependencies and I would like to fake/mock these out.
I've found Scott Miles recommendation on how to mock the core-ajax implementation. I thought I could follow that pattern easily but this only works as long as my element does not import the about to be mocked (core-ajax in this case) element.
If it does import it, then when the test tries to run I get
'Uncaught NotSupportedError: Failed to execute 'registerElement' on 'Document': Registration failed for type 'core-ajax'. A type with that name is already registered.'
If I could do something like document.unregister the core-ajax element and import it again in my test, Id be a much happier dev!!!
Polymer is awesome but if I can not unit test it, then it presents major risks (at least when building an app that will need to be maintained/changed)
How are you guys working around this? I've been digging into Polymer and PolymerLab elements repo and most of them lack tests. So far I;ve not found much reference on how to do it.
Thanks for the help!
Santiago
Scotts' recommendation was:
Instead of importing core-ajax/core-ajax.html, create your own core-ajax element.
<polymer-element name="core-ajax" attributes="response">
<script>
Polymer('core-ajax', {
attached: function() {
this.response = ['a', 'b', 'c'];
}
});
</script>
</polymer-element>
Obviously, this is just an example, the actual implementation depends on the desired mocking behavior.
This is just one way to solve it, there are many others. I'm interested to hear what you find (in)convenient.

This question is a little old. Figured I'd provide an update since this is a pretty common situation.
Polymer CLI is the recommended approach for unit testing Polymer elements. The underlying library that it uses for testing is called web-component-tester (WCT). WCT has support for stub elements. Basically, if one of your tests relies on another element to return data, you can create a stub of that element that always returns consistent data.
JS in the unit test code for specifying the stub element:
setup(function() {
replace('paper-button').with('fake-paper-button');
});
Element to be tested:
<dom-module id='x-el'>
<template>
<paper-button id="pb">button</paper-button>
</template>
</dom-module>
At test runtime, the content template would be stamped out as:
<dom-module id='x-el'>
<template>
<fake-paper-button id="pb">button</fake-paper-button>
</template>
</dom-module>
https://www.polymer-project.org/1.0/docs/tools/tests#create-stub-elements

You can try registering it imperatively with js or extend every single element you are testing and override its properties or methods you want to mock.
i think that is just about it. It's like my google-map custom element, i import the google-map and change stuff around like so:
<polymer-element name="core-gmaps" attributes="lat long mapzoom markerlat markerlong markertitle" extends="google-map">
<template>
<style>
:host{
width: 100%;
}
#vivaMap {
display: block;
height: 100%;
width: 100%;
}
</style>
<google-map id="vivaMap" latitude="0" longitude="0" zoom="18">
<google-map-marker id="vivaMarker" title="" latitude="0" longitude=""></google-map-marker>
</google-map>
</template>
<script>
Polymer("core-gmaps",{
ready: function(){
var map = this.$.vivaMap;
map.latitude = Number(this.getAttribute('lat'));
map.longitude = Number(this.getAttribute('long'));
map.zoom = Number(this.getAttribute('mapzoom'));
var mapMarker = this.$.vivaMarker;
mapMarker.latitude = Number(this.getAttribute('markerlat'));
mapMarker.longitude = Number(this.getAttribute('markerlong'));
mapMarker.title = this.getAttribute('markertitle');
/*map.addEventListener('google-map-ready', function(e) {
console.log('Map loaded!');
});*/
}
});
</script>
</polymer-element>
I am still not sure if it was worth it professionally (i may end up not using it), but was totally worth it intellectually. learned some nice stuff. since i'm extending google-map it gets registered once and only once.
EDIT:
in my case i used the ready event because i couldn't manipulate the map per se without it being at least ready. but you can choose the event callback from the lifecycle methods. The list is here.
PS.:Yes, i didn't use data binding because i couldn't. The google map api was complaining about it being NaN so i had to cast it.

Related

Trouble unit testing onClick method using Mocha Chai and Enzyme

I'm having trouble with unit testing using mocha, chai and enzyme for the following. I can't seem to understand how to unit test methods in components and how to unit test onClick methods will call those methods.
The following is what I am trying to unit test:
<Link to="/create-new-template-results" onClick={this.checkLink}>
<Button
buttonname="Next_button"
variant="primary"
label="Save"
onClickMethod={() => this.submitTemplateCreation()}
disabled={!this.disabledButtonCheck()}
/>
</Link>
.
Header: '',
Cell: value => {
return (
<div>
<img
height={34}
src="https://content.usaa.com/mcontent/static_assets/Media/icon-trash.svg"
onClick={() => this.removeAttribute(value)}
/>
</div>
);
}
.
removeAttribute = value => {
this.props.change('templateAttributeForm', value.original.name, '');
this.props.removeAttributeItem(value.index);
};
submitTemplateCreation() {
let profLvlData = Object.values(this.props.templateAttributeFormData);
let attrData = Object.keys(this.props.templateAttributeFormData);
let attributeProfLvl = attributeProfLvlUtil(attrData, profLvlData);
let templateCreationJSON = templateCreationPOSTFilter(attributeProfLvl, this.props.templateFormData);
this.props.submitTemplateCreation(templateCreationJSON);
}
Chai provides some nice tools for testing things exactly like what you are talking about.
You'll want to render your component somehow in the virtual DOM, either by using enzyme's "shallow" or "mount" functions.
Once you've done that, you can access the component using .find, and "simulate" an event using .simulate like so.
wrapper.find('Button').at(0).simulate('click');
This will find all of the 'Button' components in your wrapper, take the first one, and simulate a click. From there you can use expect() combined with any of the ways Chai provides to examine the state of the component in order to test that your button did what it was supposed to.
Since it seems like you are particularly interesting in the calling of the onClick function itself, I will add that you can specifically check to see if a function is called by doing the following with Chai.
expect(MyComponent.prototype.myOnClickFunction).to.have.property('callCount', 1);

Diving into props while testing React using Enzyme

Is there a better solution to dive into props.text in Enzyme?
Component:
export function TitleText ({ text, info, required }) {
return (
<div className={style.titleText}>
<div className={style.titleText} style={{ margin: 0 }} required={required}>{text}</div>
{info ? <InfoIcon className={style.infoIcon} /> : ''}
</div>
)
}
Test:
it('renders text from its props', () => {
const wrapper = setupTitleText('Test')
expect(wrapper.find(`.${style.titleText}`).node.props.children[0].props.children).toEqual('Test')
})
You can access the props using props(). Should at least work on shallow and mounted components.
wrapper.props().text
However, I would add a data attribute for testing.
Doing so would
let anyone working on your code would know that this element is being tested
let anyone working on your code would know that this element is being tested
prevent multiple elements being returned when you're looking to test something specific
prevent others breaking your tests if they decide to change the class.

Template #each & rendering

Why does the template get rendered the number of times that correlates with the Each in my template.
<template name="carousel">
<div class="pikachoose">
<ul class="carousel" >
{{#each article}}
<li><img src="{{image}}" width="500" height="250" alt="picture"/><span>{{caption}}</span></li>
{{/each}}
</ul>
</div>
</template>
Template.carousel.article = function () {
return News.find({},{limit: 3});
}
Template.carousel.rendered = function() {
//$(".pika-stage").remove();
alert($(".carousel").html());
//$(".carousel").PikaChoose({animationFinished: updateNewsPreview});
}
In this case it would alert 3 times.
That's the way Meteor handles data updates. Your article data function returns a cursor that is to be used in the template. Initially, the cursor is empty, and data is pulled from the server, one article at a time. Each time an article is fetched, the contents of cursor are changed (it now has one more article), and therefore the reactive article method causes template to rerender.
If you need to be sure that your code runs only once, there are several possibilities depending on what you need.
The easiest one is just to use created instead of rendered.
If you modify DOM elements, you can also mark elements you modify so that you won't process them twice:
Template.carousel.rendered = function() {
_.each(this.findAll('.class'), function(element){
if($(element).data('modified')) return;
$(element).data('modified', true);
...
});
};
You can disable reactivity for the cursor, though it's a sad solution:
Articles.find(..., {reactive: false});
The most invasive, but also the most versatile is to observe when the data is fully loaded:
Deps.autorun(function() {
Meteor.subscribe('articles', {
ready: function() {
...
},
});
});
The issue may have to do with the use of the .rendered callback. Each time the loop runs the DOM is updated so the callback will run again.
When I had this problem in the past, I found it helpful to use Meteor event handlers whenever possible, to eliminate load order issues like this one. In this case, maybe you could try a timeout, so that that the .remove() and .PikaChoose() calls only run after the DOM has been quiet for a certain interval. Hope that works for you.

How to create a DOM Element and use it in real time using Meteor and Templates

everybody.
I am using Meteor in a personal project and I could not do something very important to this project: I need to create some parts of the DOM in runtime and then populate them still in runtime, but using the Tamplates method I could not do this work. Maybe I am doing something wrong. Please, take a look in this example and say what's wrong or if there is a better way to do it.
Consider this example situation:
<template name = "exampleTemplate">
<div class="divToPopulate">
{{#each topModels}}
<div id="{{_id}}"></div>
{{/each}}
</div>
</template>
Then, in client.js I provide the data for topModels helper:
Template.exampleTemplate.topModels = function() {
return ExampleModel.find({},{limit:9});
}
When the Template is ready, I try to use those DOM elements in a javascript function called drawGraph(var domElementId) that is a Raphael.js function which draws a graph inside the DIV:
Template.exampleTemplate.rendered = function() {
var models = ExampleModel.find({},{limit:9});
models.forEach(function(model){
drawGraph(model._id);
});
}
But when I run this I get an error saying that the DOM element doesn't exist. So I was wondering if there is a better way to append an DOM element and populate it on the fly.
Thanks for any help.

How do I setup an Ember View class to be appended to a particular container?

Ember.View has a nice method called .appendTo("#container") which would allow me to specify a container div for the view. However, when I use the router and .connectOutlet method, an instance of my view is created automatically based on convention and is added to the page body element by default. Is there a way to configure the class definition of the view so that upon creation it will be inside my desired #container. Here is my view:
Jimux.BuildsView = Em.View.extend({
templateName: 'builds',
appendTo: '#jimux-header', #this was just a guess and did not work. but does some propery like this exist for the view?
tagName: 'div',
listVisible: true,
...
Another way to ask this question is: how do I tell Ember router to append a view to a particular item in the dom? By default the router appends the view to the body.
And here is the router bit:
# Connect builds controller to builds view
router.get('applicationController').connectOutlet("builds","builds", Jimux.buildsController)
To clarify, I dont want to put my whole Ember app in a container. I have several views in my application, and most of them are fine directly in the body. But there are a couple like the one mentioned in this question, which I want to put inside "#application-header" div.
You can specify the root element for your application object.
window.App = Ember.Application.create({
rootElement: '#ember-app'
});
Edit:
Having re-read your question, I think you should look into named outlets, so you could do something like:
<div id="application-header">
{{outlet builds}}
</div>
{{outlet}}
well..after understanding your question, i remember having same trouble. Also, thing is i didn't find any way to do this even after going through the Ember code. But later i understood that its for good purpose only. I know you already might have come across with handlebars with which we can acheive this. If we give a view a ID to get appended, we are constraining the application and the whole use of ember becomes useless. Ok coming to you question, as far as i know, we can acheive that appending mustache templates in you div element of HTML.
<div id="jimux-header">
{{view Jimux.BuildsView}}
</div>
This way we can use the Jimux.BuildsView where ever you want and as many times possible. The Beauty of Ember you have to say...
Just add rootElement in the application object.
var App = Ember.Application.create({
rootElement: '#container'
});