A playwright adapter for neotest.
Written in typescript and transpiled to Lua using tstl.
- 🎭 Discover, run, and parse the output of playwright tests
- ⌨️ Quick launch test attachments ( 🕵️ trace, 📼 video)
- 💅 Project selection + persistence
- ⚙️ On-the-fly presets
neotest-playwright-demo.mp4
Using lazyvim:
{
'nvim-neotest/neotest',
dependencies = {
'thenbe/neotest-playwright',
dependencies = 'nvim-telescope/telescope.nvim',
},
config = function()
require('neotest').setup({
adapters = {
require('neotest-playwright').adapter({
options = {
persist_project_selection = true,
enable_dynamic_test_discovery = true,
},
}),
},
})
end,
}
All configuration options are optional. Default values are shown below.
require('neotest-playwright').adapter({
options = {
persist_project_selection = false,
enable_dynamic_test_discovery = false,
preset = 'none', -- "none" | "headed" | "debug"
get_playwright_binary = function()
return vim.loop.cwd() .. '/node_modules/.bin/playwright'
end,
get_playwright_config = function()
return vim.loop.cwd() .. '/playwright.config.ts'
end,
-- Controls the location of the spawned test process. Has no affect on
-- neither the location of the binary nor the location of the playwright
-- config file.
get_cwd = function()
return vim.loop.cwd()
end,
env = {},
-- Extra args to always passed to playwright. These are merged with any
-- extra_args passed to neotest's run command.
extra_args = {},
-- Filter directories when searching for test files. Useful in large
-- projects (see performance notes).
filter_dir = function(name, rel_path, root)
return name ~= 'node_modules'
end,
-- Custom criteria for a file path to be a test file. Useful in large
-- projects or projects with peculiar tests folder structure. IMPORTANT:
-- When setting this option, make sure to be as strict as possible. For
-- example, the pattern should not return true for jpg files that may end up
-- in your test directory.
is_test_file = function(file_path)
-- By default, only returns true if a file contains one of several file
-- extension patterns. See default implementation here: https://github.com/thenbe/neotest-playwright/blob/53c7c9ad8724a6ee7d708c1224f9ea25fa071b61/src/discover.ts#L25-L47
local result = file_path:find('%.test%.[tj]sx?$') ~= nil or file_path:find('%.spec%.[tj]sx?$') ~= nil
-- Alternative example: Match only files that end in `test.ts`
local result = file_path:find('%.test%.ts$') ~= nil
-- Alternative example: Match only files that end in `test.ts`, but only if it has ancestor directory `e2e/tests`
local result = file_path:find('e2e/tests/.*%.test%.ts$') ~= nil
return result
end,
experimental = {
telescope = {
-- If true, a telescope picker will be used for `:NeotestPlaywrightProject`.
-- Otherwise, `vim.ui.select` is used.
-- In normal mode, `<Tab>` toggles the project under the cursor.
-- `<CR>` (enter key) applies the selection.
enabled = false,
opts = {},
},
},
},
})
neotest-playwright
allows you to conveniently toggle your playwright Projects on and off. To activate (or deactivate) a project, use the :NeotestPlaywrightProject
command. neotest-playwright
will only include the projects you've activated in any subsequent playwright commands (using the --project
flag). Your selection will persist until you either change it with :NeotestPlaywrightProject
, or restart neovim.
If you wish, you can choose to persist your project selection across neovim sessions by setting persist_project_selection
to true (see example). Selection data is keyed by the project's root directory, meaning you can persist multiple distinct selections across different projects (or git worktrees).
Presets can help you debug your tests on the fly. A preset is just a group of command line flags that come in handy in common scenarios.
To select a preset, use the
:NeotestPlaywrightPreset
command. Once a preset is selected, it remains active until you either select another preset, clear it by selecting thenone
preset, or restart neovim.
Applies the following flags:
--headed --retries 0 --timeout 0 --workers 1 --max-failures 0
Runs tests in headed mode.
💡 Tip: Use with
await page.pause()
to open the playwright inspector and debug your locators.
Applies the following flags:
--debug
Playwright uses the --debug
flag as a shortcut for multiple options. See here for more information.
Does not apply any flags. Your tests will run as defined in your playwright.config.ts
file.
neotest-playwright
can make use of the playwright
cli to unlock extra features. Most importantly, the playwright
cli provides information about which tests belongs to which project. neotest-playwright
will parse this information to display, run, and report the results of tests on a per-project basis.
To enable this, set enable_dynamic_test_discovery
to true.
This feature works by calling playwright test --list --reporter=json
. While this is a relatively fast operation, it does add some overhead. Therefore, neotest-playwright
only calls this feature once (when the adapter is first initialized). From then on, neotest-playwright
continues to rely on treesitter to track your tests and enhance them with the data previously resolved by the playwright
cli. There are times, however, where we want to refresh this data. To remedy this: neotest-playwright
exposes a command :NeotestPlaywrightRefresh
. This comes in handy in the following scenarios:
- Adding a new test
- Renaming a test
- Changing the project(s) configuration in your
playwright.config.ts
file
Displays the attachments for the test under the cursor. Upon selection, the attachment is launched.
neotest-playwright-test-attachment-2.mp4
Requires
enable_dynamic_test_discovery = true
.
- Include the consumer in your
neotest
setup:
require('neotest').setup({
consumers = {
-- add to your list of consumers
playwright = require('neotest-playwright.consumers').consumers,
},
})
- Add keybinding:
{
'thenbe/neotest-playwright',
keys = {
{
'<leader>ta',
function()
require('neotest').playwright.attachment()
end,
desc = 'Launch test attachment',
},
},
}
Use filter_dir
option to limit directories to be searched for tests.
---Filter directories when searching for test files
---@async
---@param name string Name of directory
---@param rel_path string Path to directory, relative to root
---@param root string Root directory of project
---@return boolean
filter_dir = function(name, rel_path, root)
local full_path = root .. '/' .. rel_path
if root:match('projects/my-large-monorepo') then
if full_path:match('^packages/site/test') then
return true
else
return false
end
else
return name ~= 'node_modules'
end
end
testDir
should be defined in playwright.config.ts
.
- Run
:NeotestPlaywrightProject
. Once you apply your selection, any old phantom project names will be cleared from the state file. - You may also delete the state file manually. Find the state file's path by running
:lua =vim.fn.stdpath('data') .. '/neotest-playwright.json'
.