Skip to content

Commit 61f5c59

Browse files
test(ui): add comprehensive tests for UI Editor components (#609)
* test(ui): add comprehensive tests for UI Editor components Add 22 new test files covering all UI Editor components: - Button: JButton, JButtonGroup, JIconButton, JToggleButton - Form: JDropdown, JFormField, JObjectField, JTextField, JToggle - Layout: JCard, JRow, JSection, JStack - Feedback: JLogView, JProgressBar, JStatusBar - Navigation: JBreadcrumb - Theming: JTheme, Tokens - Utilities: EnumHelpers, StyleSheetManager - Base: JComponent Coverage improved from 14% to 61.7% for UI.Editor package. Uses reflection for button click testing with graceful fallback. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net> * chore: add CodeCoverage folder to gitignore Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net> * perf(ui): optimize MessageBox for zero GC allocation - Replace HashSet with List (no need for hash-based uniqueness) - Use ArrayPool for CloseAll() instead of allocating new array - Remove LINQ dependency (use direct list indexing) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net> --------- Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 115f1ee commit 61f5c59

57 files changed

Lines changed: 7561 additions & 11 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ UnityProject/Build/*
2929
.worktrees/
3030
.security-key
3131
logs/security/
32+
/UnityProject/CodeCoverage

UnityProject/Packages/com.jasonxudeveloper.jengine.ui/Runtime/MessageBox.cs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
// THE SOFTWARE.
2525

2626
using System;
27+
using System.Buffers;
2728
using System.Collections.Generic;
28-
using System.Linq;
2929
using System.Runtime.CompilerServices;
3030
using Cysharp.Threading.Tasks;
3131
using TMPro;
@@ -106,7 +106,7 @@ private static GameObject Prefab
106106

107107
private static GameObject _prefab;
108108

109-
private static readonly HashSet<MessageBox> ActiveMessageBoxes = new();
109+
private static readonly List<MessageBox> ActiveMessageBoxes = new();
110110
private static readonly Stack<MessageBox> PooledMessageBoxes = new();
111111

112112
private const int MaxPoolSize = 10;
@@ -145,7 +145,7 @@ internal static bool TestSimulateButtonClick(bool clickOk)
145145
if (ActiveMessageBoxes.Count == 0) return false;
146146

147147
// Get the first message box (any will do for testing)
148-
var target = ActiveMessageBoxes.First();
148+
var target = ActiveMessageBoxes[0];
149149
target.HandleEvent(clickOk);
150150
return true;
151151
}
@@ -158,7 +158,7 @@ internal static (bool okVisible, bool noVisible)? TestGetButtonVisibility()
158158
{
159159
if (ActiveMessageBoxes.Count == 0) return null;
160160

161-
var target = ActiveMessageBoxes.First();
161+
var target = ActiveMessageBoxes[0];
162162
if (target._buttonOk == null || target._buttonNo == null)
163163
return null;
164164

@@ -173,7 +173,7 @@ internal static (string title, string content, string okText, string noText)? Te
173173
{
174174
if (ActiveMessageBoxes.Count == 0) return null;
175175

176-
var target = ActiveMessageBoxes.First();
176+
var target = ActiveMessageBoxes[0];
177177
return (
178178
target._title?.text,
179179
target._content?.text,
@@ -253,13 +253,22 @@ public static void Dispose()
253253

254254
public static void CloseAll()
255255
{
256-
// Create a copy to avoid modification during iteration
257-
var activeBoxes = new MessageBox[ActiveMessageBoxes.Count];
258-
ActiveMessageBoxes.CopyTo(activeBoxes);
256+
var count = ActiveMessageBoxes.Count;
257+
if (count == 0) return;
259258

260-
foreach (var messageBox in activeBoxes)
259+
// Use ArrayPool to avoid allocation
260+
var activeBoxes = ArrayPool<MessageBox>.Shared.Rent(count);
261+
try
262+
{
263+
ActiveMessageBoxes.CopyTo(activeBoxes, 0);
264+
for (var i = 0; i < count; i++)
265+
{
266+
activeBoxes[i].CancelAndClose();
267+
}
268+
}
269+
finally
261270
{
262-
messageBox.CancelAndClose();
271+
ArrayPool<MessageBox>.Shared.Return(activeBoxes, clearArray: true);
263272
}
264273
}
265274

UnityProject/Packages/com.jasonxudeveloper.jengine.ui/Tests/Editor/Components.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

UnityProject/Packages/com.jasonxudeveloper.jengine.ui/Tests/Editor/Components/Base.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
// JComponentTests.cs
2+
// EditMode unit tests for JComponent base class
3+
4+
using NUnit.Framework;
5+
using UnityEngine.UIElements;
6+
using JEngine.UI.Editor.Components;
7+
using JEngine.UI.Editor.Components.Layout;
8+
9+
namespace JEngine.UI.Tests.Editor.Components.Base
10+
{
11+
[TestFixture]
12+
public class JComponentTests
13+
{
14+
// Using JStack as concrete implementation of JComponent
15+
private JStack _component;
16+
17+
[SetUp]
18+
public void SetUp()
19+
{
20+
_component = new JStack();
21+
}
22+
23+
#region Constructor Tests
24+
25+
[Test]
26+
public void Constructor_WithBaseClassName_AddsClass()
27+
{
28+
// JStack inherits from JComponent with "j-stack" as base class
29+
Assert.IsTrue(_component.ClassListContains("j-stack"));
30+
}
31+
32+
#endregion
33+
34+
#region WithClass Tests
35+
36+
[Test]
37+
public void WithClass_AddsClassName()
38+
{
39+
_component.WithClass("custom-class");
40+
Assert.IsTrue(_component.ClassListContains("custom-class"));
41+
}
42+
43+
[Test]
44+
public void WithClass_ReturnsComponentForChaining()
45+
{
46+
var result = _component.WithClass("test");
47+
Assert.AreSame(_component, result);
48+
}
49+
50+
[Test]
51+
public void WithClass_CanAddMultipleClasses()
52+
{
53+
_component.WithClass("class1");
54+
_component.WithClass("class2");
55+
56+
Assert.IsTrue(_component.ClassListContains("class1"));
57+
Assert.IsTrue(_component.ClassListContains("class2"));
58+
}
59+
60+
#endregion
61+
62+
#region WithName Tests
63+
64+
[Test]
65+
public void WithName_SetsElementName()
66+
{
67+
_component.WithName("test-element");
68+
Assert.AreEqual("test-element", _component.name);
69+
}
70+
71+
[Test]
72+
public void WithName_ReturnsComponentForChaining()
73+
{
74+
var result = _component.WithName("test");
75+
Assert.AreSame(_component, result);
76+
}
77+
78+
[Test]
79+
public void WithName_CanOverwritePreviousName()
80+
{
81+
_component.WithName("first");
82+
_component.WithName("second");
83+
Assert.AreEqual("second", _component.name);
84+
}
85+
86+
#endregion
87+
88+
#region Add Tests
89+
90+
[Test]
91+
public void Add_SingleChild_AddsToComponent()
92+
{
93+
var child = new Label("test");
94+
_component.Add(child);
95+
96+
Assert.AreEqual(1, _component.childCount);
97+
Assert.AreSame(child, _component[0]);
98+
}
99+
100+
[Test]
101+
public void Add_MultipleChildren_AddsAllToComponent()
102+
{
103+
var child1 = new Label("test1");
104+
var child2 = new Label("test2");
105+
var child3 = new Label("test3");
106+
107+
_component.Add(child1, child2, child3);
108+
109+
Assert.AreEqual(3, _component.childCount);
110+
}
111+
112+
[Test]
113+
public void Add_NullChild_IsIgnored()
114+
{
115+
_component.Add((VisualElement)null);
116+
Assert.AreEqual(0, _component.childCount);
117+
}
118+
119+
[Test]
120+
public void Add_MixedNullAndValid_AddsOnlyValidChildren()
121+
{
122+
var child1 = new Label("test1");
123+
var child2 = new Label("test2");
124+
125+
_component.Add(child1, null, child2);
126+
127+
Assert.AreEqual(2, _component.childCount);
128+
}
129+
130+
[Test]
131+
public void Add_ReturnsComponentForChaining()
132+
{
133+
var result = _component.Add(new Label());
134+
Assert.AreSame(_component, result);
135+
}
136+
137+
#endregion
138+
139+
#region WithFlexGrow Tests
140+
141+
[Test]
142+
public void WithFlexGrow_SetsFlexGrowValue()
143+
{
144+
_component.WithFlexGrow(2f);
145+
Assert.AreEqual(2f, _component.style.flexGrow.value);
146+
}
147+
148+
[Test]
149+
public void WithFlexGrow_ReturnsComponentForChaining()
150+
{
151+
var result = _component.WithFlexGrow(1f);
152+
Assert.AreSame(_component, result);
153+
}
154+
155+
[Test]
156+
public void WithFlexGrow_ZeroValue_SetsToZero()
157+
{
158+
_component.WithFlexGrow(0f);
159+
Assert.AreEqual(0f, _component.style.flexGrow.value);
160+
}
161+
162+
#endregion
163+
164+
#region WithFlexShrink Tests
165+
166+
[Test]
167+
public void WithFlexShrink_SetsFlexShrinkValue()
168+
{
169+
_component.WithFlexShrink(2f);
170+
Assert.AreEqual(2f, _component.style.flexShrink.value);
171+
}
172+
173+
[Test]
174+
public void WithFlexShrink_ReturnsComponentForChaining()
175+
{
176+
var result = _component.WithFlexShrink(1f);
177+
Assert.AreSame(_component, result);
178+
}
179+
180+
#endregion
181+
182+
#region WithMargin Tests
183+
184+
[Test]
185+
public void WithMargin_SetsAllMargins()
186+
{
187+
_component.WithMargin(10f);
188+
189+
Assert.AreEqual(10f, _component.style.marginTop.value.value);
190+
Assert.AreEqual(10f, _component.style.marginRight.value.value);
191+
Assert.AreEqual(10f, _component.style.marginBottom.value.value);
192+
Assert.AreEqual(10f, _component.style.marginLeft.value.value);
193+
}
194+
195+
[Test]
196+
public void WithMargin_ReturnsComponentForChaining()
197+
{
198+
var result = _component.WithMargin(5f);
199+
Assert.AreSame(_component, result);
200+
}
201+
202+
#endregion
203+
204+
#region WithPadding Tests
205+
206+
[Test]
207+
public void WithPadding_SetsAllPadding()
208+
{
209+
_component.WithPadding(10f);
210+
211+
Assert.AreEqual(10f, _component.style.paddingTop.value.value);
212+
Assert.AreEqual(10f, _component.style.paddingRight.value.value);
213+
Assert.AreEqual(10f, _component.style.paddingBottom.value.value);
214+
Assert.AreEqual(10f, _component.style.paddingLeft.value.value);
215+
}
216+
217+
[Test]
218+
public void WithPadding_ReturnsComponentForChaining()
219+
{
220+
var result = _component.WithPadding(5f);
221+
Assert.AreSame(_component, result);
222+
}
223+
224+
#endregion
225+
226+
#region WithVisibility Tests
227+
228+
[Test]
229+
public void WithVisibility_True_SetsDisplayFlex()
230+
{
231+
_component.WithVisibility(true);
232+
Assert.AreEqual(DisplayStyle.Flex, _component.style.display.value);
233+
}
234+
235+
[Test]
236+
public void WithVisibility_False_SetsDisplayNone()
237+
{
238+
_component.WithVisibility(false);
239+
Assert.AreEqual(DisplayStyle.None, _component.style.display.value);
240+
}
241+
242+
[Test]
243+
public void WithVisibility_ReturnsComponentForChaining()
244+
{
245+
var result = _component.WithVisibility(true);
246+
Assert.AreSame(_component, result);
247+
}
248+
249+
[Test]
250+
public void WithVisibility_CanToggle()
251+
{
252+
_component.WithVisibility(false);
253+
Assert.AreEqual(DisplayStyle.None, _component.style.display.value);
254+
255+
_component.WithVisibility(true);
256+
Assert.AreEqual(DisplayStyle.Flex, _component.style.display.value);
257+
}
258+
259+
#endregion
260+
261+
#region Chaining Tests
262+
263+
[Test]
264+
public void FluentApi_CanChainMultipleMethods()
265+
{
266+
_component
267+
.WithName("test")
268+
.WithClass("custom")
269+
.WithMargin(5f)
270+
.WithPadding(10f)
271+
.WithFlexGrow(1f)
272+
.WithVisibility(true);
273+
274+
Assert.AreEqual("test", _component.name);
275+
Assert.IsTrue(_component.ClassListContains("custom"));
276+
Assert.AreEqual(5f, _component.style.marginTop.value.value);
277+
Assert.AreEqual(10f, _component.style.paddingTop.value.value);
278+
Assert.AreEqual(1f, _component.style.flexGrow.value);
279+
Assert.AreEqual(DisplayStyle.Flex, _component.style.display.value);
280+
}
281+
282+
#endregion
283+
}
284+
}

UnityProject/Packages/com.jasonxudeveloper.jengine.ui/Tests/Editor/Components/Base/JComponentTests.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)