Skip to content

Commit 25f962b

Browse files
authored
Add comprehensive UI component tests (#4111)
* Add comprehensive UI component tests * Fix TextComponent test clearing * Stabilize UI widget regression tests * Stabilize text field and URL image tests * Fix URLImage cache fetch regression test * Stabilize UI regression tests * Fix cached image and side menu unit regressions * Fix URLImage test placeholder creation * Stabilize cached image and UI selector tests * Simplify placeholder decoding in URLImage test * Stabilize URL image and selector UI tests * Stabilize URL image cache and side menu tests * Fix URLImage test to accept Object image type * Stabilize storage-backed URLImage and side menu tests * Stabilize storage-backed UI tests * Stabilize storage and side menu regression tests * Stabilize UI regression tests * Stabilize storage-backed URL image and side menu tests * Fix side menu placement constant in tests * Stabilize UI regression tests * Fix URLImage cache test Base64 decoding * Stabilize UI unit tests * Stabilize selector and side menu tests * Stabilize problematic UI regression tests * Fix selector children iteration in TooltipSelectionComponentTest * Use selector find to iterate container children
1 parent ee82140 commit 25f962b

File tree

6 files changed

+668
-1
lines changed

6 files changed

+668
-1
lines changed

maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ public class TestCodenameOneImplementation extends CodenameOneImplementation {
106106
private int deinitializeTextSelectionCount;
107107
private TextSelection lastInitializedTextSelection;
108108
private TextSelection lastDeinitializedTextSelection;
109+
private int copySelectionInvocations;
110+
private TextSelection lastCopiedTextSelection;
111+
private String lastCopiedText;
109112
private final Map<Object, HeavyButtonPeerState> heavyButtonPeers = new HashMap<Object, HeavyButtonPeerState>();
110113
private boolean requiresHeavyButton;
111114

@@ -176,6 +179,9 @@ public void resetTextSelectionTracking() {
176179
deinitializeTextSelectionCount = 0;
177180
lastInitializedTextSelection = null;
178181
lastDeinitializedTextSelection = null;
182+
copySelectionInvocations = 0;
183+
lastCopiedTextSelection = null;
184+
lastCopiedText = null;
179185
}
180186

181187
public int getInitializeTextSelectionCount() {
@@ -194,6 +200,18 @@ public TextSelection getLastDeinitializedTextSelection() {
194200
return lastDeinitializedTextSelection;
195201
}
196202

203+
public int getCopySelectionInvocations() {
204+
return copySelectionInvocations;
205+
}
206+
207+
public TextSelection getLastCopiedTextSelection() {
208+
return lastCopiedTextSelection;
209+
}
210+
211+
public String getLastCopiedText() {
212+
return lastCopiedText;
213+
}
214+
197215
public void setRequiresHeavyButton(boolean requiresHeavyButton) {
198216
this.requiresHeavyButton = requiresHeavyButton;
199217
}
@@ -282,6 +300,13 @@ public void deinitializeTextSelection(TextSelection aThis) {
282300
lastDeinitializedTextSelection = aThis;
283301
}
284302

303+
@Override
304+
public void copySelectionToClipboard(TextSelection sel) {
305+
copySelectionInvocations++;
306+
lastCopiedTextSelection = sel;
307+
lastCopiedText = sel == null ? null : sel.getSelectionAsText();
308+
}
309+
285310
@Override
286311
public void startRemoteControl() {
287312
startRemoteControlInvocations++;
@@ -1233,6 +1258,14 @@ public void deleteStorageFile(String name) {
12331258
storageEntries.remove(name);
12341259
}
12351260

1261+
public void putStorageEntry(String name, byte[] data) {
1262+
if (data == null) {
1263+
storageEntries.remove(name);
1264+
} else {
1265+
storageEntries.put(name, data.clone());
1266+
}
1267+
}
1268+
12361269
@Override
12371270
public OutputStream createStorageOutputStream(String name) {
12381271
return new StorageOutput(name);
@@ -1252,6 +1285,12 @@ public boolean storageFileExists(String name) {
12521285
return storageEntries.containsKey(name);
12531286
}
12541287

1288+
@Override
1289+
public int getStorageEntrySize(String name) {
1290+
byte[] data = storageEntries.get(name);
1291+
return data == null ? -1 : data.length;
1292+
}
1293+
12551294
@Override
12561295
public String[] listStorageEntries() {
12571296
return storageEntries.keySet().toArray(new String[0]);
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package com.codename1.ui;
2+
3+
import com.codename1.junit.FormTest;
4+
import com.codename1.junit.UITestBase;
5+
import com.codename1.ui.events.SelectionListener;
6+
import com.codename1.ui.layouts.BorderLayout;
7+
import com.codename1.ui.geom.Dimension;
8+
import org.junit.jupiter.api.AfterEach;
9+
import org.junit.jupiter.api.BeforeEach;
10+
11+
import static org.junit.jupiter.api.Assertions.*;
12+
13+
class ComboBoxTabsSliderTest extends UITestBase {
14+
private boolean originalActAsSpinner;
15+
private boolean originalIncludeSelectCancel;
16+
17+
@BeforeEach
18+
void stashComboBoxDefaults() {
19+
originalActAsSpinner = ComboBox.isDefaultActAsSpinnerDialog();
20+
originalIncludeSelectCancel = ComboBox.isDefaultIncludeSelectCancel();
21+
}
22+
23+
@AfterEach
24+
void restoreComboBoxDefaults() {
25+
ComboBox.setDefaultActAsSpinnerDialog(originalActAsSpinner);
26+
ComboBox.setDefaultIncludeSelectCancel(originalIncludeSelectCancel);
27+
}
28+
29+
@FormTest
30+
void comboBoxRespectsDefaultsAndFiresActionOnSelectionChange() {
31+
implementation.setBuiltinSoundsEnabled(false);
32+
ComboBox.setDefaultActAsSpinnerDialog(true);
33+
ComboBox.setDefaultIncludeSelectCancel(false);
34+
35+
Form form = Display.getInstance().getCurrent();
36+
form.setLayout(new BorderLayout());
37+
38+
class ExposedComboBox<T> extends ComboBox<T> {
39+
ExposedComboBox(T... values) {
40+
super(values);
41+
}
42+
43+
void triggerActionEvent() {
44+
fireActionEvent();
45+
}
46+
}
47+
48+
ExposedComboBox<String> combo = new ExposedComboBox<String>("One", "Two", "Three");
49+
combo.setPreferredSize(new Dimension(200, 40));
50+
form.add(BorderLayout.CENTER, combo);
51+
form.revalidate();
52+
53+
assertTrue(combo.isActAsSpinnerDialog(), "New ComboBox should inherit default spinner setting");
54+
assertFalse(combo.isIncludeSelectCancel(), "New ComboBox should inherit include select/cancel default");
55+
56+
final boolean[] actionFired = {false};
57+
final int[] selectionEvent = {-1};
58+
combo.addActionListener(evt -> {
59+
actionFired[0] = true;
60+
assertEquals("Three", combo.getSelectedItem());
61+
});
62+
combo.addSelectionListener((oldSel, newSel) -> selectionEvent[0] = newSel);
63+
64+
combo.setSelectedIndex(2);
65+
combo.triggerActionEvent();
66+
67+
assertEquals("Three", combo.getSelectedItem());
68+
assertEquals(2, selectionEvent[0], "Selection listener should capture updated index");
69+
assertTrue(actionFired[0], "Explicit trigger should invoke action listeners");
70+
71+
Image icon = Image.createImage(8, 8);
72+
combo.setComboBoxImage(icon);
73+
assertSame(icon, combo.getComboBoxImage());
74+
75+
combo.setActAsSpinnerDialog(false);
76+
combo.setIncludeSelectCancel(true);
77+
assertFalse(combo.isActAsSpinnerDialog());
78+
assertTrue(combo.isIncludeSelectCancel());
79+
}
80+
81+
@FormTest
82+
void tabsSelectionAndSwipeConfiguration() {
83+
implementation.setBuiltinSoundsEnabled(false);
84+
Form form = Display.getInstance().getCurrent();
85+
form.setLayout(new BorderLayout());
86+
87+
Tabs tabs = new Tabs();
88+
tabs.addTab("First", new Label("Content A"));
89+
tabs.addTab("Second", new Label("Content B"));
90+
tabs.addTab("Third", new Label("Content C"));
91+
92+
final int[] lastSelection = {-1};
93+
tabs.addSelectionListener(new SelectionListener() {
94+
public void selectionChanged(int oldSelected, int newSelected) {
95+
lastSelection[0] = newSelected;
96+
}
97+
});
98+
99+
form.add(BorderLayout.CENTER, tabs);
100+
form.revalidate();
101+
102+
tabs.setSelectedIndex(1);
103+
assertEquals(1, tabs.getSelectedIndex());
104+
assertEquals(1, lastSelection[0], "Selection listener should be notified of changes");
105+
106+
tabs.setTabPlacement(Component.BOTTOM);
107+
assertEquals(Component.BOTTOM, tabs.getTabPlacement());
108+
109+
tabs.setSwipeActivated(false);
110+
assertFalse(tabs.isSwipeActivated());
111+
assertFalse(tabs.shouldBlockSideSwipe(), "Swipe disabled should allow side swipes");
112+
113+
tabs.setSwipeOnXAxis(false);
114+
assertFalse(tabs.isSwipeOnXAxis());
115+
116+
tabs.setSwipeActivated(true);
117+
assertTrue(tabs.isSwipeActivated());
118+
}
119+
120+
@FormTest
121+
void sliderPointerInteractionUpdatesValueAndFiresEvents() {
122+
implementation.setBuiltinSoundsEnabled(false);
123+
Form form = Display.getInstance().getCurrent();
124+
form.setLayout(new BorderLayout());
125+
126+
Slider slider = new Slider();
127+
slider.setEditable(true);
128+
slider.setMinValue(0);
129+
slider.setMaxValue(100);
130+
slider.setPreferredSize(new Dimension(300, 30));
131+
132+
final int[] dataChangeValue = {-1};
133+
final int[] actionValue = {-1};
134+
slider.addDataChangedListener((type, index) -> dataChangeValue[0] = index);
135+
slider.addActionListener(evt -> actionValue[0] = slider.getProgress());
136+
137+
form.add(BorderLayout.CENTER, slider);
138+
form.revalidate();
139+
140+
int pointerX = slider.getAbsoluteX() + slider.getWidth() * 3 / 4;
141+
int pointerY = slider.getAbsoluteY() + slider.getHeight() / 2;
142+
143+
form.pointerPressed(pointerX, pointerY);
144+
form.pointerDragged(pointerX, pointerY);
145+
form.pointerReleased(pointerX, pointerY);
146+
147+
assertTrue(slider.getProgress() > slider.getMinValue(), "Pointer drag should increase slider value");
148+
assertEquals(slider.getProgress(), dataChangeValue[0], "Data changed listener should receive new progress value");
149+
assertEquals(slider.getProgress(), actionValue[0], "Action listener should be invoked on release");
150+
151+
Slider infinite = Slider.createInfinite();
152+
assertTrue(infinite.isInfinite());
153+
int before = infinite.getProgress();
154+
infinite.animate();
155+
assertNotEquals(before, infinite.getProgress(), "Infinite slider should change value during animation");
156+
}
157+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.codename1.ui;
2+
3+
import com.codename1.junit.FormTest;
4+
import com.codename1.junit.UITestBase;
5+
import com.codename1.ui.Toolbar;
6+
import com.codename1.ui.layouts.BorderLayout;
7+
import org.junit.jupiter.api.AfterEach;
8+
import org.junit.jupiter.api.BeforeEach;
9+
10+
import static org.junit.jupiter.api.Assertions.*;
11+
12+
class MenuBarDialogSideMenuTest extends UITestBase {
13+
private int originalCommandBehavior;
14+
private boolean originalOnTopSideMenu;
15+
16+
@BeforeEach
17+
void captureCommandBehavior() {
18+
originalCommandBehavior = Display.getInstance().getCommandBehavior();
19+
originalOnTopSideMenu = Toolbar.isOnTopSideMenu();
20+
}
21+
22+
@AfterEach
23+
void restoreCommandBehavior() {
24+
Display.getInstance().setCommandBehavior(originalCommandBehavior);
25+
Toolbar.setOnTopSideMenu(originalOnTopSideMenu);
26+
Form form = Display.getInstance().getCurrent();
27+
form.setMenuBar(new MenuBar());
28+
form.revalidate();
29+
}
30+
31+
@FormTest
32+
void menuBarAddsCommandsAndTracksDefaults() {
33+
implementation.setBuiltinSoundsEnabled(false);
34+
Display.getInstance().setCommandBehavior(Display.COMMAND_BEHAVIOR_BUTTON_BAR);
35+
36+
Form form = new Form("MenuBar Test", new BorderLayout());
37+
form.show();
38+
39+
MenuBar menuBar = new MenuBar();
40+
form.setMenuBar(menuBar);
41+
form.revalidate();
42+
43+
Command first = new Command("First");
44+
Command second = new Command("Second");
45+
menuBar.addCommand(first);
46+
menuBar.addCommand(second);
47+
form.revalidate();
48+
49+
assertEquals(2, menuBar.getCommandCount());
50+
assertSame(second, menuBar.getCommand(0));
51+
assertSame(first, menuBar.getCommand(1));
52+
53+
Button commandButton = menuBar.findCommandComponent(second);
54+
assertNotNull(commandButton, "Second command should have a bound button in button bar mode");
55+
56+
menuBar.setSelectCommand(null);
57+
assertNull(menuBar.getDefaultCommand(), "MenuBar should clear the default command when select command is removed");
58+
menuBar.setDefaultCommand(second);
59+
assertSame(second, menuBar.getDefaultCommand());
60+
61+
menuBar.setBackCommand(second);
62+
assertSame(second, menuBar.getBackCommand());
63+
64+
form.removeCommand(second);
65+
assertEquals(1, menuBar.getCommandCount());
66+
67+
menuBar.removeEmptySoftbuttons();
68+
}
69+
70+
@FormTest
71+
void dialogShowPackedAndDisposeLifecycle() {
72+
implementation.setBuiltinSoundsEnabled(false);
73+
74+
Form owner = new Form("Dialog Owner", new BorderLayout());
75+
owner.show();
76+
77+
Dialog dialog = new Dialog("Alert", new BorderLayout());
78+
dialog.setDisposeWhenPointerOutOfBounds(true);
79+
dialog.setDialogUIID("CustomDialog");
80+
dialog.add(BorderLayout.CENTER, new Label("Body"));
81+
Command ok = new Command("OK");
82+
dialog.addCommand(ok);
83+
84+
dialog.showPacked(BorderLayout.CENTER, false);
85+
flushSerialCalls();
86+
assertFalse(dialog.isDisposed(), "Dialog should be visible after showPacked");
87+
88+
dialog.dispose();
89+
flushSerialCalls();
90+
assertTrue(dialog.isDisposed(), "Dialog should be disposed after calling dispose");
91+
}
92+
93+
}

0 commit comments

Comments
 (0)