Merge pull request #19 from kbwo/feat/vitest

adapter for vitest
This commit is contained in:
Kodai Kabasawa 2024-07-15 22:49:05 +09:00 committed by GitHub
commit 3603e32025
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 2043 additions and 77 deletions

View file

@ -7,7 +7,7 @@
"path": "testing-ls-adapter",
"extra_args": ["--test-kind=cargo-test", "--workspace"],
"include_patterns": ["/**/*.rs"],
"exclude_patterns": ["/test_proj/**/*"],
"exclude_patterns": ["/demo/**/*"],
"workspace_dir": "."
}
]

View file

@ -8,7 +8,7 @@ license = "MIT"
[workspace]
members = [ "crates/adapter"]
exclude = ["test_proj"]
exclude = ["demo"]
[[bin]]
name = "testing-language-server"

View file

@ -52,6 +52,7 @@ fn handle(commands: AdapterCommands) -> Result<(), LSError> {
fn main() {
let _guard = Log::init().expect("Failed to initialize logger");
let args = AdapterCommands::parse();
tracing::info!("adapter args={:#?}", args);
if let Err(error) = handle(args) {
io::stderr()
.write_all(format!("{:#?}", error).as_bytes())

View file

@ -1,5 +1,6 @@
use crate::runner::cargo_test::CargoTestRunner;
use crate::runner::go::GoTestRunner;
use crate::runner::vitest::VitestRunner;
use std::str::FromStr;
use testing_language_server::error::LSError;
use testing_language_server::spec::DetectWorkspaceArgs;
@ -12,6 +13,7 @@ use crate::runner::jest::JestRunner;
pub enum AvailableTestKind {
CargoTest(CargoTestRunner),
Jest(JestRunner),
Vitest(VitestRunner),
GoTest(GoTestRunner),
}
impl Runner for AvailableTestKind {
@ -20,6 +22,7 @@ impl Runner for AvailableTestKind {
AvailableTestKind::CargoTest(runner) => runner.disover(args),
AvailableTestKind::Jest(runner) => runner.disover(args),
AvailableTestKind::GoTest(runner) => runner.disover(args),
AvailableTestKind::Vitest(runner) => runner.disover(args),
}
}
@ -28,6 +31,7 @@ impl Runner for AvailableTestKind {
AvailableTestKind::CargoTest(runner) => runner.run_file_test(args),
AvailableTestKind::Jest(runner) => runner.run_file_test(args),
AvailableTestKind::GoTest(runner) => runner.run_file_test(args),
AvailableTestKind::Vitest(runner) => runner.run_file_test(args),
}
}
@ -36,6 +40,7 @@ impl Runner for AvailableTestKind {
AvailableTestKind::CargoTest(runner) => runner.detect_workspaces(args),
AvailableTestKind::Jest(runner) => runner.detect_workspaces(args),
AvailableTestKind::GoTest(runner) => runner.detect_workspaces(args),
AvailableTestKind::Vitest(runner) => runner.detect_workspaces(args),
}
}
}
@ -48,6 +53,7 @@ impl FromStr for AvailableTestKind {
"cargo-test" => Ok(AvailableTestKind::CargoTest(CargoTestRunner)),
"jest" => Ok(AvailableTestKind::Jest(JestRunner)),
"go-test" => Ok(AvailableTestKind::GoTest(GoTestRunner)),
"vitest" => Ok(AvailableTestKind::Vitest(VitestRunner)),
_ => Err(anyhow::anyhow!("Unknown test kind: {}", s)),
}
}

View file

@ -1,4 +1,5 @@
use crate::runner::util::send_stdout;
use lsp_types::DiagnosticSeverity;
use std::collections::HashMap;
use std::path::PathBuf;
use std::process::Output;
@ -62,6 +63,7 @@ fn parse_diagnostics(
},
},
message,
severity: Some(DiagnosticSeverity::ERROR),
..Diagnostic::default()
};
result_map
@ -313,6 +315,7 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
}
},
message: message.to_string(),
severity: Some(DiagnosticSeverity::ERROR),
..Diagnostic::default()
}]
}]
@ -321,7 +324,7 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
#[test]
fn test_discover() {
let file_path = "../../test_proj/rust/src/lib.rs";
let file_path = "../../demo/rust/src/lib.rs";
discover(file_path).unwrap();
}
@ -330,16 +333,16 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
let current_dir = std::env::current_dir().unwrap();
let librs = current_dir.join("src/lib.rs");
let mainrs = current_dir.join("src/main.rs");
let absolute_path_of_test_proj = current_dir.join("../../test_proj/rust");
let test_proj_librs = absolute_path_of_test_proj.join("src/lib.rs");
let file_paths: Vec<String> = [librs, mainrs, test_proj_librs]
let absolute_path_of_demo = current_dir.join("../../demo/rust");
let demo_librs = absolute_path_of_demo.join("src/lib.rs");
let file_paths: Vec<String> = [librs, mainrs, demo_librs]
.iter()
.map(|file_path| file_path.to_str().unwrap().to_string())
.collect();
let workspaces = detect_workspaces(&file_paths);
assert_eq!(workspaces.len(), 2);
assert!(workspaces.contains_key(&absolute_path_of_test_proj.to_str().unwrap().to_string()));
assert!(workspaces.contains_key(&current_dir.to_str().unwrap().to_string()));
assert!(workspaces.contains_key(absolute_path_of_demo.to_str().unwrap()));
assert!(workspaces.contains_key(current_dir.to_str().unwrap()));
}
}

View file

@ -2,6 +2,7 @@ use crate::model::Runner;
use crate::runner::util::send_stdout;
use anyhow::anyhow;
use lsp_types::Diagnostic;
use lsp_types::DiagnosticSeverity;
use lsp_types::Position;
use lsp_types::Range;
use regex::Regex;
@ -116,6 +117,7 @@ fn parse_diagnostics(
},
},
message: message.clone(),
severity: Some(DiagnosticSeverity::ERROR),
..Diagnostic::default()
};
let file_path = workspace_root
@ -379,7 +381,7 @@ mod tests {
#[test]
fn test_discover() {
let file_path = "../../test_proj/go/cases_test.go";
let file_path = "../../demo/go/cases_test.go";
let test_items = discover(file_path).unwrap();
assert!(!test_items.is_empty());
}

View file

@ -1,8 +1,8 @@
use crate::runner::util::send_stdout;
use lsp_types::Diagnostic;
use lsp_types::DiagnosticSeverity;
use lsp_types::Position;
use lsp_types::Range;
use regex::Regex;
use serde_json::Value;
use std::collections::HashMap;
use std::fs;
@ -21,16 +21,12 @@ use tree_sitter::QueryCursor;
use crate::model::Runner;
use super::util::clean_ansi;
use super::util::detect_workspaces_from_file_paths;
// If the character value is greater than the line length it defaults back to the line length.
const MAX_CHAR_LENGTH: u32 = 10000;
fn clean_ansi(input: &str) -> String {
let re = Regex::new(r"\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?[m|K]").unwrap();
re.replace_all(input, "").to_string()
}
fn parse_diagnostics(
test_result: &str,
file_paths: Vec<String>,
@ -67,6 +63,7 @@ fn parse_diagnostics(
},
},
message,
severity: Some(DiagnosticSeverity::ERROR),
..Diagnostic::default()
};
result_map
@ -292,13 +289,13 @@ mod tests {
fn test_parse_diagnostics() {
let test_result = std::env::current_dir()
.unwrap()
.join("../../test_proj/jest/output.json");
.join("../../demo/jest/output.json");
let test_result = std::fs::read_to_string(test_result).unwrap();
let diagnostics = parse_diagnostics(
&test_result,
vec![
"/absolute_path/test_proj/jest/index.spec.js".to_string(),
"/absolute_path/test_proj/jest/another.spec.js".to_string(),
"/absolute_path/demo/jest/index.spec.js".to_string(),
"/absolute_path/demo/jest/another.spec.js".to_string(),
],
)
.unwrap();
@ -308,22 +305,22 @@ mod tests {
#[test]
fn test_detect_workspace() {
let current_dir = std::env::current_dir().unwrap();
let absolute_path_of_test_proj = current_dir.join("../../test_proj/jest");
let test_proj_indexjs = absolute_path_of_test_proj.join("index.spec.js");
let file_paths: Vec<String> = [test_proj_indexjs]
let absolute_path_of_demo = current_dir.join("../../demo/jest");
let demo_indexjs = absolute_path_of_demo.join("index.spec.js");
let file_paths: Vec<String> = [demo_indexjs]
.iter()
.map(|file_path| file_path.to_str().unwrap().to_string())
.collect();
let detect_result = detect_workspaces(file_paths);
assert_eq!(detect_result.len(), 1);
detect_result.iter().for_each(|(workspace, _)| {
assert_eq!(workspace, absolute_path_of_test_proj.to_str().unwrap());
assert_eq!(workspace, absolute_path_of_demo.to_str().unwrap());
});
}
#[test]
fn test_discover() {
let file_path = "../../test_proj/jest/index.spec.js";
let file_path = "../../demo/jest/index.spec.js";
let test_items = discover(file_path).unwrap();
assert_eq!(test_items.len(), 1);
assert_eq!(

View file

@ -2,3 +2,4 @@ pub mod cargo_test;
pub mod go;
pub mod jest;
pub mod util;
pub mod vitest;

View file

@ -2,9 +2,13 @@ use std::collections::HashMap;
use std::path::PathBuf;
use std::str::FromStr;
use regex::Regex;
use serde::Serialize;
use testing_language_server::error::LSError;
// If the character value is greater than the line length it defaults back to the line length.
pub const MAX_CHAR_LENGTH: u32 = 10000;
/// determine if a particular file is the root of workspace based on whether it is in the same directory
pub fn detect_workspace_from_file(file_path: PathBuf, file_names: &[String]) -> Option<String> {
let parent = file_path.parent();
@ -62,3 +66,8 @@ where
serde_json::to_writer(std::io::stdout(), &value)?;
Ok(())
}
pub fn clean_ansi(input: &str) -> String {
let re = Regex::new(r"\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?[m|K]").unwrap();
re.replace_all(input, "").to_string()
}

View file

@ -0,0 +1,330 @@
use std::{
collections::HashMap,
fs::{self, File},
};
use lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range};
use serde_json::Value;
use tempfile::tempdir;
use testing_language_server::{
error::LSError,
spec::{
DiscoverResult, DiscoverResultItem, RunFileTestResult, RunFileTestResultItem, TestItem,
},
};
use tree_sitter::{Point, Query, QueryCursor};
use crate::model::Runner;
use super::util::{clean_ansi, detect_workspaces_from_file_paths, send_stdout, MAX_CHAR_LENGTH};
#[derive(Eq, PartialEq, Hash, Debug)]
pub struct VitestRunner;
fn discover(file_path: &str) -> Result<Vec<TestItem>, LSError> {
let mut parser = tree_sitter::Parser::new();
let mut test_items: Vec<TestItem> = vec![];
parser
.set_language(&tree_sitter_javascript::language())
.expect("Error loading Rust grammar");
let source_code = std::fs::read_to_string(file_path)?;
let tree = parser.parse(&source_code, None).unwrap();
// from https://github.com/marilari88/neotest-vitest/blob/353364aa05b94b09409cbef21b79c97c5564e2ce/lua/neotest-vitest/init.lua#L101
let query_string = r#"
; -- Namespaces --
; Matches: `describe('context')`
((call_expression
function: (identifier) @func_name (#eq? @func_name "describe")
arguments: (arguments (string (string_fragment) @namespace.name) (arrow_function))
)) @namespace.definition
; Matches: `describe.only('context')`
((call_expression
function: (member_expression
object: (identifier) @func_name (#any-of? @func_name "describe")
)
arguments: (arguments (string (string_fragment) @namespace.name) (arrow_function))
)) @namespace.definition
; Matches: `describe.each(['data'])('context')`
((call_expression
function: (call_expression
function: (member_expression
object: (identifier) @func_name (#any-of? @func_name "describe")
)
)
arguments: (arguments (string (string_fragment) @namespace.name) (arrow_function))
)) @namespace.definition
; -- Tests --
; Matches: `test('test') / it('test')`
((call_expression
function: (identifier) @func_name (#any-of? @func_name "it" "test")
arguments: (arguments (string (string_fragment) @test.name) (arrow_function))
)) @test.definition
; Matches: `test.only('test') / it.only('test')`
((call_expression
function: (member_expression
object: (identifier) @func_name (#any-of? @func_name "test" "it")
)
arguments: (arguments (string (string_fragment) @test.name) (arrow_function))
)) @test.definition
; Matches: `test.each(['data'])('test') / it.each(['data'])('test')`
((call_expression
function: (call_expression
function: (member_expression
object: (identifier) @func_name (#any-of? @func_name "it" "test")
)
)
arguments: (arguments (string (string_fragment) @test.name) (arrow_function))
)) @test.definition
"#;
let query = Query::new(&tree_sitter_javascript::language(), query_string)
.expect("Error creating query");
let mut cursor = QueryCursor::new();
cursor.set_byte_range(tree.root_node().byte_range());
let source = source_code.as_bytes();
let matches = cursor.matches(&query, tree.root_node(), source);
for m in matches {
let mut namespace_name = "";
let mut test_start_position = Point::default();
let mut test_end_position = Point::default();
for capture in m.captures {
let capture_name = query.capture_names()[capture.index as usize];
let value = capture.node.utf8_text(source)?;
let start_position = capture.node.start_position();
let end_position = capture.node.end_position();
match capture_name {
"namespace.name" => {
namespace_name = value;
}
"test.definition" => {
test_start_position = start_position;
test_end_position = end_position;
}
"test.name" => {
let test_name = value;
let test_item = TestItem {
id: format!("{}:{}", namespace_name, test_name),
name: test_name.to_string(),
start_position: Range {
start: Position {
line: test_start_position.row as u32,
character: test_start_position.column as u32,
},
end: Position {
line: test_start_position.row as u32,
character: MAX_CHAR_LENGTH,
},
},
end_position: Range {
start: Position {
line: test_end_position.row as u32,
character: 0,
},
end: Position {
line: test_end_position.row as u32,
character: test_end_position.column as u32,
},
},
};
test_items.push(test_item);
test_start_position = Point::default();
test_end_position = Point::default();
}
_ => {}
}
}
}
Ok(test_items)
}
fn parse_diagnostics(
test_result: &str,
file_paths: Vec<String>,
) -> Result<RunFileTestResult, LSError> {
let mut result_map: HashMap<String, Vec<Diagnostic>> = HashMap::new();
let json: Value = serde_json::from_str(test_result)?;
let test_results = json["testResults"].as_array().unwrap();
for test_result in test_results {
let file_path = test_result["name"].as_str().unwrap();
if !file_paths.iter().any(|path| path.contains(file_path)) {
continue;
}
let assertion_results = test_result["assertionResults"].as_array().unwrap();
'assertion: for assertion_result in assertion_results {
let status = assertion_result["status"].as_str().unwrap();
if status != "failed" {
continue 'assertion;
}
let location = assertion_result["location"].as_object().unwrap();
let failure_messages = assertion_result["failureMessages"].as_array().unwrap();
let line = location["line"].as_u64().unwrap() - 1;
failure_messages.iter().for_each(|message| {
let message = clean_ansi(message.as_str().unwrap());
let diagnostic = Diagnostic {
range: lsp_types::Range {
start: lsp_types::Position {
line: line as u32,
// Line and column number is slightly incorrect.
// ref:
// Bug in json reporter line number? · vitest-dev/vitest · Discussion #5350
// https://github.com/vitest-dev/vitest/discussions/5350
// Currently, The row numbers are from the parse result, the column numbers are 0 and MAX_CHAR_LENGTH is hard-coded.
character: 0,
},
end: lsp_types::Position {
line: line as u32,
character: MAX_CHAR_LENGTH,
},
},
message,
severity: Some(DiagnosticSeverity::ERROR),
..Diagnostic::default()
};
result_map
.entry(file_path.to_string())
.or_default()
.push(diagnostic);
})
}
}
Ok(result_map
.into_iter()
.map(|(path, diagnostics)| RunFileTestResultItem { path, diagnostics })
.collect())
}
impl Runner for VitestRunner {
fn disover(&self, args: testing_language_server::spec::DiscoverArgs) -> Result<(), LSError> {
let file_paths = args.file_paths;
let mut discover_results: DiscoverResult = vec![];
for file_path in file_paths {
let tests = discover(&file_path)?;
discover_results.push(DiscoverResultItem {
tests,
path: file_path,
});
}
send_stdout(&discover_results)?;
Ok(())
}
fn run_file_test(
&self,
args: testing_language_server::spec::RunFileTestArgs,
) -> Result<(), LSError> {
let file_paths = args.file_paths;
let workspace_root = args.workspace;
let tempdir = tempdir().unwrap();
let tempdir_path = tempdir.path();
let tempfile_path = tempdir_path.join("vitest.json");
let tempfile = File::create(&tempfile_path)?;
let tempfile_path = tempfile_path.to_str().unwrap();
std::process::Command::new("vitest")
.current_dir(&workspace_root)
.args([
"--watch=false",
"--reporter=json",
"--outputFile=",
tempfile_path,
])
.output()
.unwrap();
let test_result = fs::read_to_string(tempfile_path)?;
let diagnostics: RunFileTestResult = parse_diagnostics(&test_result, file_paths)?;
send_stdout(&diagnostics)?;
drop(tempfile);
let _ = tempdir.close();
Ok(())
}
fn detect_workspaces(
&self,
args: testing_language_server::spec::DetectWorkspaceArgs,
) -> Result<(), LSError> {
send_stdout(&detect_workspaces_from_file_paths(
&args.file_paths,
&[
"package.json".to_string(),
"vitest.config.ts".to_string(),
"vitest.config.js".to_string(),
"vite.config.ts".to_string(),
"vite.config.js".to_string(),
"vitest.config.mts".to_string(),
"vitest.config.mjs".to_string(),
"vite.config.mts".to_string(),
"vite.config.mjs".to_string(),
],
))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use lsp_types::{Position, Range};
use super::*;
#[test]
fn test_discover() {
let file_path = "../../demo/vitest/basic.test.ts";
let test_items = discover(file_path).unwrap();
assert_eq!(test_items.len(), 2);
assert_eq!(
test_items,
vec![
TestItem {
id: String::from(":pass"),
name: String::from("pass"),
start_position: Range {
start: Position {
line: 4,
character: 2
},
end: Position {
line: 4,
character: MAX_CHAR_LENGTH
}
},
end_position: Range {
start: Position {
line: 6,
character: 0
},
end: Position {
line: 6,
character: 4
}
}
},
TestItem {
id: String::from(":fail"),
name: String::from("fail"),
start_position: Range {
start: Position {
line: 8,
character: 2
},
end: Position {
line: 8,
character: MAX_CHAR_LENGTH
}
},
end_position: Range {
start: Position {
line: 10,
character: 0
},
end: Position {
line: 10,
character: 4
}
}
}
]
)
}
}

View file

@ -3,7 +3,7 @@
"testing": {
"command": "testing-language-server",
"trace.server": "verbose",
"filetypes": ["rust", "javascript", "go"],
"filetypes": ["rust", "javascript", "go", "typescript"],
"initializationOptions": {
"adapterCommand": {
"rust": [
@ -14,12 +14,23 @@
"exclude_patterns": ["/**/target/**"]
}
],
"javascript": [
"jest": [
{
"path": "testing-ls-adapter",
"extra_args": ["--test-kind=jest"],
"include_patterns": ["/**/*.js"],
"exclude_patterns": ["/**/node_modules/**/*"]
"include_patterns": ["/jest/*.js"],
"exclude_patterns": ["/jest/**/node_modules/**/*"]
}
],
"vitest": [
{
"path": "testing-ls-adapter",
"extra_args": ["--test-kind=vitest"],
"include_patterns": [
"/vitest/*.test.ts",
"/vitest/config/**/*.test.ts"
],
"exclude_patterns": ["/vitest/**/node_modules/**/*"]
}
],
"go": [

View file

@ -1,4 +1,4 @@
# test_proj
# demo
To install dependencies:

View file

@ -32,9 +32,7 @@
{
"assertionResults": [
{
"ancestorTitles": [
"index"
],
"ancestorTitles": ["index"],
"duration": 3,
"failureDetails": [
{
@ -48,7 +46,7 @@
}
],
"failureMessages": [
"Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m0\u001b[39m\nReceived: \u001b[31m1\u001b[39m\n at Object.toBe (/absolute_path/test_proj/jest/index.spec.js:4:15)\n at Promise.then.completed (/absolute_path/test_proj/jest/node_modules/jest-circus/build/utils.js:298:28)\n at new Promise (<anonymous>)\n at callAsyncCircusFn (/absolute_path/test_proj/jest/node_modules/jest-circus/build/utils.js:231:10)\n at _callCircusTest (/absolute_path/test_proj/jest/node_modules/jest-circus/build/run.js:316:40)\n at _runTest (/absolute_path/test_proj/jest/node_modules/jest-circus/build/run.js:252:3)\n at _runTestsForDescribeBlock (/absolute_path/test_proj/jest/node_modules/jest-circus/build/run.js:126:9)\n at _runTestsForDescribeBlock (/absolute_path/test_proj/jest/node_modules/jest-circus/build/run.js:121:9)\n at run (/absolute_path/test_proj/jest/node_modules/jest-circus/build/run.js:71:3)\n at runAndTransformResultsToJestFormat (/absolute_path/test_proj/jest/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)\n at jestAdapter (/absolute_path/test_proj/jest/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)\n at runTestInternal (/absolute_path/test_proj/jest/node_modules/jest-runner/build/runTest.js:367:16)\n at runTest (/absolute_path/test_proj/jest/node_modules/jest-runner/build/runTest.js:444:34)"
"Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m0\u001b[39m\nReceived: \u001b[31m1\u001b[39m\n at Object.toBe (/absolute_path/demo/jest/index.spec.js:4:15)\n at Promise.then.completed (/absolute_path/demo/jest/node_modules/jest-circus/build/utils.js:298:28)\n at new Promise (<anonymous>)\n at callAsyncCircusFn (/absolute_path/demo/jest/node_modules/jest-circus/build/utils.js:231:10)\n at _callCircusTest (/absolute_path/demo/jest/node_modules/jest-circus/build/run.js:316:40)\n at _runTest (/absolute_path/demo/jest/node_modules/jest-circus/build/run.js:252:3)\n at _runTestsForDescribeBlock (/absolute_path/demo/jest/node_modules/jest-circus/build/run.js:126:9)\n at _runTestsForDescribeBlock (/absolute_path/demo/jest/node_modules/jest-circus/build/run.js:121:9)\n at run (/absolute_path/demo/jest/node_modules/jest-circus/build/run.js:71:3)\n at runAndTransformResultsToJestFormat (/absolute_path/demo/jest/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)\n at jestAdapter (/absolute_path/demo/jest/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)\n at runTestInternal (/absolute_path/demo/jest/node_modules/jest-runner/build/runTest.js:367:16)\n at runTest (/absolute_path/demo/jest/node_modules/jest-runner/build/runTest.js:444:34)"
],
"fullName": "index fail",
"invocations": 1,
@ -64,7 +62,7 @@
],
"endTime": 1714484637874,
"message": "\u001b[1m\u001b[31m \u001b[1m● \u001b[22m\u001b[1mindex fail\u001b[39m\u001b[22m\n\n \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\n Expected: \u001b[32m0\u001b[39m\n Received: \u001b[31m1\u001b[39m\n\u001b[2m\u001b[22m\n\u001b[2m \u001b[0m \u001b[90m 2 |\u001b[39m\u001b[22m\n\u001b[2m \u001b[90m 3 |\u001b[39m it(\u001b[32m\"fail\"\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\u001b[22m\n\u001b[2m \u001b[31m\u001b[1m>\u001b[22m\u001b[2m\u001b[39m\u001b[90m 4 |\u001b[39m expect(\u001b[35m1\u001b[39m)\u001b[33m.\u001b[39mtoBe(\u001b[35m0\u001b[39m)\u001b[22m\n\u001b[2m \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[2m\u001b[39m\u001b[22m\n\u001b[2m \u001b[90m 5 |\u001b[39m })\u001b[22m\n\u001b[2m \u001b[90m 6 |\u001b[39m\u001b[22m\n\u001b[2m \u001b[90m 7 |\u001b[39m })\u001b[0m\u001b[22m\n\u001b[2m\u001b[22m\n\u001b[2m \u001b[2mat Object.toBe (\u001b[22m\u001b[2m\u001b[0m\u001b[36mindex.spec.js\u001b[39m\u001b[0m\u001b[2m:4:15)\u001b[22m\u001b[2m\u001b[22m\n",
"name": "/absolute_path/test_proj/jest/index.spec.js",
"name": "/absolute_path/demo/jest/index.spec.js",
"startTime": 1714484637684,
"status": "failed",
"summary": ""
@ -72,9 +70,7 @@
{
"assertionResults": [
{
"ancestorTitles": [
"another"
],
"ancestorTitles": ["another"],
"duration": 2,
"failureDetails": [
{
@ -88,7 +84,7 @@
}
],
"failureMessages": [
"Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m0\u001b[39m\nReceived: \u001b[31m1\u001b[39m\n at Object.toBe (/absolute_path/test_proj/jest/another.spec.js:4:15)\n at Promise.then.completed (/absolute_path/test_proj/jest/node_modules/jest-circus/build/utils.js:298:28)\n at new Promise (<anonymous>)\n at callAsyncCircusFn (/absolute_path/test_proj/jest/node_modules/jest-circus/build/utils.js:231:10)\n at _callCircusTest (/absolute_path/test_proj/jest/node_modules/jest-circus/build/run.js:316:40)\n at _runTest (/absolute_path/test_proj/jest/node_modules/jest-circus/build/run.js:252:3)\n at _runTestsForDescribeBlock (/absolute_path/test_proj/jest/node_modules/jest-circus/build/run.js:126:9)\n at _runTestsForDescribeBlock (/absolute_path/test_proj/jest/node_modules/jest-circus/build/run.js:121:9)\n at run (/absolute_path/test_proj/jest/node_modules/jest-circus/build/run.js:71:3)\n at runAndTransformResultsToJestFormat (/absolute_path/test_proj/jest/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)\n at jestAdapter (/absolute_path/test_proj/jest/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)\n at runTestInternal (/absolute_path/test_proj/jest/node_modules/jest-runner/build/runTest.js:367:16)\n at runTest (/absolute_path/test_proj/jest/node_modules/jest-runner/build/runTest.js:444:34)"
"Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m0\u001b[39m\nReceived: \u001b[31m1\u001b[39m\n at Object.toBe (/absolute_path/demo/jest/another.spec.js:4:15)\n at Promise.then.completed (/absolute_path/demo/jest/node_modules/jest-circus/build/utils.js:298:28)\n at new Promise (<anonymous>)\n at callAsyncCircusFn (/absolute_path/demo/jest/node_modules/jest-circus/build/utils.js:231:10)\n at _callCircusTest (/absolute_path/demo/jest/node_modules/jest-circus/build/run.js:316:40)\n at _runTest (/absolute_path/demo/jest/node_modules/jest-circus/build/run.js:252:3)\n at _runTestsForDescribeBlock (/absolute_path/demo/jest/node_modules/jest-circus/build/run.js:126:9)\n at _runTestsForDescribeBlock (/absolute_path/demo/jest/node_modules/jest-circus/build/run.js:121:9)\n at run (/absolute_path/demo/jest/node_modules/jest-circus/build/run.js:71:3)\n at runAndTransformResultsToJestFormat (/absolute_path/demo/jest/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)\n at jestAdapter (/absolute_path/demo/jest/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)\n at runTestInternal (/absolute_path/demo/jest/node_modules/jest-runner/build/runTest.js:367:16)\n at runTest (/absolute_path/demo/jest/node_modules/jest-runner/build/runTest.js:444:34)"
],
"fullName": "another fail",
"invocations": 1,
@ -102,9 +98,7 @@
"title": "fail"
},
{
"ancestorTitles": [
"another"
],
"ancestorTitles": ["another"],
"duration": 1,
"failureDetails": [],
"failureMessages": [],
@ -122,7 +116,7 @@
],
"endTime": 1714484637974,
"message": "\u001b[1m\u001b[31m \u001b[1m● \u001b[22m\u001b[1manother fail\u001b[39m\u001b[22m\n\n \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\n Expected: \u001b[32m0\u001b[39m\n Received: \u001b[31m1\u001b[39m\n\u001b[2m\u001b[22m\n\u001b[2m \u001b[0m \u001b[90m 2 |\u001b[39m\u001b[22m\n\u001b[2m \u001b[90m 3 |\u001b[39m it(\u001b[32m\"fail\"\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\u001b[22m\n\u001b[2m \u001b[31m\u001b[1m>\u001b[22m\u001b[2m\u001b[39m\u001b[90m 4 |\u001b[39m expect(\u001b[35m1\u001b[39m)\u001b[33m.\u001b[39mtoBe(\u001b[35m0\u001b[39m)\u001b[22m\n\u001b[2m \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[2m\u001b[39m\u001b[22m\n\u001b[2m \u001b[90m 5 |\u001b[39m })\u001b[22m\n\u001b[2m \u001b[90m 6 |\u001b[39m\u001b[22m\n\u001b[2m \u001b[90m 7 |\u001b[39m it(\u001b[32m\"pass\"\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\u001b[0m\u001b[22m\n\u001b[2m\u001b[22m\n\u001b[2m \u001b[2mat Object.toBe (\u001b[22m\u001b[2m\u001b[0m\u001b[36manother.spec.js\u001b[39m\u001b[0m\u001b[2m:4:15)\u001b[22m\u001b[2m\u001b[22m\n",
"name": "/absolute_path/test_proj/jest/another.spec.js",
"name": "/absolute_path/demo/jest/another.spec.js",
"startTime": 1714484637879,
"status": "failed",
"summary": ""

View file

@ -1,5 +1,5 @@
{
"name": "test_proj",
"name": "demo",
"module": "index.js",
"type": "module",
"devDependencies": {
@ -10,4 +10,4 @@
"peerDependencies": {
"typescript": "^5.0.0"
}
}
}

View file

@ -62,6 +62,13 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "demo"
version = "0.1.0"
dependencies = [
"tokio",
]
[[package]]
name = "gimli"
version = "0.28.1"
@ -239,13 +246,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "test_proj"
version = "0.1.0"
dependencies = [
"tokio",
]
[[package]]
name = "tokio"
version = "1.37.0"

View file

@ -1,5 +1,5 @@
[package]
name = "test_proj"
name = "demo"
version = "0.1.0"
edition = "2021"

2
demo/vitest/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules
.yarn

1
demo/vitest/.yarnrc.yml Normal file
View file

@ -0,0 +1 @@
nodeLinker: node-modules

12
demo/vitest/basic.test.ts Normal file
View file

@ -0,0 +1,12 @@
import assert from "assert";
import { describe, test } from "vitest";
describe("describe text", () => {
test("pass", async () => {
assert(false);
});
test("fail", async () => {
assert(false);
});
});

16
demo/vitest/package.json Normal file
View file

@ -0,0 +1,16 @@
{
"name": "spec",
"version": "0.0.1",
"description": "neotest-vitest spec",
"main": "index.js",
"license": "MIT",
"dependencies": {
"ts-node": "^10.8.2",
"typescript": "^4.7.4"
},
"devDependencies": {
"@types/node": "^18.0.3",
"vite": "^3.0.9",
"vitest": "^0.22.1"
}
}

View file

@ -0,0 +1,13 @@
/// <reference types="vitest" />
// Configure Vitest (https://vitest.dev/config/)
import { defineConfig } from 'vite'
export default defineConfig({
test: {
/* for example, use global to avoid globals imports (describe, test, expect): */
// globals: true,
},
})

1575
demo/vitest/yarn.lock Normal file

File diff suppressed because it is too large Load diff

View file

@ -577,12 +577,12 @@ mod tests {
#[test]
fn test_check_file() {
let abs_path_of_test_proj = std::env::current_dir().unwrap().join("test_proj/rust");
let abs_path_of_demo = std::env::current_dir().unwrap().join("demo/rust");
let mut server = TestingLS {
initialize_params: InitializeParams {
workspace_folders: Some(vec![WorkspaceFolder {
uri: Url::from_file_path(&abs_path_of_test_proj).unwrap(),
name: "test_proj".to_string(),
uri: Url::from_file_path(&abs_path_of_demo).unwrap(),
name: "demo".to_string(),
}]),
..InitializeParams::default()
},
@ -591,13 +591,13 @@ mod tests {
},
workspaces_cache: Vec::new(),
};
let librs = abs_path_of_test_proj.join("lib.rs");
let librs = abs_path_of_demo.join("lib.rs");
server.check_file(librs.to_str().unwrap(), true).unwrap();
}
#[test]
fn test_check_workspace() {
let abs_path_of_test_proj = std::env::current_dir().unwrap().join("test_proj/rust");
let abs_path_of_demo = std::env::current_dir().unwrap().join("demo/rust");
let abs_path_of_rust_adapter = std::env::current_dir()
.unwrap()
.join("target/debug/testing-ls-adapter");
@ -613,8 +613,8 @@ mod tests {
let mut server = TestingLS {
initialize_params: InitializeParams {
workspace_folders: Some(vec![WorkspaceFolder {
uri: Url::from_file_path(abs_path_of_test_proj.clone()).unwrap(),
name: "test_proj".to_string(),
uri: Url::from_file_path(abs_path_of_demo.clone()).unwrap(),
name: "demo".to_string(),
}]),
..InitializeParams::default()
},
@ -634,7 +634,7 @@ mod tests {
.workspaces
.iter()
.for_each(|(workspace, paths)| {
assert_eq!(workspace, abs_path_of_test_proj.to_str().unwrap());
assert_eq!(workspace, abs_path_of_demo.to_str().unwrap());
paths.iter().for_each(|path| {
assert!(path.contains("rust/src"));
});
@ -644,20 +644,20 @@ mod tests {
#[test]
fn project_files_are_filtered_by_extension() {
let absolute_path_of_test_proj = std::env::current_dir().unwrap().join("test_proj");
let absolute_path_of_demo = std::env::current_dir().unwrap().join("demo");
let files = TestingLS::project_files(
&absolute_path_of_test_proj.clone(),
&absolute_path_of_demo.clone(),
&["/rust/src/lib.rs".to_string()],
&["/rust/target/**/*".to_string()],
);
let librs = absolute_path_of_test_proj.join("rust/src/lib.rs");
let librs = absolute_path_of_demo.join("rust/src/lib.rs");
assert_eq!(files, vec![librs.to_str().unwrap()]);
let files = TestingLS::project_files(
&absolute_path_of_test_proj.clone(),
&absolute_path_of_demo.clone(),
&["jest/*.spec.js".to_string()],
&["jest/another.spec.js".to_string()],
);
let test_file = absolute_path_of_test_proj.join("jest/index.spec.js");
let test_file = absolute_path_of_demo.join("jest/index.spec.js");
assert_eq!(files, vec![test_file.to_str().unwrap()]);
}
@ -673,18 +673,15 @@ mod tests {
extra_args: vec!["--invalid-arg".to_string()],
..Default::default()
};
let abs_path_of_test_proj = std::env::current_dir().unwrap().join("test_proj/rust");
let files = TestingLS::project_files(
&abs_path_of_test_proj.clone(),
&["/**/*.rs".to_string()],
&[],
);
let abs_path_of_demo = std::env::current_dir().unwrap().join("demo/rust");
let files =
TestingLS::project_files(&abs_path_of_demo.clone(), &["/**/*.rs".to_string()], &[]);
let server = TestingLS {
initialize_params: InitializeParams {
workspace_folders: Some(vec![WorkspaceFolder {
uri: Url::from_file_path(&abs_path_of_test_proj).unwrap(),
name: "test_proj".to_string(),
uri: Url::from_file_path(&abs_path_of_demo).unwrap(),
name: "demo".to_string(),
}]),
..InitializeParams::default()
},
@ -694,11 +691,7 @@ mod tests {
workspaces_cache: Vec::new(),
};
let diagnostics = server
.get_diagnostics(
&adapter_conf,
abs_path_of_test_proj.to_str().unwrap(),
&files,
)
.get_diagnostics(&adapter_conf, abs_path_of_demo.to_str().unwrap(), &files)
.unwrap();
assert_eq!(diagnostics.len(), 1);
let diagnostic = diagnostics.first().unwrap().1.first().unwrap();