Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test_runner throws "Promise resolution is still pending but the event loop has already resolved" in Node 20.12.0 #52304

Closed
ggrossetie opened this issue Apr 1, 2024 · 4 comments · Fixed by #52322
Labels
test_runner Issues and PRs related to the test runner subsystem.

Comments

@ggrossetie
Copy link

ggrossetie commented Apr 1, 2024

Version

v20.12.0

Platform

Linux workstation 5.19.0-46-generic #47~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Wed Jun 21 15:35:31 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

Subsystem

test_runner

What steps will reproduce the bug?

Create a file named runner.js with the following content:

import { describe, it, run } from 'node:test'
import ospath from 'node:path'
import { fileURLToPath } from 'node:url'
import assert from 'node:assert'

const __filename = fileURLToPath(import.meta.url)
const __dirname = ospath.dirname(__filename)

const runTests = async () => {
  const testFile = ospath.join(__dirname, 'test.js')
  const testsStream = run({
    files: [testFile],
  })
  for await (const event of testsStream) {
    console.log(event)
  }
  return {
    total: 0,
  }
}

describe('test', () => {
  it('should run node:run', async () => {
    const r = await runTests()
    assert.equal(r.total, 0)
  })
})

Create a file named test.js with the following content:

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

describe('something', () => {
  it('noop', () => {
  })
})

Run:

$ node --test runner.js 
Output
{
  type: 'test:plan',
  data: [Object: null prototype] { nesting: 0, count: 0 }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'tests 0' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'suites 0' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'pass 0' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'fail 0' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'cancelled 0' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'skipped 0' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'todo 0' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] {
    nesting: 0,
    message: 'duration_ms 2.349107'
  }
}
▶ test
  ✖ should run node:run (2.247107ms)
    'Promise resolution is still pending but the event loop has already resolved'

▶ test (2.410128ms)

'Promise resolution is still pending but the event loop has already resolved'

ℹ tests 1
ℹ suites 1
ℹ pass 0
ℹ fail 0
ℹ cancelled 1
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 30.92155

✖ failing tests:

test at file:/home/guillaume/Workspace/opensource/asciidoc-tck/runner.js:23:3
✖ should run node:run (2.247107ms)
'Promise resolution is still pending but the event loop has already resolved'

test at file:/home/guillaume/Workspace/opensource/asciidoc-tck/runner.js:22:1
✖ test (2.410128ms)
'Promise resolution is still pending but the event loop has already resolved'

How often does it reproduce? Is there a required condition?

Always.
No, as long as you are using Node 20.12.0.

What is the expected behavior? Why is that the expected behavior?

The code should not throw "Promise resolution is still pending but the event loop has already resolved".
With Node 20.11.0, here's the result:

$ node -v
v20.11.0
$ node --test runner.js 
Output
{
  type: 'test:enqueue',
  data: [Object: null prototype] {
    nesting: 0,
    name: '/home/guillaume/Workspace/opensource/asciidoc-tck/test.js',
    line: 1,
    column: 1,
    file: '/home/guillaume/Workspace/opensource/asciidoc-tck/test.js'
  }
}
{
  type: 'test:dequeue',
  data: [Object: null prototype] {
    nesting: 0,
    name: '/home/guillaume/Workspace/opensource/asciidoc-tck/test.js',
    line: 1,
    column: 1,
    file: '/home/guillaume/Workspace/opensource/asciidoc-tck/test.js'
  }
}
{
  type: 'test:enqueue',
  data: {
    nesting: 0,
    name: 'something',
    line: 3,
    column: 1,
    file: 'file:///home/guillaume/Workspace/opensource/asciidoc-tck/test.js'
  }
}
{
  type: 'test:dequeue',
  data: {
    nesting: 0,
    name: 'something',
    line: 3,
    column: 1,
    file: 'file:///home/guillaume/Workspace/opensource/asciidoc-tck/test.js'
  }
}
{
  type: 'test:enqueue',
  data: {
    nesting: 1,
    name: 'noop',
    line: 4,
    column: 3,
    file: 'file:///home/guillaume/Workspace/opensource/asciidoc-tck/test.js'
  }
}
{
  type: 'test:dequeue',
  data: {
    nesting: 1,
    name: 'noop',
    line: 4,
    column: 3,
    file: 'file:///home/guillaume/Workspace/opensource/asciidoc-tck/test.js'
  }
}
{
  type: 'test:start',
  data: {
    nesting: 0,
    name: 'something',
    line: 3,
    column: 1,
    file: 'file:///home/guillaume/Workspace/opensource/asciidoc-tck/test.js'
  }
}
{
  type: 'test:start',
  data: {
    nesting: 1,
    name: 'noop',
    line: 4,
    column: 3,
    file: 'file:///home/guillaume/Workspace/opensource/asciidoc-tck/test.js'
  }
}
{
  type: 'test:pass',
  data: {
    name: 'noop',
    nesting: 1,
    testNumber: 1,
    details: { duration_ms: 0.121968 },
    line: 4,
    column: 3,
    file: 'file:///home/guillaume/Workspace/opensource/asciidoc-tck/test.js'
  }
}
{
  type: 'test:plan',
  data: {
    nesting: 1,
    count: 1,
    line: 3,
    column: 1,
    file: 'file:///home/guillaume/Workspace/opensource/asciidoc-tck/test.js'
  }
}
{
  type: 'test:pass',
  data: {
    name: 'something',
    nesting: 0,
    testNumber: 1,
    details: { duration_ms: 0.926078, type: 'suite' },
    line: 3,
    column: 1,
    file: 'file:///home/guillaume/Workspace/opensource/asciidoc-tck/test.js'
  }
}
{
  type: 'test:plan',
  data: [Object: null prototype] { nesting: 0, count: 1 }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'tests 1' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'suites 1' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'pass 1' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'fail 0' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'cancelled 0' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'skipped 0' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] { nesting: 0, message: 'todo 0' }
}
{
  type: 'test:diagnostic',
  data: [Object: null prototype] {
    nesting: 0,
    message: 'duration_ms 26.914051'
  }
}
▶ test
  ✔ should run node:run (27.837554ms)
▶ test (28.374876ms)

ℹ tests 1
ℹ suites 1
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 54.926286

What do you see instead?

An exception: "Promise resolution is still pending but the event loop has already resolved".

Additional information

When using Node 20.11.0 or lower it's working.
If I remove (or comment) the following code then it works with Node 20.12.0:

for await (const event of testsStream) {
  console.log(event)
}

I'm using node:test to create a Technology Compatibility Kit (TCK) and I also use node:test to test my TCK.

@atlowChemi atlowChemi added the test_runner Issues and PRs related to the test runner subsystem. label Apr 2, 2024
@MoLow
Copy link
Member

MoLow commented Apr 2, 2024

you shouldn't be running runner.js with the --test flag. the run() function is not designed to work inside test files.

@ggrossetie
Copy link
Author

Is there a solution that could allow me to test a tool that relies on the test_runner module? In my opinion, being able to use the test_runner module as a library is a great feature.

@MoLow
Copy link
Member

MoLow commented Apr 3, 2024

yes, you can run node runner.js directly, instead of node --test runner.js. adding --test is redundant in your usecase

@ggrossetie
Copy link
Author

ggrossetie commented Apr 3, 2024

@MoLow Thanks, it's working!

For reference, I've introduced an "index.js" file since node without the --test argument cannot execute multiple files from the CLI (i.e., node tests/**.js -> node tests/index.js).

Also, thanks for improving the DX with #52322

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
test_runner Issues and PRs related to the test runner subsystem.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants