diff --git a/README.md b/README.md index 674a308c..35023682 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,10 @@ [![Descargas](https://img.shields.io/crates/d/pagetop.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop) [![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](https://git.cillero.es/manuelcillero/pagetop#licencia) -
+
+ PageTop reivindica la esencia de la web clásica usando [Rust](https://www.rust-lang.org/es) para la creación de soluciones web SSR (*renderizadas en el servidor*) basadas en HTML, CSS y JavaScript. Ofrece un conjunto de herramientas que los desarrolladores pueden implementar, extender o adaptar @@ -28,7 +29,8 @@ según las necesidades de cada proyecto, incluyendo: * **Temas** (*themes*): son extensiones que permiten modificar la apariencia de páginas y componentes. -## Guía rápida + +## ⚡️ Guía rápida La aplicación más sencilla de PageTop se ve así: @@ -37,14 +39,14 @@ use pagetop::prelude::*; #[pagetop::main] async fn main() -> std::io::Result<()> { - Application::new().await.run().await + Application::new().run()?.await } ``` Este código arranca el servidor de PageTop. Con la configuración por defecto, muestra una página de bienvenida accesible desde un navegador local en la dirección `http://localhost:8080`. -Para personalizar el servicio, puedes crear una extensión de PageTop de la siguiente manera: +Para personalizar el servicio, se puede crear una extensión de PageTop de la siguiente manera: ```rust,no_run use pagetop::prelude::*; @@ -52,28 +54,28 @@ use pagetop::prelude::*; struct HelloWorld; impl Extension for HelloWorld { - fn configure_router(&self, router: Router) -> Router { - router.route("/", web::get(hello_world)) + fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { + scfg.route("/", service::web::get().to(hello_world)); } } async fn hello_world(request: HttpRequest) -> Result { Page::new(request) - .with_child(Html::with(|_| html! { h1 { "Hello World!" } })) + .add_child(Html::with(|_| html! { h1 { "Hello World!" } })) .render() } #[pagetop::main] async fn main() -> std::io::Result<()> { - Application::prepare(&HelloWorld).await.run().await + Application::prepare(&HelloWorld).run()?.await } ``` Este programa implementa una extensión llamada `HelloWorld` que sirve una página web en la ruta raíz -(`/`) mostrando el texto "Hello World!" dentro de un elemento HTML `

`. +(`/`) mostrando el texto "Hello world!" dentro de un elemento HTML `

`. -## Proyecto +## 📂 Proyecto El código se organiza en un *workspace* donde actualmente se incluyen los siguientes subproyectos: @@ -112,7 +114,7 @@ El código se organiza en un *workspace* donde actualmente se incluyen los sigui integra [SeaORM](https://www.sea-ql.org/SeaORM) para acceder a bases de datos relacionales. -## Pruebas +## 🧪 Pruebas Para simplificar el flujo de trabajo, el repositorio incluye varios **alias de Cargo** declarados en `.cargo/config.toml`. Basta con ejecutarlos desde la raíz del proyecto: @@ -133,14 +135,14 @@ Para simplificar el flujo de trabajo, el repositorio incluye varios **alias de C > `cargo test`. -## Advertencia +## 🚧 Advertencia **PageTop** es un proyecto personal para aprender [Rust](https://www.rust-lang.org/es) y conocer su ecosistema. Su API está sujeta a cambios frecuentes. No se recomienda su uso en producción, al menos hasta que se libere la versión **1.0.0**. -## Licencia +## 📜 Licencia El código está disponible bajo una doble licencia: @@ -154,7 +156,7 @@ Puedes elegir la licencia que prefieras. Este enfoque de doble licencia es el es el ecosistema Rust. -## Contribuir +## ✨ Contribuir PageTop mantiene **un único repositorio oficial**: diff --git a/examples/app-basic.rs b/examples/app-basic.rs index 557fdfce..53743870 100644 --- a/examples/app-basic.rs +++ b/examples/app-basic.rs @@ -2,5 +2,5 @@ use pagetop::prelude::*; #[pagetop::main] async fn main() -> std::io::Result<()> { - Application::new().await.run().await + Application::new().run()?.await } diff --git a/examples/form-controls.rs b/examples/form-controls.rs index 4679039a..4c16b619 100644 --- a/examples/form-controls.rs +++ b/examples/form-controls.rs @@ -437,5 +437,5 @@ fn form_lists() -> Form { #[pagetop::main] async fn main() -> std::io::Result<()> { - Application::prepare(&FormControls).await.run().await + Application::prepare(&FormControls).run()?.await } diff --git a/examples/hello-name.rs b/examples/hello-name.rs index 3e9de96b..71439c7d 100644 --- a/examples/hello-name.rs +++ b/examples/hello-name.rs @@ -9,8 +9,8 @@ impl Extension for HelloName { } async fn hello_name( - web::Path(name): web::Path, request: HttpRequest, + web::Path(name): web::Path, ) -> Result { Page::new(request) .with_child(Html::with(move |_| { @@ -23,5 +23,5 @@ async fn hello_name( #[pagetop::main] async fn main() -> std::io::Result<()> { - Application::prepare(&HelloName).await.run().await + Application::prepare(&HelloName).run()?.await } diff --git a/examples/hello-world.rs b/examples/hello-world.rs index 287eb6eb..f1c40d23 100644 --- a/examples/hello-world.rs +++ b/examples/hello-world.rs @@ -20,5 +20,5 @@ async fn hello_world(request: HttpRequest) -> Result { #[pagetop::main] async fn main() -> std::io::Result<()> { - Application::prepare(&HelloWorld).await.run().await + Application::prepare(&HelloWorld).run()?.await } diff --git a/examples/intro-colors.rs b/examples/intro-colors.rs index 693a259f..b219c5be 100644 --- a/examples/intro-colors.rs +++ b/examples/intro-colors.rs @@ -78,5 +78,5 @@ async fn intro_colors(request: HttpRequest) -> Result { #[pagetop::main] async fn main() -> std::io::Result<()> { - Application::prepare(&IntroColors).await.run().await + Application::prepare(&IntroColors).run()?.await } diff --git a/examples/navbar-menus.rs b/examples/navbar-menus.rs index 6c288062..025a75b3 100644 --- a/examples/navbar-menus.rs +++ b/examples/navbar-menus.rs @@ -5,7 +5,6 @@ include_locales!(LOC from "examples/locale"); struct SuperMenu; -#[async_trait] impl Extension for SuperMenu { fn dependencies(&self) -> Vec { vec![ @@ -15,7 +14,7 @@ impl Extension for SuperMenu { ] } - async fn initialize(&self) { + fn initialize(&self) { let navbar_menu = bs::Navbar::brand_left(bs::navbar::Brand::new()) .with_expand(token::BreakPoint::LG) .with_item(bs::navbar::Item::nav( @@ -115,5 +114,5 @@ impl Extension for SuperMenu { #[pagetop::main] async fn main() -> std::io::Result<()> { - Application::prepare(&SuperMenu).await.run().await + Application::prepare(&SuperMenu).run()?.await } diff --git a/src/app.rs b/src/app.rs index 6949c50f..366f900e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -9,40 +9,45 @@ use crate::response::page::ErrorPage; use crate::web::{HttpRequest, Router}; use crate::{PAGETOP_VERSION, global, trace}; +use std::future::Future; use std::io::Error; use std::sync::LazyLock; /// Punto de entrada de una aplicación PageTop. /// -/// Orquesta el arranque de la aplicación. Primero se instancia con [`new()`](Application::new) o -/// [`prepare()`](Application::prepare), y después se ejecuta usando [`run()`](Application::run) (o -/// usando [`test()`](Application::test) si se está preparando un entorno de pruebas). +/// No almacena datos, **encapsula** el inicio completo de la configuración y puesta en marcha de la +/// aplicación. Para instanciarla se puede usar [`new()`](Application::new) o +/// [`prepare()`](Application::prepare). Después sólo hay que llamar a [`run()`](Application::run) +/// para ejecutar la aplicación (o a [`test()`](Application::test) si se está preparando un entorno +/// de pruebas). pub struct Application; +impl Default for Application { + fn default() -> Self { + Self::new() + } +} + impl Application { /// Crea una instancia mínima de la aplicación, sin extensión raíz. /// /// Útil para verificar que el servidor arranca correctamente. Para una aplicación real, usa /// [`prepare()`](Application::prepare) con una extensión raíz. - pub async fn new() -> Self { - Self::internal_prepare(None).await + pub fn new() -> Self { + Self::internal_prepare(None) } /// Prepara una instancia de la aplicación a partir de una extensión raíz. /// - /// Inicializa la aplicación habilitando las extensiones en orden de dependencia: primero las - /// que no dependen de ninguna otra, luego las que dependen de extensiones ya habilitadas, y así - /// hasta habilitar la extensión raíz. - /// - /// Es *async* porque cada extensión puede realizar operaciones asíncronas en su - /// [`initialize()`](crate::core::extension::Extension::initialize) (conexión a base de datos, - /// migraciones, semillas de datos...). - pub async fn prepare(root_extension: ExtensionRef) -> Self { - Self::internal_prepare(Some(root_extension)).await + /// Las dependencias se habilitan en orden: primero las que no dependen de ninguna otra, luego + /// las que dependen de extensiones ya habilitadas, y así sucesivamente hasta dejar habilitada + /// la extensión raíz. + pub fn prepare(root_extension: ExtensionRef) -> Self { + Self::internal_prepare(Some(root_extension)) } // Secuencia de arranque común a new() y prepare(). - async fn internal_prepare(root_extension: Option) -> Self { + fn internal_prepare(root_extension: Option) -> Self { // Al arrancar muestra una cabecera para la aplicación. Self::show_banner(); @@ -59,7 +64,7 @@ impl Application { extension::all::register_actions(); // Inicializa las extensiones. - extension::all::initialize_extensions().await; + extension::all::initialize_extensions(); Self } @@ -114,9 +119,9 @@ impl Application { /// Arranca el servidor web de la aplicación. /// - /// Enlaza el puerto del servidor web (puede fallar con [`std::io::Error`] si el puerto ya está - /// en uso o el proceso carece de permisos) y ejecuta el bucle de atención de peticiones. El - /// patrón habitual es: + /// Enlaza el puerto del servidor web de forma síncrona (puede fallar con [`std::io::Error`] si + /// el puerto ya está en uso o el proceso carece de permisos) y devuelve un [`Future`] que + /// ejecuta el bucle de atención de peticiones. El patrón habitual es: /// /// ```rust,no_run /// use pagetop::prelude::*; @@ -127,24 +132,26 @@ impl Application { /// /// #[pagetop::main] /// async fn main() -> std::io::Result<()> { - /// Application::prepare(&MyApp).await.run().await + /// Application::prepare(&MyApp).run()?.await /// } /// ``` - pub async fn run(self) -> Result<(), Error> { + pub fn run(self) -> Result>, Error> { let addr = format!( "{}:{}", global::SETTINGS.server.bind_address, global::SETTINGS.server.bind_port ); - // Enlaza el puerto de forma síncrona para detectar errores. + // Enlaza el puerto de forma síncrona para detectar errores antes del *await*. let std_listener = std::net::TcpListener::bind(&addr)?; std_listener.set_nonblocking(true)?; let router = Self::build_router(); - let listener = tokio::net::TcpListener::from_std(std_listener)?; - axum::serve(listener, router).await + Ok(async move { + let listener = tokio::net::TcpListener::from_std(std_listener)?; + axum::serve(listener, router).await + }) } /// Devuelve el servidor web configurado para usarlo en pruebas de integración. diff --git a/src/core/action/list.rs b/src/core/action/list.rs index adf1b476..a62c750e 100644 --- a/src/core/action/list.rs +++ b/src/core/action/list.rs @@ -25,7 +25,7 @@ impl ActionsList { F: FnMut(&A), { let list = self.0.read(); - for a in list.iter() { + for a in list.iter().rev() { if let Some(action) = (**a).downcast_ref::() { f(action); } else { @@ -40,7 +40,7 @@ impl ActionsList { F: FnMut(&A) -> std::ops::ControlFlow<()>, { let list = self.0.read(); - for a in list.iter() { + for a in list.iter().rev() { if let Some(action) = (**a).downcast_ref::() { if f(action).is_break() { break; diff --git a/src/core/extension/all.rs b/src/core/extension/all.rs index 7ceb3fec..bc26cf43 100644 --- a/src/core/extension/all.rs +++ b/src/core/extension/all.rs @@ -72,10 +72,10 @@ pub fn register_actions() { // **< INICIALIZA LAS EXTENSIONES >***************************************************************** -pub async fn initialize_extensions() { +pub fn initialize_extensions() { trace::info!("Calling application bootstrap"); for e in EXTENSIONS.get().into_iter().flatten() { - e.initialize().await; + e.initialize(); } } diff --git a/src/core/extension/definition.rs b/src/core/extension/definition.rs index d4025d0a..9187f692 100644 --- a/src/core/extension/definition.rs +++ b/src/core/extension/definition.rs @@ -1,5 +1,4 @@ use crate::actions; -use crate::async_trait; use crate::core::AnyInfo; use crate::core::action::ActionBox; use crate::core::theme::ThemeRef; @@ -25,7 +24,6 @@ use crate::web::Router; /// } /// } /// ``` -#[async_trait] pub trait Extension: AnyInfo + Send + Sync { /// Nombre de la extensión como *texto localizado* legible para el usuario. /// @@ -66,30 +64,10 @@ pub trait Extension: AnyInfo + Send + Sync { None } - /// Extensiones que deben inicializarse **antes** de esta. + /// Otras extensiones que deben habilitarse **antes** de esta. /// - /// PageTop usa este método para construir un grafo de dependencias en tiempo de ejecución entre - /// extensiones. Garantiza que todas las extensiones requeridas están presentes y se inicializan - /// en el orden apropiado, es decir, las dependencias siempre antes que la extensión que las - /// declara. - /// - /// Esta declaración es independiente de las dependencias en `Cargo.toml`, donde Rust gestiona - /// las dependencias de los *crates* en tiempo de compilación. - /// - /// # Ejemplo - /// - /// ```rust,no_run - /// # use pagetop::prelude::*; - /// # pub struct Database; - /// # impl Extension for Database {} - /// pub struct MyApp; - /// - /// impl Extension for MyApp { - /// fn dependencies(&self) -> Vec { - /// vec![&Database] - /// } - /// } - /// ``` + /// PageTop resolverá automáticamente estas dependencias respetando el orden durante el arranque + /// de la aplicación. fn dependencies(&self) -> Vec { vec![] } @@ -105,34 +83,9 @@ pub trait Extension: AnyInfo + Send + Sync { /// Inicializa la extensión durante la fase de arranque de la aplicación. /// - /// La firma extendida que se muestra es el resultado de aplicar la macro - /// [`pagetop::async_trait`](pagetop::async_trait) a la declaración del método: - /// - /// ```text - /// async fn initialize(&self) {} - /// ``` - /// - /// Solo es necesario sobrescribir este método **cuando la extensión necesita ejecutar alguna - /// lógica de inicialización**. PageTop lo invoca una sola vez, después de que todas las - /// dependencias se han inicializado y antes de aceptar cualquier petición HTTP. - /// - /// En ese caso, el bloque `impl Extension` debe llevar `#[async_trait]`: - /// - /// ```rust,no_run - /// use pagetop::prelude::*; - /// - /// pub struct MyExtension; - /// - /// #[async_trait] - /// impl Extension for MyExtension { - /// async fn initialize(&self) { - /// // lógica de inicialización... - /// } - /// } - /// ``` - /// - /// Las extensiones que no sobrescriben `initialize()` no necesitan `#[async_trait]`. - async fn initialize(&self) {} + /// Se llama una sola vez, después de que todas las dependencias se han inicializado y antes de + /// aceptar cualquier petición HTTP. + fn initialize(&self) {} /// Registra rutas, servicios y capas de la extensión en el servidor web de la aplicación. /// @@ -142,12 +95,12 @@ pub trait Extension: AnyInfo + Send + Sync { /// /// # Operaciones disponibles /// - /// | Operación | Llamada sobre `router` | - /// |------------------------------------|----------------------------------------------------| - /// | Ruta HTTP | `.route("/path", web::get(handler))` | - /// | Rutas bajo prefijo común | `.nest("/prefix", sub_router)` | - /// | Archivos estáticos | `serve_static_files!(router, [assets] => "/path")` | - /// | Estado compartido entre *handlers* | `.with_state(my_state)` | + /// | Operación | Llamada sobre `router` | + /// |------------------------------------|-------------------------------------------------| + /// | Ruta HTTP | `.route("/path", web::get(handler))` | + /// | Rutas bajo prefijo común | `.nest("/prefix", sub_router)` | + /// | Archivos estáticos | `serve_static_files!(router, [...] => "/path")` | + /// | Estado compartido entre *handlers* | `.with_state(my_state)` | /// /// # Ejemplos /// diff --git a/src/lib.rs b/src/lib.rs index c069150a..2048c36b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,7 @@ según las necesidades de cada proyecto, incluyendo: * **Temas** (*themes*): son extensiones que permiten modificar la apariencia de páginas y componentes. -## Guía rápida +# ⚡️ Guía rápida La aplicación más sencilla de PageTop se ve así: @@ -38,14 +38,14 @@ use pagetop::prelude::*; #[pagetop::main] async fn main() -> std::io::Result<()> { - Application::new().await.run().await + Application::new().run()?.await } ``` Este código arranca el servidor de PageTop. Con la configuración por defecto, muestra una página de bienvenida accesible desde un navegador local en la dirección `http://localhost:8080`. -Para personalizar el servicio, puedes crear una extensión de PageTop de la siguiente manera: +Para personalizar el servicio, se puede crear una extensión de PageTop de la siguiente manera: ```rust,no_run use pagetop::prelude::*; @@ -66,12 +66,22 @@ async fn hello_world(request: HttpRequest) -> Result { #[pagetop::main] async fn main() -> std::io::Result<()> { - Application::prepare(&HelloWorld).await.run().await + Application::prepare(&HelloWorld).run()?.await } ``` Este programa implementa una extensión llamada `HelloWorld` que sirve una página web en la ruta raíz -(`/`) mostrando el texto "Hello World!" dentro de un elemento HTML `

`. +(`/`) mostrando el texto "Hello world!" dentro de un elemento HTML `

`. + + +# 🧩 Gestión de Dependencias + +Los proyectos que utilizan PageTop gestionan las dependencias con `cargo`, como cualquier otro +proyecto en Rust. + +Sin embargo, es fundamental que cada extensión declare explícitamente sus +[dependencias](core::extension::Extension::dependencies), si las tiene, para que PageTop pueda +estructurar e inicializar la aplicación de forma modular. */ #![cfg_attr(docsrs, feature(doc_cfg))] @@ -122,21 +132,6 @@ use std::ops::Deref; /// referencia a la versión del *crate* que lo usa. pub const PAGETOP_VERSION: &str = env!("CARGO_PKG_VERSION"); -/// Re-exporta el atributo [`async_trait`](https://docs.rs/async-trait) para implementar *traits* -/// con métodos `async`. -/// -/// Si en el ámbito se declara `use pagetop::prelude::*` o `use pagetop::async_trait;` basta con -/// usar el nombre corto `#[async_trait]`. Otra opción es usar su forma cualificada -/// `#[pagetop::async_trait]`. -/// -/// Al estar incluido en PageTop, no hace falta añadir la dependencia `async-trait` en el -/// `Cargo.toml` del proyecto. -/// -/// En proyectos que crean extensiones de PageTop, su uso es necesario cuando se sobrescribe -/// [`initialize()`](crate::core::extension::Extension::initialize) en un bloque -/// [`impl Extension`](crate::core::extension::Extension). -pub use async_trait::async_trait; - pub use pagetop_macros::{AutoDefault, builder_fn, html, main, test}; pub use pagetop_statics::{StaticFile, resource}; @@ -179,8 +174,8 @@ pub type UniqueId = std::any::TypeId; /// Representa el peso lógico de una instancia en una colección ordenada por pesos. /// -/// Las instancias con pesos **más bajos**, incluyendo valores negativos, se situarán antes en la -/// ordenación. Acepta valores `i8` de -128 a 127. +/// Las instancias con pesos **más bajos**, incluyendo valores negativos (`-128..127`), se situarán +/// antes en la ordenación. pub type Weight = i8; // **< API >**************************************************************************************** diff --git a/src/prelude.rs b/src/prelude.rs index f87d25b5..20d5c800 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -4,7 +4,7 @@ pub use crate::PAGETOP_VERSION; -pub use crate::{async_trait, builder_fn, html, main, test}; +pub use crate::{builder_fn, html, main, test}; pub use crate::{AutoDefault, CowStr, Getters, StaticResources, UniqueId, Weight}; diff --git a/src/web.rs b/src/web.rs index 19f62476..3705b9ae 100644 --- a/src/web.rs +++ b/src/web.rs @@ -61,26 +61,6 @@ use std::task::{Context, Poll}; /// // ... /// } /// ``` -/// -/// # Orden de parámetros en el *handler* -/// -/// `HttpRequest` toma las extensiones de la petición al extraerse. Los extractores que leen de ahí -/// (como `Path` o `Extension`) deben aparecer **antes** en la lista de parámetros del -/// *handler*: -/// -/// ```rust,no_run -/// # use pagetop::prelude::*; -/// // Correcto: Path se declara antes que HttpRequest. -/// async fn view_post( -/// web::Path(id): web::Path, -/// request: HttpRequest, -/// ) -> Result { -/// # todo!() -/// } -/// ``` -/// -/// Los extractores que no dependen de las extensiones, como `Query` o `Method`, no están sujetos -/// a esta restricción. #[derive(Clone, Debug)] pub struct HttpRequest { uri: http::Uri, diff --git a/tests/component_poweredby.rs b/tests/component_poweredby.rs index 2a82b84f..89ff3269 100644 --- a/tests/component_poweredby.rs +++ b/tests/component_poweredby.rs @@ -4,13 +4,13 @@ use pagetop::prelude::*; /// /// Los tests de este módulo renderizan componentes directamente con `Context::default()`, por lo /// que sólo necesitan el subsistema de localización y las extensiones registradas, no un router. -async fn setup() { - Application::new().await; +fn setup() { + let _ = Application::new(); } #[pagetop::test] async fn poweredby_default_shows_only_pagetop_recognition() { - setup().await; + setup(); let mut p = PoweredBy::default(); let html = p.render(&mut Context::default()); @@ -24,7 +24,7 @@ async fn poweredby_default_shows_only_pagetop_recognition() { #[pagetop::test] async fn poweredby_new_includes_current_year_and_app_name() { - setup().await; + setup(); let mut p = PoweredBy::new(); let html = p.render(&mut Context::default()); @@ -48,7 +48,7 @@ async fn poweredby_new_includes_current_year_and_app_name() { #[pagetop::test] async fn poweredby_with_copyright_overrides_text() { - setup().await; + setup(); let custom = "2001 © FooBar Inc."; let mut p = PoweredBy::default().with_copyright(Some(custom)); @@ -60,7 +60,7 @@ async fn poweredby_with_copyright_overrides_text() { #[pagetop::test] async fn poweredby_with_copyright_none_hides_text() { - setup().await; + setup(); let mut p = PoweredBy::new().with_copyright(None::); let html = p.render(&mut Context::default()); @@ -72,7 +72,7 @@ async fn poweredby_with_copyright_none_hides_text() { #[pagetop::test] async fn poweredby_link_points_to_crates_io() { - setup().await; + setup(); let mut p = PoweredBy::default(); let html = p.render(&mut Context::default()); @@ -85,7 +85,7 @@ async fn poweredby_link_points_to_crates_io() { #[pagetop::test] async fn poweredby_getter_reflects_internal_state() { - setup().await; + setup(); // Por defecto no hay copyright. let p0 = PoweredBy::default(); diff --git a/tests/locale.rs b/tests/locale.rs index 0bf87868..51ff1863 100644 --- a/tests/locale.rs +++ b/tests/locale.rs @@ -1,12 +1,8 @@ use pagetop::prelude::*; -async fn setup() { - Application::new().await; -} - #[pagetop::test] async fn literal_text() { - setup().await; + let _app = web::test::init_router(Application::new().test()); let l10n = L10n::n("© 2025 PageTop"); assert_eq!(l10n.get(), Some("© 2025 PageTop".to_string())); @@ -14,7 +10,7 @@ async fn literal_text() { #[pagetop::test] async fn translation_without_args() { - setup().await; + let _app = web::test::init_router(Application::new().test()); let l10n = L10n::l("test_hello_world"); let translation = l10n.lookup(&Locale::resolve("es-ES")); @@ -23,7 +19,7 @@ async fn translation_without_args() { #[pagetop::test] async fn translation_with_args() { - setup().await; + let _app = web::test::init_router(Application::new().test()); let l10n = L10n::l("test_hello_user").with_arg("userName", "Manuel"); let translation = l10n.lookup(&Locale::resolve("es-ES")); @@ -32,7 +28,7 @@ async fn translation_with_args() { #[pagetop::test] async fn translation_with_plural_and_select() { - setup().await; + let _app = web::test::init_router(Application::new().test()); let l10n = L10n::l("test_shared_photos").with_args(vec![ ("userName", "Roberto"), @@ -45,7 +41,7 @@ async fn translation_with_plural_and_select() { #[pagetop::test] async fn check_fallback_language() { - setup().await; + let _app = web::test::init_router(Application::new().test()); let l10n = L10n::l("test_hello_world"); let translation = l10n.lookup(&Locale::resolve("xx-YY")); // Retrocede a "en-US". @@ -54,7 +50,7 @@ async fn check_fallback_language() { #[pagetop::test] async fn check_unknown_key() { - setup().await; + let _app = web::test::init_router(Application::new().test()); let l10n = L10n::l("non-existent-key"); let translation = l10n.lookup(&Locale::resolve("en-US")); diff --git a/tests/service.rs b/tests/service.rs index 1c3442f2..5f2ca87b 100644 --- a/tests/service.rs +++ b/tests/service.rs @@ -2,7 +2,7 @@ use pagetop::prelude::*; #[pagetop::test] async fn homepage_returns_404() { - let app = web::test::init_router(Application::new().await.test()); + let app = web::test::init_router(Application::new().test()); let req = web::test::TestRequest::get().uri("/").to_request(); let resp = web::test::send_request(&app, req).await; diff --git a/tests/util.rs b/tests/util.rs index a89d6fa0..6dafce8a 100644 --- a/tests/util.rs +++ b/tests/util.rs @@ -3,10 +3,6 @@ use pagetop::prelude::*; use std::{borrow::Cow, fs, io}; use tempfile::TempDir; -async fn setup() { - Application::new().await; -} - // **< Testing normalize_ascii() >****************************************************************** fn assert_err(input: &str, expected: util::NormalizeAsciiError) { @@ -269,7 +265,7 @@ mod unix { #[pagetop::test] async fn ok_absolute_dir() -> io::Result<()> { - setup().await; + let _app = web::test::init_router(Application::new().test()); // /tmp//sub let td = TempDir::new()?; @@ -283,7 +279,7 @@ mod unix { #[pagetop::test] async fn ok_relative_dir_with_manifest() -> io::Result<()> { - setup().await; + let _app = web::test::init_router(Application::new().test()); let td = TempDir::new()?; let sub = td.path().join("sub"); @@ -297,7 +293,7 @@ mod unix { #[pagetop::test] async fn error_not_a_directory() -> io::Result<()> { - setup().await; + let _app = web::test::init_router(Application::new().test()); let td = TempDir::new()?; let file = td.path().join("foo.txt"); @@ -315,7 +311,7 @@ mod windows { #[pagetop::test] async fn ok_absolute_dir() -> io::Result<()> { - setup().await; + let _app = web::test::init_router(Application::new().test()); // C:\Users\...\Temp\... let td = TempDir::new()?; @@ -329,7 +325,7 @@ mod windows { #[pagetop::test] async fn ok_relative_dir_with_manifest() -> io::Result<()> { - setup().await; + let _app = web::test::init_router(Application::new().test()); let td = TempDir::new()?; let sub = td.path().join("sub"); @@ -343,7 +339,7 @@ mod windows { #[pagetop::test] async fn error_not_a_directory() -> io::Result<()> { - setup().await; + let _app = web::test::init_router(Application::new().test()); let td = TempDir::new()?; let file = td.path().join("foo.txt");