npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@dusty-phillips/rescript-zora

v4.0.0

Published

lightning-fast testing for a lightning-fast compiler

Downloads

332

Readme

rescript-zora: Lightning-fast unit tests

This package provides Rescript bindings for the Zora testing framework. Rescript and Zora go very well together because they have a common mission of SPEED.

In the interest of maintaining that speed, this package is asynchronous by default, though you can create blocking tests if you prefer.

This package mostly just binds directly to Zora, but there are a couple niceties to help work with Rescript promises and the standard library.

If you've used older versions of this package

I've migrated everything to async/await syntax and it now requires Rescript 10.1. You'll need to convert any non-blocking tests in your existing codebase to return promise or define them with async, but you don't need to throw done() calls in all your async tests.

Installation

Note: If you don't have a Rescript 9.1.1 project initialized already, the fastest way to get one is with npx rescript init myproject.

Install zora and this package:

npm install --save-dev @dusty-phillips/rescript-zora

Add @dusty-phillips/rescript-zora as a dependency in your bsconfig.json:

"bs-dependencies": ["@dusty-phillips/rescript-zora"]

Suggested configuration

Recent versions of node seem to cooperate better if you explicitly use the .mjs or .cjs suffix for your files. So you'll want your bsconfig to contain either:

  • suffix: .mjs and module: es6
  • suffix: .cjs and module: commonjs

I use .mjs in this configuration, but I have tested it with .cjs and it seems to work.

You'll probably also want to add the following package-specs configuration to your bsconfig.json:

  "suffix": ".mjs",
  "package-specs": {
    "module": "es6",
    "in-source": true
  },

If you like to keep your tests separate from your source code, you'll need to add that directory so Rescript will compile your test files:

  "sources": [
    {
      "dir": "src",
      "subdirs": true
    },
    { "dir": "tests", "subdirs": true, "type": "dev" }
  ],

So a minimal bsconfig.json might look like this:

{
  "name": "myproject",
  "version": "2.0.0",
  "suffix": ".mjs",
  "sources": [
    {
      "dir": "src",
      "subdirs": true
    },
    { "dir": "tests", "subdirs": true, "type": "dev" }
  ],
  "package-specs": {
    "module": "es6",
    "in-source": true
  },
  "bs-dependencies": ["@dusty-phillips/rescript-zora"]
}

Stand-alone test

The simplest possible Zora test looks like this:

// tests/simple.test.res
open Zora

zoraBlock("should run a test synchronously", t => {
  let answer = 3.14
  t->equal(answer, 3.14, "Should be a tasty dessert")
})

Building this with rescript will output a tests/simple.mjs file that you can run directly with node:

╰─○ node tests/standalone.js
TAP version 13
# should run a test asynchronously
ok 1 - Should answer the question
# should run a test synchronously
ok 2 - Should be a tasty dessert
1..2

# ok
# success: 2
# skipped: 0
# failure: 0

This output is in Test Anything Protocol format. The zora docs go into more detail on how it works with Zora.

Combining tests

You can include multiple zoraBlock statements, or you can pass the t value into the block function:

open Zora

zoraBlock("Should run some simple blocking tests", t => {
  t->block("should greet", t => {
    t->ok(true, "hello world")
  })

  t->block("should answer question", t => {
    let answer = 42
    t->equal(answer, 42, "should be 42")
  })
})

Running tests in parallel (async)

The Block in zoraBlock indicates that this is a blocking test. It's faster to run multiple independent tests in parallel:

// tests/standaloneParallel.res

open Zora

zora("should run a test asynchronously", async t => {
  let answer = 42
  t->equal(answer, 42, "Should answer the question")
})

zora("should run a second test at the same time", async t => {
  let answer = 3.14
  t->equal(answer, 3.14, "Should be a tasty dessert")
})

Note the absence of zoraBlock, and the presence of async. You can await other promises inside the test if you want.

Combining parallel tests

You can nest parallel async tests inside a blocking or non-blocking test, and run blocking tests alongside parallel tests:

// parallel.test.res
open Zora

let wait = (amount: int) => {
  Js.Promise2.make((~resolve, ~reject) => {
    reject->ignore
    Js.Global.setTimeout(_ => {
      resolve(. Js.undefined)
    }, amount)->ignore
  })
}

zora("Some Parallel Tests", async t => {
  let state = ref(0)

  t->test("parallel 1", async t => {
    {await wait(10)}-> ignore
    t->equal(state.contents, 1, "parallel 2 should have incremented by now")
    state.contents = state.contents + 1
    t->equal(state.contents, 2, "parallel 1 should increment")
  })

  t->test("parallel 2", async t => {
    t->equal(state.contents, 0, "parallel 2 should be the first to increment")
    state.contents = state.contents + 1
    t->equal(state.contents, 1, "parallel 2 should increment")
  })

  t->test("parallel 3", async t => {
    {await wait(20)}->ignore
    t->equal(state.contents, 2, "parallel 1 and 2 should have incremented by now")
    state.contents = state.contents + 1
    t->equal(state.contents, 3, "parallel 3 should increment last")
  })
})

This is the default and preferred test setup (zora and test) to take advantage of parallelism for speed. Note that you can combine parallel and blocking tests in the same zora or zoraBlocking block as well.

Test runner

You probably don't want to run each of your test files using separate node commands, though. You can use any TAP compliant test runner (see here for a list), but your best bet is probably to use Zora's bundled pta runner with onchange for watching for file changes:

npm install --save-dev pta onchange

With these installed, you can set the test command in your scripts as follows:

  "test": "onchange --initial '{tests,src}/*.js' -- pta 'tests/*.test.js'",

Or, if you prefer to keep your tests alongside your code in your src folder:

  "test": "onchange --initial 'src/*.js' -- pta 'src/*.test.js'",

Now npm test will do what you expect: run a test runner and watch for file changes.

Skip, only, and fail

Zora exposes functions to skip tests if you need to. If you have a failing test, just replace the call to Zora.test with a call to Zora.skip. Or, if you're running blocking tests, replace Zora.block with Zora.blockSkip.

For example:

open Zora

zora("should skip some tests", t => {
  t->skip("broken test", t => {
    t->fail("Test is broken")
  })

  t->blockSkip("also broken", t => {
    t->fail("Test is broken, too")
  })

})

The above also illustrates the use of the Zora.fail assertion to force a test to be always failing.

If you want to run and debug a single test, you can run it in only mode. As with skip, change the test's name from test to only or block to blockOnly. You must also change the top level zora/zoraBlock to zoraOnly/zoraBlockOnly.

open Zora

zoraOnly("should skip some tests", t => {
  t->only("only run this test", t => {
    t->ok(true, "Only working test")
  })

  t->test("don't run this test", t => {
    t->fail("Test is broken")
  })

})

However, only tests are intended only in development mode and zora will fail by default if you try to run one. To run in only mode, you can run:

npm test -- --only

or

ZORA_ONLY=true npm test

If you use this feature a lot, you could also consider putting additional test commands in your package.json scripts, one for local only development and one for CI:

"test": "onchange --initial '{tests,src}/*.js' -- pta 'tests/*.test.js'",
"test:only": "onchange --initial '{tests,src}/*.js' -- pta --only 'tests/*.test.js'",
"test:ci": "pta 'tests/*.test.js'",

Assertions

This library models all the default assertions provided by Zora except for those dealing with raising exceptions, which don't map neatly to Rescript exceptions. There are additional bindings for checking if a Rescript option is Some() or None or if a Belt.Result is Ok() or Error() and asserting on the value therein.

In the interest of avoiding bloat, I do not intend to add a lot of other Rescript-specific assertions.

//tests/assertions.test.res
open Zora

zora("Test assertions", t => {
  t->equal(42, 42, "Numbers are equal")
  t->notEqual(42, 43, "Numbers are not equal")
  let x = {"hello": "world"}
  let y = x
  let z = {"hello": "world"}
  t->is(x, x, "object is object")
  t->is(x, y, "object is object")
  t->isNot(x, z, "object is not object with same values")
  t->equal(x, z, "Object is deep equal")
  t->ok(true, "boolean is ok")
  t->notOk(false, "boolean is not ok")
  t->optionNone(None, "None is None")
  t->optionSome(Some(x), (t, n) => t->equal(n["hello"], "world", "option should be hello world"))
  t->resultError(Belt.Result.Error(x), "Is Error Result")
  t->resultOk(Belt.Result.Ok(x), (t, n) => t->equal(n["hello"], "world", "Is Ok Result"))
})

Running in the browser

Zora supports running tests in the browser, but I have not tested it with this Rescript implementation. I am open to PRs that will make this Rescript implementation work in the browser if changes are required.

Source Maps

The biggest problem with this library is that test failures point to the lines in the compiled js files instead of Rescript itself. If someone knows how to configure rescript and zora to use source maps, I'd love a PR.

Contributing

PRs are welcome.

Releasing

This is for my reference

  • update the version in bsconfig.json
  • npx np