Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/release-beta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ jobs:
GITHUB_REPOSITORY: ${{ github.repository }}

- name: Wait for iOS Simulator
id: simulator
uses: kula-app/wait-for-services-action/ios-simulator@v1.3.0
with:
device: iPhone 17 Pro
Expand All @@ -71,7 +72,7 @@ jobs:
scheme: ScreenshotUITests

- name: Generate and Upload Screenshots to Sentry
run: bundle exec fastlane generate_and_upload_screenshots_ci
run: bundle exec fastlane generate_and_upload_screenshots_ci "simulator_udid:${{ steps.simulator.outputs.udid }}"
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
LICENSE_PLIST_GITHUB_TOKEN: ${{ steps.github_app_token.outputs.token }}
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/screenshots.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
bundler-cache: true

- name: Wait for iOS Simulator
id: simulator
uses: kula-app/wait-for-services-action/ios-simulator@v1.3.0
with:
device: iPhone 17 Pro
Expand All @@ -41,7 +42,7 @@ jobs:
scheme: ScreenshotUITests

- name: Capture and Upload Screenshots
run: bundle exec fastlane generate_and_upload_screenshots_ci
run: bundle exec fastlane generate_and_upload_screenshots_ci "simulator_udid:${{ steps.simulator.outputs.udid }}"
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
LICENSE_PLIST_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
25 changes: 16 additions & 9 deletions Targets/ScreenshotUITests/Sources/ScreenshotUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ final class ScreenshotUITests: XCTestCase {
continueAfterFailure = false
}

@MainActor
private func revealEditButton(for item: XCUIElement, named name: String, in app: XCUIApplication) -> XCUIElement {
let editButton = app.buttons["Edit \(name)"]

item.swipeLeft()
if !editButton.waitForExistence(timeout: 5) {
item.swipeLeft()
}

return editButton
}

@MainActor
func testScreenshots() throws { // swiftlint:disable:this function_body_length
let app = XCUIApplication()
Expand Down Expand Up @@ -59,11 +71,8 @@ final class ScreenshotUITests: XCTestCase {
let createdLinkButton = app.buttons.containing(NSPredicate(format: "label CONTAINS 'techprimate.com'")).firstMatch
XCTAssert(createdLinkButton.waitForExistence(timeout: 10), "Newly created link not found after creation")

// Long press to bring up context menu for edit
createdLinkButton.press(forDuration: 1.0)

let editButton = app.buttons.containing(NSPredicate(format: "label CONTAINS 'Edit'")).firstMatch
XCTAssert(editButton.waitForExistence(timeout: 10), "Edit button not found in context menu")
let editButton = revealEditButton(for: createdLinkButton, named: "techprimate.com", in: app)
XCTAssert(editButton.waitForExistence(timeout: 10), "Edit button not found in swipe actions")
editButton.tap()

// Wait for edit form to appear
Expand Down Expand Up @@ -147,10 +156,8 @@ final class ScreenshotUITests: XCTestCase {
// Open the edit list editor
let createdListButton = app.buttons.containing(NSPredicate(format: "label CONTAINS '\(lists[idx].name)'")).firstMatch
XCTAssert(createdListButton.waitForExistence(timeout: 10), "Created list button not found for list \(idx)")
createdListButton.press(forDuration: 1.0)

let editButton = app.buttons.containing(NSPredicate(format: "label CONTAINS 'Edit \(lists[idx].name)'")).firstMatch
XCTAssert(editButton.waitForExistence(timeout: 10), "Edit button not found in context menu")
let editButton = revealEditButton(for: createdListButton, named: lists[idx].name, in: app)
XCTAssert(editButton.waitForExistence(timeout: 10), "Edit button not found in swipe actions")
editButton.tap()

// Wait for the edit list form to appear
Expand Down
8 changes: 6 additions & 2 deletions fastlane/lanes/screenshots.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@
The output can then be used by run_screenshot_on_device for each device.
Options:
derived_data_path: path for build products (default: /tmp/screenshot_derived_data)
destination: xcodebuild destination (default: generic iOS Simulator)
DESC
lane :build_screenshots do |options|
derived_data_path = options[:derived_data_path] || "/tmp/screenshot_derived_data"
destination = options[:destination] || "generic/platform=iOS Simulator"

UI.message "Building screenshot test bundle..."

Expand All @@ -49,7 +51,7 @@
scheme: "ScreenshotUITests",
configuration: "Debug",
derived_data_path: derived_data_path,
destination: "generic/platform=iOS Simulator",
destination: destination,
build_for_testing: true,
xcargs: "SWIFT_TREAT_WARNINGS_AS_ERRORS=NO"
)
Expand Down Expand Up @@ -134,6 +136,7 @@
result = run_tests(
project: "Flinky.xcodeproj",
scheme: "ScreenshotUITests",
derived_data_path: derived_data_path,
xctestrun: xctestrun_path,
test_without_building: true,
destination: "platform=iOS Simulator,id=#{simulator_udid}",
Expand Down Expand Up @@ -184,6 +187,7 @@
Options:
language: language code (default: en-US)
output_dir: output directory (default: fastlane/screenshots)
expected_count: expected screenshot count (default: all configured devices)
DESC
lane :collect_screenshots do |options|
language = options[:language] || SCREENSHOT_LANGUAGE
Expand All @@ -206,7 +210,7 @@
UI.message " ✅ #{File.basename(file)}"
end

expected = SCREENSHOT_DEVICES.length * 4 # 4 screenshots per device
expected = options[:expected_count]&.to_i || SCREENSHOT_DEVICES.length * 4
if screenshots.length == expected
UI.success "✅ All #{screenshots.length} screenshots collected!"
else
Expand Down
39 changes: 15 additions & 24 deletions fastlane/lanes/utilities.rb
Original file line number Diff line number Diff line change
Expand Up @@ -134,32 +134,20 @@
Captures screenshots on a single iPhone device for faster CI builds
Use generate_screenshots for full multi-device App Store screenshots
DESC
lane :generate_screenshots_ci do
lane :generate_screenshots_ci do |options|
UI.message "Generating screenshots for CI (single device)"

capture_screenshots(
scheme: "ScreenshotUITests",
devices: [
"iPhone 17 Pro" # iPhone 6.3" display
],
languages: ["en-US"],
configuration: "Debug", # Use Debug to reduce build time (~5-6 min faster than Release)

clear_previous_screenshots: true,
concurrent_simulators: false,
skip_open_summary: true,

reinstall_app: true,
override_status_bar: true,
localize_simulator: true,
disable_slide_to_type: true,
derived_data_path = options[:derived_data_path] || "/tmp/screenshot_derived_data"
simulator_udid = options[:simulator_udid]
destination = simulator_udid ? "platform=iOS Simulator,id=#{simulator_udid}" : nil

skip_helper_version_check: true,

# See generate_screenshots: retry flakes, then fail loudly.
number_of_retries: 3,
stop_after_first_error: true
build_screenshots(derived_data_path: derived_data_path, destination: destination)
run_screenshot_on_device(
device: "iPhone 17 Pro",
simulator_udid: simulator_udid,
derived_data_path: derived_data_path
)
collect_screenshots(expected_count: 4)

UI.success "✅ CI screenshots generated successfully!"
UI.message "Screenshots generated in: fastlane/screenshots/"
Expand Down Expand Up @@ -206,8 +194,11 @@
Fast single-device screenshot generation for CI builds
Combines generate_screenshots_ci and upload_screenshots_to_sentry lanes
DESC
lane :generate_and_upload_screenshots_ci do
generate_screenshots_ci
lane :generate_and_upload_screenshots_ci do |options|
generate_screenshots_ci(
simulator_udid: options[:simulator_udid],
derived_data_path: options[:derived_data_path]
)
upload_screenshots_to_sentry
end

Expand Down
Loading