from __future__ import annotations from pathlib import Path from PIL import Image from tmux_shot.render import frames_to_gif, render_ansi_to_png def test_render_plain_text_produces_correctly_sized_image(out_dir: Path) -> None: path = render_ansi_to_png("hello world", out_dir / "plain.png", cols=20, rows=3, font_size=14) img = Image.open(path) assert img.size[0] > 0 and img.size[1] > 0 # 20 cols x 3 rows of cells, image should be a whole multiple of cell size in each axis assert img.width % 20 == 0 assert img.height % 3 == 0 def test_render_resolves_truecolor_background_and_foreground(out_dir: Path) -> None: # SGR 38;2 = truecolor fg, 48;2 = truecolor bg. Pure red bg, pure green text. ansi = "\x1b[48;2;255;0;0m\x1b[38;2;0;255;0mX\x1b[0m" path = render_ansi_to_png(ansi, out_dir / "color.png", cols=1, rows=1, font_size=20) img = Image.open(path) # corner pixel is background-only (no glyph ink there) corner = img.getpixel((0, 0)) assert corner == (255, 0, 0) def test_render_reverse_video_swaps_fg_and_bg(out_dir: Path) -> None: ansi = "\x1b[38;2;255;0;0m\x1b[48;2;0;0;255m\x1b[7mX\x1b[0m" # red fg, blue bg, reversed path = render_ansi_to_png(ansi, out_dir / "reverse.png", cols=1, rows=1, font_size=20) img = Image.open(path) # reversed: background should now be the (originally-foreground) red assert img.getpixel((0, 0)) == (255, 0, 0) def test_render_handles_ansi_16_color_names(out_dir: Path) -> None: ansi = "\x1b[42mX\x1b[0m" # classic SGR green background path = render_ansi_to_png(ansi, out_dir / "named.png", cols=1, rows=1, font_size=20) img = Image.open(path) assert img.getpixel((0, 0)) == (0, 205, 0) def test_frames_to_gif_stitches_multiple_pngs_into_one_animation(out_dir: Path) -> None: frame_paths = [] for i, label in enumerate(["one", "two", "three"]): p = render_ansi_to_png(f"frame {label}", out_dir / f"f{i}.png", cols=10, rows=1, font_size=14) frame_paths.append(p) gif_path = frames_to_gif(frame_paths, out_dir / "movie.gif", duration_ms=100) assert Path(gif_path).exists() with Image.open(gif_path) as gif: assert gif.is_animated assert gif.n_frames == 3 def test_frames_to_gif_rejects_empty_input(out_dir: Path) -> None: import pytest with pytest.raises(ValueError): frames_to_gif([], out_dir / "empty.gif")