use super::*;
#[test]
fn test_strip_ansi() {
let (text, mapping) = strip_ansi("\x1b[31mred\x1b[0m");
assert_eq!(text, "red");
assert_eq!(mapping, vec![(5, 5), (6, 6), (7, 7)]);
let (text, mapping) = strip_ansi("\x1b[01;32mgreen\x1b[0m");
assert_eq!(text, "green");
assert_eq!(mapping, vec![(8, 8), (9, 9), (10, 10), (11, 11), (12, 12)]);
let (text, mapping) = strip_ansi("\x1b[01;34mblue\x1b[0m");
assert_eq!(text, "blue");
assert_eq!(mapping, vec![(8, 8), (9, 9), (10, 10), (11, 11)]);
let (text, mapping) = strip_ansi("plain text");
assert_eq!(text, "plain text");
assert_eq!(
mapping,
vec![
(0, 0),
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5),
(6, 6),
(7, 7),
(8, 8),
(9, 9)
]
);
let (text, mapping) = strip_ansi("\x1b[31mred\x1b[0m and \x1b[32mgreen\x1b[0m");
assert_eq!(text, "red and green");
assert_eq!(
mapping,
vec![
(5, 5),
(6, 6),
(7, 7),
(12, 12),
(13, 13),
(14, 14),
(15, 15),
(16, 16),
(22, 22),
(23, 23),
(24, 24),
(25, 25),
(26, 26)
]
);
let (text, mapping) = strip_ansi("be\x1b[01;34mf\x1b[0more");
assert_eq!(text, "before");
assert_eq!(mapping, vec![(0, 0), (1, 1), (10, 10), (15, 15), (16, 16), (17, 17)]);
let (text, mapping) = strip_ansi("\x1b[01;32mbench.sh\x1b[0m");
assert_eq!(text, "bench.sh");
assert_eq!(
mapping,
vec![
(8, 8),
(9, 9),
(10, 10),
(11, 11),
(12, 12),
(13, 13),
(14, 14),
(15, 15)
]
);
let (text, mapping) = strip_ansi("\x1b[01;34mbin\x1b[0m");
assert_eq!(text, "bin");
assert_eq!(mapping, vec![(8, 8), (9, 9), (10, 10)]);
let (text, mapping) = strip_ansi("😀\x1b[32mtext\x1b[0m");
assert_eq!(text, "😀text");
assert_eq!(mapping, vec![(0, 0), (9, 6), (10, 7), (11, 8), (12, 9)]);
}
#[test]
fn test_strip_ansi_osc_sequence_bel_terminated() {
let (text, _) = strip_ansi("\x1b]0;title\x07visible");
assert_eq!(text, "visible");
}
#[test]
fn test_strip_ansi_osc_sequence_st_terminated() {
let (text, _) = strip_ansi("\x1b]8;;http://example.com\x1b\\link");
assert_eq!(text, "link");
}
#[test]
fn test_strip_ansi_two_char_escape_sequences() {
let (text, _) = strip_ansi("\x1b(Bplain");
assert_eq!(text, "plain");
let (text, _) = strip_ansi("\x1b)0plain");
assert_eq!(text, "plain");
}
#[test]
fn test_strip_ansi_unknown_escape_consumes_one_char() {
let (text, _) = strip_ansi("\x1bXdata");
assert_eq!(text, "data");
}
#[test]
fn test_strip_ansi_trailing_lone_escape() {
let (text, _) = strip_ansi("abc\x1b");
assert_eq!(text, "abc");
}
#[test]
fn test_ansi_matching_and_display() {
use crate::{DisplayContext, Matches, SkimItem};
use ratatui::style::{Color, Style};
use regex::Regex;
let input = "\x1b[32mgreen\x1b[0m text";
let delimiter = Regex::new(r"\s+").unwrap();
let item = DefaultSkimItem::new(
input,
true, &[],
&[],
&delimiter,
);
assert_eq!(item.text(), "green text");
assert!(item.ansi_info().is_some());
let context = DisplayContext {
score: 100,
matches: Matches::CharRange(6, 10),
container_width: 80,
base_style: Style::default(),
matched_style: Style::default().fg(Color::Yellow),
};
let line = item.display(context);
assert!(!line.spans.is_empty());
}
#[test]
fn test_ansi_char_indices_mapping() {
use crate::{DisplayContext, Matches, SkimItem};
use ratatui::style::{Color, Style};
use regex::Regex;
let input = "😀\x1b[32mtext\x1b[0m";
let delimiter = Regex::new(r"\s+").unwrap();
let item = DefaultSkimItem::new(
input,
true, &[],
&[],
&delimiter,
);
assert_eq!(item.text(), "😀text");
let context = DisplayContext {
score: 100,
matches: Matches::CharIndices(vec![1, 2]),
container_width: 80,
base_style: Style::default(),
matched_style: Style::default().fg(Color::Yellow),
};
let line = item.display(context);
assert!(!line.spans.is_empty());
}
#[test]
fn test_text_returns_stripped() {
use crate::SkimItem;
use regex::Regex;
let delimiter = Regex::new(r"\s+").unwrap();
let item_ansi = DefaultSkimItem::new(
"\x1b[31mred\x1b[0m",
true, &[],
&[],
&delimiter,
);
assert_eq!(
item_ansi.text(),
"red",
"text() should return stripped text when ANSI is enabled"
);
let item_no_ansi = DefaultSkimItem::new(
"\x1b[31mred\x1b[0m",
false, &[],
&[],
&delimiter,
);
assert_eq!(
item_no_ansi.text(),
"?[31mred?[0m",
"text() should return text with ? when ANSI is disabled"
);
}
#[test]
fn test_highlighting_applied() {
use crate::{DisplayContext, Matches, SkimItem};
use ratatui::style::{Color, Style};
use regex::Regex;
let delimiter = Regex::new(r"\s+").unwrap();
let item = DefaultSkimItem::new(
"\x1b[32mgreen\x1b[0m",
true, &[],
&[],
&delimiter,
);
let context = DisplayContext {
score: 100,
matches: Matches::CharIndices(vec![0]),
container_width: 80,
base_style: Style::default(),
matched_style: Style::default().bg(Color::Yellow),
};
let line = item.display(context);
let has_highlight = line.spans.iter().any(|span| span.style.bg == Some(Color::Yellow));
assert!(has_highlight, "Highlighted character should have yellow background");
let has_green_fg = line.spans.iter().any(|span| span.style.fg == Some(Color::Green));
assert!(has_green_fg, "ANSI green foreground should be preserved");
}
#[test]
fn test_char_range_highlighting() {
use crate::{DisplayContext, Matches, SkimItem};
use ratatui::style::{Color, Style};
use regex::Regex;
let delimiter = Regex::new(r"\s+").unwrap();
let item = DefaultSkimItem::new(
"\x1b[32mgreen\x1b[0m",
true, &[],
&[],
&delimiter,
);
let context = DisplayContext {
score: 100,
matches: Matches::CharRange(1, 3),
container_width: 80,
base_style: Style::default(),
matched_style: Style::default().bg(Color::Yellow),
};
let line = item.display(context);
assert!(line.spans.len() >= 2, "Should have multiple spans for highlighting");
let has_highlight = line.spans.iter().any(|span| span.style.bg == Some(Color::Yellow));
assert!(has_highlight, "Highlighted characters should have yellow background");
let has_green_fg = line.spans.iter().any(|span| span.style.fg == Some(Color::Green));
assert!(has_green_fg, "ANSI green foreground should be preserved");
}
#[test]
fn test_byte_range_highlighting() {
use crate::{DisplayContext, Matches, SkimItem};
use ratatui::style::{Color, Style};
use regex::Regex;
let delimiter = Regex::new(r"\s+").unwrap();
let item = DefaultSkimItem::new(
"\x1b[32mgreen\x1b[0m",
true, &[],
&[],
&delimiter,
);
let context = DisplayContext {
score: 100,
matches: Matches::ByteRange(1, 3),
container_width: 80,
base_style: Style::default(),
matched_style: Style::default().bg(Color::Yellow),
};
let line = item.display(context);
assert!(!line.spans.is_empty(), "Should have spans");
let has_highlight = line.spans.iter().any(|span| span.style.bg == Some(Color::Yellow));
assert!(has_highlight, "Highlighted bytes should have yellow background");
let has_green_fg = line.spans.iter().any(|span| span.style.fg == Some(Color::Green));
assert!(has_green_fg, "ANSI green foreground should be preserved");
}
#[test]
fn test_matching_with_ansi_basic() {
use crate::SkimItem;
use regex::Regex;
let delimiter = Regex::new(r"\s+").unwrap();
let item = DefaultSkimItem::new(
"\x1b[32mgreen_text\x1b[0m",
true, &[],
&[], &delimiter,
);
assert_eq!(item.text(), "green_text");
assert!(item.get_matching_ranges().is_none());
assert!(item.stripped_text().is_some());
assert!(item.ansi_info().is_some());
assert_eq!(item.stripped_text().unwrap(), "green_text");
}
#[test]
fn test_null_delimiter_with_matching_fields() {
use crate::SkimItem;
use crate::field::FieldRange;
use regex::Regex;
let delimiter = Regex::new("\x00").unwrap();
let text = "a\x00b\x00c";
let item = DefaultSkimItem::new(
text,
false, &[], &[FieldRange::Single(2)], &delimiter,
);
assert_eq!(item.text(), "abc");
let ranges = item.get_matching_ranges().expect("Should have matching ranges");
assert_eq!(ranges.len(), 1, "Should have one matching range");
assert_eq!(ranges[0], (1, 2), "Field 2 should be at position 1-2 in stripped text");
let stripped_text = item.text();
let field_text = &stripped_text[ranges[0].0..ranges[0].1];
assert_eq!(field_text, "b", "Field text should be 'b'");
}
#[test]
fn test_default_skim_item_from_string_and_display_text() {
let item = DefaultSkimItem::from("plain text".to_string());
assert_eq!(item.get_display_text(), "plain text");
assert_eq!(item.text(), "plain text");
}
#[test]
fn test_transform_fields_with_ansi_enabled() {
use regex::Regex;
let delimiter = Regex::new(r"\s+").unwrap();
let item = DefaultSkimItem::new(
"\x1b[32mone\x1b[0m two three",
true,
&[FieldRange::Single(2)],
&[],
&delimiter,
);
assert!(item.text().contains("two"));
}
#[test]
fn test_matching_fields_with_ansi_uses_stripped_text() {
use regex::Regex;
let delimiter = Regex::new(r"\s+").unwrap();
let item = DefaultSkimItem::new(
"\x1b[32mone\x1b[0m two",
true,
&[],
&[FieldRange::Single(2)],
&delimiter,
);
assert_eq!(item.text(), "one two");
let ranges = item.get_matching_ranges().expect("matching ranges present");
assert_eq!(ranges.len(), 1);
}
#[test]
fn test_display_ansi_item_with_no_matches() {
use crate::{DisplayContext, Matches, SkimItem};
use ratatui::style::Style;
use regex::Regex;
let delimiter = Regex::new(r"\s+").unwrap();
let item = DefaultSkimItem::new("\x1b[31mred\x1b[0m text", true, &[], &[], &delimiter);
let context = DisplayContext {
score: 0,
matches: Matches::None,
container_width: 80,
base_style: Style::default(),
matched_style: Style::default(),
};
let line = item.display(context);
let text: String = line.spans.iter().map(|s| s.content.as_ref()).collect::<String>();
assert!(text.contains("red"));
assert!(text.contains("text"));
}