Add Mini App artifact viewer tests
Build Claw Telegram / cleanup (push) Has been cancelled
Build Claw Telegram / build (push) Has been cancelled

This commit is contained in:
Wylabb
2026-04-05 19:07:51 +02:00
parent 0f0d41bd8d
commit a02dd94542
+95 -17
View File
@@ -6,7 +6,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
use axum::extract::{Path as AxumPath, Query, State};
use axum::http::{header, HeaderMap, StatusCode};
use axum::response::{Html, IntoResponse, Response};
use axum::response::{Html, Response};
use axum::routing::{get, post};
use axum::{Json, Router};
use channel_gateway_core::ProfileRecord;
@@ -462,24 +462,28 @@ async fn artifact_viewer(
AxumPath((turn_id, file_id)): AxumPath<(String, String)>,
Query(query): Query<ArtifactViewerQuery>,
) -> Result<Html<String>, StatusCode> {
let _ = state;
let meta = crate::gateway::lookup_artifact(&turn_id, &file_id)
.await
.ok_or(StatusCode::NOT_FOUND)?;
let token_hint = query.token.as_deref().unwrap_or("");
let extension = std::path::Path::new(&meta.file_name)
.extension()
.and_then(|e| e.to_str())
.unwrap_or("")
.to_ascii_lowercase();
let render_mode = match extension.as_str() {
"html" | "htm" => "html",
"md" | "markdown" => "markdown",
"json" => "json",
"jsx" | "tsx" => "react",
_ => "code",
};
let escaped_name = meta.file_name.replace('<', "&lt;").replace('>', "&gt;");
let page = format!(
Ok(Html(build_artifact_viewer_page(
&meta.file_name,
&turn_id,
&file_id,
token_hint,
)))
}
fn build_artifact_viewer_page(
file_name: &str,
turn_id: &str,
file_id: &str,
token_hint: &str,
) -> String {
let escaped_name = escape_html(file_name);
let render_mode = artifact_render_mode(file_name);
format!(
r##"<!DOCTYPE html>
<html>
<head>
@@ -605,8 +609,30 @@ try {{
</script>
</body>
</html>"##
);
Ok(Html(page))
)
}
fn artifact_render_mode(file_name: &str) -> &'static str {
let extension = std::path::Path::new(file_name)
.extension()
.and_then(|e| e.to_str())
.unwrap_or("")
.to_ascii_lowercase();
match extension.as_str() {
"html" | "htm" => "html",
"md" | "markdown" => "markdown",
"json" => "json",
"jsx" | "tsx" => "react",
_ => "code",
}
}
fn escape_html(value: &str) -> String {
value
.replace('&', "&amp;")
.replace('<', "&lt;")
.replace('>', "&gt;")
.replace('"', "&quot;")
}
async fn authorize(
@@ -804,3 +830,55 @@ impl From<WorkerManagerError> for MiniAppError {
Self::Worker(error)
}
}
#[cfg(test)]
mod tests {
use super::{artifact_render_mode, build_artifact_viewer_page, escape_html};
#[test]
fn artifact_render_mode_classifies_supported_extensions() {
assert_eq!(artifact_render_mode("report.html"), "html");
assert_eq!(artifact_render_mode("README.MD"), "markdown");
assert_eq!(artifact_render_mode("data.json"), "json");
assert_eq!(artifact_render_mode("widget.tsx"), "react");
assert_eq!(artifact_render_mode("component.jsx"), "react");
assert_eq!(artifact_render_mode("main.rs"), "code");
assert_eq!(artifact_render_mode("no-extension"), "code");
}
#[test]
fn escape_html_escapes_special_characters() {
assert_eq!(
escape_html("artifact & <preview> \"name\""),
"artifact &amp; &lt;preview&gt; &quot;name&quot;"
);
}
#[test]
fn artifact_viewer_page_uses_render_mode_and_escaped_name() {
let page = build_artifact_viewer_page(
"artifact <demo>.md",
"turn-1",
"file-1",
"session-token",
);
assert!(page.contains("<title>artifact &lt;demo&gt;.md</title>"));
assert!(page.contains("<h1>artifact &lt;demo&gt;.md</h1>"));
assert!(page.contains("const turnId = \"turn-1\";"));
assert!(page.contains("const fileId = \"file-1\";"));
assert!(page.contains("let token = \"session-token\";"));
assert!(page.contains("const renderMode = \"markdown\";"));
assert!(page.contains("marked.parse(text)"));
}
#[test]
fn artifact_viewer_page_includes_react_runtime_for_tsx() {
let page = build_artifact_viewer_page("demo.tsx", "turn-2", "file-2", "");
assert!(page.contains("const renderMode = \"react\";"));
assert!(page.contains("renderReactArtifact(el, text);"));
assert!(page.contains("react@18/umd/react.production.min.js"));
assert!(page.contains("@babel/standalone/babel.min.js"));
}
}