Skip to content

Commit 4deab4e

Browse files
authored
Added Snapshot Tests (#38)
* Added snapshot tests * github action run test * fixed api key secret updated test action * updated to use latest xcode * reformated lint file * clean up * imporved execution speed swift format * swiftformat removed test folder * resolved code indentation issues in tests * updated swift format action to use the one present by xcode
1 parent 35b70da commit 4deab4e

File tree

19 files changed

+2980
-24
lines changed

19 files changed

+2980
-24
lines changed

.github/workflows/run-test.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Xcode Build and Test
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- develop
8+
pull_request:
9+
branches:
10+
- main
11+
- develop
12+
workflow_dispatch:
13+
14+
jobs:
15+
test:
16+
name: Run Xcode Tests on iPhone 16 (iOS 18.1)
17+
# macOS-latest should ideally include Xcode 16+, but we'll ensure it.
18+
runs-on: macos-latest
19+
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v4
23+
24+
- name: Select Xcode 16.x for iOS 18.1 support
25+
uses: maxim-lobanov/setup-xcode@v1
26+
with:
27+
xcode-version: 'latest-stable' # Try 'latest-stable' first
28+
29+
- name: Display active Xcode version after selection
30+
run: xcodebuild -version
31+
# Verify here that Xcode 16.x (or newer) is now active.
32+
33+
- name: List available iOS Simulators and their OS versions
34+
# Now, this should show iPhone 16 with iOS 18.x if Xcode 16 is active.
35+
run: xcrun simctl list devices
36+
37+
- name: Clean Xcode Derived Data
38+
# Always good practice for CI to ensure a fresh build.
39+
run: xcodebuild clean -scheme BuilderIO -destination "platform=iOS Simulator,name=iPhone 16,OS=18.1"
40+
41+
- name: Build and Test Xcode Project on iPhone 16 (iOS 18.1)
42+
run: |
43+
SIMULATOR_NAME="iPhone 16"
44+
# Ensure this OS version is available with your selected Xcode 16.x
45+
OS_VERSION="18.1"
46+
47+
DESTINATION="platform=iOS Simulator,name=${SIMULATOR_NAME},OS=${OS_VERSION}"
48+
49+
echo "Attempting to build and test on destination: $DESTINATION"
50+
51+
xcodebuild test -scheme BuilderIO \
52+
-destination "$DESTINATION" \
53+
CODE_SIGNING_ALLOWED=NO \
54+
CODE_SIGNING_REQUIRED=NO
55+
# If you want to capture test results:
56+
# -resultBundlePath "output/TestResults.xcresult"

.github/workflows/swift-format.yml

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,21 @@ jobs:
1717
steps:
1818
- name: Checkout Repository
1919
uses: actions/checkout@v4
20-
21-
- name: Set up Swift
22-
uses: swift-actions/setup-swift@v1
20+
21+
- name: Select Xcode 16.x for swift-format support
22+
uses: maxim-lobanov/setup-xcode@v1
2323
with:
24-
swift-version: "5.9"
24+
xcode-version: 'latest-stable' # Aims for Xcode 16.x or newer
2525

26-
- name: Build swift-format
26+
- name: Verify Xcode version and swift-format availability
2727
run: |
28-
git clone https://github.com/apple/swift-format.git
29-
cd swift-format
30-
swift build -c release
31-
echo "$(pwd)/.build/release" >> $GITHUB_PATH
28+
echo "Active Xcode version:"
29+
xcodebuild -version
30+
echo "swift-format path (should be within Xcode toolchain):"
31+
xcrun --find swift-format || echo "swift-format not found via xcrun, check Xcode version."
32+
3233
3334
- name: Run swift-format lint (will fail on issues)
3435
run: |
35-
swift-format lint --recursive Sources Tests
36+
xcrun swift-format lint --recursive Sources Tests --ignore-unparsable-files
37+

Package.resolved

Lines changed: 45 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,51 @@
44
import PackageDescription
55

66
let package = Package(
7-
name: "BuilderIO",
7+
name: "BuilderIO", // Good: Clear, concise name matching the library
8+
89
platforms: [
9-
.iOS(.v17),
10+
.iOS(.v17),
1011
],
12+
13+
// --- Products ---
1114
products: [
12-
// Products define the executables and libraries a package produces, and make them visible to other packages.
1315
.library(
1416
name: "BuilderIO",
1517
targets: ["BuilderIO"]
1618
),
1719
],
20+
21+
// --- Dependencies ---
1822
dependencies: [
19-
// Dependencies declare other packages that this package depends on.
20-
// .package(url: /* package url */, from: "1.0.0"),
2123
.package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "5.0.1"),
24+
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.17.0"),
25+
.package(url: "https://github.com/WeTransfer/Mocker.git", .upToNextMajor(from: "3.0.0")),
2226
],
27+
28+
// --- Targets ---
2329
targets: [
24-
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
25-
// Targets can depend on other targets in this package, and on products in packages this package depends on.
30+
// Main library target
2631
.target(
2732
name: "BuilderIO",
2833
dependencies: [
2934
"SwiftyJSON",
3035
],
3136
resources: [
32-
.process("Resources/Fonts")
33-
]
37+
.process("Resources/Fonts")
38+
]
39+
),
40+
41+
// Test target
42+
.testTarget(
43+
name: "BuilderIOTests",
44+
dependencies: [
45+
"BuilderIO", // Dependency on the local library target
46+
"Mocker",
47+
.product(name: "SnapshotTesting", package: "swift-snapshot-testing")
48+
],
49+
resources: [
50+
.process("Resources")
51+
]
3452
),
3553
]
36-
)
54+
)

Sources/BuilderIO/Components/BuilderBlock.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ struct BuilderBlockLayout<Content: View>: View {
102102
// 1. Extract basic layout parameters
103103
let direction = responsiveStyles["flexDirection"] ?? "column"
104104
let wrap = responsiveStyles["flexWrap"] == "wrap" && direction == "row"
105-
let scroll = (responsiveStyles["overflow"] == "auto" || responsiveStyles["overflow"] == "scroll") && direction == "row"
105+
let scroll =
106+
(responsiveStyles["overflow"] == "auto" || responsiveStyles["overflow"] == "scroll")
107+
&& direction == "row"
106108

107109
let justify = responsiveStyles["justifyContent"]
108110
let alignItems = responsiveStyles["alignItems"]

Sources/BuilderIO/Extensions/Font.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ extension Font {
2323
_ = UIFont.registerFont(bundle: .module, fontName: "DMSans", fontExtension: "ttf")
2424
_ = UIFont.registerFont(bundle: .module, fontName: "DMSans-Italic", fontExtension: "ttf")
2525
}
26-
26+
2727
}
2828

2929
extension UIFont {
30-
30+
3131
static func registerFont(bundle: Bundle, fontName: String, fontExtension: String) -> Bool {
3232
guard let fontURL = bundle.url(forResource: fontName, withExtension: fontExtension) else {
3333
fatalError("Couldn't find font \(fontName)")
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import XCTest
2+
import BuilderIO
3+
import SnapshotTesting
4+
import UIKit
5+
import SwiftUI
6+
7+
@MainActor
8+
class BuilderIOPageViewTests: XCTestCase {
9+
10+
static let record = false
11+
12+
override func setUpWithError() throws {
13+
BuilderIOManager.configure(apiKey: "UNITTESTINGAPIKEY", customNavigationScheme: "builderio")
14+
15+
continueAfterFailure = false
16+
}
17+
18+
override func tearDownWithError() throws {
19+
// Clear all mocks after each test
20+
print(" 🚨 Tests deregistered")
21+
22+
BuilderIOMockManager.shared.clearAllMocks()
23+
try super.tearDownWithError()
24+
}
25+
26+
27+
func testTextView() throws {
28+
BuilderIOMockManager.shared.registerMock(for: "/text", with: "text", statusCode: 200)
29+
30+
let hostingController = makeHostingController(for: "/text", width: 375, height: 812)
31+
32+
      let expectation = XCTestExpectation(description: "Wait for view to render")
33+
34+
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
35+
36+
assertSnapshot(matching: hostingController, as: .image, record: BuilderIOPageViewTests.record)
37+
expectation.fulfill()
38+
}
39+
40+
41+
wait(for: [expectation], timeout: 3)
42+
}
43+
44+
func testLayoutsView() throws {
45+
BuilderIOMockManager.shared.registerMock(for: "/layout", with: "layout", statusCode: 200)
46+
47+
let hostingController = makeHostingController(for: "/layout", width: 375, height: 812)
48+
49+
      let expectation = XCTestExpectation(description: "Wait for view to render")
50+
51+
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
52+
53+
assertSnapshot(matching: hostingController, as: .image, record: BuilderIOPageViewTests.record)
54+
expectation.fulfill()
55+
}
56+
57+
58+
wait(for: [expectation], timeout: 3)
59+
}
60+
61+
/// - Returns: A UIHostingController wrapping the SwiftUI Text view.
62+
func makeHostingController(for url: String, width: CGFloat, height: CGFloat) -> UIHostingController<some View> {
63+
let view = BuilderIOPage(url: url, onClickEventHandler: { event in
64+
print("Handle Event Action")
65+
})
66+
67+
let hostingController = UIHostingController(rootView: view.frame(width: 375, height: 812))
68+
hostingController.view.frame = CGRect(x: 0, y: 0, width: 375, height: 812)
69+
70+
let window = UIWindow(frame: hostingController.view.frame)
71+
window.rootViewController = hostingController
72+
window.makeKeyAndVisible()
73+
74+
75+
return hostingController
76+
}
77+
78+
}

0 commit comments

Comments
 (0)