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

363 lines
18 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import XCTest
@testable import WMF
fileprivate class MockSession: Session {
private let data: Data
required init(configuration: Configuration, data: Data) {
self.data = data
super.init(configuration: configuration)
}
required init(configuration: Configuration) {
fatalError("init(configuration:) has not been implemented")
}
override func jsonDecodableTask<T>(with urlRequest: URLRequest, completionHandler: @escaping (T?, URLResponse?, Error?) -> Void) -> URLSessionDataTask? where T : Decodable {
do {
let result: SignificantEvents = try jsonDecodeData(data: data)
completionHandler(result as? T, nil, nil)
} catch let error {
XCTFail("Significant Events json failed to decode \(error)")
}
return nil
}
}
class SignificantEventsFetcherTests: XCTestCase {
fileprivate var firstPageSession: MockSession!
fileprivate var subsequentPageSession: MockSession!
fileprivate var maxCacheSession: MockSession!
fileprivate var beginningSession: MockSession!
fileprivate var templateSession: MockSession!
fileprivate var manyVariationsSession: MockSession!
override func setUpWithError() throws {
if let firstPageData = wmf_bundle().wmf_data(fromContentsOfFile: "SignificantEvents-FirstPage", ofType: "json"),
let subsequentPageData = wmf_bundle().wmf_data(fromContentsOfFile: "SignificantEvents-SubsequentPage", ofType: "json"),
let maxCacheData = wmf_bundle().wmf_data(fromContentsOfFile: "SignificantEvents-MaxCache", ofType: "json"),
let beginningData = wmf_bundle().wmf_data(fromContentsOfFile: "SignificantEvents-Beginning", ofType: "json"),
let templateData = wmf_bundle().wmf_data(fromContentsOfFile: "SignificantEvents-Templates", ofType: "json"),
let manyVariationsData = wmf_bundle().wmf_data(fromContentsOfFile: "SignificantEvents-ViewModelVariations", ofType: "json") {
firstPageSession = MockSession(configuration: Configuration.current, data: firstPageData)
subsequentPageSession = MockSession(configuration: Configuration.current, data: subsequentPageData)
maxCacheSession = MockSession(configuration: Configuration.current, data: maxCacheData)
beginningSession = MockSession(configuration: Configuration.current, data: beginningData)
templateSession = MockSession(configuration: Configuration.current, data: templateData)
manyVariationsSession = MockSession(configuration: Configuration.current, data: manyVariationsData)
} else {
XCTFail("Failure setting up MockSession for SignificantEvents")
}
}
func fetchManyVariationsResult(title: String, siteURL: URL, completion: @escaping (Result<SignificantEvents, Error>) -> Void) {
let fetcher = SignificantEventsFetcher(session: manyVariationsSession, configuration: Configuration.current)
fetcher.fetchSignificantEvents(title: title, siteURL: siteURL, completion: completion)
}
func testFetchFirstPageProducesSignificantEvents() throws {
let fetchExpectation = expectation(description: "Waiting for fetch callback")
let siteURL = URL(string: "https://en.wikipedia.org")!
let title = "United_States"
let fetcher = SignificantEventsFetcher(session: firstPageSession, configuration: Configuration.current)
fetcher.fetchSignificantEvents(title: title, siteURL: siteURL) { (result) in
switch result {
case .success(let significantEvents):
XCTAssertEqual(significantEvents.nextRvStartId, 973922738)
XCTAssertEqual(significantEvents.sha, "5ecb5d13f31361ffd24427a143ec9d32cc83edb0fd99b3af85c98b6b3462a088")
XCTAssertEqual(significantEvents.typedEvents.count, 20)
XCTAssertNotNil(significantEvents.summary)
let summary = significantEvents.summary
XCTAssertEqual(summary.earliestTimestampString, "2020-08-13T20:44:28Z")
XCTAssertEqual(summary.numChanges, 80)
XCTAssertEqual(summary.numUsers, 44)
let smallEvent = significantEvents.typedEvents[0]
switch smallEvent {
case .small(let smallChange):
XCTAssertEqual(smallChange.revId, 975463557)
XCTAssertEqual(smallChange.outputType, .small)
XCTAssertEqual(smallChange.timestampString, "2020-08-28T17:22:52Z")
default:
XCTFail("Unexpected type for firstEvent.")
}
let largeEvent = significantEvents.typedEvents[3]
switch largeEvent {
case .large(let largeChange):
XCTAssertEqual(largeChange.outputType, .large)
XCTAssertEqual(largeChange.revId, 975240668)
XCTAssertEqual(largeChange.timestampString, "2020-08-27T15:11:26Z")
XCTAssertEqual(largeChange.user, "Mason.Jones")
XCTAssertEqual(largeChange.userId, 246091)
XCTAssertEqual(largeChange.userGroups?.count, 4)
XCTAssertEqual(largeChange.userEditCount, 2733)
XCTAssertEqual(largeChange.typedChanges.count, 2)
let firstChange = largeChange.typedChanges[0]
switch firstChange {
case .addedText(let addedText):
XCTAssertEqual(addedText.outputType, .addedText)
XCTAssertEqual(addedText.sections.count, 1)
XCTAssertNotNil(addedText.snippet)
XCTAssertEqual(addedText.snippetType, .addedAndDeletedInLine)
XCTAssertEqual(addedText.characterCount, 133)
default:
XCTFail("Unexpected change type for firstChange.")
}
let secondChange = largeChange.typedChanges[1]
switch secondChange {
case .deletedText(let deletedText):
XCTAssertEqual(deletedText.outputType, .deletedText)
XCTAssertEqual(deletedText.sections.count, 1)
XCTAssertEqual(deletedText.characterCount, 53)
default:
XCTFail("Unexpected change type for secondChange.")
}
default:
XCTFail("Unexpected event type for secondEvent.")
}
case .failure:
XCTFail("Expected Success")
}
fetchExpectation.fulfill()
}
wait(for: [fetchExpectation], timeout: 10)
}
func testFetchSubsequentPageProducesSignificantEvents() throws {
let fetcher = SignificantEventsFetcher(session: subsequentPageSession, configuration: Configuration.current)
let fetchExpectation = expectation(description: "Waiting for fetch callback")
let siteURL = URL(string: "https://en.wikipedia.org")!
let title = "United_States"
fetcher.fetchSignificantEvents(title: title, siteURL: siteURL) { (result) in
switch result {
case .success(let significantEvents):
XCTAssertEqual(significantEvents.nextRvStartId, 972790429)
XCTAssertNil(significantEvents.sha)
XCTAssertEqual(significantEvents.typedEvents.count, 21)
XCTAssertNotNil(significantEvents.summary)
let talkPageEvent = significantEvents.typedEvents[16]
switch talkPageEvent {
case .newTalkPageTopic(let newTalkPageTopic):
XCTAssertEqual(newTalkPageTopic.outputType, .newTalkPageTopic)
XCTAssertEqual(newTalkPageTopic.revId, 973092925)
XCTAssertEqual(newTalkPageTopic.timestampString, "2020-08-15T09:23:08Z")
XCTAssertNotNil(newTalkPageTopic.snippet)
XCTAssertEqual(newTalkPageTopic.user, "Mykhal")
XCTAssertEqual(newTalkPageTopic.userId, 88116)
XCTAssertEqual(newTalkPageTopic.section, "== Discontinuous region category ==")
XCTAssertEqual(newTalkPageTopic.userGroups?.count, 4)
XCTAssertEqual(newTalkPageTopic.userEditCount, 3690)
default:
XCTFail("Unexpected event type for talkPageEvent.")
}
case .failure:
XCTFail("Expected Success")
}
fetchExpectation.fulfill()
}
wait(for: [fetchExpectation], timeout: 10)
}
func testFetchMaxCacheProducesSignificantEvents() throws {
let fetcher = SignificantEventsFetcher(session: maxCacheSession, configuration: Configuration.current)
let fetchExpectation = expectation(description: "Waiting for fetch callback")
let siteURL = URL(string: "https://en.wikipedia.org")!
let title = "United_States"
fetcher.fetchSignificantEvents(title: title, siteURL: siteURL) { (result) in
switch result {
case .success(let significantEvents):
XCTAssertNil(significantEvents.nextRvStartId)
XCTAssertNil(significantEvents.sha)
XCTAssertEqual(significantEvents.typedEvents.count, 0)
XCTAssertNotNil(significantEvents.summary)
XCTAssertEqual(significantEvents.summary.earliestTimestampString, "2020-06-14T01:07:03Z")
XCTAssertEqual(significantEvents.summary.numChanges, 282)
XCTAssertEqual(significantEvents.summary.numUsers, 112)
case .failure:
XCTFail("Expected Success")
}
fetchExpectation.fulfill()
}
wait(for: [fetchExpectation], timeout: 10)
}
func testFetchBeginningProducesSignificantEvents() throws {
let fetcher = SignificantEventsFetcher(session: beginningSession, configuration: Configuration.current)
let fetchExpectation = expectation(description: "Waiting for fetch callback")
let siteURL = URL(string: "https://en.wikipedia.org")!
let title = "United_States"
fetcher.fetchSignificantEvents(title: title, siteURL: siteURL) { (result) in
switch result {
case .success(let significantEvents):
XCTAssertEqual(significantEvents.nextRvStartId, 0)
XCTAssertNil(significantEvents.sha)
XCTAssertEqual(significantEvents.typedEvents.count, 13)
XCTAssertNotNil(significantEvents.summary)
case .failure:
XCTFail("Expected Success")
}
fetchExpectation.fulfill()
}
wait(for: [fetchExpectation], timeout: 10)
}
func testFetchTemplatesProducesSignificantEvents() throws {
let fetcher = SignificantEventsFetcher(session: templateSession, configuration: Configuration.current)
let fetchExpectation = expectation(description: "Waiting for fetch callback")
let siteURL = URL(string: "https://en.wikipedia.org")!
let title = "United_States"
fetcher.fetchSignificantEvents(title: title, siteURL: siteURL) { (result) in
switch result {
case .success(let significantEvents):
XCTAssertEqual(significantEvents.nextRvStartId, 973922738)
XCTAssertEqual(significantEvents.sha, "5ecb5d13f31361ffd24427a143ec9d32cc83edb0fd99b3af85c98b6b3462a088")
XCTAssertEqual(significantEvents.typedEvents.count, 1)
XCTAssertNotNil(significantEvents.summary)
switch significantEvents.typedEvents[0] {
case .large(let largeChange):
XCTAssertEqual(largeChange.revId, 670576931)
XCTAssertEqual(largeChange.timestampString, "2015-07-08T21:16:25Z")
XCTAssertEqual(largeChange.user, "CoffeeWithMarkets")
XCTAssertEqual(largeChange.userId, 17771490)
XCTAssertEqual(largeChange.typedChanges.count, 2)
let firstChange = largeChange.typedChanges[0]
switch firstChange {
case .newTemplate(let newTemplate):
XCTAssertEqual(newTemplate.sections, ["==Maine Coon=="])
XCTAssertEqual(newTemplate.typedTemplates.count, 12)
let webTemplate = newTemplate.typedTemplates[1]
switch webTemplate {
case .websiteCitation(let webTemplate):
XCTAssertEqual(webTemplate.title, "No-Vacation Nation Revisited")
XCTAssertEqual(webTemplate.urlString, "http://www.cepr.net/documents/publications/no-vacation-update-2013-05.pdf")
XCTAssertEqual(webTemplate.publisher, "[[Center for Economic and Policy Research]]")
XCTAssertEqual(webTemplate.accessDateString, "September 8, 2013")
XCTAssertNil(webTemplate.archiveDateString)
XCTAssertNil(webTemplate.archiveDotOrgUrlString)
default:
XCTFail("Unexpected template type")
}
let newsTemplate = newTemplate.typedTemplates[6]
switch newsTemplate {
case .newsCitation(let newsTemplate):
XCTAssertEqual(newsTemplate.title, "Mexico crime belies government claims of progress")
XCTAssertEqual(newsTemplate.urlString, "https://www.usatoday.com/story/news/world/2014/10/18/mexico-violence-crime/17048757")
XCTAssertEqual(newsTemplate.firstName, "David")
XCTAssertEqual(newsTemplate.lastName, "Agren")
XCTAssertEqual(newsTemplate.accessDateString, "October 19, 2014")
XCTAssertEqual(newsTemplate.sourceDateString, "October 19, 2014")
XCTAssertEqual(newsTemplate.publication, "Florida Today—USA Today")
default:
XCTFail("Unexpected template type")
}
let bookTemplate = newTemplate.typedTemplates[10]
switch bookTemplate {
case .bookCitation(let bookTemplate):
XCTAssertEqual(bookTemplate.title, "A People's History of the United States")
XCTAssertEqual(bookTemplate.firstName, "Howard")
XCTAssertEqual(bookTemplate.lastName, "Zinn")
XCTAssertEqual(bookTemplate.isbn, "978-0-06-083865-2")
XCTAssertNil(bookTemplate.locationPublished)
XCTAssertNil(bookTemplate.pagesCited)
XCTAssertEqual(bookTemplate.publisher, "[[Harper Perennial]] Modern Classics")
XCTAssertEqual(bookTemplate.yearPublished, "2005")
default:
XCTFail("Unexpected template type")
}
let journalTemplate = newTemplate.typedTemplates[11]
switch journalTemplate {
case .journalCitation(let journalTemplate):
XCTAssertEqual(journalTemplate.journal, "N. Engl. J. Med.")
XCTAssertEqual(journalTemplate.title, "First Case of 2019 Novel Coronavirus in the United States")
XCTAssertEqual(journalTemplate.pages, "929936")
XCTAssertNil(journalTemplate.lastName)
XCTAssertNil(journalTemplate.firstName)
XCTAssertEqual(journalTemplate.sourceDateString, "March 2020")
XCTAssertEqual(journalTemplate.volumeNumber, "382")
XCTAssertNil(journalTemplate.urlString)
XCTAssertNil(journalTemplate.database)
default:
XCTFail("Unexpected template type")
}
default:
XCTFail("Unexpected first significant change type")
}
let secondChange = largeChange.typedChanges[1]
switch secondChange {
case .addedText(let addedText):
XCTAssertNotNil(addedText.snippet)
XCTAssertEqual(addedText.characterCount, 775)
XCTAssertEqual(addedText.sections, ["==Maine Coon=="])
default:
XCTFail("Unexpected second change type")
}
default:
XCTFail("Unexpected event type")
}
case .failure:
XCTFail("Expected Success")
}
fetchExpectation.fulfill()
}
wait(for: [fetchExpectation], timeout: 10)
}
}