~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain-ios

1 collaborator
0
1
main
196 lines · 6.2 KB Raw History
@testable import Grain
import SwiftUI
import XCTest

/// The list, management and moderation screens. Each was entirely unexercised;
/// most are a loading state, a loaded list and an empty state, so they are
/// rendered across those branches rather than just once.
@MainActor
final class ListViewRenderTests: 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()
    }

    private static let followers = """
    {"items": [{"subject": \(Fixtures.profile), "followedBy": null}], "cursor": null}
    """

    // MARK: - Follow lists

    func testRendersEachFollowListMode() {
        MockURLProtocol.respondWithJSON(Self.followers)
        let env = TestEnvironment()
        let modes: [FollowListMode] = [
            .followers,
            .following,
            .knownFollowers,
            .galleryFavorites("at://did:plc:test/social.grain.gallery/1"),
        ]

        for mode in modes {
            ViewRender.render(
                FollowListView(client: env.client, did: "did:plc:test", mode: mode)
                    .withTestEnvironment(env)
            )
        }
    }

    func testRendersFollowListWhenEmpty() {
        MockURLProtocol.respondWithJSON(#"{"items": [], "cursor": null}"#)
        let env = TestEnvironment()

        ViewRender.render(
            FollowListView(client: env.client, did: "did:plc:test", mode: .followers)
                .withTestEnvironment(env)
        )
    }

    // MARK: - Feed management

    func testRendersFeedsManagement() {
        MockURLProtocol.respondWithJSON(#"{"feeds": [], "cursor": null}"#)
        let env = TestEnvironment()
        ViewRender.render(
            FeedsManagementView(
                prefsViewModel: FeedPreferencesViewModel(client: env.client),
                client: env.client
            )
            .withTestEnvironment(env)
        )
    }

    // MARK: - Hashtag and profile gallery feeds

    func testRendersHashtagFeed() {
        MockURLProtocol.respondWithJSON(Fixtures.feed)
        let env = TestEnvironment()
        ViewRender.render(
            HashtagFeedView(client: env.client, tag: "film").withTestEnvironment(env)
        )
    }

    func testRendersProfileGalleryFeedForEachSource() {
        MockURLProtocol.respondWithJSON(Fixtures.feed)
        let env = TestEnvironment()

        for source in [ProfileGalleryFeedSource.galleries, .favorites] {
            let vm = ProfileDetailViewModel(client: env.client)
            ViewRender.render(
                ProfileGalleryFeedView(
                    viewModel: vm,
                    client: env.client,
                    did: "did:plc:test",
                    initialUri: "at://did:plc:test/social.grain.gallery/1",
                    source: source
                )
                .withTestEnvironment(env)
            )
        }
    }

    // MARK: - Moderation

    func testRendersReportView() {
        MockURLProtocol.respondWithJSON("{}")
        let env = TestEnvironment()

        ViewRender.render(
            ReportView(
                client: env.client,
                subjectUri: "at://did:plc:test/social.grain.gallery/1",
                subjectCid: "bafygallery"
            )
            .withTestEnvironment(env)
        )
    }

    func testRendersBlockedUsers() {
        MockURLProtocol.respondWithJSON(#"{"items": [], "cursor": null}"#)
        let env = TestEnvironment()
        ViewRender.render(BlockedUsersView(client: env.client).withTestEnvironment(env))
    }

    func testRendersMutedUsers() {
        MockURLProtocol.respondWithJSON(#"{"items": [], "cursor": null}"#)
        let env = TestEnvironment()
        ViewRender.render(MutedUsersView(client: env.client).withTestEnvironment(env))
    }

    func testRendersModerationView() {
        MockURLProtocol.respondWithJSON("{}")
        let env = TestEnvironment()
        ViewRender.render(ModerationView(client: env.client).withTestEnvironment(env))
    }

    // MARK: - Settings leaves

    func testRendersNotificationSettings() {
        MockURLProtocol.respondWithJSON("{}")
        let env = TestEnvironment()
        ViewRender.render(NotificationSettingsView(client: env.client).withTestEnvironment(env))
    }

    // MARK: - Suggestions

    func testRendersSuggestedFollows() {
        MockURLProtocol.respondWithJSON("{}")
        let env = TestEnvironment()
        var suggestions = [
            SuggestedItem(did: "did:plc:a", handle: "a.test", displayName: "A", followersCount: 3),
            SuggestedItem(did: "did:plc:b", handle: "b.test", displayName: "B", followersCount: 9),
        ]

        ViewRender.render(
            SuggestedFollowsView(
                client: env.client,
                suggestions: Binding(get: { suggestions }, set: { suggestions = $0 })
            )
            .withTestEnvironment(env)
        )
    }

    // MARK: - Chrome and placeholders

    func testRendersProfileSkeleton() {
        ViewRender.render(ProfileSkeletonView(showsTabBar: true), settle: 0)
        ViewRender.render(ProfileSkeletonView(showsTabBar: false), settle: 0)
    }

    func testRendersContentWarningOverlayForEachAction() {
        for action in [LabelAction.warnMedia, .warnContent, .badge, .hide] {
            ViewRender.render(
                ContentWarningOverlay(name: "nudity", action: action, onReveal: {}),
                settle: 0
            )
        }
    }

    /// Reads the label definitions out of the environment, so it needs the same
    /// ancestors the app gives it.
    func testRendersContentLabelPicker() {
        let env = TestEnvironment()
        var selected: Set = ["nudity"]
        ViewRender.render(
            ContentLabelPicker(
                selectedLabels: Binding(get: { selected }, set: { selected = $0 })
            )
            .withTestEnvironment(env),
            settle: 0
        )
    }

    func testRendersCaptionsListPrototype() {
        ViewRender.render(CaptionsListPrototype(), settle: 0)
    }
}