Skip to content
Merged
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
106 changes: 106 additions & 0 deletions .github/workflows/ios-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: iOS Tests

on:
pull_request:
paths:
- "TableProMobile/**"
- "Packages/TableProCore/**"
- "Libs/**"
- ".github/workflows/ios-tests.yml"
push:
branches: [main]
paths:
- "TableProMobile/**"
- "Packages/TableProCore/**"
- "Libs/**"
- ".github/workflows/ios-tests.yml"
workflow_dispatch:

# Only one run per PR/branch at a time; new pushes cancel pending older ones.
concurrency:
group: ios-tests-${{ github.ref }}
cancel-in-progress: true

env:
XCODE_PROJECT: TableProMobile/TableProMobile.xcodeproj
XCODE_SCHEME: TableProMobile
TEST_DESTINATION: "platform=iOS Simulator,name=iPhone 17 Pro,OS=26.5"

jobs:
test:
name: Run iOS Tests
runs-on: macos-26
timeout-minutes: 25

steps:
- uses: actions/checkout@v4

- name: Select Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '26.5-beta'

- name: Install xcbeautify
run: brew list xcbeautify &>/dev/null || brew install xcbeautify

# macos-26 lazy-loads simulator runtimes; -downloadPlatform pulls the runtime
# matching Xcode's SDK and is a no-op when it is already present.
- name: Install iOS simulator runtime
run: sudo xcodebuild -downloadPlatform iOS

# Secrets.xcconfig is gitignored. Tests do not need analytics keys, so the
# checked-in example template is enough for the project to resolve.
- name: Stub Secrets.xcconfig
run: cp TableProMobile/Secrets.xcconfig.example TableProMobile/Secrets.xcconfig

- name: Cache static libraries
uses: actions/cache@v4
with:
path: Libs
key: ${{ runner.os }}-libs-${{ hashFiles('Libs/checksums.sha256') }}

- name: Download static libraries
env:
GH_TOKEN: ${{ github.token }}
run: scripts/download-libs.sh

- name: Resolve Swift package dependencies
run: |
xcodebuild -resolvePackageDependencies \
-project "$XCODE_PROJECT" \
-scheme "$XCODE_SCHEME" \
-skipPackagePluginValidation

- name: Run unit tests
run: |
set -o pipefail
xcodebuild test \
-project "$XCODE_PROJECT" \
-scheme "$XCODE_SCHEME" \
-destination "$TEST_DESTINATION" \
-only-testing:TableProMobileTests \
-skipPackagePluginValidation \
-resultBundlePath TestResults.xcresult \
CODE_SIGNING_ALLOWED=NO \
| xcbeautify --renderer github-actions

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: ios-test-results
path: TestResults.xcresult
retention-days: 7

# Diagnostics only on failure so happy-path runs stay quiet.
- name: Show simulator state on failure
if: failure()
run: |
echo "=== iOS runtimes ==="
xcrun simctl list runtimes | grep -E "iOS|tvOS" || true
echo "=== Eligible scheme destinations ==="
xcodebuild -showdestinations \
-project "$XCODE_PROJECT" \
-scheme "$XCODE_SCHEME" \
-skipPackagePluginValidation 2>&1 \
| grep -E "iOS Simulator.*iPhone" || true
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- iOS: Vietnamese localization completed for the iOS strings catalog (312/312 keys)
- Internal: GitHub Actions workflow `ios-tests.yml` runs the iOS unit tests on every PR and main push that touches mobile code or shared packages
- Internal: Swift Testing tests for `DataBrowserViewModel`, `ConnectionFormViewModel`, and `RowDetailViewModel` covering load lifecycle, pagination, sort/filter/search, delete, hydration, validation, edit lifecycle, save paths, and lazy cell load. Runs against in-memory `DatabaseDriver` and `SecureStore` mocks. `loadStoredCredentials`, `testConnection`, `save` on `ConnectionFormViewModel` now accept `any SecureStore` so the keychain backend can be substituted under test
- Internal: extract `RowItemLabel` shared row component for the connection list and table list, dropping the inline HStack scaffolding from both
- Internal: move per-database-type constants (`defaultPort`, `mobileDisplayName`, `mobileSupportedTypes`) onto a `DatabaseType` extension; the connection form picker and info screen read from the same source instead of duplicating the type-to-string switch
Expand Down
7 changes: 7 additions & 0 deletions TableProMobile/TableProMobile/AppState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ final class AppState {
connections = storage.load()
groups = groupStorage.load()
tags = tagStorage.load()

// Skip side-effecting callbacks (Spotlight, WidgetKit, sync wiring) when
// running unit tests inside the host app. These rely on entitlements
// that the CI simulator does not have and have caused the test runner
// to crash before it could connect to xctest.
guard ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] == nil else { return }

secureStore.cleanOrphanedCredentials(validConnectionIds: Set(connections.map(\.id)))
Task {
updateWidgetData()
Expand Down
Loading
Loading