I have an element (display-enter-button.html) that I want to test:
<template>
<data-local-storage id="localStorage"></data-local-storage>
<app-location route="{{route}}"></app-location>
<span role="button" tabindex="0" class="btn" on-click="_btnClick" on-KeyPress="_btnKeyPress">enter here</span>
</template>
<script>
class DisplayEnterButton extends Polymer.Element {
_btnClick() {
// Something happens
});
}
</script>
I want to verify that the _btnClick method gets called when I click on the enter button. This is my unit test:
<head>
<title>display-enter-button</title>
<script src="../../bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<script src="../../bower_components/web-component-tester/browser.js"></script>
<!--
Load component to test
-->
<link rel="import" href="../../src/displays/display-enter-button.html">
</head>
<body>
<!--
Add component to test fixure and give it an incrementing id
-->
<test-fixture id="fixture-one">
<template>
<display-enter-button></display-enter-button>
</template>
</test-fixture>
<script>
// Name the suite the same as the type of tests
suite('Query Selector Tests', function() {
test('On click function called', function() {
// Select element to trigger event
var circle = fixture('fixture-one').shadowRoot.querySelector('.btn');
// Spy on the method that should run
var clickButton = sinon.spy(DisplayEnterButton.prototype, '_btnClick');
// Trigger the event
circle.click();
// Test it
sinon.assert.called(clickButton);
});
});
</script>
The test runs, but I can't get past this ESLint error:
'DisplayEnterButton' is not defined no-undef
I'd like to avoid ESLint rule exceptions (such as global) if possible because I'm going to be using this pattern a lot in the future. How could I resolve this error?
An alternative to Xavier's solution that doesn't involve creating another instance of the test element is to fetch the actual element under test from the test fixture:
<test-fixture id="BasicView">
<template>
<!-- give the test element an ID to query for it in tests -->
<my-view1 id="testEl"></my-view1>
</template>
</test-fixture>
<script>
suite('my-view1 tests', function() {
test('on click', function() {
var proto = document.getElementById('testEl').constructor.prototype;
var clickButton = sinon.spy(proto, '_btnClick');
// ...
});
});
</script>
EDIT: Tony's accepted answer is the best solution
This worked as well, but creates a new instance instead of using fixture instance.
test('On click function called', function() {
// Create instance of the class we want to test the methods of
var proto = document.createElement('display-enter-button')
.constructor.prototype;
// Replace the method with a spy
var func = sinon.spy(proto, '_btnClick');
// Select the elemen to test
var btn = fixture('fixture-one').shadowRoot.querySelector('.btn');
// Simulate the trigger
btn.click();
// Check if the function was called
sinon.assert.called(func);
});
Related
I'm trying a simple click event in Vue JS, but it doesn't seem to be working. I've checked the console for errors and there's nothing in there when I click the button or before. I'm following a tutorial so as far as I can see it should work? Is there something I've missed or I'm not understanding?
<body>
<div id="app">
<!--Listening for a click event-->
<button v-on:click="increase">Click Me To Increase Counter</button>
<p>{{ counter }}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// Application instance (so here we can technically create two views dependant upon x, y or z)
new Vue({
// Element used to bind to
el: "#app",
// Data Properties (accessed from object using 'this.property')
data: {
counter: 0
},
// Element Properties
methods: {
increase: function() {
this.counter++;
}
}
});
</script>
</body>
I also tried the same code in stackoverflow snippet editor and it is working properly. Below is the working example. I hope this will be helpful.
// Application instance (so here we can technically create two views dependant upon x, y or z)
new Vue({
// Element used to bind to
el: "#app",
// Data Properties (accessed from object using 'this.property')
data: {
counter: 0
},
// Element Properties
methods: {
increase: function() {
this.counter++;
}
}
});
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script> -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<!--Listening for a click event-->
<button v-on:click="increase">Click Me To Increase Counter</button>
<p>{{ counter }}</p>
</div>
Thanks,
Jignesh Raval
Brother your Codebase is absolutely fine. It maybe your editor fault.
You just put your code in a html file and simply run with a browser. It will work perfectly.
Thanks.
I'd like to ask you people about workflow with Polymer. I know that I should use my own elements or double check if element that I need isn't alredy published. It's really nice, I admit it. However the Polymer Starter Kit comes as single-page app. Is it the recommended approach for using Polymer? What about large pages that would need a lot of data to be loaded? Are there alternative approaches?
You don't need to have all your elements rendered at same time. They can be created on the fly only when needed, and can be destroyed as well.
To create your elements on the fly, you can use DOM Manipulation methods like:
var myElement = document.createElement("my-element");
this.$.container.appendChild(myElement);
myElement.myProperty = "anything";
To remove, just do this way:
var myElement = this.$.container.querySelector("my-element");
myElement.parentNode.removeChild(myElement);
If you need dynamic load a HTML Import, you can use this.importHref if your code is inside a polymer Element (and it should be).
this.importHref('myElement.html', function(e) {
// Create your element here
});
Putting things together...
Suppose you have a polymer element like that:
<dom-module id="my-app">
<template>
<div id="container"></div>
<paper-button on-click="_loadElement">Load Element</paper-button>
<paper-button on-click="_removeElement">Remove Element</paper-button>
</template>
</dom-module>
<script>
Polymer({
is: 'my-app',
_loadElement: function() {
this.importHref('myElement.html', function(e) {
var myElement = document.createElement("my-element");
this.$.container.appendChild(myElement);
myElement.myProperty = "anything";
});
},
_removeElement: function() {
var myElement = this.$.container.querySelector("my-element");
myElement.parentNode.removeChild(myElement);
}
});
</script>
<template is="dom-bind" id="app">
<div>{{title}}</div>
<my-element></my-element>
</template>
Can I inside my-element force the auto-bind template to redraw?
I can get the template, but changing it does not trigger a redraw:
var app = document.querySelector('#app');
app.title = 'Zaphod';
Your code should work, except setting the title needs to wait until polymer is ready (i.e. elements have been registered and are ready to be interacted with).
var app = document.querySelector('#app');
document.addEventListener("WebComponentsReady", function () {
app.title = 'Zaphod';
});
I am having a problem accessing paper-input elements that are inside a paper-dialog. I cant seem to get the value of the paper-input while its inside the paper-dialog, I just get a return value of null. I know there is something like this.$.element but I am confused on how to actually use it. Does the paper-dialog have to be inside a self binding template?
once a paper-dialog is opened it goes into the shadowdom of core-overlay-layer scoping the elements from regular selectors. you can access it's children with the this.$.element syntax if the dialog is inside a auto-binding template
<body>
<template id="app" is="auto-binding">
// other html content
<paper-dialog id="dialog">
<paper-input id="input"></paper-input>
</paper-dialog>
</template>
<script>
(function () {
var app = document.querySelector("#app");
app.addEventListener('template-bound', function () {
this.getValue = function () {
return this.$.input.value;
};
});
}());
</script>
</body>
the other option would be to use a auto-binding template like before and create a declarative variable for the input value
<body>
<template id="app" is="auto-binding">
// other html content
<paper-dialog id="dialog">
<paper-input value="{{inputValue}}"></paper-input>
</paper-dialog>
</template>
<script>
(function () {
var app = document.querySelector("#app");
app.addEventListener('template-bound', function () {
this.getValue = function () {
return this.inputValue;
};
});
}());
</script>
</body>
a way to get around using the auto-binding template would be to put the dialog in a custom element and enclose all it's functionality there that would allow you to use either of these methods.
i hope this helps.
When I try document.querySelector('core-drawer-panel').togglePanel() in the console it works but when I do the following core-drawer-panel is not ready yet?
<template>
<core-drawer-panel forceNarrow>
</core-drawer-panel>
</template>
<script>
document.addEventListener('polymer-ready', function() {
document.querySelector('core-drawer-panel').togglePanel()
console.log('polymer-ready');
});
</script>
Note that I can not wrap it in a polymer element due to issues with other js frameworks.
try this
var template = document.querySelector('template');
template.addEventListener('template-bound', function () {
//code
});
with your element inside a template you need to look for template to be ready not polymer.