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
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();
};
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.
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 </>;
};
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.
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()