@hasna/terminal 2.3.0 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +64 -16
- package/package.json +1 -1
- package/src/ai.ts +8 -0
- package/src/cli.tsx +57 -18
- package/src/output-processor.ts +6 -1
- package/src/output-store.ts +58 -12
- package/src/tool-profiles.ts +139 -0
- package/temp/rtk/.claude/agents/code-reviewer.md +0 -221
- package/temp/rtk/.claude/agents/debugger.md +0 -519
- package/temp/rtk/.claude/agents/rtk-testing-specialist.md +0 -461
- package/temp/rtk/.claude/agents/rust-rtk.md +0 -511
- package/temp/rtk/.claude/agents/technical-writer.md +0 -355
- package/temp/rtk/.claude/commands/diagnose.md +0 -352
- package/temp/rtk/.claude/commands/test-routing.md +0 -362
- package/temp/rtk/.claude/hooks/bash/pre-commit-format.sh +0 -16
- package/temp/rtk/.claude/hooks/rtk-rewrite.sh +0 -70
- package/temp/rtk/.claude/hooks/rtk-suggest.sh +0 -152
- package/temp/rtk/.claude/rules/cli-testing.md +0 -526
- package/temp/rtk/.claude/skills/issue-triage/SKILL.md +0 -348
- package/temp/rtk/.claude/skills/issue-triage/templates/issue-comment.md +0 -134
- package/temp/rtk/.claude/skills/performance.md +0 -435
- package/temp/rtk/.claude/skills/pr-triage/SKILL.md +0 -315
- package/temp/rtk/.claude/skills/pr-triage/templates/review-comment.md +0 -71
- package/temp/rtk/.claude/skills/repo-recap.md +0 -206
- package/temp/rtk/.claude/skills/rtk-tdd/SKILL.md +0 -78
- package/temp/rtk/.claude/skills/rtk-tdd/references/testing-patterns.md +0 -124
- package/temp/rtk/.claude/skills/security-guardian.md +0 -503
- package/temp/rtk/.claude/skills/ship.md +0 -404
- package/temp/rtk/.github/workflows/benchmark.yml +0 -34
- package/temp/rtk/.github/workflows/dco-check.yaml +0 -12
- package/temp/rtk/.github/workflows/release-please.yml +0 -51
- package/temp/rtk/.github/workflows/release.yml +0 -343
- package/temp/rtk/.github/workflows/security-check.yml +0 -135
- package/temp/rtk/.github/workflows/validate-docs.yml +0 -78
- package/temp/rtk/.release-please-manifest.json +0 -3
- package/temp/rtk/ARCHITECTURE.md +0 -1491
- package/temp/rtk/CHANGELOG.md +0 -640
- package/temp/rtk/CLAUDE.md +0 -605
- package/temp/rtk/CONTRIBUTING.md +0 -199
- package/temp/rtk/Cargo.lock +0 -1668
- package/temp/rtk/Cargo.toml +0 -64
- package/temp/rtk/Formula/rtk.rb +0 -43
- package/temp/rtk/INSTALL.md +0 -390
- package/temp/rtk/LICENSE +0 -21
- package/temp/rtk/README.md +0 -386
- package/temp/rtk/README_es.md +0 -159
- package/temp/rtk/README_fr.md +0 -197
- package/temp/rtk/README_ja.md +0 -159
- package/temp/rtk/README_ko.md +0 -159
- package/temp/rtk/README_zh.md +0 -167
- package/temp/rtk/ROADMAP.md +0 -15
- package/temp/rtk/SECURITY.md +0 -217
- package/temp/rtk/TEST_EXEC_TIME.md +0 -102
- package/temp/rtk/build.rs +0 -57
- package/temp/rtk/docs/AUDIT_GUIDE.md +0 -432
- package/temp/rtk/docs/FEATURES.md +0 -1410
- package/temp/rtk/docs/TROUBLESHOOTING.md +0 -309
- package/temp/rtk/docs/filter-workflow.md +0 -102
- package/temp/rtk/docs/images/gain-dashboard.jpg +0 -0
- package/temp/rtk/docs/tracking.md +0 -583
- package/temp/rtk/hooks/opencode-rtk.ts +0 -39
- package/temp/rtk/hooks/rtk-awareness.md +0 -29
- package/temp/rtk/hooks/rtk-rewrite.sh +0 -61
- package/temp/rtk/hooks/test-rtk-rewrite.sh +0 -442
- package/temp/rtk/install.sh +0 -124
- package/temp/rtk/release-please-config.json +0 -10
- package/temp/rtk/scripts/benchmark.sh +0 -592
- package/temp/rtk/scripts/check-installation.sh +0 -162
- package/temp/rtk/scripts/install-local.sh +0 -37
- package/temp/rtk/scripts/rtk-economics.sh +0 -137
- package/temp/rtk/scripts/test-all.sh +0 -561
- package/temp/rtk/scripts/test-aristote.sh +0 -227
- package/temp/rtk/scripts/test-tracking.sh +0 -79
- package/temp/rtk/scripts/update-readme-metrics.sh +0 -32
- package/temp/rtk/scripts/validate-docs.sh +0 -73
- package/temp/rtk/src/aws_cmd.rs +0 -880
- package/temp/rtk/src/binlog.rs +0 -1645
- package/temp/rtk/src/cargo_cmd.rs +0 -1727
- package/temp/rtk/src/cc_economics.rs +0 -1157
- package/temp/rtk/src/ccusage.rs +0 -340
- package/temp/rtk/src/config.rs +0 -187
- package/temp/rtk/src/container.rs +0 -855
- package/temp/rtk/src/curl_cmd.rs +0 -134
- package/temp/rtk/src/deps.rs +0 -268
- package/temp/rtk/src/diff_cmd.rs +0 -367
- package/temp/rtk/src/discover/mod.rs +0 -274
- package/temp/rtk/src/discover/provider.rs +0 -388
- package/temp/rtk/src/discover/registry.rs +0 -2022
- package/temp/rtk/src/discover/report.rs +0 -202
- package/temp/rtk/src/discover/rules.rs +0 -667
- package/temp/rtk/src/display_helpers.rs +0 -402
- package/temp/rtk/src/dotnet_cmd.rs +0 -1771
- package/temp/rtk/src/dotnet_format_report.rs +0 -133
- package/temp/rtk/src/dotnet_trx.rs +0 -593
- package/temp/rtk/src/env_cmd.rs +0 -204
- package/temp/rtk/src/filter.rs +0 -462
- package/temp/rtk/src/filters/README.md +0 -52
- package/temp/rtk/src/filters/ansible-playbook.toml +0 -34
- package/temp/rtk/src/filters/basedpyright.toml +0 -47
- package/temp/rtk/src/filters/biome.toml +0 -45
- package/temp/rtk/src/filters/brew-install.toml +0 -37
- package/temp/rtk/src/filters/composer-install.toml +0 -40
- package/temp/rtk/src/filters/df.toml +0 -16
- package/temp/rtk/src/filters/dotnet-build.toml +0 -64
- package/temp/rtk/src/filters/du.toml +0 -16
- package/temp/rtk/src/filters/fail2ban-client.toml +0 -15
- package/temp/rtk/src/filters/gcc.toml +0 -49
- package/temp/rtk/src/filters/gcloud.toml +0 -22
- package/temp/rtk/src/filters/hadolint.toml +0 -24
- package/temp/rtk/src/filters/helm.toml +0 -29
- package/temp/rtk/src/filters/iptables.toml +0 -27
- package/temp/rtk/src/filters/jj.toml +0 -28
- package/temp/rtk/src/filters/jq.toml +0 -24
- package/temp/rtk/src/filters/make.toml +0 -41
- package/temp/rtk/src/filters/markdownlint.toml +0 -24
- package/temp/rtk/src/filters/mix-compile.toml +0 -27
- package/temp/rtk/src/filters/mix-format.toml +0 -15
- package/temp/rtk/src/filters/mvn-build.toml +0 -44
- package/temp/rtk/src/filters/oxlint.toml +0 -43
- package/temp/rtk/src/filters/ping.toml +0 -63
- package/temp/rtk/src/filters/pio-run.toml +0 -40
- package/temp/rtk/src/filters/poetry-install.toml +0 -50
- package/temp/rtk/src/filters/pre-commit.toml +0 -35
- package/temp/rtk/src/filters/ps.toml +0 -16
- package/temp/rtk/src/filters/quarto-render.toml +0 -41
- package/temp/rtk/src/filters/rsync.toml +0 -48
- package/temp/rtk/src/filters/shellcheck.toml +0 -27
- package/temp/rtk/src/filters/shopify-theme.toml +0 -29
- package/temp/rtk/src/filters/skopeo.toml +0 -45
- package/temp/rtk/src/filters/sops.toml +0 -16
- package/temp/rtk/src/filters/ssh.toml +0 -44
- package/temp/rtk/src/filters/stat.toml +0 -34
- package/temp/rtk/src/filters/swift-build.toml +0 -41
- package/temp/rtk/src/filters/systemctl-status.toml +0 -33
- package/temp/rtk/src/filters/terraform-plan.toml +0 -35
- package/temp/rtk/src/filters/tofu-fmt.toml +0 -16
- package/temp/rtk/src/filters/tofu-init.toml +0 -38
- package/temp/rtk/src/filters/tofu-plan.toml +0 -35
- package/temp/rtk/src/filters/tofu-validate.toml +0 -17
- package/temp/rtk/src/filters/trunk-build.toml +0 -39
- package/temp/rtk/src/filters/ty.toml +0 -50
- package/temp/rtk/src/filters/uv-sync.toml +0 -37
- package/temp/rtk/src/filters/xcodebuild.toml +0 -99
- package/temp/rtk/src/filters/yamllint.toml +0 -25
- package/temp/rtk/src/find_cmd.rs +0 -598
- package/temp/rtk/src/format_cmd.rs +0 -386
- package/temp/rtk/src/gain.rs +0 -723
- package/temp/rtk/src/gh_cmd.rs +0 -1651
- package/temp/rtk/src/git.rs +0 -2012
- package/temp/rtk/src/go_cmd.rs +0 -592
- package/temp/rtk/src/golangci_cmd.rs +0 -254
- package/temp/rtk/src/grep_cmd.rs +0 -288
- package/temp/rtk/src/gt_cmd.rs +0 -810
- package/temp/rtk/src/hook_audit_cmd.rs +0 -283
- package/temp/rtk/src/hook_check.rs +0 -171
- package/temp/rtk/src/init.rs +0 -1859
- package/temp/rtk/src/integrity.rs +0 -537
- package/temp/rtk/src/json_cmd.rs +0 -231
- package/temp/rtk/src/learn/detector.rs +0 -628
- package/temp/rtk/src/learn/mod.rs +0 -119
- package/temp/rtk/src/learn/report.rs +0 -184
- package/temp/rtk/src/lint_cmd.rs +0 -694
- package/temp/rtk/src/local_llm.rs +0 -316
- package/temp/rtk/src/log_cmd.rs +0 -248
- package/temp/rtk/src/ls.rs +0 -324
- package/temp/rtk/src/main.rs +0 -2482
- package/temp/rtk/src/mypy_cmd.rs +0 -389
- package/temp/rtk/src/next_cmd.rs +0 -241
- package/temp/rtk/src/npm_cmd.rs +0 -236
- package/temp/rtk/src/parser/README.md +0 -267
- package/temp/rtk/src/parser/error.rs +0 -46
- package/temp/rtk/src/parser/formatter.rs +0 -336
- package/temp/rtk/src/parser/mod.rs +0 -311
- package/temp/rtk/src/parser/types.rs +0 -119
- package/temp/rtk/src/pip_cmd.rs +0 -302
- package/temp/rtk/src/playwright_cmd.rs +0 -479
- package/temp/rtk/src/pnpm_cmd.rs +0 -573
- package/temp/rtk/src/prettier_cmd.rs +0 -221
- package/temp/rtk/src/prisma_cmd.rs +0 -482
- package/temp/rtk/src/psql_cmd.rs +0 -382
- package/temp/rtk/src/pytest_cmd.rs +0 -384
- package/temp/rtk/src/read.rs +0 -217
- package/temp/rtk/src/rewrite_cmd.rs +0 -50
- package/temp/rtk/src/ruff_cmd.rs +0 -402
- package/temp/rtk/src/runner.rs +0 -271
- package/temp/rtk/src/summary.rs +0 -297
- package/temp/rtk/src/tee.rs +0 -405
- package/temp/rtk/src/telemetry.rs +0 -248
- package/temp/rtk/src/toml_filter.rs +0 -1655
- package/temp/rtk/src/tracking.rs +0 -1416
- package/temp/rtk/src/tree.rs +0 -209
- package/temp/rtk/src/tsc_cmd.rs +0 -259
- package/temp/rtk/src/utils.rs +0 -432
- package/temp/rtk/src/verify_cmd.rs +0 -47
- package/temp/rtk/src/vitest_cmd.rs +0 -385
- package/temp/rtk/src/wc_cmd.rs +0 -401
- package/temp/rtk/src/wget_cmd.rs +0 -260
- package/temp/rtk/tests/fixtures/dotnet/build_failed.txt +0 -11
- package/temp/rtk/tests/fixtures/dotnet/format_changes.json +0 -31
- package/temp/rtk/tests/fixtures/dotnet/format_empty.json +0 -1
- package/temp/rtk/tests/fixtures/dotnet/format_success.json +0 -12
- package/temp/rtk/tests/fixtures/dotnet/test_failed.txt +0 -18
|
@@ -1,385 +0,0 @@
|
|
|
1
|
-
use anyhow::{Context, Result};
|
|
2
|
-
use regex::Regex;
|
|
3
|
-
use serde::Deserialize;
|
|
4
|
-
|
|
5
|
-
use crate::parser::{
|
|
6
|
-
emit_degradation_warning, emit_passthrough_warning, extract_json_object, truncate_output,
|
|
7
|
-
FormatMode, OutputParser, ParseResult, TestFailure, TestResult, TokenFormatter,
|
|
8
|
-
};
|
|
9
|
-
use crate::tracking;
|
|
10
|
-
use crate::utils::{package_manager_exec, strip_ansi};
|
|
11
|
-
|
|
12
|
-
/// Vitest JSON output structures (tool-specific format)
|
|
13
|
-
#[derive(Debug, Deserialize)]
|
|
14
|
-
struct VitestJsonOutput {
|
|
15
|
-
#[serde(rename = "testResults")]
|
|
16
|
-
test_results: Vec<VitestTestFile>,
|
|
17
|
-
#[serde(rename = "numTotalTests")]
|
|
18
|
-
num_total_tests: usize,
|
|
19
|
-
#[serde(rename = "numPassedTests")]
|
|
20
|
-
num_passed_tests: usize,
|
|
21
|
-
#[serde(rename = "numFailedTests")]
|
|
22
|
-
num_failed_tests: usize,
|
|
23
|
-
#[serde(rename = "numPendingTests", default)]
|
|
24
|
-
num_pending_tests: usize,
|
|
25
|
-
#[serde(rename = "startTime")]
|
|
26
|
-
start_time: Option<u64>,
|
|
27
|
-
#[serde(rename = "endTime")]
|
|
28
|
-
end_time: Option<u64>,
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
#[derive(Debug, Deserialize)]
|
|
32
|
-
struct VitestTestFile {
|
|
33
|
-
name: String,
|
|
34
|
-
#[serde(rename = "assertionResults")]
|
|
35
|
-
assertion_results: Vec<VitestTest>,
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
#[derive(Debug, Deserialize)]
|
|
39
|
-
struct VitestTest {
|
|
40
|
-
#[serde(rename = "fullName")]
|
|
41
|
-
full_name: String,
|
|
42
|
-
status: String,
|
|
43
|
-
#[serde(rename = "failureMessages")]
|
|
44
|
-
failure_messages: Vec<String>,
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/// Parser for Vitest JSON output
|
|
48
|
-
pub struct VitestParser;
|
|
49
|
-
|
|
50
|
-
impl OutputParser for VitestParser {
|
|
51
|
-
type Output = TestResult;
|
|
52
|
-
|
|
53
|
-
fn parse(input: &str) -> ParseResult<TestResult> {
|
|
54
|
-
// Tier 1: Try JSON parsing (with extraction fallback for pnpm/dotenv prefixes)
|
|
55
|
-
let json_result = serde_json::from_str::<VitestJsonOutput>(input).or_else(|first_err| {
|
|
56
|
-
// Fallback: Try extracting JSON object from prefixed output
|
|
57
|
-
if let Some(extracted) = extract_json_object(input) {
|
|
58
|
-
serde_json::from_str::<VitestJsonOutput>(extracted)
|
|
59
|
-
} else {
|
|
60
|
-
Err(first_err)
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
match json_result {
|
|
65
|
-
Ok(json) => {
|
|
66
|
-
let failures = extract_failures_from_json(&json);
|
|
67
|
-
let duration_ms = match (json.start_time, json.end_time) {
|
|
68
|
-
(Some(start), Some(end)) => Some(end.saturating_sub(start)),
|
|
69
|
-
_ => None,
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
let result = TestResult {
|
|
73
|
-
total: json.num_total_tests,
|
|
74
|
-
passed: json.num_passed_tests,
|
|
75
|
-
failed: json.num_failed_tests,
|
|
76
|
-
skipped: json.num_pending_tests,
|
|
77
|
-
duration_ms,
|
|
78
|
-
failures,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
ParseResult::Full(result)
|
|
82
|
-
}
|
|
83
|
-
Err(e) => {
|
|
84
|
-
// Tier 2: Try regex extraction (only fires if user overrides --reporter flag)
|
|
85
|
-
match extract_stats_regex(input) {
|
|
86
|
-
Some(result) => {
|
|
87
|
-
ParseResult::Degraded(result, vec![format!("JSON parse failed: {}", e)])
|
|
88
|
-
}
|
|
89
|
-
None => {
|
|
90
|
-
// Tier 3: Passthrough
|
|
91
|
-
ParseResult::Passthrough(truncate_output(input, 500))
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/// Extract failures from JSON structure
|
|
100
|
-
fn extract_failures_from_json(json: &VitestJsonOutput) -> Vec<TestFailure> {
|
|
101
|
-
let mut failures = Vec::new();
|
|
102
|
-
|
|
103
|
-
for file in &json.test_results {
|
|
104
|
-
for test in &file.assertion_results {
|
|
105
|
-
if test.status == "failed" {
|
|
106
|
-
let error_message = test.failure_messages.join("\n");
|
|
107
|
-
failures.push(TestFailure {
|
|
108
|
-
test_name: test.full_name.clone(),
|
|
109
|
-
file_path: file.name.clone(),
|
|
110
|
-
error_message,
|
|
111
|
-
stack_trace: None,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
failures
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/// Tier 2: Extract test statistics using regex (degraded mode)
|
|
121
|
-
fn extract_stats_regex(output: &str) -> Option<TestResult> {
|
|
122
|
-
lazy_static::lazy_static! {
|
|
123
|
-
static ref TEST_FILES_RE: Regex = Regex::new(
|
|
124
|
-
r"Test Files\s+(?:(\d+)\s+failed\s+\|\s+)?(\d+)\s+passed"
|
|
125
|
-
).unwrap();
|
|
126
|
-
static ref TESTS_RE: Regex = Regex::new(
|
|
127
|
-
r"Tests\s+(?:(\d+)\s+failed\s+\|\s+)?(\d+)\s+passed"
|
|
128
|
-
).unwrap();
|
|
129
|
-
static ref DURATION_RE: Regex = Regex::new(
|
|
130
|
-
r"Duration\s+([\d.]+)(ms|s)"
|
|
131
|
-
).unwrap();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
let clean_output = strip_ansi(output);
|
|
135
|
-
|
|
136
|
-
let mut passed = 0;
|
|
137
|
-
let mut failed = 0;
|
|
138
|
-
let mut total = 0;
|
|
139
|
-
|
|
140
|
-
// Parse test counts
|
|
141
|
-
if let Some(caps) = TESTS_RE.captures(&clean_output) {
|
|
142
|
-
if let Some(fail_str) = caps.get(1) {
|
|
143
|
-
failed = fail_str.as_str().parse().unwrap_or(0);
|
|
144
|
-
}
|
|
145
|
-
if let Some(pass_str) = caps.get(2) {
|
|
146
|
-
passed = pass_str.as_str().parse().unwrap_or(0);
|
|
147
|
-
}
|
|
148
|
-
total = passed + failed;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Parse duration
|
|
152
|
-
let duration_ms = DURATION_RE.captures(&clean_output).and_then(|caps| {
|
|
153
|
-
let value: f64 = caps[1].parse().ok()?;
|
|
154
|
-
let unit = &caps[2];
|
|
155
|
-
Some(if unit == "ms" {
|
|
156
|
-
value as u64
|
|
157
|
-
} else {
|
|
158
|
-
(value * 1000.0) as u64
|
|
159
|
-
})
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// Only return if we found valid data
|
|
163
|
-
if total > 0 {
|
|
164
|
-
Some(TestResult {
|
|
165
|
-
total,
|
|
166
|
-
passed,
|
|
167
|
-
failed,
|
|
168
|
-
skipped: 0,
|
|
169
|
-
duration_ms,
|
|
170
|
-
failures: extract_failures_regex(&clean_output),
|
|
171
|
-
})
|
|
172
|
-
} else {
|
|
173
|
-
None
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/// Extract failures using regex
|
|
178
|
-
fn extract_failures_regex(output: &str) -> Vec<TestFailure> {
|
|
179
|
-
let mut failures = Vec::new();
|
|
180
|
-
let lines: Vec<&str> = output.lines().collect();
|
|
181
|
-
let mut i = 0;
|
|
182
|
-
|
|
183
|
-
while i < lines.len() {
|
|
184
|
-
let line = lines[i];
|
|
185
|
-
if line.contains('✗') || line.contains("FAIL") {
|
|
186
|
-
let mut error_lines = vec![line.to_string()];
|
|
187
|
-
i += 1;
|
|
188
|
-
|
|
189
|
-
// Collect subsequent indented lines
|
|
190
|
-
while i < lines.len() && lines[i].starts_with(" ") {
|
|
191
|
-
error_lines.push(lines[i].trim().to_string());
|
|
192
|
-
i += 1;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if !error_lines.is_empty() {
|
|
196
|
-
failures.push(TestFailure {
|
|
197
|
-
test_name: error_lines[0].clone(),
|
|
198
|
-
file_path: String::new(),
|
|
199
|
-
error_message: error_lines[1..].join("\n"),
|
|
200
|
-
stack_trace: None,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
} else {
|
|
204
|
-
i += 1;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
failures
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
#[derive(Debug, Clone)]
|
|
212
|
-
pub enum VitestCommand {
|
|
213
|
-
Run,
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
pub fn run(cmd: VitestCommand, args: &[String], verbose: u8) -> Result<()> {
|
|
217
|
-
match cmd {
|
|
218
|
-
VitestCommand::Run => run_vitest(args, verbose),
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
fn run_vitest(args: &[String], verbose: u8) -> Result<()> {
|
|
223
|
-
let timer = tracking::TimedExecution::start();
|
|
224
|
-
|
|
225
|
-
let mut cmd = package_manager_exec("vitest");
|
|
226
|
-
cmd.arg("run"); // Force non-watch mode
|
|
227
|
-
|
|
228
|
-
// Add JSON reporter for structured output
|
|
229
|
-
cmd.arg("--reporter=json");
|
|
230
|
-
|
|
231
|
-
for arg in args {
|
|
232
|
-
cmd.arg(arg);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
let output = cmd.output().context("Failed to run vitest")?;
|
|
236
|
-
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
237
|
-
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
238
|
-
let combined = format!("{}{}", stdout, stderr);
|
|
239
|
-
|
|
240
|
-
// Parse output using VitestParser
|
|
241
|
-
let parse_result = VitestParser::parse(&stdout);
|
|
242
|
-
let mode = FormatMode::from_verbosity(verbose);
|
|
243
|
-
|
|
244
|
-
let filtered = match parse_result {
|
|
245
|
-
ParseResult::Full(data) => {
|
|
246
|
-
if verbose > 0 {
|
|
247
|
-
eprintln!("vitest run (Tier 1: Full JSON parse)");
|
|
248
|
-
}
|
|
249
|
-
data.format(mode)
|
|
250
|
-
}
|
|
251
|
-
ParseResult::Degraded(data, warnings) => {
|
|
252
|
-
if verbose > 0 {
|
|
253
|
-
emit_degradation_warning("vitest", &warnings.join(", "));
|
|
254
|
-
}
|
|
255
|
-
data.format(mode)
|
|
256
|
-
}
|
|
257
|
-
ParseResult::Passthrough(raw) => {
|
|
258
|
-
emit_passthrough_warning("vitest", "All parsing tiers failed");
|
|
259
|
-
raw
|
|
260
|
-
}
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
let exit_code = output.status.code().unwrap_or(1);
|
|
264
|
-
if let Some(hint) = crate::tee::tee_and_hint(&combined, "vitest_run", exit_code) {
|
|
265
|
-
println!("{}\n{}", filtered, hint);
|
|
266
|
-
} else {
|
|
267
|
-
println!("{}", filtered);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
timer.track("vitest run", "rtk vitest run", &combined, &filtered);
|
|
271
|
-
|
|
272
|
-
// Propagate original exit code
|
|
273
|
-
std::process::exit(exit_code)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
#[cfg(test)]
|
|
277
|
-
mod tests {
|
|
278
|
-
use super::*;
|
|
279
|
-
|
|
280
|
-
#[test]
|
|
281
|
-
fn test_vitest_parser_json() {
|
|
282
|
-
let json = r#"{
|
|
283
|
-
"numTotalTests": 13,
|
|
284
|
-
"numPassedTests": 13,
|
|
285
|
-
"numFailedTests": 0,
|
|
286
|
-
"numPendingTests": 0,
|
|
287
|
-
"testResults": [],
|
|
288
|
-
"startTime": 1000,
|
|
289
|
-
"endTime": 1450
|
|
290
|
-
}"#;
|
|
291
|
-
|
|
292
|
-
let result = VitestParser::parse(json);
|
|
293
|
-
assert_eq!(result.tier(), 1);
|
|
294
|
-
assert!(result.is_ok());
|
|
295
|
-
|
|
296
|
-
let data = result.unwrap();
|
|
297
|
-
assert_eq!(data.total, 13);
|
|
298
|
-
assert_eq!(data.passed, 13);
|
|
299
|
-
assert_eq!(data.failed, 0);
|
|
300
|
-
assert_eq!(data.duration_ms, Some(450));
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
#[test]
|
|
304
|
-
fn test_vitest_parser_regex_fallback() {
|
|
305
|
-
let text = r#"
|
|
306
|
-
Test Files 2 passed (2)
|
|
307
|
-
Tests 13 passed (13)
|
|
308
|
-
Duration 450ms
|
|
309
|
-
"#;
|
|
310
|
-
|
|
311
|
-
let result = VitestParser::parse(text);
|
|
312
|
-
assert_eq!(result.tier(), 2); // Degraded
|
|
313
|
-
assert!(result.is_ok());
|
|
314
|
-
|
|
315
|
-
let data = result.unwrap();
|
|
316
|
-
assert_eq!(data.passed, 13);
|
|
317
|
-
assert_eq!(data.failed, 0);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
#[test]
|
|
321
|
-
fn test_vitest_parser_passthrough() {
|
|
322
|
-
let invalid = "random output with no structure";
|
|
323
|
-
let result = VitestParser::parse(invalid);
|
|
324
|
-
assert_eq!(result.tier(), 3); // Passthrough
|
|
325
|
-
assert!(!result.is_ok());
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
#[test]
|
|
329
|
-
fn test_strip_ansi() {
|
|
330
|
-
let input = "\x1b[32m✓\x1b[0m test passed";
|
|
331
|
-
let output = strip_ansi(input);
|
|
332
|
-
assert_eq!(output, "✓ test passed");
|
|
333
|
-
assert!(!output.contains("\x1b"));
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
#[test]
|
|
337
|
-
fn test_vitest_parser_with_pnpm_prefix() {
|
|
338
|
-
let input = r#"
|
|
339
|
-
Scope: all 6 workspace projects
|
|
340
|
-
WARN deprecated inflight@1.0.6: This module is not supported
|
|
341
|
-
|
|
342
|
-
{"numTotalTests": 13, "numPassedTests": 13, "numFailedTests": 0, "numPendingTests": 0, "testResults": [], "startTime": 1000, "endTime": 1450}
|
|
343
|
-
"#;
|
|
344
|
-
let result = VitestParser::parse(input);
|
|
345
|
-
assert_eq!(result.tier(), 1, "Should succeed with Tier 1 (full parse)");
|
|
346
|
-
assert!(result.is_ok());
|
|
347
|
-
|
|
348
|
-
let data = result.unwrap();
|
|
349
|
-
assert_eq!(data.total, 13);
|
|
350
|
-
assert_eq!(data.passed, 13);
|
|
351
|
-
assert_eq!(data.failed, 0);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
#[test]
|
|
355
|
-
fn test_vitest_parser_with_dotenv_prefix() {
|
|
356
|
-
let input = r#"[dotenv] Loading environment variables from .env
|
|
357
|
-
[dotenv] Injected 5 variables
|
|
358
|
-
|
|
359
|
-
{"numTotalTests": 5, "numPassedTests": 4, "numFailedTests": 1, "numPendingTests": 0, "testResults": [], "startTime": 2000, "endTime": 2300}
|
|
360
|
-
"#;
|
|
361
|
-
let result = VitestParser::parse(input);
|
|
362
|
-
assert_eq!(result.tier(), 1, "Should succeed with Tier 1 (full parse)");
|
|
363
|
-
assert!(result.is_ok());
|
|
364
|
-
|
|
365
|
-
let data = result.unwrap();
|
|
366
|
-
assert_eq!(data.total, 5);
|
|
367
|
-
assert_eq!(data.passed, 4);
|
|
368
|
-
assert_eq!(data.failed, 1);
|
|
369
|
-
assert_eq!(data.duration_ms, Some(300));
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
#[test]
|
|
373
|
-
fn test_vitest_parser_with_nested_json() {
|
|
374
|
-
let input = r#"prefix text
|
|
375
|
-
{"numTotalTests": 2, "numPassedTests": 2, "numFailedTests": 0, "numPendingTests": 0, "testResults": [{"name": "test.js", "assertionResults": [{"fullName": "nested test", "status": "passed", "failureMessages": []}]}], "startTime": 1000, "endTime": 1100}
|
|
376
|
-
"#;
|
|
377
|
-
let result = VitestParser::parse(input);
|
|
378
|
-
assert_eq!(result.tier(), 1, "Should succeed with Tier 1 (full parse)");
|
|
379
|
-
assert!(result.is_ok());
|
|
380
|
-
|
|
381
|
-
let data = result.unwrap();
|
|
382
|
-
assert_eq!(data.total, 2);
|
|
383
|
-
assert_eq!(data.passed, 2);
|
|
384
|
-
}
|
|
385
|
-
}
|