#![allow(dead_code)]
use std::fs;
use std::io;
use std::path::PathBuf;
use phantom_test::{Phantom, Session, SessionBuilder};
use television::cable::CABLE_DIR_NAME;
use television::config::CONFIG_FILE_NAME;
use tempfile::{TempDir, tempdir};
pub const CI_ENV_VAR: &str = "TV_CI";
pub fn is_ci() -> bool {
std::env::var(CI_ENV_VAR).is_ok()
}
pub const DEFAULT_CONFIG_FILE: &str = "./.config/config.toml";
#[cfg(unix)]
pub const DEFAULT_CABLE_DIR: &str = "./cable/unix";
#[cfg(windows)]
pub const DEFAULT_CABLE_DIR: &str = "./cable/windows";
pub const TARGET_DIR: &str = "./tests/target_dir";
pub const DEFAULT_COLS: u16 = 120;
pub const DEFAULT_ROWS: u16 = 30;
pub fn stable_ms() -> u64 {
std::env::var("TV_TEST_STABLE_MS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(300)
}
pub fn wait_timeout_ms() -> u64 {
if let Some(v) = std::env::var("TV_TEST_WAIT_MS")
.ok()
.and_then(|v| v.parse().ok())
{
return v;
}
if is_ci() { 15_000 } else { 5_000 }
}
pub const TV_BIN_PATH: &str = match option_env!("TV_BIN_PATH") {
Some(v) => v,
None => "./target/debug/tv",
};
pub const LOCAL_CONFIG_AND_CABLE: &[&str] = &[
"--cable-dir",
DEFAULT_CABLE_DIR,
"--config-file",
DEFAULT_CONFIG_FILE,
];
pub fn phantom() -> Phantom {
Phantom::new().expect("failed to create phantom engine")
}
pub fn tv(pt: &Phantom) -> SessionBuilder<'_> {
let cwd = std::env::current_dir().expect("failed to get cwd");
pt.run(TV_BIN_PATH)
.size(DEFAULT_COLS, DEFAULT_ROWS)
.cwd(cwd.to_str().expect("cwd is not valid utf-8"))
}
pub fn tv_with_args<'p>(pt: &'p Phantom, args: &[&str]) -> SessionBuilder<'p> {
tv(pt).args(args)
}
pub fn tv_local_config_and_cable_with_args<'p>(
pt: &'p Phantom,
extra_args: &[&str],
) -> SessionBuilder<'p> {
let mut combined: Vec<&str> =
Vec::with_capacity(LOCAL_CONFIG_AND_CABLE.len() + extra_args.len());
combined.extend_from_slice(LOCAL_CONFIG_AND_CABLE);
combined.extend_from_slice(extra_args);
tv(pt).args(&combined)
}
pub fn exit_and_output(s: &Session) -> String {
s.wait()
.exit_code(0)
.timeout_ms(wait_timeout_ms())
.until()
.expect("tv did not exit with code 0 within timeout");
s.output().expect("failed to read tv output")
}
pub fn assert_frame_not_contains_any(s: &Session, texts: &[&str]) {
s.wait().stable(stable_ms()).until().unwrap();
let screen = s.screenshot().unwrap();
let text = screen.text();
for &needle in texts {
assert!(
!text.contains(needle),
"expected screen not to contain '{needle}', but got:\n{text}"
);
}
}
pub fn assert_frame_not_contains(s: &Session, text: &str) {
assert_frame_not_contains_any(s, &[text]);
}
pub fn stable_frame(s: &Session) -> String {
s.wait().stable(stable_ms()).until().unwrap();
s.screenshot().unwrap().text().to_string()
}
pub struct TempConfig {
pub config_file: PathBuf,
pub cable_dir: PathBuf,
_tempdir: TempDir,
}
impl TempConfig {
pub fn init() -> Self {
let dir = tempdir().expect("tempdir creation failed");
let cable_dir = dir.path().join(CABLE_DIR_NAME);
fs::create_dir_all(&cable_dir)
.expect("cable directory creation failed");
let config_file = dir.path().join(CONFIG_FILE_NAME);
fs::write(&config_file, "").expect("failed touching config file");
Self {
config_file,
cable_dir,
_tempdir: dir,
}
}
pub fn write_config(&self, contents: &str) -> io::Result<()> {
fs::write(&self.config_file, contents)
}
pub fn write_channel(&self, name: &str, contents: &str) -> io::Result<()> {
fs::write(self.cable_dir.join(name).with_extension("toml"), contents)
}
}