use super::without_fused_source_spans;
use crate::format;
use std::path::{Path, PathBuf};
fn assert_fused_matches_general(source: &str, what: &dyn std::fmt::Display) {
let fused = format(source).map_err(|e| e.to_string());
let general = without_fused_source_spans(|| format(source).map_err(|e| e.to_string()));
assert_eq!(fused, general, "fused and general output differ for {what}");
}
fn collect_svelte_files(dir: &Path, out: &mut Vec<PathBuf>) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
collect_svelte_files(&path, out);
} else if path.extension().is_some_and(|ext| ext == "svelte") {
out.push(path);
}
}
}
#[test]
fn fixture_trees_format_identically_without_fused_spans() {
let tests = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../tests");
if !tests.is_dir() {
eprintln!(
"skipped: {} is absent (outside the tsv repo)",
tests.display()
);
return;
}
let mut files = Vec::new();
collect_svelte_files(&tests.join("fixtures"), &mut files);
collect_svelte_files(&tests.join("fixtures_compile"), &mut files);
assert!(files.len() > 1000, "the fixture trees were not found");
for path in &files {
if let Ok(source) = std::fs::read_to_string(path) {
assert_fused_matches_general(&source, &path.display());
}
}
}
const INSERTS: &[&str] = &[
" ",
"\n",
"\t",
"/* c */",
"/** c */",
"// c\n",
"<!-- c -->",
"\"",
"'",
"=",
"(",
")",
"!",
"?",
".",
"#",
"\\u0061",
"\u{e9}",
"\u{301}",
"\u{1f600}",
"\u{600}",
"&",
];
const SEEDS: &[&str] = &[
"<div class=\"a b\" title=\"x y\" data-n=\"\">t</div>",
"<div class=\"a b \" id='x' lang=en title=\"it's\">t</div>",
"<Foo bar=\"baz\" {qux} quux={quux} a={b} c={d.e} f={g.h.i} j=\"{k}\" />",
"<p>{a} {b.c} {d.e.f} {this.g} {h?.i} {j[0]} {await} {k.class} {m[n]}</p>",
"<input bind:value={v} on:click={h} class:on={on} use:act={o.p} style:color={c} />",
"{#if a}{b}{:else if c.d}{e}{/if}",
"{#each xs as x, i (x.id)}{x.name}{i}{/each}",
"{#await p then v}{v}{:catch e}{e.message}{/await}",
"{#key k}{@html h}{@render r()}{@const c = d}{c}{/key}",
"<svelte:element this={tag} class=\"a\">{x}</svelte:element>",
"<a href=\"/x\"></a><b> </b><i>t</i><u>{v}</u><br /><Foo></Foo><Bar />",
"{#if (a)}{(b.c)}{/if}{#each (xs) as x}{ x }{/each}{@html h.i}{#key (k)}{k}{/key}",
"<script lang=\"ts\">let xs: T[];</script>{#each xs as x: T}{x}{/each}{#snippet s(a, b)}{a}{/snippet}",
"<div on:click={ h } bind:this={ el.x } {...rest} {@attach a.b} class:c={ c }>{@const d = e.f}</div>",
"<table><tr><td>t</td><td> </td><td></td></tr></table><select><option>o</option></select>",
"<div {a} {bb} cc={cc} dd=\"{dd}\" {class}><Foo {x} {y.z} {\u{e9}} /></div>",
];
#[test]
fn an_insert_at_every_position_formats_identically_without_fused_spans() {
for seed in SEEDS {
for at in (0..=seed.len()).filter(|&at| seed.is_char_boundary(at)) {
for insert in INSERTS {
let source = format!("{}{insert}{}", &seed[..at], &seed[at..]);
assert_fused_matches_general(&source, &format_args!("{source:?}"));
}
}
}
}
#[test]
fn a_line_walked_across_the_print_width_formats_identically_without_fused_spans() {
let values = [
"v",
"a\tb",
"\u{4e2d}\u{6587}",
"\u{301}x",
"x\u{600}",
"\u{1f600}",
];
for value in values {
for pad in 0..48 {
let pad = "p".repeat(pad);
for source in [
format!(
"<div aa=\"{pad}\" bbbbbbbbbb=\"{value}\" cc=\"dd ee\" {{ff}} gg={{hh}} ii={{jj.kk}}>{{ll}}</div>"
),
format!(
"<Foo aa=\"{pad}\" bbbbbbbbbb=\"{value}\" class=\"dd ee\" {{ff}} gg={{hh}} ii={{jj.kk}} />"
),
format!(
"<Foo aa=\"{pad}\" n{name}={{hh}} m{name}=\"vv\" {{ff}} gg={{hh}} iiiiiiiiiiiiiiii={{jj.kk}} />",
name = if value.contains('\t') { "" } else { value }
),
format!(
"<p>{pad} {{aaaaaaaaaa}} {{bbbbbbbbbb.cccccccccc}} {value} {{dddddddddd}} {{eeeeeeeeee.ffffffffff}} gggg</p>"
),
format!(
"{{#if {pad}aaaaaaaaaaaaaaaaaaaa.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}}<i title=\"{value}\">{{cccccccccccccccccccc}}</i>{{/if}}"
),
] {
assert_fused_matches_general(&source, &format_args!("{source:?}"));
}
}
}
}