vue-test-utils window scroll - vue.js

Need some advice on how to test a window scroll event using vue-test-utils
Below is my js
export default {
created () {
window.addEventListener('scroll', this.bannerResize);
},
methods: {
topBResize () {
var header = document.getElementById('topB');
var pos1 = header.offsetTop;
var pageYOffset = window.pageYOffset;
if (pageYOffset > pos1) {
header.classList.add('sticky');
} else {
header.classList.remove('sticky');
}
}
}
}
Below is my unit test using vue-test-utils
import {expect} from 'chai';
import {createLocalVue, shallow} from 'vue-test-utils'
const localVue = createLocalVue()
localVue.use(VueRouter)
localVue.use(Vuex)
const wrapper = shallow(Banner, {
localVue,
router,
attachToDocument: true
})
describe('topB.vue', () => {
it('topB resize', () => {
wrapper.setData({ bsize: true })
const dBanner = wrapper.find('#topB')
wrapper.trigger('scroll')
const pageYOffset = 500;
const pos1 = 200;
expect(dBanner.classes()).contains('sticky')
})
})
The test fails when you check if the sticky class is added.
How do I test this method ? I would like to see the sticky class added when window scrolls vertically
Thanks,
RD

You're triggering a scroll event on the div#topB wrapper, but the event listener is on window. JSDom, used by Mocha and Jest, doesn't support the normal JavaScript methods of scrolling window via window.scrollTo/window.scroll, but assuming you mount the test instance with attachToDocument, you can still manually dispatch a scroll event with:
window.dispatchEvent(new CustomEvent('scroll', { detail: 2000 }))
Your event handler would have to parse the event detail for this value and fallback to window.pageYOffset.
// var pageYOffset = window.pageYOffset;
var pageYOffset = !Number.isNaN(e.detail) ? e.detail : window.pageYOffset;
see GitHub repro 1
Alternatively, you can assume the scroll-event handler would be called upon scrolling; and test by running the scroll-event handler (wrapper.vm.topBResize()) directly, and then checking for the expected outcome. You can set window.pageYOffset before running the handler:
window.pageYOffset = 1000;
wrapper.vm.topBResize();
expect(dBanner.classes()).contains('sticky');
wrapper.pageYOffset = 0;
see GitHub repro 2

Related

How to test Vue Component method call within an async method

I believe I am struggling to properly mock my methods here. Here is my situation, I have a component with two methods;
name: 'MyComponent',
methods: {
async submitAction(input) {
// does await things
// then ...
this.showToastMessage();
},
showToastMessage() {
// does toast message things
},
}
And I want to write a test that will assert that showToastMessage() is called when submitAction(input) is called. My basic test looking something like this;
test('the toast alert method is called', () => {
let showToastMessage = jest.fn();
const spy = jest.spyOn(MyComponent.methods, 'showToastMessage');
const wrapper = shallowMount(MyComponent, { localVue });
const input = // some input data
wrapper.vm.submitAction(input); // <--- this calls showToastMessage
expect(spy).toHaveBeenCalled();
};
NOTE: localVue is declare as such at the top of the file const localVue = createLocalVue();
I confirmed that both submitAction() and showToastMessage() methods are being called during the tests, by sneaking a couple of console.log()'s and observing it in the test output, however the test still fails;
expect(jest.fn()).toHaveBeenCalledWith(...expected)
Expected: called with 0 arguments
Number of calls: 0
566 | const wrapper = shallowMount(MyComponent, { localVue } );
567 | wrapper.vm.submitAction(input);
> 568 | expect(spy).toHaveBeenCalledWith();
I've tried spying on both methods as well
const parentSpy = jest.spyOn(MyComponent.methods, 'submitAction');
const spy = jest.spyOn(MyComponent.methods, 'showToastMessage');
// ...
expect(spy).toHaveBeenCalled()
same results, test fail.
What am I missing?
Tech Stack: vue 3, jest, node 14
#TekkSparrow you can pass a heap of stuff into the shallowMount function. It accepts an object as a second argument which can look something like
import { shallowMount, createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
const localVue = createLocalVue()
localVue.use(Vuex)
let mocks = {
// this could be something like the below examples
// I had in a previous project
$route: {
query: '',
path: '/some-path'
},
$router: [],
$validator: {
validateAll: jest.fn()
},
$toast: {
show: jest.fn(),
error: jest.fn()
},
}
let propsData = {
// some props you want to overwrite or test.
// needs to be called propsData
}
let methods = {
showToastMessage: jest.fn()
}
let store = new Vuex.Store({
actions: {
UPLOAD_ASSET: jest.fn(),
},
})
const wrapper = shallowMount(MyComponent, { mocks, propsData, methods, store, localVue })
I believe that by doing similar to the above, your mocked function will run and be recorded by the Jest spy.
Took me a minute to realize/try this, but looks like since my calling function is async that I was suppose to make my test async, and await the main method call. This seems to have done the trick. Here's what ended up being my solution:
test('the toast alert method is called', async () => {
let showToastMessage = jest.fn();
const spy = jest.spyOn(MyComponent.methods, 'showToastMessage');
const wrapper = shallowMount(MyComponent, { localVue });
const input = // some input data
await wrapper.vm.submitAction(input);
expect(spy).toHaveBeenCalled();
};

How to do drag only without drop in Testcafe

So I have a scenario where I want to capture a popup element message whenever I drag one element to another element.
public async dragTransitiontToSegment(item: number, transitionName: string) {
const _tailSegment = Selector('.rolling').nth(item);
const _transitionPanel = Selector('.effects-selector.editor-panel .item-container')
const _transitionType = _transitionPanel.withText(transitionName);
await t.click(_transitionPanel);
await t.dragToElement(_transitionType,_tailSegment,{speed:0.01});
}
Right now I've change the speed for the drag but it was still to fast to capture the message I want, because the dragToElement fucntion will drop it. Is there a way to just drag and hold it ?
at present, TestCafe doesn't allow you to drag without drop out of the box. You can simulate the sequence of events (the mousedown, mousemove, or HTML5 drag events)
import { Selector, ClientFunction } from 'testcafe';
function triggerMouseEvent (selector, type, options) {
const dispatchFunc = ClientFunction((type, options = {}) => {
options.bubbles = true;
options.cancelable = true;
options.view = window;
const event = new MouseEvent(type, options);
const targetElement = elementSelector();
targetElement.dispatchEvent(event);
}, { dependencies: { elementSelector: selector } });
return dispatchFunc(type, options);
}
fixture`Fixture`
.page`http://devexpress.github.io/testcafe/example`;
test('Test', async t => {
await t.click('#tried-test-cafe');
const slider = Selector('span.ui-slider-handle.ui-corner-all');
await triggerMouseEvent(slider, 'mousedown');
await t.wait(1000);
const offsetLeft = await Selector('.slider-value').withText('5').offsetLeft;
await triggerMouseEvent(slider, 'mousemove', { clientX: offsetLeft });
await t.wait(1000);
await t
.expect(slider.offsetLeft).gte(352)
.expect(slider.offsetLeft).lte(353);
});
Also, TestCafe 1.15 will include the t.dispatchEvent method that allows you to trigger events using TestController.

React Native Hooks initializer not taking the correct value

What I am trying to do is sync a list of attendees from an online database, and if the current user is in the list, then disable a button, else enable the button.
I am using react native hook (I am not sure if I am using the term correctly as I am fairly new to react), in order to set the value of disabling the button.
The issue that I am facing is that the value is getting initialized to false, even tho it should clearly get initialized to true.
After adding some logging I made sure that the function is executing correctly and reaching the code where it sets the value to true.
const [buttonDisabled, changeButtonState] = useState( () => {
var database = firebase.database();
var userId = firebase.auth().currentUser.uid;
const dbRef = firebase.database().ref();
var Attendees = [];
var disable = false;
dbRef.child("gameAttendees").child(gameinfo.gameID).get().then((snapshot) => {
if (snapshot.exists()) {
Attendees = snapshot.val().Attendees;
for(var i=0;i<Attendees.length;i++){
if(Attendees[i]==userId){
return true;
}
}
} else {
console.log("no value");
return false;
}
}).catch((error) => {
console.error(error);
});
});
Adding an example of an async mount effect:
const Comp = () => {
const [s, setS] = useState(); // State will be undefined for first n renders
useEffect(() => {
// Call the async function and set the component state some time in the future
someAsyncFunction().then(result => setS(result));
}, []); // An effect with no dependencies will run only once on mount
return </>;
};

how to test global event bus which is inside mounted method in jest

export default class Test extends Vue {
mounted() {
this.getData();
eventBus.$on("get", (id: string) => {
this.displayData(id);
});
}
getData(){
return "hello"
}
displayData(id){
}
I have written spec for the Test component like below. I have used Global Event Bus and trying to check event is emitted or not.
const EventBus = new Vue();
const GlobalPlugins = {
install(v:any) {
// Event bus
v.prototype.eventBus = EventBus;
}
};
const localVue = createLocalVue()
localVue.prototype.eventBus = createLocalVue();
localVue.use(GlobalPlugins);
describe('Test TestSuite', () => {
let wrapper: any
let TestObj: any;
beforeEach(() => {
const mocks = {
eventBus: {
$on: jest.fn(),
$emit: jest.fn()
}
};
wrapper = mount(Test, {
mocks,
localVue,
router
});
console.log(eventBus.$emit) ---> returns null
TestObj = wrapper.findComponent(Test).vm;
console.log(eventBus.$emit) ---> returns null
});
Here, I tried to check the get event is emitted or not. but it gives null object only.
Problem solved by emitting event and mocking components before mounting.

Testing Methods within Vue Components using Jasmine

I have the following test which works great
it('does not render chapter div or error div', () => {
const payLoad = chapter;
const switcher = 'guild';
var vm = getComponent(payLoad, switcher).$mount();
expect(vm.$el.querySelector('#chapter-card')).toBeNull();
expect(vm.$el.querySelector('#error-card')).toBeNull();
});
To do this I wrote a helper method that mounts a component:
const getComponent = (prop1) => {
let vm = new Vue({
template: '<div><compd :payLoad="group" :index="index" "></compd ></div></div>',
components: {
compd,
},
data: {
payLoad: prop1,
},
})
return vm;
}
however, I have a method within my vue component compd. For simplicitys sake, lets call it
add(num,num){
return num+num;
}
I want to be able to write a test case similar to the following:
it('checks the add method works', () => {
expect(compd.add(1,2).toBe(3));
});
I cannot figure out how to do this. Has anyone any suggestions?
The documentation here:
https://v2.vuejs.org/v2/guide/unit-testing.html
Does not cover testing methods.
Source code from vue repo
As you can see the method gets called simply on the instance
const vm = new Vue({
data: {
a: 1
},
methods: {
plus () {
this.a++
}
}
})
vm.plus()
expect(vm.a).toBe(2)
You can also access the method via $options like in this case (vue source code)
const A = Vue.extend({
methods: {
a () {}
}
})
const vm = new A({
methods: {
b () {}
}
})
expect(typeof vm.$options.methods.a).toBe('function')
Update:
To test child components use $children to access the necessary child. Example
var childToTest = vm.$children.find((comp)=>comp.$options.name === 'accordion')` assuming name is set to `accordion`
After that you can
childToTest.plus();
vm.$nextTick(()=>{
expect(childToTest.someData).toBe(someValue)
done(); //call test done callback here
})
If you have a single child component and not a v-for put a ref on it
`
vm.$refs.mycomponent.myMethod()