mod support;
use std::fs;
use predicates::prelude::*;
use support::lx_no_colour;
use tempfile::tempdir;
fn lx_with_theme(config_content: &str) -> (tempfile::TempDir, assert_cmd::Command) {
let dir = tempdir().expect("failed to create tempdir");
let config_path = dir.path().join("config.toml");
fs::write(&config_path, config_content).unwrap();
let mut cmd = assert_cmd::Command::cargo_bin("lx").expect("binary lx not found");
cmd.env("LX_CONFIG", config_path)
.env("HOME", "/nonexistent")
.env_remove("LS_COLORS")
.env_remove("LX_COLORS")
.arg("--colour=always");
(dir, cmd)
}
#[test]
fn theme_directory_colour() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[theme.test]
directory = "bold red"
"#);
let work = tempdir().expect("failed to create workdir");
fs::create_dir(work.path().join("mydir")).unwrap();
cmd.args(["--theme=test", "-1"])
.arg(work.path())
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;31m"));
}
#[test]
fn theme_date_colour() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[theme.test]
date = "bold cyan"
"#);
cmd.args(["--theme=test", "-l", "Cargo.toml"])
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;36m"));
}
#[test]
fn theme_x11_colour() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[theme.test]
date = "tomato"
"#);
cmd.args(["--theme=test", "-l", "Cargo.toml"])
.assert()
.success()
.stdout(predicate::str::contains("\x1b[38;2;255;99;71m"));
}
#[test]
fn theme_hex_colour() {
let (_dir, mut cmd) = lx_with_theme(
"version = \"0.3\"\n[theme.test]\ndate = \"#ff8700\"\n"
);
cmd.args(["--theme=test", "-l", "Cargo.toml"])
.assert()
.success()
.stdout(predicate::str::contains("\x1b[38;2;255;135;0m"));
}
#[test]
fn theme_extension_colour() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[theme.test]
use-style = "myexts"
[style.myexts]
"*.txt" = "bold magenta"
"#);
let work = tempdir().expect("failed to create workdir");
fs::write(work.path().join("readme.txt"), "").unwrap();
cmd.args(["--theme=test", "-1"])
.arg(work.path())
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;35m"));
}
#[test]
fn theme_filename_colour() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[theme.test]
use-style = "mynames"
[style.mynames]
Makefile = "bold underline yellow"
"#);
let work = tempdir().expect("failed to create workdir");
fs::write(work.path().join("Makefile"), "").unwrap();
cmd.args(["--theme=test", "-1"])
.arg(work.path())
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;4;33m"));
}
#[test]
fn theme_via_personality() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[personality.lx]
theme = "ocean"
[theme.ocean]
date = "bold cyan"
"#);
cmd.args(["-l", "Cargo.toml"])
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;36m"));
}
#[test]
fn theme_inherited_through_personality() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[personality.default]
theme = "ocean"
[personality.myview]
inherits = "default"
format = "long"
[theme.ocean]
date = "bold cyan"
"#);
cmd.args(["-pmyview", "Cargo.toml"])
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;36m"));
}
#[test]
fn theme_cli_overrides_personality() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[personality.lx]
theme = "ocean"
[theme.ocean]
date = "bold cyan"
[theme.warm]
date = "bold red"
"#);
cmd.args(["--theme=warm", "-l", "Cargo.toml"])
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;31m"));
}
#[test]
fn theme_overrides_env() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[theme.test]
date = "bold red"
"#);
cmd.env("LX_COLORS", "da=32")
.args(["--theme=test", "-l", "Cargo.toml"])
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;31m"));
}
#[test]
fn style_class_reference() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[class]
testclass = ["*.xyz"]
[theme.test]
inherits = "exa"
use-style = "mystyle"
[style.mystyle]
class.testclass = "bold magenta"
"#);
let work = tempdir().expect("failed to create workdir");
fs::write(work.path().join("data.xyz"), "").unwrap();
cmd.args(["--theme=test", "-1"])
.arg(work.path())
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;35m"));
}
#[test]
fn style_class_overrides_exa_default() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[theme.test]
inherits = "exa"
use-style = "custom"
[style.custom]
class.compressed = "bold cyan"
"#);
let work = tempdir().expect("failed to create workdir");
fs::write(work.path().join("archive.zip"), "").unwrap();
cmd.args(["--theme=test", "-1"])
.arg(work.path())
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;36m"));
}
#[test]
fn style_quoted_pattern_and_class() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[class]
data = ["*.csv"]
[theme.test]
inherits = "exa"
use-style = "mixed"
[style.mixed]
class.data = "bold green"
"Makefile" = "bold red"
"#);
let work = tempdir().expect("failed to create workdir");
fs::write(work.path().join("results.csv"), "").unwrap();
fs::write(work.path().join("Makefile"), "").unwrap();
cmd.args(["--theme=test", "-1"])
.arg(work.path())
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;32m")) .stdout(predicate::str::contains("\x1b[1;31m")); }
#[test]
fn user_class_overrides_compiled_in() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[class]
compressed = ["*.myarc"]
[theme.test]
inherits = "exa"
use-style = "exa"
"#);
let work = tempdir().expect("failed to create workdir");
fs::write(work.path().join("data.myarc"), "").unwrap();
fs::write(work.path().join("stuff.zip"), "").unwrap();
cmd.args(["--theme=test", "-1"])
.arg(work.path())
.assert()
.success()
.stdout(predicate::str::contains("\x1b[31m"));
}
#[test]
fn theme_inherits_exa() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[theme.custom]
inherits = "exa"
date = "bold red"
"#);
cmd.args(["--theme=custom", "-l", "src"])
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;34m"))
.stdout(predicate::str::contains("\x1b[1;31m"));
}
#[test]
fn theme_without_inherits_is_blank() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[theme.bare]
date = "bold red"
"#);
cmd.args(["--theme=bare", "-l", "src"])
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;31m"))
.stdout(predicate::str::contains("\x1b[1;34m").not());
}
#[test]
fn theme_inherits_custom() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[theme.base]
inherits = "exa"
date = "bold cyan"
[theme.child]
inherits = "base"
directory = "bold red"
"#);
cmd.args(["--theme=child", "-l", "src"])
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;31m")) .stdout(predicate::str::contains("\x1b[1;36m")); }
#[test]
fn theme_inheritance_cycle_detected() {
let (_dir, mut cmd) = lx_with_theme(r#"
version = "0.3"
[theme.a]
inherits = "b"
[theme.b]
inherits = "a"
"#);
cmd.args(["--theme=a", "-1", "Cargo.toml"])
.assert()
.success();
}
#[test]
fn no_theme_works() {
lx_no_colour()
.args(["-1", "Cargo.toml"])
.assert()
.success()
.stdout(predicate::str::contains("Cargo.toml"));
}
#[test]
fn default_theme_produces_colour() {
let mut cmd = assert_cmd::Command::cargo_bin("lx").expect("binary lx not found");
cmd.env("LX_CONFIG", "/nonexistent")
.env("HOME", "/nonexistent")
.env_remove("LS_COLORS")
.env_remove("LX_COLORS")
.arg("--colour=always")
.args(["-l", "src"])
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;34m"))
.stdout(predicate::str::contains("\x1b[34m"));
}
#[test]
fn default_theme_colours_filetypes() {
let mut cmd = assert_cmd::Command::cargo_bin("lx").expect("binary lx not found");
cmd.env("LX_CONFIG", "/nonexistent")
.env("HOME", "/nonexistent")
.env_remove("LS_COLORS")
.env_remove("LX_COLORS")
.arg("--colour=always");
let work = tempdir().expect("failed to create workdir");
fs::write(work.path().join("archive.zip"), "").unwrap();
cmd.args(["-1"])
.arg(work.path())
.assert()
.success()
.stdout(predicate::str::contains("\x1b[31m"));
}
#[test]
fn init_config_preserves_default_colours() {
let dir = tempdir().expect("failed to create tempdir");
let mut init = assert_cmd::Command::cargo_bin("lx").expect("binary lx not found");
init.args(["--init-config"])
.env("HOME", dir.path())
.env("LX_CONFIG", "/nonexistent")
.assert()
.success();
let config_path = dir.path().join(".lxconfig.toml");
assert!(config_path.exists());
let mut cmd = assert_cmd::Command::cargo_bin("lx").expect("binary lx not found");
cmd.env("LX_CONFIG", &config_path)
.env("HOME", dir.path())
.env_remove("LS_COLORS")
.env_remove("LX_COLORS")
.arg("--colour=always")
.args(["-l", "src"])
.assert()
.success()
.stdout(predicate::str::contains("\x1b[1;34m"));
}