Add a centralized ui module with Arc's visual identity: colored commit IDs (magenta), bookmarks (cyan), tags (yellow), status symbols, and diff highlighting. Update all command output and tests accordingly.
357 lines
9.2 KiB
Rust
357 lines
9.2 KiB
Rust
use std::process::Command;
|
|
use tempfile::TempDir;
|
|
|
|
fn arc_cmd() -> Command {
|
|
let mut cmd = Command::new(env!("CARGO_BIN_EXE_arc"));
|
|
cmd.env("NO_COLOR", "1");
|
|
cmd
|
|
}
|
|
|
|
fn init_repo() -> TempDir {
|
|
let dir = TempDir::new().unwrap();
|
|
arc_cmd()
|
|
.arg("init")
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed to init");
|
|
dir
|
|
}
|
|
|
|
fn commit_file(dir: &TempDir, name: &str, content: &str, msg: &str) -> String {
|
|
std::fs::write(dir.path().join(name), content).unwrap();
|
|
let output = arc_cmd()
|
|
.args(["commit", msg])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed to commit");
|
|
assert!(output.status.success());
|
|
String::from_utf8_lossy(&output.stdout)
|
|
.trim()
|
|
.strip_prefix("✓ committed ")
|
|
.unwrap()
|
|
.to_string()
|
|
}
|
|
|
|
#[test]
|
|
fn switch_to_bookmark() {
|
|
let dir = init_repo();
|
|
commit_file(&dir, "a.txt", "v1\n", "first on main");
|
|
|
|
arc_cmd()
|
|
.args(["mark", "add", "feature"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
commit_file(&dir, "a.txt", "v2\n", "second on main");
|
|
|
|
let output = arc_cmd()
|
|
.args(["switch", "feature"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
assert!(output.status.success());
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(stdout.contains("switched to bookmark feature"));
|
|
|
|
let content = std::fs::read_to_string(dir.path().join("a.txt")).unwrap();
|
|
assert_eq!(content, "v1\n");
|
|
}
|
|
|
|
#[test]
|
|
fn switch_updates_head_to_attached() {
|
|
let dir = init_repo();
|
|
commit_file(&dir, "a.txt", "v1\n", "first");
|
|
|
|
arc_cmd()
|
|
.args(["mark", "add", "feature"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
commit_file(&dir, "a.txt", "v2\n", "second");
|
|
|
|
arc_cmd()
|
|
.args(["switch", "feature"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
let head = std::fs::read_to_string(dir.path().join(".arc").join("HEAD")).unwrap();
|
|
assert!(head.contains("attached"));
|
|
assert!(head.contains("feature"));
|
|
}
|
|
|
|
#[test]
|
|
fn switch_to_tag() {
|
|
let dir = init_repo();
|
|
commit_file(&dir, "a.txt", "v1\n", "first");
|
|
|
|
arc_cmd()
|
|
.args(["tag", "add", "v1.0"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
commit_file(&dir, "a.txt", "v2\n", "second");
|
|
|
|
let output = arc_cmd()
|
|
.args(["switch", "v1.0"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
assert!(output.status.success());
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(stdout.contains("switched to tag v1.0"));
|
|
|
|
let content = std::fs::read_to_string(dir.path().join("a.txt")).unwrap();
|
|
assert_eq!(content, "v1\n");
|
|
|
|
let head = std::fs::read_to_string(dir.path().join(".arc").join("HEAD")).unwrap();
|
|
assert!(head.contains("detached"));
|
|
}
|
|
|
|
#[test]
|
|
fn switch_to_commit_prefix() {
|
|
let dir = init_repo();
|
|
let id1 = commit_file(&dir, "a.txt", "v1\n", "first");
|
|
commit_file(&dir, "a.txt", "v2\n", "second");
|
|
|
|
let short = &id1[..12];
|
|
let output = arc_cmd()
|
|
.args(["switch", short])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
assert!(output.status.success());
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(stdout.contains("switched to commit"));
|
|
|
|
let content = std::fs::read_to_string(dir.path().join("a.txt")).unwrap();
|
|
assert_eq!(content, "v1\n");
|
|
}
|
|
|
|
#[test]
|
|
fn switch_fails_with_dirty_worktree() {
|
|
let dir = init_repo();
|
|
commit_file(&dir, "a.txt", "v1\n", "first");
|
|
|
|
arc_cmd()
|
|
.args(["mark", "add", "feature"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
std::fs::write(dir.path().join("a.txt"), "modified\n").unwrap();
|
|
|
|
let output = arc_cmd()
|
|
.args(["switch", "feature"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(stderr.contains("uncommitted changes"));
|
|
}
|
|
|
|
#[test]
|
|
fn switch_removes_deleted_files() {
|
|
let dir = init_repo();
|
|
commit_file(&dir, "a.txt", "a\n", "first");
|
|
commit_file(&dir, "b.txt", "b\n", "add b");
|
|
|
|
arc_cmd()
|
|
.args(["mark", "add", "with-b"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
let id1_output = arc_cmd()
|
|
.args(["log"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
let _ = String::from_utf8_lossy(&id1_output.stdout);
|
|
|
|
let first_id = {
|
|
let log_output = arc_cmd()
|
|
.args(["log"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
let stdout = String::from_utf8_lossy(&log_output.stdout);
|
|
let lines: Vec<&str> = stdout.lines().collect();
|
|
let last_commit_line = lines.iter().rev().find(|l| l.starts_with("●")).unwrap();
|
|
let parts: Vec<&str> = last_commit_line.split_whitespace().collect();
|
|
parts[1].to_string()
|
|
};
|
|
|
|
arc_cmd()
|
|
.args(["mark", "add", "just-a", &first_id])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
arc_cmd()
|
|
.args(["switch", "just-a"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
assert!(dir.path().join("a.txt").exists());
|
|
assert!(!dir.path().join("b.txt").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn switch_adds_new_files() {
|
|
let dir = init_repo();
|
|
commit_file(&dir, "a.txt", "a\n", "first");
|
|
|
|
arc_cmd()
|
|
.args(["mark", "add", "no-b"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
commit_file(&dir, "b.txt", "b\n", "add b");
|
|
|
|
arc_cmd()
|
|
.args(["mark", "add", "with-b"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
arc_cmd()
|
|
.args(["switch", "no-b"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
assert!(!dir.path().join("b.txt").exists());
|
|
|
|
arc_cmd()
|
|
.args(["switch", "with-b"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
assert!(dir.path().join("b.txt").exists());
|
|
let content = std::fs::read_to_string(dir.path().join("b.txt")).unwrap();
|
|
assert_eq!(content, "b\n");
|
|
}
|
|
|
|
#[test]
|
|
fn switch_preserves_ignored_files() {
|
|
let dir = init_repo();
|
|
std::fs::write(dir.path().join(".arcignore"), "*.log\n").unwrap();
|
|
commit_file(&dir, "a.txt", "a\n", "first");
|
|
|
|
arc_cmd()
|
|
.args(["mark", "add", "feature"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
std::fs::write(dir.path().join("debug.log"), "log data\n").unwrap();
|
|
commit_file(&dir, "a.txt", "v2\n", "second");
|
|
|
|
arc_cmd()
|
|
.args(["switch", "feature"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
assert!(dir.path().join("debug.log").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn switch_back_and_forth() {
|
|
let dir = init_repo();
|
|
commit_file(&dir, "a.txt", "main-v1\n", "main first");
|
|
|
|
arc_cmd()
|
|
.args(["mark", "add", "feature"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
commit_file(&dir, "a.txt", "main-v2\n", "main second");
|
|
|
|
arc_cmd()
|
|
.args(["switch", "feature"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
let content = std::fs::read_to_string(dir.path().join("a.txt")).unwrap();
|
|
assert_eq!(content, "main-v1\n");
|
|
|
|
arc_cmd()
|
|
.args(["switch", "main"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
let content = std::fs::read_to_string(dir.path().join("a.txt")).unwrap();
|
|
assert_eq!(content, "main-v2\n");
|
|
}
|
|
|
|
#[test]
|
|
fn switch_cleans_empty_directories() {
|
|
let dir = init_repo();
|
|
std::fs::create_dir_all(dir.path().join("src")).unwrap();
|
|
std::fs::write(dir.path().join("src/main.rs"), "fn main() {}\n").unwrap();
|
|
commit_file(&dir, "root.txt", "root\n", "first");
|
|
|
|
arc_cmd()
|
|
.args(["mark", "add", "with-src"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
std::fs::remove_file(dir.path().join("src/main.rs")).unwrap();
|
|
std::fs::remove_dir(dir.path().join("src")).unwrap();
|
|
commit_file(&dir, "root.txt", "root v2\n", "remove src");
|
|
|
|
arc_cmd()
|
|
.args(["mark", "add", "no-src"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
arc_cmd()
|
|
.args(["switch", "with-src"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
assert!(dir.path().join("src/main.rs").exists());
|
|
|
|
arc_cmd()
|
|
.args(["switch", "no-src"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
assert!(!dir.path().join("src").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn switch_fails_for_unknown_target() {
|
|
let dir = init_repo();
|
|
commit_file(&dir, "a.txt", "hello\n", "first");
|
|
|
|
let output = arc_cmd()
|
|
.args(["switch", "nonexistent"])
|
|
.current_dir(dir.path())
|
|
.output()
|
|
.expect("failed");
|
|
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(stderr.contains("unknown revision"));
|
|
}
|