Jest: Config option for setting pretendToBeVisual

Created on 11 Oct 2018  路  6Comments  路  Source: facebook/jest

馃殌 Feature Proposal

An option to set jsdom pretendToBeVisual inside test cases.

Motivation

I need to test code that set a timestamp value depending on document.hidden.
jsdom toggles the value depending on pretendToBeVisual as documented in the jsdom README.

Currently pretendToBeVisual appears to be set on test start and not changeable from within a single test.

I see that I can set a custom environment configuration documentation.

Example

To test the following function getTimeDuration:

/**
 * First call starts a timer and returns a function
 * that when called will give the elapsed time in
 * milisecond, while subtracting "hidden" time.
 * @returns {function} Calling this function will return the elapsed time.
 */
export default function getTimeDuration () {
  const startTimestamp = Date.now()
  let hiddenTimeDelta = 0
  let hiddenTimeStart = 0

  document.addEventListener('visibilitychange', trackHiddenTime)

  function trackHiddenTime () {
    if (document.hidden) {
      hiddenTimeStart = Date.now()
    } else {
      hiddenTimeDelta += (Date.now() - hiddenTimeStart)
    }
  }

  return () => {
    document.removeEventListener('visibilitychange', trackHiddenTime)

    return (Date.now() - hiddenTimeDelta) - startTimestamp
  }
}

We could write the following test code:

test('visibilitychange duration used by PayloadTiming', () => {
  // jestDateMock.advanceTo(0) is
  // needed due to issue: https://github.com/hustcc/jest-date-mock/issues/15
  jestDateMock.advanceTo(0)

  const timer = getTimeDuration()

  jestDateMock.advanceBy(10) // visible state

  jsdom.pretendToBeVisual = false // hidden state
  document.dispatchEvent(new Event('visibilitychange'))
  jestDateMock.advanceBy(10)

  jsdom.pretendToBeVisual = true // visible state
  document.dispatchEvent(new Event('visibilitychange'))
  jestDateMock.advanceBy(10)

  expect(timer()).toBe(20)
  jestDateMock.clear()
})

Pitch

Currently, I do not see any way to test the Page Visibility API in jest without this option. Regardless of the actual name. I'm not attached in any way to jsdom.pretendToBeVisual.

Please let me know if this change has to be done upstream in jsdom before it can be available in jest or if there is work-around possible in how jest utilitizes jsdom.

Most helpful comment

For future readers who find this thread.

The simple and correct way to set document.hidden in jest and jsdom is to add the following to your test.

let hidden = true
Object.defineProperty(document, "hidden", {
  configurable: true,
  get () { return hidden },
  set (bool) { hidden = Boolean(bool) }
})

In effect you will shadow document.hidden. A declined patch that also sets visibilityState can be found here: https://github.com/jsdom/jsdom/pull/2392

All 6 comments

You can use testEnvironmentOptions to set it, see https://jestjs.io/docs/en/configuration#testenvironmentoptions-object

Access to jsdom as the test is running won't happen (breaks the abstraction), if you want that use https://www.npmjs.com/package/jest-environment-jsdom-global

@SimenB My point with:

I see that I can set a custom environment configuration documentation.

Is that it is currently not possible to toggle pretendToBeVisual and have the same document object.

But I can infer that the answer to:

Please let me know if this change has to be done upstream in jsdom before it can be available in jest or if there is work-around possible in how jest utilitizes jsdom.

Is that it has to be implemented in jsdom. Am I correct?

That seems like an upstream issue, yes 馃檪 I linked a module which gives you raw access to the jsdom instance. If that doesn't work, something'd have to change with jsdom itself

Will I then be able to set jest testEnvironmentOptions inside a test as shown in the initial post?

@SimenB I have added a feature request to jsdom. Didn't see your second link until now. I will check it out - thanks

For future readers who find this thread.

The simple and correct way to set document.hidden in jest and jsdom is to add the following to your test.

let hidden = true
Object.defineProperty(document, "hidden", {
  configurable: true,
  get () { return hidden },
  set (bool) { hidden = Boolean(bool) }
})

In effect you will shadow document.hidden. A declined patch that also sets visibilityState can be found here: https://github.com/jsdom/jsdom/pull/2392

Was this page helpful?
0 / 5 - 0 ratings