Search code examples
typescriptunit-testingjestjsbazelmonorepo

Jest Testing with Bazel Throws "Module ts-jest in the transform option was not found"


I have a working Bazel BUILD file that looks like this:

package(default_visibility = ["//visibility:public"])

load("@io_bazel_rules_docker//nodejs:image.bzl", "nodejs_image")
load("@npm_bazel_typescript//:index.bzl", "ts_library")

# TODO run jest tests and stop build if test not passes
# TODO also run tests from dependent packages
load("@lbm//:jest.bzl", "jest_test")
jest_test(
    name = "test",
    srcs = glob(
        include = ["**/*.ts"],
    ),
    jest_config = "@lbm//:jest.config.js",
    deps = [
        "//packages/enums/src:lib",
        "//packages/hello/src:lib",
        "@npm//faker",
        "@npm//@types/faker",
        "@npm//express",
        "@npm//@types/express",
        "@npm//jest",
        "@npm//ts-jest",
        "@npm//@types/jest",
    ],
)

ts_library(
    name = "lib",
    srcs = glob(
        include = ["**/*.ts"],
        exclude = ["**/*.spec.ts"]
    ),
    deps = [
        "//packages/enums/src:lib",
        "//packages/hello/src:lib",
        "@npm//faker",
        "@npm//@types/faker",
        "@npm//express",
        "@npm//@types/express",
    ],
)

nodejs_image(
    name = "server",
    data = [":lib"],
    entry_point = ":index.ts",
)

load("@io_bazel_rules_docker//container:container.bzl", "container_push")

container_push(
   name = "push_server",
   image = ":server",
   format = "Docker",
   registry = "gcr.io",
   repository = "learning-bazel-monorepo/server",
   tag = "dev",
)

Building the server works fine. But running the test fails.

When I run bazel test //services/server/src:test, I get the following output:

INFO: Analyzed target //services/server/src:test (0 packages loaded, 0 targets configured).
INFO: Found 1 test target...
FAIL: //services/server/src:test (see /home/flolu/.cache/bazel/_bazel_flolu/698f7adad10ea020bcdb85216703ce08/execroot/lbm/bazel-out/k8-fastbuild/testlogs
/services/server/src/test/test.log)
Target //services/server/src:test up-to-date:
  bazel-bin/services/server/src/test_loader.js
  bazel-bin/services/server/src/test.sh
INFO: Elapsed time: 0.947s, Critical Path: 0.72s
INFO: 2 processes: 2 linux-sandbox.
INFO: Build completed, 1 test FAILED, 2 total actions
//services/server/src:test                                               FAILED in 0.1s
  /home/flolu/.cache/bazel/_bazel_flolu/698f7adad10ea020bcdb85216703ce08/execroot/lbm/bazel-out/k8-fastbuild/testlogs/services/server/src/test/test.log

INFO: Build completed, 1 test FAILED, 2 total actions

and the test.log file has to following content:

exec ${PAGER:-/usr/bin/less} "$0" || exit 1
Executing tests from //services/server/src:test
-----------------------------------------------------------------------------
● Validation Error:

  Module ts-jest in the transform option was not found.
         <rootDir> is: /home/flolu/.cache/bazel/_bazel_flolu/698f7adad10ea020bcdb85216703ce08/sandbox/linux-sandbox/3/execroot/lbm/bazel-out/k8-fastbuild/bin/services/server/src/test.sh.runfiles/lbm/external/lbm

  Configuration Documentation:
  https://jestjs.io/docs/configuration.html

So it seems as if something with ts-jest is not working. When running jest manually, I don't get any errors.

My [jest.config.js][2] in the root of the project looks like this:

module.exports = {
  roots: ['<rootDir>/services/server/src', '<rootDir>/packages/hello/src'],
  testMatch: ['**/__tests__/**/*.+(ts|tsx|js)', '**/?(*.)+(spec|test).+(ts|tsx|js)'],
  transform: {
    '^.+\\.(ts|tsx)$': 'ts-jest',
  },
};

You can try it yourself by cloning this repo: https://github.com/flolude/minimal-bazel-monorepo

Update 1

I've tried to implement the original Answer from @Charlie OConor, but then I get this error:

services/server/src/util.spec.ts:1:21 - error TS2307: Cannot find module './util'.

1 import { add } from './util';
                      ~~~~~~~~

That is why I added the util.ts file to the srcs like this:

srcs = glob(
    include = ["**/*.ts"],
),

But then I get this error:

ERROR: /home/flolu/Desktop/minimal-bazel-monorepo/services/server/src/BUILD:33:1: in args attribute of nodejs_test rule //services/server/src:test: label '//services/server/src:test_lib.js' in $(location) expression expands to more than one file, please use $(locations //services/server/src:test_lib.js) instead.  Files (at most 5 shown) are: [services/server/src/index.js, services/server/src/util.js, services/server/src/util.spec.js]. Since this rule was created by the macro 'jest_test', the error might have been caused by the macro implementation
ERROR: Analysis of target '//services/server/src:test' failed; build aborted: Analysis of target '//services/server/src:test' failed; build aborted
INFO: Elapsed time: 4.487s
INFO: 0 processes.

Solution

  • Edit Real Issue

    I took a second look. Leaving the bellow answer since I think it has useful stuff in it. Your version of build_bazel_rules_nodejs was rather old. Update to the newest version 1.01. You were on version 0.42.2 which I think has some weird ways it handles npm dependencies.

    http_archive(
        name = "build_bazel_rules_nodejs",
        sha256 = "e1a0d6eb40ec89f61a13a028e7113aa3630247253bcb1406281b627e44395145",
        urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.0.1/rules_nodejs-1.0.1.tar.gz"],
    )
    

    Also update the "@bazel/typescript": "^1.0.1", in your package.json since they need to be in sync. There are some other breaking changes between the other libs you are using in the repo. I got it to work by commenting out all the docker and other stuff that wasn't relevant. It shouldn't be too hard to update those.

    Original Answer

    Probably not the answers you're looking for, but you probably don't need ts-jest. Its job is to make your life easier not having a separate compile step you need to coordinate before running a test. Great if your running scripts form a package.json but you have bazel which is all about defining the dependency graph. This makes it easy to create a ts_library for your test then depend on it, well the javascript output in jest_test

    Add another tsconfig.test.json with

    {
      "extends": "./tsconfig.json",
      "lib": ["jest"]
    }
    
    

    This will have it typecheck correctly with all the jest stuff, describe(), it(), etc.

    To compose to tsconfigs.json use ts_config so place this in /BUILD

    load("@npm_bazel_typescript//:index.bzl", "ts_config")
    ts_config(
        name = "tsconfig.jest.json",
        src = "tsconfig.test.json",
        deps = [
            ":tsconfig.json",
        ],
    )
    

    Then create the ts_library for your test and define the javascript output to use in jest_test

    load("@npm_bazel_typescript//:index.bzl", "ts_library")
    ts_library(
        name = "test_lib",
        srcs = ["util.spec.ts"],
        # References the test tsconfig
        tsconfig = "//:tsconfig.jest.json",
        deps = [
            "//packages/enums/src:lib",
            "//packages/hello/src:lib",
            "@npm//faker",
            "@npm//@types/faker",
            "@npm//express",
            "@npm//@types/express",
            "@npm//cors",
            "@npm//@types/jest",
        ],
    )
    
    filegroup(
        name = "test_lib.js",
        srcs = [":test_lib"],
        output_group = "es5_sources",
    )
    
    load("@lbm//:jest.bzl", "jest_test")
    jest_test(
        name = "test",
        srcs = [
            ":test_lib.js",
        ],
        jest_config = "@lbm//:jest.config.js",
        deps = [
            # UPDATE HERE
            ":lib",
            # END UPDATE 
            "//packages/enums/src:lib",
            "//packages/hello/src:lib",
            "@npm//faker",
            "@npm//express",
            "@npm//jest",
        ],
    )
    

    These rules are kinda boiler paltey so you could wrap them up into gen rules.

    I did pull your code and have no idea why you aren't able to source ts-node. You can even import it in jestconfig.js. Not that you want to but it shows that the dependency is there. My assumption is there is an underlying issue with around jest or ts-jest, but I couldn't find anything.