Skip to content

Commit cbc49ad

Browse files
authored
First part of creating a screenshot process for iOS Simulator
1 parent fa60cbb commit cbc49ad

File tree

9 files changed

+1024
-63
lines changed

9 files changed

+1024
-63
lines changed

.github/workflows/scripts-ios.yml

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ on:
77
- 'scripts/setup-workspace.sh'
88
- 'scripts/build-ios-port.sh'
99
- 'scripts/build-ios-app.sh'
10+
- 'scripts/run-ios-ui-tests.sh'
11+
- 'scripts/ios/tests/**'
12+
- 'scripts/ios/screenshots/**'
1013
- 'scripts/templates/**'
1114
- '!scripts/templates/**/*.md'
1215
- 'CodenameOne/src/**'
@@ -25,6 +28,9 @@ on:
2528
- 'scripts/setup-workspace.sh'
2629
- 'scripts/build-ios-port.sh'
2730
- 'scripts/build-ios-app.sh'
31+
- 'scripts/run-ios-ui-tests.sh'
32+
- 'scripts/ios/tests/**'
33+
- 'scripts/ios/screenshots/**'
2834
- 'scripts/templates/**'
2935
- '!scripts/templates/**/*.md'
3036
- 'CodenameOne/src/**'
@@ -39,24 +45,32 @@ on:
3945

4046
jobs:
4147
build-ios:
48+
permissions:
49+
contents: read
50+
pull-requests: write
51+
issues: write
4252
runs-on: macos-15 # pinning macos-15 avoids surprises during the cutover window
4353
timeout-minutes: 60 # allow enough time for dependency installs and full build
4454
concurrency: # ensure only one mac build runs at once
45-
group: mac-ci
46-
cancel-in-progress: false # queue new ones instead of canceling in-flight
55+
group: mac-ci-${{ github.workflow }}-${{ github.ref_name }}
56+
cancel-in-progress: true
57+
58+
env:
59+
GITHUB_TOKEN: ${{ secrets.CN1SS_GH_TOKEN }}
60+
GH_TOKEN: ${{ secrets.CN1SS_GH_TOKEN }}
4761

4862
steps:
4963
- uses: actions/checkout@v4
5064

5165
- name: Ensure CocoaPods tooling
5266
run: |
5367
set -euo pipefail
54-
if ! command -v pod >/dev/null 2>&1; then
55-
sudo gem install cocoapods --no-document
56-
fi
57-
if ! ruby -rrubygems -e "exit(Gem::Specification::find_all_by_name('xcodeproj').empty? ? 1 : 0)"; then
58-
sudo gem install xcodeproj --no-document
68+
if ! command -v ruby >/dev/null; then
69+
echo "ruby not found"; exit 1
5970
fi
71+
GEM_USER_DIR="$(ruby -e 'print Gem.user_dir')"
72+
export PATH="$GEM_USER_DIR/bin:$PATH"
73+
gem install cocoapods xcodeproj --no-document --user-install
6074
pod --version
6175
6276
- name: Restore cn1-binaries cache
@@ -77,6 +91,32 @@ jobs:
7791
timeout-minutes: 25
7892

7993
- name: Build sample iOS app and compile workspace
94+
id: build-ios-app
8095
run: ./scripts/build-ios-app.sh -q -DskipTests
8196
timeout-minutes: 30
8297

98+
- name: Run iOS UI screenshot tests
99+
env:
100+
ARTIFACTS_DIR: ${{ github.workspace }}/artifacts
101+
run: |
102+
set -euo pipefail
103+
mkdir -p "${ARTIFACTS_DIR}"
104+
105+
echo "workspace='${{ steps.build-ios-app.outputs.workspace }}'"
106+
echo "scheme='${{ steps.build-ios-app.outputs.scheme }}'"
107+
108+
./scripts/run-ios-ui-tests.sh \
109+
"${{ steps.build-ios-app.outputs.workspace }}" \
110+
"" \
111+
"${{ steps.build-ios-app.outputs.scheme }}"
112+
timeout-minutes: 30
113+
114+
- name: Upload iOS artifacts
115+
if: always()
116+
uses: actions/upload-artifact@v4
117+
with:
118+
name: ios-ui-tests
119+
path: artifacts
120+
if-no-files-found: warn
121+
retention-days: 14
122+

scripts/android/tests/PostPrComment.java

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
import java.util.regex.Pattern;
2323

2424
public class PostPrComment {
25-
private static final String MARKER = "<!-- CN1SS_SCREENSHOT_COMMENT -->";
26-
private static final String LOG_PREFIX = "[run-android-instrumentation-tests]";
25+
private static final String DEFAULT_MARKER = "<!-- CN1SS_SCREENSHOT_COMMENT -->";
26+
private static final String DEFAULT_LOG_PREFIX = "[run-android-instrumentation-tests]";
27+
private static String marker = DEFAULT_MARKER;
28+
private static String logPrefix = DEFAULT_LOG_PREFIX;
2729

2830
public static void main(String[] args) throws Exception {
2931
int exitCode = execute(args);
@@ -35,6 +37,9 @@ private static int execute(String[] args) throws Exception {
3537
if (arguments == null) {
3638
return 2;
3739
}
40+
marker = arguments.marker != null ? arguments.marker : DEFAULT_MARKER;
41+
logPrefix = arguments.logPrefix != null ? arguments.logPrefix : DEFAULT_LOG_PREFIX;
42+
3843
Path bodyPath = arguments.body;
3944
if (!Files.isRegularFile(bodyPath)) {
4045
return 0;
@@ -44,10 +49,10 @@ private static int execute(String[] args) throws Exception {
4449
if (body.isEmpty()) {
4550
return 0;
4651
}
47-
if (!body.contains(MARKER)) {
48-
body = body.stripTrailing() + "\n\n" + MARKER;
52+
if (!body.contains(marker)) {
53+
body = body.stripTrailing() + "\n\n" + marker;
4954
}
50-
String bodyWithoutMarker = body.replace(MARKER, "").trim();
55+
String bodyWithoutMarker = body.replace(marker, "").trim();
5156
if (bodyWithoutMarker.isEmpty()) {
5257
return 0;
5358
}
@@ -154,7 +159,7 @@ private static CommentContext locateExistingComment(HttpClient client, Map<Strin
154159
for (Object comment : comments) {
155160
Map<String, Object> commentMap = JsonUtil.asObject(comment);
156161
String bodyText = stringValue(commentMap.get("body"), "");
157-
if (bodyText.contains(MARKER)) {
162+
if (bodyText.contains(marker)) {
158163
existingComment = commentMap;
159164
Map<String, Object> user = JsonUtil.asObject(commentMap.get("user"));
160165
String login = stringValue(user.get("login"), null);
@@ -181,7 +186,7 @@ private static CommentContext locateExistingComment(HttpClient client, Map<Strin
181186
.uri(URI.create("https://api.github.com/repos/" + repo + "/issues/" + prNumber + "/comments"))
182187
.timeout(Duration.ofSeconds(20))
183188
.headers(headers.entrySet().stream().flatMap(e -> java.util.stream.Stream.of(e.getKey(), e.getValue())).toArray(String[]::new))
184-
.POST(HttpRequest.BodyPublishers.ofString(JsonUtil.stringify(Map.of("body", MARKER))))
189+
.POST(HttpRequest.BodyPublishers.ofString(JsonUtil.stringify(Map.of("body", marker))))
185190
.build();
186191
HttpResponse<String> createResponse = client.send(createRequest, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
187192
if (createResponse.statusCode() >= 200 && createResponse.statusCode() < 300) {
@@ -406,11 +411,11 @@ private static String stringValue(Object value, String fallback) {
406411
}
407412

408413
private static void log(String message) {
409-
System.out.println(LOG_PREFIX + " " + message);
414+
System.out.println(logPrefix + " " + message);
410415
}
411416

412417
private static void err(String message) {
413-
System.err.println(LOG_PREFIX + " " + message);
418+
System.err.println(logPrefix + " " + message);
414419
}
415420

416421
private record CommentContext(long commentId, boolean createdPlaceholder) {
@@ -422,15 +427,21 @@ private record AttachmentReplacement(String body, List<String> missing) {
422427
private static class Arguments {
423428
final Path body;
424429
final Path previewDir;
430+
final String marker;
431+
final String logPrefix;
425432

426-
private Arguments(Path body, Path previewDir) {
433+
private Arguments(Path body, Path previewDir, String marker, String logPrefix) {
427434
this.body = body;
428435
this.previewDir = previewDir;
436+
this.marker = marker;
437+
this.logPrefix = logPrefix;
429438
}
430439

431440
static Arguments parse(String[] args) {
432441
Path body = null;
433442
Path previewDir = null;
443+
String marker = null;
444+
String logPrefix = null;
434445
for (int i = 0; i < args.length; i++) {
435446
String arg = args[i];
436447
switch (arg) {
@@ -448,6 +459,20 @@ static Arguments parse(String[] args) {
448459
}
449460
previewDir = Path.of(args[i]);
450461
}
462+
case "--marker" -> {
463+
if (++i >= args.length) {
464+
System.err.println("Missing value for --marker");
465+
return null;
466+
}
467+
marker = args[i];
468+
}
469+
case "--log-prefix" -> {
470+
if (++i >= args.length) {
471+
System.err.println("Missing value for --log-prefix");
472+
return null;
473+
}
474+
logPrefix = args[i];
475+
}
451476
default -> {
452477
System.err.println("Unknown argument: " + arg);
453478
return null;
@@ -458,7 +483,7 @@ static Arguments parse(String[] args) {
458483
System.err.println("--body is required");
459484
return null;
460485
}
461-
return new Arguments(body, previewDir);
486+
return new Arguments(body, previewDir, marker, logPrefix);
462487
}
463488
}
464489

scripts/android/tests/RenderScreenshotReport.java

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
import java.util.Map;
99

1010
public class RenderScreenshotReport {
11-
private static final String MARKER = "<!-- CN1SS_SCREENSHOT_COMMENT -->";
11+
private static final String DEFAULT_MARKER = "<!-- CN1SS_SCREENSHOT_COMMENT -->";
12+
private static final String DEFAULT_TITLE = "Android screenshot updates";
13+
private static final String DEFAULT_SUCCESS_MESSAGE = "✅ Native Android screenshot tests passed.";
1214

1315
public static void main(String[] args) throws Exception {
1416
Arguments arguments = Arguments.parse(args);
@@ -24,7 +26,11 @@ public static void main(String[] args) throws Exception {
2426
String text = Files.readString(comparePath, StandardCharsets.UTF_8);
2527
Object parsed = JsonUtil.parse(text);
2628
Map<String, Object> data = JsonUtil.asObject(parsed);
27-
SummaryAndComment output = buildSummaryAndComment(data);
29+
String marker = arguments.marker != null ? arguments.marker : DEFAULT_MARKER;
30+
String title = arguments.title != null ? arguments.title : DEFAULT_TITLE;
31+
String successMessage = arguments.successMessage != null ? arguments.successMessage : DEFAULT_SUCCESS_MESSAGE;
32+
33+
SummaryAndComment output = buildSummaryAndComment(data, title, marker, successMessage);
2834
writeLines(arguments.summaryOut, output.summaryLines);
2935
writeLines(arguments.commentOut, output.commentLines);
3036
}
@@ -43,7 +49,7 @@ private static void writeLines(Path path, List<String> lines) throws IOException
4349
Files.writeString(path, sb.toString(), StandardCharsets.UTF_8);
4450
}
4551

46-
private static SummaryAndComment buildSummaryAndComment(Map<String, Object> data) {
52+
private static SummaryAndComment buildSummaryAndComment(Map<String, Object> data, String title, String marker, String successMessage) {
4753
List<String> summaryLines = new ArrayList<>();
4854
List<String> commentLines = new ArrayList<>();
4955
Object resultsObj = data.get("results");
@@ -114,8 +120,10 @@ private static SummaryAndComment buildSummaryAndComment(Map<String, Object> data
114120
}
115121

116122
if (!commentEntries.isEmpty()) {
117-
commentLines.add("### Android screenshot updates");
118-
commentLines.add("");
123+
if (title != null && !title.isEmpty()) {
124+
commentLines.add("### " + title);
125+
commentLines.add("");
126+
}
119127
for (Map<String, Object> entry : commentEntries) {
120128
String test = stringValue(entry.get("test"), "");
121129
String status = stringValue(entry.get("status"), "");
@@ -127,11 +135,11 @@ private static SummaryAndComment buildSummaryAndComment(Map<String, Object> data
127135
if (!commentLines.isEmpty() && !commentLines.get(commentLines.size() - 1).isEmpty()) {
128136
commentLines.add("");
129137
}
130-
commentLines.add(MARKER);
138+
commentLines.add(marker);
131139
} else {
132-
commentLines.add("✅ Native Android screenshot tests passed.");
140+
commentLines.add(successMessage != null ? successMessage : DEFAULT_SUCCESS_MESSAGE);
133141
commentLines.add("");
134-
commentLines.add(MARKER);
142+
commentLines.add(marker);
135143
}
136144
return new SummaryAndComment(summaryLines, commentLines);
137145
}
@@ -258,17 +266,26 @@ private static class Arguments {
258266
final Path compareJson;
259267
final Path commentOut;
260268
final Path summaryOut;
269+
final String marker;
270+
final String title;
271+
final String successMessage;
261272

262-
private Arguments(Path compareJson, Path commentOut, Path summaryOut) {
273+
private Arguments(Path compareJson, Path commentOut, Path summaryOut, String marker, String title, String successMessage) {
263274
this.compareJson = compareJson;
264275
this.commentOut = commentOut;
265276
this.summaryOut = summaryOut;
277+
this.marker = marker;
278+
this.title = title;
279+
this.successMessage = successMessage;
266280
}
267281

268282
static Arguments parse(String[] args) {
269283
Path compare = null;
270284
Path comment = null;
271285
Path summary = null;
286+
String marker = null;
287+
String title = null;
288+
String successMessage = null;
272289
for (int i = 0; i < args.length; i++) {
273290
String arg = args[i];
274291
switch (arg) {
@@ -293,6 +310,27 @@ static Arguments parse(String[] args) {
293310
}
294311
summary = Path.of(args[i]);
295312
}
313+
case "--marker" -> {
314+
if (++i >= args.length) {
315+
System.err.println("Missing value for --marker");
316+
return null;
317+
}
318+
marker = args[i];
319+
}
320+
case "--title" -> {
321+
if (++i >= args.length) {
322+
System.err.println("Missing value for --title");
323+
return null;
324+
}
325+
title = args[i];
326+
}
327+
case "--success-message" -> {
328+
if (++i >= args.length) {
329+
System.err.println("Missing value for --success-message");
330+
return null;
331+
}
332+
successMessage = args[i];
333+
}
296334
default -> {
297335
System.err.println("Unknown argument: " + arg);
298336
return null;
@@ -303,7 +341,7 @@ static Arguments parse(String[] args) {
303341
System.err.println("--compare-json, --comment-out, and --summary-out are required");
304342
return null;
305343
}
306-
return new Arguments(compare, comment, summary);
344+
return new Arguments(compare, comment, summary, marker, title, successMessage);
307345
}
308346
}
309347
}

0 commit comments

Comments
 (0)