How to mock day.js using testcafe to click on calender - testing

I'm trying to click on a calendar using test cafe with today's date, I tried using moment to get the date format but not able click on calendar, I'm using selector (withText), it always returns no selector found.

Example for https://www.phila.gov/the-latest/archives/#/?templates=featured
import { Selector } from 'testcafe';
fixture `Select date`
.page `https://www.phila.gov/the-latest/archives/#/?templates=featured`
test('Select date', async t => {
const dateInput = Selector('input[name="start"]');
await t
.click(dateInput)
.click(dateInput.parent('.vdp-datepicker').find('.cell.day').withText('2'))
.expect(dateInput.value).eql('May. 02, 2020');
});

Related

Vuex Store keeps state somehow between tests

I have a strange problem with testing the frontend part of my project. I use Vue components for the frontend. The project is website for teachers to set appointments for the assistent(s) so the assistant can ready everything for class.
First let me explain the structure.
I have a component which lists all appointments by date. Every date is a seperate card and all the appointments for one date are rows on that card. Each row is a specific timeslot. Appointments can be added to the list either by clicking on a button at the top of the card or by clicking the row number.
So I created three components: AppointmentsList.Vue which gets the appointments from the backend and builds a list of the cards, AppointmentsCard.Vue, which receives the date and all the appointments for that date as props, and lastly AppointmentRow.Vue which show the detailsof the appointment in a row of the table. The state, appointments, requested dates and other data, is kept in a Vuex store.
I build the project using TDD, using Jest and Vue-test-utils for writing the tests. Mocks are used to simulate the responses of the backend. Tests showing the appointments with the cards and rows works fine. But when testing the buttons I encountered a strange problem.
In the following code I show you my tests, redacted for brevity. First everything is imported and then the responses of the backend for varieous endpoints are defined. Only the appointmentResponse is important. Note that two appointments are returned.
The function createStore builds a store out of the modules. I keep all state, getters and mutations in modules. Before each test is run, I create a new store and initialize the store with the data of the responses using the mutations of the store. After each test the jest mocks are cleared and the vue-test-utils wrapper is destroyed.
*AppointmentList.spec.js*
import {mount, createLocalVue} from '#vue/test-utils'
import flushPromises from 'flush-promises'
import AppointmentsList from '../../resources/js/components/AppointmentsList.vue'
import axios from 'axios'
import Vuex from 'vuex'
import appointmentsmodule from '../../resources/js/storemodules/appointmentsModule.js'
import classhoursmodule from '../../resources/js/storemodules/classhoursModule.js'
import classroomsmodule from '../../resources/js/storemodules/classroomsModule.js'
import experimentsmodule from '../../resources/js/storemodules/experimentsModule.js'
import locationsmodule from '../../resources/js/storemodules/locationsModule.js'
import subjectsmodule from '../../resources/js/storemodules/subjectsModule.js'
import usersmodule from '../../resources/js/storemodules/usersModule.js'
import Vue from 'vue'
import { wrap } from 'lodash'
let appointmentResponse={"status":200,"lines":2,"data":[{"id":1,"subject_id":1,"owner_id":1,"group_id":1,"appointment_at":"2021-12-29T00:00:00.000000Z","classhour_id":1,"classroom_id":1,"experiment_id":null,"short_name":"magnam","description":"Sit cum quae quae quo quo consequatur.","demo":"0","toa_preferred_id":null,"location_id":1,"created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":2,"subject_id":2,"owner_id":1,"group_id":1,"appointment_at":"2021-12-28T00:00:00.000000Z","classhour_id":1,"classroom_id":1,"experiment_id":null,"short_name":"possimus","description":"Velit eos sed esse reprehenderit.","demo":"0","toa_preferred_id":null,"location_id":1,"created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let userResponse={"status":200,"lines":3,"data":[{"id":1,"code":"Est","name":"Mr. Americo Mertz I","email":"user1#hetstreek.nl","actual_location":"1","registrar":"1","email_verified_at":"2022-01-04T15:50:26.000000Z","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z","department_location_id":null,"welcome_valid_until":null,"roles":[{"id":2,"name":"toa","guard_name":"web","created_at":"2022-01-04T15:50:24.000000Z","updated_at":"2022-01-04T15:50:24.000000Z","pivot":{"model_id":"1","role_id":"2","model_type":"App\\Models\\User"}},{"id":1,"name":"beheerder","guard_name":"web","created_at":"2022-01-04T15:50:22.000000Z","updated_at":"2022-01-04T15:50:22.000000Z","pivot":{"model_id":"1","role_id":"1","model_type":"App\\Models\\User"}}]},{"id":2,"code":"Est","name":"Mr. Americo Mertz I","email":"user2#hetstreek.nl","actual_location":"2","registrar":"1","email_verified_at":"2022-01-04T15:50:26.000000Z","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z","department_location_id":null,"welcome_valid_until":null,"roles":[{"id":3,"name":"docent","guard_name":"web","created_at":"2022-01-04T15:50:25.000000Z","updated_at":"2022-01-04T15:50:25.000000Z","pivot":{"model_id":"2","role_id":"3","model_type":"App\\Models\\User"}}]},{"id":3,"code":"Est","name":"Mr. Americo Mertz I","email":"user3#hetstreek.nl","actual_location":"1","registrar":"1","email_verified_at":"2022-01-04T15:50:26.000000Z","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z","department_location_id":"{\"department_id\":1,\"location_id\":1,\"updated_at\":\"2022-01-04T15:50:26.000000Z\",\"created_at\":\"2022-01-04T15:50:26.000000Z\",\"id\":1}","welcome_valid_until":null,"roles":[{"id":4,"name":"leerling","guard_name":"web","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z","pivot":{"model_id":"3","role_id":"4","model_type":"App\\Models\\User"}}]}]}
let classhourResponse={"status":200,"lines":2,"data":[{"id":1,"name":"9","starttime":"15:22","endtime":"12:56","location_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":3,"name":"1","starttime":"21:47","endtime":"20:16","location_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let classroomResponse={"status":200,"lines":2,"data":[{"id":1,"name":"non","number":"756","in_use":"1","student_accessible":"0","teacher_accessible":"1","location_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":3,"name":"ut","number":"214","in_use":"1","student_accessible":"0","teacher_accessible":"1","location_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let experimentResponse={"status":200,"lines":2,"data":[{"id":1,"name":"nam","description":"Aliquam nihil voluptas aut vel neque.","student_selectable":"0","user_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":3,"name":"similique","description":"Exercitationem officiis excepturi aut veniam voluptatum.","student_selectable":"1","user_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let locationResponse={"status":200,"lines":2,"data":[{"id":1,"name":"Omnis.","school_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":2,"name":"Illo.","school_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let schoolResponse={"status":200,"lines":1,"data":[{"id":1,"schoolname":"Jaydon Mante","domain":"hetstreek.nl","max_locations":"1","payed_at":null,"due_date":null,"active":"1","storage_folder":"jaydonmante_hetstreeknl","created_at":"2022-01-05T14:41:49.000000Z","updated_at":"2022-01-05T14:41:49.000000Z"}]}
let subjectResponse={"status":200,"lines":2,"data":[{"id":1,"code":"SK","description":"Quis.","block_for_days":"9","color":"A3A3A3","created_at":"2022-01-05T18:54:41.000000Z","updated_at":"2022-01-05T18:54:41.000000Z"},{"id":2,"code":"re","description":"Libero.","block_for_days":"3","color":"A3A3A3","created_at":"2022-01-05T18:54:41.000000Z","updated_at":"2022-01-05T18:54:41.000000Z"}]}
function createStore(){
return {
modules:{
appointments:appointmentsmodule,
classhours:classhoursmodule,
classrooms:classroomsmodule,
experiments:experimentsmodule,
locations:locationsmodule,
subjects: subjectsmodule,
users: usersmodule
}
}
}
let wrapper
let store
beforeEach(()=>{
const newStore=createStore()
//define a new store for each test
store=new Vuex.Store(newStore)
//sets the range of dates for which the appointments need to be shown.
let startdate = new Date(2021, 11, 27)
let enddate = new Date(startdate)
enddate.setDate(startdate.getDate()+3)
//initialize the store
store.commit('setAppointmentsPeriod',{startdate,enddate})
store.commit('setSubjectFilter', [])
store.commit('storeAppointments',[])
store.commit('storeClasshours',classhourResponse.data)
store.commit('storeClassrooms',classroomResponse.data)
store.commit('storeExperiments',experimentResponse.data)
store.commit('storeLocations',locationResponse.data)
store.commit('storeSubjects',subjectResponse.data)
store.commit('storeUsers',userResponse.data)
//log to show the appointments are empty
console.log(store.state.appointments.appointments)
jest.clearAllMocks()
})
afterEach(()=>{
jest.clearAllMocks
wrapper.destroy()
})
jest.mock("axios")
const localVue =createLocalVue()
localVue.use(Vuex)
When the component AppointmentsList is mounted in the test it detects the change of period from the store, which triggers a request to the backend and loads the appointments for that period.
The first test tests the button a top of the card. It triggers the button and verifies the modal to add an appointment is opened, fills in the form, triggers the submit button and verifies that the data is send to the backend, and the store is updated so now three appointments should be in the store. This works fine.
test('add button adds appointment to list', async ()=>{
//set up the mock data.
let classhour=1
let appointmentToAdd={
id:3,
owner_id:1,
experiment_id:null,
short_name:"Schaduwpracticum",
description:"Lichtkastje met voeding, kartonnetje, scherm, liniaal",
group_id:1,
subject_id:1,
toa_preferred_id:1,
appointment_at:"2021-12-27",
classhour_id:classhour,
classroom_id:1,
demo:false,
location_id:1,
created_at:new Date(),
updated_at:new Date()
}
//mock axios responses. Get returns the appointments, post returns the added appointment.
axios.get.mockResolvedValue({status:200 , data:appointmentResponse.data})
axios.post.mockResolvedValue({status:200, data:{"status":200, "lines":1,"data":appointmentToAdd}})
//appointment is added for this date.
let checkDate=new Date(2021,11,27)
//show all two appointments
wrapper = mount(AppointmentsList, {store, localVue})
await flushPromises()
//verify two appointments in the store
expect(store.state.appointments.appointments).toHaveLength(2)
//find add button for first date
//click buttons open add/edit modal with date field prefilled
await wrapper.find('[data-cy="20211227"]').trigger('click')
const wrappedAddAppointment=wrapper.findComponent({name:'add-appointment'})
//check modal is opened with correct date
expect(wrappedAddAppointment.vm.$props.modalState).toBe(true)
expect(wrappedAddAppointment.vm.$data.appointment.appointment_at).toStrictEqual(checkDate)
//fill in fields and click submit
await wrappedAddAppointment.find('input[id="shortname"]').setValue(appointmentToAdd.short_name)
await wrappedAddAppointment.find('input[id="description"]').setValue(appointmentToAdd.desc)
await wrappedAddAppointment.find('input[id="teacher"]').setValue(1)
await wrappedAddAppointment.find('input[id="group"]').setValue(appointmentToAdd.group_id)
await wrappedAddAppointment.find('input[id="subject"]').setValue(appointmentToAdd.subject_id)
await wrappedAddAppointment.find('input[id="preferredtoa"]').setValue(appointmentToAdd.toa_preferred_id)
await wrappedAddAppointment.find('input[id="appointment_at"]').setValue(appointmentToAdd.appointment_at)
await wrappedAddAppointment.find('input[id="classhour"]').setValue(appointmentToAdd.classhour_id)
await wrappedAddAppointment.find('input[id="classroom"]').setValue(appointmentToAdd.classroom_id)
await wrappedAddAppointment.find('input[id="demo"]').setChecked(false)
await wrappedAddAppointment.find('button[name="save-button"]').trigger('click')
//check axios post(/appointments) is called
expect(axios.post).toHaveBeenCalledTimes(1)
expect(axios.post.mock.calls[0][0]).toContain('/appointments')
expect(axios.get).toHaveBeenCalledTimes(2)
//verify the modal is closed
expect(wrappedAddAppointment.vm.$props.modalState).toBe(false)
//verify the appointment is added to the store
expect(store.state.appointments.appointments).toHaveLength(3)
//verify added appointment is in list
expect(wrapper.text()).toContain(appointmentToAdd.short_name)
})
The next test tests clicking the row number. It should also open the AddAppointment modal with date and classhour (=row number) prefilled. As adding the appointment is already tested, the test stops.
test('clicking classhour adds appointment to list', async ()=>{
//setup ajax responses
axios.get.mockResolvedValue({status:200 , data:appointmentResponse.data})
axios.post.mockResolvedValue({status:200, data:{"status":200, "lines":1,"data":appointmentToAdd}})
let checkDate=new Date(2021,11,27)
console.log(store.state.appointments.appointments)
//show all appointments
const wrapper = mount(AppointmentsList, {store, localVue})
await flushPromises()
expect(axios.get).toHaveBeenCalledTimes(1)
--> expect(store.state.appointments.appointments).toHaveLength(2)
//test fails on line above. Added next two lines to check the appointments in the store and the return value of the axios call.
console.log(store.state.appointments.appointments) //shows three appointments with the last one being the appointment added in the previous test
console.log(axios.get.mock.results[0].value) //shows only two appointments returned as expected
//find classhour button of date 27-dec-2021 and classhour 3
await wrapper.find('[data-cy="20211227classhour3"]').trigger('click')
const wrappedAddAppointment=wrapper.findComponent({name:'add-appointment'})
//check modal is opened with correct date and classhour
expect(wrappedAddAppointment.vm.$props.modalState).toBe(true)
expect(wrappedAddAppointment.vm.$data.appointment.appointment_at).toStrictEqual(checkDate)
expect(wrappedAddAppointment.vm.$data.appointment.classhour.id).toBe(3)
})
This test fails at the line marked with an arrow. Jest reports not two appointments but three in the store. As I have rebuild the store between tests, this make no sense to me. The added appointment should no longer be in the store. The mock call clearly shows two appointments in the response. But it gets stranger even more. Instead of adding the two initial appointments to the store I decided to change the response to return no appointments. I changed the mockresponse to:
axios.get.mockResolvedValue({status:200 , data:{status:200, lines:0, data:[]} })
and the verification to
expect(store.state.appointments.appointments).toHaveLength(0)
This test passes, so the added appointment from the first test is not retained.
Can anyone help me shed some light on this?
Edit. Added the appointmentModule as requested in the comments.
const appointmentsmodule = {
state(){
return{
appointments:[],
appointmentPeriod:{
startdate:null,
enddate:null
},
subjectFilter:[],
}
},
mutations:{
storeAppointments(state, appointments){
//console.log('storeAppointments', appointments)
state.appointments = appointments
},
setAppointmentsPeriod(state, period){
//console.log('setAppointmentsPeriod', period)
state.appointmentPeriod = period
},
setSubjectFilter(state, filter){
//console.log('set Filter', filter)
state.subjectFilter= filter
},
addAppointment(state, appointmentToAdd){
state.appointments.push(appointmentToAdd)
}
},
getters:{
getFilteredAppointments: state=>{
//console.log('getting filtered appointments')
if(state.subjectFilter.length>0){
////console.log('filtered')
return state.appointments.filter(appointment=>{
/* //console.log(state.subjectFilter)
//console.log(appointment.subject_id)
//console.log(state.subjectFilter.includes(appointment.subject_id)) */
return state.subjectFilter.includes(appointment.subject_id)})
}
else{
////console.log('no filter')
return state.appointments
}
},
getAppointmentsPeriod(state){
//console.log('getting appointments period')
return state.appointmentPeriod
}
}
}
export default appointmentsmodule
I solved the problem. When storing the response from the backend I replaced the array in the vuex.state with the new array, thereby breaking reactivity.
When I use
state.appointments.splice(0,Infinity, ...newAppointments)
all the elements of the original array are replaced with the new elements. Reactivity is preserved and the tests passes

Automating mat-option in testcafe

Tried automating dropdown using the below methods but the dropdown values couldn't be selected.
Method 1:
const comboOption = Selector("mat-option").child("span").withExactText("Hello");
await t.click(comboOption);
Method 2:
ClientFunction(() => {
document.getElementsByClassName('mat-option-text')[0].innerText = 'Hello';
document.getElementsByClassName('mat-option-text')[0].click();
return "Hello";});
The mat-option tag is not within mat-select. It is outside mat-select and within div tag.
Are there other ways to achieve automating mat-option ?
Thank you for the code snippets.
As far as I understand, you are trying to click an option element in another select element.
I created a simple test that should perform the steps you described:
import { Selector } from 'testcafe';
fixture`Getting Started`
.page`http://devexpress.github.io/testcafe/example`;
const selectElement = Selector('#preferred-interface');
const optionElement = selectElement.find('option');
test('My first test', async t => {
await t
.click(selectElement)
.click(optionElement.withText('Both'))
.expect(selectElement.value).eql('Both');
});
If I misunderstood your question, could you please share a simple example of your .html and a detailed description of
what you want to do in the test and which results you expect?

dueChange event for duet picker not working with Ionic 5 / Vue 3

I am using Duet Date Picker in the Ionic 5/ Vue 3 application. The event listener for the duetChange is not working for me.
Here is my code snippet:
<duet-date-picker #duetChange="handleInput($event)"identifier="date" :localization.prop="localisation" direction="left"></duet-date-picker>
handleInput(e: any) {
console.log("e", e);
this.$emit("input", this.content);
}
I have even tried following listners:
v-on:duetChange="handleInput($event)"
v-on:change="handleInput($event)"
#change="handleInput($event)"
Is this the right way to add an event listener or am I missing something?
Here is the code sandbox link:
https://codesandbox.io/s/old-silence-1f0nx?file=/src/App.vue
TIA
I tried to use the duetDatePicker in a Vue CodeSandbox, the following works:
<duet-date-picker
#duetChange="handleInput"
identifier="date"
:localization.prop="localisation_uk"
>
Along with the above, my methods object contained the handleInput function declaration.
Check the App.vue file in the codesandbox for details.
I was able to get it to work the old fashion way...
onMounted(() => {
// Select the date picker component
const date = document.getElementById("date-picker");
// Listen for when date is selected
date.addEventListener("duetChange", function (e) {
console.log("selected date", e.detail.valueAsDate);
});
});
<duet-date-picker id="date-picker"
#duetChange="handleInput"
identifier="date"
:localization.prop="localisation_uk"
>

Select element by containing text in TestCafe

How can I select an HTML element containing specific text?
In Selenium Xpath selectors are used, but TestCafe doesn't support Xpath.
How do I do it in TestCafe?
According to the official documentation, in order to select an element containing a certain text, I should use the .withText() method with the selected element, example given is:
import { Selector } from 'testcafe';
fixture `Example`
.page `https://devexpress.github.io/testcafe/example/`;
test('Click test', async t => {
const selectBasedOnText = Selector('label').withText('I have tried TestCafe');
await t
.click(selectBasedOnText);
});
for selecting a label element with the text "I have tried TestCafe".
what worked for me:
import xPathToCss from 'xpath-to-css'
and then use as ex:
.click(xPathToCss("//a[#class='icon-menu login show-login-panel list-item list-item-next']"));
no,
testcafe supports xpath
https://github.com/Programmingarea/XpathSelectorIhaveNotCreated
download the xpath selector
after downloading that
go to your testcafe file and type:
importing xpath-selector
import XPathSelector from './xpath-selector';
using it:
const usingxpath = XPathSelector("your xpath");
simple!
if any doubt ask it in reply

Select a value in Drop down box using protractor

I'm learning protractor and i came across with an issue selecting a given value from an Autocomplete.
How can i click a given string which has following source code using the protractor
I'm practicing in the following URL: https://material.angular.io/components/autocomplete/overview#option-groups
Protractor is only able to interact with elements present in the DOM of the page. The elements for the underlying state options will not be loaded into the DOM until the input box for the State Group has been interacted with.
You can select the Maine option as follows:
app.js
describe('desribe the test', () => {
it('the it', async () => {
await browser.get('https://material.angular.io/components/autocomplete/overview#option-groups');
let statesGroupField = element(by.xpath('//input[#placeholder="States Group"]'));
await statesGroupField.click();
let maineDropdownOption = element(by.xpath('//span[text()="Maine"]'));
await maineDropdownOption.click();
await browser.driver.sleep(5000);
})
})