How to change the browser date in protractor

前端 未结 1 1754
隐瞒了意图╮
隐瞒了意图╮ 2021-01-24 00:59

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).

相关标签:
1条回答
  • 2021-01-24 01:34

    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:

    • angular: 1.5.10
    • protractor: 5.1.2
    • cucumber-js: 1.3.3
    • timeshift-js: 1.0.1

    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:

    • the tricky part is serialising the timeshift-js library. I did not want to package it with my app, so that meant I had to inject it on demand during test
    • you will not see the browser logs unless you explicitly go and get them using https://github.com/angular/protractor/blob/master/docs/faq.md#how-can-i-get-hold-of-the-browsers-console
    • you cannot manipulate the browser time until you have done at least one page get, the browser needs to have loaded some HTML and JS before you can manipulate Date in the browser JS context
    0 讨论(0)
提交回复
热议问题