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
363 lines
18 KiB
Swift
363 lines
18 KiB
Swift
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, "929–936")
|
||
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)
|
||
}
|
||
}
|