using Annulus; using Azimuth; using System; using System.Collections.Generic; using UnitTestSharp; namespace Glass.UnitTests { public class DropdownTests : TestFixture { public static readonly Rect ScreenRect = DropdownPopupTests.ScreenRect; public static readonly Rect DropdownBounds = DropdownPopupTests.AnchorRect; public static readonly Material NarrowLabel = new ScrollViewTests.SizedSolidMaterial(new Vector(40, 16)); public static readonly Material WideLabel = new ScrollViewTests.SizedSolidMaterial(new Vector(90, 16)); public static Dropdown NewDropdown() { var dropdown = new Dropdown { Bounds = DropdownBounds }; dropdown.Popup.ScrollViewMaterial = ScrollViewTests.WithBars(DropdownPopupTests.Thickness); dropdown.Popup.ContentSize = DropdownPopupTests.ContentSize; return dropdown; } public static void Walk(Control root) { root.UpdateInSubtree(TimeSpan.Zero, new UpdateState { ScreenSize = ScreenRect.Size, ParentCanvasRect = ScreenRect, ParentViewportRect = ScreenRect, ParentBounds = ScreenRect, }); } public static Material Bordered3 => ScrollViewTests.Bordered(3, "face"); public static Rect Box(Scalar x, Scalar y, Scalar width, Scalar height) { return DropdownPopupTests.Box(x, y, width, height); } public static ButtonMaterial Arrow(Scalar width, Scalar height) { return new ScrollViewTests.SizedSolidMaterial(new Vector(width, height)); } public static Dropdown NewSizedFace(ButtonMaterial arrow = default, Material background = null, Scalar arrowGap = default) { var dropdown = new Dropdown { Bounds = Box(0, 0, 200, 40) }; dropdown.DropdownMaterial = new DropdownMaterial { Background = background, ArrowButton = arrow, ArrowGap = arrowGap, }; return dropdown; } public static Dropdown OpenedAndPlaced() { Dropdown dropdown = NewDropdown(); dropdown.Open(); Walk(dropdown); return dropdown; } /// /// The label a popup row frames, i.e. what its item was built from. /// [IgnoreTest] public static Material LabelMaterialOf(Control row) { // A missing chrome state leaves the label unframed. Material nominal = ((Button)row).ButtonMaterial.NominalMaterial; return nominal is FramedMaterial framed ? framed.Body : nominal; } [IgnoreTest] public static Control[] Rows(Dropdown dropdown) { var rows = new List(); foreach (Control row in dropdown.Popup.CanvasChildren.BackToFront()) { rows.Add(row); } return rows.ToArray(); } [IgnoreTest] public static int DrawsOf(RendererTests.TestRenderer renderer, Material material) { int returnMe = 0; foreach (RendererTests.TestRenderer.RenderArguments call in renderer.renderCalls) { if (ReferenceEquals(call.material, material)) { ++returnMe; } } return returnMe; } /// /// The local rect a material was drawn at, for a material drawn exactly once. /// [IgnoreTest] public static Rect RectOf(RendererTests.TestRenderer renderer, Material material) { foreach (RendererTests.TestRenderer.RenderArguments call in renderer.renderCalls) { if (ReferenceEquals(call.material, material)) { return call.rect; } } throw new InvalidOperationException($"{material} was never drawn."); } public class CtorTests : TestFixture { public void Always_OwnsAClosedPopup() { Dropdown dropdown = NewDropdown(); CheckNotNull(dropdown.Popup); CheckFalse(dropdown.IsOpen); } public void Always_HoldsTheArrowAndThePopupBeneathItself() { Dropdown dropdown = NewDropdown(); bool foundArrow = false; bool foundPopup = false; foreach (Control child in dropdown.DirectChildren.BackToFront()) { foundArrow = foundArrow || child == dropdown.ArrowButton; foundPopup = foundPopup || child == dropdown.Popup; } CheckTrue(foundArrow); CheckTrue(foundPopup); } public void Always_HasNoItemsUntilRebuilt() { CheckEqual(0, NewDropdown().Popup.CanvasChildren.Count); } } public class ArrowButtonTests : TestFixture { public void Always_DocksOnTheRightAtItsPreferredSizeCenteredVertically() { CheckEqual(Box(180, 10, 20, 20), NewSizedFace(Arrow(20, 20)).ArrowButton.Bounds); } public void BorderedBackground_InsetsIt() { Dropdown dropdown = NewSizedFace(Arrow(20, 20), Bordered3); CheckEqual(Box(174, 7, 20, 20), dropdown.ArrowButton.Bounds); } public void TallerThanTheFace_ShrinksToFit() { Dropdown dropdown = NewSizedFace(Arrow(20, 30)); dropdown.Bounds = Box(0, 0, 200, 10); CheckEqual(Box(180, 0, 20, 10), dropdown.ArrowButton.Bounds); } public void WiderThanTheFace_TakesTheWholeWidth() { Dropdown dropdown = NewSizedFace(Arrow(20, 20)); dropdown.Bounds = Box(0, 0, 15, 40); CheckEqual(Box(0, 10, 15, 20), dropdown.ArrowButton.Bounds); } public void NoMaterial_HasNoSize() { CheckEqual(Vector.Zero, NewSizedFace().ArrowButton.Bounds.Size); } public void ChangingItsMaterial_RelaysOutTheArrow() { Dropdown dropdown = NewSizedFace(Arrow(20, 20)); dropdown.ArrowButton.ButtonMaterial = Arrow(40, 20); CheckEqual(Box(160, 10, 40, 20), dropdown.ArrowButton.Bounds); } } public class ArrowAnchorTests : TestFixture { public void Default_IsRight() { CheckEqual(Anchor.Right, NewDropdown().ArrowAnchor); } public void Left_DocksTheArrowOnTheLeftCenteredVertically() { Dropdown dropdown = NewSizedFace(Arrow(20, 20)); dropdown.ArrowAnchor = Anchor.Left; CheckEqual(Box(0, 10, 20, 20), dropdown.ArrowButton.Bounds); } public void Left_WithABorderedBackground_InsetsTheArrowVertically() { Dropdown dropdown = NewSizedFace(Arrow(20, 20), Bordered3); dropdown.ArrowAnchor = Anchor.Left; CheckEqual(Box(0, 7, 20, 20), dropdown.ArrowButton.Bounds); } public void Left_WiderThanTheFace_TakesTheWholeWidth() { Dropdown dropdown = NewSizedFace(Arrow(20, 20)); dropdown.ArrowAnchor = Anchor.Left; dropdown.Bounds = Box(0, 0, 15, 40); CheckEqual(Box(0, 10, 15, 20), dropdown.ArrowButton.Bounds); } public void ChangingBackAndForth_RelaysOutTheFaceEachTime() { Dropdown dropdown = NewSizedFace(Arrow(20, 20)); dropdown.ArrowAnchor = Anchor.Left; dropdown.ArrowAnchor = Anchor.Right; CheckEqual(Box(180, 10, 20, 20), dropdown.ArrowButton.Bounds); } public void Top_Throws() { Dropdown dropdown = NewDropdown(); CheckThrow(typeof(ArgumentOutOfRangeException)); dropdown.ArrowAnchor = Anchor.Top; } public void Bottom_Throws() { Dropdown dropdown = NewDropdown(); CheckThrow(typeof(ArgumentOutOfRangeException)); dropdown.ArrowAnchor = Anchor.Bottom; } } public class BoundsTests : TestFixture { public void Setting_RelaysOutTheArrow() { Dropdown dropdown = NewSizedFace(Arrow(20, 20)); dropdown.Bounds = Box(0, 0, 300, 60); CheckEqual(Box(280, 20, 20, 20), dropdown.ArrowButton.Bounds); } public void Setting_RelaysOutTheItemsToTheNewWidth() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel }); dropdown.Bounds = Box(0, 0, 300, 40); CheckEqual(300, Rows(dropdown)[0].Bounds.Width); } } public class MaterialTests : TestFixture { public void Getting_ReturnsWhatWasSet() { Material background = Bordered3; Dropdown dropdown = NewSizedFace(); dropdown.Material = background; CheckEqual(background, dropdown.Material); } public void Setting_RelaysOutTheArrow() { Dropdown dropdown = NewSizedFace(Arrow(20, 20)); dropdown.Material = Bordered3; CheckEqual(Box(174, 7, 20, 20), dropdown.ArrowButton.Bounds); } } public class DropdownMaterialTests : TestFixture { public void Getting_GathersEachPartFromItsControl() { Material background = Bordered3; ButtonMaterial arrow = Arrow(20, 20); ButtonMaterial item = Arrow(10, 10); ButtonMaterial selected = Arrow(12, 12); Material popupBackground = ScrollViewTests.Bordered(2, "popup"); Dropdown dropdown = NewSizedFace(); dropdown.Material = background; dropdown.ArrowButton.ButtonMaterial = arrow; dropdown.Popup.ItemButtonMaterial = item; dropdown.Popup.SelectedItemButtonMaterial = selected; dropdown.Popup.ScrollViewMaterial = new ScrollViewMaterial { Background = popupBackground }; DropdownMaterial gathered = dropdown.DropdownMaterial; CheckEqual(background, gathered.Background); CheckEqual(arrow, gathered.ArrowButton); CheckEqual(item, gathered.Popup.ItemButton); CheckEqual(selected, gathered.Popup.SelectedItemButton); CheckEqual(popupBackground, gathered.Popup.ScrollView.Background); } public void Setting_SpreadsEachPartToItsControl() { Material background = Bordered3; ButtonMaterial arrow = Arrow(20, 20); ButtonMaterial item = Arrow(10, 10); ButtonMaterial selected = Arrow(12, 12); Material popupBackground = ScrollViewTests.Bordered(2, "popup"); Dropdown dropdown = NewSizedFace(); dropdown.DropdownMaterial = new DropdownMaterial { Background = background, ArrowButton = arrow, Popup = new DropdownPopupMaterial { ItemButton = item, SelectedItemButton = selected, ScrollView = new ScrollViewMaterial { Background = popupBackground }, }, }; CheckEqual(background, dropdown.Material); CheckEqual(arrow, dropdown.ArrowButton.ButtonMaterial); CheckEqual(item, dropdown.Popup.ItemButtonMaterial); CheckEqual(selected, dropdown.Popup.SelectedItemButtonMaterial); CheckEqual(popupBackground, dropdown.Popup.ScrollViewMaterial.Background); } public void Setting_RelaysOutTheArrow() { Dropdown dropdown = NewSizedFace(); dropdown.DropdownMaterial = new DropdownMaterial { Background = Bordered3, ArrowButton = Arrow(20, 20), }; CheckEqual(Box(174, 7, 20, 20), dropdown.ArrowButton.Bounds); } public void Setting_RelaysOutExistingItemsToTheNewPopupChrome() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel }); dropdown.DropdownMaterial = new DropdownMaterial { Popup = new DropdownPopupMaterial { ScrollView = new ScrollViewMaterial { Background = Bordered3 }, }, }; CheckEqual(194, Rows(dropdown)[0].Bounds.Width); } public void Setting_RelaysOutExistingItemsToTheNewItemChrome() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel }); dropdown.DropdownMaterial = new DropdownMaterial { Popup = new DropdownPopupMaterial { ItemButton = new ButtonMaterial(Bordered3) }, }; CheckEqual(new Vector(200, 22), Rows(dropdown)[0].Bounds.Size); } public void RoundTrips_TheArrowGap() { Dropdown dropdown = NewSizedFace(); dropdown.DropdownMaterial = new DropdownMaterial { ArrowGap = 8 }; CheckEqual(8, dropdown.DropdownMaterial.ArrowGap); } // ArrowGap moves the boundary between the label and the arrow, which is only observable in what the // (otherwise private) label area draws — ArrowButton.Bounds stays flush against its anchored edge either // way, so it can't tell these cases apart. public void Setting_ArrowGap_NarrowsTheLabelBehindTheArrow() { Dropdown dropdown = NewSizedFace(Arrow(20, 20), arrowGap: 8); dropdown.Rebuild(new[] { NarrowLabel }); dropdown.Select(0); var renderer = new RendererTests.NullTestRenderer(); renderer.RenderSubtree(context: 0, dropdown, new DrawState()); CheckEqual(172, RectOf(renderer, NarrowLabel).Width); } public void Setting_ArrowGap_ClampsSoATightFaceNeverThrows() { Dropdown dropdown = NewSizedFace(Arrow(20, 20), arrowGap: 8); dropdown.Rebuild(new[] { NarrowLabel }); dropdown.Select(0); dropdown.Bounds = Box(0, 0, 24, 40); var renderer = new RendererTests.NullTestRenderer(); renderer.RenderSubtree(context: 0, dropdown, new DrawState()); CheckEqual(0, DrawsOf(renderer, NarrowLabel)); } } public class RebuildTests : TestFixture { public void RowsNarrowerThanTheFace_StretchToFillIt() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel }); CheckEqual(dropdown.Bounds.Width, Rows(dropdown)[0].Bounds.Width); } public void Always_ClearsAnyPriorSelection() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); dropdown.Select(1); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); CheckNull(dropdown.SelectedIndex); CheckNull(dropdown.Popup.SelectedIndex); } public void Null_Throws() { Dropdown dropdown = NewSizedFace(); CheckThrow(typeof(ArgumentNullException)); dropdown.Rebuild(null); } public void GenericOverload_MapsEachItemThroughLabelForInOrder() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { "first", "second" }, item => item == "first" ? NarrowLabel : WideLabel); Control[] rows = Rows(dropdown); CheckEqual(NarrowLabel, LabelMaterialOf(rows[0])); CheckEqual(WideLabel, LabelMaterialOf(rows[1])); } public void GenericOverload_EmptyList_LeavesThePopupWithNoRows() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(Array.Empty(), item => NarrowLabel); CheckEqual(0, dropdown.Popup.CanvasChildren.Count); } public void GenericOverload_NullItems_Throws() { Dropdown dropdown = NewSizedFace(); CheckThrow(typeof(ArgumentNullException)); dropdown.Rebuild(null, item => NarrowLabel); } public void GenericOverload_NullLabelFor_Throws() { Dropdown dropdown = NewSizedFace(); CheckThrow(typeof(ArgumentNullException)); dropdown.Rebuild(new[] { "first" }, null); } public void ClickingARow_SelectsThatIndex() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); Rows(dropdown)[1].Click(default, MouseButtonFlags.Left); CheckEqual(1, dropdown.SelectedIndex); } public void ClickingARow_ClosesThePopup() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); dropdown.Open(); Rows(dropdown)[0].Click(default, MouseButtonFlags.Left); CheckFalse(dropdown.IsOpen); } } public class SelectedIndexTests : TestFixture { public void BeforeAnySelection_IsNull() { CheckNull(NewDropdown().SelectedIndex); } } public class SelectTests : TestFixture { public void ValidIndex_SetsSelectedIndex() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); dropdown.Select(1); CheckEqual(1, dropdown.SelectedIndex); } public void ValidIndex_ClosesThePopup() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); dropdown.Open(); dropdown.Select(0); CheckFalse(dropdown.IsOpen); } public void Null_ClearsTheSelection() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); dropdown.Select(0); dropdown.Select(null); CheckNull(dropdown.SelectedIndex); } public void NullWithNoItems_DoesNotThrow() { NewDropdown().Select(null); } public void ValidIndex_SelectsThatRowInTheList() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); dropdown.Select(1); CheckEqual(1, dropdown.Popup.SelectedIndex); } public void Null_ClearsTheListsSelection() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); dropdown.Select(1); dropdown.Select(null); CheckNull(dropdown.Popup.SelectedIndex); } public void ClickingARow_SelectsItInTheList() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); Rows(dropdown)[1].Click(default, MouseButtonFlags.Left); CheckEqual(1, dropdown.Popup.SelectedIndex); } public void ValidIndex_FiresTheEventWithThatIndex() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); int? firedWith = null; dropdown.OnSelectionChanged += index => firedWith = index; dropdown.Select(1); CheckEqual(1, firedWith); } public void Null_FiresTheEventWithNull() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); dropdown.Select(0); int timesFired = 0; int? firedWith = 0; dropdown.OnSelectionChanged += index => { ++timesFired; firedWith = index; }; dropdown.Select(null); CheckEqual(1, timesFired); CheckNull(firedWith); } public void IndexEqualToCount_Throws() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); CheckThrow(typeof(ArgumentOutOfRangeException)); dropdown.Select(2); } public void NegativeIndex_Throws() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); CheckThrow(typeof(ArgumentOutOfRangeException)); dropdown.Select(-1); } public void ValidIndex_ShowsThatLabelOnTheFace() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); dropdown.Select(1); var renderer = new RendererTests.NullTestRenderer(); renderer.RenderSubtree(context: 0, dropdown, new DrawState()); CheckEqual(1, DrawsOf(renderer, WideLabel)); } public void Null_DrawsNeitherLabel() { Dropdown dropdown = NewSizedFace(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); dropdown.Select(0); dropdown.Select(null); var renderer = new RendererTests.NullTestRenderer(); renderer.RenderSubtree(context: 0, dropdown, new DrawState()); CheckEqual(0, DrawsOf(renderer, NarrowLabel)); CheckEqual(0, DrawsOf(renderer, WideLabel)); } } public class UpdateTests : TestFixture { public void WhileOpen_ShowsTheArrowPressed() { CheckTrue(OpenedAndPlaced().ArrowButton.Pressed); } public void ClosingIt_ReleasesTheArrow() { Dropdown dropdown = OpenedAndPlaced(); dropdown.Close(); Walk(dropdown); CheckFalse(dropdown.ArrowButton.Pressed); } public void ThePopupClosingItself_ReleasesTheArrow() { Dropdown dropdown = OpenedAndPlaced(); dropdown.Popup.MouseDown(DropdownPopupTests.At(DropdownPopupTests.OutsideThePopup), MouseButtonFlags.Left); Walk(dropdown); CheckFalse(dropdown.ArrowButton.Pressed); } public void WhileClosed_LeavesAPressInProgressOnTheArrowAlone() { Dropdown dropdown = NewDropdown(); dropdown.ArrowButton.ButtonsDown = MouseButtonFlags.Left; Walk(dropdown); CheckTrue(dropdown.ArrowButton.Pressed); } } public class MouseDownTests : TestFixture { public void Enabled_ClaimsTheEvent() { CheckTrue(NewDropdown().MouseDown(DropdownPopupTests.At(new Vector(120, 210)), MouseButtonFlags.Left)); } public void Disabled_LetsTheEventThrough() { Dropdown dropdown = NewDropdown(); dropdown.Enabled = false; CheckFalse(dropdown.MouseDown(DropdownPopupTests.At(new Vector(120, 210)), MouseButtonFlags.Left)); } public void Always_StillFiresTheHandler() { Dropdown dropdown = NewDropdown(); int fired = 0; dropdown.OnMouseDown += (in MouseState _, MouseButtonFlags __) => ++fired; dropdown.MouseDown(DropdownPopupTests.At(new Vector(120, 210)), MouseButtonFlags.Left); CheckEqual(1, fired); } } public class ClickTests : TestFixture { public void Enabled_OpensThePopupAndClaimsTheEvent() { Dropdown dropdown = NewDropdown(); CheckTrue(dropdown.Click(DropdownPopupTests.At(new Vector(120, 210)), MouseButtonFlags.Left)); CheckTrue(dropdown.IsOpen); } public void Disabled_LeavesItClosedAndLetsTheEventThrough() { Dropdown dropdown = NewDropdown(); dropdown.Enabled = false; CheckFalse(dropdown.Click(DropdownPopupTests.At(new Vector(120, 210)), MouseButtonFlags.Left)); CheckFalse(dropdown.IsOpen); } public void Always_StillFiresTheHandler() { Dropdown dropdown = NewDropdown(); int fired = 0; dropdown.OnClick += (in MouseState _, MouseButtonFlags __) => ++fired; dropdown.Click(DropdownPopupTests.At(new Vector(120, 210)), MouseButtonFlags.Left); CheckEqual(1, fired); } } public class FaceInputTests : TestFixture { private static readonly Vector OnTheFace = new Vector(120, 210); private static readonly Vector OnTheArrow = new Vector(240, 215); private static readonly Vector OnTheLeftArrow = new Vector(110, 215); private MouseManager manager; private Dropdown dropdown; public override void TestSetup() { manager = new MouseManager(); dropdown = NewDropdown(); dropdown.ArrowButton.ButtonMaterial = Arrow(20, 20); manager.Controls.Add(dropdown); } [IgnoreTest] private void Click(Vector position) { manager.MouseDown(DropdownPopupTests.At(position), MouseButtonFlags.Left); manager.MouseUp(DropdownPopupTests.At(position), MouseButtonFlags.Left); } public void ClickingTheFace_OpensThePopup() { Click(OnTheFace); CheckTrue(dropdown.IsOpen); } public void ClickingTheArrow_OpensThePopup() { Click(OnTheArrow); CheckTrue(dropdown.IsOpen); } public void PressingTheArrow_ReachesTheArrowButton() { Control reached = manager.MouseDown(DropdownPopupTests.At(OnTheArrow), MouseButtonFlags.Left); CheckEqual(dropdown.ArrowButton, reached); } public void PressingTheArrowOnTheLeft_ReachesTheArrowButton() { dropdown.ArrowAnchor = Anchor.Left; Control reached = manager.MouseDown(DropdownPopupTests.At(OnTheLeftArrow), MouseButtonFlags.Left); CheckEqual(dropdown.ArrowButton, reached); } public void PressingTheFaceButReleasingElsewhere_LeavesItClosed() { manager.MouseDown(DropdownPopupTests.At(OnTheFace), MouseButtonFlags.Left); manager.MouseUp(DropdownPopupTests.At(DropdownPopupTests.OutsideThePopup), MouseButtonFlags.Left); CheckFalse(dropdown.IsOpen); } public void ClickingADisabledFace_LeavesItClosed() { dropdown.Enabled = false; Click(OnTheFace); CheckFalse(dropdown.IsOpen); } public void ClickingTheFaceWhileOpen_ClosesIt() { Click(OnTheFace); Walk(dropdown); Click(OnTheFace); CheckFalse(dropdown.IsOpen); } public void ClickingTheArrowWhileOpen_ClosesIt() { Click(OnTheArrow); Walk(dropdown); Click(OnTheArrow); CheckFalse(dropdown.IsOpen); } } public class OpenTests : TestFixture { public void Always_IsOpenAtOnce() { Dropdown dropdown = NewDropdown(); dropdown.Open(); CheckTrue(dropdown.IsOpen); } public void Always_ShowsThePopupBesideItOnTheNextUpdate() { Dropdown dropdown = NewDropdown(); dropdown.Open(); CheckFalse(dropdown.Popup.Visible); Walk(dropdown); CheckTrue(dropdown.Popup.Visible); CheckEqual(Box(100, 230, 150, 100), dropdown.Popup.Bounds); } public void InsideAnOffsetParent_PlacesThePopupBesideItsOnScreenBounds() { var parent = new Panel { Material = Material.Invisible, Bounds = Box(50, 40, 500, 500), }; Dropdown dropdown = NewDropdown(); dropdown.Bounds = Box(100, 200, 150, 30); parent.CanvasChildren.Add(dropdown); dropdown.Open(); Walk(parent); CheckEqual(Box(150, 270, 150, 100), dropdown.Popup.Bounds); } public void OpeningAScrollingList_WidensThePopupBesideTheRowsRatherThanNarrowingThem() { Dropdown dropdown = NewSizedFace(); dropdown.Popup.ScrollViewMaterial = new ScrollViewMaterial { VerticalScrollBar = ScrollViewTests.WithBars(20).VerticalScrollBar, }; dropdown.Popup.MaxHeight = 10; dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); dropdown.Open(); Walk(dropdown); CheckEqual(200, Rows(dropdown)[0].Bounds.Width); CheckEqual(220, dropdown.Popup.Bounds.Width); } } public class CloseTests : TestFixture { public void WhileOpen_ClosesAndHidesThePopup() { Dropdown dropdown = OpenedAndPlaced(); dropdown.Close(); CheckFalse(dropdown.IsOpen); CheckFalse(dropdown.Popup.Visible); } } public class EnabledTests : TestFixture { public void Disabling_ClosesAnOpenPopup() { Dropdown dropdown = OpenedAndPlaced(); dropdown.Enabled = false; CheckFalse(dropdown.IsOpen); CheckFalse(dropdown.Popup.Visible); } public void Enabling_DoesNotReopenIt() { Dropdown dropdown = OpenedAndPlaced(); dropdown.Enabled = false; dropdown.Enabled = true; Walk(dropdown); CheckFalse(dropdown.IsOpen); CheckFalse(dropdown.Popup.Visible); } public void Disabling_LeavesAClosedDropdownClosed() { Dropdown dropdown = NewDropdown(); dropdown.Enabled = false; CheckFalse(dropdown.IsOpen); CheckFalse(dropdown.Enabled); } public void Disabling_DisablesTheArrow() { Dropdown dropdown = NewDropdown(); dropdown.Enabled = false; CheckFalse(dropdown.ArrowButton.Enabled); } public void Enabling_EnablesTheArrowAgain() { Dropdown dropdown = NewDropdown(); dropdown.Enabled = false; dropdown.Enabled = true; CheckTrue(dropdown.ArrowButton.Enabled); } public void Disabling_DisablesEachItem() { Dropdown dropdown = NewDropdown(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); dropdown.Enabled = false; foreach (Control item in dropdown.Popup.CanvasChildren.BackToFront()) { CheckFalse(item.Enabled); } } public void Enabling_EnablesEachItemAgain() { Dropdown dropdown = NewDropdown(); dropdown.Rebuild(new[] { NarrowLabel, WideLabel }); dropdown.Enabled = false; dropdown.Enabled = true; foreach (Control item in dropdown.Popup.CanvasChildren.BackToFront()) { CheckTrue(item.Enabled); } } } public class VisibleTests : TestFixture { public void Hiding_ClosesAnOpenPopup() { Dropdown dropdown = OpenedAndPlaced(); dropdown.Visible = false; CheckFalse(dropdown.IsOpen); CheckFalse(dropdown.Popup.Visible); } public void Showing_DoesNotReopenIt() { Dropdown dropdown = OpenedAndPlaced(); dropdown.Visible = false; dropdown.Visible = true; Walk(dropdown); CheckFalse(dropdown.IsOpen); CheckFalse(dropdown.Popup.Visible); } public void Hiding_LeavesAClosedDropdownClosed() { Dropdown dropdown = NewDropdown(); dropdown.Visible = false; CheckFalse(dropdown.IsOpen); CheckFalse(dropdown.Visible); } } public class ModalInputTests : TestFixture { private static readonly Vector OnTheOwner = new Vector(120, 210); private static readonly Vector OutsideThePopup = DropdownPopupTests.OutsideThePopup; private static readonly Vector OnTheItem = new Vector(110, 240); private static readonly Vector InAGapBetweenItems = new Vector(200, 300); private MouseManager manager; private MouseManagerTests.RecordingControl backdrop; private MouseManagerTests.RecordingControl item; private Dropdown dropdown; private int ownerClicks; public override void TestSetup() { ownerClicks = 0; manager = new MouseManager(); backdrop = new MouseManagerTests.RecordingControl("backdrop") { Handles = true, Bounds = ScreenRect }; item = new MouseManagerTests.RecordingControl("item") { Handles = true, Bounds = Box(0, 0, 100, 20), }; dropdown = NewDropdown(); dropdown.Popup.CanvasChildren.Add(item); dropdown.OnClick += (in MouseState _, MouseButtonFlags __) => ++ownerClicks; manager.Controls.Add(backdrop); manager.Controls.Add(dropdown); } [IgnoreTest] private void OpenAndPlace() { dropdown.Open(); Walk(dropdown); } [IgnoreTest] private Control Press(Vector position) { return manager.MouseDown(DropdownPopupTests.At(position), MouseButtonFlags.Left); } [IgnoreTest] private void Release(Vector position) { manager.MouseUp(DropdownPopupTests.At(position), MouseButtonFlags.Left); } public void PressingOutsideThePopup_ClosesItWithoutReachingAnythingBeneath() { OpenAndPlace(); CheckEqual(dropdown.Popup, Press(OutsideThePopup)); CheckFalse(dropdown.IsOpen); CheckEqual(0, backdrop.MouseDownCount); } public void PressingTheOwnerWhileOpen_ClosesItWithoutTheOwnerHearingIt() { OpenAndPlace(); Press(OnTheOwner); Release(OnTheOwner); CheckFalse(dropdown.IsOpen); CheckEqual(0, ownerClicks); } public void ClickingTheOwnerOnceItIsClosed_ReachesTheOwner() { OpenAndPlace(); Press(OnTheOwner); Release(OnTheOwner); Press(OnTheOwner); Release(OnTheOwner); CheckEqual(1, ownerClicks); } public void PressingAnItem_ReachesTheItemAndLeavesThePopupOpen() { OpenAndPlace(); CheckEqual(item, Press(OnTheItem)); CheckEqual(1, item.MouseDownCount); CheckTrue(dropdown.IsOpen); } public void PressingBetweenItems_IsClaimedByThePopupRatherThanPassedThrough() { OpenAndPlace(); CheckEqual(dropdown.Popup, Press(InAGapBetweenItems)); CheckEqual(0, backdrop.MouseDownCount); CheckTrue(dropdown.IsOpen); } public void MovingThePointer_DoesNotReachAnythingBeneath() { OpenAndPlace(); CheckEqual(dropdown.Popup, manager.MouseMove(DropdownPopupTests.At(OutsideThePopup))); CheckEqual(0, backdrop.MouseMoveStartCount); } public void TheWheelOverThePopup_DoesNotReachAnythingBeneath() { OpenAndPlace(); CheckEqual(dropdown.Popup, manager.MouseWheel(DropdownPopupTests.At(InAGapBetweenItems), -1)); CheckEqual(0, backdrop.MouseWheelCount); } public void TheWheelOutsideThePopup_DoesNotReachAnythingBeneathAndLeavesItOpen() { OpenAndPlace(); CheckEqual(dropdown.Popup, manager.MouseWheel(DropdownPopupTests.At(OutsideThePopup), -1)); CheckEqual(0, backdrop.MouseWheelCount); CheckTrue(dropdown.IsOpen); } public void WhileClosed_LetsInputReachWhatIsBeneath() { Walk(dropdown); CheckEqual(backdrop, Press(OutsideThePopup)); } } } }