use std::fs;
use std::path::{Path, PathBuf};
use axum::body::Body;
use axum::http::{Request, StatusCode};
use http_body_util::BodyExt;
use tokio::sync::broadcast;
use tower::ServiceExt;
use docgen_build::{build_site, BuildMode, BuildOptions};
use docgen_server::{router, AppState};
fn setup_fixture(root: &Path) {
let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let workspace = manifest.parent().unwrap().parent().unwrap();
let fixture = workspace.join("fixtures/site-basic");
fs::create_dir_all(root.join("docs/guide")).unwrap();
fs::copy(fixture.join("docs/index.md"), root.join("docs/index.md")).unwrap();
fs::copy(
fixture.join("docs/guide/intro.md"),
root.join("docs/guide/intro.md"),
)
.unwrap();
}
fn walk(dir: &Path, out: &mut Vec<PathBuf>) {
for entry in fs::read_dir(dir).unwrap() {
let p = entry.unwrap().path();
if p.is_dir() {
walk(&p, out);
} else {
out.push(p);
}
}
}
const DEV_MARKERS: &[&str] = &[
"__docgen/livereload",
"__docgen/editor",
"__codemirror",
"docgenEditor",
"EventSource('/__docgen/livereload')",
"data-docgen-edit",
];
#[test]
fn static_build_has_no_editor_or_reload() {
let root = tempfile::tempdir().unwrap();
setup_fixture(root.path());
let out = tempfile::tempdir().unwrap();
build_site(&BuildOptions {
project_root: root.path(),
out_dir: out.path(),
mode: BuildMode::Production,
})
.unwrap();
let mut files = Vec::new();
walk(out.path(), &mut files);
assert!(!files.is_empty(), "build emitted nothing");
for f in &files {
let rel = f
.strip_prefix(out.path())
.unwrap()
.to_string_lossy()
.to_string();
assert!(
!rel.contains("__codemirror") && !rel.contains("__docgen"),
"dev path leaked into static dist: {rel}"
);
if let Ok(text) = fs::read_to_string(f) {
for m in DEV_MARKERS {
assert!(
!text.contains(m),
"dev marker {m:?} leaked into static dist file {rel}"
);
}
}
}
assert!(!out.path().join("__codemirror/codemirror.js").exists());
assert!(!out.path().join("__docgen/editor.js").exists());
}
#[tokio::test]
async fn dev_serve_injects_editor_and_reload() {
let root = tempfile::tempdir().unwrap();
setup_fixture(root.path());
let out = tempfile::tempdir().unwrap();
build_site(&BuildOptions {
project_root: root.path(),
out_dir: out.path(),
mode: BuildMode::Dev,
})
.unwrap();
docgen_assets::emit(&docgen_assets::dev_assets(), out.path()).unwrap();
let docs_dir = root.path().join("docs").canonicalize().unwrap();
let (reload_tx, _rx) = broadcast::channel(16);
let state = AppState::new(
root.path().to_path_buf(),
out.path().to_path_buf(),
docs_dir,
4321,
reload_tx,
);
let resp = router(state)
.oneshot(
Request::builder()
.uri("/")
.header("host", "127.0.0.1:4321")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let body = resp.into_body().collect().await.unwrap().to_bytes();
let html = String::from_utf8(body.to_vec()).unwrap();
for m in ["__docgen/livereload.js", "docgen-ctl--edit", "/edit/"] {
assert!(html.contains(m), "dev serve missing injected marker {m}");
}
}