Q:

How to change the browser date in protractor

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). 
I am writing a test that verifies operation B is working correctly, but I cannot perform operation B is working unless I have operation A that occured last month.  My protractor setup runs on top of real APIs, and mocking data is not an option that I will consider (granted if I chose to mock API responses this would be easier). 
The APIs do not allow me to manipulate the created/update dates of operation A, so my options to test this scenario are to manipulate the underlying DB or to push the browser into the next month while testing.  Assuming I want to take the browser approach, how can I get this working with protractor? 

A:

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

