package mujica import ( "fmt" "testing" "time" "github.com/egoist/mygo/ui" ) var calendarToday = Date{2026, time.October, 5} // datetimeTestLabel is the en-US spoken name of a day cell. func datetimeTestLabel(d Date) string { return fmt.Sprintf("%v, %v %d, %d", d.Weekday(), d.Month, d.Day, d.Year) } func calendarTester(value *Date, opts CalendarOptions, changes *int) *ui.Tester { opts.Today = calendarToday return ui.NewTester(func(c *ui.Context) { Use(c, Settings{Mode: Light}) if Calendar(c, value, opts).Changed() { *changes++ } }, 400, 400) } func TestCalendarClickChoosesDay(t *testing.T) { v, n := Date{}, 0 tt := calendarTester(&v, CalendarOptions{}, &n) if !tt.HasText("October 2026") { t.Fatalf("empty value should show today's month, got %v", tt.Texts()) } if err := tt.Click("Monday, October 12, 2026"); err != nil { t.Fatal(err) } if v != (Date{2026, time.October, 12}) || n != 1 { t.Fatalf("value %v changes %d", v, n) } if err := tt.Click("Monday, October 12, 2026"); err != nil { t.Fatal(err) } if n != 1 { t.Fatalf("same day reported change again: %d", n) } } func TestCalendarKeyboard(t *testing.T) { v, n := Date{2026, time.October, 15}, 0 tt := calendarTester(&v, CalendarOptions{}, &n) tt.Key(0, ui.KeyTab) tt.Key(0, ui.KeyTab) tt.Key(0, ui.KeyTab) tt.Key(0, ui.KeyTab) tt.Key(0, ui.KeyTab) if !tt.Focused("Thursday, October 15, 2026") { t.Fatal("Tab should land on the chosen day") } tt.Key(0, ui.KeyRight) tt.Key(0, ui.KeyDown) if !tt.Focused("Friday, October 23, 2026") { t.Fatal("arrows should move the focus by day and week") } tt.Key(0, ui.KeyPageDown) if !tt.HasText("November 2026") || !tt.Focused("Monday, November 23, 2026") { t.Fatalf("PageDown should move a month: %v", tt.Texts()[:3]) } tt.Key(ui.Shift, ui.KeyPageUp) if !tt.HasText("November 2025") { t.Fatal("Shift+PageUp should move a year") } tt.Key(0, ui.KeyEnd) tt.Key(0, ui.KeyEnter) if v != (Date{2025, time.November, 30}) || n != 1 { t.Fatalf("Enter chose %v (%d changes)", v, n) } tt.Key(0, ui.KeyHome) tt.Key(0, ui.KeySpace) if v != (Date{2025, time.November, 1}) || n != 2 { t.Fatalf("Space chose %v", v) } } func TestCalendarLimits(t *testing.T) { v, n := Date{2026, time.October, 10}, 0 opts := CalendarOptions{ Min: Date{2026, time.October, 5}, Max: Date{2026, time.October, 20}, DisableDate: func(d Date) bool { return d.Weekday() == time.Sunday }, } tt := calendarTester(&v, opts, &n) for _, label := range []string{"Thursday, October 1, 2026", "Sunday, October 11, 2026", "Wednesday, October 21, 2026"} { if err := tt.Click(label); err != nil { t.Fatal(err) } } if v != (Date{2026, time.October, 10}) || n != 0 { t.Fatalf("disabled days changed the value to %v", v) } if err := tt.Click("Saturday, October 10, 2026"); err != nil { t.Fatal(err) } tt.Key(0, ui.KeyRight) if !tt.Focused("Monday, October 12, 2026") { t.Fatal("arrows should skip the disabled Sunday") } } func TestCalendarWeekStart(t *testing.T) { for _, tc := range []struct { ws WeekStart loc Locale first string }{{WeekStartLocale, EnUS, "Sunday"}, {WeekStartLocale, ZhCN, "星期一"}, {WeekStartMonday, EnUS, "Monday"}, {WeekStartSaturday, EnUS, "Saturday"}} { v := Date{2026, time.October, 5} tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{Locale: tc.loc}) Calendar(c, &v, CalendarOptions{WeekStart: tc.ws, Today: calendarToday}) }, 400, 400) texts := tt.Texts() var first string for i, s := range texts { if s == "2026年10月" || s == "October 2026" { first = texts[i+3] break } } if first != tc.first { t.Errorf("%v %v: first column %q, want %q", tc.ws, tc.loc, first, tc.first) } } } func TestCalendarDisabled(t *testing.T) { v, n := Date{2026, time.October, 10}, 0 tt := calendarTester(&v, CalendarOptions{Disabled: true}, &n) _ = tt.Click("Monday, October 12, 2026") _ = tt.Click("Next month") if v != (Date{2026, time.October, 10}) || n != 0 || !tt.HasText("October 2026") { t.Fatalf("disabled calendar changed: %v", v) } } func TestCalendarRejectsInvalidValue(t *testing.T) { defer func() { if recover() == nil { t.Fatal("expected panic for February 30") } }() v := Date{2026, time.February, 30} ui.NewTester(func(c *ui.Context) { Use(c, Settings{}) Calendar(c, &v, CalendarOptions{}) }, 400, 400) } func TestCalendarSnapshot(t *testing.T) { v := Date{2026, time.October, 14} snapshot(t, "calendar", 320, 340, func(c *ui.Context) { ui.Column(c).Padding(16).Children(func() { Calendar(c, &v, CalendarOptions{Today: calendarToday, Max: Date{2026, time.October, 28}, DisableDate: func(d Date) bool { return d.Day == 20 }}) }) }) }