Testing Typescript Projects

This page will discuss common topics around working with test suites using the Mill build tool

Defining Unit Test Suites

build.mill (download, browse)
package build

import mill._, javascriptlib._

object bar extends TypeScriptModule {
  object test extends TypeScriptTests with TestModule.Jest
}

object baz extends TypeScriptModule {
  object test extends TypeScriptTests with TestModule.Vitest
}

object foo extends TypeScriptModule {
  object test extends TypeScriptTests with TestModule.Mocha
}

object qux extends TypeScriptModule {
  object test extends TypeScriptTests with TestModule.Jasmine
}

Documentation for mill.example.javascriptlib This build defines 4 modules bar, baz, foo & qux with test suites configured to use Jest, Vitest, Mocaha & Jasmine respectively

> mill foo.test
...
...4 passing...
...

> mill bar.test
...Calculator
...
Test Suites:...1 passed, 1 total...
Tests:...4 passed, 4 total...
...

> mill baz.test
.../calculator.test.ts...
...Test Files  1 passed...
...Tests  4 passed...
...

> mill qux.test
...
4 specs, 0 failures

Test Dependencies

build.mill (download, browse)
package build

import mill._, javascriptlib._

object bar extends TypeScriptModule {
  def npmDeps = Seq("immutable@4.3.7")
  object test extends TypeScriptTests with TestModule.Jest
}

object foo extends TypeScriptModule {
  def moduleDeps = Seq(bar)
  object test extends TypeScriptTests with TestModule.Jest
}

Documentation for mill.example.javascriptlib In this example, foo depend on bar, but we also make foo.test depend on bar.test.

That lets foo.test make use of the default function exported from bar/test/utils/bar.tests.utils.ts, allowing us to re-use this test helper throughout multiple modules' test suites.

> mill foo.test
PASS .../foo.test.ts
...
Test Suites:...1 passed, 1 total...
Tests:...3 passed, 3 total...
...

> mill bar.test
PASS .../bar.test.ts
...
Test Suites:...1 passed, 1 total...
Tests:...1 passed, 1 total...
...

Integration Suite with Cypress

build.mill (download, browse)
package build

import mill._, javascriptlib._

object client extends ReactScriptsModule

object server extends TypeScriptModule {

  def npmDeps =
    Seq("@types/cors@^2.8.17", "@types/express@^5.0.0", "cors@^2.8.5", "express@^4.21.1")

  /** Bundle client as resource */
  def resources = Task {
    os.copy(client.bundle().path, Task.dest / "build")
    super.resources() ++ Seq(PathRef(Task.dest))
  }

  override def forkEnv = super.forkEnv() + ("PORT" -> "4000")

  object test extends TypeScriptTests with TestModule.Cypress {
    def service = server
    def port = "4000"
  }
}

Documentation for mill.example.javascriptlib In this example we demonstrate integration testing using cypress mill server.test will start the service on the speicifed port, run tests with configurations defined in cypress.config.ts and kill the service once completed

> mill server.test
...
...Server listening on port 4000
... app.cy.ts...
... All specs passed!...
...

Integration Suite with PlayWright

build.mill (download, browse)
package build

import mill._, javascriptlib._

object client extends ReactScriptsModule

object server extends TypeScriptModule {

  def npmDeps =
    Seq("@types/cors@^2.8.17", "@types/express@^5.0.0", "cors@^2.8.5", "express@^4.21.1")

  /** Bundle client as resource */
  def resources = Task {
    os.copy(client.bundle().path, Task.dest / "build")
    super.resources() ++ Seq(PathRef(Task.dest))
  }

  def forkEnv = super.forkEnv() + ("PORT" -> "3000")

  object test extends TypeScriptTests with TestModule.PlayWright {
    def service = server
    def port = "6000"
  }
}

Documentation for mill.example.javascriptlib In this example we demonstrate integration testing using playwright mill server.test will start the service on the speicifed port, run tests with configurations defined in playwright.config.ts and kill the service once completed

> mill server.test
...
...Server listening on port 6000
...
...1 passed...
...