diff --git a/crates/templates/src/lib.rs b/crates/templates/src/lib.rs index bb208eb44..72d704b0a 100644 --- a/crates/templates/src/lib.rs +++ b/crates/templates/src/lib.rs @@ -9,7 +9,10 @@ //! Templates rendering -use std::{collections::HashSet, sync::Arc}; +use std::{ + collections::{BTreeMap, HashSet}, + sync::Arc, +}; use anyhow::Context as _; use arc_swap::ArcSwap; @@ -383,7 +386,7 @@ register_templates! { pub fn render_recovery_disabled(WithLanguage) { "pages/recovery/disabled.html" } /// Render the form used by the `form_post` response mode - pub fn render_form_post(WithLanguage>) { "form_post.html" } + pub fn render_form_post<#[sample(EmptyContext)] T: Serialize>(WithLanguage>) { "form_post.html" } /// Render the HTML error page pub fn render_error(ErrorContext) { "pages/error.html" } @@ -439,7 +442,13 @@ register_templates! { impl Templates { /// Render all templates with the generated samples to check if they render - /// properly + /// properly. + /// + /// Returns the renders in a map whose keys are template names + /// and the values are lists of renders (according to the list + /// of samples). + /// Samples are stable across re-runs and can be used for + /// acceptance testing. /// /// # Errors /// @@ -448,47 +457,8 @@ impl Templates { &self, now: chrono::DateTime, rng: &mut impl Rng, - ) -> anyhow::Result<()> { - check::render_not_found(self, now, rng)?; - check::render_app(self, now, rng)?; - check::render_swagger(self, now, rng)?; - check::render_swagger_callback(self, now, rng)?; - check::render_login(self, now, rng)?; - check::render_register(self, now, rng)?; - check::render_password_register(self, now, rng)?; - check::render_register_steps_verify_email(self, now, rng)?; - check::render_register_steps_email_in_use(self, now, rng)?; - check::render_register_steps_display_name(self, now, rng)?; - check::render_register_steps_registration_token(self, now, rng)?; - check::render_consent(self, now, rng)?; - check::render_policy_violation(self, now, rng)?; - check::render_sso_login(self, now, rng)?; - check::render_index(self, now, rng)?; - check::render_recovery_start(self, now, rng)?; - check::render_recovery_progress(self, now, rng)?; - check::render_recovery_finish(self, now, rng)?; - check::render_recovery_expired(self, now, rng)?; - check::render_recovery_consumed(self, now, rng)?; - check::render_recovery_disabled(self, now, rng)?; - check::render_form_post::(self, now, rng)?; - check::render_error(self, now, rng)?; - check::render_email_recovery_txt(self, now, rng)?; - check::render_email_recovery_html(self, now, rng)?; - check::render_email_recovery_subject(self, now, rng)?; - check::render_email_verification_txt(self, now, rng)?; - check::render_email_verification_html(self, now, rng)?; - check::render_email_verification_subject(self, now, rng)?; - check::render_upstream_oauth2_link_mismatch(self, now, rng)?; - check::render_upstream_oauth2_login_link(self, now, rng)?; - check::render_upstream_oauth2_suggest_link(self, now, rng)?; - check::render_upstream_oauth2_do_register(self, now, rng)?; - check::render_device_link(self, now, rng)?; - check::render_device_consent(self, now, rng)?; - check::render_account_deactivated(self, now, rng)?; - check::render_account_locked(self, now, rng)?; - check::render_account_logged_out(self, now, rng)?; - check::render_device_name(self, now, rng)?; - Ok(()) + ) -> anyhow::Result>> { + check::all(self, now, rng) } } diff --git a/crates/templates/src/macros.rs b/crates/templates/src/macros.rs index 00aa9bd41..b93ed1931 100644 --- a/crates/templates/src/macros.rs +++ b/crates/templates/src/macros.rs @@ -31,7 +31,9 @@ macro_rules! register_templates { pub fn $name:ident // Optional list of generics. Taken from // https://newbedev.com/rust-macro-accepting-type-with-generic-parameters - $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + // For sample rendering, we also require a 'sample' generic parameter to be provided, + // using #[sample(Type)] attribute syntax + $(< $( #[sample( $generic_default:tt )] $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? // Type of context taken by the template ( $param:ty ) { @@ -69,9 +71,31 @@ macro_rules! register_templates { pub mod check { use super::*; + /// Check and render all templates with all samples. + /// + /// Returns the sample renders. The keys in the map are the template names. + /// + /// # Errors + /// + /// Returns an error if any template fails to render with any of the sample. + pub(crate) fn all(templates: &Templates, now: chrono::DateTime, rng: &mut impl rand::Rng) -> anyhow::Result<::std::collections::BTreeMap<&'static str, Vec>> { + let mut out = ::std::collections::BTreeMap::new(); + // TODO shouldn't the Rng be independent for each render? + $( + out.insert( + $template, + $name $(::< $( $generic_default ),* >)? (templates, now, rng)? + ); + )* + + Ok(out) + } + $( #[doc = concat!("Render the `", $template, "` template with sample contexts")] /// + /// Returns the sample renders. + /// /// # Errors /// /// Returns an error if the template fails to render with any of the sample.