mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 00:08:39 +09:00
Dactyloidae iOS initial commit
This commit is contained in:
parent
daa6179d22
commit
7154a0497e
2123 changed files with 197052 additions and 0 deletions
284
mobile/ios/ClientTests/ActivityStreamTests.swift
Normal file
284
mobile/ios/ClientTests/ActivityStreamTests.swift
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import UIKit
|
||||
import XCTest
|
||||
@testable import Client
|
||||
import Shared
|
||||
import Storage
|
||||
import Deferred
|
||||
import SyncTelemetry
|
||||
|
||||
class ActivityStreamTests: XCTestCase {
|
||||
var profile: MockProfile!
|
||||
var panel: ActivityStreamPanel!
|
||||
var mockPingClient: MockPingClient!
|
||||
var telemetry: ActivityStreamTracker!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
self.profile = MockProfile()
|
||||
self.telemetry = ActivityStreamTracker(eventsTracker: MockPingClient(), sessionsTracker: MockPingClient())
|
||||
self.panel = ActivityStreamPanel(profile: profile, telemetry: self.telemetry)
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
mockPingClient = nil
|
||||
}
|
||||
|
||||
func testDeletionOfSingleSuggestedSite() {
|
||||
let siteToDelete = panel.defaultTopSites()[0]
|
||||
|
||||
panel.hideURLFromTopSites(siteToDelete)
|
||||
let newSites = panel.defaultTopSites()
|
||||
|
||||
XCTAssertFalse(newSites.contains(siteToDelete, f: { (a, b) -> Bool in
|
||||
return a.url == b.url
|
||||
}))
|
||||
}
|
||||
|
||||
func testDeletionOfAllDefaultSites() {
|
||||
let defaultSites = panel.defaultTopSites()
|
||||
defaultSites.forEach({
|
||||
panel.hideURLFromTopSites($0)
|
||||
})
|
||||
|
||||
let newSites = panel.defaultTopSites()
|
||||
XCTAssertTrue(newSites.isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Telemetry Tests
|
||||
extension ActivityStreamTests {
|
||||
fileprivate func expectedPayloadForEvent(_ event: String, source: String, position: Int) -> [String: Any] {
|
||||
return [
|
||||
"event": event,
|
||||
"page": "NEW_TAB",
|
||||
"source": source,
|
||||
"action_position": position,
|
||||
"app_version": AppInfo.appVersion,
|
||||
"build": AppInfo.buildNumber,
|
||||
"locale": Locale.current.identifier,
|
||||
"release_channel": AppConstants.BuildChannel.rawValue
|
||||
]
|
||||
}
|
||||
|
||||
fileprivate func expectedBadStatePayload(state: String, source: String) -> [String: Any] {
|
||||
return [
|
||||
"event": state,
|
||||
"page": "NEW_TAB",
|
||||
"source": source,
|
||||
"app_version": AppInfo.appVersion,
|
||||
"build": AppInfo.buildNumber,
|
||||
"locale": Locale.current.identifier,
|
||||
"release_channel": AppConstants.BuildChannel.rawValue
|
||||
]
|
||||
}
|
||||
|
||||
fileprivate func assertPayload(_ actual: [String: Any], matches: [String: Any]) {
|
||||
XCTAssertTrue(actual.count == matches.count)
|
||||
actual.enumerated().forEach { index, element in
|
||||
if let actualValue = element.1 as? Int,
|
||||
let matchesValue = matches[element.0] as? Int {
|
||||
XCTAssertTrue(actualValue == matchesValue)
|
||||
}
|
||||
|
||||
if let actualValue = element.1 as? String,
|
||||
let matchesValue = matches[element.0] as? String {
|
||||
XCTAssertTrue(actualValue == matchesValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testHighlightEmitsEventOnTap() {
|
||||
let mockSite = Site(url: "http://mozilla.org", title: "Mozilla")
|
||||
panel.highlights = [mockSite]
|
||||
panel.selectItemAtIndex(0, inSection: .highlights)
|
||||
|
||||
let pingsSent = (telemetry.eventsTracker as! MockPingClient).pingsReceived
|
||||
XCTAssertEqual(pingsSent.count, 1)
|
||||
let eventPing = pingsSent[0]
|
||||
assertPayload(eventPing, matches: expectedPayloadForEvent("CLICK", source: "HIGHLIGHTS", position: 0))
|
||||
}
|
||||
|
||||
func testContextMenuOnTopSiteEmitsRemoveEvent() {
|
||||
let mockSite = Site(url: "http://mozilla.org", title: "Mozilla")
|
||||
let topSitesContextMenu = panel.contextMenu(for: mockSite, with: IndexPath(item: 0, section: ActivityStreamPanel.Section.topSites.rawValue))
|
||||
|
||||
let removeAction = topSitesContextMenu?.actions[0].find { $0.title == Strings.RemoveContextMenuTitle }
|
||||
removeAction?.handler?(removeAction!)
|
||||
|
||||
let pingsSent = (telemetry.eventsTracker as! MockPingClient).pingsReceived
|
||||
XCTAssertEqual(pingsSent.count, 1)
|
||||
let removePing = pingsSent[0]
|
||||
assertPayload(removePing, matches: expectedPayloadForEvent("REMOVE", source: "TOP_SITES", position: 0))
|
||||
}
|
||||
|
||||
func testContextMenuOnHighlightsEmitsRemoveDismissEvents() {
|
||||
let mockSite = Site(url: "http://mozilla.org", title: "Mozilla")
|
||||
let highlightsContextMenu = panel.contextMenu(for: mockSite, with: IndexPath(row: 0, section: ActivityStreamPanel.Section.highlights.rawValue))
|
||||
|
||||
let dismiss = highlightsContextMenu?.actions[0].find { $0.title == Strings.RemoveContextMenuTitle }
|
||||
let delete = highlightsContextMenu?.actions[0].find { $0.title == Strings.DeleteFromHistoryContextMenuTitle }
|
||||
|
||||
dismiss?.handler?(dismiss!)
|
||||
delete?.handler?(delete!)
|
||||
|
||||
// Check to see that they emitted telemetry events
|
||||
let pingsSent = (telemetry.eventsTracker as! MockPingClient).pingsReceived
|
||||
XCTAssertEqual(pingsSent.count, 2)
|
||||
|
||||
let dismissPing = pingsSent[0]
|
||||
assertPayload(dismissPing, matches: expectedPayloadForEvent("DISMISS", source: "HIGHLIGHTS", position: 0))
|
||||
|
||||
let deletePing = pingsSent[1]
|
||||
assertPayload(deletePing, matches: expectedPayloadForEvent("DELETE", source: "HIGHLIGHTS", position: 0))
|
||||
}
|
||||
|
||||
func testSessionReportedWhenViewAppearsAndDisappears() {
|
||||
// Simulate the panel opening and closing with a second in between for some session_duration
|
||||
panel.viewWillAppear(false)
|
||||
var pingsSent = (telemetry.sessionsTracker as! MockPingClient).pingsReceived
|
||||
XCTAssertEqual(pingsSent.count, 0)
|
||||
|
||||
wait(1)
|
||||
panel.viewDidDisappear(false)
|
||||
|
||||
pingsSent = (telemetry.sessionsTracker as! MockPingClient).pingsReceived
|
||||
XCTAssertEqual(pingsSent.count, 1)
|
||||
|
||||
let eventPing = pingsSent[0]
|
||||
XCTAssertNotNil(eventPing["session_duration"])
|
||||
}
|
||||
|
||||
func testBadStateEventsForHighlights() {
|
||||
let goodSite = Site(url: "http://mozilla.org", title: "Mozilla")
|
||||
goodSite.icon = Favicon(url: "http://image", date: Date(), type: .local)
|
||||
goodSite.metadata = PageMetadata(id: nil,
|
||||
siteURL: "http://mozilla.org",
|
||||
mediaURL: "http://image",
|
||||
title: "Mozilla",
|
||||
description: "Web",
|
||||
type: nil,
|
||||
providerName: nil,
|
||||
mediaDataURI: nil)
|
||||
let badSite = Site(url: "http://mozilla.org", title: "Mozilla")
|
||||
profile.recommendations = MockRecommender(highlights: [goodSite, badSite])
|
||||
|
||||
// Since invalidateHighlights calls back into the main thread, we can't
|
||||
// simply call .value on this to block since the app will dead lock when
|
||||
// trying to call back onto a blocked main thread.
|
||||
let expect = XCTestExpectation(description: "Sent bad highlight pings")
|
||||
panel.getHighlights() >>> {
|
||||
expect.fulfill()
|
||||
}
|
||||
|
||||
wait(for: [expect], timeout: 3)
|
||||
let pingsSent = (self.telemetry.eventsTracker as! MockPingClient).pingsReceived
|
||||
XCTAssertEqual(pingsSent.count, 2)
|
||||
assertPayload(pingsSent[0],
|
||||
matches: expectedBadStatePayload(state: "MISSING_METADATA_IMAGE", source: "HIGHLIGHTS"))
|
||||
assertPayload(pingsSent[1],
|
||||
matches: expectedBadStatePayload(state: "MISSING_FAVICON", source: "HIGHLIGHTS"))
|
||||
}
|
||||
|
||||
func testBadStateEventsForTopSites() {
|
||||
let goodSite = Site(url: "http://mozilla.org", title: "Mozilla")
|
||||
goodSite.icon = Favicon(url: "http://image", date: Date(), type: .local)
|
||||
goodSite.metadata = PageMetadata(id: nil,
|
||||
siteURL: "http://mozilla.org",
|
||||
mediaURL: "http://image",
|
||||
title: "Mozilla",
|
||||
description: "Web",
|
||||
type: nil,
|
||||
providerName: nil,
|
||||
mediaDataURI: nil)
|
||||
let badSite = Site(url: "http://mozilla.org", title: "Mozilla")
|
||||
profile.history = MockTopSitesHistory(sites: [goodSite, badSite])
|
||||
|
||||
// Since invalidateHighlights calls back into the main thread, we can't
|
||||
// simply call .value on this to block since the app will dead lock when
|
||||
// trying to call back onto a blocked main thread.
|
||||
let expect = XCTestExpectation(description: "Sent bad top site pings")
|
||||
panel.getTopSites() >>> {
|
||||
expect.fulfill()
|
||||
}
|
||||
|
||||
wait(for: [expect], timeout: 3)
|
||||
let pingsSent = (self.telemetry.eventsTracker as! MockPingClient).pingsReceived
|
||||
XCTAssertEqual(pingsSent.count, 2)
|
||||
assertPayload(pingsSent[0],
|
||||
matches: expectedBadStatePayload(state: "MISSING_METADATA_IMAGE", source: "TOP_SITES"))
|
||||
assertPayload(pingsSent[1],
|
||||
matches: expectedBadStatePayload(state: "MISSING_FAVICON", source: "TOP_SITES"))
|
||||
}
|
||||
}
|
||||
|
||||
class MockPingClient: PingCentreClient {
|
||||
|
||||
var pingsReceived: [[String: Any]] = []
|
||||
|
||||
public func sendPing(_ data: [String : Any], validate: Bool) -> Success {
|
||||
pingsReceived.append(data)
|
||||
return succeed()
|
||||
}
|
||||
|
||||
public func sendBatch(_ data: [[String : Any]], validate: Bool) -> Success {
|
||||
pingsReceived += data
|
||||
return succeed()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class MockRecommender: HistoryRecommendations {
|
||||
func repopulateHighlights() -> Success {
|
||||
return succeed()
|
||||
}
|
||||
|
||||
var highlights: [Site]
|
||||
|
||||
init(highlights: [Site]) {
|
||||
self.highlights = highlights
|
||||
}
|
||||
|
||||
func getHighlights() -> Deferred<Maybe<Cursor<Site>>> {
|
||||
return deferMaybe(ArrayCursor(data: highlights))
|
||||
}
|
||||
|
||||
func getRecentBookmarks(_ limit: Int) -> Deferred<Maybe<Cursor<Site>>> {
|
||||
return deferMaybe(ArrayCursor(data: []))
|
||||
}
|
||||
|
||||
func repopulate(invalidateTopSites shouldInvalidateTopSites: Bool, invalidateHighlights shouldInvalidateHighlights: Bool) -> Success {
|
||||
return succeed()
|
||||
}
|
||||
|
||||
func removeHighlightForURL(_ url: String) -> Success {
|
||||
guard let foundSite = highlights.filter({ $0.url == url }).first else {
|
||||
return succeed()
|
||||
}
|
||||
let foundIndex = highlights.index(of: foundSite)!
|
||||
highlights.remove(at: foundIndex)
|
||||
return succeed()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class MockTopSitesHistory: MockableHistory {
|
||||
let mockTopSites: [Site]
|
||||
|
||||
init(sites: [Site]) {
|
||||
mockTopSites = sites
|
||||
}
|
||||
|
||||
override func getTopSitesWithLimit(_ limit: Int) -> Deferred<Maybe<Cursor<Site>>> {
|
||||
return deferMaybe(ArrayCursor(data: mockTopSites))
|
||||
}
|
||||
|
||||
override func getPinnedTopSites() -> Deferred<Maybe<Cursor<Site>>> {
|
||||
return deferMaybe(ArrayCursor(data: []))
|
||||
}
|
||||
|
||||
override func updateTopSitesCacheIfInvalidated() -> Deferred<Maybe<Bool>> {
|
||||
return deferMaybe(true)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue