use std::path::{Path, PathBuf};
use gpui::{div, CursorStyle, Styled, TestAppContext};
use herogpui_components::extend::{cursor_interactive, interactive_cursor};
use herogpui_theme::{set_theme, ActiveTheme, Theme, ThemeProvider};
#[gpui::test]
fn a_theme_override_reaches_the_element_a_component_styles(cx: &mut TestAppContext) {
cx.update(ThemeProvider::init);
let (stock, gpui_pointer) = cx.update(|cx| {
let mut ours = cursor_interactive(div(), cx);
let mut theirs = div().cursor_pointer();
(ours.style().mouse_cursor, theirs.style().mouse_cursor)
});
assert_eq!(
stock, gpui_pointer,
"the untouched theme must style an element exactly as cursor_pointer() did"
);
cx.update(|cx| {
set_theme(
Theme::builder("arrow", Theme::light())
.cursor_interactive(CursorStyle::Arrow)
.build(),
cx,
);
});
cx.update(|cx| {
assert_eq!(
cx.layout().cursor_interactive,
CursorStyle::Arrow,
"set_theme must publish the token app-wide"
);
assert_eq!(
interactive_cursor(cx),
CursorStyle::Arrow,
"the value components capture for `when` / `hover` closures follows the theme"
);
let mut el = cursor_interactive(div(), cx);
assert_eq!(
el.style().mouse_cursor,
Some(CursorStyle::Arrow),
"the helper every component calls must write the themed cursor onto the element"
);
});
}
#[test]
fn no_component_source_calls_gpuis_cursor_pointer() {
let src = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let mut offenders = Vec::new();
for file in rust_sources(&src) {
let text = std::fs::read_to_string(&file).expect("component source must be readable");
for (n, line) in text.lines().enumerate() {
if line.trim_start().starts_with("//") || line.trim_start().starts_with("///") {
continue;
}
if line.contains(".cursor_pointer()") {
offenders.push(format!("{}:{}", file.display(), n + 1));
}
}
}
assert!(
offenders.is_empty(),
"these sites bypass LayoutTheme::cursor_interactive; use util::cursor_interactive \
(or util::interactive_cursor inside a closure) instead:\n{}",
offenders.join("\n")
);
}
fn rust_sources(dir: &Path) -> Vec<PathBuf> {
let mut out = Vec::new();
let entries = std::fs::read_dir(dir).expect("component source tree must be readable");
for entry in entries {
let path = entry.expect("directory entry must be readable").path();
if path.is_dir() {
out.extend(rust_sources(&path));
} else if path.extension().is_some_and(|e| e == "rs") {
out.push(path);
}
}
out
}