penguinui
This commit is contained in:
@@ -207,10 +207,20 @@ mod tests {
|
||||
body.contains("href=\"/static/app.css\""),
|
||||
"{path} is missing the shared stylesheet"
|
||||
);
|
||||
assert!(
|
||||
!body.contains("<style>"),
|
||||
// The shell carries exactly two <style> elements, and neither is
|
||||
// page styling: the cascade-order declaration that puts app.css
|
||||
// between Tailwind's preflight and its utilities, and Penguin UI's
|
||||
// @theme. Any third one is a page inlining CSS instead of
|
||||
// linking the stylesheet.
|
||||
assert_eq!(
|
||||
body.matches("<style").count(),
|
||||
2,
|
||||
"{path} inlines CSS instead of linking the stylesheet"
|
||||
);
|
||||
assert!(
|
||||
body.contains("@layer theme, base, app, components, utilities;"),
|
||||
"{path} lost the cascade order app.css depends on"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,8 @@ pub(crate) fn render_builder(page: &AddTablePageState) -> String {
|
||||
}
|
||||
|
||||
/// Used when the page itself cannot be loaded (auth or backend failure).
|
||||
/// There is no draft left to render, so this replaces the builder — the dialog
|
||||
/// is what tells the user why the form just emptied.
|
||||
pub(crate) fn render_submission_error(message: &str) -> String {
|
||||
render(&Alert::error("Could not create the table", message))
|
||||
}
|
||||
@@ -145,6 +147,41 @@ mod tests {
|
||||
assert!(html.contains(r#"name="accounting_currency" value="EUR" list="currency-codes""#));
|
||||
}
|
||||
|
||||
/// The dialog only exists when there is a failure, and it carries the same
|
||||
/// message as the inline alert behind it.
|
||||
#[test]
|
||||
fn a_failure_is_shown_as_a_dialog_as_well_as_an_alert() {
|
||||
let mut state = page();
|
||||
assert!(!render_builder(&state).contains(r#"role="dialog""#));
|
||||
|
||||
state.error = Some("That column already exists.".to_string());
|
||||
let html = render_builder(&state);
|
||||
assert!(html.contains(r#"role="dialog""#));
|
||||
assert!(html.contains("x-data=\"{ modalIsOpen: true }\""));
|
||||
assert_eq!(html.matches("That column already exists.").count(), 2);
|
||||
}
|
||||
|
||||
/// The dialog is Tailwind + Alpine, so the page has to load them; the
|
||||
/// `head` block lives two levels up, in `ui/base.html`.
|
||||
#[test]
|
||||
fn the_page_loads_what_the_dialog_needs() {
|
||||
let html = render_page(&page());
|
||||
|
||||
assert!(html.contains("@tailwindcss/browser@4"));
|
||||
assert!(html.contains("@alpinejs/focus@3"));
|
||||
assert!(html.contains("htmx:beforeSwap"));
|
||||
}
|
||||
|
||||
/// A load failure has no draft to render, so the dialog is the response.
|
||||
#[test]
|
||||
fn a_load_failure_answers_with_the_dialog() {
|
||||
let html = render_submission_error("The backend is unreachable.");
|
||||
|
||||
assert!(!html.contains("Template error"), "{html}");
|
||||
assert!(html.contains(r#"role="dialog""#));
|
||||
assert!(html.contains("The backend is unreachable."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_link_mode_is_shown_on_the_button_that_cycles_it() {
|
||||
let html = render_builder(&page());
|
||||
|
||||
@@ -47,7 +47,8 @@ impl Default for Nav {
|
||||
}
|
||||
}
|
||||
|
||||
/// The swap target every form POST answers with.
|
||||
/// The swap target every form POST answers with. An error carries the dialog
|
||||
/// that puts the message in front of the user; see `ui/alert_fragment.html`.
|
||||
#[derive(Template)]
|
||||
#[template(path = "ui/alert_fragment.html")]
|
||||
pub(crate) struct Alert<'a> {
|
||||
@@ -117,3 +118,24 @@ pub(crate) fn render<T: Template>(template: &T) -> String {
|
||||
.render()
|
||||
.unwrap_or_else(|error| format!("<p class=\"error\">Template error: {error}</p>"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Every form on the site answers through `Alert`, so this is the one
|
||||
/// place that decides a failure cannot go unnoticed.
|
||||
#[test]
|
||||
fn a_failed_form_gets_a_dialog_and_a_successful_one_does_not() {
|
||||
let failure = render(&Alert::error("Could not save", "A name is required."));
|
||||
assert!(!failure.contains("Template error"), "{failure}");
|
||||
assert!(failure.contains(r#"role="alert""#));
|
||||
assert!(failure.contains(r#"role="dialog""#));
|
||||
// Once in the inline alert, once in the dialog.
|
||||
assert_eq!(failure.matches("A name is required.").count(), 2);
|
||||
|
||||
let success = render(&Alert::success("Saved", "Two rows written."));
|
||||
assert!(success.contains(r#"role="alert""#));
|
||||
assert!(!success.contains(r#"role="dialog""#));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user