✨ Añade gestión de errores catastróficos
- Los errores controlados (403, 404, fatales) se renderizan con el tema activo; un `panic!` no controlado se intercepta con un HTML mínimo y autónomo, sin depender del tema ni de los componentes. - Corrige además que `Context::template()` no seguía los cambios de tema hechos con `with_theme()` después de crear la página. - Renombra `DefaultRegion(s)`/`DefaultTemplate(s)`, añade `Theme::admin_template()` y `Page::admin()`, y documenta la creación de temas e `InRegion` en `core/theme.rs`.
This commit is contained in:
parent
0e72d51276
commit
24bd13414c
12 changed files with 458 additions and 139 deletions
54
tests/error_handling.rs
Normal file
54
tests/error_handling.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
// **< CatchPanicLayer >****************************************************************************
|
||||
|
||||
struct PanicExtension;
|
||||
|
||||
#[async_trait]
|
||||
impl Extension for PanicExtension {
|
||||
fn configure_router(&self, router: Router) -> Router {
|
||||
router.route("/boom", web::get(boom))
|
||||
}
|
||||
}
|
||||
|
||||
async fn boom() -> Result<Markup, ErrorPage> {
|
||||
panic!("boom")
|
||||
}
|
||||
|
||||
#[pagetop::test]
|
||||
async fn panic_in_handler_returns_minimal_500_page_instead_of_crashing() {
|
||||
let app = web::test::init_router(Application::prepare(&PanicExtension).await.test());
|
||||
|
||||
let req = web::test::TestRequest::get().uri("/boom").to_request();
|
||||
let resp = web::test::send_request(&app, req).await;
|
||||
|
||||
assert_eq!(resp.status(), web::http::StatusCode::INTERNAL_SERVER_ERROR);
|
||||
assert_eq!(
|
||||
resp.headers().get(web::http::header::CONTENT_TYPE).unwrap(),
|
||||
"text/html; charset=utf-8"
|
||||
);
|
||||
|
||||
let body = web::test::read_body_text(resp).await;
|
||||
assert!(body.contains("An unexpected error has occurred"));
|
||||
}
|
||||
|
||||
// **< ErrorPage::NotFound >************************************************************************
|
||||
|
||||
// `EXTENSIONS` es un `OnceLock` global (`core/extension/all.rs`): se inicializa una sola vez por
|
||||
// binario de test. Todos los tests de este fichero comparten la misma extensión raíz
|
||||
// (`PanicExtension`) para que el orden de ejecución en paralelo no cambie qué rutas quedan
|
||||
// registradas.
|
||||
#[pagetop::test]
|
||||
async fn unknown_route_returns_themed_404_page() {
|
||||
let app = web::test::init_router(Application::prepare(&PanicExtension).await.test());
|
||||
|
||||
let req = web::test::TestRequest::get()
|
||||
.uri("/does-not-exist")
|
||||
.to_request();
|
||||
let resp = web::test::send_request(&app, req).await;
|
||||
|
||||
assert_eq!(resp.status(), web::http::StatusCode::NOT_FOUND);
|
||||
|
||||
let body = web::test::read_body_text(resp).await;
|
||||
assert!(body.contains("<html"));
|
||||
}
|
||||
83
tests/theme_template.rs
Normal file
83
tests/theme_template.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
// **< Tema con plantilla propia >******************************************************************
|
||||
|
||||
struct MarkerTemplate;
|
||||
|
||||
#[async_trait]
|
||||
impl Template for MarkerTemplate {
|
||||
async fn render(&self, _cx: &mut Context) -> Markup {
|
||||
html! { "marker-template-output" }
|
||||
}
|
||||
}
|
||||
|
||||
struct MarkerTheme;
|
||||
|
||||
#[async_trait]
|
||||
impl Extension for MarkerTheme {
|
||||
fn theme(&self) -> Option<ThemeRef> {
|
||||
Some(&Self)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Theme for MarkerTheme {
|
||||
fn default_template(&self) -> TemplateRef {
|
||||
&MarkerTemplate
|
||||
}
|
||||
|
||||
fn admin_template(&self) -> TemplateRef {
|
||||
&MarkerTemplate
|
||||
}
|
||||
}
|
||||
|
||||
async fn render_active_template(cx: &mut Context) -> String {
|
||||
let template = cx.template();
|
||||
template.render(cx).await.into_string()
|
||||
}
|
||||
|
||||
// **< Context::template() sigue al tema activo >***************************************************
|
||||
|
||||
#[pagetop::test]
|
||||
async fn with_theme_updates_the_effective_template() {
|
||||
// Sin cambiar de tema, la plantilla activa no es la de `MarkerTheme`.
|
||||
let mut cx = Context::new(None);
|
||||
assert_ne!(
|
||||
render_active_template(&mut cx).await,
|
||||
"marker-template-output"
|
||||
);
|
||||
|
||||
// Tras cambiar de tema con `with_theme()`, la plantilla activa pasa a ser la de ese tema, sin
|
||||
// necesidad de llamar a `with_template()` explícitamente.
|
||||
let mut cx = Context::new(None).with_theme(&MarkerTheme);
|
||||
assert_eq!(
|
||||
render_active_template(&mut cx).await,
|
||||
"marker-template-output"
|
||||
);
|
||||
}
|
||||
|
||||
#[pagetop::test]
|
||||
async fn explicit_template_is_not_overridden_by_a_later_with_theme() {
|
||||
// Una plantilla fijada explícitamente con `with_template()` prevalece aunque `with_theme()` se
|
||||
// llame después, en cualquier orden.
|
||||
let mut cx = Context::new(None)
|
||||
.with_template(&MarkerTemplate)
|
||||
.with_theme(&pagetop::base::theme::Basic);
|
||||
|
||||
assert_eq!(
|
||||
render_active_template(&mut cx).await,
|
||||
"marker-template-output"
|
||||
);
|
||||
}
|
||||
|
||||
// **< Page::admin() sigue al tema activo >*********************************************************
|
||||
|
||||
#[pagetop::test]
|
||||
async fn page_admin_template_follows_a_later_with_theme() {
|
||||
let request = web::test::TestRequest::get().to_http_request();
|
||||
|
||||
let mut page = Page::admin(request).with_theme(&MarkerTheme);
|
||||
let markup = page.context().template().render(page.context()).await;
|
||||
|
||||
assert_eq!(markup.into_string(), "marker-template-output");
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue