116 lines
5.9 KiB
Rust
116 lines
5.9 KiB
Rust
//! M13.15 scenario 5 — Essential Graphics through the CLI: a MOGRT-style template built by an
|
|
//! After Effects-style script (`effectcraft-cli script`), its controls changed with commands on
|
|
//! an instance, rendered, exported as a template and imported into a fresh project.
|
|
|
|
use std::path::PathBuf;
|
|
use std::process::Command;
|
|
|
|
use serde_json::Value;
|
|
|
|
fn cli(dir: &PathBuf, args: &[&str]) -> (i32, Value) {
|
|
let out = Command::new(env!("CARGO_BIN_EXE_effectcraft-cli")).current_dir(dir).args(args).arg("--json").output().unwrap();
|
|
let stdout = String::from_utf8_lossy(&out.stdout).to_string();
|
|
let v = serde_json::from_str(stdout.trim()).unwrap_or_else(|e| panic!("{args:?}: {e}: {stdout} {}", String::from_utf8_lossy(&out.stderr)));
|
|
(out.status.code().unwrap_or(-1), v)
|
|
}
|
|
|
|
fn ok(dir: &PathBuf, args: &[&str]) -> Value {
|
|
let (code, v) = cli(dir, args);
|
|
assert_eq!(code, 0, "{args:?}: {v}");
|
|
v
|
|
}
|
|
|
|
const BUILD: &str = r#"
|
|
app.beginUndoGroup("Build Template");
|
|
var comp = app.project.items.addComp("Lower Third", 320, 90, 1, 2, 12);
|
|
var bar = comp.layers.addSolid([1, 1, 1], "Bar", 320, 40, 1, 2);
|
|
bar.property("ADBE Transform Group").property("ADBE Position").setValue([160, 65]);
|
|
var t = comp.layers.addText("Jane Doe");
|
|
t.name = "Name";
|
|
t.property("ADBE Transform Group").property("ADBE Position").setValue([160, 30]);
|
|
var fill = bar.property("ADBE Effect Parade").addProperty("ADBE Fill");
|
|
t.property("ADBE Text Properties").property("ADBE Text Document").addToMotionGraphicsTemplateAs(comp, "Title");
|
|
fill.property("ADBE Fill-0002").addToMotionGraphicsTemplateAs(comp, "Bar Color");
|
|
bar.property("ADBE Transform Group").property("ADBE Opacity").addToMotionGraphicsTemplateAs(comp, "Bar Opacity");
|
|
comp.motionGraphicsTemplateName = "Lower Third";
|
|
app.endUndoGroup();
|
|
writeLn("controls: " + comp.motionGraphicsTemplateControllerCount);
|
|
comp.motionGraphicsTemplateName;
|
|
"#;
|
|
|
|
#[test]
|
|
fn essential_graphics_template_via_script_and_commands() {
|
|
let dir = std::env::temp_dir().join(format!("ec-qa-template-{}", std::process::id()));
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
std::fs::write(dir.join("build.jsx"), BUILD).unwrap();
|
|
|
|
let r = ok(&dir, &["script", "build.jsx", "--save-as", "t.ecproj"]);
|
|
assert_eq!(r["ok"], true, "{r}");
|
|
assert_eq!(r["output"], "controls: 3");
|
|
let list = ok(&dir, &["exec", "essential.list", r##"{"comp":"Lower Third"}"##, "t.ecproj"]);
|
|
let controls: Vec<(String, String)> =
|
|
list["controls"].as_array().unwrap().iter().map(|c| (c["name"].as_str().unwrap().to_string(), c["type"].as_str().unwrap().to_string())).collect();
|
|
assert_eq!(
|
|
controls,
|
|
[("Title".into(), "text".into()), ("Bar Color".into(), "color".into()), ("Bar Opacity".into(), "slider".into())],
|
|
"ADBE Fill-0002 is the Fill's Color: {list}"
|
|
);
|
|
|
|
// An edit comp with an instance; controls addressed by name.
|
|
let r = ok(
|
|
&dir,
|
|
&[
|
|
"run",
|
|
"t.ecproj",
|
|
"comp.new",
|
|
r##"{"name":"Edit","width":320,"height":90,"frameRate":12,"duration":2}"##,
|
|
"layer.addItem",
|
|
r##"{"item":"Lower Third"}"##,
|
|
"comp.new",
|
|
r##"{"name":"Plain","width":320,"height":90,"frameRate":12,"duration":2}"##,
|
|
"layer.addItem",
|
|
r##"{"item":"Lower Third"}"##,
|
|
"comp.open",
|
|
r##"{"comp":"Edit"}"##,
|
|
"essential.set",
|
|
r##"{"layer":"#1","control":"Title","value":"John Smith"}"##,
|
|
"essential.set",
|
|
r##"{"layer":"#1","control":"Bar Color","value":"#00c080"}"##,
|
|
"essential.set",
|
|
r##"{"layer":"#1","control":"bar opacity","value":50}"##,
|
|
"--save",
|
|
],
|
|
);
|
|
let inst = ok(&dir, &["exec", "essential.instance", r##"{"comp":"Edit","layer":"#1"}"##, "t.ecproj"]);
|
|
assert!(inst["controls"].as_array().unwrap().iter().all(|c| c["overridden"] == true), "{inst}");
|
|
let (code, e) = cli(&dir, &["exec", "essential.set", r##"{"comp":"Edit","layer":"#1","control":"Subtitle","value":"x"}"##, "t.ecproj"]);
|
|
assert_eq!(code, 1);
|
|
assert!(e["error"].as_str().unwrap().contains("controls: Title, Bar Color, Bar Opacity"), "{e} ({r})");
|
|
|
|
// Render both instances and compare.
|
|
ok(&dir, &["render-frame", "t.ecproj", "--comp", "Edit", "--time", "1", "--out", "edit.png"]);
|
|
ok(&dir, &["render-frame", "t.ecproj", "--comp", "Plain", "--time", "1", "--out", "plain.png"]);
|
|
let edit = image::open(dir.join("edit.png")).unwrap().to_rgba8();
|
|
let plain = image::open(dir.join("plain.png")).unwrap().to_rgba8();
|
|
let bar = edit.get_pixel(10, 75).0;
|
|
assert!(bar[0] < 15 && (bar[1] as i32 - 96).abs() < 8 && (bar[2] as i32 - 64).abs() < 8, "50 % of #00c080 over black: {bar:?}");
|
|
let pbar = plain.get_pixel(10, 75).0;
|
|
assert!(pbar[0] > 240 && pbar[1] < 15, "the template's own red: {pbar:?}");
|
|
let diff = |y0: u32, y1: u32| -> u32 {
|
|
(y0..y1).flat_map(|y| (0..320).map(move |x| (x, y))).filter(|(x, y)| edit.get_pixel(*x, *y).0 != plain.get_pixel(*x, *y).0).count() as u32
|
|
};
|
|
assert!(diff(10, 45) > 100, "the title text differs");
|
|
|
|
// Export the template and bring it into a fresh project.
|
|
let x = ok(&dir, &["exec", "essential.exportTemplate", r##"{"comp":"Lower Third","path":"lt.ectemplate"}"##, "t.ecproj"]);
|
|
assert_eq!(x["controls"], 3, "{x}");
|
|
let info = ok(&dir, &["exec", "essential.templateInfo", r##"{"path":"lt.ectemplate"}"##, "--empty"]);
|
|
assert_eq!(info["name"], "Lower Third", "{info}");
|
|
let imp = ok(&dir, &["run", "--empty", "essential.importTemplate", r##"{"path":"lt.ectemplate"}"##, "--save-as", "fresh.ecproj"]);
|
|
assert!(imp["saved"].is_string(), "{imp}");
|
|
let list = ok(&dir, &["exec", "essential.list", r##"{"comp":"Lower Third"}"##, "fresh.ecproj"]);
|
|
assert_eq!(list["controls"].as_array().unwrap().len(), 3, "{list}");
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
}
|