Implement comprehensive frontend integration testing with Playwright
Some checks failed
Integration Tests / e2e-tests (push) Failing after 4s
Integration Tests / unit-tests (push) Failing after 1m1s

- Add Playwright E2E testing framework with cross-browser support (Chrome, Firefox)
- Create authentication helpers for CalDAV server integration
- Implement calendar interaction helpers with event creation, drag-and-drop, and view switching
- Add comprehensive drag-and-drop test suite with event cleanup
- Configure CI/CD integration with Gitea Actions for headless testing
- Support both local development and CI environments with proper dependency management
- Include video recording, screenshots, and HTML reporting for test debugging
- Handle Firefox-specific timing and interaction challenges with force clicks and timeouts

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Connor Johnstone
2025-09-08 11:54:40 -04:00
parent 927cd7d2bb
commit 7d00a2dadb
768 changed files with 647255 additions and 0 deletions

View File

@@ -36,3 +36,6 @@ pub use recurring_edit_modal::{RecurringEditAction, RecurringEditModal};
pub use route_handler::RouteHandler;
pub use sidebar::{Sidebar, Theme, ViewMode};
pub use week_view::WeekView;
#[cfg(test)]
pub mod tests;

View File

@@ -0,0 +1,96 @@
use wasm_bindgen_test::*;
use chrono::{DateTime, Utc, TimeZone};
use crate::components::{MonthView, WeekView};
use crate::models::ical::VEvent;
use yew::prelude::*;
#[wasm_bindgen_test]
fn month_view_renders_without_events() {
let props = yew::props!(
current_date: Utc.with_ymd_and_hms(2023, 12, 15, 0, 0, 0).unwrap(),
events: vec![],
on_event_click: Callback::noop(),
on_date_click: Callback::noop(),
on_event_drag: Callback::noop(),
visible_calendar_paths: vec![],
);
let _month_view = html! { <MonthView ..props /> };
// Test passes if no panic occurs during rendering
}
#[wasm_bindgen_test]
fn week_view_renders_without_events() {
let props = yew::props!(
current_date: Utc.with_ymd_and_hms(2023, 12, 15, 0, 0, 0).unwrap(),
events: vec![],
on_event_click: Callback::noop(),
on_event_drag: Callback::noop(),
visible_calendar_paths: vec![],
);
let _week_view = html! { <WeekView ..props /> };
// Test passes if no panic occurs during rendering
}
#[wasm_bindgen_test]
fn month_view_handles_events() {
// Create a test event
let mut test_event = VEvent::new("test-event-123".to_string());
test_event.summary = Some("Test Event".to_string());
test_event.dtstart = Some(Utc.with_ymd_and_hms(2023, 12, 15, 14, 0, 0).unwrap());
test_event.dtend = Some(Utc.with_ymd_and_hms(2023, 12, 15, 15, 0, 0).unwrap());
let props = yew::props!(
current_date: Utc.with_ymd_and_hms(2023, 12, 15, 0, 0, 0).unwrap(),
events: vec![test_event],
on_event_click: Callback::noop(),
on_date_click: Callback::noop(),
on_event_drag: Callback::noop(),
visible_calendar_paths: vec!["test-calendar".to_string()],
);
let _month_view = html! { <MonthView ..props /> };
// Test passes if no panic occurs during rendering with events
}
#[wasm_bindgen_test]
fn week_view_handles_events() {
// Create a test event
let mut test_event = VEvent::new("test-event-456".to_string());
test_event.summary = Some("Weekly Test Event".to_string());
test_event.dtstart = Some(Utc.with_ymd_and_hms(2023, 12, 15, 10, 0, 0).unwrap());
test_event.dtend = Some(Utc.with_ymd_and_hms(2023, 12, 15, 11, 30, 0).unwrap());
let props = yew::props!(
current_date: Utc.with_ymd_and_hms(2023, 12, 15, 0, 0, 0).unwrap(),
events: vec![test_event],
on_event_click: Callback::noop(),
on_event_drag: Callback::noop(),
visible_calendar_paths: vec!["test-calendar".to_string()],
);
let _week_view = html! { <WeekView ..props /> };
// Test passes if no panic occurs during rendering with events
}
#[wasm_bindgen_test]
fn event_time_calculations() {
// Test event duration calculation
let start_time = Utc.with_ymd_and_hms(2023, 12, 15, 10, 0, 0).unwrap();
let end_time = Utc.with_ymd_and_hms(2023, 12, 15, 11, 30, 0).unwrap();
let duration = end_time - start_time;
assert_eq!(duration.num_minutes(), 90);
// Test same-day event
assert_eq!(start_time.date_naive(), end_time.date_naive());
// Test hour extraction
assert_eq!(start_time.hour(), 10);
assert_eq!(end_time.hour(), 11);
}

View File

@@ -0,0 +1,87 @@
use wasm_bindgen_test::*;
use yew::prelude::*;
use chrono::{DateTime, Utc, TimeZone};
use crate::components::EventModal;
use crate::models::ical::VEvent;
#[wasm_bindgen_test]
fn event_modal_renders_in_create_mode() {
let props = yew::props!(
is_visible: true,
on_close: Callback::noop(),
on_save: Callback::noop(),
event: None,
calendars: vec![],
default_date: Some(Utc.with_ymd_and_hms(2023, 12, 15, 0, 0, 0).unwrap()),
default_time: Some("14:00".to_string()),
on_delete: Callback::noop(),
);
let _modal = html! { <EventModal ..props /> };
// Test passes if no panic occurs during rendering in create mode
}
#[wasm_bindgen_test]
fn event_modal_renders_in_edit_mode() {
let mut test_event = VEvent::new("test-edit-event".to_string());
test_event.summary = Some("Edit Test Event".to_string());
test_event.dtstart = Some(Utc.with_ymd_and_hms(2023, 12, 15, 16, 0, 0).unwrap());
test_event.dtend = Some(Utc.with_ymd_and_hms(2023, 12, 15, 17, 0, 0).unwrap());
test_event.description = Some("Test description".to_string());
let props = yew::props!(
is_visible: true,
on_close: Callback::noop(),
on_save: Callback::noop(),
event: Some(test_event),
calendars: vec![],
default_date: None,
default_time: None,
on_delete: Callback::noop(),
);
let _modal = html! { <EventModal ..props /> };
// Test passes if no panic occurs during rendering in edit mode
}
#[wasm_bindgen_test]
fn event_modal_renders_when_hidden() {
let props = yew::props!(
is_visible: false,
on_close: Callback::noop(),
on_save: Callback::noop(),
event: None,
calendars: vec![],
default_date: None,
default_time: None,
on_delete: Callback::noop(),
);
let _modal = html! { <EventModal ..props /> };
// Test passes if no panic occurs when modal is hidden
}
#[wasm_bindgen_test]
fn event_data_validation() {
// Test event UID generation and validation
let event = VEvent::new("test-uid-123".to_string());
assert!(!event.uid.is_empty());
assert_eq!(event.uid, "test-uid-123");
// Test event with summary
let mut event_with_summary = VEvent::new("summary-test".to_string());
event_with_summary.summary = Some("Test Summary".to_string());
assert_eq!(event_with_summary.summary.unwrap(), "Test Summary");
// Test event time validation
let start = Utc.with_ymd_and_hms(2023, 12, 15, 10, 0, 0).unwrap();
let end = Utc.with_ymd_and_hms(2023, 12, 15, 11, 0, 0).unwrap();
assert!(end > start, "End time should be after start time");
let duration = end - start;
assert_eq!(duration.num_hours(), 1, "Duration should be 1 hour");
}

View File

@@ -0,0 +1,8 @@
use wasm_bindgen_test::*;
// Configure tests to run in browser
wasm_bindgen_test_configure!(run_in_browser);
pub mod sidebar_tests;
pub mod calendar_tests;
pub mod event_modal_tests;

View File

@@ -0,0 +1,59 @@
use wasm_bindgen_test::*;
use yew::prelude::*;
use crate::components::{Sidebar, Theme, ViewMode};
#[wasm_bindgen_test]
fn sidebar_renders_correctly() {
let props = yew::props!(
theme: Theme::Default,
on_theme_change: Callback::noop(),
view_mode: ViewMode::Month,
on_view_change: Callback::noop(),
calendars: vec![],
external_calendars: vec![],
on_calendar_visibility_change: Callback::noop(),
on_external_calendar_visibility_change: Callback::noop(),
user_principal: Some("testuser".to_string()),
on_logout: Callback::noop(),
on_create_calendar: Callback::noop(),
on_add_external_calendar: Callback::noop(),
on_color_change: Callback::noop(),
color_picker_open: None,
on_color_picker_toggle: Callback::noop(),
on_create_event: Callback::noop(),
style: "default".to_string(),
on_style_change: Callback::noop(),
);
let _sidebar = html! { <Sidebar ..props /> };
// Test passes if no panic occurs during rendering
}
#[wasm_bindgen_test]
fn sidebar_theme_enum_conversions() {
// Test Theme enum Display trait
assert_eq!(format!("{}", Theme::Default), "default");
assert_eq!(format!("{}", Theme::Ocean), "ocean");
assert_eq!(format!("{}", Theme::Forest), "forest");
assert_eq!(format!("{}", Theme::Dark), "dark");
// Test Theme from string parsing
assert_eq!(Theme::from("default"), Theme::Default);
assert_eq!(Theme::from("ocean"), Theme::Ocean);
assert_eq!(Theme::from("forest"), Theme::Forest);
assert_eq!(Theme::from("dark"), Theme::Dark);
assert_eq!(Theme::from("unknown"), Theme::Default); // Should default to Default
}
#[wasm_bindgen_test]
fn sidebar_view_mode_conversions() {
// Test ViewMode enum Display trait
assert_eq!(format!("{}", ViewMode::Month), "month");
assert_eq!(format!("{}", ViewMode::Week), "week");
// Test ViewMode from string parsing
assert_eq!(ViewMode::from("month"), ViewMode::Month);
assert_eq!(ViewMode::from("week"), ViewMode::Week);
assert_eq!(ViewMode::from("unknown"), ViewMode::Month); // Should default to Month
}