-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[recipe] Add Vue.js recipe #1276
Comments
Thanks @blake-newman! |
Meanwhile, @knpwrs has written an article that is very well: http://knpw.rs/blog/testing-vue-in-node |
@forresst Thanks for this! |
Hey, guys. @jackmellis commented on my post with his own approach which is rather interesting. He points to a forum post he made as well as two modules he made: |
jackmellis/require-extension-hooks-vue#1 This adds more support for compiling of templates which should a) improve overall speeds and b) support es2015 features in templates |
I've been playing with ava this last week with vue. It seems to work fine with require-extension-hooks - although loading One issue I've come across is that ava will often hang when an assertion fails on a reactive property. I assume it's to do with trying to build a fairly complicated vue-embroiled stack trace? Example: // where vm.dirty === false
// This test causes ava to hang until the timeout has elapsed (and even then sometimes it keeps hanging)
test('...', t => {
t.true(vm.dirty);
});
// This test works fine
test('...', t => {
var dirty = vm.dirty;
t.true(dirty); // dirty == false
}); |
I could make it work using vue-node. |
Here's an example repo - https://github.com/eddyerburgh/avoriaz-ava-example |
Nice. I've taken the exact test code and done the same with require-extension-hooks instead of webpack. Goes from 6.8s to 2.4s on my machine... |
Will add recipe over next couple of days, should have enough free time to complete.
Extension hooks seems to have least impact, and with caching techniques this could be much faster to an already fast solution. Less configuration required aswell. Also mapping of code coverage is very accurate and not yet seen any issues. |
Hi, How about mocking dependencies in vue component in this approach? |
Ideally any dependencies you need to mock should be provided via props. When that's the case, mocking is trivial. |
@knpwrs Thank you for a quick answer to my question. I'm new in Vue.js world and I don't fully understand how I should test Vue components (everything in a single file). Yesterday I created a simple setup with ava using guides from this issue. The solution suggested in this issue doesn't use vue-loader so I can't mock ES6 modules, right? That's why I asked about other ways to easily mock ES6 modules. There is no constructor dependency mechanism or IoC container in place. Here are some files with my approach: For now, I just created some method to simply return an instance, so I can easily mock it in tests here. I'm curious about how can I use props to pass ES6 module dependency. Isn't props designed to pass some variables from the layout? I had one additional problem. I couldn't test mounted component with transition <template>
<transition appear name="slideFromBottom"> I guess it's a limitation of the approach suggested in this issue, right? |
This might be a discussion for the vue forum. I haven't tried it myself and I'm not sure his well they'll play together but I'm assuming require-extension-hooks should work with rewire so that would give you a simple way to mock your required modules. As for transitions; I'm not sure if anyone else has had issues like this? You could always try registering a mock transition component? Abd now shameless plugging: You can do component dependency injection with vue-inject and quick quick component instances with mocked properties and props with vuenit :) |
I'm going to back up @jackmellis and say that it's probably better for a vue forum, but if you wanted to shoot me at email at ken@kenpowers.net we could discuss it at length. Keep in mind that my recommendations are coming from general knowledge of component-based architecture (mostly React). This is by no means the only way to do things, just what I've found to work best for me. Essentially the pattern I would recommend is to pass all external state as props. In your case that would mean passing the accepted state as a prop, and emitting an "accepted" event when the user has accepted the policy. The root (entry-point) of your application, save for any other state-management patterns (vuex, etc), should be responsible for getting state from external resources (cookies, local storage, whatever else). Consider a component tree where application state is passed unidirectionally from the root component to child components. By having your cookie law component materialize its own state from cookies, you effectively have multiple component tree roots. This isn't necessarily wrong, in fact in some cases it's what would be recommended (see the concept of container vs presentational components in React/Redux), but I would encourage keeping such patterns to a minimum. The idea is to be able to spin up your components in any environment without having to rely on the existence of any particular API in the environment. So now you have a generic component which is easy to test, but now you may be asking how to test the entire application. At this point such a task should be handled by integration/e2e testing, which is a whole different beast. You may also be thinking that it's appropriate to have the cookie law component read state from cookies since that is the component's only purpose, but you should also keep in mind that there isn't actually a "cookie law", just a "user tracking law" (please note, I am not a lawyer, this is not legal advice). By constructing your component in a way where the accepted state is passed as a prop, your component more reflects the reality of the law, regardless of where the accepted state is actually stored. I realize this has been long and rambly, so TL;DR: pass the accepted state as a prop, emit an accepted event when the user accepts. IMHO, realizing that this is not the only, or necessarily the most correct way to do things in all situations, the component should be constructed in such a way that it is not tracking the accepted state, but relying on the application to provide and manage accepted state. |
@knpwrs Thank you for such detailed answer :) I agree with your point of view, but in this particular application, I only need some small components that don't form any bigger component. So it feels a little overengineered in this particular case. I definitely take a closer look at this approach when I start writing some bigger components. @jackmellis Do you have any example code how to use vue-loader with require-extension-hooks? I'm quite new on this topic and I'm not even frontend developer :) Any example would be nice, so I could try to figure out how it works. |
Looks promising |
@blake-newman using the PR you opened as a base I tried adding this to my current vue app and I'm running into an issue no matter how I have it setup. Error logs➜ wvvw.me git:(master) ✗ yarn ava test/*.spec.js
yarn ava v0.23.3
$ "/Users/xo/code/wvvw.me/node_modules/.bin/ava" test/post.spec.js
You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
[Vue warn]: Vue is a constructor and should be called with the `new` keyword
/Users/xo/code/wvvw.me/node_modules/vue/dist/vue.runtime.common.js:3414
this._init(options);
^
TypeError: this._init is not a function
at Array.Vue$2 (/Users/xo/code/wvvw.me/node_modules/vue/dist/vue.runtime.common.js:3414:8)
at hook (/Users/xo/code/wvvw.me/node_modules/require-extension-hooks/hook.js:15:26)
at Object.require.extensions.(anonymous function) [as .vue] (/Users/xo/code/wvvw.me/node_modules/ava/lib/process-adapter.js:100:4)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.require (module.js:498:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (/Users/xo/code/wvvw.me/test/post.spec.js:4:1)
at Module._compile (module.js:571:32)
at extensions.(anonymous function) (/Users/xo/code/wvvw.me/node_modules/require-precompiled/index.js:13:11)
at Object.require.extensions.(anonymous function) [as .js] (/Users/xo/code/wvvw.me/node_modules/ava/lib/process-adapter.js:100:4)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.require (module.js:498:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (/Users/xo/code/wvvw.me/node_modules/ava/lib/test-worker.js:49:1)
at Module._compile (module.js:571:32)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.runMain (module.js:605:10)
at run (bootstrap_node.js:423:7)
at startup (bootstrap_node.js:147:9)
at bootstrap_node.js:538:3
1 exception
✖ test/post.spec.js exited with a non-zero exit code: 1
error Command failed with exit code 1. post.spec.jsimport Vue from 'vue';
import test from 'ava';
import Post from '../src/components/Post.vue';
test(t => {
const N = Vue.extend(Post);
const vm = new N({
propsData: {
post: {
title: 'Test Post',
content: 'This is a test post.',
tags: ['test', 'post', 'example']
}
}
});
Vue.nextTick(() => {
t.is(vm.$el.textContent, 'This is a test post.');
});
}); ./src/components/Post.vue<template>
<div v-bind:class="['post', 'styled', (post.published ? '' : 'unpublished')]">
<template v-if="post.published || (!post.published && user)">
<h1 class="title">{{post.title}}</h1>
<div class="content" v-html="marked(post.content)"></div>
<span class="meta">
<a v-bind:href="post.permalink">{{new Date(post.date).toDateString()}}</a>
<span class="owner">by <a v-bind:href="'/user/' + owner.id">{{owner.name}}</a></span>
</span>
</template>
<template v-else>
<p class="content">This post hasn't been published yet.</p>
</template>
</div>
</template>
<script>
import Vue from 'vue';
export default Vue.extend({
name: 'post',
props: {
post: {
type: Object
},
user: {
type: Object
}
},
computed: {
owner() {
var vm = this;
var isAnonymous = 'author' in vm.post;
return {
name: isAnonymous ? vm.post.author.username : 'anonymous',
id: isAnonymous ? vm.post.author._id : 'anonymous'
};
}
}
})
</script>
|
Here's what I got working. One thing to keep in mind is you can't use |
@OmgImAlexis I've published require-extension-hooks-vue 0.2.2 which may or may not fix your issue...🤞 |
Since most people use The code below allows
Edit: Although this is working nyc isn't returning anything in the coverage report.
|
Note: this is not an issue but a reminder for me to add a recipe for Vue file support.
Add a Vue.js recipe for Vue file transpiling. The new
vue-node
(https://github.com/knpwrs/vue-node) works great as a solution to get Ava and Vue.js working along side each other. Little configuration needed other than using prexisting webpack configs.I have it fully working together, although it is quite slow. I believe there is extra precompilation happening, caching transpiled code in
vue-node
should solve this issue.I'm also looking into checking coverage reports with Vue.js files, since webpack is driving the transpiling it should work (fingers crossed).
Once I have completed my investigation I will add a recipe.ill keep this issue up to date. May be a couple of weeks, due to finishing a work project.
The text was updated successfully, but these errors were encountered: