app-reviews/Frameworks/Feed/Test/Tests/View Models/FeedListViewModelTests.swift

495 lines
13 KiB
Swift
Raw Normal View History

//
// FeedListViewModelTests.swift
// ReviewsFeedTests
//
// Created by Javier Cicchelli on 22/03/2024.
// Copyright © 2024 Röck+Cöde. All rights reserved.
//
import Combine
import ReviewsFoundationKit
import ReviewsiTunesKit
import XCTest
@testable import ReviewsFeed
final class FeedListViewModelTests: XCTestCase {
// MARK: Properties
private var sut: FeedListViewController.ViewModel!
private var cancellables: Set<AnyCancellable> = []
// MARK: Setup
override func setUp() async throws {
sut = .init(
configuration: .init(session: .mock),
coordination: nil
)
}
override func tearDown() async throws {
cancellables.removeAll()
}
// MARK: Initialisers tests
func testInit() {
// GIVEN
// WHEN
// THEN
XCTAssertEqual(sut.filter, .all)
XCTAssertFalse(sut.isFilterEnabled)
XCTAssertFalse(sut.isFiltering)
XCTAssertFalse(sut.isLoading)
XCTAssertFalse(sut.isWordsShowing)
XCTAssertTrue(sut.items.isEmpty)
XCTAssertEqual(sut.itemsCount, 0)
XCTAssertEqual(sut.state, .initial)
XCTAssertTrue(sut.words.isEmpty)
}
// MARK: Functions tests
func testFetch_allItems_whenResponseOK_withSomeItems() async {
let expectation = XCTestExpectation()
var isLoading: [Bool] = []
// GIVEN
MockURLProtocol.response = .init(
statusCode: 200,
object: Feed(entries: .sample)
)
// WHEN
sut.$isLoading
.collect(3)
.sink { value in
isLoading.append(contentsOf: value)
expectation.fulfill()
}
.store(in: &cancellables)
await sut.fetch()
wait(for: [expectation], timeout: 2)
// THEN
XCTAssertEqual(isLoading, [false, true, false])
XCTAssertTrue(sut.isFilterEnabled)
XCTAssertFalse(sut.isWordsShowing)
XCTAssertFalse(sut.items.isEmpty)
XCTAssertEqual(sut.items.count, 5)
XCTAssertEqual(sut.itemsCount, 5)
XCTAssertEqual(sut.state, .populated)
}
func testFetch_allItems_whenResponseOK_withNoItems() async {
let expectation = XCTestExpectation()
var isLoading: [Bool] = []
// GIVEN
MockURLProtocol.response = .init(
statusCode: 200,
object: Feed(entries: .none)
)
// WHEN
sut.$isLoading
.collect(3)
.sink { value in
isLoading.append(contentsOf: value)
expectation.fulfill()
}
.store(in: &cancellables)
await sut.fetch()
wait(for: [expectation], timeout: 2)
// THEN
XCTAssertEqual(isLoading, [false, true, false])
XCTAssertFalse(sut.isFilterEnabled)
XCTAssertFalse(sut.isWordsShowing)
XCTAssertTrue(sut.items.isEmpty)
XCTAssertEqual(sut.itemsCount, 0)
XCTAssertEqual(sut.state, .empty)
}
func testFetch_allItems_whenResponseNotOK() async {
let expectation = XCTestExpectation()
var isLoading: [Bool] = []
// GIVEN
MockURLProtocol.response = .init(statusCode: 404)
// WHEN
sut.$isLoading
.collect(3)
.sink { value in
isLoading.append(contentsOf: value)
expectation.fulfill()
}
.store(in: &cancellables)
await sut.fetch()
wait(for: [expectation], timeout: 2)
// THEN
XCTAssertEqual(isLoading, [false, true, false])
XCTAssertFalse(sut.isFilterEnabled)
XCTAssertFalse(sut.isWordsShowing)
XCTAssertTrue(sut.items.isEmpty)
XCTAssertEqual(sut.itemsCount, 0)
XCTAssertEqual(sut.state, .error)
}
func testFetch_filteredItems_whenResponseOK_withSomeItems() async {
let expectation = XCTestExpectation()
var isLoading: [Bool] = []
// GIVEN
MockURLProtocol.response = .init(
statusCode: 200,
object: Feed(entries: .sample)
)
// WHEN
sut.$isLoading
.collect(3)
.sink { value in
isLoading.append(contentsOf: value)
expectation.fulfill()
}
.store(in: &cancellables)
sut.filter(by: .only1Star)
await sut.fetch()
wait(for: [expectation], timeout: 2)
// THEN
XCTAssertEqual(isLoading, [false, true, false])
XCTAssertEqual(sut.filter, .only1Star)
XCTAssertTrue(sut.isFilterEnabled)
XCTAssertTrue(sut.isWordsShowing)
XCTAssertFalse(sut.items.isEmpty)
XCTAssertEqual(sut.items.count, 1)
XCTAssertEqual(sut.itemsCount, 2)
XCTAssertEqual(sut.state, .populated)
XCTAssertFalse(sut.words.isEmpty)
XCTAssertEqual(sut.words.count, 3)
}
func testFetch_filteredItems_whenResponseOK_withNoItems() async {
let expectation = XCTestExpectation()
var isLoading: [Bool] = []
// GIVEN
MockURLProtocol.response = .init(
statusCode: 200,
object: Feed(entries: .none)
)
// WHEN
sut.$isLoading
.collect(3)
.sink { value in
isLoading.append(contentsOf: value)
expectation.fulfill()
}
.store(in: &cancellables)
sut.filter(by: .only1Star)
await sut.fetch()
wait(for: [expectation], timeout: 2)
// THEN
XCTAssertEqual(isLoading, [false, true, false])
XCTAssertEqual(sut.filter, .only1Star)
XCTAssertFalse(sut.isFilterEnabled)
XCTAssertFalse(sut.isWordsShowing)
XCTAssertTrue(sut.items.isEmpty)
XCTAssertEqual(sut.itemsCount, 0)
XCTAssertEqual(sut.state, .empty)
XCTAssertTrue(sut.words.isEmpty)
}
func testFetch_filteredItems_whenResponseNotOK() async {
let expectation = XCTestExpectation()
var isLoading: [Bool] = []
// GIVEN
MockURLProtocol.response = .init(statusCode: 404)
// WHEN
sut.$isLoading
.collect(3)
.sink { value in
isLoading.append(contentsOf: value)
expectation.fulfill()
}
.store(in: &cancellables)
sut.filter(by: .only1Star)
await sut.fetch()
wait(for: [expectation], timeout: 2)
// THEN
XCTAssertEqual(isLoading, [false, true, false])
XCTAssertEqual(sut.filter, .only1Star)
XCTAssertFalse(sut.isFilterEnabled)
XCTAssertFalse(sut.isWordsShowing)
XCTAssertTrue(sut.items.isEmpty)
XCTAssertEqual(sut.itemsCount, 0)
XCTAssertEqual(sut.state, .error)
XCTAssertTrue(sut.words.isEmpty)
}
func testFilter_forNewOption_withSomeItems() async {
let expectation = XCTestExpectation()
var filter: [FilterOption] = []
// GIVEN
MockURLProtocol.response = .init(
statusCode: 200,
object: Feed(entries: .sample)
)
// WHEN
sut.$filter
.dropFirst()
.sink { value in
filter.append(value)
expectation.fulfill()
}
.store(in: &cancellables)
await sut.fetch()
sut.filter(by: .only1Star)
wait(for: [expectation], timeout: 2)
// THEN
XCTAssertEqual(filter, [.only1Star])
XCTAssertFalse(sut.items.isEmpty)
XCTAssertEqual(sut.items.count, 1)
XCTAssertEqual(sut.itemsCount, 2)
XCTAssertFalse(sut.words.isEmpty)
XCTAssertEqual(sut.words.count, 3)
}
func testFilter_forNewOption_withNoItems() async {
let expectation = XCTestExpectation()
var filter: [FilterOption] = []
// GIVEN
MockURLProtocol.response = .init(
statusCode: 200,
object: Feed(entries: .none)
)
// WHEN
sut.$filter
.dropFirst()
.sink { value in
filter.append(value)
expectation.fulfill()
}
.store(in: &cancellables)
await sut.fetch()
sut.filter(by: .only1Star)
wait(for: [expectation], timeout: 2)
// THEN
XCTAssertEqual(filter, [.only1Star])
XCTAssertTrue(sut.items.isEmpty)
XCTAssertEqual(sut.items.count, 0)
XCTAssertEqual(sut.itemsCount, 0)
XCTAssertTrue(sut.words.isEmpty)
XCTAssertEqual(sut.words.count, 0)
}
func testFilter_forSameOption_withSomeItems() async {
let expectation = XCTestExpectation()
var filter: [FilterOption] = []
// GIVEN
MockURLProtocol.response = .init(
statusCode: 200,
object: Feed(entries: .sample)
)
// WHEN
sut.$filter
.dropFirst()
.sink { value in
filter.append(value)
expectation.fulfill()
}
.store(in: &cancellables)
await sut.fetch()
sut.filter(by: .only1Star)
sut.filter(by: .only1Star)
wait(for: [expectation], timeout: 2)
// THEN
XCTAssertEqual(filter, [.only1Star])
XCTAssertFalse(sut.items.isEmpty)
XCTAssertEqual(sut.items.count, 1)
XCTAssertEqual(sut.itemsCount, 2)
XCTAssertFalse(sut.words.isEmpty)
XCTAssertEqual(sut.words.count, 3)
}
func testFilter_forSameOption_withNoItems() async {
let expectation = XCTestExpectation()
var filter: [FilterOption] = []
// GIVEN
MockURLProtocol.response = .init(
statusCode: 200,
object: Feed(entries: .none)
)
// WHEN
sut.$filter
.dropFirst()
.sink { value in
filter.append(value)
expectation.fulfill()
}
.store(in: &cancellables)
await sut.fetch()
sut.filter(by: .only1Star)
wait(for: [expectation], timeout: 2)
// THEN
XCTAssertEqual(filter, [.only1Star])
XCTAssertTrue(sut.items.isEmpty)
XCTAssertEqual(sut.items.count, 0)
XCTAssertEqual(sut.itemsCount, 0)
XCTAssertTrue(sut.words.isEmpty)
XCTAssertEqual(sut.words.count, 0)
}
func testItemFor_index_withSomeItems() async {
// GIVEN
MockURLProtocol.response = .init(
statusCode: 200,
object: Feed(entries: .sample)
)
// WHEN
await sut.fetch()
let item = sut.item(for: 0)
// THEN
XCTAssertNotNil(item)
}
func testItemFor_indexOutOfBounds_withSomeItems() async {
// GIVEN
MockURLProtocol.response = .init(
statusCode: 200,
object: Feed(entries: .sample)
)
// WHEN
await sut.fetch()
let item = sut.item(for: 10)
// THEN
XCTAssertNil(item)
}
func testItemFor_index_withFilteredItems() async {
// GIVEN
MockURLProtocol.response = .init(
statusCode: 200,
object: Feed(entries: .sample)
)
// WHEN
await sut.fetch()
sut.filter(by: .only1Star)
let item = sut.item(for: 1)
// THEN
XCTAssertNil(item)
}
func testItemFor_indexOutOfBounds_withFilteredItems() async {
// GIVEN
MockURLProtocol.response = .init(
statusCode: 200,
object: Feed(entries: .sample)
)
// WHEN
await sut.fetch()
sut.filter(by: .only1Star)
let item = sut.item(for: 2)
// THEN
XCTAssertNil(item)
}
func testItemFor_index_withNoItems() async {
// GIVEN
MockURLProtocol.response = .init(
statusCode: 200,
object: Feed(entries: .none)
)
// WHEN
await sut.fetch()
let item = sut.item(for: 0)
// THEN
XCTAssertNil(item)
}
}