Update default state based on the property - unit-testing

I am working on a button component that also receives a default state value.
export default {
props: {
defaultState: {
type: Boolean,
default: false
}
},
data() {
return {
currentState: this.defaultState
}
},
computed: {
isActive() {
return this.currentState;
},
}
...
}
And i can am using it like <button :defaultState="true"/>.
Now the problem is when I am trying to write a test for my component I always gets the false (which is default value) value of currentState after using the wrapper.setProps({ defaultState: true }) that should be true
it.only ('should work with dynamic state change', async () => {
wrapper.setProps({
defaultState: true
});
await wrapper.vm.$nextTick();
// shows the true
console.log( wrapper.vm.defaultState );
// should be true but i get false
console.log( wrapper.vm.currentState );
});
Can anybody please point me to the right direction and what I have missed?

a better solution for this is to create a computed property. This would eliminate the need for both the data property and the watcher:
export default {
props: {
defaultState: {
type: Boolean,
default: false
}
},
computed: {
currentState() {
return this.defaultState
}
}
}
However, if your component is this simple, the computed currentValue property is not needed at all! Because all it does it repeat the value of the defaultState prop which by itself is already reactive.
So it means you are adding complexity to a component just to make it work for the tests... and the component would work perfectly even if you didn't have this.

Related

How can i coverage a promise response with Jasmine and Karma

I have a function that returns and treats a promise, I need to cover the return that is inside then but I don't know how I can do this, I'm currently trying as follows:
confirmRemoveUser(user: IUser) {
this.modalService
.open('Confirma a exclusão do usuário selecionado?', {
titleText: 'Confirmando exclusão',
confirmButtonText: 'Sim',
cancelButtonText: 'Cancelar',
closeButtonText: 'Fechar',
buttonType: 'danger'
})
.result.then(
(result: BentoModalConfirmationCloseReason) => {
if (result === BentoModalConfirmationCloseReason.Confirm) {
if (this.removeUser(user)) {
this.toastService.open('Usuário excluído com sucesso!', { type: 'success', close: true });
} else {
this.toastService.open('Falha ao excluir o usuário!', { type: 'warning', close: true, duration: 0 });
}
}
}
);
}
I'm currently using callthrough () and imagine that with some parameter I can get the promise but I don't know how:
it('Given_ConfirmRemoveUser_When_UserStepIsCalled_Then_UserIsRemoved', (done) => {
component.selectedJob = {
};
component.selectedArea = {
};
component.users = [{
}];
spyOn(modalService, 'open').withArgs('This is modal msg').and.callThrough();
component.confirmRemoveUser(component.users[0]);
expect(modalService.open).toHaveBeenCalled();
done();
});
And my coverage is like the image below:
Image here!
UPDATE
New Error
Your test should work when it is rewritten as follows:
it('Given_ConfirmRemoveUser_When_UserStepIsCalled_Then_UserIsRemoved', (done) => {
spyOn(modalService, 'open').and.returnValue(Promise.resolve(BentoModalConfirmationCloseReason.Confirm));
spyOn(toastService, 'open').and.stub();
component.confirmRemoveUser(component.users[0])
.then(r => {
expect(toastService.open).toHaveBeenCalled();
done();
})
.catch(e => fail(e));
});
You probably also want to know what will be displayed in the toast. Therefore it makes sense to rather use expect(toastService.open).toHaveBeenCalledWith(?);.
UPDATE
Above solution only works if confirmRemoveUser would return a Promise.
confirmRemoveUser(user: IUser) {
return this.modalService
...
In your case, the use of the done function does not make sense. You need to use async and await.
it('Given_ConfirmRemoveUser_When_UserStepIsCalled_Then_UserIsRemoved', async () => {
spyOn(modalService, 'open').and.returnValue(Promise.resolve(BentoModalConfirmationCloseReason.Confirm));
spyOn(toastService, 'open').and.stub();
await component.confirmRemoveUser(component.users[0]);
expect(toastService.open).toHaveBeenCalled();
});
The same can be achieved with fakeAsync and flush.
import { fakeAsync, flush } from '#angular/core/testing';
...
it('Given_ConfirmRemoveUser_When_UserStepIsCalled_Then_UserIsRemoved', fakeAsync(() => {
spyOn(modalService, 'open').and.returnValue(Promise.resolve(BentoModalConfirmationCloseReason.Confirm));
spyOn(toastService, 'open').and.stub();
component.confirmRemoveUser(component.users[0]);
flush();
expect(toastService.open).toHaveBeenCalled();
}));

Jest, expected mock function to have been called, but it was not called

Testing lifecycle methods when a VueJS component renders on the transition group.
I've been writing tests for lifecycle methods when the component renders on the transition group of the following VueJS component I've made little progress on getting it to work and would appreciate advice regarding this. I also tried switching between shallow mounting and mounting the component though that seemed to make no difference.
import { shallowMount } from '#vue/test-utils';
import StaggeredTransition from '../src/index';
const staggeredTransitionWrapper = componentData =>
shallowMount(StaggeredTransition, {
...componentData,
});
const staggeredTransition = staggeredTransitionWrapper();
describe('StaggeredTransition.vue', () => {
it('should render a staggered transition component', () => {
expect(staggeredTransition.element.tagName).toBe('SPAN');
expect(staggeredTransition.html()).toMatchSnapshot();
});
it('should mock calling the enter method', () => {
const enterMock = jest.fn();
StaggeredTransition.methods.enter = enterMock;
const staggeredTransitionWrapper2 = componentData =>
shallowMount(StaggeredTransition, { ...componentData });
const staggeredTransition2 = staggeredTransitionWrapper2({
slots: {
default: '<h1 :key="1">Staggered transition test</h1>',
},
});
expect(enterMock).toBeCalled();
});
});
Code for the StaggeredTransition component
<template>
<transition-group
:tag="tag"
:name="'staggered-' + type"
:css="false"
appear
#before-enter="beforeEnter"
#enter="enter"
#leave="leave"
>
<slot />
</transition-group>
</template>
<script>
const { log } = console;
export default {
name: 'StaggeredTransition',
props: {
type: {
type: String,
options: ['fade', 'slide'],
required: false,
default: 'fade',
},
tag: {
type: String,
required: false,
default: 'div',
},
delay: {
type: Number,
required: false,
default: 100,
},
},
methods: {
beforeEnter(el) {
console.log('beforeEnter');
el.classList.add(`staggered-${this.type}-item`);
},
enter(el, done) {
console.log('enter');
setTimeout(() => {
el.classList.add(`staggered-${this.type}-item--visible`);
done();
}, this.getCalculatedDelay(el));
},
leave(el, done) {
console.log('leave');
setTimeout(() => {
el.classList.remove(`staggered-${this.type}-item--visible`);
done();
}, this.getCalculatedDelay(el));
},
getCalculatedDelay(el) {
console.log('getCalculatedDelay');
if (typeof el.dataset.index === 'undefined') {
log(
'data-index attribute is not set. Please set it in order to
make the staggered transition working.',
);
}
return el.dataset.index * this.delay;
},
},
};
</script>

AdmobFree interstitial.show() - ERROR: interstitial not ready yet

I first call
this.admobFree.interstitial.prepare()
.then(() => {
this.interstitialPrepared = true;
console.log('AdMob Interstitial Ad is prepared, will be presented if autoShow is true, otherwise, call showInterstitial().');
})
.catch((err) => {
console.error(err);
})
And this.interstitialPrepared = true; is called so I assume my ad is ready.
But if I call this.admobFree.interstitial.show() after my this.interstitialPrepared var switch to true, I still have the following error "ERROR: interstitial not ready yet.".
Notice :
It works well with the following config :
this.adMobProvider.interstitialConfig = {
autoShow: false,
isTesting : true
};
But not when I want to test with real ads
this.adMobProvider.interstitialConfig = {
autoShow: false,
isTesting : false,
id:"ca-app-pub-277368299xxxxxxxx"
};
I had the same problem and I did this way:
I did not put the interstitial.show() after interstitial.prepare().then(..);
I subscribed these events: INTERSTITIAL_LOAD and INTERSTITIAL_LOAD_FAIL;
I only showed it loaded successfully;
Example:
this.admobFree.on(this.admobFree.events.INTERSTITIAL_LOAD).subscribe(() => {
this.admobFree.interstitial.show().then(() => {
// Show successful
}).catch((errorShow) => {
// ...
});
});
this.admobFree.on(this.admobFree.events.INTERSTITIAL_LOAD_FAIL).subscribe(() => {
// ...
});
I hope to help you.
In my case autoShow: trueworked for me. Remember when autoShow is true you don't need to call the intersit

Ember component function outside of action function

I have a problem calling a function outside action functions from an actions function. As you can see from the code below, I have a selectClient action that calls two functions, createCompanyAccount and createPrivateAccount. But I always get a this.createPrivateAccount is undefined. I have tried using self, but to no avail. Weirdly, I thought I would have to use self.createCompanyAccount, but then I get a self.createCompanyAccount is not defined.
I use Ember 2.12 and Ember Data 2.16.3.
import Ember from 'ember';
export default Ember.Component.extend({
store: Ember.inject.service(),
tagName: '',
/**
* Actions
*/
actions: {
// Select from selectList
selectClient(element) {
let self = this;
if (element.company) {
this.get('store').query('account', { 'filter' : {'orgnumber': element.orgNumber}}).then(
(accounts) => {
/* Organisation exist already */
},
(error) => {
let code = Number(error.errors[0].status);
if (code === 404) {
// company does not exist, so lets create it, and an account.
this.createCompanyAccount(element).then(
(account) => {
/* Do stuff... */
}
);
}
}
);
} else {
this.createPrivateAccount(element).then(
(anonUser) => {
/* Do stuff... */
}
);
}
}
},
createCompanyAccount(company) {
let account = this.get('store').createRecord('account', {
type: 'company',
});
// Check if postal address is set on result
if (typeof company.addressObject !== 'undefined') {
let postAddress = this.get('store').createRecord('address', {
address: company.addressObject.streetName,
zip: company.addressObject.zip,
postal_address: company.addressObject.postalAddress
});
account.get('addresses').pushObject(postAddress);
}
this.get('store').createRecord('company', {
name: company.name,
org_number: Number(company.orgNumber),
account: account
}).save().then((new_company) => {
return new_company.get('account');
});
},
createPrivateAccount(person) {
let account = this.get('store').createRecord('account', {
type: 'anonuser'
});
// Check if postal address is set on result
if (typeof person.addressObject !== 'undefined') {
let postAddress = this.get('store').createRecord('address', {
address: person.addressObject.streetName,
zip: person.addressObject.zip,
postal_address: person.addressObject.postalAddress
});
account.get('addresses').pushObject(postAddress);
}
this.get('store').createRecord('anonUser', {
first_name: person.firstName,
sur_name: person.surName,
email: person.email,
phone: person.phone,
account: account,
}).save().then((new_person) => {
return new_person.get('account');
});
}
});
Can anyone see where I go wrong? I can note that there is a few other functions that I have removed for clarity.
Thank you,
Tommy
Your issue is not about this.createPrivateAccount and this.createCompanyAccount being undefined. I think both of them are being executed but they do return undefined but you are expecting a Promise. Therefore this.createPrivateAccount().then is undefined.

Access controller from nested object

I need to access an ember controllers context from within a regular object. Currently I am saving a reference to the controller context in the init() method that seems a little crappy.
let self = this //saving the context here
export default Ember.controller.extend({
init() {
this._super(...arguments);
self = this;
},
settings: {
crud: {
read: {
enabled: true,
default() {
return self.get('blah.blah'); //Need to access the controller context
}
}
}
}
});
So I need access to the controller self.get('blah.blah'). Is there a better way to do this?
Use a computed property closure
export default Ember.controller.extend({
settings: Ember.computed(function() {
const controller = this;
return {
crud: {
read: {
enabled: true,
defaults() {
return controller.get('blah.blah');
}
}
}
};
})
});
Accessing object
this.get('settings').crud.read.defaults(); // "blah.blah"