56

String.prototype.replaceAll() is a useful method and while building and executing everything works fine. However, all Jest-tests fail with the following error:

TypeError: replaceAll is not a function

These are my dependencies:

"dependencies": {
  "core-js": "^3.6.5",
  "vue": "^2.6.11",
  "vue-class-component": "^7.2.3",
  "vue-i18n": "^8.22.0",
  "vue-property-decorator": "^8.4.2",
  "vue-router": "^3.3.4",
  "vuex": "^3.5.1"
},
"devDependencies": {
  "@vue/test-utils": "^1.1.0",
  "jest-junit": "^12.0.0",
  "ts-jest": "^26.4.1",
  "typescript": "~3.9.3",
  "vue-jest": "^3.0.7",
  "vue-template-compiler": "^2.6.10"
},

How can I fix this behavior?

Rafael Tavares
  • 5,678
  • 4
  • 32
  • 48
leonheess
  • 16,068
  • 14
  • 77
  • 112

4 Answers4

60

This most likely happens because String.prototype.replaceAll is not implemented in Node.js (at least as of version v14.15.0).

One alternative you could use are regular expressions like in this example:

const str = 'foo-foo';
const regex = /foo/g; // Note the 'g' flag, which matches all occurrences of the expression

console.log(str.replace(regex, 'bar')); // 'bar-bar'

You can check here for more info on regular expressions.

Piper
  • 1,266
  • 3
  • 15
  • 26
ElCholoGamer
  • 615
  • 6
  • 9
  • 1
    And if you need to pass some random string. Maybe html tag with attributes. Then this becomes even bigger issue. – Eugene Sep 01 '21 at 17:58
42

The problem

This happens because replaceAll is a new function not implemented in all browsers nor older Node.js versions (older than 15.0), as mentioned in the other answer. You can see which browsers support it in Can I Use:

enter image description here

But, instead o using .replace with a RegExp, you can add a polyfill to support older browsers (if needed). In my case, I'm using Electron, so I need to do it for Jest only.

The shim can be found here.

The solution

Using as a polyfill for browsers

  1. Install as a dependency with npm i string.prototype.replaceall or yarn add string.prototype.replaceall;

  2. Add the following code in your project. It need to be added in one place only.

import replaceAllInserter from 'string.prototype.replaceall';

replaceAllInserter.shim();

Using in Jest only

Update Node.js

As @leonheess mentioned in the comments, you can update Node.js to a more recent version. You'll need a version that uses the version 8.5 from V8, which is when .replaceAll was implemented.

According to the list available on the Node.js website, neither version uses V8 8.5, but as of Node.js 15.0.0, the version 8.6 of V8 is used. So, upgrade to Node.js v15 or higher.

Use as a polyfill

If you want to fix this problem for Jest only, you can do as mentioned in this issue:

  1. Install as a dev dependency with npm i -D string.prototype.replaceall or yarn add -D string.prototype.replaceall;

  2. Modify your Jest config, add a setupFilesAfterEnv property as bellow:

{
  "jest": {
    "setupFilesAfterEnv": ["<rootDir>/jestSetup.js"]
  }
}
  1. Add the following code to your jestSetup file:
import replaceAllInserter from 'string.prototype.replaceall';

replaceAllInserter.shim();
JoSSte
  • 2,953
  • 6
  • 34
  • 54
Rafael Tavares
  • 5,678
  • 4
  • 32
  • 48
5

You can also add this anywhere in your main.js file

if (typeof String.prototype.replaceAll === "undefined") {
  String.prototype.replaceAll = function (match, replace) {
    return this.replace(new RegExp(match, 'g'), () => replace);
  }
}
SEMICS
  • 181
  • 3
  • 5
  • This does not work if the `match` is an actual regex like `.`, but I combined your answer with [this one](https://stackoverflow.com/a/13340174/4759619) – MikeL Nov 14 '22 at 12:08
2

You can use Babel for transpiling your code (to ES5) and also allow it to inject polyfills where needed.

In your case, using .replaceAll method fails because, as explained in other answers, the tests are running on a Node version which does not support that method.

I do not advice doing that the other answerers say, because they are simply fixing this specific problem, but you might ran into similar problems if you'll use other methods not supported by your current Node version.

To guarantee never to have such problems, Babel is used alongside core-js.

Here's an important quote from this page: https://jestjs.io/docs/getting-started#using-babel

babel-jest is automatically installed when installing Jest and will automatically transform files if a babel configuration exists in your project.

You would also need to install a Babel preset for vue so it would compile you vue-specific files: https://www.npmjs.com/package/@vue/babel-preset-app

So, in your root folder, look for the Babel configuration file, or create one: babel.config.js (used to be .babelrc.js or a .json file).

Use this configuration:

  1. make sure to update your core-js package and explicitly mention the minor version when configuring it for Babel (see below)
  2. must activate the useBuiltIns setting in @babel/preset-env for core-js polyfills to work.
  3. the targets setting should also be specified, since your tests run on Node (and not your browser). You can also set up a .browserlist file for that.
module.exports = {
  presets: [
    ['@babel/preset-env', { 
      "corejs": "3.26", 
      "useBuiltIns": "usage", 
      "targets": { "node": "current" } 
    }],
    '@babel/preset-react'],
  plugins: [
    "@babel/plugin-transform-runtime"
  ]
};

Interesting Read:

Running Mocha 6 ES6 tests with Babel 7, how to set up?

vsync
  • 118,978
  • 58
  • 307
  • 400