using Annulus; using Azimuth; using Glass.UnitTests.Materials; using System; using System.Collections.Generic; using System.Globalization; using UnitTestSharp; namespace Glass.UnitTests { public class NumberFieldTests : TestFixture { public static readonly NumberGrid ZeroToHundred = new NumberGrid { Max = 100 }; /// /// A field with a default behavior and no material or bounds. /// public static NumberField NewUnstyledField(NumberField.Data data) { return new NumberField(new NumberFieldBehavior(), data); } /// /// A 100x40 field at (10, 10) with 20x20 buttons: its label spans x 10-90, its button column x 90-110. /// public static NumberField NewField(NumberGrid grid) { var field = new NumberField(new NumberFieldBehavior(), new NumberField.Data { Grid = grid }) { NumberFieldMaterial = new NumberFieldMaterial { IncrementButton = Glass.UnitTests.NumberFieldMaterialTests.FixedSizeButton(20, 20), DecrementButton = Glass.UnitTests.NumberFieldMaterialTests.FixedSizeButton(20, 20), }, }; field.Bounds = Rect.FromPositionAndSize(new Vector(10, 10), new Vector(100, 40)); return field; } public static NumberField NewField() => NewField(ZeroToHundred); public static MouseState At(Scalar x, Scalar y) => new MouseState { Position = new Vector(x, y) }; public static readonly MouseState OnTheLabel = At(50, 30); public static readonly MouseState OnTheButtons = At(100, 30); /// /// Presses on the label at x 50 and drags to , leaving the scrub in progress. /// public static void ScrubTo(NumberField field, Scalar x) { field.MouseDown(OnTheLabel, MouseButtonFlags.Left); field.MouseMove(At(x, 30)); } public class CtorTests : TestFixture { public void KeepsTheBehaviorItWasGiven() { var behavior = new NumberFieldBehavior(); CheckEqual(behavior, new NumberField(behavior, new NumberField.Data()).Behavior); } public void NullBehavior_Throws() { CheckThrow(typeof(ArgumentNullException)); new NumberField(null, new NumberField.Data()); } public void InvalidGrid_Throws() { CheckThrow(typeof(ArgumentException)); NewUnstyledField(new NumberField.Data { Grid = new NumberGrid { Max = 1, Resolution = 0.3m } }); } public void TakesTheDatasGridAndFormat() { var grid = new NumberGrid { Max = 10, Resolution = 0.5m }; Func format = (value, culture) => "custom"; var field = NewUnstyledField(new NumberField.Data { Grid = grid, Format = format }); CheckEqual(grid, field.Grid); CheckEqual(format, field.Format); } public void Value_IsSnappedOntoTheDatasGridNotTheDefaultOne() { var field = NewUnstyledField(new NumberField.Data { Grid = ZeroToHundred, Value = 50.4m }); CheckEqual(50, field.Value); } public void ValueOutsideTheDatasGrid_IsClampedIntoIt() { var field = NewUnstyledField(new NumberField.Data { Grid = ZeroToHundred, Value = 250 }); CheckEqual(100, field.Value); } public void LabelShowsTheValueInTheBehaviorsCultureAndTheDatasFormat() { var field = new NumberField( new NumberFieldBehavior { Culture = CultureInfo.GetCultureInfo("fr-FR") }, new NumberField.Data { Grid = new NumberGrid { Max = 10, Resolution = 0.5m }, Value = 1.5m, Format = (value, culture) => value.ToString("F1", culture) + " MW", }); CheckEqual("1,5 MW", field.DisplayText); } public void ButtonsReflectTheStartingValue() { var field = NewUnstyledField(new NumberField.Data { Grid = ZeroToHundred, Value = 100 }); CheckFalse(field.IncrementButton.Enabled); Check(field.DecrementButton.Enabled); } public void DefaultData_IsZeroOnTheDefaultGrid() { var field = NewUnstyledField(new NumberField.Data()); CheckEqual(0, field.Value); CheckEqual(new NumberGrid(), field.Grid); CheckEqual("0", field.DisplayText); } } public class GridTests : TestFixture { public void InvalidGrid_ThrowsWithoutApplyingAnyOfIt() { var field = NewField(); try { field.Grid = new NumberGrid { Max = 1, Resolution = 0.3m }; } catch (ArgumentException) { } CheckEqual(ZeroToHundred, field.Grid); } public void InvalidGrid_Throws() { var field = NewField(); CheckThrow(typeof(ArgumentException)); field.Grid = new NumberGrid { Max = 1, Resolution = 0.3m }; } public void ValueOutsideTheNewRange_IsSnappedIntoIt() { var field = NewField(); field.Value = 50; field.Grid = new NumberGrid { Max = 10 }; CheckEqual(10, field.Value); } public void ResnappingTheValue_RaisesOnValueChanged() { var field = NewField(); field.Value = 50; decimal? raisedWith = null; field.OnValueChanged += (_, __, valueNow) => raisedWith = valueNow; field.Grid = new NumberGrid { Max = 10 }; CheckEqual(10, raisedWith); } public void ResnappingTheValue_RaisesOnceTheFieldShowsTheNewGrid() { var field = NewField(); field.Value = 50; string shownWhenRaised = null; bool incrementEnabledWhenRaised = true; field.OnValueChanged += (numberField, _, __) => { shownWhenRaised = numberField.DisplayText; incrementEnabledWhenRaised = numberField.IncrementButton.Enabled; }; field.Grid = new NumberGrid { Max = 10, Resolution = 0.5m }; CheckEqual("10.0", shownWhenRaised); CheckFalse(incrementEnabledWhenRaised); } public void ValueSurvivingTheSnap_DoesNotRaiseOnValueChanged() { var field = NewField(); field.Value = 5; int raised = 0; field.OnValueChanged += (_, __, ___) => ++raised; field.Grid = new NumberGrid { Max = 10 }; CheckEqual(0, raised); } public void MoreDecimalPlaces_RefreshesTheLabelEvenWithTheValueUnchanged() { var field = NewField(); field.Value = 5; field.Grid = new NumberGrid { Max = 10, Resolution = 0.01m }; CheckEqual("5.00", field.DisplayText); } public void WideningPastTheValue_ReenablesTheIncrementButton() { var field = NewField(); field.Value = 100; field.Grid = new NumberGrid { Max = 200 }; Check(field.IncrementButton.Enabled); } } public class ValueTests : TestFixture { public void IsSnappedOntoTheGrid() { var field = NewField(new NumberGrid { Max = 10, Resolution = 0.5m }); field.Value = 2.7m; CheckEqual(2.5m, field.Value); } public void IsClampedIntoRange() { var field = NewField(); field.Value = 250; CheckEqual(100, field.Value); } public void Change_RaisesOnValueChangedWithTheFieldAndBothValues() { var field = NewField(); field.Value = 7; NumberField raisedBy = null; decimal? raisedBefore = null; decimal? raisedNow = null; field.OnValueChanged += (numberField, valueBefore, valueNow) => { raisedBy = numberField; raisedBefore = valueBefore; raisedNow = valueNow; }; field.Value = 42; CheckEqual(field, raisedBy); CheckEqual(7, raisedBefore); CheckEqual(42, raisedNow); } public void Change_RaisesOnceTheFieldShowsTheNewValue() { var field = NewField(); string shownWhenRaised = null; bool incrementEnabledWhenRaised = true; field.OnValueChanged += (numberField, _, __) => { shownWhenRaised = numberField.DisplayText; incrementEnabledWhenRaised = numberField.IncrementButton.Enabled; }; field.Value = 100; CheckEqual("100", shownWhenRaised); CheckFalse(incrementEnabledWhenRaised); } public void SnappingBackToTheCurrentValue_DoesNotRaiseOnValueChanged() { var field = NewField(new NumberGrid { Max = 10, Resolution = 0.5m }); field.Value = 2.5m; int raised = 0; field.OnValueChanged += (_, __, ___) => ++raised; field.Value = 2.6m; CheckEqual(0, raised); } public void Change_UpdatesDisplayText() { var field = NewField(); field.Value = 42; CheckEqual("42", field.DisplayText); } public void AtMax_DisablesOnlyTheIncrementButton() { var field = NewField(); field.Value = 100; CheckFalse(field.IncrementButton.Enabled); Check(field.DecrementButton.Enabled); } public void BetweenTheBounds_EnablesBothButtons() { var field = NewField(); field.Value = 50; Check(field.IncrementButton.Enabled); Check(field.DecrementButton.Enabled); } } /// /// Records each OnValueChanged as "before->now". /// public static List RecordValueChanges(NumberField field) { var returnMe = new List(); field.OnValueChanged += (_, valueBefore, valueNow) => returnMe.Add($"{valueBefore}->{valueNow}"); return returnMe; } public class RaisesValueChangedWhileScrubbingTests : TestFixture { public static NumberField NewFieldAtFifty(bool raisesValueChangedWhileScrubbing) { var field = NewField(); field.Value = 50; field.RaisesValueChangedWhileScrubbing = raisesValueChangedWhileScrubbing; return field; } public void DefaultsToTrue() { Check(NewField().RaisesValueChangedWhileScrubbing); } public void True_RaisesEachStepAsItHappens() { var field = NewFieldAtFifty(raisesValueChangedWhileScrubbing: true); List changes = RecordValueChanges(field); ScrubTo(field, 58); field.MouseMove(At(66, 30)); CheckEqual(new[] { "50->51", "51->52" }, changes); } public void False_RaisesNothingMidScrub() { var field = NewFieldAtFifty(raisesValueChangedWhileScrubbing: false); List changes = RecordValueChanges(field); ScrubTo(field, 58); field.MouseMove(At(66, 30)); CheckEqual(0, changes.Count); CheckEqual(52, field.Value); } public void False_RaisesTheNetChangeOnceOnRelease() { var field = NewFieldAtFifty(raisesValueChangedWhileScrubbing: false); List changes = RecordValueChanges(field); ScrubTo(field, 58); field.MouseMove(At(66, 30)); field.MouseUp(At(66, 30), MouseButtonFlags.Left); CheckEqual(new[] { "50->52" }, changes); } public void False_ReleasedBackWhereItStarted_RaisesNothing() { var field = NewFieldAtFifty(raisesValueChangedWhileScrubbing: false); List changes = RecordValueChanges(field); ScrubTo(field, 66); field.MouseMove(At(50, 30)); field.MouseUp(At(50, 30), MouseButtonFlags.Left); CheckEqual(0, changes.Count); } public void False_Cancelled_RaisesNothing() { var field = NewFieldAtFifty(raisesValueChangedWhileScrubbing: false); List changes = RecordValueChanges(field); ScrubTo(field, 66); field.CancelMouseInput(); CheckEqual(0, changes.Count); CheckEqual(50, field.Value); } public void False_CancelledOntoANarrowedGrid_RaisesTheChangeFromTheStart() { var field = NewFieldAtFifty(raisesValueChangedWhileScrubbing: false); List changes = RecordValueChanges(field); ScrubTo(field, 66); field.Grid = new NumberGrid { Max = 10 }; field.CancelMouseInput(); CheckEqual(new[] { "50->10" }, changes); } public void True_Cancelled_RaisesTheRevert() { var field = NewFieldAtFifty(raisesValueChangedWhileScrubbing: true); List changes = RecordValueChanges(field); ScrubTo(field, 58); field.CancelMouseInput(); CheckEqual(new[] { "50->51", "51->50" }, changes); } public void False_ChangesOutsideAScrub_StillRaise() { var field = NewFieldAtFifty(raisesValueChangedWhileScrubbing: false); List changes = RecordValueChanges(field); field.Value = 60; field.StepBy(1); CheckEqual(new[] { "50->60", "60->61" }, changes); } } public class OnScrubFinishedTests : TestFixture { public void Release_RaisesTheFieldAndTheNetChange() { var field = NewField(); field.Value = 50; NumberField raisedBy = null; string raisedWith = null; field.OnScrubFinished += (numberField, valueBefore, valueNow) => { raisedBy = numberField; raisedWith = $"{valueBefore}->{valueNow}"; }; ScrubTo(field, 66); field.MouseUp(At(66, 30), MouseButtonFlags.Left); CheckEqual(field, raisedBy); CheckEqual("50->52", raisedWith); } public void RaisedAfterTheNetOnValueChanged() { var field = NewField(); field.RaisesValueChangedWhileScrubbing = false; var raised = new List(); field.OnValueChanged += (_, __, ___) => raised.Add(nameof(NumberField.OnValueChanged)); field.OnScrubFinished += (_, __, ___) => raised.Add(nameof(NumberField.OnScrubFinished)); ScrubTo(field, 58); field.MouseUp(At(58, 30), MouseButtonFlags.Left); CheckEqual(new[] { nameof(NumberField.OnValueChanged), nameof(NumberField.OnScrubFinished) }, raised); } public void ReleasedBackWhereItStarted_IsNotRaised() { var field = NewField(); int raised = 0; field.OnScrubFinished += (_, __, ___) => ++raised; ScrubTo(field, 66); field.MouseMove(At(50, 30)); field.MouseUp(At(50, 30), MouseButtonFlags.Left); CheckEqual(0, raised); } public void Cancelled_IsNotRaised() { var field = NewField(); int raised = 0; field.OnScrubFinished += (_, __, ___) => ++raised; ScrubTo(field, 66); field.CancelMouseInput(); CheckEqual(0, raised); } } public class NumberFieldDataTests : TestFixture { public void Getter_ReflectsChangesMadeThroughTheOtherProperties() { var field = NewField(); Func format = (value, culture) => "custom"; field.Value = 7; field.Format = format; NumberField.Data data = field.NumberFieldData; CheckEqual(ZeroToHundred, data.Grid); CheckEqual(7, data.Value); CheckEqual(format, data.Format); } public void Setter_SnapsTheValueOntoTheNewGridRatherThanTheOld() { var field = NewField(new NumberGrid { Max = 10 }); field.NumberFieldData = new NumberField.Data { Grid = ZeroToHundred, Value = 50 }; CheckEqual(50, field.Value); CheckEqual(ZeroToHundred, field.Grid); } public void Setter_AppliesTheFormat() { var field = NewField(); field.NumberFieldData = new NumberField.Data { Grid = ZeroToHundred, Format = (value, culture) => value + " MW", }; CheckEqual("0 MW", field.DisplayText); } public void Setter_RaisesOnValueChangedOnceWithTheOldAndNewValues() { var field = NewField(new NumberGrid { Max = 10 }); field.Value = 5; int raised = 0; decimal? raisedBefore = null; decimal? raisedNow = null; field.OnValueChanged += (_, valueBefore, valueNow) => { ++raised; raisedBefore = valueBefore; raisedNow = valueNow; }; field.NumberFieldData = new NumberField.Data { Grid = ZeroToHundred, Value = 50 }; CheckEqual(1, raised); CheckEqual(5, raisedBefore); CheckEqual(50, raisedNow); } public void UnchangedValue_RefreshesTheLabelWithoutRaisingOnValueChanged() { var field = NewField(); field.Value = 5; int raised = 0; field.OnValueChanged += (_, __, ___) => ++raised; field.NumberFieldData = new NumberField.Data { Grid = new NumberGrid { Max = 10, Resolution = 0.5m }, Value = 5, }; CheckEqual(0, raised); CheckEqual("5.0", field.DisplayText); } public void InvalidGrid_ThrowsWithoutApplyingAnyOfIt() { var field = NewField(); field.Value = 7; try { field.NumberFieldData = new NumberField.Data { Grid = new NumberGrid { Max = 1, Resolution = 0.3m }, Value = 1, Format = (value, culture) => "custom", }; } catch (ArgumentException) { } CheckEqual(ZeroToHundred, field.Grid); CheckEqual(7, field.Value); CheckNull(field.Format); } } public class BehaviorTests : TestFixture { public static NumberField NewFieldShowingOneAndAHalf(NumberFieldBehavior behavior) { var field = NewField(new NumberGrid { Max = 10, Resolution = 0.5m }); field.Value = 1.5m; field.Behavior = behavior; return field; } public void Null_Throws() { var field = NewField(); CheckThrow(typeof(ArgumentNullException)); field.Behavior = null; } public void Assigning_ReformatsTheLabelAtOnce() { var field = NewFieldShowingOneAndAHalf( new NumberFieldBehavior { Culture = CultureInfo.GetCultureInfo("fr-FR") }); CheckEqual("1,5", field.DisplayText); } public void CultureChangedAfterAssigning_ShowsFromTheNextValueChange() { var behavior = new NumberFieldBehavior(); var field = NewFieldShowingOneAndAHalf(behavior); behavior.Culture = CultureInfo.GetCultureInfo("fr-FR"); field.Value = 2; CheckEqual("2,0", field.DisplayText); } public void ScrubDistanceChangedAfterAssigning_AppliesToTheNextScrub() { var behavior = new NumberFieldBehavior(); var field = NewField(); field.Behavior = behavior; behavior.ScrubDistancePerStep = 4; ScrubTo(field, 58); CheckEqual(2, field.Value); } } public class FormatTests : TestFixture { public void Unset_ShowsGridDecimalPlacesDigits() { var field = NewField(new NumberGrid { Max = 1, Resolution = 0.001m }); field.Value = 0.5m; CheckEqual("0.500", field.DisplayText); } public void Custom_ReplacesTheDefaultAndReformatsTheLabel() { var field = NewField(); field.Value = 7; field.Format = (value, culture) => value + " MW"; CheckEqual("7 MW", field.DisplayText); } public void Custom_IsHandedBehaviorsCulture() { CultureInfo french = CultureInfo.GetCultureInfo("fr-FR"); var field = NewField(); field.Behavior = new NumberFieldBehavior { Culture = french }; CultureInfo handed = null; field.Format = (value, culture) => { handed = culture; return ""; }; CheckEqual(french, handed); } public void ClearedBackToNull_RestoresTheDefault() { var field = NewField(); field.Value = 7; field.Format = (value, culture) => "custom"; field.Format = null; CheckEqual("7", field.DisplayText); } } public class NumberFieldMaterialTests : TestFixture { static readonly Material Background = new Material { DebugName = "background" }; static readonly TextMaterial LabelTemplate = TextMaterialTests.UnmarginedText("template"); static readonly ButtonMaterial Increment = new ButtonMaterial(new Material { DebugName = "increment" }); static readonly ButtonMaterial Decrement = new ButtonMaterial(new Material { DebugName = "decrement" }); static readonly NumberFieldMaterial FullSet = new NumberFieldMaterial { Background = Background, Label = LabelTemplate, IncrementButton = Increment, DecrementButton = Decrement, }; public void Setter_AssignsTheBackgroundAndButtonsDirectly() { var field = NewUnstyledField(new NumberField.Data()); field.NumberFieldMaterial = FullSet; CheckEqual(Background, field.Material); CheckEqual(Increment, field.IncrementButton.ButtonMaterial); CheckEqual(Decrement, field.DecrementButton.ButtonMaterial); } public void Setter_GivesTheLabelACopyOfTheTemplateShowingTheValue() { var field = NewUnstyledField(new NumberField.Data { Grid = ZeroToHundred, Value = 42 }); field.NumberFieldMaterial = FullSet; var labelMaterial = (TextMaterial)field.Label.Material; CheckNotEqual(LabelTemplate, labelMaterial); CheckEqual("42", labelMaterial.Text.ToString()); } public void ValueChanges_ReachTheCopyButNotTheTemplate() { var field = NewUnstyledField(new NumberField.Data { Grid = ZeroToHundred }); field.NumberFieldMaterial = FullSet; field.Value = 42; CheckEqual("42", ((TextMaterial)field.Label.Material).Text.ToString()); CheckEqual("template", LabelTemplate.Text.ToString()); } public void BackgroundWithABorder_InsetsTheChildrenImmediately() { var field = NewField(); NumberFieldMaterial material = field.NumberFieldMaterial; material.Background = new Material { BorderSize = new Rect.BorderSize(5) }; field.NumberFieldMaterial = material; CheckEqual(Rect.FromPositionAndSize(Vector.Zero, new Vector(70, 30)), field.Label.Bounds); } public void NoLabelTemplate_LeavesTheLabelWithoutAMaterial() { var field = NewUnstyledField(new NumberField.Data()); field.NumberFieldMaterial = new NumberFieldMaterial(); CheckNull(field.Label.Material); } public void Getter_RoundTripsWhatTheSetterAssigned() { var field = NewUnstyledField(new NumberField.Data()); field.NumberFieldMaterial = FullSet; CheckEqual(FullSet, field.NumberFieldMaterial); } } public class MaterialTests : TestFixture { public void BackgroundBorder_InsetsTheChildrenImmediately() { var field = NewField(); field.Material = new Material { BorderSize = new Rect.BorderSize(5) }; CheckEqual(Rect.FromPositionAndSize(Vector.Zero, new Vector(70, 30)), field.Label.Bounds); } } public class LayoutTests : TestFixture { public void LabelFillsWhatTheButtonColumnLeaves() { CheckEqual(Rect.FromPositionAndSize(Vector.Zero, new Vector(80, 40)), NewField().Label.Bounds); } public void IncrementButton_TakesTheTopHalfOfTheColumn() { CheckEqual(Rect.FromPositionAndSize(new Vector(80, 0), new Vector(20, 20)), NewField().IncrementButton.Bounds); } public void DecrementButton_TakesTheBottomHalfOfTheColumn() { CheckEqual(Rect.FromPositionAndSize(new Vector(80, 20), new Vector(20, 20)), NewField().DecrementButton.Bounds); } public void ColumnWiderThanTheField_IsClampedToIt() { var field = NewField(); field.Bounds = Rect.FromPositionAndSize(Vector.Zero, new Vector(15, 40)); CheckEqual(15, field.IncrementButton.Bounds.Width); CheckEqual(0, field.Label.Bounds.Width); } public void ChangingAButtonMaterial_RelaysOutImmediately() { var field = NewField(); field.DecrementButton.ButtonMaterial = Glass.UnitTests.NumberFieldMaterialTests.FixedSizeButton(30, 20); CheckEqual(70, field.Label.Bounds.Width); } } public class PreferredBoundsSizeTests : TestFixture { /// /// NewField's 20px button column, with a label of 3x50px monospace glyphs. /// public static NumberField NewFieldWithALabel(NumberGrid grid) { var field = NewField(grid); NumberFieldMaterial material = field.NumberFieldMaterial; material.Label = TextMaterialTests.UnmarginedText("", glyphWidth: 3, glyphHeight: 50); field.NumberFieldMaterial = material; return field; } public void MinWider_FitsTheLabelAtMin() { var field = NewFieldWithALabel(new NumberGrid { Min = -100, Max = 5 }); // "-100" is four glyphs beside the button column, and the glyphs outgrow the column's 40px. CheckEqual(new Vector(32, 50), field.PreferredBoundsSize); } public void MaxWider_FitsTheLabelAtMax() { var field = NewFieldWithALabel(new NumberGrid { Min = 5, Max = 10000 }); CheckEqual(new Vector(35, 50), field.PreferredBoundsSize); } public void CustomFormat_IsWhatGetsMeasured() { var field = NewFieldWithALabel(new NumberGrid { Max = 5 }); field.Format = (value, culture) => value + " MW"; CheckEqual(new Vector(32, 50), field.PreferredBoundsSize); } public void NoLabelTemplate_IsJustTheButtonColumn() { CheckEqual(new Vector(20, 40), NewField().PreferredBoundsSize); } } public class StepByTests : TestFixture { public void MovesAlongTheStepLattice() { var field = NewField(new NumberGrid { Max = 100, Step = 5 }); field.Value = 3; field.StepBy(2); CheckEqual(10, field.Value); } public void ClampsAtTheEnds() { var field = NewField(); field.StepBy(-3); CheckEqual(0, field.Value); } } public class IncrementButtonClickTests : TestFixture { public void StepsUpOnce() { var field = NewField(new NumberGrid { Max = 100, Step = 5 }); field.IncrementButton.Click(new MouseState(), MouseButtonFlags.Left); CheckEqual(5, field.Value); } } public class DecrementButtonClickTests : TestFixture { public void StepsDownOnce() { var field = NewField(new NumberGrid { Max = 100, Step = 5 }); field.Value = 50; field.DecrementButton.Click(new MouseState(), MouseButtonFlags.Left); CheckEqual(45, field.Value); } } public class EnabledTests : TestFixture { public void SettingFalse_DisablesBothButtons() { var field = NewField(); field.Value = 50; field.Enabled = false; CheckFalse(field.IncrementButton.Enabled); CheckFalse(field.DecrementButton.Enabled); } public void SettingTrueAgain_KeepsAButtonAtItsBoundDisabled() { var field = NewField(); field.Enabled = false; field.Enabled = true; Check(field.IncrementButton.Enabled); CheckFalse(field.DecrementButton.Enabled); } } public class MouseWheelTests : TestFixture { public void AwayFromTheUser_StepsUp() { var field = NewField(); CheckTrue(field.MouseWheel(new MouseState(), notches: 2)); CheckEqual(2, field.Value); } public void TowardTheUser_StepsDown() { var field = NewField(); field.Value = 50; field.MouseWheel(new MouseState(), notches: -1); CheckEqual(49, field.Value); } public void PartialNotches_AddUpToAStep() { var field = NewField(); field.MouseWheel(new MouseState(), notches: 0.5); CheckEqual(0, field.Value); field.MouseWheel(new MouseState(), notches: 0.5); CheckEqual(1, field.Value); } public void AlreadyAtTheLimit_StillClaimsTheWheel() { var field = NewField(); CheckTrue(field.MouseWheel(new MouseState(), notches: -1)); } public void SingleValueGrid_DeclinesIt() { var field = NewField(new NumberGrid { Min = 5, Max = 5 }); CheckFalse(field.MouseWheel(new MouseState(), notches: 1)); } public void Disabled_DeclinesItAndStaysPut() { var field = NewField(); field.Enabled = false; CheckFalse(field.MouseWheel(new MouseState(), notches: 1)); CheckEqual(0, field.Value); } } public class MouseDownTests : TestFixture { public void LeftOnTheLabel_IsClaimed() { CheckTrue(NewField().MouseDown(OnTheLabel, MouseButtonFlags.Left)); } public void JustInsideTheLabelsRightEdge_IsClaimed() { CheckTrue(NewField().MouseDown(At(85, 30), MouseButtonFlags.Left)); } public void OnTheButtonColumn_IsDeclined() { CheckFalse(NewField().MouseDown(OnTheButtons, MouseButtonFlags.Left)); } public void RightOnTheLabel_IsDeclined() { CheckFalse(NewField().MouseDown(OnTheLabel, MouseButtonFlags.Right)); } public void Disabled_IsDeclined() { var field = NewField(); field.Enabled = false; CheckFalse(field.MouseDown(OnTheLabel, MouseButtonFlags.Left)); } public void SingleValueGrid_IsDeclined() { var field = NewField(new NumberGrid { Min = 5, Max = 5 }); CheckFalse(field.MouseDown(OnTheLabel, MouseButtonFlags.Left)); } } public class MouseMoveTests : TestFixture { public void OneScrubDistanceRight_StepsUpOnce() { var field = NewField(); ScrubTo(field, 58); CheckEqual(1, field.Value); } public void UnderHalfAScrubDistance_LeavesTheValue() { var field = NewField(); ScrubTo(field, 53); CheckEqual(0, field.Value); } public void Leftward_StepsDown() { var field = NewField(); field.Value = 50; ScrubTo(field, 26); CheckEqual(47, field.Value); } public void DraggingBack_ReturnsExactlyToTheStart() { var field = NewField(); field.Value = 50; ScrubTo(field, 90); field.MouseMove(At(50, 30)); CheckEqual(50, field.Value); } public void Upward_StepsUp() { var field = NewField(); field.Value = 50; field.MouseDown(OnTheLabel, MouseButtonFlags.Left); field.MouseMove(At(50, 22)); CheckEqual(51, field.Value); } public void Downward_StepsDown() { var field = NewField(); field.Value = 50; field.MouseDown(OnTheLabel, MouseButtonFlags.Left); field.MouseMove(At(50, 54)); CheckEqual(47, field.Value); } public void DiagonalUpAndRight_CountsBothAxes() { var field = NewField(); field.Value = 50; field.MouseDown(OnTheLabel, MouseButtonFlags.Left); field.MouseMove(At(58, 22)); CheckEqual(52, field.Value); } public void DiagonalDownAndRight_CancelsOut() { var field = NewField(); field.Value = 50; field.MouseDown(OnTheLabel, MouseButtonFlags.Left); field.MouseMove(At(90, 70)); CheckEqual(50, field.Value); } public void DraggingBackVertically_ReturnsExactlyToTheStart() { var field = NewField(); field.Value = 50; field.MouseDown(OnTheLabel, MouseButtonFlags.Left); field.MouseMove(At(50, -10)); field.MouseMove(At(50, 30)); CheckEqual(50, field.Value); } public void PushingPastMaxUpward_TurningBackDownRespondsAtOnce() { var field = NewField(new NumberGrid { Max = 3 }); field.MouseDown(OnTheLabel, MouseButtonFlags.Left); field.MouseMove(At(50, -70)); field.MouseMove(At(50, -62)); CheckEqual(2, field.Value); } public void ScrubDistancePerStep_SetsTheScale() { var field = NewField(); field.Behavior = new NumberFieldBehavior { ScrubDistancePerStep = 4 }; ScrubTo(field, 58); CheckEqual(2, field.Value); } public void PushingPastMax_TurningBackRespondsAtOnce() { var field = NewField(new NumberGrid { Max = 3 }); ScrubTo(field, 150); field.MouseMove(At(142, 30)); CheckEqual(2, field.Value); } public void PushingPastMin_TurningBackRespondsAtOnce() { var field = NewField(new NumberGrid { Max = 3 }); field.Value = 3; ScrubTo(field, -50); field.MouseMove(At(-42, 30)); CheckEqual(1, field.Value); } public void WithoutAScrub_IsDeclined() { CheckFalse(NewField().MouseMove(OnTheLabel)); } } public class MouseUpTests : TestFixture { public void EndsTheScrub() { var field = NewField(); ScrubTo(field, 58); CheckTrue(field.MouseUp(At(58, 30), MouseButtonFlags.Left)); field.MouseMove(At(90, 30)); CheckEqual(1, field.Value); } public void RightReleaseMidScrub_KeepsScrubbing() { var field = NewField(); ScrubTo(field, 58); field.MouseUp(At(58, 30), MouseButtonFlags.Right); field.MouseMove(At(66, 30)); CheckEqual(2, field.Value); } public void WithoutAScrub_IsDeclined() { CheckFalse(NewField().MouseUp(OnTheLabel, MouseButtonFlags.Left)); } } public class CancelMouseInputTests : TestFixture { public void MidScrub_PutsTheValueBackAndEndsTheScrub() { var field = NewField(); field.Value = 50; ScrubTo(field, 90); field.CancelMouseInput(); field.MouseMove(At(10, 30)); CheckEqual(50, field.Value); } public void GridNarrowedMidScrub_PutsTheValueBackWithinTheNewGrid() { var field = NewField(); field.Value = 50; ScrubTo(field, 90); field.Grid = new NumberGrid { Max = 10 }; field.CancelMouseInput(); CheckEqual(10, field.Value); } public void WithoutAScrub_LeavesTheValue() { var field = NewField(); field.Value = 50; field.CancelMouseInput(); CheckEqual(50, field.Value); } } public class CursorAtTests : TestFixture { private sealed class SentinelCursor : Cursor { } public void OverTheLabel_IsTheScrubCursor() { var cursor = new SentinelCursor(); var field = NewField(); field.ScrubCursor = cursor; CheckEqual(cursor, field.CursorAt(OnTheLabel)); } public void OverTheButtons_IsNull() { var field = NewField(); field.ScrubCursor = new SentinelCursor(); CheckNull(field.CursorAt(OnTheButtons)); } public void MidScrubOffTheLabel_StaysTheScrubCursor() { var cursor = new SentinelCursor(); var field = NewField(); field.ScrubCursor = cursor; ScrubTo(field, 500); CheckEqual(cursor, field.CursorAt(At(500, 30))); } public void Disabled_IsNull() { var field = NewField(); field.ScrubCursor = new SentinelCursor(); field.Enabled = false; CheckNull(field.CursorAt(OnTheLabel)); } public void SingleValueGrid_IsNull() { var field = NewField(new NumberGrid { Min = 5, Max = 5 }); field.ScrubCursor = new SentinelCursor(); CheckNull(field.CursorAt(OnTheLabel)); } } } }