Skip to content

Instantly share code, notes, and snippets.

@mgrybyk
Last active February 4, 2022 01:06
Show Gist options
  • Save mgrybyk/a2fec1b0dae7dd1ecafbc4e39817d351 to your computer and use it in GitHub Desktop.
Save mgrybyk/a2fec1b0dae7dd1ecafbc4e39817d351 to your computer and use it in GitHub Desktop.
WebdriverIO snippets
const lang = 'en'
describe('element ui date picker', () => {
it('set current date +60 days', () => {
browser.url('https://element.eleme.io/#/en-US/component/date-picker')
const datePickerInput = $('.el-date-editor>input')
expect(datePickerInput).toBeClickable()
const datePicker = new DatePickerElement(datePickerInput)
const dateToBeSet = currentDateShift(60) // current date + 60 days
datePicker.setValue(dateToBeSet)
expect(datePickerInput).toHaveValue(formatDate(dateToBeSet))
browser.pause(2000) // demo purposes
})
})
// date helpers
const timeZoneDate = (date) => new Date(date.getTime() - date.getTimezoneOffset() * 60 * 1000)
const formatDate = (date) => timeZoneDate(date).toISOString().split('T')[0]
const dateNoTime = (date) => new Date(formatDate(date))
const dateShift = (shift, date) => new Date(dateNoTime(date).getTime() + shift * 24 * 60 * 60 * 1000)
const currentDateShift = (shift) => dateShift(shift, new Date())
// every date picker in Element UI is a dropdown
class CustomDropdownElement {
constructor(rootSelector, toggleSelectorOrElement) {
this.rootSelector = rootSelector
this.toggleSelectorOrElement = toggleSelectorOrElement
}
get $toggle() {
return typeof this.toggleSelectorOrElement === 'string' ? $(this.toggleSelectorOrElement) : this.toggleSelectorOrElement
}
get $dropdown() {
return $(this.rootSelector)
}
open() {
this.$toggle.click({ wait: true })
this.$dropdown.waitForClickable()
this.waitForAnimation()
}
/**
* show/hide timeout https://element.eleme.io/#/en-US/component/dropdown
*/
waitForAnimation() {
browser.pause(150)
}
}
// implementation os single date picker, there are also other types
class DatePickerElement extends CustomDropdownElement {
constructor(inputSelectorOrElement) {
const dropdownSelector = '.el-date-picker.el-popper[x-placement]'
super(dropdownSelector, inputSelectorOrElement)
this.calendarElement = new DatePickerCalendarElement(dropdownSelector)
}
setValue(date) {
this.open()
const currentMonth = this.calendarElement.getCurrentMonth(DatePickerElement.getMonthFn)
const currentYear = this.calendarElement.getCurrentYear(DatePickerElement.getYearFn)
// change month if needed
if (currentMonth !== date.getMonth()) {
let directionButton
if (currentYear !== date.getFullYear()) {
directionButton = currentYear > date.getFullYear() ? this.calendarElement.$prevMonth : this.calendarElement.$nextMonth
} else {
directionButton = currentMonth > date.getMonth() ? this.calendarElement.$prevMonth : this.calendarElement.$nextMonth
}
browser.waitUntil(() => {
directionButton.click()
return (
this.calendarElement.getCurrentMonth(DatePickerElement.getMonthFn) === date.getMonth() &&
this.calendarElement.getCurrentYear(DatePickerElement.getYearFn) === date.getFullYear()
)
})
}
// set day
this.calendarElement.getDayOfMonthElement(date.getDate()).click()
this.$dropdown.waitForExist({ reverse: true })
}
static getYearFn = ($header) => DatePickerElement.getYearOrMonthFn($header, 1)
static getMonthFn = ($header) => DatePickerElement.getYearOrMonthFn($header, 2)
static getYearOrMonthFn = ($header, idx) => $header.$(`span:nth-of-type(${idx})`).getText().trim()
}
// base part of every date picker
class DatePickerCalendarElement {
constructor(rootSelector) {
this.rootSelector = rootSelector
}
get $root() {
return $(this.rootSelector)
}
get $header() {
return this.$root.$('[class*="picker__header"]')
}
get $prevMonth() {
return this.$header.$('.el-icon-arrow-left')
}
get $nextMonth() {
return this.$header.$('.el-icon-arrow-right')
}
get $selectedDay() {
return this.$root.$('.el-date-table .current')
}
getDayOfMonthElement(day) {
return this.$root.$(`.//table[@class="el-date-table"]//td[contains(@class, "available")]//*[normalize-space(text()) = "${day}"]`)
}
getCurrentMonth(getMonthText) {
let result = -1
browser.waitUntil(
() => {
const month = getMonthText(this.$header)
result = monthsLocalizedArray(lang).indexOf(month)
return result > -1
},
{ timeoutMsg: 'failed to get current month from calendar' }
)
return result
}
getCurrentYear(getYearText) {
let result = -1
browser.waitUntil(
() => {
result = Number.parseInt(getYearText(this.$header))
return Number.isInteger(result)
},
{ timeoutMsg: 'failed to get current year from calendar' }
)
return result
}
}
/**
* full-icu package has to be installed to make `Date.toLocaleString` work on some systems
* see https://stackoverflow.com/questions/23199909/using-tolocalestring-in-node-js/23200062#23200062
*/
const monthsLocalizedArray = function (locale = 'en') {
const result = []
for (let i = 0; i < 12; i++) {
result.push(new Date(2010, i).toLocaleString(locale, { month: 'long' }))
}
return result
}
const lang = 'en'
describe('element ui date picker', () => {
it('set current date +60 days', () => {
browser.url('https://element.eleme.io/#/en-US/component/date-picker')
const datePickerInput = $('.el-date-editor>input')
expect(datePickerInput).toBeClickable()
const datePicker = new DatePickerElement(datePickerInput)
const dateToBeSet = currentDateShift(60) // current date + 60 days
datePicker.setValue(dateToBeSet)
expect(datePickerInput).toHaveValue(formatDate(dateToBeSet))
browser.pause(2000) // demo purposes
})
})
// date helpers
const timeZoneDate = (date: Date) => new Date(date.getTime() - date.getTimezoneOffset() * 60 * 1000)
const formatDate = (date: Date) => timeZoneDate(date).toISOString().split('T')[0]
const dateNoTime = (date: Date) => new Date(formatDate(date))
const dateShift = (shift: number, date: Date) => new Date(dateNoTime(date).getTime() + shift * 24 * 60 * 60 * 1000)
const currentDateShift = (shift: number) => dateShift(shift, new Date())
// every date picker in Element UI is a dropdown
abstract class CustomDropdownElement {
readonly rootSelector: string
readonly toggleSelectorOrElement: string | WebdriverIO.Element
constructor(rootSelector: string, toggleSelectorOrElement: string | WebdriverIO.Element) {
this.rootSelector = rootSelector
this.toggleSelectorOrElement = toggleSelectorOrElement
}
protected get $toggle() {
return typeof this.toggleSelectorOrElement === 'string' ? $(this.toggleSelectorOrElement) : this.toggleSelectorOrElement
}
protected get $dropdown() {
return $(this.rootSelector)
}
public open() {
this.$toggle.click({ wait: true })
this.$dropdown.waitForClickable()
this.waitForAnimation()
}
/**
* show/hide timeout https://element.eleme.io/#/en-US/component/dropdown
*/
waitForAnimation() {
browser.pause(150)
}
}
// implementation os single date picker, there are also other types
class DatePickerElement extends CustomDropdownElement {
private calendarElement: DatePickerCalendarElement
constructor(inputSelectorOrElement: string | WebdriverIO.Element) {
const dropdownSelector = '.el-date-picker.el-popper[x-placement]'
super(dropdownSelector, inputSelectorOrElement)
this.calendarElement = new DatePickerCalendarElement(dropdownSelector)
}
public setValue(date: Date) {
this.open()
const currentMonth = this.calendarElement.getCurrentMonth(DatePickerElement.getMonthFn)
const currentYear = this.calendarElement.getCurrentYear(DatePickerElement.getYearFn)
// change month if needed
if (currentMonth !== date.getMonth()) {
let directionButton: WebdriverIO.Element
if (currentYear !== date.getFullYear()) {
directionButton = currentYear > date.getFullYear() ? this.calendarElement.$prevMonth : this.calendarElement.$nextMonth
} else {
directionButton = currentMonth > date.getMonth() ? this.calendarElement.$prevMonth : this.calendarElement.$nextMonth
}
browser.waitUntil(() => {
directionButton.click()
return (
this.calendarElement.getCurrentMonth(DatePickerElement.getMonthFn) === date.getMonth() &&
this.calendarElement.getCurrentYear(DatePickerElement.getYearFn) === date.getFullYear()
)
})
}
// set day
this.calendarElement.getDayOfMonthElement(date.getDate()).click()
this.$dropdown.waitForExist({ reverse: true })
}
private static getYearFn = ($header: WebdriverIO.Element) => DatePickerElement.getYearOrMonthFn($header, 1)
private static getMonthFn = ($header: WebdriverIO.Element) => DatePickerElement.getYearOrMonthFn($header, 2)
private static getYearOrMonthFn = ($header: WebdriverIO.Element, idx: number) => $header.$(`span:nth-of-type(${idx})`).getText().trim()
}
// base part of every date picker
class DatePickerCalendarElement {
private rootSelector: string
constructor(rootSelector: string) {
this.rootSelector = rootSelector
}
private get $root() {
return $(this.rootSelector)
}
private get $header() {
return this.$root.$('[class*="picker__header"]')
}
get $prevMonth() {
return this.$header.$('.el-icon-arrow-left')
}
get $nextMonth() {
return this.$header.$('.el-icon-arrow-right')
}
get $selectedDay() {
return this.$root.$('.el-date-table .current')
}
getDayOfMonthElement(day: number) {
return this.$root.$(`.//table[@class="el-date-table"]//td[contains(@class, "available")]//*[normalize-space(text()) = "${day}"]`)
}
getCurrentMonth(getMonthText: ($header: WebdriverIO.Element) => string) {
let result = -1
browser.waitUntil(
() => {
const month = getMonthText(this.$header)
result = monthsLocalizedArray(lang).indexOf(month)
return result > -1
},
{ timeoutMsg: 'failed to get current month from calendar' }
)
return result
}
getCurrentYear(getYearText: ($header: WebdriverIO.Element) => string) {
let result = -1
browser.waitUntil(
() => {
result = Number.parseInt(getYearText(this.$header))
return Number.isInteger(result)
},
{ timeoutMsg: 'failed to get current year from calendar' }
)
return result
}
}
/**
* full-icu package has to be installed to make `Date.toLocaleString` work on some systems
* see https://stackoverflow.com/questions/23199909/using-tolocalestring-in-node-js/23200062#23200062
*/
const monthsLocalizedArray = function (locale = 'en') {
const result = []
for (let i = 0; i < 12; i++) {
result.push(new Date(2010, i).toLocaleString(locale, { month: 'long' }))
}
return result
}
it('emulate file drop event', () => {
browser.url('https://the-internet.herokuapp.com/upload')
const fileInput = $('input[type="file"]')
expect(fileInput).toExist()
fileInput.setValue('/Users/m/sources/multiforce-backoffice-site/foo.pdf')
const dropArea = $('#drag-drop-upload')
expect(dropArea).toBeVisible()
expect(dropArea).not.toHaveChildren()
browser.execute((fd) => {
function fireMouseEvent(type, relatedTarget) {
const evt = new MouseEvent(type, { relatedTarget, bubbles: true })
evt.dataTransfer = { ...new DataTransfer() }
evt.dataTransfer.files = document.querySelector('input[type="file"]').files
relatedTarget.dispatchEvent(evt)
}
fireMouseEvent('drop', fd)
}, dropArea)
expect(dropArea).toHaveChildren(1)
browser.pause(2000) // demo
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment