In my component set
data(){
categories: this.$parent.categories => which I set in main.js
}
Code file main.js
import categories from '../config/categories';
new Vue({
router,
data: {
categories: categories
}
});
I created 1 function unit test
it(‘check component is a button’,() => {
const wrapper = shallow(FormSearch);
expect(wrapper.contains(‘button’)).toBe(true);
});
I run test then show error: Error in data(): "TypeError: Cannot read property ‘categories’ of undefined"
How to fix it. Help me.
Why not import you categories config file directly into your component?
import Categories from '../config/categories'
then your data method can directly access it:
data () { return { categories: Categories }}
You'll find that much easier to test
yes, thanks you. I changed. I run test then it happens error other
Cannot find module '../../config/categories' from 'mycomponet.vue'.
Although. I run project on browser just working well.
How to fix it. Thanks you very much
For Testing , you can set mock data to escape undefined error while testing . But it is not standard solution .....
it(‘check component is a button’,() => {
const wrapper = shallow(FormSearch);
let mockCategories = { // mock category data }
wrapper.$parent = {
categories: mockCategories
}
expect(wrapper.contains(‘button’)).toBe(true);
});
Try this approach:
const Parent = {
data: () => ({
val: true
}),
template: '<div />'
}
const wrapper = shallowMount(TestComponent, {
parent: Parent
})
I am writing unit tests for my vuejs components in a larger application. My application state is all in the vuex store, so almost all of my components pull data from vuex.
I can't find a working example for writing unit test for this. I have found
Where Evan You says:
When unit testing a component in isolation, you can inject a mocked store directly into the component using the store option.
But I can't find a good example of how to test these components. I have tried a bunch of ways. Below is the only way I have seen people do it. stackoverflow question and answer
Basically it looks like the
test.vue:
<template>
<div>test<div>
</template>
<script>
<script>
export default {
name: 'test',
computed: {
name(){
return this.$store.state.test;
}
}
}
</script>
test.spec.js
import ...
const store = new Vuex.Store({
state: {
test: 3
}
});
describe('test.vue', () => {
it('should get test from store', () => {
const parent = new Vue({
template: '<div><test ref="test"></test></div>',
components: { test },
store: store
}).$mount();
expect(parent.$refs.test.name).toBe(3);
});
}
Note the "ref" this example doesn't work without it.
Is this really the right way to do this? It seems like it will get messy fast, because it requires adding the props into the template as a string.
Evan's quote seems to imply that the store can be added directly to the child component (ie not the parent like the example).
How do you do that?
The answer is actually really straightforward but not currently documented.
const propsData = { prop: { id: 1} };
const store = new Vuex.Store({state, getters});
const Constructor = Vue.extend(importedComponent);
const component = new Constructor({ propsData, store });
Note the store passed to the constructor. propsData is currently documented, the "store" option isn't.
Also if you are using Laravel, there are weird problems with the webpack versions you may be running.
The
[vuex] must call Vue.use(Vuex),
Error was caused by useing laravel-elixir-webpack-official.
If you did the fix:
npm install webpack#2.1.0-beta.22 --save-dev
for this https://github.com/JeffreyWay/laravel-elixir-webpack-official/issues/17
Your tests that include Vuex seem to break with the
[vuex] must call Vue.use(Vuex)
even when you have Vue.use(Vuex)
I have a text input and i'm listening for the changes.
mycomponent.ts
ngOnInit() {
this.searchInput = new Control();
this.searchInput.valueChanges
.distinctUntilChanged()
.subscribe(newValue => this.search(newValue))
}
search(query) {
// do something to search
}
mycomponent.html
<search-box>
<input type="text" [ngFormControl]="searchInput" >
</search-box>
Running the application everything works fine, but i want to unit-test it.
So here's what i tried
mycomponent.spec.ts
beforeEach(done => {
createComponent().then(fix => {
cmpFixture = fix
mockResponse()
instance = cmpFixture.componentInstance
cmpFixture.detectChanges();
done();
})
})
describe('on searching on the list', () => {
let compiled, input
beforeEach(() => {
cmpFixture.detectChanges();
compiled = cmpFixture.debugElement.nativeElement;
spyOn(instance, 'search').and.callThrough()
input = compiled.querySelector('search-box > input')
input.value = 'fake-search-query'
cmpFixture.detectChanges();
})
it('should call the .search() method', () => {
expect(instance.search).toHaveBeenCalled()
})
})
Test fails as the .search() method is not called.
I guess i have to set the value in another way to have the test realize of the change but i really don't know how.
Anyone has ideas?
It might be a little bit late, but it seems that your code is not dispatching input event after setting input element value:
// ...
input.value = 'fake-search-query';
input.dispatchEvent(new Event('input'));
cmpFixture.detectChanges();
// ...
Updating input html field from within an Angular 2 test
Triggering the value change of FormControl is as simple as:
cmpFixture.debugElement.componentInstance.searchInput.setValue(newValue);
Custom component with #input, subscriptions, two way data binding
If you got a custom component you would need further changes in your application to be able to successfully unit test your application
have a look at the gist here this will give you some idea
https://gist.github.com/AikoPath/050ad0ffb91d628d4b10ef81736af386/raw/846c7bcfc54be8cce78eba8d12015bf749b91eee/#ViewChild(ComponentUnderTestComponent).js
More over complete reading over here carefully otherwise you can easily get confused again -
https://betterprogramming.pub/testing-angular-components-with-input-3bd6c07cfaf6
I have uses angular translate from here (http://pascalprecht.github.io/angular-translate/) and it's just work fine, but it break my controller's unit test whith Error:
Unexpected request: GET scripts/i18n/locale-en.json
I don't understant why?
I use yeoman and test with karma.
app.js:
'use strict';
(function() {
angular.module('wbApp', ['authService', 'authUserService', 'checkUserDirective', 'ui.bootstrap', 'pascalprecht.translate'])
.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/login.html',
controller: 'LoginCtrl',
access: {
isFree: true
}
})
.when('/main', {
templateUrl: 'views/main.html',
controller: 'MainCtrl',
access: {
isFree: false
}
})
.otherwise({
redirectTo: '/'
});
});
})();
configTranslate.js:
'use strict';
(function() {
angular.module('wbApp')
.config(['$translateProvider',
function($translateProvider) {
$translateProvider.useStaticFilesLoader({
prefix: 'scripts/i18n/locale-',
suffix: '.json'
});
$translateProvider.preferredLanguage('en');
}]);
})();
karma.conf.js:
files = [
...
'app/bower_components/angular-translate/angular-translate.js',
'app/bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js',
...
];
controller test:
'use strict';
describe('Controller: LoginCtrl', function() {
// load the controller's module
beforeEach(module('wbApp'));
var LoginCtrl, scope, location, httpMock, authUser;
// Initialize the controller and a mock scope
beforeEach(inject(function($controller, $rootScope, $location, $httpBackend, AuthUser) {
authUser = AuthUser;
location = $location;
httpMock = $httpBackend;
scope = $rootScope.$new();
LoginCtrl = $controller('LoginCtrl', {
$scope: scope
});
httpMock.when('GET', 'scripts/i18n/locale-en.json').passThrough();
}));
it(...);
...
});
if i add this in test controller, product same error:
httpMock.when('GET', 'scripts/i18n/locale-en.json').respond(200);
httpMock.flush();
or
httpMock.when('GET', 'scripts/i18n/locale-en.json').passThrough();
httpMock.flush();
i find this post How do I test controllers with Angular Translate initialized in App Config? but not helped me :/
I extensively use $httpBackend in my tests and it works fine, but in this case it is ineffective. If I comment the line:
$translateProvider.preferredLanguage('en');
obviously an error, if I add on the runtime (in my controllers)
$translate.uses(local);
I end up with the same error?
So I turn to the translation configuration (configTranslate.js) or at runtime is the same result:
Unexpected request: GET scripts/i18n/locale-en.json
Here is the syntax that I tested, either in a "beforeEach(inject(function(...});"
or in a test "it('...', function() {...});"
httpMock.expectGET('scripts/i18n/locale-en.json');
httpMock.when('GET', 'scripts/i18n/locale-en.json').passThrough();
httpMock.when('GET', 'scripts/i18n/locale-en.json').respond(data);
with at end
httpMock.flush();
I also tried a $ apply
httpMock.expectGET('scripts/i18n/locale-fr.json');
scope.$apply(function(){
$translate.uses('fr');
});
httpMock.flush();
nothing happens, Still this error is driving me crazy ..
If you have any suggestion
it's a known issue, please follow the documentation here: unit testing angular
The solution
Unfortunately, this issue is caused by the design of
angular-translate. To get around these errors, all we can do is to
overwrite our module configuration in our test suite, that it doesn't
use asynchronous loader at all. When there's no asynchronous loader,
there's no XHR and therefore no error.
So how do we overwrite our module configuration at runtime for our
test suite? When instantiating an angular module, we can always apply
a inline function which is executed as configuration function. This
configuration function can be used to overwrite the modules
configuration since we have access to all providers.
Using the $provide provider, we can build a custom loader factory,
which should then be used instead of the static files loader.
beforeEach(module('myApp', function ($provide, $translateProvider) {
$provide.factory('customLoader', function () {
// loader logic goes here
});
$translateProvider.useLoader('customLoader');
}));
Please read more in the above link provided.
We took the approach of ignoring the translation loader in unit tests, rather than being forced to modify each of the spec files.
One way to do it could be by separating the loader configuration to a separate file and then exclude it in karma.
So for example you can create a file app-i18n-loader.js (all other module configurations takes place in a different file):
angular
.module('myApp')
.config(loaderConfig);
loaderConfig.$inject = ['$translateProvider', '$translatePartialLoaderProvider'];
function loaderConfig($translateProvider, $translatePartialLoaderProvider) {
$translateProvider.useLoader('$translatePartialLoader', {
urlTemplate: 'assets/i18n/{part}/{lang}.json'
});
$translatePartialLoaderProvider.addPart('myApp');
}
And in your karma.conf.js exclude the file:
files: [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
//...
'bower_components/angular-translate/angular-translate.js',
'bower_components/angular-translate-loader-partial/angular-translate-loader-partial.js',
'app/**/*.mdl.js',
'app/**/*.js'
],
exclude: [
'app/app-i18n-loader.js'
],
(Note: Answer edited to a solution that does not require grunt/gulp).
I wanted a solution,
which was not too hacky
which didn't require me to change my actual application code,
which wouldn't interfere with the ability to load additional modules
and most importantly which wouldn't require me to change every
single test.
This is what I ended up with:
// you need to load the 3rd party module first
beforeEach(module('pascalprecht.translate'));
// overwrite useStaticFilesLoader to get rid of request to translation file
beforeEach(module(function ($translateProvider) {
$translateProvider.useStaticFilesLoader = function () {
};
}));
Assuming you don't need the actual translations for your unit tests, this works great. Just put the beforeEach on a global level, preferably in it's own file inside the test folder. It will be executed before every other test then.
I encountered this problem with protractor tests. My solution was to mock translations like this:
angular.module('app')
.config(function ($translateProvider) {
$translateProvider.translations('en', {});
$translateProvider.preferredLanguage('en');
})
Now no language files are downloaded, no strings get translated and I just test against the string keys in specifications:
expect(element(by.css('#title')).getText()).toEqual('TITLE_TEXT');
Try putting to test method:
it('should ...', function() {
httpMock.when('GET', 'scripts/i18n/locale-en.json').respond({});
httpMock.expectGET('scripts/i18n/locale-en.json');
scope.resetForm(); // Action which fires a http request
httpMock.flush(); // Flush must be called after the http request
}
See examples from Angular docs
Please have a look at https://github.com/PascalPrecht/angular-translate/blob/master/test/unit/service/loader-static-files.spec.js as a reference.
In general, I would recommend using a standard translation loader for unit tests (without the hassle of http loadings) which means you can provide the labels with $translateProvider.translations(). Why? Because you do not have to test the remote loading functionality which is part of angular-translate project.
None of the solutions worked for me but I came with these solutions:
1) If you need to use scope.$apply(), or should deal with states in your test (after the $apply() the 2nd approach won't work), override your app's translations with the $translateProvider.translations() method, using a plugin to load JSON files
beforeEach(module(function ($translateProvider) {
$translateProvider.translations('en', readJSON('scripts/i18n/locale-en.json'));
}));
2) If your tested controller depends on the $translate service you can use a plugin to load JSON files and combine it with $httpBackend to load your locale file when angular-translate requests it.
beforeEach(inject(function (_$httpBackend_) {
$httpBackend = _$httpBackend_;
$httpBackend.whenGET('scripts/i18n/locale-en.json').respond(readJSON('scripts/i18n/locale-en.json'));
$httpBackend.flush();
})));
Note this should be below your beforeEach(module('myApp')); or you will get an $injector error.
I made a simple mock service for $translate
$translate=function (translation) {
return {
then: function (callback) {
var translated={};
translation.map(function (transl) {
translated[transl]=transl;
});
return callback(translated);
}
}
};
Usage example here : https://gist.github.com/dam1/5858bdcabb89effca457
I use this pattern.
ApplicationModule set regular angular-translate config.
test code load 'testModule' instead of 'applicationModule'
// application module .js
(function() {
'use strict';
angular
.module('applicationModule', [
'ngAnimate',
'ngResource',
'ui.router',
'pascalprecht.translate'
])
.config(['$stateProvider', '$urlRouterProvider', '$translateProvider', '$translatePartialLoaderProvider', config]);
function config($stateProvider, $urlRouterProvider, $translateProvider, $translatePartialLoaderProvider) {
// set routing ...
$translateProvider.useStaticFilesLoader({
prefix: 'i18n/locale-',
suffix: '.json'
});
$translateProvider.useMessageFormatInterpolation();
$translateProvider.fallbackLanguage(['en']);
$translateProvider
.registerAvailableLanguageKeys(['en', 'ko'], {
'en_US': 'en',
'ko_KR': 'ko'
})
.determinePreferredLanguage(navigator.browserLanguage);
$translateProvider.addInterpolation('$translateMessageFormatInterpolation');
$translateProvider.useSanitizeValueStrategy('escaped');
}
})();
// test.module.js
(function() {
'use strict';
angular
.module('testModule', ['applicationModule'])
.config(['$translateProvider', '$translatePartialLoaderProvider', config])
.run(['$httpBackend', run]);
function config($translateProvider, $translatePartialLoaderProvider) {
$translateProvider.useLoader('$translatePartialLoader', {
urlTemplate: 'i18n/locale-en.json'
});
$translatePartialLoaderProvider.addPart('applicationModule');
}
function run($httpBackend) {
$httpBackend.when('GET', 'i18n/locale-en.json').respond(200);
}
})();
// someDirective.spec.js
describe("a3Dashboard", function() {
beforeEach(module("testModule"))
var element, $scope;
beforeEach(inject(function($compile, $rootScope) {
$scope = $rootScope;
element = angular.element("<div>{{2 + 2}}</div>");
$compile(element)($rootScope)
}))
it('should equal 4', function() {
$scope.$digest();
expect(element.html()).toBe("4");
})
})
Late to the table with this, but I got round this by specifying that Karma simply serve the files as per this entry in karma.conf.js:
files: [
...
{pattern: 'scripts/i18n/*.json', included: false, served: true},
...
]
The 2016 answer for this is to preprocess your json into your tests and properly test translations work on your directives.
I use karma-ng-json2js-preprocessor. Follow all the steps to setup your karma.conf then in your test file, prepend the relevant file as a module, then set that information in $translateProvider.
beforeEach(module('myApp', '/l10n/english-translation.json'));
// Mock translations for this template
beforeEach(module(function($translateProvider, englishTranslation) {
$translateProvider.translations('en_us', englishTranslation);
$translateProvider.useSanitizeValueStrategy(null);
$translateProvider.preferredLanguage('en_us');
}));
Note according to the plugin, it uses your filename to generate a camelcased module name. You can play with the function inside the module's /lib but basically it remove all dashes but KEEPS underscores in a camelCase. So en_us becomes En_us.
You'll also need to tell your test that it is expecting that file as a GEt.
$httpBackend.expect('GET', '/l10n/english-translation.json').respond(200);
I am converting from Ember data 0.13 to 1.0.0 Beta 1. In 0.13, I was using the becameError and becameInvalid states to know whether there was a problem when saving a record.
In 1.0.0 there is no longer a transaction and you need to use the save promise to handle errors. See below:
save: function() {
this.get('model').save().then(function () {
alert("Record saved");
}, function () {
alert("Problem");
});
},
In the above, I want to make a distinction between validation errors and all the rest (just as it was before in 0.13 with becameError and becameInvalid).
Is there a way to access the error object and how to read the validation errors included in the json response ? Before this was via this.get('content.errors') ...
Hoep somebody can help
Marc
Three steps:
Return errors in a proper format. If it Rails application, then:
\# Rails controller, update function
format.json { render json: {errors: #post.errors.messages}, status: :unprocessable_entity }
Set errors in promise
// app.js
save: function() {
self = this;
this.get('model').save().then(function () {
alert("Record saved");
}, function (response) {
self.set('errors', response.responseJSON.errors);
});
}
Display errors in a handlebar template
<\!-- index.html -->
{{input type="text" value=title}}<span class="alert-error">{{errors.title}}</span>
Not sure if this helps you substitute the bacameInvalid and becameError since states are now being removed, but you could try this as a catchall workaround:
Ember.RSVP.configure('onerror', function(error) {
console.log(error.message);
console.log(error.stack);
});
Hope it helps.