Skip to content

Commit 577bd7b

Browse files
Add AssertJ Swing testing for some UI components
1 parent 2d37382 commit 577bd7b

8 files changed

Lines changed: 931 additions & 1 deletion

File tree

.github/workflows/swing-tests.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Swing UI Tests
2+
3+
on: workflow_dispatch
4+
5+
jobs:
6+
swing-tests:
7+
8+
runs-on: ubuntu-latest
9+
10+
steps:
11+
12+
- uses: actions/checkout@v4
13+
14+
- name: Set up JDK
15+
uses: actions/setup-java@v4
16+
with:
17+
java-version: 21
18+
distribution: 'temurin'
19+
cache: maven
20+
21+
- name: Install Xvfb
22+
run: sudo apt-get install -y xvfb
23+
24+
- name: Run Swing Tests with Xvfb
25+
run: xvfb-run -a mvn -B test -Pswing-tests -pl Framework/Stratus --file pom.xml

Framework/Stratus/pom.xml

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,20 @@
2323
<artifactId>junit</artifactId>
2424
<version>4.13.1</version>
2525
<scope>test</scope>
26-
</dependency><!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
26+
</dependency>
27+
<dependency>
28+
<groupId>org.assertj</groupId>
29+
<artifactId>assertj-swing-junit</artifactId>
30+
<version>3.17.1</version>
31+
<scope>test</scope>
32+
</dependency>
33+
<dependency>
34+
<groupId>org.assertj</groupId>
35+
<artifactId>assertj-core</artifactId>
36+
<version>3.24.2</version>
37+
<scope>test</scope>
38+
</dependency>
39+
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
2740
<dependency>
2841
<groupId>org.swinglabs.swingx</groupId>
2942
<artifactId>swingx-graphics</artifactId>
@@ -73,7 +86,61 @@
7386
</execution>
7487
</executions>
7588
</plugin>
89+
<plugin>
90+
<groupId>org.apache.maven.plugins</groupId>
91+
<artifactId>maven-surefire-plugin</artifactId>
92+
<version>3.2.5</version>
93+
<configuration>
94+
<argLine>
95+
--add-opens java.base/java.lang=ALL-UNNAMED
96+
--add-opens java.desktop/javax.swing=ALL-UNNAMED
97+
--add-opens java.desktop/java.awt=ALL-UNNAMED
98+
--add-opens java.base/java.util=ALL-UNNAMED
99+
</argLine>
100+
<forkCount>1</forkCount>
101+
<reuseForks>false</reuseForks>
102+
<includes>
103+
<include>**/*Test.java</include>
104+
</includes>
105+
<!-- Swing tests excluded by default (require display/Xvfb) -->
106+
<excludes>
107+
<exclude>**/*SwingTest.java</exclude>
108+
</excludes>
109+
</configuration>
110+
</plugin>
76111
</plugins>
77112
</build>
78113

114+
<profiles>
115+
<!-- Profile for running Swing UI tests (requires display or Xvfb) -->
116+
<profile>
117+
<id>swing-tests</id>
118+
<build>
119+
<plugins>
120+
<plugin>
121+
<groupId>org.apache.maven.plugins</groupId>
122+
<artifactId>maven-surefire-plugin</artifactId>
123+
<version>3.2.5</version>
124+
<configuration>
125+
<argLine>
126+
--add-opens java.base/java.lang=ALL-UNNAMED
127+
--add-opens java.desktop/javax.swing=ALL-UNNAMED
128+
--add-opens java.desktop/java.awt=ALL-UNNAMED
129+
--add-opens java.base/java.util=ALL-UNNAMED
130+
</argLine>
131+
<forkCount>1</forkCount>
132+
<reuseForks>false</reuseForks>
133+
<includes>
134+
<include>**/*SwingTest.java</include>
135+
</includes>
136+
<excludes>
137+
<exclude>none</exclude>
138+
</excludes>
139+
</configuration>
140+
</plugin>
141+
</plugins>
142+
</build>
143+
</profile>
144+
</profiles>
145+
79146
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package org.peakaboo.framework.stratus.components.ui.fluentcontrols.button;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import javax.swing.JButton;
6+
import javax.swing.JFrame;
7+
import javax.swing.JPanel;
8+
9+
import org.assertj.swing.core.GenericTypeMatcher;
10+
import org.assertj.swing.fixture.FrameFixture;
11+
import org.assertj.swing.fixture.JButtonFixture;
12+
import org.junit.Test;
13+
import org.peakaboo.framework.stratus.test.StratusSwingTest;
14+
15+
public class FluentButtonSwingTest extends StratusSwingTest {
16+
17+
private FrameFixture window;
18+
private FluentButton button;
19+
20+
@Override
21+
protected void onSetUp() {
22+
super.onSetUp();
23+
24+
JFrame frame = createOnEDT(() -> {
25+
JFrame f = new JFrame("FluentButton Test");
26+
JPanel panel = new JPanel();
27+
button = new FluentButton("Test Button");
28+
panel.add(button);
29+
f.add(panel);
30+
f.setSize(300, 100);
31+
f.setLocationRelativeTo(null);
32+
return f;
33+
});
34+
35+
window = new FrameFixture(robot(), frame);
36+
window.show();
37+
robot().waitForIdle();
38+
}
39+
40+
@Test
41+
public void testButtonWithTextIsVisible() {
42+
JButtonFixture buttonFixture = window.button(new GenericTypeMatcher<JButton>(JButton.class) {
43+
@Override
44+
protected boolean isMatching(JButton b) {
45+
return b instanceof FluentButton && "Test Button".equals(b.getText());
46+
}
47+
});
48+
buttonFixture.requireVisible();
49+
buttonFixture.requireEnabled();
50+
}
51+
52+
@Test
53+
public void testButtonActionTriggered() {
54+
boolean[] actionCalled = {false};
55+
56+
createOnEDT(() -> {
57+
button.withAction(() -> actionCalled[0] = true);
58+
return null;
59+
});
60+
robot().waitForIdle();
61+
62+
// Trigger action programmatically
63+
createOnEDT(() -> {
64+
button.doClick();
65+
return null;
66+
});
67+
robot().waitForIdle();
68+
69+
assertThat(actionCalled[0]).isTrue();
70+
}
71+
72+
@Test
73+
public void testButtonWithTooltip() {
74+
createOnEDT(() -> {
75+
button.withTooltip("Test Tooltip");
76+
return null;
77+
});
78+
robot().waitForIdle();
79+
80+
// Stratus wraps tooltips in HTML for styling
81+
assertThat(button.getToolTipText()).contains("Test Tooltip");
82+
}
83+
84+
@Test
85+
public void testButtonCanBeDisabled() {
86+
createOnEDT(() -> {
87+
button.setEnabled(false);
88+
return null;
89+
});
90+
robot().waitForIdle();
91+
92+
JButtonFixture buttonFixture = window.button(new GenericTypeMatcher<JButton>(JButton.class) {
93+
@Override
94+
protected boolean isMatching(JButton b) {
95+
return b instanceof FluentButton;
96+
}
97+
});
98+
buttonFixture.requireDisabled();
99+
}
100+
101+
@Test
102+
public void testButtonTextCanBeChanged() {
103+
createOnEDT(() -> {
104+
button.withText("New Text");
105+
return null;
106+
});
107+
robot().waitForIdle();
108+
109+
assertThat(button.getText()).isEqualTo("New Text");
110+
}
111+
112+
@Override
113+
protected void onTearDown() throws Exception {
114+
if (window != null) {
115+
window.cleanUp();
116+
}
117+
super.onTearDown();
118+
}
119+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package org.peakaboo.framework.stratus.components.ui.header;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import javax.swing.JButton;
6+
import javax.swing.JFrame;
7+
8+
import org.assertj.swing.core.GenericTypeMatcher;
9+
import org.assertj.swing.fixture.FrameFixture;
10+
import org.assertj.swing.fixture.JButtonFixture;
11+
import org.junit.Test;
12+
import org.peakaboo.framework.stratus.components.ui.fluentcontrols.button.FluentButton;
13+
import org.peakaboo.framework.stratus.test.StratusSwingTest;
14+
15+
public class HeaderBoxSwingTest extends StratusSwingTest {
16+
17+
private FrameFixture window;
18+
private HeaderBox headerBox;
19+
20+
@Override
21+
protected void onSetUp() {
22+
super.onSetUp();
23+
24+
JFrame frame = createOnEDT(() -> {
25+
JFrame f = new JFrame("HeaderBox Test");
26+
headerBox = new HeaderBox();
27+
headerBox.setShowClose(true);
28+
f.add(headerBox);
29+
f.setSize(400, 100);
30+
f.setLocationRelativeTo(null); // Centre on screen
31+
return f;
32+
});
33+
34+
window = new FrameFixture(robot(), frame);
35+
window.show();
36+
37+
// Wait for EDT to process the rebuild scheduled by setShowClose
38+
robot().waitForIdle();
39+
}
40+
41+
@Test
42+
public void testCloseButtonVisible() {
43+
// Find the FluentButton (there should only be one - the close button)
44+
JButtonFixture closeButton = window.button(new GenericTypeMatcher<JButton>(JButton.class) {
45+
@Override
46+
protected boolean isMatching(JButton button) {
47+
return button instanceof FluentButton;
48+
}
49+
});
50+
closeButton.requireVisible();
51+
}
52+
53+
@Test
54+
public void testCloseButtonAction() {
55+
boolean[] closeCalled = {false};
56+
headerBox.setOnClose(() -> closeCalled[0] = true);
57+
58+
// Find the close button
59+
JButtonFixture closeButton = window.button(new GenericTypeMatcher<JButton>(JButton.class) {
60+
@Override
61+
protected boolean isMatching(JButton button) {
62+
return button instanceof FluentButton;
63+
}
64+
});
65+
66+
// Trigger action programmatically instead of clicking
67+
// (clicking can fail with "out of bounds" errors in some test environments)
68+
FluentButton fluentButton = (FluentButton) closeButton.target();
69+
createOnEDT(() -> {
70+
fluentButton.doClick();
71+
return null;
72+
});
73+
74+
robot().waitForIdle();
75+
assertThat(closeCalled[0]).isTrue();
76+
}
77+
78+
@Override
79+
protected void onTearDown() throws Exception {
80+
if (window != null) {
81+
window.cleanUp();
82+
}
83+
super.onTearDown();
84+
}
85+
}

0 commit comments

Comments
 (0)