open-standards/wai/wai-app/tests/exports.rs
Transaction Science dd970f3407 The catalogue's check reads every keyword, and wai's manifests name no format
joule-ui-catalogue now holds the node form both ways: the reading of a node and the
catalogue writer. joule-ui-a2ui keeps its names on that reading, so wai-app depends on
the neutral crate and neither format's name is left under wai/, in a manifest or a
lock file. A test holds the two lock files and the manifest to that.

check_catalogue compares the document with what the registry writes, every keyword of
it, and reads it as a schema: on each node it builds, the catalogue and the reading
must agree. That includes a node with no props for a widget that requires some, a key
folding onto a slot of an open widget, and every child a widget does not admit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-07 03:08:05 -05:00

71 lines
3.3 KiB
Rust

//! The registry's two exports, committed under `widgets/`, are what the registry gives
//! today, byte for byte, and the catalogue is one the registry's ingest takes back. A
//! widget added to the registry fails here until the files are written again:
//!
//! WAI_APP_BLESS=1 cargo test --test exports the_committed_files
use joule_ui_core::{PropSchema, PropType, WidgetSchema};
use joule_ui_lang::Library;
use std::path::PathBuf;
use wai_app::registry::{self, ROOT};
fn dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("widgets")
}
fn committed(name: &str) -> Vec<u8> {
std::fs::read(dir().join(name)).unwrap_or_else(|e| panic!("{name}: {e} (write it with WAI_APP_BLESS=1 cargo test --test exports the_committed_files)"))
}
#[test]
fn the_committed_files_are_what_the_registry_gives() {
let bless = std::env::var_os("WAI_APP_BLESS").is_some();
for (name, bytes) in registry::exports() {
if bless {
std::fs::write(dir().join(name), &bytes).unwrap();
}
assert!(committed(name) == bytes, "{name} is not what the registry gives: write it with WAI_APP_BLESS=1 cargo test --test exports the_committed_files");
}
let mut listed: Vec<String> = std::fs::read_dir(dir()).unwrap().map(|e| e.unwrap().file_name().to_string_lossy().into_owned()).filter(|n| !n.starts_with('.')).collect();
listed.sort();
assert_eq!(listed, ["catalogue.json", "library.json"], "the directory holds the exports alone");
}
/// The drift test's own negative control: the same comparison on a registry with one
/// widget more, or one prop more, fails for both files.
#[test]
fn a_registry_changed_without_writing_the_files_again_differs_from_them() {
let added = registry::registry().with(WidgetSchema::new("Clock", "The room's clock."));
let widened = registry::registry().with(WidgetSchema::new("Chat", "The room's chat.").prop("limit", PropSchema::optional(PropType::Number)));
for r in [added, widened] {
let lib = Library::new(r, Some(ROOT.into())).unwrap();
for (name, bytes) in registry::exports_of(&lib) {
assert!(committed(name) != bytes, "{name} did not change with the registry");
}
}
}
#[test]
fn the_committed_catalogue_comes_back_through_the_ingest() {
let v: serde_json::Value = serde_json::from_slice(&committed("catalogue.json")).unwrap();
joule_ui_catalogue::check_catalogue(&v, &registry::registry()).unwrap();
assert_eq!(v["$ref"], format!("#/$defs/{ROOT}"));
assert_eq!(v["$id"], registry::CATALOGUE_ID);
for n in registry::library().names() {
assert_eq!(v["$defs"][&n]["description"].as_str(), registry::registry().get(&n).map(|s| s.description.as_str()), "{n}");
}
}
/// The published files are WAI's widgets and nothing else, and the manifests that
/// build them say the same: none of them names a format.
#[test]
fn the_files_name_no_format() {
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let manifests = ["Cargo.toml", "Cargo.lock", "../wai-rs/Cargo.lock"].map(|f| (f, std::fs::read(root.join(f)).unwrap()));
for (name, bytes) in registry::exports().into_iter().chain(manifests) {
let text = String::from_utf8(bytes).unwrap().to_lowercase();
for word in [concat!("a", "2ui"), concat!("open", "ui")] {
assert!(!text.contains(word), "{name} names a format");
}
}
}