fix TSamFailedOnly and the output formatting for the jest-runner
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
output
|
||||
.nvimlog
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
- Rolle: **TDD-first Entwickler**
|
||||
- Jede neue Funktion, jedes neue Kommando und jede Verhaltensänderung **muss durch Tests abgesichert sein**.
|
||||
- Nach jeder Code-Änderung **Tests via `bash run_test.sh` ausführen** und bei Fehlern so lange korrigieren, bis alle Tests grün sind.
|
||||
- Antworten immer auf Deutsch.
|
||||
- **Nicht raten**:
|
||||
- Bei unklaren oder mehrdeutigen Anforderungen **Arbeit stoppen** und Klarstellung verlangen.
|
||||
- TODO/NOTE im Code ist zulässig, stilles Raten nicht.
|
||||
|
||||
@@ -311,20 +311,30 @@ local function run_cmd(cmd, cwd, handlers)
|
||||
})
|
||||
end
|
||||
|
||||
local function pick_display(results, key)
|
||||
if type(results.display) == "table" and type(results.display[key]) == "table" then
|
||||
return results.display[key]
|
||||
end
|
||||
return results[key]
|
||||
end
|
||||
|
||||
local function format_results(results)
|
||||
local lines = {}
|
||||
if type(results.passes) == "table" then
|
||||
for _, title in ipairs(results.passes) do
|
||||
local passes = pick_display(results, "passes")
|
||||
if type(passes) == "table" then
|
||||
for _, title in ipairs(passes) do
|
||||
table.insert(lines, "[ PASS ] - " .. title)
|
||||
end
|
||||
end
|
||||
if type(results.skips) == "table" then
|
||||
for _, title in ipairs(results.skips) do
|
||||
local skips = pick_display(results, "skips")
|
||||
if type(skips) == "table" then
|
||||
for _, title in ipairs(skips) do
|
||||
table.insert(lines, "[ SKIP ] - " .. title)
|
||||
end
|
||||
end
|
||||
if type(results.failures) == "table" then
|
||||
for _, title in ipairs(results.failures) do
|
||||
local failures = pick_display(results, "failures")
|
||||
if type(failures) == "table" then
|
||||
for _, title in ipairs(failures) do
|
||||
table.insert(lines, "[ FAIL ] - " .. title)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -241,21 +241,28 @@ end
|
||||
|
||||
function runner.parse_results(output)
|
||||
if not output or output == "" then
|
||||
return { passes = {}, failures = {}, skips = {} }
|
||||
return { passes = {}, failures = {}, skips = {}, display = { passes = {}, failures = {}, skips = {} } }
|
||||
end
|
||||
local passes = {}
|
||||
local failures = {}
|
||||
local skips = {}
|
||||
local display = { passes = {}, failures = {}, skips = {} }
|
||||
for line in output:gmatch("[^\n]+") do
|
||||
local ok, data = pcall(vim.json.decode, line)
|
||||
if ok and type(data) == "table" then
|
||||
if data.Test and data.Test ~= "" then
|
||||
if data.Action == "pass" then
|
||||
table.insert(passes, data.Test)
|
||||
local short = data.Test:match("([^/]+)$") or data.Test
|
||||
table.insert(display.passes, short)
|
||||
elseif data.Action == "fail" then
|
||||
table.insert(failures, data.Test)
|
||||
local short = data.Test:match("([^/]+)$") or data.Test
|
||||
table.insert(display.failures, short)
|
||||
elseif data.Action == "skip" then
|
||||
table.insert(skips, data.Test)
|
||||
local short = data.Test:match("([^/]+)$") or data.Test
|
||||
table.insert(display.skips, short)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -264,6 +271,7 @@ function runner.parse_results(output)
|
||||
passes = collect_unique(passes),
|
||||
failures = collect_unique(failures),
|
||||
skips = collect_unique(skips),
|
||||
display = display,
|
||||
}
|
||||
end
|
||||
|
||||
@@ -273,6 +281,7 @@ function runner.output_parser()
|
||||
local failures = {}
|
||||
local passes = {}
|
||||
local skips = {}
|
||||
local display = { passes = {}, failures = {}, skips = {} }
|
||||
|
||||
return {
|
||||
on_line = function(line, _state)
|
||||
@@ -284,31 +293,38 @@ function runner.output_parser()
|
||||
if not name or name == "" then
|
||||
return nil
|
||||
end
|
||||
local short = name:match("([^/]+)$") or name
|
||||
if data.Action == "pass" and not seen_pass[name] then
|
||||
seen_pass[name] = true
|
||||
table.insert(passes, name)
|
||||
table.insert(display.passes, short)
|
||||
return {
|
||||
passes = { name },
|
||||
failures = {},
|
||||
skips = {},
|
||||
display = { passes = { short }, failures = {}, skips = {} },
|
||||
failures_all = vim.deepcopy(failures),
|
||||
}
|
||||
elseif data.Action == "fail" and not seen_fail[name] then
|
||||
seen_fail[name] = true
|
||||
table.insert(failures, name)
|
||||
table.insert(display.failures, short)
|
||||
return {
|
||||
passes = {},
|
||||
failures = { name },
|
||||
skips = {},
|
||||
display = { passes = {}, failures = { short }, skips = {} },
|
||||
failures_all = vim.deepcopy(failures),
|
||||
}
|
||||
elseif data.Action == "skip" and not seen_pass[name] then
|
||||
seen_pass[name] = true
|
||||
table.insert(skips, name)
|
||||
table.insert(display.skips, short)
|
||||
return {
|
||||
passes = {},
|
||||
failures = {},
|
||||
skips = { name },
|
||||
display = { passes = {}, failures = {}, skips = { short } },
|
||||
failures_all = vim.deepcopy(failures),
|
||||
}
|
||||
end
|
||||
|
||||
@@ -4,5 +4,5 @@ return js.new({
|
||||
name = "js-jest",
|
||||
framework = "jest",
|
||||
command = { "npx", "jest" },
|
||||
json_args = { "--json" },
|
||||
json_args = { "--json", "--verbose" },
|
||||
})
|
||||
|
||||
@@ -418,28 +418,123 @@ function M.new(opts)
|
||||
}
|
||||
end
|
||||
|
||||
local function parse_jest_like(output)
|
||||
local ok, data = pcall(vim.json.decode, output)
|
||||
if not ok or type(data) ~= "table" then
|
||||
local function parse_jest_results(data)
|
||||
if type(data) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
local passes = {}
|
||||
local failures = {}
|
||||
local skips = {}
|
||||
local display = { passes = {}, failures = {}, skips = {} }
|
||||
for _, result in ipairs(data.testResults or {}) do
|
||||
for _, assertion in ipairs(result.assertionResults or {}) do
|
||||
local title = assertion.fullName or assertion.title
|
||||
if assertion.status == "passed" and title then
|
||||
table.insert(passes, title)
|
||||
elseif assertion.status == "failed" and title then
|
||||
table.insert(failures, title)
|
||||
local full = assertion.fullName or assertion.title
|
||||
local short = assertion.title or assertion.fullName
|
||||
if assertion.status == "passed" and full then
|
||||
table.insert(passes, full)
|
||||
if short then
|
||||
table.insert(display.passes, short)
|
||||
end
|
||||
elseif assertion.status == "failed" and full then
|
||||
table.insert(failures, full)
|
||||
if short then
|
||||
table.insert(display.failures, short)
|
||||
end
|
||||
elseif (assertion.status == "pending" or assertion.status == "skipped" or assertion.status == "todo")
|
||||
and title then
|
||||
table.insert(skips, title)
|
||||
and full then
|
||||
table.insert(skips, full)
|
||||
if short then
|
||||
table.insert(display.skips, short)
|
||||
end
|
||||
end
|
||||
end
|
||||
return { passes = passes, failures = failures, skips = skips }
|
||||
end
|
||||
return { passes = passes, failures = failures, skips = skips, display = display }
|
||||
end
|
||||
|
||||
local function parse_jest_like(output)
|
||||
local ok, data = pcall(vim.json.decode, output)
|
||||
if ok and type(data) == "table" then
|
||||
return parse_jest_results(data)
|
||||
end
|
||||
if not output or output == "" then
|
||||
return nil
|
||||
end
|
||||
local start_idx = output:find("{", 1, true)
|
||||
if not start_idx then
|
||||
return nil
|
||||
end
|
||||
local end_idx = nil
|
||||
for i = #output, start_idx, -1 do
|
||||
if output:sub(i, i) == "}" then
|
||||
end_idx = i
|
||||
break
|
||||
end
|
||||
end
|
||||
if not end_idx then
|
||||
return nil
|
||||
end
|
||||
local candidate = output:sub(start_idx, end_idx)
|
||||
local ok2, data2 = pcall(vim.json.decode, candidate)
|
||||
if not ok2 or type(data2) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
return parse_jest_results(data2)
|
||||
end
|
||||
|
||||
local jest_symbols = {
|
||||
pass = string.char(0xE2, 0x9C, 0x93),
|
||||
fail = string.char(0xE2, 0x9C, 0x95),
|
||||
skip = string.char(0xE2, 0x97, 0x8B),
|
||||
}
|
||||
|
||||
local function shorten_js_name(name)
|
||||
if not name or name == "" then
|
||||
return nil
|
||||
end
|
||||
local last = name:match(".*>%s*([^>]+)$")
|
||||
if last then
|
||||
return last:gsub("^%s+", ""):gsub("%s+$", "")
|
||||
end
|
||||
last = name:match(".*›%s*([^›]+)$")
|
||||
if last then
|
||||
return last:gsub("^%s+", ""):gsub("%s+$", "")
|
||||
end
|
||||
last = name:match(".*»%s*([^»]+)$")
|
||||
if last then
|
||||
return last:gsub("^%s+", ""):gsub("%s+$", "")
|
||||
end
|
||||
return name
|
||||
end
|
||||
|
||||
local function trim_jest_verbose_name(name)
|
||||
if not name then
|
||||
return nil
|
||||
end
|
||||
name = name:gsub("%s+%(%d+%.?%d*%s*ms%)%s*$", "")
|
||||
name = name:gsub("%s+%(%d+%.?%d*%s*sec%)%s*$", "")
|
||||
name = name:gsub("%s+%b()%s*$", "")
|
||||
name = name:gsub("%s+$", "")
|
||||
return name
|
||||
end
|
||||
|
||||
local function parse_jest_verbose_line(line)
|
||||
if not line or line == "" then
|
||||
return nil, nil
|
||||
end
|
||||
local name = line:match("^%s*" .. jest_symbols.pass .. "%s+(.+)$")
|
||||
if name then
|
||||
return "pass", trim_jest_verbose_name(name)
|
||||
end
|
||||
name = line:match("^%s*" .. jest_symbols.fail .. "%s+(.+)$")
|
||||
if name then
|
||||
return "fail", trim_jest_verbose_name(name)
|
||||
end
|
||||
name = line:match("^%s*" .. jest_symbols.skip .. "%s+(.+)$")
|
||||
if name then
|
||||
return "skip", trim_jest_verbose_name(name)
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local function parse_mocha(output)
|
||||
@@ -448,6 +543,7 @@ function M.new(opts)
|
||||
local passes = {}
|
||||
local failures = {}
|
||||
local skips = {}
|
||||
local display = { passes = {}, failures = {}, skips = {} }
|
||||
for line in (output or ""):gmatch("[^\n]+") do
|
||||
local ok_line, entry = pcall(vim.json.decode, line)
|
||||
if ok_line and type(entry) == "table" then
|
||||
@@ -456,56 +552,89 @@ function M.new(opts)
|
||||
if not entry.event then
|
||||
payload = entry[2] or entry["2"] or {}
|
||||
end
|
||||
local title = payload.fullTitle or payload.title
|
||||
if event == "pass" and title then
|
||||
table.insert(passes, title)
|
||||
elseif event == "fail" and title then
|
||||
table.insert(failures, title)
|
||||
elseif event == "pending" and title then
|
||||
table.insert(skips, title)
|
||||
local full = payload.fullTitle or payload.title
|
||||
local short = payload.title or payload.fullTitle
|
||||
if event == "pass" and full then
|
||||
table.insert(passes, full)
|
||||
if short then
|
||||
table.insert(display.passes, short)
|
||||
end
|
||||
elseif event == "fail" and full then
|
||||
table.insert(failures, full)
|
||||
if short then
|
||||
table.insert(display.failures, short)
|
||||
end
|
||||
elseif event == "pending" and full then
|
||||
table.insert(skips, full)
|
||||
if short then
|
||||
table.insert(display.skips, short)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if #passes == 0 and #failures == 0 and #skips == 0 then
|
||||
return nil
|
||||
end
|
||||
return { passes = passes, failures = failures, skips = skips }
|
||||
return { passes = passes, failures = failures, skips = skips, display = display }
|
||||
end
|
||||
local passes = {}
|
||||
local failures = {}
|
||||
local skips = {}
|
||||
local display = { passes = {}, failures = {}, skips = {} }
|
||||
if type(data.tests) == "table" then
|
||||
for _, test in ipairs(data.tests) do
|
||||
local title = test.fullTitle or test.title
|
||||
if test.state == "passed" and title then
|
||||
table.insert(passes, title)
|
||||
elseif test.state == "failed" and title then
|
||||
table.insert(failures, title)
|
||||
elseif test.state == "pending" and title then
|
||||
table.insert(skips, title)
|
||||
local full = test.fullTitle or test.title
|
||||
local short = test.title or test.fullTitle
|
||||
if test.state == "passed" and full then
|
||||
table.insert(passes, full)
|
||||
if short then
|
||||
table.insert(display.passes, short)
|
||||
end
|
||||
elseif test.state == "failed" and full then
|
||||
table.insert(failures, full)
|
||||
if short then
|
||||
table.insert(display.failures, short)
|
||||
end
|
||||
elseif test.state == "pending" and full then
|
||||
table.insert(skips, full)
|
||||
if short then
|
||||
table.insert(display.skips, short)
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif type(data.passes) == "table" or type(data.failures) == "table" then
|
||||
for _, test in ipairs(data.passes or {}) do
|
||||
local title = test.fullTitle or test.title
|
||||
if title then
|
||||
table.insert(passes, title)
|
||||
local full = test.fullTitle or test.title
|
||||
local short = test.title or test.fullTitle
|
||||
if full then
|
||||
table.insert(passes, full)
|
||||
if short then
|
||||
table.insert(display.passes, short)
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, test in ipairs(data.failures or {}) do
|
||||
local title = test.fullTitle or test.title
|
||||
if title then
|
||||
table.insert(failures, title)
|
||||
local full = test.fullTitle or test.title
|
||||
local short = test.title or test.fullTitle
|
||||
if full then
|
||||
table.insert(failures, full)
|
||||
if short then
|
||||
table.insert(display.failures, short)
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, test in ipairs(data.pending or {}) do
|
||||
local title = test.fullTitle or test.title
|
||||
if title then
|
||||
table.insert(skips, title)
|
||||
local full = test.fullTitle or test.title
|
||||
local short = test.title or test.fullTitle
|
||||
if full then
|
||||
table.insert(skips, full)
|
||||
if short then
|
||||
table.insert(display.skips, short)
|
||||
end
|
||||
end
|
||||
end
|
||||
return { passes = passes, failures = failures, skips = skips }
|
||||
end
|
||||
return { passes = passes, failures = failures, skips = skips, display = display }
|
||||
end
|
||||
|
||||
local function parse_output(output)
|
||||
@@ -523,11 +652,137 @@ function M.new(opts)
|
||||
local state = { raw = {}, done = false, saw_stream = false }
|
||||
local failures = {}
|
||||
local skips = {}
|
||||
local jest_seen_pass = {}
|
||||
local jest_seen_fail = {}
|
||||
local jest_seen_skip = {}
|
||||
local jest_json_collecting = false
|
||||
local jest_json_buffer = {}
|
||||
local jest_streamed = false
|
||||
|
||||
local function reset_list(dst, src)
|
||||
for i = #dst, 1, -1 do
|
||||
dst[i] = nil
|
||||
end
|
||||
for _, item in ipairs(src or {}) do
|
||||
if item and item ~= "" then
|
||||
table.insert(dst, item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function emit_jest_results(results, emit_output)
|
||||
if not results then
|
||||
return nil
|
||||
end
|
||||
local display = results.display or {}
|
||||
if emit_output == false then
|
||||
reset_list(failures, results.failures or {})
|
||||
return {
|
||||
passes = {},
|
||||
failures = {},
|
||||
skips = {},
|
||||
failures_all = vim.deepcopy(failures),
|
||||
}
|
||||
end
|
||||
local out = {
|
||||
passes = {},
|
||||
failures = {},
|
||||
skips = {},
|
||||
failures_all = {},
|
||||
display = { passes = {}, failures = {}, skips = {} },
|
||||
}
|
||||
for i, title in ipairs(results.passes or {}) do
|
||||
if title and not jest_seen_pass[title] then
|
||||
jest_seen_pass[title] = true
|
||||
table.insert(out.passes, title)
|
||||
local show = display.passes and display.passes[i] or title
|
||||
table.insert(out.display.passes, show)
|
||||
end
|
||||
end
|
||||
for i, title in ipairs(results.failures or {}) do
|
||||
if title and not jest_seen_fail[title] then
|
||||
jest_seen_fail[title] = true
|
||||
table.insert(failures, title)
|
||||
table.insert(out.failures, title)
|
||||
local show = display.failures and display.failures[i] or title
|
||||
table.insert(out.display.failures, show)
|
||||
end
|
||||
end
|
||||
for i, title in ipairs(results.skips or {}) do
|
||||
if title and not jest_seen_skip[title] then
|
||||
jest_seen_skip[title] = true
|
||||
table.insert(out.skips, title)
|
||||
local show = display.skips and display.skips[i] or title
|
||||
table.insert(out.display.skips, show)
|
||||
end
|
||||
end
|
||||
out.failures_all = vim.deepcopy(failures)
|
||||
if #out.passes == 0 and #out.failures == 0 and #out.skips == 0 then
|
||||
return nil
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
local function feed_jest_json(line)
|
||||
if not jest_json_collecting then
|
||||
if not line:match("^%s*{") then
|
||||
return nil
|
||||
end
|
||||
jest_json_collecting = true
|
||||
jest_json_buffer = { line }
|
||||
else
|
||||
table.insert(jest_json_buffer, line)
|
||||
end
|
||||
local candidate = table.concat(jest_json_buffer, "\n")
|
||||
local ok_buf, data_buf = pcall(vim.json.decode, candidate)
|
||||
if ok_buf and type(data_buf) == "table" then
|
||||
jest_json_collecting = false
|
||||
jest_json_buffer = {}
|
||||
return data_buf
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function handle_jest_json(data)
|
||||
local results = parse_jest_results(data)
|
||||
if not results then
|
||||
return nil
|
||||
end
|
||||
state.done = true
|
||||
state.saw_stream = true
|
||||
return emit_jest_results(results, not jest_streamed)
|
||||
end
|
||||
return {
|
||||
on_line = function(line, _state)
|
||||
if state.done then
|
||||
return nil
|
||||
end
|
||||
if runner.framework == "jest" then
|
||||
local kind, name = parse_jest_verbose_line(line)
|
||||
if kind and name then
|
||||
jest_streamed = true
|
||||
local results = { passes = {}, failures = {}, skips = {} }
|
||||
if kind == "pass" then
|
||||
results.passes = { name }
|
||||
elseif kind == "fail" then
|
||||
results.failures = { name }
|
||||
elseif kind == "skip" then
|
||||
results.skips = { name }
|
||||
end
|
||||
results.display = {
|
||||
passes = kind == "pass" and { name } or {},
|
||||
failures = kind == "fail" and { name } or {},
|
||||
skips = kind == "skip" and { name } or {},
|
||||
}
|
||||
return emit_jest_results(results, true)
|
||||
end
|
||||
|
||||
local data = feed_jest_json(line)
|
||||
if data then
|
||||
return handle_jest_json(data)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
if runner.framework == "mocha" and runner.json_args then
|
||||
local uses_stream = false
|
||||
for i = 1, #runner.json_args - 1 do
|
||||
@@ -551,6 +806,11 @@ function M.new(opts)
|
||||
passes = { payload.fullTitle },
|
||||
failures = {},
|
||||
skips = {},
|
||||
display = {
|
||||
passes = { payload.title or payload.fullTitle },
|
||||
failures = {},
|
||||
skips = {},
|
||||
},
|
||||
failures_all = vim.deepcopy(failures),
|
||||
}
|
||||
elseif event == "fail" and payload.fullTitle then
|
||||
@@ -560,6 +820,11 @@ function M.new(opts)
|
||||
passes = {},
|
||||
failures = { payload.fullTitle },
|
||||
skips = {},
|
||||
display = {
|
||||
passes = {},
|
||||
failures = { payload.title or payload.fullTitle },
|
||||
skips = {},
|
||||
},
|
||||
failures_all = vim.deepcopy(failures),
|
||||
}
|
||||
elseif event == "pending" and payload.fullTitle then
|
||||
@@ -569,6 +834,11 @@ function M.new(opts)
|
||||
passes = {},
|
||||
failures = {},
|
||||
skips = { payload.fullTitle },
|
||||
display = {
|
||||
passes = {},
|
||||
failures = {},
|
||||
skips = { payload.title or payload.fullTitle },
|
||||
},
|
||||
failures_all = vim.deepcopy(failures),
|
||||
}
|
||||
elseif event == "start" or event == "end" then
|
||||
@@ -599,29 +869,48 @@ function M.new(opts)
|
||||
if skip_title then
|
||||
skip_title = skip_title:gsub("%s+$", "")
|
||||
table.insert(skips, skip_title)
|
||||
state.saw_stream = true
|
||||
return {
|
||||
passes = {},
|
||||
failures = {},
|
||||
skips = { skip_title },
|
||||
display = {
|
||||
passes = {},
|
||||
failures = {},
|
||||
skips = { shorten_js_name(skip_title) or skip_title },
|
||||
},
|
||||
failures_all = vim.deepcopy(failures),
|
||||
}
|
||||
end
|
||||
ok_title = ok_title:gsub("%s+#%s+time=.*$", "")
|
||||
state.saw_stream = true
|
||||
return {
|
||||
passes = { ok_title },
|
||||
failures = {},
|
||||
skips = {},
|
||||
display = {
|
||||
passes = { shorten_js_name(ok_title) or ok_title },
|
||||
failures = {},
|
||||
skips = {},
|
||||
},
|
||||
failures_all = vim.deepcopy(failures),
|
||||
}
|
||||
end
|
||||
local fail_title = line:match("^not ok%s+%d+%s+%-%s+(.+)")
|
||||
if fail_title then
|
||||
fail_title = fail_title:gsub("%s+#%s+time=.*$", "")
|
||||
local display_name = shorten_js_name(fail_title)
|
||||
table.insert(failures, fail_title)
|
||||
state.saw_stream = true
|
||||
return {
|
||||
passes = {},
|
||||
failures = { fail_title },
|
||||
skips = {},
|
||||
display = {
|
||||
passes = {},
|
||||
failures = { display_name or fail_title },
|
||||
skips = {},
|
||||
},
|
||||
failures_all = vim.deepcopy(failures),
|
||||
}
|
||||
end
|
||||
@@ -637,6 +926,18 @@ function M.new(opts)
|
||||
return results
|
||||
end,
|
||||
on_complete = function(output, _state)
|
||||
if runner.framework == "jest" then
|
||||
if state.done then
|
||||
return nil
|
||||
end
|
||||
local results = parse_jest_like(output)
|
||||
if results then
|
||||
state.done = true
|
||||
state.saw_stream = true
|
||||
return emit_jest_results(results, not jest_streamed)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
if state.done or state.saw_stream then
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -91,11 +91,11 @@ describe("TSamFailedOnly", function()
|
||||
|
||||
assert.equals(2, #calls)
|
||||
assert.are.same(
|
||||
{ "npx", "jest", "--json", "/tmp/project/foo_failed_only.test.ts", "-t", "inner 2" },
|
||||
{ "npx", "jest", "--json", "--verbose", "/tmp/project/foo_failed_only.test.ts", "-t", "inner 2" },
|
||||
calls[1].cmd
|
||||
)
|
||||
assert.are.same(
|
||||
{ "npx", "jest", "--json", "-t", "outer inner 2", "/tmp/project/foo_failed_only.test.ts" },
|
||||
{ "npx", "jest", "--json", "--verbose", "-t", "outer inner 2", "/tmp/project/foo_failed_only.test.ts" },
|
||||
calls[2].cmd
|
||||
)
|
||||
end)
|
||||
@@ -332,7 +332,7 @@ describe("TSamFailedOnly", function()
|
||||
assert.equals(3, #calls)
|
||||
assert.are.same(calls[1].cmd, calls[3].cmd)
|
||||
assert.are.same(
|
||||
{ "npx", "jest", "--json", "-t", "outer inner 2", "/tmp/project/foo_failed_only_last.test.ts" },
|
||||
{ "npx", "jest", "--json", "--verbose", "-t", "outer inner 2", "/tmp/project/foo_failed_only_last.test.ts" },
|
||||
calls[2].cmd
|
||||
)
|
||||
end)
|
||||
|
||||
@@ -52,7 +52,7 @@ describe("test-samurai js runner (jest)", function()
|
||||
assert.is_true(spec.cwd:match("tmp$") ~= nil)
|
||||
|
||||
local cmd_spec = jest.build_command(spec)
|
||||
assert.are.same({ "npx", "jest", "--json", spec.file, "-t", "inner 2" }, cmd_spec.cmd)
|
||||
assert.are.same({ "npx", "jest", "--json", "--verbose", spec.file, "-t", "inner 2" }, cmd_spec.cmd)
|
||||
end)
|
||||
|
||||
it("returns describe block when cursor is between it() calls", function()
|
||||
|
||||
@@ -130,7 +130,7 @@ describe("TSamLast", function()
|
||||
|
||||
assert.equals(2, #calls)
|
||||
assert.are.same(
|
||||
{ "npx", "jest", "--json", "/tmp/project/foo_last.test.ts", "-t", "inner 2" },
|
||||
{ "npx", "jest", "--json", "--verbose", "/tmp/project/foo_last.test.ts", "-t", "inner 2" },
|
||||
calls[1].cmd
|
||||
)
|
||||
assert.are.same(calls[1].cmd, calls[2].cmd)
|
||||
|
||||
@@ -151,11 +151,11 @@ describe("test-samurai output formatting", function()
|
||||
local has_skip = false
|
||||
local has_fail = false
|
||||
for _, line in ipairs(lines) do
|
||||
if line == "[ PASS ] - outer inner 1" then
|
||||
if line == "[ PASS ] - inner 1" then
|
||||
has_pass = true
|
||||
elseif line == "[ SKIP ] - outer inner skip" then
|
||||
elseif line == "[ SKIP ] - inner skip" then
|
||||
has_skip = true
|
||||
elseif line == "[ FAIL ] - outer inner 2" then
|
||||
elseif line == "[ FAIL ] - inner 2" then
|
||||
has_fail = true
|
||||
end
|
||||
end
|
||||
@@ -218,6 +218,186 @@ describe("test-samurai output formatting", function()
|
||||
assert.is_false(has_raw_json)
|
||||
end)
|
||||
|
||||
it("formats multi-line jest JSON output and ignores non-JSON lines", function()
|
||||
local lines_out = {
|
||||
"Some log line",
|
||||
"{",
|
||||
' "testResults": [',
|
||||
" {",
|
||||
' "assertionResults": [',
|
||||
' {"status":"passed","title":"inner 1","fullName":"outer inner 1"}',
|
||||
" ]",
|
||||
" }",
|
||||
" ]",
|
||||
"}",
|
||||
}
|
||||
|
||||
local orig_jobstart = vim.fn.jobstart
|
||||
vim.fn.jobstart = function(_cmd, opts)
|
||||
if opts and opts.on_stdout then
|
||||
opts.on_stdout(1, lines_out, nil)
|
||||
end
|
||||
if opts and opts.on_exit then
|
||||
opts.on_exit(1, 0, nil)
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_name(bufnr, "/tmp/output_multiline_json.test.ts")
|
||||
vim.bo[bufnr].filetype = "typescript"
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||
'describe("outer", function() {',
|
||||
' it("inner 1", function() {',
|
||||
" -- inside 1",
|
||||
" })",
|
||||
"})",
|
||||
})
|
||||
|
||||
vim.api.nvim_set_current_buf(bufnr)
|
||||
vim.api.nvim_win_set_cursor(0, { 3, 0 })
|
||||
|
||||
core.run_nearest()
|
||||
|
||||
local out_buf = vim.api.nvim_get_current_buf()
|
||||
local lines = vim.api.nvim_buf_get_lines(out_buf, 0, -1, false)
|
||||
|
||||
vim.fn.jobstart = orig_jobstart
|
||||
|
||||
local has_pass = false
|
||||
local has_raw_json = false
|
||||
for _, line in ipairs(lines) do
|
||||
if line == "[ PASS ] - inner 1" then
|
||||
has_pass = true
|
||||
elseif line == ' "testResults": [' then
|
||||
has_raw_json = true
|
||||
end
|
||||
end
|
||||
assert.is_true(has_pass)
|
||||
assert.is_false(has_raw_json)
|
||||
end)
|
||||
|
||||
it("formats jest verbose output as PASS/FAIL/SKIP lines", function()
|
||||
local check = string.char(0xE2, 0x9C, 0x93)
|
||||
local cross = string.char(0xE2, 0x9C, 0x95)
|
||||
local circle = string.char(0xE2, 0x97, 0x8B)
|
||||
|
||||
local orig_jobstart = vim.fn.jobstart
|
||||
vim.fn.jobstart = function(_cmd, opts)
|
||||
if opts and opts.on_stdout then
|
||||
opts.on_stdout(1, {
|
||||
" PASS /tmp/output_verbose.test.ts",
|
||||
" " .. check .. " inner 1 (5 ms)",
|
||||
" " .. circle .. " inner skip (skipped)",
|
||||
" " .. cross .. " inner 2 (1 ms)",
|
||||
}, nil)
|
||||
end
|
||||
if opts and opts.on_exit then
|
||||
opts.on_exit(1, 1, nil)
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_name(bufnr, "/tmp/output_verbose.test.ts")
|
||||
vim.bo[bufnr].filetype = "typescript"
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||
'describe("outer", function() {',
|
||||
' it("inner 1", function() {',
|
||||
" -- inside 1",
|
||||
" })",
|
||||
"",
|
||||
' it("inner 2", function() {',
|
||||
" -- inside 2",
|
||||
" })",
|
||||
"})",
|
||||
})
|
||||
|
||||
vim.api.nvim_set_current_buf(bufnr)
|
||||
vim.api.nvim_win_set_cursor(0, { 7, 0 })
|
||||
|
||||
core.run_all()
|
||||
|
||||
local out_buf = vim.api.nvim_get_current_buf()
|
||||
local lines = vim.api.nvim_buf_get_lines(out_buf, 0, -1, false)
|
||||
|
||||
vim.fn.jobstart = orig_jobstart
|
||||
|
||||
local has_pass = false
|
||||
local has_skip = false
|
||||
local has_fail = false
|
||||
local has_raw_verbose = false
|
||||
for _, line in ipairs(lines) do
|
||||
if line == "[ PASS ] - inner 1" then
|
||||
has_pass = true
|
||||
elseif line == "[ SKIP ] - inner skip" then
|
||||
has_skip = true
|
||||
elseif line == "[ FAIL ] - inner 2" then
|
||||
has_fail = true
|
||||
elseif line:match("^%s*PASS%s+") then
|
||||
has_raw_verbose = true
|
||||
end
|
||||
end
|
||||
assert.is_true(has_pass)
|
||||
assert.is_true(has_skip)
|
||||
assert.is_true(has_fail)
|
||||
assert.is_false(has_raw_verbose)
|
||||
end)
|
||||
|
||||
it("formats go subtests as short names", function()
|
||||
local json_line = vim.json.encode({
|
||||
Action = "pass",
|
||||
Test = "TestHandleGet/returns_200",
|
||||
})
|
||||
|
||||
local orig_jobstart = vim.fn.jobstart
|
||||
vim.fn.jobstart = function(_cmd, opts)
|
||||
if opts and opts.on_stdout then
|
||||
opts.on_stdout(1, { json_line }, nil)
|
||||
end
|
||||
if opts and opts.on_exit then
|
||||
opts.on_exit(1, 0, nil)
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_name(bufnr, "/tmp/output_go_short_test.go")
|
||||
vim.bo[bufnr].filetype = "go"
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||
"package main",
|
||||
"import \"testing\"",
|
||||
"",
|
||||
"func TestHandleGet(t *testing.T) {",
|
||||
" t.Run(\"returns_200\", func(t *testing.T) {",
|
||||
" -- inside test",
|
||||
" })",
|
||||
"}",
|
||||
})
|
||||
|
||||
vim.api.nvim_set_current_buf(bufnr)
|
||||
vim.api.nvim_win_set_cursor(0, { 6, 0 })
|
||||
|
||||
core.run_nearest()
|
||||
|
||||
local out_buf = vim.api.nvim_get_current_buf()
|
||||
local lines = vim.api.nvim_buf_get_lines(out_buf, 0, -1, false)
|
||||
|
||||
vim.fn.jobstart = orig_jobstart
|
||||
|
||||
local has_pass = false
|
||||
local has_raw_json = false
|
||||
for _, line in ipairs(lines) do
|
||||
if line == "[ PASS ] - returns_200" then
|
||||
has_pass = true
|
||||
elseif line == json_line then
|
||||
has_raw_json = true
|
||||
end
|
||||
end
|
||||
assert.is_true(has_pass)
|
||||
assert.is_false(has_raw_json)
|
||||
end)
|
||||
|
||||
it("does not print raw JSON output for mocha json-stream", function()
|
||||
test_samurai.setup({
|
||||
runner_modules = {
|
||||
@@ -323,7 +503,7 @@ describe("test-samurai output formatting", function()
|
||||
local has_pass = false
|
||||
local has_raw_json = false
|
||||
for _, line in ipairs(lines) do
|
||||
if line == "[ PASS ] - API :: /brands... GET: /" then
|
||||
if line == "[ PASS ] - GET: /" then
|
||||
has_pass = true
|
||||
elseif line == pass_line then
|
||||
has_raw_json = true
|
||||
@@ -419,10 +599,12 @@ describe("test-samurai output formatting", function()
|
||||
|
||||
local pass_line = vim.json.encode({
|
||||
event = "pass",
|
||||
title = "inner 1",
|
||||
fullTitle = "outer inner 1",
|
||||
})
|
||||
local fail_line = vim.json.encode({
|
||||
event = "fail",
|
||||
title = "inner 2",
|
||||
fullTitle = "outer inner 2",
|
||||
})
|
||||
|
||||
@@ -475,9 +657,9 @@ describe("test-samurai output formatting", function()
|
||||
local has_pass = false
|
||||
local has_fail = false
|
||||
for _, line in ipairs(lines) do
|
||||
if line == "[ PASS ] - outer inner 1" then
|
||||
if line == "[ PASS ] - inner 1" then
|
||||
has_pass = true
|
||||
elseif line == "[ FAIL ] - outer inner 2" then
|
||||
elseif line == "[ FAIL ] - inner 2" then
|
||||
has_fail = true
|
||||
end
|
||||
end
|
||||
@@ -516,9 +698,9 @@ describe("test-samurai output formatting", function()
|
||||
opts.on_stdout(1, {
|
||||
"TAP version 13",
|
||||
"1..2",
|
||||
"ok 1 - outer inner 1 # time=1.00ms",
|
||||
"ok 2 - outer inner skip # SKIP not now",
|
||||
"not ok 2 - outer inner 2 # time=2.00ms",
|
||||
"ok 1 - outer > inner 1 # time=1.00ms",
|
||||
"ok 2 - outer > inner skip # SKIP not now",
|
||||
"not ok 2 - outer > inner 2 # time=2.00ms",
|
||||
}, nil)
|
||||
end
|
||||
if opts and opts.on_exit then
|
||||
@@ -556,11 +738,11 @@ describe("test-samurai output formatting", function()
|
||||
local has_skip = false
|
||||
local has_fail = false
|
||||
for _, line in ipairs(lines) do
|
||||
if line == "[ PASS ] - outer inner 1" then
|
||||
if line == "[ PASS ] - inner 1" then
|
||||
has_pass = true
|
||||
elseif line == "[ SKIP ] - outer inner skip" then
|
||||
elseif line == "[ SKIP ] - inner skip" then
|
||||
has_skip = true
|
||||
elseif line == "[ FAIL ] - outer inner 2" then
|
||||
elseif line == "[ FAIL ] - inner 2" then
|
||||
has_fail = true
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user