connor4312 / nodejs-testing

VS Code integration for node:test native tests
MIT License
50 stars 7 forks source link

TypeScript tests appear in Test Explorer, but are not run. #33

Open alexc155 opened 7 months ago

alexc155 commented 7 months ago

Using the config

"nodejs-testing.extensions": [
    {
      "extensions": ["mjs", "cjs", "js"],
      "parameters": []
    },
    {
      "extensions": ["mts", "cts", "ts"],
      "parameters": ["--import", "tsx"]
    }
  ]

I can make TypeScript tests appear in the Test Explorer 😀

However, when I run all tests the Test Results pane shows 0/0 tests passed and the Test Explorer fails to show any green or red ticks.

I wonder if this is because node is being used as the test runner and the glob for detecting tests doesn't include the .ts extension?

This is similar to running npm --test --import tsx

Changing the test runner to tsx probably wouldn't help as it uses the same glob patterns to discover tests.

I have tried adding the glob to the parameters in the config:

"nodejs-testing.extensions": [
    {
      "extensions": ["mjs", "cjs", "js"],
      "parameters": []
    },
    {
      "extensions": ["mts", "cts", "ts"],
      "parameters": ["--import", "tsx", "./test/**/*.test.ts"]
    }
  ]

but then all the tests fail with Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'foo/test/**/*.test.ts' imported from foo/

I thought perhaps the filePatterns property in settings might be used for this, but it appears to only control the discovery of tests rather than their execution.

Please could you tell me what I'm doing wrong, or provide a setting I can use to pass a glob pattern of test files to the runner?

Thanks!

connor4312 commented 7 months ago

However, when I run all tests the Test Results pane shows 0/0 tests passed and the Test Explorer fails to show any green or red ticks.

Please enable the nodejs-testing.verbose user setting and share the output from the Test Results when you do this. Thanks!

alexc155 commented 7 months ago

Hi,

I've discovered what's wrong: In my tests I like to include the path of the test file so that I can easily jump to it from the test output. My tests look like this:

import { describe, it } from 'node:test';
import assert from 'node:assert';

describe(`${__filename} - When doing a thing`, () => {
  it('it returns true', () => {
    // ARRANGE
    const expected: boolean = true;

    // ACT
    const actual: boolean = true;

    // ASSERT
    assert(expected === actual);
  });
});

It's the ${__filename} that's causing the problem. Without it, your extension runs the test fine. With the variable I get the following in the verbose output:

worker1> starting /my/path/test/thing.test.ts
worker1> node --import" "tsx" "--test-reporter" "file:///Users/me/.vscode/extensions/connor4312.nodejs-testing-1.5.0/out/runner-loader.js" "/my/path/test/thing.test.ts
worker1> {"type":"test:enqueue","data":{"nesting":0,"name":"/my/path/test/thing.test.ts - When doing a thing","line":2,"column":875,"file":"/my/path/test/thing.test.ts"}}
worker1> {"type":"test:dequeue","data":{"nesting":0,"name":"/my/path/test/thing.test.ts - When doing a thing","line":2,"column":875,"file":"/my/path/test/thing.test.ts"}}
worker1> {"type":"test:enqueue","data":{"nesting":1,"name":"it returns true","line":2,"column":941,"file":"/my/path/test/thing.test.ts"}}
worker1> {"type":"test:dequeue","data":{"nesting":1,"name":"it returns true","line":2,"column":941,"file":"/my/path/test/thing.test.ts"}}
worker1> {"type":"test:start","data":{"nesting":0,"name":"/my/path/test/thing.test.ts - When doing a thing","line":2,"column":875,"file":"/my/path/test/thing.test.ts"}}
worker1> {"type":"test:start","data":{"nesting":1,"name":"it returns true","line":2,"column":941,"file":"/my/path/test/thing.test.ts"}}
worker1> {"type":"test:pass","data":{"name":"it returns true","nesting":1,"testNumber":1,"details":{"duration_ms":0.147959},"line":2,"column":941,"file":"/my/path/test/thing.test.ts"}}
worker1> {"type":"test:plan","data":{"nesting":1,"count":1,"line":2,"column":875,"file":"/my/path/test/thing.test.ts"}}
worker1> {"type":"test:pass","data":{"name":"/my/path/test/thing.test.ts - When doing a thing","nesting":0,"testNumber":1,"details":{"duration_ms":0.774542,"type":"suite"},"line":2,"column":875,"file":"/my/path/test/thing.test.ts"}}
worker1> {"type":"test:plan","data":{"nesting":0,"count":1}}
worker1> {"type":"test:diagnostic","data":{"nesting":0,"message":"tests 1"}}
worker1> {"type":"test:diagnostic","data":{"nesting":0,"message":"suites 1"}}
worker1> {"type":"test:diagnostic","data":{"nesting":0,"message":"pass 1"}}
worker1> {"type":"test:diagnostic","data":{"nesting":0,"message":"fail 0"}}
worker1> {"type":"test:diagnostic","data":{"nesting":0,"message":"cancelled 0"}}
worker1> {"type":"test:diagnostic","data":{"nesting":0,"message":"skipped 0"}}
worker1> {"type":"test:diagnostic","data":{"nesting":0,"message":"todo 0"}}
worker1> {"type":"test:diagnostic","data":{"nesting":0,"message":"duration_ms 56.545125"}}
worker1> stdout closed

0/0 tests passed

It looks like it's running the test, but not gathering the result?

alexc155 commented 7 months ago

Once I'd got the tests working, I found a separate thing. Any code that referenced a path to a file (even using resolve from node:path) was failing because the file could not be found. This appears to be because the tests are executing from the directory they are in, rather than from the root of the project.

For example, if I have:

test/thing.test.ts =>

import { describe, it } from 'node:test';
import assert from 'node:assert';
import { existsSync } from 'node:fs';
import { resolve } from 'node:path';

describe(`When doing a thing`, () => {
  it('it returns true', () => {
    // ARRANGE
    console.log(resolve('test/thing.test.ts'));
    const expected: boolean = existsSync(resolve('test/thing.test.ts'));

    // ACT
    const actual: boolean = true;

    // ASSERT
    assert(expected === actual);
  });
});

then the test runs fine from the command line. But the test fails from your extension and shows the path as

/test/test/thing.test.ts

I have managed to fix this by using a package called app-root-path instead of resolve:

import { describe, it } from 'node:test';
import assert from 'node:assert';
import { existsSync } from 'node:fs';
import appRootPath from 'app-root-path';

describe(`When doing a thing`, () => {
  it('it returns true', () => {
    // ARRANGE
    console.log(`${appRootPath}/test/thing.test.ts`);
    const expected: boolean = existsSync(`${appRootPath}/test/thing.test.ts`);

    // ACT
    const actual: boolean = true;

    // ASSERT
    assert(expected === actual);
  });
});

I wonder whether this is because you are setting the current working directory here as the directory each test file is in?

ssalbdivad commented 7 months ago

Thanks for your help @alexc155!

I'd love to migrate ArkType to node's test runner but I'm still encountering lots of problems with TS discovery and imports. Will be keeping an eye on this- awesome to see the early support for test coverage metrics ❤️

connor4312 commented 7 months ago

It's the ${__filename} that's causing the problem. Without it, your extension runs the test fine. With the variable I get the following in the verbose output:

Ah, yes. This is expected. At the moment tests are discovered purely by static code analysis, which does not make any attempt to evaluate template literals with replacements. During runs, any additional tests not found in the test tree are ignored. We could add support for that, however, such that tests emitted during runs are added to the tree if they aren't already there.

This appears to be because the tests are executing from the directory they are in, rather than from the root of the project.

This probably duplicates at. At the moment I would use ${__dirname} to ensure that the paths are absoltue.

hyperactivepuss commented 5 months ago

get "the same" result but with error and ts-node/esm!

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '${workspaceFolder}' imported from /home/user/Documents

{
 "extensions": [
        "ts"
      ],
      "filePatterns": [
        "**/*.test.ts"
      ],
      "parameters": [
        "--experimental-specifier-resolution=node",
        "--import",
        "${workspaceFolder}/utils/test-runner/register.js"
      ]
    }

node --experimental-specifier-resolution=node" "--import" "${workspaceFolder}/utils/test-runner/register.js" .........

every parameter in cli command wrapped with double quote but right after the "node" it missed. dunno if it does matter works if I replace ${workspaceFolder} with ../../../