1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
@testable import Grain
import SwiftUI
import XCTest
/// The app's top-level screens, none of which were reachable from the existing
/// suite. Rendering a screen also renders the cards, rows and strips it is
/// built from, so these cover a good deal more than the file they name.
@MainActor
final class ScreenRenderTests: GrainTestCase {
private var account: TestAccount!
/// Async overrides: the synchronous `setUp`/`tearDown` are nonisolated, so
/// touching main-actor state from them warns.
override func setUp() async throws {
try await super.setUp()
account = TestAccount()
}
override func tearDown() async throws {
MockURLProtocol.handler = nil
account.restore()
try await super.tearDown()
}
// MARK: - Feed
func testRendersFeed() {
MockURLProtocol.respondWithJSON(Fixtures.feed)
let env = TestEnvironment()
ViewRender.render(FeedView(client: env.client).withTestEnvironment(env))
}
func testRendersFeedWhenItFailsToLoad() {
MockURLProtocol.respondWithError(statusCode: 500)
let env = TestEnvironment()
ViewRender.render(FeedView(client: env.client).withTestEnvironment(env))
}
func testRendersFeedWhenEmpty() {
MockURLProtocol.respondWithJSON(#"{"items": [], "cursor": null}"#)
let env = TestEnvironment()
ViewRender.render(FeedView(client: env.client).withTestEnvironment(env))
}
// MARK: - Search
func testRendersSearch() {
MockURLProtocol.respondWithJSON(Fixtures.feed)
let env = TestEnvironment()
ViewRender.render(SearchView(client: env.client).withTestEnvironment(env))
}
func testRendersSearchWhenItFailsToLoad() {
MockURLProtocol.respondWithError(statusCode: 500)
let env = TestEnvironment()
ViewRender.render(SearchView(client: env.client).withTestEnvironment(env))
}
// MARK: - Notifications
func testRendersNotifications() {
MockURLProtocol.respondWithJSON(Fixtures.notifications)
let env = TestEnvironment()
let vm = NotificationsViewModel(client: env.client)
ViewRender.render(
NotificationsView(client: env.client, viewModel: vm).withTestEnvironment(env)
)
}
func testRendersNotificationsWhenEmpty() {
MockURLProtocol.respondWithJSON(#"{"notifications": [], "cursor": null}"#)
let env = TestEnvironment()
let vm = NotificationsViewModel(client: env.client)
ViewRender.render(
NotificationsView(client: env.client, viewModel: vm).withTestEnvironment(env)
)
}
// MARK: - Settings
func testRendersSettings() {
MockURLProtocol.respondWithJSON("{}")
let env = TestEnvironment()
ViewRender.render(SettingsView(client: env.client).withTestEnvironment(env))
}
// MARK: - Tab tree
/// MainTabView builds the whole signed-in tree, so this reaches the tab
/// bar, the selected tab's screen and the shared chrome around them.
func testRendersMainTabTree() {
MockURLProtocol.respondWithJSON(Fixtures.feed)
let env = TestEnvironment()
ViewRender.render(
MainTabView(pendingDeepLink: .constant(nil)).withTestEnvironment(env),
settle: 0.2
)
}
}