use super::*;
#[test]
fn resolves_classnames_bind_dynamic_source_expressions() -> TestResult {
let source_text = r#"import bind from "classnames/bind";
import styles from "./styles.module.scss";
const cx = bind.bind(styles);
const tone = "item--primary";
const icon = { glyph: "item__icon" };
const prefix = "item--";
export const view = <div className={cx(tone, icon.glyph, `item--${variant}`, { "item--danger": danger, item__label: true }, ok && "item--ok", active ? "item--on" : "item--off", prefix + state)} />;
"#;
let mut state = LspShellState::default();
handle_lsp_message(
&mut state,
json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"workspaceFolders": [
{
"uri": "file:///workspace-a",
"name": "workspace-a",
},
],
},
}),
);
handle_lsp_message(
&mut state,
json!({
"jsonrpc": "2.0",
"method": "textDocument/didOpen",
"params": {
"textDocument": {
"uri": "file:///workspace-a/src/App.tsx",
"languageId": "typescriptreact",
"version": 1,
"text": source_text,
},
},
}),
);
handle_lsp_message(
&mut state,
json!({
"jsonrpc": "2.0",
"method": "textDocument/didOpen",
"params": {
"textDocument": {
"uri": "file:///workspace-a/src/styles.module.scss",
"languageId": "scss",
"version": 1,
"text": ".item--primary {}\n.item__icon {}\n.item--large {}\n.item--danger {}\n.item__label {}\n.item--ok {}\n.item--on {}\n.item--off {}\n.item--muted {}\n",
},
},
}),
);
let tone_position = parser_position_for_byte_offset(
source_text,
fixture_find(
source_text,
"tone,",
"source fixture contains tone reference",
)?,
);
let tone_definition = handle_lsp_message(
&mut state,
json!({
"jsonrpc": "2.0",
"id": 2,
"method": "textDocument/definition",
"params": {
"textDocument": {
"uri": "file:///workspace-a/src/App.tsx",
},
"position": tone_position,
},
}),
);
assert_eq!(
tone_definition
.as_ref()
.and_then(|value| value.pointer("/result/0/range")),
Some(&json!({
"start": {
"line": 0,
"character": 1,
},
"end": {
"line": 0,
"character": 14,
},
})),
);
let icon_position = parser_position_for_byte_offset(
source_text,
fixture_find(
source_text,
"icon.glyph",
"source fixture contains object property reference",
)?,
);
let icon_definition = handle_lsp_message(
&mut state,
json!({
"jsonrpc": "2.0",
"id": 3,
"method": "textDocument/definition",
"params": {
"textDocument": {
"uri": "file:///workspace-a/src/App.tsx",
},
"position": icon_position,
},
}),
);
assert_eq!(
icon_definition
.as_ref()
.and_then(|value| value.pointer("/result/0/range")),
Some(&json!({
"start": {
"line": 1,
"character": 1,
},
"end": {
"line": 1,
"character": 11,
},
})),
);
let template_position = parser_position_for_byte_offset(
source_text,
fixture_find(
source_text,
"`item--",
"source fixture contains template prefix reference",
)? + 1,
);
let template_definition = handle_lsp_message(
&mut state,
json!({
"jsonrpc": "2.0",
"id": 4,
"method": "textDocument/definition",
"params": {
"textDocument": {
"uri": "file:///workspace-a/src/App.tsx",
},
"position": template_position,
},
}),
);
let template_targets = template_definition
.as_ref()
.and_then(|value| value.pointer("/result"))
.and_then(Value::as_array)
.cloned()
.unwrap_or_default();
assert!(
template_targets
.iter()
.any(|target| target.pointer("/range/start/line") == Some(&json!(2)))
);
assert!(
!template_targets
.iter()
.any(|target| target.pointer("/range/start/line") == Some(&json!(1)))
);
let object_key_position = parser_position_for_byte_offset(
source_text,
fixture_find(
source_text,
"item__label",
"source fixture contains object key reference",
)?,
);
let object_key_definition = handle_lsp_message(
&mut state,
json!({
"jsonrpc": "2.0",
"id": 5,
"method": "textDocument/definition",
"params": {
"textDocument": {
"uri": "file:///workspace-a/src/App.tsx",
},
"position": object_key_position,
},
}),
);
assert_eq!(
object_key_definition
.as_ref()
.and_then(|value| value.pointer("/result/0/range/start/line")),
Some(&json!(4)),
);
Ok(())
}