Test

Writing Te

n How to debug and tests

Children

ts

Note: You may see references to older test utilities such as runLegacyMultiWorkspaceTest. Please use latest test functions as described below

To write a test, please use the describeMultiWS and describeSingleWS functions. These work in the same way as mocha's describe, except that they will set up a multi-vault or a single-vault workspace before running any tests. You can then place tests inside to write your test cases, or even add describes to further organize your tests. Here's a simple example of its usage:

Example

// Need these imports
import { WorkspaceType } from "@dendronhq/common-all";
import { ENGINE_HOOKS } from "@dendronhq/engine-test-utils";
import { getDWorkspace } from "../../workspace";
import { describeMultiWS, setupBeforeAfter } from "../testUtilsV3";
import { expect } from "../testUtilsv2";
import { ExtensionProvider } from "../../ExtensionProvider";
// Then create a test suite
suite("GIVEN testing code setupLegacyWorkspaceMulti", function () {
  // has to be function(), not arrow
  describeMultiWS(
    "WHEN configured for NATIVE workspace",
    {
      preSetupHook: ENGINE_HOOKS.setupBasic, // optional, do something before the workspace initializes. You can use a preset like this, or write your own code.
      workspaceType: WorkspaceType.NATIVE, // optional
    },
    () => {
      // this can NOT be async, must be a regular function

      // If you need something to be done after the workspace initializes, but before tests, you can add a `before` or `beforeEach` hook here

      test("THEN initializes correctly", () => {
        // can be a regular function or async
        // You can access the workspace inside the test like this:
        const { engine, wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        // Then perform any actions and checks
        const testNote = engine.notes["foo"];
        expect(testNote).toBeTruthy();
      });

      test("THEN is of NATIVE type", (done) => {
        const { type } = ExtensionProvider.getDWorkspace();
        expect(type).toEqual(WorkspaceType.NATIVE);
      });
    }
  );
  // ...
});

Note: the top level test function passed to describeMultiWS or describeSingleWS cannot be async. (In the example above, this is the () => { // this can NOT be async ...} block) Otherwise, the framework may report the tests as passing even if there are asserts or exceptions failing. A run-time guard has been added that should fail the test if an async test function is provided.

However, the test() functions within the callback themselves can be async with a done parameter like this example, or they can be async functions without the done. Either will work correctly, you can choose based on what you need for the function you are testing.

The first object allows you to configure the workspace that's being created. It has a lot of configuration options, see here and here. The most common ones used are the following ones:

Skipping or limiting which tests run temporarily

When writing new tests, it's useful to only run a single test, or to skip a test. describe allows you to do this with describe.only and describe.skip, e.g. you just add .only or .skip to the function name.

suite("GIVEN thing", function () {
  describe.only("This one will run", () => {
    // ...
  });

  describe("This one won't", () => {

  });
});

describe*WS functions also support this, allowing you to skip or limit which tests run temporarily.

Gotchas

  • check that tests pass not just locally but also in the pipeline (usually runs on much worse hardware). the main discrepancy you'll see timeout issues in the pipeline. a common value to set for long running tests is 5e3 (5s). see timeout
  • if you create disposables, remember to clean them up
    const disposable = vscode.workspace.onDidSaveTextDocument(() => {
      assert(false, "Callback not expected");
    });
    ...
    disposable.dispose()
    done();
    

Arguments

afterHook

  • NOTE: this is deprecated. please use an after block inside your test instead

Run after all the tests have run

beforeHook

Run before we stub vscode mock workspace

preActivateHook

Run before dendron is activated

preSetupHook

Use this to set up the notes in the test workspace, and anything else that you want to happen before the workspace setup is completed and the engine is initialized. You can use a preset like this example, or you can also create notes yourself like this example.

workspaceType

This is optional. It allows you to set up a Code or Native workspace. It defaults to Code. Native workspaces don't have the dendron.code-workspace file and may have small differences in behavior.

modConfigCb

You can use this callback to modify the dendron.yml configuration for the test workspace. See here for an example.

timeout

  • default: 2e3

Custom timeout for test in milleseconds You will need to set this when stepping through mocha tests using a debugger, otherwise the test will timeout during debugging See Breakpoints for more details

Test etiquette

Your tests should follow a BDD-Light style. The example above shows how this can be accomplished. If you want to add some "AND" clauses, you can nest your tests inside describes to achieve that.

Each test should test a single functionality. The tests should not depend on each other: mocha allows skipping individual tests, and it should be possible to skip one test without skipping the entire describe*WS block. To achieve this, make sure that each tests does any necessary setup inside itself, or in the preSetupHook.

If you need to do some action before all tests in describe*WS block and it needs to be done after the workspace has initialized, you can use the before mocha hook to do so. Simply add it inside describe*WS like this:

describeMultiWS(
  "WHEN ...",
  {
    /* ... */
    preSetupHook: async () => {
      // Action to do before workspace initializes
    },
  },
  () => {
    before(async (done) => {
      // can be async or regular
      // Action to do after workspace initialized, but before any tests are run
    });

    test("THEN ...", async () => {
      // can be async or regular
      /* ... test code here */
    });
  }
);

There's also a beforeEach mocha hook if this action needs to be repeated before each test.

Details

Note that VSCode uses mocha as its default test runner whereas Dendron uses jest.

We shim the jest methods when testing the plugin though so that we can re-use the same logic between our non-vscode packages and the Dendron VSCode Plugin.

Executing Tests

Run All Tests

  1. Open the debug view inside vscode
  2. Run Extension Integ Tests in the dropdown
  • TIP: consider enabling "Uncaught Exceptions" under "Breakpoints" when running tests. Otherwise, if you forget to await a function that returns a promise and that function throws an exception, the test will appear to pass even though an exception was thrown.

Limited test cases

See ../packages/plugin-core/src/test/suite-integ/index.ts (Private)

Modify the files variable

glob(pattern, { cwd: testsRoot }, (err, files) => {
  if (err) {
    return e(err);
  }
  // Make changes here
  files = ["ChangeWorkspace.test.js", "CodeActionProvider.test.js"];
  ...

Run from CLI

Running a single test

  1. Open a test inside 'src/test/suite-integ/' from plugin-core
  2. Run the build task Extension Integ Tests - File

Manual Testing

To manually test new changes, launch an instance of the test workspace.

Summary

The test workspace is included with the Dendron repo and is used for manual testing of new features

Quickstart

  1. Run the development version of Dendron by following the steps here
  2. Run > Change Workspace and change to the path of the test workspace
  • this should be $DENDRON_REPO_ROOT/test-workspace
  • NOTE: when you initially change to this workspace, you'll need to restart the debugger or VSCode will complain about multiple instances of Dendron. The next time you start the debugger, VSCode will remember the last workspace you've used so you can skip this step

Pre-requisites

Setup

  1. Launch engine server
    cd $DENDRON_REPO_ROOT/test-workspace && ./scripts/dev.sh
    
  2. Run Change Workspace (Private) and change into $DENDRON_REPO_ROOT/test-workspace

Cook

Use with regular workspace

Comment the following lines in test-workspace/dendron.yml to launch it without the CLI engine server

dev:
  # nextServerUrl: 'http://localhost:3000'
  # engineServerPort: 3005

Testing Dendron in non-note files

There's a folder named other-files in the test workspace which contains files that are not notes. This includes markdown files that are not Dendron notes, and code files. If you are working on a feature that may affect non-note files, make sure to test it with these files.

Cook

subscribeToEngineStateChange

Use this method to subscribe to engine state event changes when working with tests in plugin-core. This can be used in situations where the engine state changes asynchorously from test logic (such as from vscode event callbacks)

Upating the cursor position

Creating notes after engine is started

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

check if you are running in test mode

getStage() === test


Children
  1. Cook
  2. Dendron Extension Testing Internals
  3. Remote

Backlinks