Javier Cicchelli 9bcdaa697b [Setup] Basic project structure (#1)
This PR contains all the work related to setting up this project as required to implement the [Assignment](https://repo.rock-n-code.com/rock-n-code/deep-linking-assignment/wiki/Assignment) on top, as intended.

To summarise this work:
- [x] created a new **Xcode** project;
- [x] cloned the `Wikipedia` app and inserted it into the **Xcode** project;
- [x] created the `Locations` app and also, its `Libraries` package;
- [x] created the `Shared` package to share dependencies between the apps;
- [x] added a `Makefile` file and implemented some **environment** and **help** commands.

Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Reviewed-on: rock-n-code/deep-linking-assignment#1
2023-04-08 18:37:13 +00:00

142 lines
5.5 KiB
Swift

@testable import WMF
@testable import Wikipedia
import XCTest
class NotificationsCenterViewModelTests: XCTestCase {
enum TestError: Error {
case failureSettingUpModelController
case failurePullingFixtures
case failureConvertingDataToNotificationsResult
case failurePullingNetworkNotificationModels
case failureSavingNetworkNotificationModelsToDatabase
case failurePullingManagedObjectFromDatabase
case failureConvertingManagedObjectToViewModel
}
var dataFileName: String {
XCTFail("Must override dataFileName.")
return ""
}
private var data: Data!
private var networkModels: [RemoteNotificationsAPIController.NotificationsResult.Notification]!
let dataStore = MWKDataStore.temporary()
lazy var languageLinkController = {
dataStore.languageLinkController
}()
lazy var configuration = {
dataStore.configuration
}()
private let modelController: RemoteNotificationsModelController! = try? RemoteNotificationsModelController.temporaryModelController()
override func setUp(completion: @escaping (Error?) -> Void) {
if let gmtTimeZone = TimeZone(abbreviation: "GMT") {
NSTimeZone.default = gmtTimeZone
}
guard modelController != nil else {
completion(TestError.failureSettingUpModelController)
return
}
if let data = wmf_bundle().wmf_data(fromContentsOfFile: dataFileName, ofType: "json") {
self.data = data
} else {
completion(TestError.failurePullingFixtures)
return
}
let decoder = JSONDecoder()
let networkResult: RemoteNotificationsAPIController.NotificationsResult
do {
networkResult = try decoder.decode(RemoteNotificationsAPIController.NotificationsResult.self, from: data)
} catch {
completion(TestError.failureConvertingDataToNotificationsResult)
return
}
guard let networkModels = networkResult.query?.notifications?.list,
networkModels.count > 0 else {
completion(TestError.failurePullingNetworkNotificationModels)
return
}
self.networkModels = networkModels
saveNetworkModels(networkModels: networkModels) { result in
switch result {
case .success:
completion(nil)
case .failure:
completion(TestError.failureSavingNetworkNotificationModelsToDatabase)
}
}
}
override func tearDown(completion: @escaping (Error?) -> Void) {
NSTimeZone.resetSystemTimeZone()
completion(nil)
}
func fetchManagedObject(identifier: String) throws -> RemoteNotification {
let predicate = NSPredicate(format: "id == %@", identifier)
guard let managedObject = try? self.modelController.fetchNotifications(predicate: predicate).first else {
throw TestError.failurePullingManagedObjectFromDatabase
}
return managedObject
}
func detailViewModelFromIdentifier(identifier: String) throws -> NotificationsCenterDetailViewModel {
let notification = try fetchManagedObject(identifier: identifier)
guard let apiIdentifier = notification.wiki,
let project = WikimediaProject(notificationsApiIdentifier: apiIdentifier, languageLinkController: languageLinkController) else {
throw TestError.failureConvertingManagedObjectToViewModel
}
let commonViewModel = NotificationsCenterCommonViewModel(configuration: configuration, notification: notification, project: project)
return NotificationsCenterDetailViewModel(commonViewModel: commonViewModel)
}
private func saveNetworkModels(networkModels: [RemoteNotificationsAPIController.NotificationsResult.Notification], completion: @escaping (Result<Void, Error>) -> Void) {
let backgroundContext = modelController!.newBackgroundContext()
modelController!.createNewNotifications(moc: backgroundContext, notificationsFetchedFromTheServer: Set(networkModels)) { result in
DispatchQueue.main.async {
switch result {
case .success:
completion(.success(()))
case .failure(let error):
completion(.failure(error))
}
}
}
}
func testActions(expectedText: String, expectedURL: URL?, expectedIcon: NotificationsCenterIconType?, expectedDestinationText: String?, actionToTest: NotificationsCenterAction, isMarkAsRead: Bool = false, isNotificationSettings: Bool = false, actionType: NotificationsCenterActionData.LoggingLabel?) throws {
let expectedActionData = NotificationsCenterActionData(text: expectedText, url: expectedURL, iconType: expectedIcon, destinationText: expectedDestinationText, actionType: actionType)
let expectedAction: NotificationsCenterAction
if isMarkAsRead {
expectedAction = NotificationsCenterAction.markAsReadOrUnread(expectedActionData)
} else if isNotificationSettings {
expectedAction = NotificationsCenterAction.notificationSubscriptionSettings(expectedActionData)
} else {
expectedAction = NotificationsCenterAction.custom(expectedActionData)
}
XCTAssertEqual(expectedAction, actionToTest, "Invalid action")
XCTAssertEqual(expectedActionData.actionType?.stringValue, actionToTest.actionData?.actionType?.stringValue)
}
}