use std::fs;
use std::path::Path;
use std::time::Duration;
use base64::Engine;
use regex::Regex;
use super::browser::Page;
use super::{build_app, preview, scratch, write_files};
const WAIT: Duration = Duration::from_secs(20);
const PNG_1X1: &str = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
const BMP_1X1: &str =
"Qk06AAAAAAAAADYAAAAoAAAAAQAAAAEAAAABABgAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AA==";
fn write_base64(path: &Path, b64: &str) {
let bytes = base64::engine::general_purpose::STANDARD
.decode(b64)
.unwrap();
fs::write(path, bytes).unwrap();
}
async fn build_clean(root: &Path) -> Vec<String> {
let _ = fs::remove_dir_all(root.join("dist"));
build_app(root).await.expect("the app builds");
fs::read_dir(root.join("dist/assets"))
.unwrap()
.flatten()
.map(|e| e.file_name().to_string_lossy().into_owned())
.collect()
}
#[tokio::test(flavor = "multi_thread")]
async fn assets_under_the_limit_inline_and_limit_zero_emits_all() {
let root = scratch("assets-inline");
let big = format!(
r#"<svg xmlns="http://www.w3.org/2000/svg">{}</svg>"#,
"<rect/>".repeat(900)
);
write_files(
&root,
&[
("package.json", r#"{"name":"inline-app","version":"1.0.0"}"#),
("src/small.svg", r#"<svg xmlns="http://www.w3.org/2000/svg"><rect/></svg>"#),
("src/big.svg", big.as_str()),
(
"src/crlf.svg",
"<svg xmlns=\"http://www.w3.org/2000/svg\">\r\n <rect width=\"1\" height=\"1\"/>\r\n</svg>\r\n",
),
(
"src/main.js",
"import small from \"./small.svg\";\nimport big from \"./big.svg\";\nimport png from \"./dot.png\";\n\
import crlf from \"./crlf.svg\";\nimport bmp from \"./dot.bmp\";\n\
window.__S = small; window.__B = big; window.__PNG = png; window.__CRLF = crlf; window.__BMP = bmp;\n\
window.__READY = true;\n",
),
(
"index.html",
r#"<!doctype html><html><head><title>t</title></head><body><script type="module" src="/src/main.js"></script></body></html>"#,
),
],
);
write_base64(&root.join("src/dot.png"), PNG_1X1);
write_base64(&root.join("src/dot.bmp"), BMP_1X1);
let files = build_clean(&root).await;
assert!(
files
.iter()
.any(|f| f.starts_with("big-") && f.ends_with(".svg")),
"big.svg emitted (over limit): {files:?}"
);
assert!(
!files
.iter()
.any(|f| f.starts_with("small-") && f.ends_with(".svg")),
"small.svg inlined (under limit): {files:?}"
);
assert!(
!files.iter().any(|f| f.ends_with(".png")),
"png inlined (under limit): {files:?}"
);
if !super::browser::skip() {
let server = preview(&root.join("dist"), "/").await;
let page = Page::open(&server.url).await;
page.wait_for("window.__READY", WAIT).await;
let s: String = page.eval("window.__S").await;
let b: String = page.eval("window.__B").await;
let png: String = page.eval("window.__PNG").await;
let crlf: String = page.eval("window.__CRLF").await;
let bmp: String = page.eval("window.__BMP").await;
assert!(
s.starts_with("data:image/svg+xml,"),
"small svg is an inline data uri: {s}"
);
assert!(
png.starts_with("data:image/png;base64,"),
"png is an inline base64 data uri: {png}"
);
assert!(
Regex::new(r"/assets/big-.*\.svg$").unwrap().is_match(&b),
"big svg is an emitted file url: {b}"
);
assert!(
crlf.starts_with("data:image/svg+xml,"),
"a CRLF svg inlines too: {crlf}"
);
assert!(
!crlf.contains('\r') && !crlf.contains('\n'),
"no line terminator survives in the data uri: {crlf:?}"
);
assert!(
crlf.contains("%3crect"),
"the CRLF svg kept its content: {crlf}"
);
assert!(
bmp.starts_with("data:image/bmp;base64,"),
"bmp inlines with its own mime type: {bmp}"
);
assert_eq!(page.errors(), Vec::<String>::new(), "page errors");
page.close().await;
}
write_files(
&root,
&[("oj.config.json", r#"{"build":{"assetsInlineLimit":0}}"#)],
);
let files = build_clean(&root).await;
assert!(
files.iter().any(|f| f.starts_with("small-")) && files.iter().any(|f| f.ends_with(".png")),
"limit 0 emits every asset: {files:?}"
);
assert!(
files.iter().any(|f| f.ends_with(".bmp")),
"limit 0 emits the bmp too: {files:?}"
);
}