Skip to content

Commit 773f376

Browse files
authored
Merge pull request #1 from KlacsoRobert/add-snapshot-testing
add snapshot testing with snapshots
2 parents 51c538b + 1c24dd5 commit 773f376

File tree

18 files changed

+706
-20
lines changed

18 files changed

+706
-20
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: Snapshot Test iOS
2+
description: A GitHub Actions workflow for running snapshot tests on iOS.
3+
4+
inputs:
5+
xcworkspacePath:
6+
description: 'Path to the Xcode workspace file'
7+
required: true
8+
9+
xcodeSnapshotTestScheme:
10+
description: 'Scheme to run snapshot tests on'
11+
required: true
12+
13+
runs:
14+
using: composite
15+
16+
steps:
17+
- name: Run snapshot tests on iOS
18+
id: run-ios-snapshot-tests
19+
shell: bash
20+
run: |
21+
xcodebuild -workspace "${{ inputs.xcworkspacePath }}" \
22+
-scheme ${{ inputs.xcodeSnapshotTestScheme }} \
23+
-sdk iphonesimulator \
24+
-destination 'platform=iOS Simulator,name=iPhone 14,OS=16.4' \
25+
test
26+
27+
- name: Process failed iOS snapshot test
28+
if: failure()
29+
id: failed-ios-screenshots
30+
shell: bash
31+
run: |
32+
if [[ -z ${GITHUB_TOKEN} ]]; then
33+
echo "Missing GITHUB_TOKEN variable"
34+
exit 1
35+
fi
36+
37+
if [[ -z ${GITHUB_REPOSITORY} ]]; then
38+
echo "Missing GITHUB_REPOSITORY variable"
39+
exit 1
40+
fi
41+
42+
if [[ -z ${PR_BRANCH} ]]; then
43+
echo "Missing PR_BRANCH variable"
44+
exit 1
45+
fi
46+
47+
FAILED_SNAPSHOTS="$(find . -name "__FailedSnapshots__" -type d)"
48+
SNAPSHOTS="$(find . -name "__Snapshots__" -type d)"
49+
PR_NUMBER="${GITHUB_REF#refs/pull/}"
50+
PR_NUMBER="${PR_NUMBER/\/merge/}"
51+
NEW_BRANCH_NAME="snapshots/pr-${PR_NUMBER}"
52+
53+
cp -a ${FAILED_SNAPSHOTS}/* ${SNAPSHOTS}
54+
rm -rf ${FAILED_SNAPSHOTS}
55+
echo "PR_NUMBER=${PR_NUMBER}" >> "${GITHUB_OUTPUT}"
56+
57+
git config user.name "${GITHUB_ACTOR}"
58+
git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
59+
git fetch origin "${PR_BRANCH}"
60+
git checkout --track "origin/${PR_BRANCH}"
61+
git checkout -b "${NEW_BRANCH_NAME}"
62+
git add ${SNAPSHOTS}
63+
git commit -m "test(snapshots): Update screenshots"
64+
git push --force-with-lease --set-upstream origin "${NEW_BRANCH_NAME}":"${NEW_BRANCH_NAME}"
65+
echo "PR_COMMENT=\"Screenshot tests failed.\n\n[See differences](https://github.com/${GITHUB_REPOSITORY}/compare/${PR_BRANCH}...${NEW_BRANCH_NAME})\n\nMerge the branch if it's an intentional change.\"" >> "${GITHUB_OUTPUT}"
66+
67+
unset FAILED_SNAPSHOTS
68+
unset SNAPSHOTS
69+
unset PR_NUMBER
70+
unset NEW_BRANCH_NAME
71+
env:
72+
GITHUB_TOKEN: ${{ github.token }}
73+
GITHUB_REPOSITORY: ${{ github.repository }}
74+
PR_BRANCH: ${{ github.head_ref }}
75+
GITHUB_ACTOR: ${{ github.actor }}
76+
77+
- name: Comment PR if iOS snapshot tests failed
78+
uses: octokit/[email protected]
79+
if: always() && steps.failed-ios-screenshots.outputs.PR_COMMENT
80+
with:
81+
route: POST /repos/:repo/issues/:issue_number/comments
82+
repo: ${{ github.repository }}
83+
issue_number: ${{ steps.failed-ios-screenshots.outputs.PR_NUMBER }}
84+
body: ${{ steps.failed-ios-screenshots.outputs.PR_COMMENT }}
85+
env:
86+
GITHUB_TOKEN: ${{ github.token }}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Test Snapshots on iOS
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
paths:
8+
- '.github/actions/snapshot_test_ios/action.yml'
9+
- '.github/workflows/test_snapshot_test_ios.yml'
10+
11+
jobs:
12+
test_snapshot_test_ios:
13+
runs-on: macos-13
14+
15+
permissions:
16+
contents: write
17+
pull-requests: write
18+
19+
steps:
20+
- uses: actions/[email protected]
21+
22+
- name: Select Xcode version
23+
run: |
24+
sudo xcode-select -s /Applications/Xcode_14.3.app
25+
26+
- name: Pod Install
27+
shell: bash
28+
run: |
29+
pod install
30+
31+
- name: Cache Derived Data
32+
uses: actions/[email protected]
33+
if: ${{ !inputs.disableDerivedDataCache }}
34+
with:
35+
path: |
36+
~/Library/Developer/Xcode/DerivedData/
37+
key: ${{ runner.os }}-xcode-${{ hashFiles('**/Podfile.lock', '**/Package.resolved') }}
38+
restore-keys: |
39+
${{ runner.os }}-xcode-
40+
41+
- name: Run snapshot tests on iOS
42+
uses: ./.github/actions/snapshot_test_ios
43+
with:
44+
xcworkspacePath: 'SnapshotArticle.xcworkspace'
45+
xcodeSnapshotTestScheme: 'SnapshotArticle'

Extensions/View+Extensions.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// View+Extensions.swift
3+
// SnapshotArticleTests
4+
//
5+
// Created by Robert Klacso on 2023. 08. 03..
6+
//
7+
8+
import SwiftUI
9+
10+
extension View {
11+
func toViewController() -> UIViewController {
12+
let viewController = UIHostingController(rootView: self)
13+
viewController.view.frame = UIScreen.main.bounds
14+
return viewController
15+
}
16+
}

Podfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Uncomment the next line to define a global platform for your project
2+
platform :ios, '15.0'
3+
4+
target 'SnapshotArticle' do
5+
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
6+
use_frameworks!
7+
8+
# Pods for app
9+
pod 'Sourcery', :subspecs => ['CLI-Only']
10+
11+
end

Podfile.lock

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
PODS:
2+
- Sourcery/CLI-Only (2.0.2)
3+
4+
DEPENDENCIES:
5+
- Sourcery/CLI-Only
6+
7+
SPEC REPOS:
8+
trunk:
9+
- Sourcery
10+
11+
SPEC CHECKSUMS:
12+
Sourcery: 9ae7eb711f830e8c3c02b98dc28fbd7e32e9d971
13+
14+
PODFILE CHECKSUM: ec02f1c9d2f5b6c378ddad350b7ec1879d7d2ae8
15+
16+
COCOAPODS: 1.12.1

PreviewProvider.stencil

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import SwiftUI
2+
import SnapshotTesting
3+
import XCTest
4+
@testable import SnapshotArticle
5+
6+
private func assertPreviewSnapshot<T: PreviewProvider>(_ target: T.Type,
7+
file: StaticString = #file,
8+
testName: String = #function,
9+
line: UInt = #line,
10+
isDarkMode: Bool = false) {
11+
for preview in T._allPreviews {
12+
let content = preview.content
13+
let viewController = content.toViewController()
14+
if isDarkMode {
15+
viewController.overrideUserInterfaceStyle = .dark
16+
}
17+
assertSnapshot(matching: viewController,
18+
as: .image(on: .iPhone13),
19+
file: file,
20+
testName: testName,
21+
line: line)
22+
}
23+
}
24+
25+
class PreviewSnapshotTests: XCTestCase {
26+
{% for type in types.based.PreviewProvider %}
27+
func test{{type.name}}() {
28+
assertPreviewSnapshot({{type.name}}.self)
29+
}
30+
31+
func test{{type.name}}_dark() {
32+
assertPreviewSnapshot({{type.name}}.self, isDarkMode: true)
33+
}
34+
35+
{% endfor %}
36+
}

0 commit comments

Comments
 (0)