My use case is that I have a UI which prevents operation B unless operation A happenned in the previous calendar month (accounting lifecycle madness - does not matter).
Once I have a mechanism to manipulate browser time, then the solution I chose was to start a scenario, perform operation A, advance the browser time to one month in the future, then perform operation B.
This involved using protractors browser.executeScript
to run JS in the browser context, and override the JS Date object using the timeshift-js module.
Here is the code
This code is using:
I can write scenarios like this:
Feature: Travelling in time
Scenario: Scenario that manipulates time
When I view some page
And I check what time it is
And I do operation A
And I advance the browser by 1 days
And I check what time it is
Then I can do operation B
Scenario: Scenario Double Check that the next scenario gets the correct time
When I view the list of maintenance requests
And I check what time it is
And here is the step implementation. There is nothing here cucumber specific so it should be easy to adapt to a jasmine or mocha based describe/it framework:
const timeShiftLibraryString = fs.readFileSync(`path/to/node_modules/timeshift-js/timeshift.js`, 'utf-8')
module.exports = function () {
this.When(/I advance the browser by (-?[0-9]+) (day|month)s?$/, function (offset, unit) {
const now = moment()
const future = moment().add(offset, unit)
const amountOfMillisecondsToAdvanceBrowser = (future.unix() - now.unix()) * 1000
const tolerance = 15 * 1000
const advanceTime = function (futureOffsetInMilliseconds, timeShiftLibraryString) {
// these two lines are only necessary because I dont want Timeshift in my production code, only during test
const timeshiftLibrary = new Function(timeShiftLibraryString)
timeshiftLibrary.call(window)
Date = window.TimeShift.Date
window.TimeShift.setTime(Date.now() + futureOffsetInMilliseconds)
return Date.now()
}
return browser.executeScript(advanceTime, amountOfMillisecondsToAdvanceBrowser, timeShiftLibraryString).then((browserTime) => {
const expectedTime = Date.now() + amountOfMillisecondsToAdvanceBrowser - tolerance
this.logger.debug(`Time manipulation complete: browserTime = ${moment(browserTime)}`)
if (browserTime >= expectedTime) {
return Promise.resolve(browserTime)
}
return Promise.reject(new Error(`advanceTime did not work: reported browserTime: ${browserTime}. Expected Time: ${expectedTime}`))
})
})
this.When(/I check what time it is/, function () {
const whatTimeIsItInTheBrowser = function () {
return Date.now()
}
return browser.executeScript(whatTimeIsItInTheBrowser).then((browserTime) => {
console.log(`Browser Time = ${moment(browserTime)}`)
})
})
}
Considerations: