use atomcode_core::semantic::SemanticSearcher;
use std::io::Write;
use std::sync::Mutex;
static SEARCHER: std::sync::LazyLock<Mutex<SemanticSearcher>> =
std::sync::LazyLock::new(|| Mutex::new(SemanticSearcher::new()));
fn with_searcher<F, R>(f: F) -> R
where
F: FnOnce(&mut SemanticSearcher) -> R,
{
let mut s = SEARCHER.lock().unwrap();
f(&mut s)
}
fn write_temp_vue(content: &str) -> (tempfile::NamedTempFile, std::path::PathBuf) {
let mut f = tempfile::Builder::new().suffix(".vue").tempfile().unwrap();
f.write_all(content.as_bytes()).unwrap();
f.flush().unwrap();
let path = f.path().to_path_buf();
(f, path)
}
#[test]
fn vue_extracts_script_functions() {
let vue = r#"
<script setup lang="ts">
function fetchData() { return 1; }
const handleClick = () => { console.log('click'); }
</script>
<template>
<div>Hello</div>
</template>
"#;
let (_f, path) = write_temp_vue(vue);
let symbols = with_searcher(|s| s.list_symbols(&path).unwrap());
let names: Vec<&str> = symbols.iter().map(|s| s.name.as_str()).collect();
assert!(
names.contains(&"fetchData"),
"Should find fetchData, got: {:?}",
names
);
assert!(
names.contains(&"handleClick"),
"Should find handleClick, got: {:?}",
names
);
}
#[test]
fn vue_extracts_template_components() {
let vue = r#"
<script setup lang="ts">
function setup() {}
</script>
<template>
<div>
<TaskCard v-for="task in tasks" :key="task.id" />
<UserProfile v-if="user" :user="user" />
<button @click="handleSubmit">Submit</button>
</div>
</template>
"#;
let (_f, path) = write_temp_vue(vue);
let symbols = with_searcher(|s| s.list_symbols(&path).unwrap());
let names: Vec<&str> = symbols.iter().map(|s| s.name.as_str()).collect();
assert!(
names.contains(&"<TaskCard>"),
"Should find <TaskCard>, got: {:?}",
names
);
assert!(
names.contains(&"<UserProfile>"),
"Should find <UserProfile>, got: {:?}",
names
);
assert!(
names.contains(&"<button>"),
"Should find <button> with @click, got: {:?}",
names
);
}
#[test]
fn vue_filters_plain_divs() {
let vue = r#"
<script setup lang="ts">
</script>
<template>
<div class="container">
<span>text</span>
<p>paragraph</p>
<div v-if="show">Conditional div</div>
<CustomComponent />
</div>
</template>
"#;
let (_f, path) = write_temp_vue(vue);
let symbols = with_searcher(|s| s.list_symbols(&path).unwrap());
let names: Vec<&str> = symbols.iter().map(|s| s.name.as_str()).collect();
assert!(!names.contains(&"<span>"), "Should NOT find plain <span>");
assert!(!names.contains(&"<p>"), "Should NOT find plain <p>");
assert!(
names.contains(&"<div>"),
"Should find <div v-if>, got: {:?}",
names
);
assert!(
names.contains(&"<CustomComponent>"),
"Should find <CustomComponent>, got: {:?}",
names
);
}
#[test]
fn vue_template_symbols_have_line_numbers() {
let vue = r#"<script setup lang="ts">
function setup() {}
</script>
<template>
<div>
<MyComponent v-for="item in items" />
</div>
</template>
"#;
let (_f, path) = write_temp_vue(vue);
let symbols = with_searcher(|s| s.list_symbols(&path).unwrap());
let comp = symbols.iter().find(|s| s.name == "<MyComponent>").unwrap();
assert!(
comp.start_line >= 5 && comp.start_line <= 7,
"Expected line ~6, got {}",
comp.start_line
);
}
#[test]
fn vue_without_template_still_works() {
let vue = r#"
<script setup lang="ts">
function init() { return 42; }
</script>
"#;
let (_f, path) = write_temp_vue(vue);
let symbols = with_searcher(|s| s.list_symbols(&path).unwrap());
let names: Vec<&str> = symbols.iter().map(|s| s.name.as_str()).collect();
assert!(
names.contains(&"init"),
"Should find init from script, got: {:?}",
names
);
}
#[test]
fn vue_skeleton_includes_template_info() {
let vue = r#"
<script setup lang="ts">
function fetchUsers() { return []; }
function deleteUser(id: number) { return true; }
</script>
<template>
<div>
<UserTable v-if="users.length" :users="users" />
<EmptyState v-else />
</div>
</template>
"#;
let (_f, path) = write_temp_vue(vue);
let skeleton = with_searcher(|s| s.skeleton(&path).unwrap());
assert!(
skeleton.contains("fetchUsers"),
"Skeleton should contain fetchUsers"
);
assert!(
skeleton.contains("deleteUser"),
"Skeleton should contain deleteUser"
);
}
#[test]
fn html_file_extracts_elements() {
let html = r#"<!DOCTYPE html>
<html>
<head><title>Test</title></head>
<body>
<nav class="navbar">Navigation</nav>
<main>
<section id="hero">Hero</section>
<form action="/api" method="POST">
<input type="text" />
</form>
</main>
</body>
</html>"#;
let mut f = tempfile::Builder::new().suffix(".html").tempfile().unwrap();
f.write_all(html.as_bytes()).unwrap();
f.flush().unwrap();
let path = f.path().to_path_buf();
let symbols = with_searcher(|s| s.list_symbols(&path).unwrap());
assert!(!symbols.is_empty(), "Should find HTML elements");
let names: Vec<&str> = symbols.iter().map(|s| s.name.as_str()).collect();
assert!(
names.contains(&"html")
|| names.contains(&"body")
|| names.contains(&"nav")
|| names.contains(&"main")
|| names.contains(&"section")
|| names.contains(&"form"),
"Should find structural HTML elements, got: {:?}",
names
);
}