#![cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
use pixelsrc::wasm::{list_sprites, render_to_png, render_to_rgba, validate};
const MINIMAL_DOT: &str =
r##"{"type": "sprite", "name": "dot", "palette": {"{x}": "#FF0000"}, "grid": ["{x}"]}"##;
const HEART_SPRITE: &str = r##"{"type": "palette", "name": "reds", "colors": {"{_}": "#00000000", "{r}": "#FF0000", "{p}": "#FF6B6B"}}
{"type": "sprite", "name": "heart", "palette": "reds", "grid": ["{_}{r}{r}{_}", "{r}{r}{r}{r}", "{_}{r}{r}{_}", "{_}{_}{r}{_}"]}"##;
const TRANSPARENT_SPRITE: &str = r##"{"type": "sprite", "name": "transparent", "palette": {"{_}": "#00000000", "{x}": "#FF000080"}, "grid": ["{_}{x}", "{x}{_}"]}"##;
const MULTI_SPRITE: &str = r##"{"type": "sprite", "name": "first", "palette": {"{a}": "#FF0000"}, "grid": ["{a}"]}
{"type": "sprite", "name": "second", "palette": {"{b}": "#00FF00"}, "grid": ["{b}"]}
{"type": "sprite", "name": "third", "palette": {"{c}": "#0000FF"}, "grid": ["{c}"]}"##;
#[wasm_bindgen_test]
fn test_render_minimal_sprite_to_png() {
let result = render_to_png(MINIMAL_DOT);
assert!(!result.is_empty(), "PNG output should not be empty");
assert!(result.len() >= 8, "PNG should have at least header bytes");
assert_eq!(result[0], 0x89, "First PNG magic byte");
assert_eq!(result[1], 0x50, "Second PNG magic byte (P)");
assert_eq!(result[2], 0x4E, "Third PNG magic byte (N)");
assert_eq!(result[3], 0x47, "Fourth PNG magic byte (G)");
}
#[wasm_bindgen_test]
fn test_render_png_empty_input() {
let result = render_to_png("");
assert!(result.is_empty(), "Empty input should produce empty PNG");
}
#[wasm_bindgen_test]
fn test_render_png_palette_only() {
let jsonl = r##"{"type": "palette", "name": "test", "colors": {"{x}": "#FF0000"}}"##;
let result = render_to_png(jsonl);
assert!(result.is_empty(), "Palette-only input should produce empty PNG");
}
#[wasm_bindgen_test]
fn test_render_png_complex_sprite() {
let result = render_to_png(HEART_SPRITE);
assert!(!result.is_empty(), "Complex sprite should produce PNG");
assert_eq!(&result[0..4], &[0x89, 0x50, 0x4E, 0x47], "Should have PNG magic bytes");
}
#[wasm_bindgen_test]
fn test_render_minimal_sprite_to_rgba() {
let result = render_to_rgba(MINIMAL_DOT);
assert_eq!(result.width(), 1, "Width should be 1");
assert_eq!(result.height(), 1, "Height should be 1");
let pixels = result.pixels();
assert_eq!(pixels.len(), 4, "Should have 4 bytes (1 RGBA pixel)");
assert_eq!(pixels[0], 255, "Red channel");
assert_eq!(pixels[1], 0, "Green channel");
assert_eq!(pixels[2], 0, "Blue channel");
assert_eq!(pixels[3], 255, "Alpha channel (opaque)");
}
#[wasm_bindgen_test]
fn test_render_rgba_no_sprites() {
let result = render_to_rgba(r##"{"type": "palette", "name": "empty", "colors": {}}"##);
assert_eq!(result.width(), 0, "Width should be 0 when no sprites");
assert_eq!(result.height(), 0, "Height should be 0 when no sprites");
assert!(result.pixels().is_empty(), "Pixels should be empty");
let warnings = result.warnings();
assert!(warnings.iter().any(|w| w.contains("No sprites")), "Should warn about missing sprites");
}
#[wasm_bindgen_test]
fn test_render_rgba_dimensions() {
let result = render_to_rgba(HEART_SPRITE);
assert_eq!(result.width(), 4, "Heart width should be 4");
assert_eq!(result.height(), 4, "Heart height should be 4");
let pixels = result.pixels();
assert_eq!(pixels.len(), 4 * 4 * 4, "Should have 16 pixels * 4 bytes");
}
#[wasm_bindgen_test]
fn test_render_rgba_empty_input() {
let result = render_to_rgba("");
assert_eq!(result.width(), 0);
assert_eq!(result.height(), 0);
assert!(result.pixels().is_empty());
}
#[wasm_bindgen_test]
fn test_render_named_sprite_first() {
let result = render_to_rgba(MULTI_SPRITE);
assert_eq!(result.width(), 1);
assert_eq!(result.height(), 1);
let pixels = result.pixels();
assert_eq!(pixels[0], 255, "Should render first sprite (red)");
assert_eq!(pixels[1], 0);
assert_eq!(pixels[2], 0);
}
#[wasm_bindgen_test]
fn test_render_named_sprite_with_palette_ref() {
let result = render_to_rgba(HEART_SPRITE);
assert_eq!(result.width(), 4);
assert_eq!(result.height(), 4);
let warnings = result.warnings();
assert!(
!warnings.iter().any(|w| w.contains("not found")),
"Should not warn about palette: {:?}",
warnings
);
}
#[wasm_bindgen_test]
fn test_render_named_sprite_inline_palette() {
let jsonl = r##"{"type": "sprite", "name": "inline_test", "palette": {"{g}": "#00FF00"}, "grid": ["{g}"]}"##;
let result = render_to_rgba(jsonl);
assert_eq!(result.width(), 1);
assert_eq!(result.height(), 1);
let pixels = result.pixels();
assert_eq!(pixels[0], 0);
assert_eq!(pixels[1], 255);
assert_eq!(pixels[2], 0);
}
#[wasm_bindgen_test]
fn test_list_sprites_single() {
let result = list_sprites(MINIMAL_DOT);
assert_eq!(result.len(), 1);
assert_eq!(result[0], "dot");
}
#[wasm_bindgen_test]
fn test_list_sprites_multiple() {
let result = list_sprites(MULTI_SPRITE);
assert_eq!(result.len(), 3);
assert_eq!(result[0], "first");
assert_eq!(result[1], "second");
assert_eq!(result[2], "third");
}
#[wasm_bindgen_test]
fn test_list_sprites_empty() {
let result = list_sprites("");
assert!(result.is_empty());
}
#[wasm_bindgen_test]
fn test_list_sprites_palette_only() {
let jsonl = r##"{"type": "palette", "name": "test", "colors": {}}"##;
let result = list_sprites(jsonl);
assert!(result.is_empty());
}
#[wasm_bindgen_test]
fn test_list_sprites_mixed_content() {
let jsonl = r##"{"type": "palette", "name": "p1", "colors": {}}
{"type": "sprite", "name": "s1", "palette": {}, "grid": []}
{"type": "palette", "name": "p2", "colors": {}}
{"type": "sprite", "name": "s2", "palette": {}, "grid": []}"##;
let result = list_sprites(jsonl);
assert_eq!(result.len(), 2);
assert_eq!(result[0], "s1");
assert_eq!(result[1], "s2");
}
#[wasm_bindgen_test]
fn test_validate_valid_input() {
let result = validate(HEART_SPRITE);
assert!(result.is_empty(), "Valid input should have no warnings: {:?}", result);
}
#[wasm_bindgen_test]
fn test_validate_valid_minimal() {
let result = validate(MINIMAL_DOT);
assert!(result.is_empty(), "Minimal valid sprite should have no warnings");
}
#[wasm_bindgen_test]
fn test_validate_invalid_json() {
let result = validate(r##"{"type": "sprite", "name": "bad""##);
assert!(!result.is_empty(), "Invalid JSON should produce warnings");
}
#[wasm_bindgen_test]
fn test_validate_missing_palette() {
let jsonl =
r##"{"type": "sprite", "name": "orphan", "palette": "nonexistent", "grid": ["{x}"]}"##;
let result = validate(jsonl);
assert!(!result.is_empty(), "Missing palette reference should warn");
assert!(
result.iter().any(|w| w.contains("not found")),
"Should mention palette not found: {:?}",
result
);
}
#[wasm_bindgen_test]
fn test_validate_empty_input() {
let result = validate("");
assert!(result.is_empty(), "Empty input is valid (no errors)");
}
#[wasm_bindgen_test]
fn test_validate_unknown_type() {
let result = validate(r##"{"type": "unknown", "name": "test"}"##);
let _ = result;
}
#[wasm_bindgen_test]
fn test_validate_size_mismatch() {
let jsonl = r##"{"type": "sprite", "name": "mismatch", "palette": {"{x}": "#FF0000"}, "size": [3, 1], "grid": ["{x}"]}"##;
let result = validate(jsonl);
let _ = result;
}
#[wasm_bindgen_test]
fn test_render_with_transparency_full() {
let jsonl = r##"{"type": "sprite", "name": "clear", "palette": {"{_}": "#00000000"}, "grid": ["{_}"]}"##;
let result = render_to_rgba(jsonl);
assert_eq!(result.width(), 1);
assert_eq!(result.height(), 1);
let pixels = result.pixels();
assert_eq!(pixels[3], 0, "Alpha should be 0 (fully transparent)");
}
#[wasm_bindgen_test]
fn test_render_with_transparency_partial() {
let jsonl =
r##"{"type": "sprite", "name": "semi", "palette": {"{s}": "#FF000080"}, "grid": ["{s}"]}"##;
let result = render_to_rgba(jsonl);
let pixels = result.pixels();
assert_eq!(pixels[0], 255, "Red channel");
assert_eq!(pixels[1], 0, "Green channel");
assert_eq!(pixels[2], 0, "Blue channel");
assert_eq!(pixels[3], 128, "Alpha should be 128 (50% transparent)");
}
#[wasm_bindgen_test]
fn test_render_with_transparency_mixed() {
let result = render_to_rgba(TRANSPARENT_SPRITE);
assert_eq!(result.width(), 2);
assert_eq!(result.height(), 2);
let pixels = result.pixels();
assert_eq!(pixels[3], 0, "First pixel should be transparent");
assert_eq!(pixels[4], 255, "Second pixel red channel");
assert_eq!(pixels[7], 128, "Second pixel alpha (semi)");
assert_eq!(pixels[8], 255, "Third pixel red channel");
assert_eq!(pixels[11], 128, "Third pixel alpha (semi)");
assert_eq!(pixels[15], 0, "Fourth pixel should be transparent");
}
#[wasm_bindgen_test]
fn test_render_transparency_in_png() {
let result = render_to_png(TRANSPARENT_SPRITE);
assert!(!result.is_empty(), "Should produce PNG");
assert_eq!(&result[0..4], &[0x89, 0x50, 0x4E, 0x47], "Valid PNG");
assert!(result.len() > 16, "PNG should have substantial content");
}
#[wasm_bindgen_test]
fn test_malformed_json_lines() {
let jsonl = r##"{"type": "sprite", "name": "good", "palette": {"{x}": "#FF0000"}, "grid": ["{x}"]}
not valid json
{"type": "sprite", "name": "also_good", "palette": {"{y}": "#00FF00"}, "grid": ["{y}"]}"##;
let sprites = list_sprites(jsonl);
assert!(sprites.len() >= 1, "Should parse at least one sprite");
}
#[wasm_bindgen_test]
fn test_unicode_sprite_name() {
let jsonl = r##"{"type": "sprite", "name": "emoji_🚀", "palette": {"{x}": "#FF0000"}, "grid": ["{x}"]}"##;
let result = list_sprites(jsonl);
assert_eq!(result.len(), 1);
assert!(result[0].contains("emoji") || result[0].contains("🚀"));
}
#[wasm_bindgen_test]
fn test_large_sprite() {
let mut grid = Vec::new();
for _ in 0..10 {
grid.push(r#""{x}{x}{x}{x}{x}{x}{x}{x}{x}{x}""#);
}
let jsonl = format!(
r##"{{"type": "sprite", "name": "large", "palette": {{"{{x}}": "#FF0000"}}, "grid": [{}]}}"##,
grid.join(",")
);
let result = render_to_rgba(&jsonl);
assert_eq!(result.width(), 10);
assert_eq!(result.height(), 10);
assert_eq!(result.pixels().len(), 10 * 10 * 4);
}