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
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
- in-memory engine tests: ../packages/engine-test-utils/src/__tests__/engine-server/enginev2.spec.ts (Private)
- api engine tests: ../packages/engine-test-utils/src/__tests__/api-server/engine.spec.ts (Private)
Executing
Run All Tests
- Using CLI
cd $MONOREPO_ROOT
yarn ci:test:cli
Run a Single Test
- Open the
.spec.ts
file you want to test in VSCode - Use command prompt and run
> Tasks: Run tasks
- Run the following task
> test:watch root
Backlinks