From 6e0b0c92d17bec61833915d71d333639d24f2217 Mon Sep 17 00:00:00 2001 From: Wind-Explorer Date: Mon, 10 Aug 2026 23:39:45 +0800 Subject: [PATCH] fix blank screen on load --- Dockerfile | 3 +++ frontend/src/main.test.ts | 22 ++++++++++++++++++++++ frontend/src/main.ts | 12 +++++++++++- frontend/vitest.config.ts | 10 +++++++--- tests/http_security.rs | 26 ++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 frontend/src/main.test.ts diff --git a/Dockerfile b/Dockerfile index af487df..58c4fbd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,9 @@ FROM rust:1.88-bookworm AS backend WORKDIR /build COPY Cargo.toml Cargo.lock* ./ COPY src ./src +COPY tests ./tests +COPY fixtures ./fixtures +COPY Dockerfile compose.example.yml ./ COPY --from=frontend /build/frontend/dist ./frontend/dist RUN cargo test --release && cargo build --release --bin minecraft-log-viewer diff --git a/frontend/src/main.test.ts b/frontend/src/main.test.ts new file mode 100644 index 0000000..822bcd2 --- /dev/null +++ b/frontend/src/main.test.ts @@ -0,0 +1,22 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; + +describe("application entry point", () => { + afterEach(() => { + vi.unstubAllGlobals(); + document.body.innerHTML = ""; + }); + + it("mounts the Svelte application", async () => { + document.body.innerHTML = '
'; + vi.stubGlobal( + "fetch", + vi.fn(() => new Promise(() => {})), + ); + + await import("./main"); + + expect(document.querySelector("header strong")?.textContent).toBe( + "Connecting", + ); + }); +}); diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 3a8d74c..60912b5 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1 +1,11 @@ -import './style.css';import App from './App.svelte';new App({target:document.getElementById('app')!}); +import "./style.css"; +import { mount } from "svelte"; +import App from "./App.svelte"; + +const target = document.getElementById("app"); + +if (!target) { + throw new Error("Missing #app mount target"); +} + +mount(App, { target }); diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts index b8e11f4..6f29bcd 100644 --- a/frontend/vitest.config.ts +++ b/frontend/vitest.config.ts @@ -1,3 +1,7 @@ -import { defineConfig } from 'vitest/config'; -import { svelte } from '@sveltejs/vite-plugin-svelte'; -export default defineConfig({ plugins: [svelte()], test: { environment: 'jsdom' } }); +import { defineConfig } from "vitest/config"; +import { svelte } from "@sveltejs/vite-plugin-svelte"; +export default defineConfig({ + plugins: [svelte()], + resolve: { conditions: ["browser"] }, + test: { environment: "jsdom" }, +}); diff --git a/tests/http_security.rs b/tests/http_security.rs index ea7abba..25d4b61 100644 --- a/tests/http_security.rs +++ b/tests/http_security.rs @@ -85,6 +85,32 @@ fn request(peer: &str) -> Request { request } +#[tokio::test] +async fn frontend_security_policy_allows_its_external_assets() { + let (app, _dir, _tx) = app().await; + let mut request = Request::builder() + .uri("/") + .body(axum::body::Body::empty()) + .unwrap(); + request.extensions_mut().insert(ConnectInfo( + "203.0.113.4:5000".parse::().unwrap(), + )); + + let response = app.oneshot(request).await.unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + assert_eq!( + response.headers()[header::CONTENT_SECURITY_POLICY], + "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self'; script-src 'self'; frame-ancestors 'none'" + ); + let body = axum::body::to_bytes(response.into_body(), 8192) + .await + .unwrap(); + let html = String::from_utf8(body.to_vec()).unwrap(); + assert!(html.contains("rel=\"stylesheet\"")); + assert!(!html.contains("