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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
@testable import Grain
import SwiftUI
import XCTest
/// The screens behind Settings' navigation links. Each is its own view with its
/// own load, so rendering the Settings list never reaches them — they only
/// appear once something is pushed.
@MainActor
final class SettingsDestinationRenderTests: 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()
MockURLProtocol.respondByPath(Fixtures.routes)
}
override func tearDown() async throws {
MockURLProtocol.handler = nil
account.restore()
try await super.tearDown()
}
private let settle: TimeInterval = 0.3
// MARK: - Account rows
/// The switcher draws a row per account, and the active one is marked and
/// unselectable so you can't switch to where you already are.
func testRendersAnAccountRowInEachState() {
let signedIn = StoredAccount(did: "did:plc:test", handle: "tester.grain.social", avatar: "https://test.local/a.jpg")
let other = StoredAccount(did: "did:plc:other", handle: "other.test", avatar: nil)
ViewRender.render(AccountRow(account: signedIn, isActive: true, isSwitching: false, onTap: {}), settle: 0)
ViewRender.render(AccountRow(account: other, isActive: false, isSwitching: false, onTap: {}), settle: 0)
ViewRender.render(AccountRow(account: other, isActive: false, isSwitching: true, onTap: {}), settle: 0)
}
/// An account whose handle never resolved falls back to its DID rather than
/// drawing a blank row.
func testAnAccountWithNoHandleFallsBackToItsDID() {
let nameless = StoredAccount(did: "did:plc:nameless", handle: nil, avatar: nil)
ViewRender.render(AccountRow(account: nameless, isActive: false, isSwitching: false, onTap: {}), settle: 0)
}
// MARK: - Account detail
func testRendersTheAccountDetailScreen() {
let env = TestEnvironment()
env.auth.userHandle = "tester.grain.social"
ViewRender.render(
AccountDetailView(client: env.client).withTestEnvironment(env), settle: settle
)
}
/// Signed out there is no handle or DID to show, and the destructive
/// actions have nothing to act on.
func testRendersTheAccountDetailScreenWithNoSession() {
let env = TestEnvironment(authenticated: false)
ViewRender.render(
AccountDetailView(client: env.client).withTestEnvironment(env), settle: settle
)
}
// MARK: - Adding an account
/// Signing in to a second account without disturbing the first.
func testRendersTheAddAccountScreen() {
let env = TestEnvironment()
ViewRender.render(AddAccountView().withTestEnvironment(env), settle: settle)
}
// MARK: - Feeds and appearance
func testRendersTheFeedsSettingsScreen() {
ViewRender.render(FeedsSettingsView(), settle: 0)
}
/// The appearance list marks whichever option is stored, so it has to be
/// rendered against each of them.
func testRendersTheAppearanceScreenForEachOption() {
let key = "appearance"
let saved = StorageEnvironment.defaults.string(forKey: key)
defer {
if let saved {
StorageEnvironment.defaults.set(saved, forKey: key)
} else {
StorageEnvironment.defaults.removeObject(forKey: key)
}
}
for option in ["auto", "light", "dark"] {
StorageEnvironment.defaults.set(option, forKey: key)
ViewRender.render(AppearanceSettingsView(), settle: 0.1)
}
}
// MARK: - Upload defaults
/// The toggles are seeded from the account's stored preferences, so this
/// only shows real state once that fetch lands.
func testRendersTheUploadDefaultsScreen() {
let env = TestEnvironment()
ViewRender.render(
UploadDefaultsView(client: env.client).withTestEnvironment(env), settle: settle
)
}
/// With the preferences fetch failing the toggles stay on their defaults
/// rather than the screen breaking.
func testRendersTheUploadDefaultsScreenWhenPreferencesFailToLoad() {
MockURLProtocol.respondWithError(statusCode: 500)
let env = TestEnvironment()
ViewRender.render(
UploadDefaultsView(client: env.client).withTestEnvironment(env), settle: settle
)
}
/// Signed out there is no context to fetch preferences with.
func testRendersTheUploadDefaultsScreenWithNoSession() {
let env = TestEnvironment(authenticated: false)
ViewRender.render(
UploadDefaultsView(client: env.client).withTestEnvironment(env), settle: settle
)
}
}