Vue: vm.$isServer returns false when using Jest in testEnvironment Node

Created on 20 Dec 2018  ·  9Comments  ·  Source: vuejs/vue

Version

2.5.21

Reproduction link

https://github.com/futureaus/servue/tree/min-rep

Steps to reproduce

npm install --dev
jest

What is expected?

.vue file to render correctly

What is actually happening?

  × renders file correctly (2710ms)

  ● renders file correctly

    ReferenceError: head is not defined

      at Object.eval (eval at <anonymous> (node_modules/lodash.template/index.js:1089:12), <anonymous>:8:11)
      at TemplateRenderer.renderSync (node_modules/vue-server-renderer/build.js:8020:16)
      at RenderContext.done (node_modules/vue-server-renderer/build.js:8264:39)
      at RenderContext.next (node_modules/vue-server-renderer/build.js:2459:19)
      at RenderContext.cachedWrite [as write] (node_modules/vue-server-renderer/build.js:2323:9)
      at RenderContext.next (node_modules/vue-server-renderer/build.js:2473:25)
      at cachedWrite (node_modules/vue-server-renderer/build.js:2323:9)
      at renderStringNode$1 (node_modules/vue-server-renderer/build.js:7599:5)
      at RenderContext.renderNode (node_modules/vue-server-renderer/build.js:7402:5)
      at RenderContext.next (node_modules/vue-server-renderer/build.js:2469:23)
      at cachedWrite (node_modules/vue-server-renderer/build.js:2323:9)
      at renderElement (node_modules/vue-server-renderer/build.js:7642:5)
      at renderNode (node_modules/vue-server-renderer/build.js:7406:5)
      at renderComponentInner (node_modules/vue-server-renderer/build.js:7524:3)
      at renderComponent (node_modules/vue-server-renderer/build.js:7488:5)
      at renderNode (node_modules/vue-server-renderer/build.js:7404:5)

undefined
Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        4.839s, estimated 7s
Ran all test suites.

This only happens when using Jest, to test in a normal node environment use node tests/normal.js

Jest, or an error with the environment detection of Vue is causing global.process.env.VUE_ENV to return undefined instead of 'server'.

Jest is running in "testEnvironment" : "node"

The $isServer is being used in lib/imports/headify.js

Most helpful comment

I think the issue is with docs, mainly., but the general ones for SSR, nothing jest-specific.

vue-server-renderer does set process.env.VUE_ENV = server. But you don't pass that environment variable into your app bundle, so inside of our bundle, it's not set, and consequently, $isServer returns false.

We need to update docs here: https://ssr.vuejs.org/guide/build-config.html#server-config

It's demonstrated in the hackernew repo (here) but shoul definitely be mentioned in the docs explicitly, of course..

All 9 comments

Most of the tests in vue are written for front end, so it makes sense to have that variable to false by default. If you are interested in the check, it's done at src/core/util/env.js (search for isServerRendering). With vue test utils, you can change $isServer by using the mocks property in the options passed to mount and shallowMount

cc @eddyerburgh maybe you know something else I'm missing here

@posva

vue-server-renderer sets VUE_ENV to server but it's not working on the node environment in Jest

Shouldn't vue respect Jest's node environment

no, because it would require people to explicitly mock $isServer to false which is the most common use case

@posva it may be worth noting, I am using vue-server-renderer and creating a bundle renderer, i'm not looking to test vue files. How can I mock it this way, when vue-server-renderer is isolated?

There's no info anywhere about using vue-server-renderer and mocking a node environment anywhere for jest.

I think the issue is with docs, mainly., but the general ones for SSR, nothing jest-specific.

vue-server-renderer does set process.env.VUE_ENV = server. But you don't pass that environment variable into your app bundle, so inside of our bundle, it's not set, and consequently, $isServer returns false.

We need to update docs here: https://ssr.vuejs.org/guide/build-config.html#server-config

It's demonstrated in the hackernew repo (here) but shoul definitely be mentioned in the docs explicitly, of course..

@LinusBorg thanks, this fixed my issue, however, I am still confused as to why this is required only in the jest environment, and not in normal node

I would assume it's because jest runs tests in individual sandboxed processes, so the code bundled by bundleRenderer, when run inside of a test, doesn't get the VUE_ENV variable that was assigned in another process during test setup.

The "normal node" code, on the other hand, runs the renderer in the same process, so it can access the env var that vue-server-renderer has set in that process.

My change to the webpack config will inject the env variable as a static string into the server bundle, so it doesn't really access global.process.env when rendering.

Bur for example, your jest test runs fine when setting the env variable externally:

VUE_ENV=server yarn jest

I needed $isServer to be true just for one individual Vue instance in my tests. Temporarily overriding Vue.prototype.$isServer was not an option since it is not writable.

Eventually I've found a solution which might be interesting for others, therefore I'll share it here: I created a function which hotswaps a Vue instance's whole prototype and replaces it with a proxy of itself which returns true when asked for $isServer:

function enableSsr(vm) {
  Object.setPrototypeOf(
    vm,
    new Proxy(Object.getPrototypeOf(vm), {
      get: (target, key, receiver) => key === '$isServer'
        ? true
        : Reflect.get(target, key, receiver)
    })
  )
}

You can use it like this:

const vm = new Vue(...)

enableSsr(vm)

If you have to test mixins or similar stuff that relies on $isServer, you may need to run that function pretty early, in a beforeCreate hook:

new Vue({
  beforeCreate() {
    enableSsr(this)
  },

  // ...
})

Hope this helps. 🙂

Was this page helpful?
0 / 5 - 0 ratings