package e2e import ( "fmt" "reflect" "testing" "pad/internal/ui" ) // ElementAssertions provides fluent assertions on element slices. type ElementAssertions struct { elements []ui.Element t testing.TB failed bool } // NewElementAssertions creates assertions for a frame. func NewElementAssertions(t testing.TB, elements []ui.Element) *ElementAssertions { return &ElementAssertions{ elements: elements, t: t, failed: false, } } // HasElementCount asserts the number of elements. func (ea *ElementAssertions) HasElementCount(count int) *ElementAssertions { if len(ea.elements) != count { ea.t.Errorf("expected %d elements, got %d", count, len(ea.elements)) ea.failed = true } return ea } // HasElementWithID asserts an element exists with the given ID. func (ea *ElementAssertions) HasElementWithID(id string) *ElementAssertions { found := false for _, elem := range ea.elements { if idElem, ok := elem.(interface{ ID() string }); ok { if idElem.ID() == id { found = true break } } } if !found { ea.t.Errorf("expected element with ID %q, not found", id) ea.failed = true } return ea } // HasElementOfType asserts an element of the given type exists. func (ea *ElementAssertions) HasElementOfType(t reflect.Type) *ElementAssertions { found := false for _, elem := range ea.elements { if reflect.TypeOf(elem) == t { found = true break } } if !found { ea.t.Errorf("expected element of type %v, not found", t) ea.failed = true } return ea } // HasLabelWithText asserts a Label element contains the given text. func (ea *ElementAssertions) HasLabelWithText(text string) *ElementAssertions { found := false for _, elem := range ea.elements { if label, ok := elem.(ui.Label); ok { if label.Text == text { found = true break } } } if !found { ea.t.Errorf("expected Label with text %q, not found", text) ea.failed = true } return ea } // HasListViewWithItemCount asserts a ListView has the expected number of items. func (ea *ElementAssertions) HasListViewWithItemCount(count int) *ElementAssertions { var listView *ui.ListView for _, elem := range ea.elements { if lv, ok := elem.(ui.ListView); ok { listView = &lv break } } if listView == nil { ea.t.Error("expected ListView element, not found") ea.failed = true return ea } if len(listView.Items) != count { ea.t.Errorf("expected ListView with %d items, got %d", count, len(listView.Items)) ea.failed = true } return ea } // HasListViewWithItems asserts a ListView has the expected items. func (ea *ElementAssertions) HasListViewWithItems(expected []string) *ElementAssertions { var listView *ui.ListView for _, elem := range ea.elements { if lv, ok := elem.(ui.ListView); ok { listView = &lv break } } if listView == nil { ea.t.Error("expected ListView element, not found") ea.failed = true return ea } for _, expItem := range expected { found := false for _, item := range listView.Items { if item.Text == expItem { found = true break } } if !found { ea.t.Errorf("expected ListView item %q, not found", expItem) ea.failed = true } } return ea } // HasElementInRegion asserts an element exists within the given region bounds. func (ea *ElementAssertions) HasElementInRegion(region ui.Region) *ElementAssertions { found := false for _, elem := range ea.elements { elemRegion := elem.Region() if elemRegion.X >= region.X && elemRegion.Y >= region.Y && elemRegion.X+elemRegion.W <= region.X+region.W && elemRegion.Y+elemRegion.H <= region.Y+region.H { found = true break } } if !found { ea.t.Errorf("expected element in region %+v, not found", region) ea.failed = true } return ea } // HasVisibleElement asserts at least one visible element exists. func (ea *ElementAssertions) HasVisibleElement() *ElementAssertions { found := false for _, elem := range ea.elements { if elem.Visible() { found = true break } } if !found { ea.t.Error("expected at least one visible element, none found") ea.failed = true } return ea } // HasButtonWithText asserts a Button element with the given text exists. func (ea *ElementAssertions) HasButtonWithText(text string) *ElementAssertions { found := false for _, elem := range ea.elements { if btn, ok := elem.(ui.Button); ok { if btn.Text == text { found = true break } } } if !found { ea.t.Errorf("expected Button with text %q, not found", text) ea.failed = true } return ea } // HasTextFieldWithID asserts a TextField with the given ID exists. func (ea *ElementAssertions) HasTextFieldWithID(id string) *ElementAssertions { found := false for _, elem := range ea.elements { if tf, ok := elem.(ui.TextField); ok { if tf.ID() == id { found = true break } } } if !found { ea.t.Errorf("expected TextField with ID %q, not found", id) ea.failed = true } return ea } // HasIconWithName asserts an Icon with the given name exists. func (ea *ElementAssertions) HasIconWithName(name string) *ElementAssertions { found := false for _, elem := range ea.elements { if icon, ok := elem.(ui.Icon); ok { if icon.Name == name { found = true break } } } if !found { ea.t.Errorf("expected Icon with name %q, not found", name) ea.failed = true } return ea } // GetElementByID returns the element with the given ID, or nil if not found. func (ea *ElementAssertions) GetElementByID(id string) ui.Element { for _, elem := range ea.elements { if idElem, ok := elem.(interface{ ID() string }); ok { if idElem.ID() == id { return elem } } } return nil } // GetElementByType returns the first element of the given type, or nil if not found. func (ea *ElementAssertions) GetElementByType(t reflect.Type) ui.Element { for _, elem := range ea.elements { if reflect.TypeOf(elem) == t { return elem } } return nil } // GetListItems returns all list item texts from the first ListView found. func (ea *ElementAssertions) GetListItems() []string { for _, elem := range ea.elements { if lv, ok := elem.(ui.ListView); ok { items := make([]string, len(lv.Items)) for i, item := range lv.Items { items[i] = item.Text } return items } } return nil } // GetLabelTexts returns all label texts from the frame. func (ea *ElementAssertions) GetLabelTexts() []string { var texts []string for _, elem := range ea.elements { if label, ok := elem.(ui.Label); ok { texts = append(texts, label.Text) } } return texts } // GetButtonTexts returns all button texts from the frame. func (ea *ElementAssertions) GetButtonTexts() []string { var texts []string for _, elem := range ea.elements { if btn, ok := elem.(ui.Button); ok { texts = append(texts, btn.Text) } } return texts } // GetElementRegions returns all element regions from the frame. func (ea *ElementAssertions) GetElementRegions() []ui.Region { regions := make([]ui.Region, len(ea.elements)) for i, elem := range ea.elements { regions[i] = elem.Region() } return regions } // Failed returns true if any assertion has failed. func (ea *ElementAssertions) Failed() bool { return ea.failed } // --- Helper functions --- // regionsOverlap checks if two regions overlap. func regionsOverlap(a, b ui.Region) bool { return !(a.X+a.W <= b.X || b.X+b.W <= a.X || a.Y+a.H <= b.Y || b.Y+b.H <= a.Y) } // AssertNoOverlappingElements asserts that no elements in the frame overlap. func AssertNoOverlappingElements(t testing.TB, frame []ui.Element) { for i := 0; i < len(frame); i++ { for j := i + 1; j < len(frame); j++ { regionA := frame[i].Region() regionB := frame[j].Region() if regionsOverlap(regionA, regionB) { t.Errorf("elements %d (%T) and %d (%T) overlap: %+v vs %+v", i, frame[i], j, frame[j], regionA, regionB) } } } } // AssertElementPositions asserts that elements are positioned within expected bounds. func AssertElementPositions(t testing.TB, frame []ui.Element, bounds ui.Region) { for i, elem := range frame { region := elem.Region() if region.X < bounds.X || region.Y < bounds.Y || region.X+region.W > bounds.X+bounds.W || region.Y+region.H > bounds.Y+bounds.H { t.Errorf("element %d (%T) is outside expected bounds: %+v vs %+v", i, elem, region, bounds) } } } // AssertFrameStructure asserts the basic structure of a frame. func AssertFrameStructure(t testing.TB, frame []ui.Element, expectedCount int) { if len(frame) != expectedCount { t.Errorf("expected %d elements, got %d", expectedCount, len(frame)) } } // PrintFrame prints all elements in a frame for debugging. func PrintFrame(frame []ui.Element) string { var result string for i, elem := range frame { region := elem.Region() result += fmt.Sprintf(" [%d] %T: region=%+v visible=%v\n", i, elem, region, elem.Visible()) } return result }