Test

Details

All packages have tests written in jest. The are located under src/__test__/{pkgName}

- src/
    - __tests__/
        - api-server
        - common-all
        - common-server
        - dendron-cli
        - pods-core
        - engine-server
        - common-frontend
        - __snapshots__
  • NOTE: as of 2022-04-02, we use jest@26.6.0 to run tests

Writing

When writing a test for a package, put the test underneath the {pkgName} folder

For any tests where you need to setup a Dendron workspace, reference the engine, or use vaults, you should use runEngineTestV5. This function setups a workspace in a temporary directory with one or more vaults and lets you run your tests against a real Dendron environment.

Things to Note

  1. preSetupHook: ENGINE_HOOKS.setupSchemaPreset
  • Use preSetupHook to initialize workspace with pre-created notes and templates

Testing the Invocation of a Callback

If you're trying to validate that a callback was invoked as part of your test, be aware of the following behavior in async jest/mocha testing:

Incorrect:

test("Testing that onMyCallbackInvoked was invoked (WRONG)", (done) => {
  foo.onMyCallbackInvoked((bar) => {
    expect(bar).toBeTruthy();
    done();
  });
});

Correct:

test("Testing that onMyCallbackInvoked was invoked (CORRECT)", (done) => {
  foo.onMyCallbackInvoked((bar) => {
    try {
      expect(bar).toBeTruthy();
    } catch (err) {
      // Passing an argument to the done function will cause the test harness to
      // fail immediately, which is what we want.
      done(err);
    }
  });
});

The reason this is necessary is because an exception in the callback (such as a failure in an expect() call) will result in a failed promise, not an outright exception. Consequently, the test will instead just timeout waiting for done() to be called instead of failing immediately. Furthermore, instead of getting a helpful error message like expect(bar).toBeTruthy(); line failed, it will just report an ambiguous problem saying that the test hit a timeout.

There's a utility function that wraps the try / catch logic, which is the preferred way to write the test:

import {testAssertsInsideCallback} from "@dendronhq/common-test-utils";
...
test("Testing that onMyCallbackInvoked was invoked (CORRECT & PREFERRED WAY)", (done) => {
  foo.onMyCallbackInvoked((bar) => {
    testAssertsInsideCallback(() => {
      expect(bar).toBeTruthy();
    }, done);
  });
});

Creating notes after engine is started

If your test cases involve creating notes that cannot be done in the hooks, use NoteTestUtilsV4.createNoteWithEngine or CreateNoteFactory.createWithEngine in the test methods themselves

Testing a new Engine Method

Context

Engine methods are tested in multiple places, listed in Engine Test Contexts (Private)

To reduce writing the same test cases multiple times, engine tests are written in the following format where ENGINE_PRESETS contains a common set of test cases that can be tested both in memory and on the server.

describe("engine, notes/", () => {
  const nodeType = "NOTES";

  ENGINE_PRESETS.forEach((pre) => {
    const { name, presets } = pre;
    describe(name, () => {
      test.each(
        _.map(presets[nodeType], (v, k) => {
          return [k, v];
        })
      )("%p", async (_key, TestCase) => {
        const { testFunc, ...opts } = TestCase;
        await runEngineTestV5(testFunc, { ...opts, createEngine, expect });
      });
    });
  });
});

When running tests, jest will go over every test case in ENGINE_PRESETS. The list of all presets can be found in ../packages/engine-test-utils/src/presets/engine-server/index.ts (Private) and are organized by API method.

Creating a new preset

Follow the convention in ../packages/engine-test-utils/src/presets/engine-server/getByPath.ts (Private) to create a new preset. After the preset is created, tests will automatically run via Engine Test Contexts (Private)

Running a single test case from one preset

While working on a new preset, you might want to test just your single preset vs running over everything. In order to narrow the test case to a single preset, use getPreset in ../packages/engine-test-utils/src/presets/engine-server/index.ts (Private)

Example use case:

import { getPreset } from "../../presets";

test.only("bond", async () => {
  const preset = getPreset({
    key: "BASIC",
    nodeType: "NOTES",
    presetName: "render",
    presets: ENGINE_PRESETS,
  });
  const { testFunc, ...opts } = preset;
  await runEngineTestV5(testFunc, { ...opts, createEngine, expect });
});

Run a preset

Similar to Running a single test case from one preset (Private), except for running an entire preset at once

const presetName = "rename";
const group = getPresetGroup({ nodeType, presetName, presets: ENGINE_PRESETS });
describe.only(presetName, () => {
  test.each(
    _.map(group, (v, k) => {
      return [k, v];
    })
  )("%p", async (_key, TestCase) => {
    const { testFunc, ...opts } = TestCase;
    await runEngineTestV5(testFunc, { ...opts, expect });
  });
});

Ref

Engine Test Contexts

Executing

Run All Tests

  • Using CLI
cd $MONOREPO_ROOT
yarn ci:test:cli

Run a Single Test

  1. Open the .spec.ts file you want to test in VSCode
  2. Use command prompt and run > Tasks: Run tasks
  3. Run the following task > test:watch root

Backlinks