I am writing a webapp with VueJs, I am trying to setup unit test for it, I got inspired from vue-mdl unit-tests. But the tests are not running properly for my code and I am getting vm.$el as undefined, so not able to move forward at all.
Here is the component, I am trying to test:
Confirmation.vue
<template>
<div>
Your order has been confirmed with the following details.
</div>
</template>
<script type="text/javascript">
export default {
data () {
return {
data_from_pg: null
}
}
}
</script>
and here is test for it, which fails
Confirmation.spec.js
import Confirmation from 'src/components/Confirmation'
import { vueTest } from '../../utils'
describe('Confirmation', () => {
let vm
let confirmation
before(() => {
vm = vueTest(Confirmation)
console.log('vm.$el ' + vm.$el) => this prints undefined
confirmation = vm.$el.querySelector('#confirmation') => so this line gives error
// confirmation = vm.$('#confirmation')
})
it('exists', () => {
confirmation.should.exist
confirmation.should.be.visible
})
})
utils.js
export function vueTest (Component) {
const Class = Vue.extend(Component)
Class.prototype.$ = function (selector) {
return this.$el.querySelector(selector)
}
Class.prototype.nextTick = function () {
return new Promise((resolve) => {
this.$nextTick(resolve)
})
}
const vm = new Class({
replace: false,
el: 'body'
})
return vm
}
My complete code is available here, with all the test config, which I have tried to change many times, but could not figure out how to make it work. Please let me know if you see some error somewhere.
The vueTest function in utils is trying to load the Vue instance into the body tag:
const vm = new Class({
replace: false,
el: 'body'
})
return vm
The unit tests do not load index.html as an entry point into the app, but rather the individual components that you want to test; Therefore, you do not have access to document or html elements and the component is never mounted. I'd suggest using vm.$mount():
If elementOrSelector argument is not provided, the template will be rendered as an off-document element.
You could change the above lines to something like the following
const vm = new Class();
vm.$mount();
return vm;
Your tests should now have access to the $el property.
Related
I am having trouble understanding what the testing flow would be for testing functions which use functions loaded from a JavaScript library from Intercom.
My method looks like this:
export const generateButton = (handleOnClick) => {
case "moo":
return <button onClick={() => Intercom('show')}>Sign Up</button>
default:
return 'moo'
The error I get when running this is:
ReferenceError: Intercom is not defined
So I figured it out, I needed to add a new file and point jest set up on package.json to it like so (the file added is mockObject)
"setupFiles": [
"./config/jest/setupJest.js",
"./config/jest/mockObject.js"
],
then in the file itself has this in it
global.Intercom = () => {
console.log('Intercom called')
}
If I understand what you're trying to do then create a dummyFunction to replace Intercom in your tests. Something like this...
const Intercom = jest.fn();
describe('button click', () => {
it('Intercom is called correctly', () => {
// whatever component contains the button should be mounted
const wrapper = mount(<YourComponentHere />);
// you may need to add a class to target the specific button
wrapper.find('button').simulate('click');
expect(dummyFunction).toHaveBeenCalledWith('show');
expect(dummyFunction).toHaveBeenCalledTimes(1);
});
});
I want to change a data property and run a method on my Vue instance within Laravel. However due to using webpack and laravel I can't seem to access the instance how I would expect to:
So window.app doesn't appear to be the correct instance of my Vue class.
Below is the Blade View i'm loading, as you can see I append a script tag to my main layout.blade.php, simply trying to change the Vue instance data property, and run a method.
#push('scripts')
<script>
app.unsaved = true;
app.triggerEvent(null, 'models', null);
</script>
#endpush
Below is part of my app.js (resources/assets/js/app.js):
const app = new Vue({
el: '#app',
components: {
'models-select': ModelsSelect
},
data: {
showModel: false,
unsaved: false
},
mounted: function() {
let _self = this;
(function() {
document.querySelectorAll("input, textarea, select").forEach(function(e) {
e.addEventListener('change', function() {
_self.unsaved = true;
e.classList.add('is-changed');
});
});
function unloadPage(){
if (_self.unsaved) return 'You appear to have un-saved changes!';
}
window.onbeforeunload = unloadPage;
})();
},
methods: {
triggerEvent: function(event, target, property)
{
app.$refs[target].update(event, property);
}
As you can see i'd expect to manipulate the Vue instance through the global app variable I have defined within the app.js. However this doesn't appear to be the case.
I get the following error when running the triggerEvent method:
app.triggerEvent is not a function
In your app.js file, change const app = new Vue({ to window.app = new Vue({.
Then within your <script> tags, change it to this.
<script>
window.app.unsaved = true;
window.app.triggerEvent(null, 'models', null);
</script>
I have a service (no view) that depends on DialogService from aurelia-dialog, and uses that for opening a dialog. I want to test my service. While doing that I got the following error on dialogService.open(...).
Error: DialogRenderer must implement getDialogContainer().
at Renderer.getDialogContainer (webpack-internal:///./node_modules/aurelia-dialog/dist/native-modules/renderer.js:14:15)
at DialogService.open (webpack-internal:///./node_modules/aurelia-dialog/dist/native-modules/dialog-service.js:139:106) ...
I got the idea that I need to load the plugin aurelia-dialog before running any of test cases. To this end, I tried the following.
import { bootstrap } from "aurelia-bootstrapper";
import { Aurelia, Container, PLATFORM } from "aurelia-framework";
describe("test specs", () => {
let container: Container;
beforeAll(async () => {
await bootstrap(async (aurelia: Aurelia) => {
aurelia.use
.standardConfiguration()
.developmentLogging()
.plugin(PLATFORM.moduleName("aurelia-dialog"), (config) => {
config.useDefaults();
config.settings.lock = true;
config.settings.centerHorizontalOnly = false;
config.settings.startingZIndex = 5;
config.settings.keyboard = true;
});
Container.instance = container = new Container();
aurelia.container = container;
await aurelia.start();
});
});
});
But than I got the following error on aurelia.start.
Error: Loader must implement loadAllModules(ids).
at Loader.loadAllModules (webpack-internal:///./node_modules/aurelia-loader/dist/native-modules/aurelia-loader.js:113:11)
at ViewEngine.importViewResources (webpack-internal:///./node_modules/aurelia-templating/dist/native-modules/aurelia-templating.js:3593:24)
at eval (webpack-internal:///aurelia-framework:646:23)
at <Jasmine>
at loadResources (webpack-internal:///aurelia-framework:637:7)
at eval (webpack-internal:///aurelia-framework:731:14)
at next (webpack-internal:///aurelia-framework:577:30)
at runTasks (webpack-internal:///aurelia-framework:583:10)
at eval (webpack-internal:///aurelia-framework:936:16)
at <Jasmine>
at eval (webpack-internal:///aurelia-framework:935:21)
at <Jasmine>
at FrameworkConfiguration.apply (webpack-internal:///aurelia-framework:919:42)
at Aurelia.start (webpack-internal:///aurelia-framework:463:37) ...
Thus, my question is how to correctly load the aurelia-dialog plugin in this scenario?
I am trying to write a demo isomorphic Cycle.js/Hapi.js app, but it fails with an exception in xstream while rendering on the server. What is going wrong here? I've based my app on Cycle.js' isomorphic app example.
The traceback looks like this:
TypeError: Uncaught error: s[i]._add is not a function
at CombineProducer._start (/Users/arve/Projects/hapi-cycle/node_modules/xstream/core.js:190:22)
at Stream._add (/Users/arve/Projects/hapi-cycle/node_modules/xstream/core.js:976:19)
at MapOperator._start (/Users/arve/Projects/hapi-cycle/node_modules/xstream/core.js:717:18)
at Stream._add (/Users/arve/Projects/hapi-cycle/node_modules/xstream/core.js:976:19)
at LastOperator._start (/Users/arve/Projects/hapi-cycle/node_modules/xstream/core.js:596:18)
at Stream._add (/Users/arve/Projects/hapi-cycle/node_modules/xstream/core.js:976:19)
at Stream.addListener (/Users/arve/Projects/hapi-cycle/node_modules/xstream/core.js:1050:14)
at Object.streamSubscribe (/Users/arve/Projects/hapi-cycle/node_modules/#cycle/xstream-adapter/lib/index.js:39:16)
at /Users/arve/Projects/hapi-cycle/node_modules/#cycle/base/lib/index.js:49:30
at Array.map (native)
The render code looks basically as follows:
import Cycle from '#cycle/xstream-run'
import xs from 'xstream'
import {html, section, h1, p, head, title, body, div, script, makeHTMLDriver,} from '#cycle/dom'
import serialize from 'serialize-javascript'
import Logger from '#arve.knudsen/js-logger'
let logger = Logger.get('server.rendering')
let wrapVTreeWithHtmlBoilerplate = ([vtree, context,]) => {
return (
html([
head([
title('Cycle Isomorphism Example'),
]),
body([
div('.app-container', [vtree,]),
script(`window.appContext = ${serialize(context)};`),
// script(clientBundle),
]),
])
);
}
let main = (sources) => {
let vtree = (
section('.home', [
h1('The homepage'),
p('Welcome to our spectacular web page with nothing special here.'),
])
)
return {
DOM: vtree,
}
}
let renderIndex = (request, reply) => {
let context = xs.of({})
Cycle.run((sources) => {
let vtree = main(sources).DOM
let wrappedVTree = xs.combine(vtree, context)
.map(wrapVTreeWithHtmlBoilerplate)
.last()
return {
DOM: wrappedVTree,
};
}, {
DOM: makeHTMLDriver((html) => {
let wrappedHtml = `<!doctype html>${html}`
}),
context: () => {return context},
PreventDefault: () => {},
})
}
You can find the full source code here.
I'm running on OS X using Node v6.6.0, babel-node 6.14.0, Hapi 15.0.3, #cycle/dom 12.2.5 and #cycle/xstream-run 3.1.0. Let me know if you need more info.
The reason for the error was that the rendered VTree was not a stream. I changed the code to the following and it works:
let vtree = sources.context.map(({}) => {
return (
section('.home', [
h1('The homepage'),
p('Welcome to our spectacular web page with nothing special here.'),
])
)
})
return {
DOM: vtree,
}
The sources.context.map call (which I borrowed from the original isomorphic example) ensures that vtree is a stream.
I use default Aurelia's sceleton-esnext updated to last version .
I added this line to App (
Example from doc. Customizing the Navigation Pipeline)
config.addPipelineStep('authorize', AuthorizeStep);
After this I catch error running 'gulp test'
Chrome 52.0.2743 (Linux 0.0.0) the App module contains a router property FAILED
TypeError: config.addPipelineStep is not a function
Test
it('contains a router property', () => {
expect(sut.router).toBeDefined();
});
Test is going well without line.
I just came across this issue. To fix, you just need to add an empty method into your RouterStub:
class RouterStub {
configure(handler) {
handler(this);
}
map(routes) {
this.routes = routes;
}
addPipelineStep(stepName, stepClass) {
}
}
then in your test:
describe('the App module', () => {
var app;
var mockedRouter;
beforeEach(() => {
mockedRouter = new RouterStub();
app = new App();
app.configureRouter(mockedRouter, mockedRouter);
});
it('contains a router property', () => {
expect(app.router).toBeDefined();
});
});
If you're trying to test the pipeline step you'll need to mock the router itself and test the actual logic, but if you just want your tests to run (i.e. be defined, check the router title etc), this will work.