How to use testSwiftUIView_tvOS method of CancellingWKWebViewNavigationDelegate class

Best Swift-snapshot-testing code snippet using CancellingWKWebViewNavigationDelegate.testSwiftUIView_tvOS

Run Swift-snapshot-testing automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

SnapshotKitTests.swift

Source: SnapshotKitTests.swift Github

copy
1import Foundation
2#if canImport(FoundationNetworking)
3import FoundationNetworking
4#endif
5#if canImport(SceneKit)
6import SceneKit
7#endif
8#if canImport(SpriteKit)
9import SpriteKit
10#endif
11#if canImport(SwiftUI)
12import SwiftUI
13#endif
14#if canImport(WebKit)
15import WebKit
16#endif
17#if canImport(UIKit)
18import UIKit.UIView
19#endif
20import XCTest
21
22@testable import SnapshotKit
23
24final class SnapshotKitTests: XCTestCase {
25    override func setUp() {
26        super.setUp()
27        diffTool = "ksdiff"
28//        isRecording = true
29    }
30
31    override func tearDown() {
32        isRecording = false
33        super.tearDown()
34    }
35
36    func testAny() {
37        struct User { let id: Int, name: String, bio: String }
38        let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.")
39        assertSnapshot(matching: user, as: .dump)
40        _assertInlineSnapshot(matching: user, as: .dump, with: """
41        ▿ User
42          - bio: "Blobbed around the world."
43          - id: 1
44          - name: "Blobby"
45        """)
46    }
47
48    func testAnyAsJson() throws {
49        struct User: Encodable { let id: Int, name: String, bio: String }
50        let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.")
51
52        let data = try JSONEncoder().encode(user)
53        let any = try JSONSerialization.jsonObject(with: data, options: [])
54
55        assertSnapshot(matching: any, as: .json)
56    }
57
58    func testAnySnapshotStringConvertible() {
59        assertSnapshot(matching: "a" as Character, as: .dump, named: "character")
60        assertSnapshot(matching: Data("Hello, world!".utf8), as: .dump, named: "data")
61        assertSnapshot(matching: Date(timeIntervalSinceReferenceDate: 0), as: .dump, named: "date")
62        assertSnapshot(matching: NSObject(), as: .dump, named: "nsobject")
63        assertSnapshot(matching: "Hello, world!", as: .dump, named: "string")
64        assertSnapshot(matching: "Hello, world!".dropLast(8), as: .dump, named: "substring")
65        assertSnapshot(matching: URL(string: "https://www.pointfree.co")!, as: .dump, named: "url")
66        // Inline
67        _assertInlineSnapshot(matching: "a" as Character, as: .dump, with: """
68        - "a"
69        """)
70        _assertInlineSnapshot(matching: Data("Hello, world!".utf8), as: .dump, with: """
71        - 13 bytes
72        """)
73        _assertInlineSnapshot(matching: Date(timeIntervalSinceReferenceDate: 0), as: .dump, with: """
74        - 2001-01-01T00:00:00Z
75        """)
76        _assertInlineSnapshot(matching: NSObject(), as: .dump, with: """
77        - <NSObject>
78        """)
79        _assertInlineSnapshot(matching: "Hello, world!", as: .dump, with: """
80        - "Hello, world!"
81        """)
82        _assertInlineSnapshot(matching: "Hello, world!".dropLast(8), as: .dump, with: """
83        - "Hello"
84        """)
85        _assertInlineSnapshot(matching: URL(string: "https://www.pointfree.co")!, as: .dump, with: """
86        - https://www.pointfree.co
87        """)
88    }
89
90    func testDeterministicDictionaryAndSetSnapshots() {
91        struct Person: Hashable { let name: String }
92        struct DictionarySetContainer { let dict: [String: Int], set: Set<Person> }
93        let set = DictionarySetContainer(
94            dict: ["c": 3, "a": 1, "b": 2],
95            set: [.init(name: "Brandon"), .init(name: "Stephen")]
96        )
97        assertSnapshot(matching: set, as: .dump)
98        _assertInlineSnapshot(matching: set, as: .dump, with: """
99        ▿ DictionarySetContainer
100          ▿ dict: 3 key/value pairs
101            ▿ (2 elements)
102              - key: "a"
103              - value: 1
104            ▿ (2 elements)
105              - key: "b"
106              - value: 2
107            ▿ (2 elements)
108              - key: "c"
109              - value: 3
110          ▿ set: 2 members
111            ▿ Person
112              - name: "Brandon"
113            ▿ Person
114              - name: "Stephen"
115        """)
116    }
117
118    func testCaseIterable() {
119        enum Direction: String, CaseIterable {
120            case up, down, left, right
121            var rotatedLeft: Direction {
122                switch self {
123                case .up: return .left
124                case .down: return .right
125                case .left: return .down
126                case .right: return .up
127                }
128            }
129        }
130
131        assertSnapshot(
132            matching: { $0.rotatedLeft },
133            as: Snapshotting<Direction, String>.func(into: .description)
134        )
135    }
136
137    #if os(iOS) || os(tvOS) || os(macOS)
138    func testCGPath() {
139        let path = CGPath.heart
140
141        let osName: String
142        #if os(iOS)
143        osName = "iOS"
144        #elseif os(tvOS)
145        osName = "tvOS"
146        #elseif os(macOS)
147        osName = "macOS"
148        #endif
149
150        assertSnapshot(matching: path, as: .image, named: osName)
151        assertSnapshot(matching: path, as: .elementsDescription, named: osName)
152    }
153    #endif
154
155    func testData() {
156        let data = Data([0xDE, 0xAD, 0xBE, 0xEF])
157
158        assertSnapshot(matching: data, as: .data)
159    }
160
161    func testEncodable() {
162        struct User: Encodable { let id: Int, name: String, bio: String }
163        let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.")
164
165        assertSnapshot(matching: user, as: .json)
166        assertSnapshot(matching: user, as: .plist)
167    }
168
169    func testMultipleSnapshots() {
170        assertSnapshot(matching: [1], as: .dump)
171        assertSnapshot(matching: [1, 2], as: .dump)
172    }
173
174    func testNamedAssertion() {
175        struct User { let id: Int, name: String, bio: String }
176        let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.")
177        assertSnapshot(matching: user, as: .dump, named: "named")
178    }
179
180    #if os(macOS)
181    func testNSBezierPath() {
182        let path = NSBezierPath.heart
183
184        assertSnapshot(matching: path, as: .image, named: "macOS")
185        assertSnapshot(matching: path, as: .elementsDescription, named: "macOS")
186    }
187
188    func testNSView() {
189        let view = NSView()
190        view.frame = CGRect(origin: .zero, size: .init(width: 10, height: 10))
191        view.wantsLayer = true
192        view.layer?.backgroundColor = NSColor.green.cgColor
193        view.layer?.cornerRadius = 5
194
195        assertSnapshot(matching: view, as: .image)
196        assertSnapshot(matching: view, as: .recursiveDescription)
197    }
198    #endif
199
200    #if os(iOS) || os(macOS) || os(tvOS)
201    func testPrecision() {
202        #if os(iOS) || os(tvOS)
203        let label = UILabel()
204        #if os(iOS)
205        label.frame = CGRect(origin: .zero, size: CGSize(width: 43.5, height: 20.5))
206        #elseif os(tvOS)
207        label.frame = CGRect(origin: .zero, size: CGSize(width: 98, height: 46))
208        #endif
209        label.backgroundColor = .white
210        #elseif os(macOS)
211        let label = NSTextField()
212        label.frame = CGRect(origin: .zero, size: CGSize(width: 37, height: 16))
213        label.backgroundColor = .white
214        label.textColor = .black
215        label.isBezeled = false
216        label.isEditable = false
217        #endif
218
219        label.text = "Hello."
220        assertSnapshot(matching: label, as: .image(precision: 0.9), named: platform)
221        label.text = "Hello"
222        assertSnapshot(matching: label, as: .image(precision: 0.9), named: platform)
223    }
224    #endif
225
226    #if os(iOS) || os(macOS) || os(tvOS)
227    func testSCNView() {
228        XCTExpectFailure()
229        XCTFail("Whether the test passes or fails, it crashes in addition because a host app seems to be required")
230        if ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { return }
231
232        let scene = SCNScene()
233
234        let sphereGeometry = SCNSphere(radius: 3)
235        sphereGeometry.segmentCount = 200
236        let sphereNode = SCNNode(geometry: sphereGeometry)
237        sphereNode.position = SCNVector3Zero
238        scene.rootNode.addChildNode(sphereNode)
239
240        sphereGeometry.firstMaterial?.diffuse.contents = URL(fileURLWithPath: String(#file), isDirectory: false)
241            .deletingLastPathComponent()
242            .appendingPathComponent("__Fixtures__/earth.png")
243
244        let cameraNode = SCNNode()
245        cameraNode.camera = SCNCamera()
246        cameraNode.position = SCNVector3Make(0, 0, 8)
247        scene.rootNode.addChildNode(cameraNode)
248
249        let omniLight = SCNLight()
250        omniLight.type = .omni
251        let omniLightNode = SCNNode()
252        omniLightNode.light = omniLight
253        omniLightNode.position = SCNVector3Make(10, 10, 10)
254        scene.rootNode.addChildNode(omniLightNode)
255
256        assertSnapshot(
257            matching: scene,
258            as: .image(size: .init(width: 500, height: 500)),
259            named: platform
260        )
261    }
262    #endif
263
264    #if os(iOS) || os(macOS) || os(tvOS)
265    func testSKView() {
266        XCTExpectFailure()
267        XCTFail("Whether the test passes or fails, it crashes in addition because a host app seems to be required")
268        if ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { return }
269
270        let scene = SKScene(size: .init(width: 50, height: 50))
271        let node = SKShapeNode(circleOfRadius: 15)
272        node.fillColor = .red
273        node.position = .init(x: 25, y: 25)
274        scene.addChild(node)
275
276        assertSnapshot(
277            matching: scene,
278            as: .image(size: .init(width: 50, height: 50)),
279            named: platform
280        )
281    }
282    #endif
283
284    #if os(iOS) || os(tvOS)
285    func testTraits() {
286        let viewController = TestViewController()
287
288        #if os(iOS)
289        assertSnapshot(matching: viewController, as: .image(on: .iPhoneSe), named: "iphone-se")
290        assertSnapshot(matching: viewController, as: .image(on: .iPhone8), named: "iphone-8")
291        assertSnapshot(matching: viewController, as: .image(on: .iPhone8Plus), named: "iphone-8-plus")
292        assertSnapshot(matching: viewController, as: .image(on: .iPhoneX), named: "iphone-x")
293        assertSnapshot(matching: viewController, as: .image(on: .iPhoneXr), named: "iphone-xr")
294        assertSnapshot(matching: viewController, as: .image(on: .iPhoneXsMax), named: "iphone-xs-max")
295        assertSnapshot(matching: viewController, as: .image(on: .iPadMini), named: "ipad-mini")
296        assertSnapshot(matching: viewController, as: .image(on: .iPad9_7), named: "ipad-9-7")
297        assertSnapshot(matching: viewController, as: .image(on: .iPad10_2), named: "ipad-10-2")
298        assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5), named: "ipad-pro-10-5")
299        assertSnapshot(matching: viewController, as: .image(on: .iPadPro11), named: "ipad-pro-11")
300        assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9), named: "ipad-pro-12-9")
301
302        assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPhoneSe), named: "iphone-se")
303        assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPhone8), named: "iphone-8")
304        assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPhone8Plus), named: "iphone-8-plus")
305        assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPhoneX), named: "iphone-x")
306        assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPhoneXr), named: "iphone-xr")
307        assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPhoneXsMax), named: "iphone-xs-max")
308        assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPadMini), named: "ipad-mini")
309        assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPad9_7), named: "ipad-9-7")
310        assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPad10_2), named: "ipad-10-2")
311        assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPadPro10_5), named: "ipad-pro-10-5")
312        assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPadPro11), named: "ipad-pro-11")
313        assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPadPro12_9), named: "ipad-pro-12-9")
314
315        assertSnapshot(matching: viewController, as: .image(on: .iPhoneSe(.portrait)), named: "iphone-se")
316        assertSnapshot(matching: viewController, as: .image(on: .iPhone8(.portrait)), named: "iphone-8")
317        assertSnapshot(matching: viewController, as: .image(on: .iPhone8Plus(.portrait)), named: "iphone-8-plus")
318        assertSnapshot(matching: viewController, as: .image(on: .iPhoneX(.portrait)), named: "iphone-x")
319        assertSnapshot(matching: viewController, as: .image(on: .iPhoneXr(.portrait)), named: "iphone-xr")
320        assertSnapshot(matching: viewController, as: .image(on: .iPhoneXsMax(.portrait)), named: "iphone-xs-max")
321        assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.landscape)), named: "ipad-mini")
322        assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.landscape)), named: "ipad-9-7")
323        assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.landscape)), named: "ipad-10-2")
324        assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.landscape)), named: "ipad-pro-10-5")
325        assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.landscape)), named: "ipad-pro-11")
326        assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.landscape)), named: "ipad-pro-12-9")
327
328        assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.landscape(splitView: .oneThird))), named: "ipad-mini-33-split-landscape")
329        assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.landscape(splitView: .oneHalf))), named: "ipad-mini-50-split-landscape")
330        assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.landscape(splitView: .twoThirds))), named: "ipad-mini-66-split-landscape")
331        assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.portrait(splitView: .oneThird))), named: "ipad-mini-33-split-portrait")
332        assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.portrait(splitView: .twoThirds))), named: "ipad-mini-66-split-portrait")
333
334        assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.landscape(splitView: .oneThird))), named: "ipad-9-7-33-split-landscape")
335        assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.landscape(splitView: .oneHalf))), named: "ipad-9-7-50-split-landscape")
336        assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.landscape(splitView: .twoThirds))), named: "ipad-9-7-66-split-landscape")
337        assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.portrait(splitView: .oneThird))), named: "ipad-9-7-33-split-portrait")
338        assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.portrait(splitView: .twoThirds))), named: "ipad-9-7-66-split-portrait")
339
340        assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.landscape(splitView: .oneThird))), named: "ipad-10-2-split-landscape")
341        assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.landscape(splitView: .oneHalf))), named: "ipad-10-2-50-split-landscape")
342        assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.landscape(splitView: .twoThirds))), named: "ipad-10-2-66-split-landscape")
343        assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.portrait(splitView: .oneThird))), named: "ipad-10-2-33-split-portrait")
344        assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.portrait(splitView: .twoThirds))), named: "ipad-10-2-66-split-portrait")
345
346        assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.landscape(splitView: .oneThird))), named: "ipad-pro-10inch-33-split-landscape")
347        assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.landscape(splitView: .oneHalf))), named: "ipad-pro-10inch-50-split-landscape")
348        assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.landscape(splitView: .twoThirds))), named: "ipad-pro-10inch-66-split-landscape")
349        assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.portrait(splitView: .oneThird))), named: "ipad-pro-10inch-33-split-portrait")
350        assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.portrait(splitView: .twoThirds))), named: "ipad-pro-10inch-66-split-portrait")
351
352        assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.landscape(splitView: .oneThird))), named: "ipad-pro-11inch-33-split-landscape")
353        assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.landscape(splitView: .oneHalf))), named: "ipad-pro-11inch-50-split-landscape")
354        assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.landscape(splitView: .twoThirds))), named: "ipad-pro-11inch-66-split-landscape")
355        assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.portrait(splitView: .oneThird))), named: "ipad-pro-11inch-33-split-portrait")
356        assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.portrait(splitView: .twoThirds))), named: "ipad-pro-11inch-66-split-portrait")
357
358        assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.landscape(splitView: .oneThird))), named: "ipad-pro-12inch-33-split-landscape")
359        assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.landscape(splitView: .oneHalf))), named: "ipad-pro-12inch-50-split-landscape")
360        assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.landscape(splitView: .twoThirds))), named: "ipad-pro-12inch-66-split-landscape")
361        assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.portrait(splitView: .oneThird))), named: "ipad-pro-12inch-33-split-portrait")
362        assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.portrait(splitView: .twoThirds))), named: "ipad-pro-12inch-66-split-portrait")
363
364        assertSnapshot(matching: viewController, as: .image(on: .iPhoneSe(.landscape)), named: "iphone-se-alternative")
365        assertSnapshot(matching: viewController, as: .image(on: .iPhone8(.landscape)), named: "iphone-8-alternative")
366        assertSnapshot(matching: viewController, as: .image(on: .iPhone8Plus(.landscape)), named: "iphone-8-plus-alternative")
367        assertSnapshot(matching: viewController, as: .image(on: .iPhoneX(.landscape)), named: "iphone-x-alternative")
368        assertSnapshot(matching: viewController, as: .image(on: .iPhoneXr(.landscape)), named: "iphone-xr-alternative")
369        assertSnapshot(matching: viewController, as: .image(on: .iPhoneXsMax(.landscape)), named: "iphone-xs-max-alternative")
370        assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.portrait)), named: "ipad-mini-alternative")
371        assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.portrait)), named: "ipad-9-7-alternative")
372        assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.portrait)), named: "ipad-10-2-alternative")
373        assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.portrait)), named: "ipad-pro-10-5-alternative")
374        assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.portrait)), named: "ipad-pro-11-alternative")
375        assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.portrait)), named: "ipad-pro-12-9-alternative")
376
377        allContentSizes.forEach { name, contentSize in
378            assertSnapshot(
379                matching: viewController,
380                as: .image(on: .iPhoneSe, traits: .init(preferredContentSizeCategory: contentSize)),
381                named: "iphone-se-\(name)"
382            )
383        }
384        #elseif os(tvOS)
385        assertSnapshot(matching: viewController, as: .image(on: .tv), named: "tv")
386        assertSnapshot(matching: viewController, as: .image(on: .tv4K), named: "tv4k")
387        #endif
388    }
389    #endif
390
391    #if os(iOS)
392    func testCollectionViewsWithMultipleScreenSizes() {
393        final class CollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
394            let flowLayout: UICollectionViewFlowLayout = {
395                let layout = UICollectionViewFlowLayout()
396                layout.scrollDirection = .horizontal
397                layout.minimumLineSpacing = 20
398                return layout
399            }()
400
401            lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
402
403            override func viewDidLoad() {
404                super.viewDidLoad()
405
406                view.backgroundColor = .white
407                view.addSubview(collectionView)
408
409                collectionView.backgroundColor = .white
410                collectionView.dataSource = self
411                collectionView.delegate = self
412                collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
413                collectionView.translatesAutoresizingMaskIntoConstraints = false
414
415                NSLayoutConstraint.activate([
416                    collectionView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
417                    collectionView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
418                    collectionView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
419                    collectionView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor)
420                ])
421
422                collectionView.reloadData()
423            }
424
425            override func viewDidLayoutSubviews() {
426                super.viewDidLayoutSubviews()
427                collectionView.collectionViewLayout.invalidateLayout()
428            }
429
430            override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
431                super.traitCollectionDidChange(previousTraitCollection)
432                collectionView.collectionViewLayout.invalidateLayout()
433            }
434
435            func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
436                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
437                cell.contentView.backgroundColor = .orange
438                return cell
439            }
440
441            func collectionView(_: UICollectionView, numberOfItemsInSection _: Int) -> Int {
442                20
443            }
444
445            func collectionView(
446                _ collectionView: UICollectionView,
447                layout _: UICollectionViewLayout,
448                sizeForItemAt _: IndexPath
449            ) -> CGSize {
450                CGSize(
451                    width: min(collectionView.frame.width - 50, 300),
452                    height: collectionView.frame.height
453                )
454            }
455        }
456
457        let viewController = CollectionViewController()
458
459        assertSnapshots(matching: viewController, as: [
460            "ipad": .image(on: .iPadPro12_9),
461            "iphoneSe": .image(on: .iPhoneSe),
462            "iphone8": .image(on: .iPhone8),
463            "iphoneMax": .image(on: .iPhoneXsMax)
464        ])
465    }
466
467    func testUIView() {
468        let view = UIView()
469        view.frame = .init(origin: .zero, size: .init(width: 20, height: 20))
470        view.backgroundColor = .cyan
471        view.layer.cornerRadius = 10
472
473        assertSnapshot(matching: view, as: .image)
474        assertSnapshot(matching: view, as: .recursiveDescription)
475    }
476
477    func testUIViewController() {
478        let view = UIView()
479        view.backgroundColor = .cyan
480        view.layer.cornerRadius = 10
481        view.translatesAutoresizingMaskIntoConstraints = false
482
483        let viewController = UIViewController()
484        viewController.view.addSubview(view)
485
486        NSLayoutConstraint.activate([
487            view.topAnchor.constraint(equalTo: viewController.view.topAnchor),
488            view.bottomAnchor.constraint(equalTo: viewController.view.bottomAnchor),
489            view.leftAnchor.constraint(equalTo: viewController.view.leftAnchor),
490            view.rightAnchor.constraint(equalTo: viewController.view.rightAnchor)
491        ])
492
493        assertSnapshot(matching: viewController, as: .image(size: .init(width: 100, height: 100)))
494    }
495
496    func testUIViewControllerLifeCycle() {
497        class ViewController: UIViewController {
498            let viewDidLoadExpectation: XCTestExpectation
499            let viewWillAppearExpectation: XCTestExpectation
500            let viewDidAppearExpectation: XCTestExpectation
501            let viewWillDisappearExpectation: XCTestExpectation
502            let viewDidDisappearExpectation: XCTestExpectation
503            init(
504                viewDidLoadExpectation: XCTestExpectation,
505                viewWillAppearExpectation: XCTestExpectation,
506                viewDidAppearExpectation: XCTestExpectation,
507                viewWillDisappearExpectation: XCTestExpectation,
508                viewDidDisappearExpectation: XCTestExpectation
509            ) {
510                self.viewDidLoadExpectation = viewDidLoadExpectation
511                self.viewWillAppearExpectation = viewWillAppearExpectation
512                self.viewDidAppearExpectation = viewDidAppearExpectation
513                self.viewWillDisappearExpectation = viewWillDisappearExpectation
514                self.viewDidDisappearExpectation = viewDidDisappearExpectation
515                super.init(nibName: nil, bundle: nil)
516            }
517
518            @available(*, unavailable)
519            required init?(coder _: NSCoder) {
520                fatalError("init(coder:) has not been implemented")
521            }
522
523            override func viewDidLoad() {
524                super.viewDidLoad()
525                viewDidLoadExpectation.fulfill()
526            }
527
528            override func viewWillAppear(_ animated: Bool) {
529                super.viewWillAppear(animated)
530                viewWillAppearExpectation.fulfill()
531            }
532
533            override func viewDidAppear(_ animated: Bool) {
534                super.viewDidAppear(animated)
535                viewDidAppearExpectation.fulfill()
536            }
537
538            override func viewWillDisappear(_ animated: Bool) {
539                super.viewWillDisappear(animated)
540                viewWillDisappearExpectation.fulfill()
541            }
542
543            override func viewDidDisappear(_ animated: Bool) {
544                super.viewDidDisappear(animated)
545                viewDidDisappearExpectation.fulfill()
546            }
547        }
548
549        let viewDidLoadExpectation = expectation(description: "viewDidLoad")
550        let viewWillAppearExpectation = expectation(description: "viewWillAppear")
551        let viewDidAppearExpectation = expectation(description: "viewDidAppear")
552        let viewWillDisappearExpectation = expectation(description: "viewWillDisappear")
553        let viewDidDisappearExpectation = expectation(description: "viewDidDisappear")
554        viewWillAppearExpectation.expectedFulfillmentCount = 2
555        viewDidAppearExpectation.expectedFulfillmentCount = 2
556        viewWillDisappearExpectation.expectedFulfillmentCount = 2
557        viewDidDisappearExpectation.expectedFulfillmentCount = 2
558
559        let viewController = ViewController(
560            viewDidLoadExpectation: viewDidLoadExpectation,
561            viewWillAppearExpectation: viewWillAppearExpectation,
562            viewDidAppearExpectation: viewDidAppearExpectation,
563            viewWillDisappearExpectation: viewWillDisappearExpectation,
564            viewDidDisappearExpectation: viewDidDisappearExpectation
565        )
566
567        assertSnapshot(matching: viewController, as: .image)
568        assertSnapshot(matching: viewController, as: .image)
569
570        wait(
571            for: [
572                viewDidLoadExpectation,
573                viewWillAppearExpectation,
574                viewDidAppearExpectation,
575                viewWillDisappearExpectation,
576                viewDidDisappearExpectation
577            ],
578            timeout: 1,
579            enforceOrder: true
580        )
581    }
582
583    func testCALayer() {
584        let layer = CALayer()
585        layer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
586        layer.backgroundColor = UIColor.red.cgColor
587        layer.borderWidth = 4.0
588        layer.borderColor = UIColor.black.cgColor
589        assertSnapshot(matching: layer, as: .image)
590    }
591
592    func testCALayerWithGradient() {
593        let baseLayer = CALayer()
594        baseLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
595        let gradientLayer = CAGradientLayer()
596        gradientLayer.colors = [UIColor.red.cgColor, UIColor.yellow.cgColor]
597        gradientLayer.frame = baseLayer.frame
598        baseLayer.addSublayer(gradientLayer)
599        assertSnapshot(matching: baseLayer, as: .image(subpixelThreshold: 1))
600    }
601
602    func testViewControllerHierarchy() {
603        let page = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
604        page.setViewControllers([UIViewController()], direction: .forward, animated: false)
605        let tab = UITabBarController()
606        tab.viewControllers = [
607            UINavigationController(rootViewController: page),
608            UINavigationController(rootViewController: UIViewController()),
609            UINavigationController(rootViewController: UIViewController()),
610            UINavigationController(rootViewController: UIViewController()),
611            UINavigationController(rootViewController: UIViewController())
612        ]
613        assertSnapshot(matching: tab, as: .hierarchy)
614    }
615    #endif
616
617    #if os(iOS) || os(tvOS)
618    func testUIBezierPath() {
619        let path = UIBezierPath.heart
620
621        let osName: String
622        #if os(iOS)
623        osName = "iOS"
624        #elseif os(tvOS)
625        osName = "tvOS"
626        #endif
627
628        assertSnapshot(matching: path, as: .image, named: osName)
629        assertSnapshot(matching: path, as: .elementsDescription, named: osName)
630    }
631    #endif
632
633    func testURLRequest() {
634        var get = URLRequest(url: URL(string: "https://www.pointfree.co/")!)
635        get.addValue("pf_session={}", forHTTPHeaderField: "Cookie")
636        get.addValue("text/html", forHTTPHeaderField: "Accept")
637        get.addValue("application/json", forHTTPHeaderField: "Content-Type")
638        assertSnapshot(matching: get, as: .raw, named: "get")
639        assertSnapshot(matching: get, as: .curl, named: "get-curl")
640
641        var getWithQuery = URLRequest(url: URL(string: "https://www.pointfree.co?key_2=value_2&key_1=value_1&key_3=value_3")!)
642        getWithQuery.addValue("pf_session={}", forHTTPHeaderField: "Cookie")
643        getWithQuery.addValue("text/html", forHTTPHeaderField: "Accept")
644        getWithQuery.addValue("application/json", forHTTPHeaderField: "Content-Type")
645        assertSnapshot(matching: getWithQuery, as: .raw, named: "get-with-query")
646        assertSnapshot(matching: getWithQuery, as: .curl, named: "get-with-query-curl")
647
648        var post = URLRequest(url: URL(string: "https://www.pointfree.co/subscribe")!)
649        post.httpMethod = "POST"
650        post.addValue("pf_session={\"user_id\":\"0\"}", forHTTPHeaderField: "Cookie")
651        post.addValue("text/html", forHTTPHeaderField: "Accept")
652        post.httpBody = Data("pricing[billing]=monthly&pricing[lane]=individual".utf8)
653        assertSnapshot(matching: post, as: .raw, named: "post")
654        assertSnapshot(matching: post, as: .curl, named: "post-curl")
655
656        var postWithJSON = URLRequest(url: URL(string: "http://dummy.restapiexample.com/api/v1/create")!)
657        postWithJSON.httpMethod = "POST"
658        postWithJSON.addValue("application/json", forHTTPHeaderField: "Content-Type")
659        postWithJSON.addValue("application/json", forHTTPHeaderField: "Accept")
660        postWithJSON.httpBody = Data("{\"name\":\"tammy134235345235\", \"salary\":0, \"age\":\"tammy133\"}".utf8)
661        assertSnapshot(matching: postWithJSON, as: .raw, named: "post-with-json")
662        assertSnapshot(matching: postWithJSON, as: .curl, named: "post-with-json-curl")
663
664        var head = URLRequest(url: URL(string: "https://www.pointfree.co/")!)
665        head.httpMethod = "HEAD"
666        head.addValue("pf_session={}", forHTTPHeaderField: "Cookie")
667        assertSnapshot(matching: head, as: .raw, named: "head")
668        assertSnapshot(matching: head, as: .curl, named: "head-curl")
669
670        post = URLRequest(url: URL(string: "https://www.pointfree.co/subscribe")!)
671        post.httpMethod = "POST"
672        post.addValue("pf_session={\"user_id\":\"0\"}", forHTTPHeaderField: "Cookie")
673        post.addValue("application/json", forHTTPHeaderField: "Accept")
674        post.httpBody = Data("""
675        {"pricing": {"lane": "individual","billing": "monthly"}}
676        """.utf8)
677        _assertInlineSnapshot(matching: post, as: .raw(pretty: true), with: """
678        POST https://www.pointfree.co/subscribe
679        Accept: application/json
680        Cookie: pf_session={"user_id":"0"}
681
682        {
683          "pricing" : {
684            "billing" : "monthly",
685            "lane" : "individual"
686          }
687        }
688        """)
689    }
690
691    #if os(iOS) || os(macOS)
692    func testWebView() throws {
693        let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false)
694            .deletingLastPathComponent()
695            .appendingPathComponent("__Fixtures__/pointfree.html")
696        let html = try String(contentsOf: fixtureUrl)
697        let webView = WKWebView()
698        webView.loadHTMLString(html, baseURL: nil)
699
700        assertSnapshot(
701            matching: webView,
702            as: .image(precision: 0.9, size: .init(width: 800, height: 600)),
703            named: platform
704        )
705    }
706    #endif
707
708    #if os(iOS) || os(tvOS)
709    func testViewWithZeroHeightOrWidth() {
710        var rect = CGRect(x: 0, y: 0, width: 350, height: 0)
711        var view = UIView(frame: rect)
712        view.backgroundColor = .red
713        assertSnapshot(matching: view, as: .image, named: "noHeight")
714
715        rect = CGRect(x: 0, y: 0, width: 0, height: 350)
716        view = UIView(frame: rect)
717        view.backgroundColor = .green
718        assertSnapshot(matching: view, as: .image, named: "noWidth")
719
720        rect = CGRect(x: 0, y: 0, width: 0, height: 0)
721        view = UIView(frame: rect)
722        view.backgroundColor = .blue
723        assertSnapshot(matching: view, as: .image, named: "noWidth.noHeight")
724    }
725    #endif
726
727    #if os(iOS)
728    func testEmbeddedWebView() throws {
729        let label = UILabel()
730        label.text = "Hello, Blob!"
731
732        let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false)
733            .deletingLastPathComponent()
734            .appendingPathComponent("__Fixtures__/pointfree.html")
735        let html = try String(contentsOf: fixtureUrl)
736        let webView = WKWebView()
737        webView.loadHTMLString(html, baseURL: nil)
738        webView.isHidden = true
739
740        let stackView = UIStackView(arrangedSubviews: [label, webView])
741        stackView.axis = .vertical
742
743        assertSnapshot(
744            matching: stackView,
745            as: .image(size: .init(width: 800, height: 600)),
746            named: platform
747        )
748    }
749    #endif
750
751    #if os(iOS) || os(macOS)
752    final class ManipulatingWKWebViewNavigationDelegate: NSObject, WKNavigationDelegate {
753        func webView(_ webView: WKWebView, didFinish _: WKNavigation!) {
754            webView.evaluateJavaScript("document.body.children[0].classList.remove(\"hero\")") // Change layout
755        }
756    }
757
758    func testWebViewWithManipulatingNavigationDelegate() throws {
759        let manipulatingWKWebViewNavigationDelegate = ManipulatingWKWebViewNavigationDelegate()
760        let webView = WKWebView()
761        webView.navigationDelegate = manipulatingWKWebViewNavigationDelegate
762
763        let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false)
764            .deletingLastPathComponent()
765            .appendingPathComponent("__Fixtures__/pointfree.html")
766        let html = try String(contentsOf: fixtureUrl)
767        webView.loadHTMLString(html, baseURL: nil)
768
769        assertSnapshot(
770            matching: webView,
771            as: .image(precision: 0.9, size: .init(width: 800, height: 600)),
772            named: platform
773        )
774
775        _ = manipulatingWKWebViewNavigationDelegate
776    }
777
778    final class CancellingWKWebViewNavigationDelegate: NSObject, WKNavigationDelegate {
779        func webView(
780            _: WKWebView,
781            decidePolicyFor _: WKNavigationAction,
782            decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
783        ) {
784            decisionHandler(.cancel)
785        }
786    }
787
788    func testWebViewWithCancellingNavigationDelegate() throws {
789        let cancellingWKWebViewNavigationDelegate = CancellingWKWebViewNavigationDelegate()
790        let webView = WKWebView()
791        webView.navigationDelegate = cancellingWKWebViewNavigationDelegate
792
793        let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false)
794            .deletingLastPathComponent()
795            .appendingPathComponent("__Fixtures__/pointfree.html")
796        let html = try String(contentsOf: fixtureUrl)
797        webView.loadHTMLString(html, baseURL: nil)
798
799        assertSnapshot(
800            matching: webView,
801            as: .image(size: .init(width: 800, height: 600)),
802            named: platform
803        )
804
805        _ = cancellingWKWebViewNavigationDelegate
806    }
807    #endif
808
809    #if os(iOS)
810    @available(iOS 13.0, *)
811    func testSwiftUIView_iOS() {
812        let view = TestView().environment(\.colorScheme, .light)
813        let precision: Float = 0.99
814
815        assertSnapshot(matching: view, as: .image(precision: precision, traits: .init(userInterfaceStyle: .light)))
816        assertSnapshot(matching: view, as: .image(precision: precision, layout: .sizeThatFits, traits: .init(userInterfaceStyle: .light)), named: "size-that-fits")
817        assertSnapshot(matching: view, as: .image(precision: precision, layout: .fixed(width: 200, height: 100), traits: .init(userInterfaceStyle: .light)), named: "fixed")
818        assertSnapshot(matching: view, as: .image(precision: precision, layout: .device(config: .iPhoneSe), traits: .init(userInterfaceStyle: .light)), named: "device")
819    }
820    #endif
821
822    #if os(macOS)
823    @available(macOS 11.0, *)
824    func testSwiftUIView_macOS() {
825        let view = TestView().environment(\.colorScheme, .light)
826
827        assertSnapshot(matching: view, as: .image(size: .init(width: 100, height: 50), precision: 0.98))
828    }
829    #endif
830
831    #if os(tvOS)
832    @available(tvOS 13.0, *)
833    func testSwiftUIView_tvOS() {
834        let view = TestView().environment(\.colorScheme, .light)
835        let precision: Float = 0.98
836
837        assertSnapshot(matching: view, as: .image(precision: precision))
838        assertSnapshot(matching: view, as: .image(precision: precision, layout: .sizeThatFits), named: "size-that-fits")
839        assertSnapshot(matching: view, as: .image(precision: precision, layout: .fixed(width: 300, height: 100)), named: "fixed")
840        assertSnapshot(matching: view, as: .image(precision: precision, layout: .device(config: .tv)), named: "device")
841    }
842    #endif
843}
844
845#if os(iOS) || os(tvOS)
846class TestViewController: UIViewController {
847    override func viewDidLoad() {
848        super.viewDidLoad()
849
850        let topView = UIView()
851        let leadingView = UIView()
852        let trailingView = UIView()
853        let bottomView = UIView()
854
855        navigationItem.leftBarButtonItem = .init(barButtonSystemItem: .add, target: nil, action: nil)
856
857        view.backgroundColor = .white
858
859        topView.backgroundColor = .blue
860        leadingView.backgroundColor = .green
861        trailingView.backgroundColor = .red
862        bottomView.backgroundColor = .magenta
863
864        topView.translatesAutoresizingMaskIntoConstraints = false
865        leadingView.translatesAutoresizingMaskIntoConstraints = false
866        trailingView.translatesAutoresizingMaskIntoConstraints = false
867        bottomView.translatesAutoresizingMaskIntoConstraints = false
868
869        view.addSubview(topView)
870        view.addSubview(leadingView)
871        view.addSubview(trailingView)
872        view.addSubview(bottomView)
873
874        let constant: CGFloat = 50
875
876        NSLayoutConstraint.activate([
877            topView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
878            topView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
879            topView.widthAnchor.constraint(equalToConstant: constant),
880            topView.heightAnchor.constraint(equalToConstant: constant),
881            leadingView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
882            leadingView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor),
883            leadingView.widthAnchor.constraint(equalToConstant: constant),
884            leadingView.heightAnchor.constraint(equalToConstant: constant),
885            trailingView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
886            trailingView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor),
887            trailingView.widthAnchor.constraint(equalToConstant: constant),
888            trailingView.heightAnchor.constraint(equalToConstant: constant),
889            bottomView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
890            bottomView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
891            bottomView.widthAnchor.constraint(equalToConstant: constant),
892            bottomView.heightAnchor.constraint(equalToConstant: constant)
893        ])
894    }
895
896    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
897        super.traitCollectionDidChange(previousTraitCollection)
898        view.setNeedsUpdateConstraints()
899        view.updateConstraintsIfNeeded()
900    }
901}
902#endif
903
904#if canImport(SwiftUI)
905@available(iOS 13.0, macOS 11.0, tvOS 13.0, *)
906private struct TestView: SwiftUI.View {
907    var body: some SwiftUI.View {
908        HStack {
909            SwiftUI.Image(systemName: "checkmark.circle.fill")
910            Text("Checked").fixedSize()
911        }
912        .padding(5)
913        .background(RoundedRectangle(cornerRadius: 5).fill(Color.blue))
914        .padding(10)
915        .background(Color.yellow)
916    }
917}
918#endif
919
920#if os(iOS)
921private let allContentSizes =
922    [
923        "extra-small": UIContentSizeCategory.extraSmall,
924        "small": .small,
925        "medium": .medium,
926        "large": .large,
927        "extra-large": .extraLarge,
928        "extra-extra-large": .extraExtraLarge,
929        "extra-extra-extra-large": .extraExtraExtraLarge,
930        "accessibility-medium": .accessibilityMedium,
931        "accessibility-large": .accessibilityLarge,
932        "accessibility-extra-large": .accessibilityExtraLarge,
933        "accessibility-extra-extra-large": .accessibilityExtraExtraLarge,
934        "accessibility-extra-extra-extra-large": .accessibilityExtraExtraExtraLarge
935    ]
936#endif
937
Full Screen

SnapshotTestingTests.swift

Source: SnapshotTestingTests.swift Github

copy
1import Foundation
2#if canImport(FoundationNetworking)
3import FoundationNetworking
4#endif
5#if canImport(SceneKit)
6import SceneKit
7#endif
8#if canImport(SpriteKit)
9import SpriteKit
10import SwiftUI
11#endif
12#if canImport(WebKit)
13import WebKit
14#endif
15#if canImport(UIKit)
16import UIKit.UIView
17#endif
18import XCTest
19
20@testable import SnapshotTesting
21
22final class SnapshotTestingTests: XCTestCase {
23  override func setUp() {
24    super.setUp()
25    diffTool = "ksdiff"
26//    isRecording = true
27  }
28
29  override func tearDown() {
30    isRecording = false
31    super.tearDown()
32  }
33
34  func testAny() {
35    struct User { let id: Int, name: String, bio: String }
36    let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.")
37    assertSnapshot(matching: user, as: .dump)
38    _assertInlineSnapshot(matching: user, as: .dump, with: """
39    ▿ User
40      - bio: "Blobbed around the world."
41      - id: 1
42      - name: "Blobby"
43    """)
44  }
45
46  @available(macOS 10.13, *)
47  func testAnyAsJson() throws {
48    struct User: Encodable { let id: Int, name: String, bio: String }
49    let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.")
50
51    let data = try JSONEncoder().encode(user)
52    let any = try JSONSerialization.jsonObject(with: data, options: [])
53
54    assertSnapshot(matching: any, as: .json)
55  }
56
57  func testAnySnapshotStringConvertible() {
58    assertSnapshot(matching: "a" as Character, as: .dump, named: "character")
59    assertSnapshot(matching: Data("Hello, world!".utf8), as: .dump, named: "data")
60    assertSnapshot(matching: Date(timeIntervalSinceReferenceDate: 0), as: .dump, named: "date")
61    assertSnapshot(matching: NSObject(), as: .dump, named: "nsobject")
62    assertSnapshot(matching: "Hello, world!", as: .dump, named: "string")
63    assertSnapshot(matching: "Hello, world!".dropLast(8), as: .dump, named: "substring")
64    assertSnapshot(matching: URL(string: "https://www.pointfree.co")!, as: .dump, named: "url")
65    // Inline
66    _assertInlineSnapshot(matching: "a" as Character, as: .dump, with: """
67    - "a"
68    """)
69    _assertInlineSnapshot(matching: Data("Hello, world!".utf8), as: .dump, with: """
70    - 13 bytes
71    """)
72    _assertInlineSnapshot(matching: Date(timeIntervalSinceReferenceDate: 0), as: .dump, with: """
73    - 2001-01-01T00:00:00Z
74    """)
75    _assertInlineSnapshot(matching: NSObject(), as: .dump, with: """
76    - <NSObject>
77    """)
78    _assertInlineSnapshot(matching: "Hello, world!", as: .dump, with: """
79    - "Hello, world!"
80    """)
81    _assertInlineSnapshot(matching: "Hello, world!".dropLast(8), as: .dump, with: """
82    - "Hello"
83    """)
84    _assertInlineSnapshot(matching: URL(string: "https://www.pointfree.co")!, as: .dump, with: """
85    - https://www.pointfree.co
86    """)
87  }
88
89  func testAutolayout() {
90    #if os(iOS)
91    let vc = UIViewController()
92    vc.view.translatesAutoresizingMaskIntoConstraints = false
93    let subview = UIView()
94    subview.translatesAutoresizingMaskIntoConstraints = false
95    vc.view.addSubview(subview)
96    NSLayoutConstraint.activate([
97      subview.topAnchor.constraint(equalTo: vc.view.topAnchor),
98      subview.bottomAnchor.constraint(equalTo: vc.view.bottomAnchor),
99      subview.leftAnchor.constraint(equalTo: vc.view.leftAnchor),
100      subview.rightAnchor.constraint(equalTo: vc.view.rightAnchor),
101      ])
102    assertSnapshot(matching: vc, as: .image)
103    #endif
104  }
105
106  func testDeterministicDictionaryAndSetSnapshots() {
107    struct Person: Hashable { let name: String }
108    struct DictionarySetContainer { let dict: [String: Int], set: Set<Person> }
109    let set = DictionarySetContainer(
110      dict: ["c": 3, "a": 1, "b": 2],
111      set: [.init(name: "Brandon"), .init(name: "Stephen")]
112    )
113    assertSnapshot(matching: set, as: .dump)
114    _assertInlineSnapshot(matching: set, as: .dump, with: """
115    ▿ DictionarySetContainer
116      ▿ dict: 3 key/value pairs
117        ▿ (2 elements)
118          - key: "a"
119          - value: 1
120        ▿ (2 elements)
121          - key: "b"
122          - value: 2
123        ▿ (2 elements)
124          - key: "c"
125          - value: 3
126      ▿ set: 2 members
127        ▿ Person
128          - name: "Brandon"
129        ▿ Person
130          - name: "Stephen"
131    """)
132  }
133
134  func testCaseIterable() {
135    enum Direction: String, CaseIterable {
136      case up, down, left, right
137      var rotatedLeft: Direction {
138        switch self {
139        case .up:    return .left
140        case .down:  return .right
141        case .left:  return .down
142        case .right: return .up
143        }
144      }
145    }
146
147    assertSnapshot(
148      matching: { $0.rotatedLeft },
149      as: Snapshotting<Direction, String>.func(into: .description)
150    )
151  }
152
153  func testCGPath() {
154    #if os(iOS) || os(tvOS) || os(macOS)
155    let path = CGPath.heart
156
157    let osName: String
158    #if os(iOS)
159    osName = "iOS"
160    #elseif os(tvOS)
161    osName = "tvOS"
162    #elseif os(macOS)
163    osName = "macOS"
164    #endif
165
166    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
167      assertSnapshot(matching: path, as: .image, named: osName)
168    }
169
170    if #available(iOS 11.0, OSX 10.13, tvOS 11.0, *) {
171      assertSnapshot(matching: path, as: .elementsDescription, named: osName)
172    }
173    #endif
174  }
175
176  func testData() {
177    let data = Data([0xDE, 0xAD, 0xBE, 0xEF])
178
179    assertSnapshot(matching: data, as: .data)
180  }
181
182  func testEncodable() {
183    struct User: Encodable { let id: Int, name: String, bio: String }
184    let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.")
185
186    if #available(iOS 11.0, macOS 10.13, tvOS 11.0, *) {
187      assertSnapshot(matching: user, as: .json)
188    }
189    assertSnapshot(matching: user, as: .plist)
190  }
191
192  func testMixedViews() {
193//    #if os(iOS) || os(macOS)
194//    // NB: CircleCI crashes while trying to instantiate SKView.
195//    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
196//      let webView = WKWebView(frame: .init(x: 0, y: 0, width: 50, height: 50))
197//      webView.loadHTMLString("🌎", baseURL: nil)
198//
199//      let skView = SKView(frame: .init(x: 50, y: 0, width: 50, height: 50))
200//      let scene = SKScene(size: .init(width: 50, height: 50))
201//      let node = SKShapeNode(circleOfRadius: 15)
202//      node.fillColor = .red
203//      node.position = .init(x: 25, y: 25)
204//      scene.addChild(node)
205//      skView.presentScene(scene)
206//
207//      let view = View(frame: .init(x: 0, y: 0, width: 100, height: 50))
208//      view.addSubview(webView)
209//      view.addSubview(skView)
210//
211//      assertSnapshot(matching: view, as: .image, named: platform)
212//    }
213//    #endif
214  }
215
216  func testMultipleSnapshots() {
217    assertSnapshot(matching: [1], as: .dump)
218    assertSnapshot(matching: [1, 2], as: .dump)
219  }
220
221  func testNamedAssertion() {
222    struct User { let id: Int, name: String, bio: String }
223    let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.")
224    assertSnapshot(matching: user, as: .dump, named: "named")
225  }
226
227  func testNSBezierPath() {
228    #if os(macOS)
229    let path = NSBezierPath.heart
230
231    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
232      assertSnapshot(matching: path, as: .image, named: "macOS")
233    }
234
235    assertSnapshot(matching: path, as: .elementsDescription, named: "macOS")
236    #endif
237  }
238
239  func testNSView() {
240    #if os(macOS)
241    let button = NSButton()
242    button.bezelStyle = .rounded
243    button.title = "Push Me"
244    button.sizeToFit()
245    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
246      assertSnapshot(matching: button, as: .image)
247      assertSnapshot(matching: button, as: .recursiveDescription)
248    }
249    #endif
250  }
251  
252  func testNSViewWithLayer() {
253    #if os(macOS)
254    let view = NSView()
255    view.frame = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 10.0)
256    view.wantsLayer = true
257    view.layer?.backgroundColor = NSColor.green.cgColor
258    view.layer?.cornerRadius = 5
259    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
260      assertSnapshot(matching: view, as: .image)
261      assertSnapshot(matching: view, as: .recursiveDescription)
262    }
263    #endif
264  }
265
266  func testPrecision() {
267    #if os(iOS) || os(macOS) || os(tvOS)
268    #if os(iOS) || os(tvOS)
269    let label = UILabel()
270    #if os(iOS)
271    label.frame = CGRect(origin: .zero, size: CGSize(width: 43.5, height: 20.5))
272    #elseif os(tvOS)
273    label.frame = CGRect(origin: .zero, size: CGSize(width: 98, height: 46))
274    #endif
275    label.backgroundColor = .white
276    #elseif os(macOS)
277    let label = NSTextField()
278    label.frame = CGRect(origin: .zero, size: CGSize(width: 37, height: 16))
279    label.backgroundColor = .white
280    label.isBezeled = false
281    label.isEditable = false
282    #endif
283    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
284      label.text = "Hello."
285      assertSnapshot(matching: label, as: .image(precision: 0.9), named: platform)
286      label.text = "Hello"
287      assertSnapshot(matching: label, as: .image(precision: 0.9), named: platform)
288    }
289    #endif
290  }
291
292  func testSCNView() {
293//    #if os(iOS) || os(macOS) || os(tvOS)
294//    // NB: CircleCI crashes while trying to instantiate SCNView.
295//    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
296//      let scene = SCNScene()
297//
298//      let sphereGeometry = SCNSphere(radius: 3)
299//      sphereGeometry.segmentCount = 200
300//      let sphereNode = SCNNode(geometry: sphereGeometry)
301//      sphereNode.position = SCNVector3Zero
302//      scene.rootNode.addChildNode(sphereNode)
303//
304//      sphereGeometry.firstMaterial?.diffuse.contents = URL(fileURLWithPath: String(#file), isDirectory: false)
305//        .deletingLastPathComponent()
306//        .appendingPathComponent("__Fixtures__/earth.png")
307//
308//      let cameraNode = SCNNode()
309//      cameraNode.camera = SCNCamera()
310//      cameraNode.position = SCNVector3Make(0, 0, 8)
311//      scene.rootNode.addChildNode(cameraNode)
312//
313//      let omniLight = SCNLight()
314//      omniLight.type = .omni
315//      let omniLightNode = SCNNode()
316//      omniLightNode.light = omniLight
317//      omniLightNode.position = SCNVector3Make(10, 10, 10)
318//      scene.rootNode.addChildNode(omniLightNode)
319//
320//      assertSnapshot(
321//        matching: scene,
322//        as: .image(size: .init(width: 500, height: 500)),
323//        named: platform
324//      )
325//    }
326//    #endif
327  }
328
329  func testSKView() {
330//    #if os(iOS) || os(macOS) || os(tvOS)
331//    // NB: CircleCI crashes while trying to instantiate SKView.
332//    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
333//      let scene = SKScene(size: .init(width: 50, height: 50))
334//      let node = SKShapeNode(circleOfRadius: 15)
335//      node.fillColor = .red
336//      node.position = .init(x: 25, y: 25)
337//      scene.addChild(node)
338//
339//      assertSnapshot(
340//        matching: scene,
341//        as: .image(size: .init(width: 50, height: 50)),
342//        named: platform
343//      )
344//    }
345//    #endif
346  }
347
348  func testTableViewController() {
349    #if os(iOS)
350    class TableViewController: UITableViewController {
351      override func viewDidLoad() {
352        super.viewDidLoad()
353        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
354      }
355      override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
356        return 10
357      }
358      override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
359        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
360        cell.textLabel?.text = "\(indexPath.row)"
361        return cell
362      }
363    }
364    let tableViewController = TableViewController()
365    assertSnapshot(matching: tableViewController, as: .image(on: .iPhoneSe))
366    #endif
367  }
368
369  func testAssertMultipleSnapshot() {
370    #if os(iOS)
371    class TableViewController: UITableViewController {
372      override func viewDidLoad() {
373        super.viewDidLoad()
374        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
375      }
376      override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
377        return 10
378      }
379      override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
380        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
381        cell.textLabel?.text = "\(indexPath.row)"
382        return cell
383      }
384    }
385    let tableViewController = TableViewController()
386    assertSnapshots(matching: tableViewController, as: ["iPhoneSE-image" : .image(on: .iPhoneSe), "iPad-image" : .image(on: .iPadMini)])
387    assertSnapshots(matching: tableViewController, as: [.image(on: .iPhoneX), .image(on: .iPhoneXsMax)])
388    #endif
389  }
390
391  func testTraits() {
392    #if os(iOS) || os(tvOS)
393    if #available(iOS 11.0, tvOS 11.0, *) {
394      class MyViewController: UIViewController {
395        let topLabel = UILabel()
396        let leadingLabel = UILabel()
397        let trailingLabel = UILabel()
398        let bottomLabel = UILabel()
399
400        override func viewDidLoad() {
401          super.viewDidLoad()
402
403          self.navigationItem.leftBarButtonItem = .init(barButtonSystemItem: .add, target: nil, action: nil)
404
405          self.view.backgroundColor = .white
406
407          self.topLabel.text = "What's"
408          self.leadingLabel.text = "the"
409          self.trailingLabel.text = "point"
410          self.bottomLabel.text = "?"
411
412          self.topLabel.translatesAutoresizingMaskIntoConstraints = false
413          self.leadingLabel.translatesAutoresizingMaskIntoConstraints = false
414          self.trailingLabel.translatesAutoresizingMaskIntoConstraints = false
415          self.bottomLabel.translatesAutoresizingMaskIntoConstraints = false
416
417          self.view.addSubview(self.topLabel)
418          self.view.addSubview(self.leadingLabel)
419          self.view.addSubview(self.trailingLabel)
420          self.view.addSubview(self.bottomLabel)
421
422          NSLayoutConstraint.activate([
423            self.topLabel.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
424            self.topLabel.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor),
425            self.leadingLabel.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
426            self.leadingLabel.trailingAnchor.constraint(lessThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor),
427//            self.leadingLabel.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor),
428            self.leadingLabel.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor),
429            self.trailingLabel.leadingAnchor.constraint(greaterThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor),
430            self.trailingLabel.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
431            self.trailingLabel.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor),
432            self.bottomLabel.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
433            self.bottomLabel.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor),
434            ])
435        }
436
437        override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
438          super.traitCollectionDidChange(previousTraitCollection)
439          self.topLabel.font = .preferredFont(forTextStyle: .headline, compatibleWith: self.traitCollection)
440          self.leadingLabel.font = .preferredFont(forTextStyle: .body, compatibleWith: self.traitCollection)
441          self.trailingLabel.font = .preferredFont(forTextStyle: .body, compatibleWith: self.traitCollection)
442          self.bottomLabel.font = .preferredFont(forTextStyle: .subheadline, compatibleWith: self.traitCollection)
443          self.view.setNeedsUpdateConstraints()
444          self.view.updateConstraintsIfNeeded()
445        }
446      }
447
448      let viewController = MyViewController()
449
450      #if os(iOS)
451      assertSnapshot(matching: viewController, as: .image(on: .iPhoneSe), named: "iphone-se")
452      assertSnapshot(matching: viewController, as: .image(on: .iPhone8), named: "iphone-8")
453      assertSnapshot(matching: viewController, as: .image(on: .iPhone8Plus), named: "iphone-8-plus")
454      assertSnapshot(matching: viewController, as: .image(on: .iPhoneX), named: "iphone-x")
455      assertSnapshot(matching: viewController, as: .image(on: .iPhoneXr), named: "iphone-xr")
456      assertSnapshot(matching: viewController, as: .image(on: .iPhoneXsMax), named: "iphone-xs-max")
457      assertSnapshot(matching: viewController, as: .image(on: .iPadMini), named: "ipad-mini")
458      assertSnapshot(matching: viewController, as: .image(on: .iPad9_7), named: "ipad-9-7")
459      assertSnapshot(matching: viewController, as: .image(on: .iPad10_2), named: "ipad-10-2")
460      assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5), named: "ipad-pro-10-5")
461      assertSnapshot(matching: viewController, as: .image(on: .iPadPro11), named: "ipad-pro-11")
462      assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9), named: "ipad-pro-12-9")
463
464      assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPhoneSe), named: "iphone-se")
465      assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPhone8), named: "iphone-8")
466      assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPhone8Plus), named: "iphone-8-plus")
467      assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPhoneX), named: "iphone-x")
468      assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPhoneXr), named: "iphone-xr")
469      assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPhoneXsMax), named: "iphone-xs-max")
470      assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPadMini), named: "ipad-mini")
471      assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPad9_7), named: "ipad-9-7")
472      assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPad10_2), named: "ipad-10-2")
473      assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPadPro10_5), named: "ipad-pro-10-5")
474      assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPadPro11), named: "ipad-pro-11")
475      assertSnapshot(matching: viewController, as: .recursiveDescription(on: .iPadPro12_9), named: "ipad-pro-12-9")
476
477      assertSnapshot(matching: viewController, as: .image(on: .iPhoneSe(.portrait)), named: "iphone-se")
478      assertSnapshot(matching: viewController, as: .image(on: .iPhone8(.portrait)), named: "iphone-8")
479      assertSnapshot(matching: viewController, as: .image(on: .iPhone8Plus(.portrait)), named: "iphone-8-plus")
480      assertSnapshot(matching: viewController, as: .image(on: .iPhoneX(.portrait)), named: "iphone-x")
481      assertSnapshot(matching: viewController, as: .image(on: .iPhoneXr(.portrait)), named: "iphone-xr")
482      assertSnapshot(matching: viewController, as: .image(on: .iPhoneXsMax(.portrait)), named: "iphone-xs-max")
483      assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.landscape)), named: "ipad-mini")
484      assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.landscape)), named: "ipad-9-7")
485      assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.landscape)), named: "ipad-10-2")
486      assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.landscape)), named: "ipad-pro-10-5")
487      assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.landscape)), named: "ipad-pro-11")
488      assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.landscape)), named: "ipad-pro-12-9")
489
490      assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.landscape(splitView: .oneThird))), named: "ipad-mini-33-split-landscape")
491      assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.landscape(splitView: .oneHalf))), named: "ipad-mini-50-split-landscape")
492      assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.landscape(splitView: .twoThirds))), named: "ipad-mini-66-split-landscape")
493      assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.portrait(splitView: .oneThird))), named: "ipad-mini-33-split-portrait")
494      assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.portrait(splitView: .twoThirds))), named: "ipad-mini-66-split-portrait")
495
496      assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.landscape(splitView: .oneThird))), named: "ipad-9-7-33-split-landscape")
497      assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.landscape(splitView: .oneHalf))), named: "ipad-9-7-50-split-landscape")
498      assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.landscape(splitView: .twoThirds))), named: "ipad-9-7-66-split-landscape")
499      assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.portrait(splitView: .oneThird))), named: "ipad-9-7-33-split-portrait")
500      assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.portrait(splitView: .twoThirds))), named: "ipad-9-7-66-split-portrait")
501
502      assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.landscape(splitView: .oneThird))), named: "ipad-10-2-split-landscape")
503      assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.landscape(splitView: .oneHalf))), named: "ipad-10-2-50-split-landscape")
504      assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.landscape(splitView: .twoThirds))), named: "ipad-10-2-66-split-landscape")
505      assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.portrait(splitView: .oneThird))), named: "ipad-10-2-33-split-portrait")
506      assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.portrait(splitView: .twoThirds))), named: "ipad-10-2-66-split-portrait")
507      
508      assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.landscape(splitView: .oneThird))), named: "ipad-pro-10inch-33-split-landscape")
509      assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.landscape(splitView: .oneHalf))), named: "ipad-pro-10inch-50-split-landscape")
510      assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.landscape(splitView: .twoThirds))), named: "ipad-pro-10inch-66-split-landscape")
511      assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.portrait(splitView: .oneThird))), named: "ipad-pro-10inch-33-split-portrait")
512      assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.portrait(splitView: .twoThirds))), named: "ipad-pro-10inch-66-split-portrait")
513      
514      assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.landscape(splitView: .oneThird))), named: "ipad-pro-11inch-33-split-landscape")
515      assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.landscape(splitView: .oneHalf))), named: "ipad-pro-11inch-50-split-landscape")
516      assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.landscape(splitView: .twoThirds))), named: "ipad-pro-11inch-66-split-landscape")
517      assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.portrait(splitView: .oneThird))), named: "ipad-pro-11inch-33-split-portrait")
518      assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.portrait(splitView: .twoThirds))), named: "ipad-pro-11inch-66-split-portrait")
519      
520      assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.landscape(splitView: .oneThird))), named: "ipad-pro-12inch-33-split-landscape")
521      assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.landscape(splitView: .oneHalf))), named: "ipad-pro-12inch-50-split-landscape")
522      assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.landscape(splitView: .twoThirds))), named: "ipad-pro-12inch-66-split-landscape")
523      assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.portrait(splitView: .oneThird))), named: "ipad-pro-12inch-33-split-portrait")
524      assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.portrait(splitView: .twoThirds))), named: "ipad-pro-12inch-66-split-portrait")
525      
526      assertSnapshot(
527        matching: viewController, as: .image(on: .iPhoneSe(.landscape)), named: "iphone-se-alternative")
528      assertSnapshot(
529        matching: viewController, as: .image(on: .iPhone8(.landscape)), named: "iphone-8-alternative")
530      assertSnapshot(
531        matching: viewController, as: .image(on: .iPhone8Plus(.landscape)), named: "iphone-8-plus-alternative")
532      assertSnapshot(
533        matching: viewController, as: .image(on: .iPhoneX(.landscape)), named: "iphone-x-alternative")
534      assertSnapshot(
535        matching: viewController, as: .image(on: .iPhoneXr(.landscape)), named: "iphone-xr-alternative")
536      assertSnapshot(
537        matching: viewController, as: .image(on: .iPhoneXsMax(.landscape)), named: "iphone-xs-max-alternative")
538      assertSnapshot(
539        matching: viewController, as: .image(on: .iPadMini(.portrait)), named: "ipad-mini-alternative")
540      assertSnapshot(
541        matching: viewController, as: .image(on: .iPad9_7(.portrait)), named: "ipad-9-7-alternative")
542      assertSnapshot(
543        matching: viewController, as: .image(on: .iPad10_2(.portrait)), named: "ipad-10-2-alternative")
544      assertSnapshot(
545        matching: viewController, as: .image(on: .iPadPro10_5(.portrait)), named: "ipad-pro-10-5-alternative")
546      assertSnapshot(
547        matching: viewController, as: .image(on: .iPadPro11(.portrait)), named: "ipad-pro-11-alternative")
548      assertSnapshot(
549        matching: viewController, as: .image(on: .iPadPro12_9(.portrait)), named: "ipad-pro-12-9-alternative")
550
551      allContentSizes.forEach { name, contentSize in
552          assertSnapshot(
553            matching: viewController,
554            as: .image(on: .iPhoneSe, traits: .init(preferredContentSizeCategory: contentSize)),
555            named: "iphone-se-\(name)"
556          )
557      }
558      #elseif os(tvOS)
559      assertSnapshot(
560        matching: viewController, as: .image(on: .tv), named: "tv")
561      assertSnapshot(
562        matching: viewController, as: .image(on: .tv4K), named: "tv4k")
563      #endif
564    }
565    #endif
566  }
567
568  func testTraitsEmbeddedInTabNavigation() {
569    #if os(iOS)
570    if #available(iOS 11.0, *) {
571      class MyViewController: UIViewController {
572        let topLabel = UILabel()
573        let leadingLabel = UILabel()
574        let trailingLabel = UILabel()
575        let bottomLabel = UILabel()
576
577        override func viewDidLoad() {
578          super.viewDidLoad()
579
580          self.navigationItem.leftBarButtonItem = .init(barButtonSystemItem: .add, target: nil, action: nil)
581
582          self.view.backgroundColor = .white
583
584          self.topLabel.text = "What's"
585          self.leadingLabel.text = "the"
586          self.trailingLabel.text = "point"
587          self.bottomLabel.text = "?"
588
589          self.topLabel.translatesAutoresizingMaskIntoConstraints = false
590          self.leadingLabel.translatesAutoresizingMaskIntoConstraints = false
591          self.trailingLabel.translatesAutoresizingMaskIntoConstraints = false
592          self.bottomLabel.translatesAutoresizingMaskIntoConstraints = false
593
594          self.view.addSubview(self.topLabel)
595          self.view.addSubview(self.leadingLabel)
596          self.view.addSubview(self.trailingLabel)
597          self.view.addSubview(self.bottomLabel)
598
599          NSLayoutConstraint.activate([
600            self.topLabel.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
601            self.topLabel.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor),
602            self.leadingLabel.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
603            self.leadingLabel.trailingAnchor.constraint(lessThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor),
604//            self.leadingLabel.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor),
605            self.leadingLabel.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor),
606            self.trailingLabel.leadingAnchor.constraint(greaterThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor),
607            self.trailingLabel.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
608            self.trailingLabel.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor),
609            self.bottomLabel.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
610            self.bottomLabel.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor),
611            ])
612        }
613
614        override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
615          super.traitCollectionDidChange(previousTraitCollection)
616          self.topLabel.font = .preferredFont(forTextStyle: .headline, compatibleWith: self.traitCollection)
617          self.leadingLabel.font = .preferredFont(forTextStyle: .body, compatibleWith: self.traitCollection)
618          self.trailingLabel.font = .preferredFont(forTextStyle: .body, compatibleWith: self.traitCollection)
619          self.bottomLabel.font = .preferredFont(forTextStyle: .subheadline, compatibleWith: self.traitCollection)
620          self.view.setNeedsUpdateConstraints()
621          self.view.updateConstraintsIfNeeded()
622        }
623      }
624
625      let myViewController = MyViewController()
626      let navController = UINavigationController(rootViewController: myViewController)
627      let viewController = UITabBarController()
628      viewController.setViewControllers([navController], animated: false)
629
630      assertSnapshot(matching: viewController, as: .image(on: .iPhoneSe), named: "iphone-se")
631      assertSnapshot(matching: viewController, as: .image(on: .iPhone8), named: "iphone-8")
632      assertSnapshot(matching: viewController, as: .image(on: .iPhone8Plus), named: "iphone-8-plus")
633      assertSnapshot(matching: viewController, as: .image(on: .iPhoneX), named: "iphone-x")
634      assertSnapshot(matching: viewController, as: .image(on: .iPhoneXr), named: "iphone-xr")
635      assertSnapshot(matching: viewController, as: .image(on: .iPhoneXsMax), named: "iphone-xs-max")
636      assertSnapshot(matching: viewController, as: .image(on: .iPadMini), named: "ipad-mini")
637      assertSnapshot(matching: viewController, as: .image(on: .iPad9_7), named: "ipad-9-7")
638      assertSnapshot(matching: viewController, as: .image(on: .iPad10_2), named: "ipad-10-2")
639      assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5), named: "ipad-pro-10-5")
640      assertSnapshot(matching: viewController, as: .image(on: .iPadPro11), named: "ipad-pro-11")
641      assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9), named: "ipad-pro-12-9")
642
643      assertSnapshot(matching: viewController, as: .image(on: .iPhoneSe(.portrait)), named: "iphone-se")
644      assertSnapshot(matching: viewController, as: .image(on: .iPhone8(.portrait)), named: "iphone-8")
645      assertSnapshot(matching: viewController, as: .image(on: .iPhone8Plus(.portrait)), named: "iphone-8-plus")
646      assertSnapshot(matching: viewController, as: .image(on: .iPhoneX(.portrait)), named: "iphone-x")
647      assertSnapshot(matching: viewController, as: .image(on: .iPhoneXr(.portrait)), named: "iphone-xr")
648      assertSnapshot(matching: viewController, as: .image(on: .iPhoneXsMax(.portrait)), named: "iphone-xs-max")
649      assertSnapshot(matching: viewController, as: .image(on: .iPadMini(.landscape)), named: "ipad-mini")
650      assertSnapshot(matching: viewController, as: .image(on: .iPad9_7(.landscape)), named: "ipad-9-7")
651      assertSnapshot(matching: viewController, as: .image(on: .iPad10_2(.landscape)), named: "ipad-10-2")
652      assertSnapshot(matching: viewController, as: .image(on: .iPadPro10_5(.landscape)), named: "ipad-pro-10-5")
653      assertSnapshot(matching: viewController, as: .image(on: .iPadPro11(.landscape)), named: "ipad-pro-11")
654      assertSnapshot(matching: viewController, as: .image(on: .iPadPro12_9(.landscape)), named: "ipad-pro-12-9")
655
656      assertSnapshot(
657        matching: viewController, as: .image(on: .iPhoneSe(.landscape)), named: "iphone-se-alternative")
658      assertSnapshot(
659        matching: viewController, as: .image(on: .iPhone8(.landscape)), named: "iphone-8-alternative")
660      assertSnapshot(
661        matching: viewController, as: .image(on: .iPhone8Plus(.landscape)), named: "iphone-8-plus-alternative")
662      assertSnapshot(
663        matching: viewController, as: .image(on: .iPhoneX(.landscape)), named: "iphone-x-alternative")
664      assertSnapshot(
665        matching: viewController, as: .image(on: .iPhoneXr(.landscape)), named: "iphone-xr-alternative")
666      assertSnapshot(
667        matching: viewController, as: .image(on: .iPhoneXsMax(.landscape)), named: "iphone-xs-max-alternative")
668      assertSnapshot(
669        matching: viewController, as: .image(on: .iPadMini(.portrait)), named: "ipad-mini-alternative")
670      assertSnapshot(
671        matching: viewController, as: .image(on: .iPad9_7(.portrait)), named: "ipad-9-7-alternative")
672      assertSnapshot(
673        matching: viewController, as: .image(on: .iPad10_2(.portrait)), named: "ipad-10-2-alternative")
674      assertSnapshot(
675        matching: viewController, as: .image(on: .iPadPro10_5(.portrait)), named: "ipad-pro-10-5-alternative")
676      assertSnapshot(
677        matching: viewController, as: .image(on: .iPadPro11(.portrait)), named: "ipad-pro-11-alternative")
678      assertSnapshot(
679        matching: viewController, as: .image(on: .iPadPro12_9(.portrait)), named: "ipad-pro-12-9-alternative")
680    }
681    #endif
682  }
683
684  func testCollectionViewsWithMultipleScreenSizes() {
685    #if os(iOS)
686
687    final class CollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
688
689      let flowLayout: UICollectionViewFlowLayout = {
690        let layout = UICollectionViewFlowLayout()
691        layout.scrollDirection = .horizontal
692        layout.minimumLineSpacing = 20
693        return layout
694      }()
695
696      lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
697
698      override func viewDidLoad() {
699        super.viewDidLoad()
700
701        view.backgroundColor = .white
702        view.addSubview(collectionView)
703
704        collectionView.backgroundColor = .white
705        collectionView.dataSource = self
706        collectionView.delegate = self
707        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
708        collectionView.translatesAutoresizingMaskIntoConstraints = false
709
710        NSLayoutConstraint.activate([
711          collectionView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
712          collectionView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
713          collectionView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
714          collectionView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor)
715        ])
716
717        collectionView.reloadData()
718      }
719
720      override func viewDidLayoutSubviews() {
721        super.viewDidLayoutSubviews()
722        collectionView.collectionViewLayout.invalidateLayout()
723      }
724
725      override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
726        super.traitCollectionDidChange(previousTraitCollection)
727        collectionView.collectionViewLayout.invalidateLayout()
728      }
729
730      func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
731        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
732        cell.contentView.backgroundColor = .orange
733        return cell
734      }
735
736      func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
737        return 20
738      }
739
740      func collectionView(
741        _ collectionView: UICollectionView,
742        layout collectionViewLayout: UICollectionViewLayout,
743        sizeForItemAt indexPath: IndexPath
744        ) -> CGSize {
745        return CGSize(
746          width: min(collectionView.frame.width - 50, 300),
747          height: collectionView.frame.height
748        )
749      }
750
751    }
752
753    let viewController = CollectionViewController()
754
755    assertSnapshots(matching: viewController, as: [
756      "ipad": .image(on: .iPadPro12_9),
757      "iphoneSe": .image(on: .iPhoneSe),
758      "iphone8": .image(on: .iPhone8),
759      "iphoneMax": .image(on: .iPhoneXsMax)
760    ])
761    #endif
762  }
763
764  func testTraitsWithView() {
765    #if os(iOS)
766    if #available(iOS 11.0, *) {
767      let label = UILabel()
768      label.font = .preferredFont(forTextStyle: .title1)
769      label.adjustsFontForContentSizeCategory = true
770      label.text = "What's the point?"
771
772      allContentSizes.forEach { name, contentSize in
773        assertSnapshot(
774          matching: label,
775          as: .image(traits: .init(preferredContentSizeCategory: contentSize)),
776          named: "label-\(name)"
777        )
778      }
779    }
780    #endif
781  }
782
783  func testTraitsWithViewController() {
784    #if os(iOS)
785    let label = UILabel()
786    label.font = .preferredFont(forTextStyle: .title1)
787    label.adjustsFontForContentSizeCategory = true
788    label.text = "What's the point?"
789
790    let viewController = UIViewController()
791    viewController.view.addSubview(label)
792
793    label.translatesAutoresizingMaskIntoConstraints = false
794    NSLayoutConstraint.activate([
795      label.leadingAnchor.constraint(equalTo: viewController.view.layoutMarginsGuide.leadingAnchor),
796      label.topAnchor.constraint(equalTo: viewController.view.layoutMarginsGuide.topAnchor),
797      label.trailingAnchor.constraint(equalTo: viewController.view.layoutMarginsGuide.trailingAnchor)
798    ])
799
800    allContentSizes.forEach { name, contentSize in
801      assertSnapshot(
802        matching: viewController,
803        as: .recursiveDescription(on: .iPhoneSe, traits: .init(preferredContentSizeCategory: contentSize)),
804        named: "label-\(name)"
805      )
806    }
807    #endif
808  }
809
810  func testUIBezierPath() {
811    #if os(iOS) || os(tvOS)
812    let path = UIBezierPath.heart
813
814    let osName: String
815    #if os(iOS)
816    osName = "iOS"
817    #elseif os(tvOS)
818    osName = "tvOS"
819    #endif
820
821    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
822      assertSnapshot(matching: path, as: .image, named: osName)
823    }
824
825    if #available(iOS 11.0, tvOS 11.0, *) {
826      assertSnapshot(matching: path, as: .elementsDescription, named: osName)
827    }
828    #endif
829  }
830
831  func testUIView() {
832    #if os(iOS)
833    let view = UIButton(type: .contactAdd)
834    assertSnapshot(matching: view, as: .image)
835    assertSnapshot(matching: view, as: .recursiveDescription)
836    #endif
837  }
838
839  func testUIViewControllerLifeCycle() {
840    #if os(iOS)
841    class ViewController: UIViewController {
842      let viewDidLoadExpectation: XCTestExpectation
843      let viewWillAppearExpectation: XCTestExpectation
844      let viewDidAppearExpectation: XCTestExpectation
845      let viewWillDisappearExpectation: XCTestExpectation
846      let viewDidDisappearExpectation: XCTestExpectation
847      init(viewDidLoadExpectation: XCTestExpectation,
848           viewWillAppearExpectation: XCTestExpectation,
849           viewDidAppearExpectation: XCTestExpectation,
850           viewWillDisappearExpectation: XCTestExpectation,
851           viewDidDisappearExpectation: XCTestExpectation){
852        self.viewDidLoadExpectation = viewDidLoadExpectation
853        self.viewWillAppearExpectation = viewWillAppearExpectation
854        self.viewDidAppearExpectation = viewDidAppearExpectation
855        self.viewWillDisappearExpectation = viewWillDisappearExpectation
856        self.viewDidDisappearExpectation = viewDidDisappearExpectation
857        super.init(nibName: nil, bundle: nil)
858      }
859      required init?(coder: NSCoder) {
860        fatalError("init(coder:) has not been implemented")
861      }
862      override func viewDidLoad() {
863        super.viewDidLoad()
864        viewDidLoadExpectation.fulfill()
865      }
866      override func viewWillAppear(_ animated: Bool) {
867        super.viewWillAppear(animated)
868        viewWillAppearExpectation.fulfill()
869      }
870      override func viewDidAppear(_ animated: Bool) {
871        super.viewDidAppear(animated)
872        viewDidAppearExpectation.fulfill()
873      }
874      override func viewWillDisappear(_ animated: Bool) {
875        super.viewWillDisappear(animated)
876        viewWillDisappearExpectation.fulfill()
877      }
878      override func viewDidDisappear(_ animated: Bool) {
879        super.viewDidDisappear(animated)
880        viewDidDisappearExpectation.fulfill()
881      }
882    }
883
884    let viewDidLoadExpectation = expectation(description: "viewDidLoad")
885    let viewWillAppearExpectation = expectation(description: "viewWillAppear")
886    let viewDidAppearExpectation = expectation(description: "viewDidAppear")
887    let viewWillDisappearExpectation = expectation(description: "viewWillDisappear")
888    let viewDidDisappearExpectation = expectation(description: "viewDidDisappear")
889    viewWillAppearExpectation.expectedFulfillmentCount = 4
890    viewDidAppearExpectation.expectedFulfillmentCount = 4
891    viewWillDisappearExpectation.expectedFulfillmentCount = 4
892    viewDidDisappearExpectation.expectedFulfillmentCount = 4
893
894    let viewController = ViewController(
895      viewDidLoadExpectation: viewDidLoadExpectation,
896      viewWillAppearExpectation: viewWillAppearExpectation,
897      viewDidAppearExpectation: viewDidAppearExpectation,
898      viewWillDisappearExpectation: viewWillDisappearExpectation,
899      viewDidDisappearExpectation: viewDidDisappearExpectation
900    )
901
902    assertSnapshot(matching: viewController, as: .image)
903    assertSnapshot(matching: viewController, as: .image)
904
905    wait(for: [
906      viewDidLoadExpectation,
907      viewWillAppearExpectation,
908      viewDidAppearExpectation,
909      viewWillDisappearExpectation,
910      viewDidDisappearExpectation,
911    ], timeout: 1.0, enforceOrder: true)
912    #endif
913  }
914
915  func testCALayer() {
916    #if os(iOS)
917    let layer = CALayer()
918    layer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
919    layer.backgroundColor = UIColor.red.cgColor
920    layer.borderWidth = 4.0
921    layer.borderColor = UIColor.black.cgColor
922    assertSnapshot(matching: layer, as: .image)
923    #endif
924  }
925
926  func testCALayerWithGradient() {
927    #if os(iOS)
928    let baseLayer = CALayer()
929    baseLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
930    let gradientLayer = CAGradientLayer()
931    gradientLayer.colors = [UIColor.red.cgColor, UIColor.yellow.cgColor]
932    gradientLayer.frame = baseLayer.frame
933    baseLayer.addSublayer(gradientLayer)
934    assertSnapshot(matching: baseLayer, as: .image)
935    #endif
936  }
937
938  func testViewControllerHierarchy() {
939    #if os(iOS)
940    let page = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
941    page.setViewControllers([UIViewController()], direction: .forward, animated: false)
942    let tab = UITabBarController()
943    tab.viewControllers = [
944      UINavigationController(rootViewController: page),
945      UINavigationController(rootViewController: UIViewController()),
946      UINavigationController(rootViewController: UIViewController()),
947      UINavigationController(rootViewController: UIViewController()),
948      UINavigationController(rootViewController: UIViewController())
949    ]
950    assertSnapshot(matching: tab, as: .hierarchy)
951    #endif
952  }
953
954  func testURLRequest() {
955    var get = URLRequest(url: URL(string: "https://www.pointfree.co/")!)
956    get.addValue("pf_session={}", forHTTPHeaderField: "Cookie")
957    get.addValue("text/html", forHTTPHeaderField: "Accept")
958    get.addValue("application/json", forHTTPHeaderField: "Content-Type")
959    assertSnapshot(matching: get, as: .raw, named: "get")
960    assertSnapshot(matching: get, as: .curl, named: "get-curl")
961
962    var getWithQuery = URLRequest(url: URL(string: "https://www.pointfree.co?key_2=value_2&key_1=value_1&key_3=value_3")!)
963    getWithQuery.addValue("pf_session={}", forHTTPHeaderField: "Cookie")
964    getWithQuery.addValue("text/html", forHTTPHeaderField: "Accept")
965    getWithQuery.addValue("application/json", forHTTPHeaderField: "Content-Type")
966    assertSnapshot(matching: getWithQuery, as: .raw, named: "get-with-query")
967    assertSnapshot(matching: getWithQuery, as: .curl, named: "get-with-query-curl")
968
969    var post = URLRequest(url: URL(string: "https://www.pointfree.co/subscribe")!)
970    post.httpMethod = "POST"
971    post.addValue("pf_session={\"user_id\":\"0\"}", forHTTPHeaderField: "Cookie")
972    post.addValue("text/html", forHTTPHeaderField: "Accept")
973    post.httpBody = Data("pricing[billing]=monthly&pricing[lane]=individual".utf8)
974    assertSnapshot(matching: post, as: .raw, named: "post")
975    assertSnapshot(matching: post, as: .curl, named: "post-curl")
976    
977    var postWithJSON = URLRequest(url: URL(string: "http://dummy.restapiexample.com/api/v1/create")!)
978    postWithJSON.httpMethod = "POST"
979    postWithJSON.addValue("application/json", forHTTPHeaderField: "Content-Type")
980    postWithJSON.addValue("application/json", forHTTPHeaderField: "Accept")
981    postWithJSON.httpBody = Data("{\"name\":\"tammy134235345235\", \"salary\":0, \"age\":\"tammy133\"}".utf8)
982    assertSnapshot(matching: postWithJSON, as: .raw, named: "post-with-json")
983    assertSnapshot(matching: postWithJSON, as: .curl, named: "post-with-json-curl")
984
985    var head = URLRequest(url: URL(string: "https://www.pointfree.co/")!)
986    head.httpMethod = "HEAD"
987    head.addValue("pf_session={}", forHTTPHeaderField: "Cookie")
988    assertSnapshot(matching: head, as: .raw, named: "head")
989    assertSnapshot(matching: head, as: .curl, named: "head-curl")
990
991    post = URLRequest(url: URL(string: "https://www.pointfree.co/subscribe")!)
992    post.httpMethod = "POST"
993    post.addValue("pf_session={\"user_id\":\"0\"}", forHTTPHeaderField: "Cookie")
994    post.addValue("application/json", forHTTPHeaderField: "Accept")
995    post.httpBody = Data("""
996                         {"pricing": {"lane": "individual","billing": "monthly"}}
997                         """.utf8)
998    _assertInlineSnapshot(matching: post, as: .raw(pretty: true), with: """
999    POST https://www.pointfree.co/subscribe
1000    Accept: application/json
1001    Cookie: pf_session={"user_id":"0"}
1002    
1003    {
1004      "pricing" : {
1005        "billing" : "monthly",
1006        "lane" : "individual"
1007      }
1008    }
1009    """)
1010  }
1011
1012  func testWebView() throws {
1013    #if os(iOS) || os(macOS)
1014    let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false)
1015      .deletingLastPathComponent()
1016      .appendingPathComponent("__Fixtures__/pointfree.html")
1017    let html = try String(contentsOf: fixtureUrl)
1018    let webView = WKWebView()
1019    webView.loadHTMLString(html, baseURL: nil)
1020    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
1021      assertSnapshot(
1022        matching: webView,
1023        as: .image(size: .init(width: 800, height: 600)),
1024        named: platform
1025      )
1026    }
1027    #endif
1028  }
1029
1030  func testViewWithZeroHeightOrWidth() {
1031    #if os(iOS) || os(tvOS)
1032    var rect = CGRect(x: 0, y: 0, width: 350, height: 0)
1033    var view = UIView(frame: rect)
1034    view.backgroundColor = .red
1035    assertSnapshot(matching: view, as: .image, named: "noHeight")
1036    
1037    rect = CGRect(x: 0, y: 0, width: 0, height: 350)
1038    view = UIView(frame: rect)
1039    view.backgroundColor = .green
1040    assertSnapshot(matching: view, as: .image, named: "noWidth")
1041
1042    rect = CGRect(x: 0, y: 0, width: 0, height: 0)
1043    view = UIView(frame: rect)
1044    view.backgroundColor = .blue
1045    assertSnapshot(matching: view, as: .image, named: "noWidth.noHeight")
1046    #endif
1047  }
1048
1049  func testEmbeddedWebView() throws {
1050    #if os(iOS)
1051    let label = UILabel()
1052    label.text = "Hello, Blob!"
1053
1054    let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false)
1055      .deletingLastPathComponent()
1056      .appendingPathComponent("__Fixtures__/pointfree.html")
1057    let html = try String(contentsOf: fixtureUrl)
1058    let webView = WKWebView()
1059    webView.loadHTMLString(html, baseURL: nil)
1060    webView.isHidden = true
1061
1062    let stackView = UIStackView(arrangedSubviews: [label, webView])
1063    stackView.axis = .vertical
1064
1065    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
1066      assertSnapshot(
1067        matching: stackView,
1068        as: .image(size: .init(width: 800, height: 600)),
1069        named: platform
1070      )
1071    }
1072    #endif
1073  }
1074
1075  #if os(iOS) || os(macOS)
1076  final class ManipulatingWKWebViewNavigationDelegate: NSObject, WKNavigationDelegate {
1077    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
1078      webView.evaluateJavaScript("document.body.children[0].classList.remove(\"hero\")") // Change layout
1079    }
1080  }
1081  func testWebViewWithManipulatingNavigationDelegate() throws {
1082    let manipulatingWKWebViewNavigationDelegate = ManipulatingWKWebViewNavigationDelegate()
1083    let webView = WKWebView()
1084    webView.navigationDelegate = manipulatingWKWebViewNavigationDelegate
1085
1086    let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false)
1087      .deletingLastPathComponent()
1088      .appendingPathComponent("__Fixtures__/pointfree.html")
1089    let html = try String(contentsOf: fixtureUrl)
1090    webView.loadHTMLString(html, baseURL: nil)
1091    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
1092      assertSnapshot(
1093        matching: webView,
1094        as: .image(size: .init(width: 800, height: 600)),
1095        named: platform
1096      )
1097    }
1098    _ = manipulatingWKWebViewNavigationDelegate
1099  }
1100
1101  #if os(iOS) || os(macOS)
1102  func testWebViewWithRealUrl() throws {
1103    let manipulatingWKWebViewNavigationDelegate = ManipulatingWKWebViewNavigationDelegate()
1104    let webView = WKWebView()
1105    webView.navigationDelegate = manipulatingWKWebViewNavigationDelegate
1106
1107    webView.load(URLRequest(url: URL(string: "https://www.pointfree.co")!))
1108    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
1109      assertSnapshot(
1110        matching: webView,
1111        as: .image(size: .init(width: 800, height: 600)),
1112        named: platform
1113      )
1114    }
1115    _ = manipulatingWKWebViewNavigationDelegate
1116  }
1117  #endif
1118
1119  final class CancellingWKWebViewNavigationDelegate: NSObject, WKNavigationDelegate {
1120    func webView(
1121      _ webView: WKWebView,
1122      decidePolicyFor navigationAction: WKNavigationAction,
1123      decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
1124    ) {
1125      decisionHandler(.cancel)
1126    }
1127  }
1128  func testWebViewWithCancellingNavigationDelegate() throws {
1129    let cancellingWKWebViewNavigationDelegate = CancellingWKWebViewNavigationDelegate()
1130    let webView = WKWebView()
1131    webView.navigationDelegate = cancellingWKWebViewNavigationDelegate
1132
1133    let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false)
1134      .deletingLastPathComponent()
1135      .appendingPathComponent("__Fixtures__/pointfree.html")
1136    let html = try String(contentsOf: fixtureUrl)
1137    webView.loadHTMLString(html, baseURL: nil)
1138    if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") {
1139      assertSnapshot(
1140        matching: webView,
1141        as: .image(size: .init(width: 800, height: 600)),
1142        named: platform
1143      )
1144    }
1145    _ = cancellingWKWebViewNavigationDelegate
1146  }
1147  #endif
1148
1149  @available(iOS 13.0, *)
1150  func testSwiftUIView_iOS() {
1151    #if os(iOS)
1152    struct MyView: SwiftUI.View {
1153      var body: some SwiftUI.View {
1154        HStack {
1155          Image(systemName: "checkmark.circle.fill")
1156            Text("Checked").fixedSize()
1157        }
1158        .padding(5)
1159        .background(RoundedRectangle(cornerRadius: 5.0).fill(Color.blue))
1160        .padding(10)
1161      }
1162    }
1163
1164    let view = MyView().background(Color.yellow)
1165
1166    assertSnapshot(matching: view, as: .image(traits: .init(userInterfaceStyle: .light)))
1167    assertSnapshot(matching: view, as: .image(layout: .sizeThatFits, traits: .init(userInterfaceStyle: .light)), named: "size-that-fits")
1168    assertSnapshot(matching: view, as: .image(layout: .fixed(width: 200.0, height: 100.0), traits: .init(userInterfaceStyle: .light)), named: "fixed")
1169    assertSnapshot(matching: view, as: .image(layout: .device(config: .iPhoneSe), traits: .init(userInterfaceStyle: .light)), named: "device")
1170    #endif
1171  }
1172
1173  @available(tvOS 13.0, *)
1174  func testSwiftUIView_tvOS() {
1175    #if os(tvOS)
1176    struct MyView: SwiftUI.View {
1177      var body: some SwiftUI.View {
1178        HStack {
1179          Image(systemName: "checkmark.circle.fill")
1180            Text("Checked").fixedSize()
1181        }
1182        .padding(5)
1183        .background(RoundedRectangle(cornerRadius: 5.0).fill(Color.blue))
1184        .padding(10)
1185      }
1186    }
1187    let view = MyView().background(Color.yellow)
1188
1189    assertSnapshot(matching: view, as: .image())
1190    assertSnapshot(matching: view, as: .image(layout: .sizeThatFits), named: "size-that-fits")
1191    assertSnapshot(matching: view, as: .image(layout: .fixed(width: 300.0, height: 100.0)), named: "fixed")
1192    assertSnapshot(matching: view, as: .image(layout: .device(config: .tv)), named: "device")
1193    #endif
1194  }
1195
1196  @available(*, deprecated)
1197  func testIsRecordingProxy() {
1198    SnapshotTesting.record = true
1199    XCTAssertEqual(isRecording, true)
1200
1201    SnapshotTesting.record = false
1202    XCTAssertEqual(isRecording, false)
1203  }
1204}
1205
1206#if os(iOS)
1207private let allContentSizes =
1208  [
1209    "extra-small": UIContentSizeCategory.extraSmall,
1210    "small": .small,
1211    "medium": .medium,
1212    "large": .large,
1213    "extra-large": .extraLarge,
1214    "extra-extra-large": .extraExtraLarge,
1215    "extra-extra-extra-large": .extraExtraExtraLarge,
1216    "accessibility-medium": .accessibilityMedium,
1217    "accessibility-large": .accessibilityLarge,
1218    "accessibility-extra-large": .accessibilityExtraLarge,
1219    "accessibility-extra-extra-large": .accessibilityExtraExtraLarge,
1220    "accessibility-extra-extra-extra-large": .accessibilityExtraExtraExtraLarge,
1221    ]
1222#endif
1223
1224#if os(Linux) || os(Windows)
1225extension SnapshotTestingTests {
1226  static var allTests : [(String, (SnapshotTestingTests) -> () throws -> Void)] {
1227    return [
1228      ("testAny", testAny),
1229      ("testAnySnapshotStringConvertible", testAnySnapshotStringConvertible),
1230      ("testAutolayout", testAutolayout),
1231      ("testDeterministicDictionaryAndSetSnapshots", testDeterministicDictionaryAndSetSnapshots),
1232      ("testEncodable", testEncodable),
1233      ("testMixedViews", testMixedViews),
1234      ("testMultipleSnapshots", testMultipleSnapshots),
1235      ("testNamedAssertion", testNamedAssertion),
1236      ("testPrecision", testPrecision),
1237      ("testSCNView", testSCNView),
1238      ("testSKView", testSKView),
1239      ("testTableViewController", testTableViewController),
1240      ("testTraits", testTraits),
1241      ("testTraitsEmbeddedInTabNavigation", testTraitsEmbeddedInTabNavigation),
1242      ("testTraitsWithView", testTraitsWithView),
1243      ("testUIView", testUIView),
1244      ("testURLRequest", testURLRequest),
1245      ("testWebView", testWebView),
1246    ]
1247  }
1248}
1249#endif
1250
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)