Doc TypeError in Jest test environment

Hello everyone,

I am trying to test yjs with Jest. Unfortunately I was not able to get an initial test running because of a TypeError.
Building my code and running it in the browser works. I was not able to get the yjs Doc running in the test environment.
(Yes, I could mock yjs, but in this case I want to test my code together with the yjs library)

Here is a basic dummy example I was not able to run:

// example.spec.ts

import { Doc } from "yjs";

describe("example test", () => {

  it("should be defined", () => {
    const doc = new Doc();

    expect(doc).toBeDefined();
  });

});

Error message:

TypeError: _yjs.Doc is not a constructor

  4 | 
  5 |   it("should be defined", () => {
> 6 |     const doc = new Doc();
    |                 ^

The error message when trying to import the doc from the dist directory:

  ● Test suite failed to run

    Cannot find module 'yjs/dist/src/internals' from 'example.spec.ts'

    > 1 | import { Doc } from "yjs/dist/src/internals";
        | ^
      2 | 

Does anyone have an idea, how to approach or solve this error?
(Sry for my beginner question :see_no_evil:)

Hi @flow,

I’m sorry this is not working for you. I’m partially to blame for this because I export Yjs as a ESM module (a fairly new standardized javascript module format). A lot of old bundlers don’t interpret ESM modules properly and then you get errors like yours. I still export Yjs as a ESM module because it reduces the bundle size and this module format works natively in the browser.

Some things you could try:

  • Import the cjs module (the old module format that all bundlers understand). Try import * as Y from "yjs/dist/yjs.cjs"
  • Make sure to use the import * as Y syntax, and not import { Doc } from 'yjs'. A lot of old bundlers differentiate between these two (especially if they depend on the cjs bundle).
  • Import the mjs module explicitly (the new module format). Try import * as Y from "yjs/dist/yjs.mjs"

Let me know if any of the proposed solutions works. If not, we will find another solution.

Hi @dmonad,

thank you for the fast answer, hints with the modules and the proposed solutions!
Unfortunately I was not able to get my setup working. So I ran some more tests.

Here is my setup and the things I tried that did or did not work out:

Setup

I want to use yjs in a react application. Moreover, I use create-react-app which offers a maintained build and test environment.
I would say that create-react-app is the most common tool for building react applicaions. Everything is maintained in the react-scripts package. The configuration options for jest are quite limited, so extending their config might not be ideal. There is an option to eject the config but then you loose support by the tool. So it would be great to find a solution without ejecting.

Problem

For everyone new to the topic, here is a post, I would say describes the problem quite well:

The following issue describes the problem with .mjs files, CRA and Jest:

Might there be a way to support older bundlers with yjs or do we have to wait until Jest officially supports it and create-react-app adapts this version of jest?

My experiments

I created a demo project for trying out the different import statements. Unfortunately none of them worked. The problem is when importing the .mjs or .cjs file directly, the runner (and also my IDE type checking) was not able to find the files.

Commit with the test files:

The GitHub Action results from running the tests:


Another approach was to clone the yjs repo and adapt the package.json & rollup.config.js to the following (only showing my changes):

// rollup.config.js:

...previos imports

import pkg from "./package.json"; // <------ added

export default [{
  input: './src/index.js',
  output: {
    name: 'Y',
    file: pkg.main,  // <---------------- changed
    format: 'cjs',
    sourcemap: true,
    ...
  },
  external: id => /^lib0\//.test(id)
}, {
  input: './src/index.js',
  output: {
    name: 'Y',
    file: pkg.module,   // <---------------- changed
    format: 'esm',
    sourcemap: true
  },
  external: id => /^lib0\//.test(id)

package.json

{
    "main": "./dist/yjs.js",
    "module": "./dist/yjs.esm.js",
    "unpkg": "./dist/yjs.esm.js",
}

This brought me that step further, that it could find the Doc. But the next error was that it does not find the Observerable class from lib0.

I stopped my experimenting here, because I am not sure, if this is the right way to approach the problem. As far as I get it, yjs bundels with the new javascript module format (or as some might call it “Michael Jackson Scripts” :wink: ). Webpack 4, Babel or in this case here Jest lack behind which makes importing .mjs files quite hard / not possible for now.
So might there be an option in yjs bundling to support both options?


Some more things that I encountered:

It is quite interesting, that when I start a codesandbox (default react typescript template) with react, react-scripts and typescript (which uses an older version of all the packages and node 10.x instead of 14.x) the test works with the ESM module import.
Here is the setup (on top the right side browser preview window there is a Tab which states “Tests” where you can open the test results):


At the moment I am a little confused, why it seems to work in CodeSandbox but not in a new local setup of create-react-app.

Any ideas for new approaches or possible solutions?