coc-test is an integration test runner for coc.nvim extensions. It bundles
JavaScript and TypeScript tests, starts Vim or Neovim with coc.nvim and the
current extension activated, and runs the tests with Node.js's built-in test
runner.
Each test file runs in an isolated child process with its own editor, coc.nvim instance, and data directory. The runner reports per-file progress in real time, cleans up all child resources when a test finishes, and prints failure details with source-mapped stack traces.
- Node.js 22.15 or newer
- Vim or Neovim
- A coc.nvim extension with a valid
mainentry inpackage.json
Install coc-test as a development dependency in the extension root:
npm install --save-dev coc-testRun the interactive initializer:
npx coc-test --initThe initializer:
- checks whether
vimandnvimare available; - optionally adds the
coc-testconfiguration topackage.json; - creates a starter test in
test/using a filename you choose; - optionally adds a
testscript when one does not already exist; and - optionally creates a GitHub Actions workflow, with a filename you choose, for both Vim and Neovim.
Existing test files, scripts, and workflows are not overwritten without confirmation.
Run all TypeScript tests under test/:
npx coc-test 'test/**/*.test.ts'Neovim is used by default. Select Vim explicitly with --vim:
npx coc-test --vim 'test/**/*.test.ts'Tests use the APIs from node:test. Imports from coc.nvim refer to the
coc.nvim instance attached to that test's editor. Importing the extension's
main entry returns the exports of the activated extension.
import assert from 'node:assert/strict'
import { beforeEach, describe, it } from 'node:test'
import { commands, workspace } from 'coc.nvim'
import extension from '../lib/index.js'
beforeEach(async () => {
await workspace.nvim.command('enew!')
})
describe('extension', () => {
it('loads the activated extension', () => {
assert.ok(extension)
assert.equal(typeof commands.executeCommand, 'function')
})
it('communicates with the editor', async () => {
assert.equal(await workspace.nvim.eval('1 + 1'), 2)
})
})When the extension is built before testing, the extension import path must
resolve to the same file as the package's main field.
Extensions can configure entryFile (see [Configuration] (#configuration));
their source modules can then be imported directly with relative paths.
Configure the runner with a coc-test object in the extension's
package.json:
{
"coc-test": {
"entryFile": "src/index.ts",
"user-settings": {
"suggest.noselect": true
},
"externals": ["vscode-languageserver"],
"target": "commonjs"
}
}| Property | Default | Description |
|---|---|---|
user-settings |
{} |
coc.nvim settings written to the isolated test configuration. |
entryFile |
— | Source entry to bundle and activate instead of the package main file. |
externals |
[] |
esbuild external specifiers to leave out of the entryFile bundle. |
target |
"commonjs" |
Bundle format: "commonjs" or "esm". |
Without entryFile, coc-test activates the extension's main file. With
entryFile, it builds the source in memory and activates that bundle; no
bundle file or generated package manifest is written to the project.
Tests can import source modules with normal relative paths:
import { getStore } from '../src/index.ts'
import { store } from '../src/store.ts'These imports resolve to the same module instances the plugin itself runs, so state is shared between the plugin and the tests instead of loading independent copies.
Dependencies imported by entryFile, including packages in node_modules,
are bundled by default. Add a package to externals when it must be resolved
at runtime instead. Values are passed to esbuild's external option; a package
name also covers its subpaths.
coc.nvim is supplied by the test runtime rather than bundled. Node.js built-in
modules also remain external.
target chooses both the esbuild output and how coc.nvim loads the in-memory
source:
"commonjs"is the default and works with Node.js 22.15 or newer."esm"uses coc.nvim's ESM source loader and requires Node.js 24 or newer.
Pass -w or --watch to keep the runner active:
npx coc-test --watch 'test/**/*.test.ts'When a test file or one of its imported dependencies changes, only the affected test file is rerun. When the extension entry or one of its local dependencies changes, all tests are rerun.
If a change occurs while tests are running, the affected run is cancelled first. Its Node.js child process, editor, and coc.nvim environment are fully released before the replacement run starts.
By default, coc-test downloads the latest coc.nvim release and caches it by
version, so repeated runs reuse the extracted checkout.
During coc.nvim or runner development, use an existing build to avoid downloading:
npx coc-test --coc-path /path/to/coc.nvim 'test/**/*.test.ts'You can also set COC_TEST_COC_PATH:
COC_TEST_COC_PATH=/path/to/coc.nvim npm test--nvim Run tests on Neovim (default)
--vim Run tests on Vim
-w, --watch Watch files and rerun affected tests
--test-name-pattern <pattern> Run tests matching a regular expression (must be valid)
--coc-path <directory> Use an existing coc.nvim build
-u, --use <version> Use a specific coc.nvim release tag
-d, --download Force a fresh coc.nvim download
--force-exit Force the main process to exit after the run
Long options also accept --option=value, for example
--test-name-pattern=^foo$ or --coc-path=/path/to/coc.nvim.
Run npx coc-test --help for the complete command reference.
VIM_COMMANDoverrides thevimexecutable.NVIM_COMMANDoverrides thenvimexecutable.COC_TEST_COC_PATHprovides a local coc.nvim directory instead of--coc-path.NO_COLORdisables ANSI colors during--init.FORCE_COLORforces ANSI colors during--init.
The unit tests do not download coc.nvim:
npm testRun the integration fixture against an existing coc.nvim checkout or build:
npm run test:nvim -- --coc-path /path/to/coc.nvim
npm run test:vim -- --coc-path /path/to/coc.nvim
# Alternatively, provide the path through the environment.
COC_TEST_COC_PATH=/path/to/coc.nvim npm run test:nvimMIT