diff --git a/lua/neotest-jest/init.lua b/lua/neotest-jest/init.lua index b5e84ae..b049198 100644 --- a/lua/neotest-jest/init.lua +++ b/lua/neotest-jest/init.lua @@ -67,9 +67,37 @@ function adapter.build_position(file_path, source, captured_nodes) end ---@type TSNode - local test_name_node = captured_nodes[match_type .. ".name"] - local name = vim.treesitter.get_node_text(test_name_node, source) + local node = captured_nodes[match_type .. ".name"] + local name = vim.treesitter.get_node_text(node, source) local definition = captured_nodes[match_type .. ".definition"] + local type = node:type() + local nonStringNode = false + + if type == "string" then + -- If the node is a string then strip the quotes from the name by getting + -- it's first named child (string_fragment). This works for single- and + -- double-quotes and is necessary since we match anything in the queries + -- used in discover_positions + local content = node:named_child(0) + + if content then + name = vim.treesitter.get_node_text(content, source) + end + elseif type == "template_string" then + -- If the node is a template string then concatenate its named children + -- which is essentially the inner part of the backticks thus stripping + -- backticks. This is necessary since we match anything in the queries used + -- in discover_positions + local new_name = {} + + for _, named_child in ipairs(node:named_children()) do + table.insert(new_name, vim.treesitter.get_node_text(named_child, source)) + end + + name = table.concat(new_name, "") + else + nonStringNode = true + end return { type = match_type, @@ -77,8 +105,8 @@ function adapter.build_position(file_path, source, captured_nodes) name = name, range = { definition:range() }, -- Record the position of the line where the string name occurs - test_name_range = match_type == "test" and { test_name_node:range() } or nil, - is_parameterized = captured_nodes["each_property"] and true or false, + test_name_range = match_type == "test" and { node:range() } or nil, + is_parameterized = (captured_nodes["each_property"] or nonStringNode) and true or false, } end @@ -100,28 +128,19 @@ function adapter.discover_positions(path) ; Matches: `describe('context', () => {})` ((call_expression function: (identifier) @func_name (#eq? @func_name "describe") - arguments: (arguments ([ - (string (string_fragment) @namespace.name) - (template_string (_) @namespace.name) - ]) (arrow_function)) + arguments: (arguments ((_) @namespace.name) (arrow_function)) )) @namespace.definition ; Matches: `describe('context', function() {})` ((call_expression function: (identifier) @func_name (#eq? @func_name "describe") - arguments: (arguments ([ - (string (string_fragment) @namespace.name) - (template_string (_) @namespace.name) - ]) (function_expression)) + arguments: (arguments ((_) @namespace.name) (function_expression)) )) @namespace.definition ; Matches: `describe('context', wrapper())` ((call_expression function: (identifier) @func_name (#eq? @func_name "describe") - arguments: (arguments ([ - (string (string_fragment) @namespace.name) - (template_string (_) @namespace.name) - ]) (call_expression)) + arguments: (arguments ((_) @namespace.name) (call_expression)) )) @namespace.definition ; Matches: `describe.only('context', () => {})` @@ -129,10 +148,7 @@ function adapter.discover_positions(path) function: (member_expression object: (identifier) @func_name (#eq? @func_name "describe") ) - arguments: (arguments ([ - (string (string_fragment) @namespace.name) - (template_string (_) @namespace.name) - ]) (arrow_function)) + arguments: (arguments ((_) @namespace.name) (arrow_function)) )) @namespace.definition ; Matches: `describe.only('context', function() {})` @@ -140,10 +156,7 @@ function adapter.discover_positions(path) function: (member_expression object: (identifier) @func_name (#eq? @func_name "describe") ) - arguments: (arguments ([ - (string (string_fragment) @namespace.name) - (template_string (_) @namespace.name) - ]) (function_expression)) + arguments: (arguments ((_) @namespace.name) (function_expression)) )) @namespace.definition ; Matches: `describe.only('context', wrapper())` @@ -151,10 +164,7 @@ function adapter.discover_positions(path) function: (member_expression object: (identifier) @func_name (#eq? @func_name "describe") ) - arguments: (arguments ([ - (string (string_fragment) @namespace.name) - (template_string (_) @namespace.name) - ]) (call_expression)) + arguments: (arguments ((_) @namespace.name) (call_expression)) )) @namespace.definition ; Matches: `describe.each(['data'])('context', () => {})` @@ -165,10 +175,7 @@ function adapter.discover_positions(path) property: (property_identifier) @each_property (#eq? @each_property "each") ) ) - arguments: (arguments ([ - (string (string_fragment) @namespace.name) - (template_string (_) @namespace.name) - ]) (arrow_function)) + arguments: (arguments ((_) @namespace.name) (arrow_function)) )) @namespace.definition ; Matches: `describe.each(['data'])('context', function() {})` @@ -179,10 +186,7 @@ function adapter.discover_positions(path) property: (property_identifier) @each_property (#eq? @each_property "each") ) ) - arguments: (arguments ([ - (string (string_fragment) @namespace.name) - (template_string (_) @namespace.name) - ]) (function_expression)) + arguments: (arguments ((_) @namespace.name) (function_expression)) )) @namespace.definition ; Matches: `describe.each(['data'])('context', wrapper())` @@ -192,10 +196,7 @@ function adapter.discover_positions(path) object: (identifier) @func_name (#eq? @func_name "describe") ) ) - arguments: (arguments ([ - (string (string_fragment) @namespace.name) - (template_string (_) @namespace.name) - ]) (call_expression)) + arguments: (arguments ((_) @namespace.name) (call_expression)) )) @namespace.definition ; ######### @@ -205,28 +206,19 @@ function adapter.discover_positions(path) ; Matches: `it('test', () => {}) / test('test', () => {})` ((call_expression function: (identifier) @func_name (#any-of? @func_name "it" "test") - arguments: (arguments ([ - (string (string_fragment) @test.name) - (template_string (_) @test.name) - ]) (arrow_function)) + arguments: (arguments ((_) @test.name) (arrow_function)) )) @test.definition ; Matches: `it('test', function() {}) / test('test', function() {})` ((call_expression function: (identifier) @func_name (#any-of? @func_name "it" "test") - arguments: (arguments ([ - (string (string_fragment) @test.name) - (template_string (_) @test.name) - ]) (function_expression)) + arguments: (arguments ((_) @test.name) (function_expression)) )) @test.definition ; Matches: `it('test', wrapper()) / test('test', wrapper())` ((call_expression function: (identifier) @func_name (#any-of? @func_name "it" "test") - arguments: (arguments ([ - (string (string_fragment) @test.name) - (template_string (_) @test.name) - ]) (call_expression)) + arguments: (arguments ((_) @test.name) (call_expression)) )) @test.definition ; Matches: `test.only('test', () => {}) / it.only('test', () => {})` @@ -234,10 +226,7 @@ function adapter.discover_positions(path) function: (member_expression object: (identifier) @func_name (#any-of? @func_name "test" "it") ) - arguments: (arguments ([ - (string (string_fragment) @test.name) - (template_string (_) @test.name) - ]) (arrow_function)) + arguments: (arguments ((_) @test.name) (arrow_function)) )) @test.definition ; Matches: `test.only('test', function() {}) / it.only('test', function() {})` @@ -245,10 +234,7 @@ function adapter.discover_positions(path) function: (member_expression object: (identifier) @func_name (#any-of? @func_name "test" "it") ) - arguments: (arguments ([ - (string (string_fragment) @test.name) - (template_string (_) @test.name) - ]) (function_expression)) + arguments: (arguments ((_) @test.name) (function_expression)) )) @test.definition ; Matches: `test.only('test', wrapper()) / it.only('test', wrapper())` @@ -256,10 +242,7 @@ function adapter.discover_positions(path) function: (member_expression object: (identifier) @func_name (#any-of? @func_name "test" "it") ) - arguments: (arguments ([ - (string (string_fragment) @test.name) - (template_string (_) @test.name) - ]) (call_expression)) + arguments: (arguments ((_) @test.name) (call_expression)) )) @test.definition ; Matches: `test.each(['data'])('test', () => {}) / it.each(['data'])('test', () => {})` @@ -270,10 +253,7 @@ function adapter.discover_positions(path) property: (property_identifier) @each_property (#eq? @each_property "each") ) ) - arguments: (arguments ([ - (string (string_fragment) @test.name) - (template_string (_) @test.name) - ]) (arrow_function)) + arguments: (arguments ((_) @test.name) (arrow_function)) )) @test.definition ; Matches: `test.each(['data'])('test', function() {}) / it.each(['data'])('test', function() {})` @@ -284,10 +264,7 @@ function adapter.discover_positions(path) property: (property_identifier) @each_property (#eq? @each_property "each") ) ) - arguments: (arguments ([ - (string (string_fragment) @test.name) - (template_string (_) @test.name) - ]) (function_expression)) + arguments: (arguments ((_) @test.name) (function_expression)) )) @test.definition ; Matches: `test.each(['data'])('test', wrapper()) / it.each(['data'])('test', wrapper())` @@ -298,10 +275,7 @@ function adapter.discover_positions(path) property: (property_identifier) @each_property (#eq? @each_property "each") ) ) - arguments: (arguments ([ - (string (string_fragment) @test.name) - (template_string (_) @test.name) - ]) (call_expression)) + arguments: (arguments ((_) @test.name) (call_expression)) )) @test.definition ]] @@ -447,9 +421,12 @@ function adapter.build_spec(args) local testNamePattern = ".*" if pos.type == types.PositionType.test or pos.type == types.PositionType.namespace then - -- pos.id in form "path/to/file::Describe text::test text" - local testName = pos.id:sub(pos.id:find("::") + 2) - testName, _ = testName:gsub("::", " ") + -- Check if the position is a parametric (runtime) test by seeing if there is a corresponding + -- source-level test + local sourceLevelTest = parameterized_tests.getParametricTestToSourceLevelTest(pos) + local testName = sourceLevelTest or pos.id + + testName, _ = testName:sub(pos.id:find("::") + 2):gsub("::", " ") testNamePattern = util.escapeTestPattern(testName) -- If the position or any of its enclosing blocks are parameterized, replace any diff --git a/lua/neotest-jest/parameterized-tests.lua b/lua/neotest-jest/parameterized-tests.lua index e1485ba..3e9b592 100644 --- a/lua/neotest-jest/parameterized-tests.lua +++ b/lua/neotest-jest/parameterized-tests.lua @@ -1,15 +1,15 @@ local lib = require("neotest.lib") local jest_util = require("neotest-jest.jest-util") local types = require("neotest.types") -local logger = require("neotest.logging") local M = {} ---@class neotest-jest.RuntimeTestInfo ---@field pos_id string ---@field name string ----@field namespace_pos_id string ----@field namespace_name string + +---@type table> +local parametricTestToSourceLevelTest = {} local JEST_PARAMETER_TYPES = { "%%p", @@ -207,13 +207,12 @@ function M.enrichPositionsWithParameterizedTests(file_path, parsed_parameterized -- Get all runtime test information for path local jest_test_discovery_output = runJestTestDiscovery(file_path) - logger.warn(jest_test_discovery_output) - if jest_test_discovery_output == nil then return end local tests_by_position = getTestsByPosition(jest_test_discovery_output) + parametricTestToSourceLevelTest[file_path] = {} -- For each parameterized test, find all tests that were in the same position -- as it and add new range-less (range = nil) children to the tree @@ -246,15 +245,6 @@ function M.enrichPositionsWithParameterizedTests(file_path, parsed_parameterized for _, test_result in ipairs(parameterized_test_results_for_position) do tryCreateNamespaceNodes(tree, test_result.pos_id) - -- Only create a new node if the test position has any test parameters - -- ('$param' or '%j') in the name. Otherwise, we would use a position - -- id that matches the source-level test name which would overwrite - -- the real position id in the tree. - -- - -- There is no way for neotest-jest or jest to distinguish between - -- tests that share the same name anyway so not creating new nodes is - -- acceptable for now - -- if hasTestParameters(tree, pos) then if not tree:get_key(test_result.pos_id) then createNewChildNode( tree, @@ -270,6 +260,12 @@ function M.enrichPositionsWithParameterizedTests(file_path, parsed_parameterized source_pos_id = pos.id, } ) + + if not parametricTestToSourceLevelTest[file_path] then + parametricTestToSourceLevelTest[file_path] = {} + end + + parametricTestToSourceLevelTest[file_path][pos.id] = test_result.pos_id end end end @@ -327,4 +323,14 @@ function M.replaceTestParametersWithRegex(test_name) return result end +---@param pos neotest.Position +---@return string? +function M.getParametricTestToSourceLevelTest(pos) + if parametricTestToSourceLevelTest[pos.path] then + return parametricTestToSourceLevelTest[pos.path][pos.id] + end + + return nil +end + return M diff --git a/spec/json/array.test.json b/spec/json/array.json similarity index 100% rename from spec/json/array.test.json rename to spec/json/array.json diff --git a/spec/json/backtickInTestNames.test.json b/spec/json/backtickInTestNames.json similarity index 100% rename from spec/json/backtickInTestNames.test.json rename to spec/json/backtickInTestNames.json diff --git a/spec/json/basic.test.json b/spec/json/basic.json similarity index 100% rename from spec/json/basic.test.json rename to spec/json/basic.json diff --git a/spec/json/basic-parse-fail.test.json b/spec/json/basicParseFail.json similarity index 100% rename from spec/json/basic-parse-fail.test.json rename to spec/json/basicParseFail.json diff --git a/spec/json/basic-skipped-failed.test.json b/spec/json/basicSkippedFailed.json similarity index 98% rename from spec/json/basic-skipped-failed.test.json rename to spec/json/basicSkippedFailed.json index 6aa2e98..666cbe6 100644 --- a/spec/json/basic-skipped-failed.test.json +++ b/spec/json/basicSkippedFailed.json @@ -182,7 +182,7 @@ ], "endTime": 1751198748669, "message": "\u001b[1m\u001b[31m \u001b[1m? \u001b[22m\u001b[1mdescribe text � 3\u001b[39m\u001b[22m\n\n ReferenceError: assert is not defined", - "name": "./spec/tests/basic-skipped-failed.test.ts", + "name": "./spec/tests/basicSkippedFailed.test.ts", "startTime": 1751198748377, "status": "failed", "summary": "" diff --git a/spec/json/nestedDescribe.test.json b/spec/json/nestedDescribe.json similarity index 100% rename from spec/json/nestedDescribe.test.json rename to spec/json/nestedDescribe.json diff --git a/spec/json/nonStringTestNames.json b/spec/json/nonStringTestNames.json new file mode 100644 index 0000000..928d4d2 --- /dev/null +++ b/spec/json/nonStringTestNames.json @@ -0,0 +1,144 @@ +{ + "numFailedTestSuites": 0, + "numFailedTests": 0, + "numPassedTestSuites": 1, + "numPassedTests": 5, + "numPendingTestSuites": 0, + "numPendingTests": 0, + "numRuntimeErrorTestSuites": 0, + "numTodoTests": 0, + "numTotalTestSuites": 1, + "numTotalTests": 5, + "openHandles": [], + "snapshot": { + "added": 0, + "didUpdate": false, + "failure": false, + "filesAdded": 0, + "filesRemoved": 0, + "filesRemovedList": [], + "filesUnmatched": 0, + "filesUpdated": 0, + "matched": 0, + "total": 0, + "unchecked": 0, + "uncheckedKeysByFile": [], + "unmatched": 0, + "updated": 0 + }, + "startTime": 1761927012081, + "success": true, + "testResults": [ + { + "assertionResults": [ + { + "ancestorTitles": [ + "non-string test names" + ], + "duration": 2, + "failing": false, + "failureDetails": [], + "failureMessages": [], + "fullName": "non-string test names Test", + "invocations": 1, + "location": { + "column": 3, + "line": 16 + }, + "numPassingAsserts": 1, + "retryReasons": [], + "startAt": 1761927012402, + "status": "passed", + "title": "Test" + }, + { + "ancestorTitles": [ + "non-string test names" + ], + "duration": 1, + "failing": false, + "failureDetails": [], + "failureMessages": [], + "fullName": "non-string test names name", + "invocations": 1, + "location": { + "column": 3, + "line": 20 + }, + "numPassingAsserts": 1, + "retryReasons": [], + "startAt": 1761927012408, + "status": "passed", + "title": "name" + }, + { + "ancestorTitles": [ + "non-string test names" + ], + "duration": 1, + "failing": false, + "failureDetails": [], + "failureMessages": [], + "fullName": "non-string test names arrow", + "invocations": 1, + "location": { + "column": 3, + "line": 24 + }, + "numPassingAsserts": 1, + "retryReasons": [], + "startAt": 1761927012409, + "status": "passed", + "title": "arrow" + }, + { + "ancestorTitles": [ + "non-string test names" + ], + "duration": 0, + "failing": false, + "failureDetails": [], + "failureMessages": [], + "fullName": "non-string test names func", + "invocations": 1, + "location": { + "column": 3, + "line": 28 + }, + "numPassingAsserts": 1, + "retryReasons": [], + "startAt": 1761927012410, + "status": "passed", + "title": "func" + }, + { + "ancestorTitles": [ + "non-string test names" + ], + "duration": 0, + "failing": false, + "failureDetails": [], + "failureMessages": [], + "fullName": "non-string test names 123", + "invocations": 1, + "location": { + "column": 3, + "line": 32 + }, + "numPassingAsserts": 1, + "retryReasons": [], + "startAt": 1761927012411, + "status": "passed", + "title": "123" + } + ], + "endTime": 1761927012413, + "message": "", + "name": "./spec/tests/nonStringTestNames.test.ts", + "startTime": 1761927012117, + "status": "passed", + "summary": "" + } + ], + "wasInterrupted": false +} diff --git a/spec/json/parameterized.test.json b/spec/json/parameterized.json similarity index 100% rename from spec/json/parameterized.test.json rename to spec/json/parameterized.json diff --git a/spec/json/parametricDescribeAndTest.test.json b/spec/json/parametricDescribeAndTest.json similarity index 100% rename from spec/json/parametricDescribeAndTest.test.json rename to spec/json/parametricDescribeAndTest.json diff --git a/spec/json/parametricDescribesOnly.test.json b/spec/json/parametricDescribesOnly.json similarity index 100% rename from spec/json/parametricDescribesOnly.test.json rename to spec/json/parametricDescribesOnly.json diff --git a/spec/json/templateStrings.test.json b/spec/json/templateStrings.json similarity index 100% rename from spec/json/templateStrings.test.json rename to spec/json/templateStrings.json diff --git a/spec/tests/basic-skipped-failed.test.ts b/spec/tests/basicSkippedFailed.test.ts similarity index 100% rename from spec/tests/basic-skipped-failed.test.ts rename to spec/tests/basicSkippedFailed.test.ts diff --git a/spec/tests/nonStringTestNames.test.ts b/spec/tests/nonStringTestNames.test.ts new file mode 100644 index 0000000..6307e8e --- /dev/null +++ b/spec/tests/nonStringTestNames.test.ts @@ -0,0 +1,35 @@ +class Test { + public name: string + + constructor(name: string) { + this.name = name + } +} + +const arrow = () => {} + +function func(): void {} + +const test = new Test('name') + +describe('non-string test names', () => { + it(Test, () => { + expect(true).toBe(true) + }) + + it(test.name, () => { + expect(true).toBe(true) + }) + + it(arrow, () => { + expect(true).toBe(true) + }) + + it(func, () => { + expect(true).toBe(true) + }) + + it(123, () => { + expect(true).toBe(true) + }) +}) diff --git a/tests/adapter_discover_positions_spec.lua b/tests/adapter_discover_positions_spec.lua index 68a6b1f..38f694c 100644 --- a/tests/adapter_discover_positions_spec.lua +++ b/tests/adapter_discover_positions_spec.lua @@ -49,7 +49,7 @@ describe("adapter.discover_positions", function() name = "1", path = path, range = { 5, 2, 7, 4 }, - test_name_range = { 5, 6, 5, 7 }, + test_name_range = { 5, 5, 5, 8 }, type = PositionType.test, }, }, @@ -60,7 +60,7 @@ describe("adapter.discover_positions", function() name = "2", path = path, range = { 9, 2, 11, 4 }, - test_name_range = { 9, 6, 9, 7 }, + test_name_range = { 9, 5, 9, 8 }, type = PositionType.test, }, }, @@ -71,7 +71,7 @@ describe("adapter.discover_positions", function() name = "3", path = path, range = { 13, 2, 15, 4 }, - test_name_range = { 13, 8, 13, 9 }, + test_name_range = { 13, 7, 13, 10 }, type = PositionType.test, }, }, @@ -82,7 +82,7 @@ describe("adapter.discover_positions", function() name = "4", path = path, range = { 17, 2, 19, 4 }, - test_name_range = { 17, 8, 17, 9 }, + test_name_range = { 17, 7, 17, 10 }, type = PositionType.test, }, }, @@ -93,7 +93,7 @@ describe("adapter.discover_positions", function() name = "5", path = path, range = { 21, 2, 23, 5 }, - test_name_range = { 21, 6, 21, 7 }, + test_name_range = { 21, 5, 21, 8 }, type = PositionType.test, }, }, @@ -114,7 +114,7 @@ describe("adapter.discover_positions", function() name = "1", path = path, range = { 27, 2, 29, 4 }, - test_name_range = { 27, 6, 27, 7 }, + test_name_range = { 27, 5, 27, 8 }, type = PositionType.test, }, }, @@ -125,7 +125,7 @@ describe("adapter.discover_positions", function() name = "2", path = path, range = { 31, 2, 33, 4 }, - test_name_range = { 31, 6, 31, 7 }, + test_name_range = { 31, 5, 31, 8 }, type = PositionType.test, }, }, @@ -136,7 +136,7 @@ describe("adapter.discover_positions", function() name = "3", path = path, range = { 35, 2, 37, 4 }, - test_name_range = { 35, 8, 35, 9 }, + test_name_range = { 35, 7, 35, 10 }, type = PositionType.test, }, }, @@ -147,7 +147,7 @@ describe("adapter.discover_positions", function() name = "4", path = path, range = { 39, 2, 41, 4 }, - test_name_range = { 39, 8, 39, 9 }, + test_name_range = { 39, 7, 39, 10 }, type = PositionType.test, }, }, @@ -158,7 +158,7 @@ describe("adapter.discover_positions", function() name = "5", path = path, range = { 43, 2, 45, 5 }, - test_name_range = { 43, 6, 43, 7 }, + test_name_range = { 43, 5, 43, 8 }, type = PositionType.test, }, }, @@ -199,7 +199,7 @@ describe("adapter.discover_positions", function() name = "1", path = path, range = { 5, 2, 7, 4 }, - test_name_range = { 5, 6, 5, 7 }, + test_name_range = { 5, 5, 5, 8 }, type = PositionType.test, }, }, @@ -210,7 +210,7 @@ describe("adapter.discover_positions", function() name = "2", path = path, range = { 9, 2, 11, 4 }, - test_name_range = { 9, 6, 9, 7 }, + test_name_range = { 9, 5, 9, 8 }, type = PositionType.test, }, }, @@ -221,7 +221,7 @@ describe("adapter.discover_positions", function() name = "3", path = path, range = { 13, 2, 15, 4 }, - test_name_range = { 13, 8, 13, 9 }, + test_name_range = { 13, 7, 13, 10 }, type = PositionType.test, }, }, @@ -232,7 +232,7 @@ describe("adapter.discover_positions", function() name = "4", path = path, range = { 17, 2, 19, 4 }, - test_name_range = { 17, 8, 17, 9 }, + test_name_range = { 17, 7, 17, 10 }, type = PositionType.test, }, }, @@ -243,7 +243,7 @@ describe("adapter.discover_positions", function() name = "5", path = path, range = { 21, 2, 23, 5 }, - test_name_range = { 21, 6, 21, 7 }, + test_name_range = { 21, 5, 21, 8 }, type = PositionType.test, }, }, @@ -264,7 +264,7 @@ describe("adapter.discover_positions", function() name = "1", path = path, range = { 27, 2, 29, 4 }, - test_name_range = { 27, 6, 27, 7 }, + test_name_range = { 27, 5, 27, 8 }, type = PositionType.test, }, }, @@ -275,7 +275,7 @@ describe("adapter.discover_positions", function() name = "2", path = path, range = { 31, 2, 33, 4 }, - test_name_range = { 31, 6, 31, 7 }, + test_name_range = { 31, 5, 31, 8 }, type = PositionType.test, }, }, @@ -286,7 +286,7 @@ describe("adapter.discover_positions", function() name = "3", path = path, range = { 35, 2, 37, 4 }, - test_name_range = { 35, 8, 35, 9 }, + test_name_range = { 35, 7, 35, 10 }, type = PositionType.test, }, }, @@ -297,7 +297,7 @@ describe("adapter.discover_positions", function() name = "4", path = path, range = { 39, 2, 41, 4 }, - test_name_range = { 39, 8, 39, 9 }, + test_name_range = { 39, 7, 39, 10 }, type = PositionType.test, }, }, @@ -308,7 +308,7 @@ describe("adapter.discover_positions", function() name = "5", path = path, range = { 43, 2, 45, 5 }, - test_name_range = { 43, 6, 43, 7 }, + test_name_range = { 43, 5, 43, 8 }, type = PositionType.test, }, }, @@ -349,7 +349,7 @@ describe("adapter.discover_positions", function() name = "` 1", path = path, range = { 1, 2, 3, 4 }, - test_name_range = { 1, 6, 1, 9 }, + test_name_range = { 1, 5, 1, 10 }, type = PositionType.test, }, }, @@ -360,7 +360,7 @@ describe("adapter.discover_positions", function() name = "2`", path = path, range = { 5, 2, 7, 4 }, - test_name_range = { 5, 8, 5, 10 }, + test_name_range = { 5, 7, 5, 11 }, type = PositionType.test, }, }, @@ -371,7 +371,7 @@ describe("adapter.discover_positions", function() name = "`` 3", path = path, range = { 9, 2, 11, 4 }, - test_name_range = { 9, 6, 9, 10 }, + test_name_range = { 9, 5, 9, 11 }, type = PositionType.test, }, }, @@ -382,7 +382,92 @@ describe("adapter.discover_positions", function() name = "` 4`", path = path, range = { 13, 2, 15, 4 }, - test_name_range = { 13, 8, 13, 12 }, + test_name_range = { 13, 7, 13, 13 }, + type = PositionType.test, + }, + }, + }, + } + + assert.are.same(positions, expected_output) + end) + + async.it("provides meaningful names from a spec with non-string test names", function() + package.loaded["neotest-jest"] = nil + + local adapter = require("neotest-jest")({ jestCommand = "jest" }) + local path = "./spec/tests/nonStringTestNames.test.ts" + local positions = adapter.discover_positions(path):to_list() + + local expected_output = { + { + id = path, + name = "nonStringTestNames.test.ts", + path = path, + range = { 0, 0, 35, 0 }, + type = PositionType.file, + }, + { + { + id = path .. "::non-string test names", + is_parameterized = false, + name = "non-string test names", + path = path, + range = { 14, 0, 34, 2 }, + type = PositionType.namespace, + }, + { + { + id = path .. "::non-string test names::Test", + is_parameterized = true, + name = "Test", + path = path, + range = { 15, 2, 17, 4 }, + test_name_range = { 15, 5, 15, 9 }, + type = PositionType.test, + }, + }, + { + { + id = path .. "::non-string test names::test.name", + is_parameterized = true, + name = "test.name", + path = path, + range = { 19, 2, 21, 4 }, + test_name_range = { 19, 5, 19, 14 }, + type = PositionType.test, + }, + }, + { + { + id = path .. "::non-string test names::arrow", + is_parameterized = true, + name = "arrow", + path = path, + range = { 23, 2, 25, 4 }, + test_name_range = { 23, 5, 23, 10 }, + type = PositionType.test, + }, + }, + { + { + id = path .. "::non-string test names::func", + is_parameterized = true, + name = "func", + path = path, + range = { 27, 2, 29, 4 }, + test_name_range = { 27, 5, 27, 9 }, + type = PositionType.test, + }, + }, + { + { + id = path .. "::non-string test names::123", + is_parameterized = true, + name = "123", + path = path, + range = { 31, 2, 33, 4 }, + test_name_range = { 31, 5, 31, 8 }, type = PositionType.test, }, }, @@ -423,7 +508,7 @@ describe("adapter.discover_positions", function() name = "Array1 %d", path = path, range = { 1, 2, 3, 4 }, - test_name_range = { 1, 22, 1, 31 }, + test_name_range = { 1, 21, 1, 32 }, type = PositionType.test, }, }, @@ -434,7 +519,7 @@ describe("adapter.discover_positions", function() name = "Array2", path = path, range = { 5, 2, 7, 4 }, - test_name_range = { 5, 22, 5, 28 }, + test_name_range = { 5, 21, 5, 29 }, type = PositionType.test, }, }, @@ -445,7 +530,7 @@ describe("adapter.discover_positions", function() name = "Array3 %d", path = path, range = { 9, 2, 11, 4 }, - test_name_range = { 9, 24, 9, 33 }, + test_name_range = { 9, 23, 9, 34 }, type = PositionType.test, }, }, @@ -456,7 +541,7 @@ describe("adapter.discover_positions", function() name = "Array4 %d", path = path, range = { 13, 2, 15, 4 }, - test_name_range = { 13, 24, 13, 33 }, + test_name_range = { 13, 23, 13, 34 }, type = PositionType.test, }, }, @@ -477,7 +562,7 @@ describe("adapter.discover_positions", function() name = "Array1 %d", path = path, range = { 19, 2, 21, 4 }, - test_name_range = { 19, 22, 19, 31 }, + test_name_range = { 19, 21, 19, 32 }, type = PositionType.test, }, }, @@ -488,7 +573,7 @@ describe("adapter.discover_positions", function() name = "Array2 %d", path = path, range = { 23, 2, 25, 4 }, - test_name_range = { 23, 22, 23, 31 }, + test_name_range = { 23, 21, 23, 32 }, type = PositionType.test, }, }, @@ -499,7 +584,7 @@ describe("adapter.discover_positions", function() name = "Array3 %d", path = path, range = { 27, 2, 29, 4 }, - test_name_range = { 27, 24, 27, 33 }, + test_name_range = { 27, 23, 27, 34 }, type = PositionType.test, }, }, @@ -510,7 +595,7 @@ describe("adapter.discover_positions", function() name = "Array4", path = path, range = { 31, 2, 33, 4 }, - test_name_range = { 31, 24, 31, 30 }, + test_name_range = { 31, 23, 31, 31 }, type = PositionType.test, }, }, @@ -572,7 +657,7 @@ describe("adapter.discover_positions", function() name = "Array1 %d", path = path, range = { 1, 2, 3, 4 }, - test_name_range = { 1, 22, 1, 31 }, + test_name_range = { 1, 21, 1, 32 }, type = PositionType.test, }, { @@ -610,7 +695,7 @@ describe("adapter.discover_positions", function() name = "Array2", path = path, range = { 5, 2, 7, 4 }, - test_name_range = { 5, 22, 5, 28 }, + test_name_range = { 5, 21, 5, 29 }, type = PositionType.test, }, }, @@ -621,7 +706,7 @@ describe("adapter.discover_positions", function() name = "Array3 %d", path = path, range = { 9, 2, 11, 4 }, - test_name_range = { 9, 24, 9, 33 }, + test_name_range = { 9, 23, 9, 34 }, type = PositionType.test, }, { @@ -659,7 +744,7 @@ describe("adapter.discover_positions", function() name = "Array4 %d", path = path, range = { 13, 2, 15, 4 }, - test_name_range = { 13, 24, 13, 33 }, + test_name_range = { 13, 23, 13, 34 }, type = PositionType.test, }, { @@ -707,7 +792,7 @@ describe("adapter.discover_positions", function() name = "Array1 %d", path = path, range = { 19, 2, 21, 4 }, - test_name_range = { 19, 22, 19, 31 }, + test_name_range = { 19, 21, 19, 32 }, type = PositionType.test, }, { @@ -745,7 +830,7 @@ describe("adapter.discover_positions", function() name = "Array2 %d", path = path, range = { 23, 2, 25, 4 }, - test_name_range = { 23, 22, 23, 31 }, + test_name_range = { 23, 21, 23, 32 }, type = PositionType.test, }, { @@ -783,7 +868,7 @@ describe("adapter.discover_positions", function() name = "Array3 %d", path = path, range = { 27, 2, 29, 4 }, - test_name_range = { 27, 24, 27, 33 }, + test_name_range = { 27, 23, 27, 34 }, type = PositionType.test, }, { @@ -821,7 +906,7 @@ describe("adapter.discover_positions", function() name = "Array4", path = path, range = { 31, 2, 33, 4 }, - test_name_range = { 31, 24, 31, 30 }, + test_name_range = { 31, 23, 31, 31 }, type = PositionType.test, }, }, @@ -871,7 +956,7 @@ describe("adapter.discover_positions", function() name = "test 1", path = path, range = { 2, 4, 4, 6 }, - test_name_range = { 2, 8, 2, 14 }, + test_name_range = { 2, 7, 2, 15 }, type = PositionType.test, }, { @@ -918,7 +1003,7 @@ describe("adapter.discover_positions", function() name = "test 2", path = path, range = { 6, 4, 8, 6 }, - test_name_range = { 6, 8, 6, 14 }, + test_name_range = { 6, 7, 6, 15 }, type = PositionType.test, }, { @@ -965,7 +1050,7 @@ describe("adapter.discover_positions", function() name = "test 3", path = path, range = { 10, 4, 12, 6 }, - test_name_range = { 10, 8, 10, 14 }, + test_name_range = { 10, 7, 10, 15 }, type = PositionType.test, }, { @@ -1060,97 +1145,97 @@ describe("adapter.discover_positions", function() assert.are.same(positions, expected_output) end) - -- async.it("provides meaningful names for parametric describes and tests", function() - -- package.loaded["neotest-jest"] = nil - -- - -- local path = get_test_absolute_path("parametricDescribeAndTest.test.ts") - -- local adapter = require("neotest-jest")({ jestCommand = "jest", jest_test_discovery = true }) - -- local positions = adapter.discover_positions(path):to_list() - -- - -- local expected_output = { - -- { - -- id = path, - -- name = "parametricDescribeAndTest.test.ts", - -- path = path, - -- range = { 0, 0, 12, 0 }, - -- type = PositionType.file, - -- }, - -- { - -- { - -- id = path .. "::greeting %s", - -- is_parameterized = true, - -- name = "greeting %s", - -- path = path, - -- range = { 4, 0, 11, 2 }, - -- type = PositionType.namespace, - -- }, - -- { - -- { - -- id = path .. "::greeting %s::should greet using %s!", - -- is_parameterized = true, - -- name = "should greet using %s!", - -- path = path, - -- range = { 5, 2, 10, 4 }, - -- test_name_range = { 8, 6, 8, 28 }, - -- type = PositionType.test, - -- }, - -- }, - -- }, - -- { - -- { - -- id = path .. "::greeting Alice", - -- name = "greeting Alice", - -- path = path, - -- type = PositionType.namespace, - -- }, - -- { - -- { - -- id = path .. "::greeting Alice::should greet using Hello!", - -- name = "should greet using Hello!", - -- path = path, - -- source_pos_id = path .. "::greeting %s::should greet using %s!", - -- type = PositionType.test, - -- }, - -- }, - -- { - -- { - -- id = path .. "::greeting Alice::should greet using Hi!", - -- name = "should greet using Hi!", - -- path = path, - -- source_pos_id = path .. "::greeting %s::should greet using %s!", - -- type = PositionType.test, - -- }, - -- }, - -- }, - -- { - -- { - -- id = path .. "::greeting Bob", - -- name = "greeting Bob", - -- path = path, - -- type = PositionType.namespace, - -- }, - -- { - -- { - -- id = path .. "::greeting Bob::should greet using Hello!", - -- name = "should greet using Hello!", - -- path = path, - -- source_pos_id = path .. "::greeting %s::should greet using %s!", - -- type = PositionType.test, - -- }, - -- }, - -- { - -- { - -- id = path .. "::greeting Bob::should greet using Hi!", - -- name = "should greet using Hi!", - -- path = path, - -- source_pos_id = path .. "::greeting %s::should greet using %s!", - -- type = PositionType.test, - -- }, - -- }, - -- }, - -- } - -- - -- assert.are.same(positions, expected_output) - -- end) + async.it("provides meaningful names for parametric describes and tests", function() + package.loaded["neotest-jest"] = nil + + local path = get_test_absolute_path("parametricDescribeAndTest.test.ts") + local adapter = require("neotest-jest")({ jestCommand = "jest", jest_test_discovery = true }) + local positions = adapter.discover_positions(path):to_list() + + local expected_output = { + { + id = path, + name = "parametricDescribeAndTest.test.ts", + path = path, + range = { 0, 0, 12, 0 }, + type = PositionType.file, + }, + { + { + id = path .. "::greeting %s", + is_parameterized = true, + name = "greeting %s", + path = path, + range = { 4, 0, 11, 2 }, + type = PositionType.namespace, + }, + { + { + id = path .. "::greeting %s::should greet using %s!", + is_parameterized = true, + name = "should greet using %s!", + path = path, + range = { 5, 2, 10, 4 }, + test_name_range = { 8, 5, 8, 29 }, + type = PositionType.test, + }, + { + { + id = path .. "::greeting Alice::should greet using Hello!", + name = "should greet using Hello!", + path = path, + source_pos_id = path .. "::greeting %s::should greet using %s!", + type = PositionType.test, + }, + }, + { + { + id = path .. "::greeting Alice::should greet using Hi!", + name = "should greet using Hi!", + path = path, + source_pos_id = path .. "::greeting %s::should greet using %s!", + type = PositionType.test, + }, + }, + { + { + id = path .. "::greeting Bob::should greet using Hello!", + name = "should greet using Hello!", + path = path, + source_pos_id = path .. "::greeting %s::should greet using %s!", + type = PositionType.test, + }, + }, + { + { + id = path .. "::greeting Bob::should greet using Hi!", + name = "should greet using Hi!", + path = path, + source_pos_id = path .. "::greeting %s::should greet using %s!", + type = PositionType.test, + }, + }, + }, + { + { + id = path .. "::greeting Alice", + name = "greeting Alice", + path = path, + type = PositionType.namespace, + }, + }, + { + { + id = path .. "::greeting Bob", + name = "greeting Bob", + path = path, + type = PositionType.namespace, + }, + }, + }, + } + + assert.are.same(positions, expected_output) + end) end) end) diff --git a/tests/adapter_results_spec.lua b/tests/adapter_results_spec.lua index ec0ea02..eccf5c3 100644 --- a/tests/adapter_results_spec.lua +++ b/tests/adapter_results_spec.lua @@ -45,7 +45,7 @@ describe("adapter.results", function() package.loaded["neotest-jest"] = nil local adapter = require("neotest-jest")({}) local path = "./spec/tests/basic.test.ts" - local tree = discover_positions(adapter, path, "./spec/json/basic.test.json") + local tree = discover_positions(adapter, path, "./spec/json/basic.json") local neotest_results = adapter.results(spec, strategy_result, tree) assert.are.same(neotest_results, { @@ -160,7 +160,7 @@ describe("adapter.results", function() local adapter = require("neotest-jest")({}) local path = "./spec/tests/nestedDescribe.test.ts" - local tree = discover_positions(adapter, path, "./spec/json/nestedDescribe.test.json") + local tree = discover_positions(adapter, path, "./spec/json/nestedDescribe.json") local neotest_results = adapter.results(spec, strategy_result, tree) assert.are.same(neotest_results, { @@ -203,7 +203,7 @@ describe("adapter.results", function() local adapter = require("neotest-jest")({}) local path = "./spec/tests/templateStrings.test.ts" - local tree = discover_positions(adapter, path, "./spec/json/templateStrings.test.json") + local tree = discover_positions(adapter, path, "./spec/json/templateStrings.json") local neotest_results = adapter.results(spec, strategy_result, tree) assert.are.same(neotest_results, { @@ -313,7 +313,7 @@ describe("adapter.results", function() local adapter = require("neotest-jest")({}) local path = "./spec/tests/backtickInTestNames.test.ts" - local tree = discover_positions(adapter, path, "./spec/json/backtickInTestNames.test.json") + local tree = discover_positions(adapter, path, "./spec/json/backtickInTestNames.json") local neotest_results = adapter.results(spec, strategy_result, tree) assert.are.same(neotest_results, { @@ -364,12 +364,77 @@ describe("adapter.results", function() lib.files.read:revert() end) + async.it("creates neotest results for tests with non-string test names", function() + package.loaded["neotest-jest"] = nil + + local adapter = require("neotest-jest")({}) + local path = "./spec/tests/nonStringTestNames.test.ts" + local tree = discover_positions(adapter, path, "./spec/json/nonStringTestNames.json") + local neotest_results = adapter.results(spec, strategy_result, tree) + + assert.are.same(neotest_results, { + [path .. "::non-string test names::Test"] = { + status = types.ResultStatus.passed, + short = "Test: passed", + output = strategy_result.output, + location = { + line = 16, + column = 3, + }, + }, + [path .. "::non-string test names::name"] = { + status = types.ResultStatus.passed, + short = "name: passed", + output = strategy_result.output, + location = { + line = 20, + column = 3, + }, + }, + [path .. "::non-string test names::arrow"] = { + status = types.ResultStatus.passed, + short = "arrow: passed", + output = strategy_result.output, + location = { + line = 24, + column = 3, + }, + }, + [path .. "::non-string test names::func"] = { + status = types.ResultStatus.passed, + short = "func: passed", + output = strategy_result.output, + location = { + line = 28, + column = 3, + }, + }, + [path .. "::non-string test names::123"] = { + status = types.ResultStatus.passed, + short = "123: passed", + output = strategy_result.output, + location = { + line = 32, + column = 3, + }, + }, + }) + + ---@diagnostic disable-next-line: param-type-mismatch + assert.stub(lib.files.read).was.called_with(spec.context.results_path) + ---@diagnostic disable-next-line: param-type-mismatch, undefined-field + assert.stub(logger.error).was_not_called() + + ---@diagnostic disable-next-line: undefined-field + lib.files.read:revert() + end) + async.it("creates neotest results for parametrized tests", function() package.loaded["neotest-jest"] = nil local adapter = require("neotest-jest")({ jest_test_discovery = true }) local path = "./spec/tests/array.test.ts" - local tree = discover_positions(adapter, path, "./spec/json/array.test.json") + local tree = discover_positions(adapter, path, "./spec/json/array.json") local neotest_results = adapter.results(spec, strategy_result, tree) -- TODO: does not work since test names and positions are the same @@ -611,7 +676,7 @@ describe("adapter.results", function() local adapter = require("neotest-jest")({ jest_test_discovery = true }) local path = "./spec/tests/parameterized.test.ts" - local tree = discover_positions(adapter, path, "./spec/json/parameterized.test.json") + local tree = discover_positions(adapter, path, "./spec/json/parameterized.json") local neotest_results = adapter.results(spec, strategy_result, tree) assert.are.same(neotest_results, { @@ -690,7 +755,7 @@ describe("adapter.results", function() local adapter = require("neotest-jest")({ jest_test_discovery = true }) local path = "./spec/tests/parametricDescribesOnly.test.ts" - local tree = discover_positions(adapter, path, "./spec/json/parametricDescribesOnly.test.json") + local tree = discover_positions(adapter, path, "./spec/json/parametricDescribesOnly.json") local neotest_results = adapter.results(spec, strategy_result, tree) assert.are.same(neotest_results, { @@ -851,8 +916,7 @@ describe("adapter.results", function() local adapter = require("neotest-jest")({ jest_test_discovery = true }) local path = "./spec/tests/parametricDescribeAndTest.test.ts" - local tree = - discover_positions(adapter, path, "./spec/json/parametricDescribeAndTest.test.json") + local tree = discover_positions(adapter, path, "./spec/json/parametricDescribeAndTest.json") local neotest_results = adapter.results(spec, strategy_result, tree) assert.are.same(neotest_results, { @@ -912,8 +976,8 @@ describe("adapter.results", function() package.loaded["neotest-jest"] = nil local adapter = require("neotest-jest")({}) - local path = "./spec/tests/basic-skipped-failed.test.ts" - local tree = discover_positions(adapter, path, "./spec/json/basic-skipped-failed.test.json") + local path = "./spec/tests/basicSkippedFailed.test.ts" + local tree = discover_positions(adapter, path, "./spec/json/basicSkippedFailed.json") local neotest_results = adapter.results(spec, strategy_result, tree) assert.are.same(neotest_results, { @@ -1017,7 +1081,7 @@ describe("adapter.results", function() local path = "./spec/tests/basic.test.ts" local adapter = require("neotest-jest")({}) - local tree = discover_positions(adapter, path, "./spec/json/basic-parse-fail.test.json") + local tree = discover_positions(adapter, path, "./spec/json/basicParseFail.json") local neotest_results = adapter.results(spec, strategy_result, tree) assert.are.same(neotest_results, {})