// // AnalyticsGeneratorTests.swift // // // Created by Thibaut Schmitt on 06/09/2022. // import Foundation import XCTest import ToolCore @testable import ResgenSwift final class AnalyticsGeneratorTests: XCTestCase { private func getAnalyticsDefinition( id: String, path: String = "", action: String = "", category: String = "", name: String, type: AnalyticsDefinition.TagType, tags: [String] ) -> AnalyticsDefinition { let definition = AnalyticsDefinition(id: id, name: name, type: type) definition.tags = tags definition.path = path definition.action = action definition.category = category return definition } func testGeneratedExtensionContentFirebase() { // Given let sectionOne = AnalyticsCategory(id: "section_one") sectionOne.definitions = [ getAnalyticsDefinition(id: "s1_def_one", name: "s1 def one", type: .screen, tags: ["ios", "iosonly"]), getAnalyticsDefinition(id: "s1_def_two", name: "s1 def two", type: .event, tags: ["ios", "iosonly"]), ] let sectionTwo = AnalyticsCategory(id: "section_two") sectionTwo.definitions = [ getAnalyticsDefinition(id: "s2_def_one", name: "s2 def one", type: .screen, tags: ["ios","iosonly"]), getAnalyticsDefinition(id: "s2_def_two", name: "s2 def two", type: .event, tags: ["droid","droidonly"]), ] let sectionThree = AnalyticsCategory(id: "section_three") sectionThree.definitions = [ getAnalyticsDefinition(id: "s3_def_one", name: "s3 def one", type: .screen, tags: ["droid","droidonly"]), getAnalyticsDefinition(id: "s3_def_two", name: "s3 def two", type: .event, tags: ["droid","droidonly"]), ] // When AnalyticsGenerator.targets = [Analytics.TargetType.firebase] let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], tags: ["ios", "iosonly"], staticVar: false, extensionName: "GenAnalytics") // Expect Analytics let expect = """ // Generated by ResgenSwift.Analytics 1.2 import Firebase // MARK: - Protocol protocol AnalyticsManagerProtocol { func logScreen(name: String, path: String) func logEvent( name: String, action: String, category: String, params: [String: Any]? ) } // MARK: - Firebase class FirebaseAnalyticsManager: AnalyticsManagerProtocol { func logScreen(name: String, path: String) { var parameters = [ AnalyticsParameterScreenName: name ] Analytics.logEvent( AnalyticsEventScreenView, parameters: parameters ) } func logEvent( name: String, action: String, category: String, params: [String: Any]? ) { var parameters: [String:Any] = [ "action": action, "category": category, ] if let supplementaryParameters = params { parameters.merge(supplementaryParameters) { (origin, new) -> Any in return origin } } Analytics.logEvent( name, parameters: parameters ) } } // MARK: - Manager class AnalyticsManager { static var shared = AnalyticsManager() // MARK: - Properties var managers: [AnalyticsManagerProtocol] = [] private var isEnabled: Bool = true // MARK: - Methods func setAnalyticsEnabled(_ enable: Bool) { isEnabled = enable } func configure() { managers.append(FirebaseAnalyticsManager()) } private func logScreen(name: String, path: String) { guard isEnabled else { return } managers.forEach { manager in manager.logScreen(name: name, path: path) } } private func logEvent( name: String, action: String, category: String, params: [String: Any]? ) { guard isEnabled else { return } managers.forEach { manager in manager.logEvent( name: name, action: action, category: category, params: params ) } } // MARK: - section_one func logScreenS1DefOne() { logScreen( name: "s1 def one", path: "" ) } func logEventS1DefTwo() { logEvent( name: "s1 def two", action: "", category: "", params: [] ) } // MARK: - section_two func logScreenS2DefOne() { logScreen( name: "s2 def one", path: "" ) } } """ if extensionContent != expect { print(prettyFirstDifferenceBetweenStrings(s1: extensionContent, s2: expect)) } XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest()) } func testGeneratedExtensionContentMatomo() { // Given let sectionOne = AnalyticsCategory(id: "section_one") sectionOne.definitions = [ getAnalyticsDefinition(id: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: .screen, tags: ["ios", "iosonly"]), getAnalyticsDefinition(id: "s1_def_two", action: "test", category: "test", name: "s1 def two", type: .event, tags: ["ios", "iosonly"]), ] let sectionTwo = AnalyticsCategory(id: "section_two") sectionTwo.definitions = [ getAnalyticsDefinition(id: "s2_def_one", path: "s2_def_one/", name: "s2 def one", type: .screen, tags: ["ios","iosonly"]), getAnalyticsDefinition(id: "s2_def_two", action: "test", category: "test", name: "s2 def two", type: .event, tags: ["droid","droidonly"]), ] let sectionThree = AnalyticsCategory(id: "section_three") sectionThree.definitions = [ getAnalyticsDefinition(id: "s3_def_one", path: "s3_def_one/", name: "s3 def one", type: .screen, tags: ["droid","droidonly"]), getAnalyticsDefinition(id: "s3_def_two", action: "test", category: "test", name: "s3 def two", type: .event, tags: ["droid","droidonly"]), ] // When AnalyticsGenerator.targets = [Analytics.TargetType.matomo] let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], tags: ["ios", "iosonly"], staticVar: false, extensionName: "GenAnalytics") // Expect Analytics let expect = """ // Generated by ResgenSwift.Analytics 1.2 import MatomoTracker // MARK: - Protocol protocol AnalyticsManagerProtocol { func logScreen(name: String, path: String) func logEvent( name: String, action: String, category: String, params: [String: Any]? ) } // MARK: - Matomo class MatomoAnalyticsManager: AnalyticsManagerProtocol { // MARK: - Properties private var tracker: MatomoTracker // MARK: - Init init(siteId: String, url: String) { debugPrint("[Matomo service] Server URL: \\(url)") debugPrint("[Matomo service] Site ID: \\(siteId)") tracker = MatomoTracker( siteId: siteId, baseURL: URL(string: url)! ) #if DEBUG tracker.dispatchInterval = 5 #endif #if DEBUG tracker.logger = DefaultLogger(minLevel: .verbose) #endif debugPrint("[Matomo service] Configured with content base: \\(tracker.contentBase?.absoluteString ?? "-")") debugPrint("[Matomo service] Opt out: \\(tracker.isOptedOut)") } // MARK: - Methods func logScreen(name: String, path: String) { guard !tracker.isOptedOut else { return } guard let trackerUrl = tracker.contentBase?.absoluteString else { return } let urlString = URL(string: "\\(trackerUrl)" + "/" + "\\(path)" + "iOS") tracker.track( view: [name], url: urlString ) } func logEvent( name: String, action: String, category: String, params: [String: Any]? ) { guard !tracker.isOptedOut else { return } tracker.track( eventWithCategory: category, action: action, name: name, number: nil, url: nil ) } } // MARK: - Manager class AnalyticsManager { static var shared = AnalyticsManager() // MARK: - Properties var managers: [AnalyticsManagerProtocol] = [] private var isEnabled: Bool = true // MARK: - Methods func setAnalyticsEnabled(_ enable: Bool) { isEnabled = enable } func configure(siteId: String, url: String) { managers.append( MatomoAnalyticsManager( siteId: siteId, url: url ) ) } private func logScreen(name: String, path: String) { guard isEnabled else { return } managers.forEach { manager in manager.logScreen(name: name, path: path) } } private func logEvent( name: String, action: String, category: String, params: [String: Any]? ) { guard isEnabled else { return } managers.forEach { manager in manager.logEvent( name: name, action: action, category: category, params: params ) } } // MARK: - section_one func logScreenS1DefOne() { logScreen( name: "s1 def one", path: "s1_def_one/" ) } func logEventS1DefTwo() { logEvent( name: "s1 def two", action: "test", category: "test", params: [] ) } // MARK: - section_two func logScreenS2DefOne() { logScreen( name: "s2 def one", path: "s2_def_one/" ) } } """ if extensionContent != expect { print(prettyFirstDifferenceBetweenStrings(s1: extensionContent, s2: expect)) } XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest()) } func testGeneratedExtensionContentMatomoAndFirebase() { // Given let sectionOne = AnalyticsCategory(id: "section_one") sectionOne.definitions = [ getAnalyticsDefinition(id: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: .screen, tags: ["ios", "iosonly"]), getAnalyticsDefinition(id: "s1_def_two", action: "test", category: "test", name: "s1 def two", type: .event, tags: ["ios", "iosonly"]), ] let sectionTwo = AnalyticsCategory(id: "section_two") sectionTwo.definitions = [ getAnalyticsDefinition(id: "s2_def_one", path: "s2_def_one/", name: "s2 def one", type: .screen, tags: ["ios","iosonly"]), getAnalyticsDefinition(id: "s2_def_two", action: "test", category: "test", name: "s2 def two", type: .event, tags: ["droid","droidonly"]), ] let sectionThree = AnalyticsCategory(id: "section_three") sectionThree.definitions = [ getAnalyticsDefinition(id: "s3_def_one", path: "s3_def_one/", name: "s3 def one", type: .screen, tags: ["droid","droidonly"]), getAnalyticsDefinition(id: "s3_def_two", action: "test", category: "test", name: "s3 def two", type: .event, tags: ["droid","droidonly"]), ] // When AnalyticsGenerator.targets = [Analytics.TargetType.matomo, Analytics.TargetType.firebase] let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], tags: ["ios", "iosonly"], staticVar: false, extensionName: "GenAnalytics") // Expect Analytics let expect = """ // Generated by ResgenSwift.Analytics 1.2 import MatomoTracker import Firebase // MARK: - Protocol protocol AnalyticsManagerProtocol { func logScreen(name: String, path: String) func logEvent( name: String, action: String, category: String, params: [String: Any]? ) } // MARK: - Matomo class MatomoAnalyticsManager: AnalyticsManagerProtocol { // MARK: - Properties private var tracker: MatomoTracker // MARK: - Init init(siteId: String, url: String) { debugPrint("[Matomo service] Server URL: \\(url)") debugPrint("[Matomo service] Site ID: \\(siteId)") tracker = MatomoTracker( siteId: siteId, baseURL: URL(string: url)! ) #if DEBUG tracker.dispatchInterval = 5 #endif #if DEBUG tracker.logger = DefaultLogger(minLevel: .verbose) #endif debugPrint("[Matomo service] Configured with content base: \\(tracker.contentBase?.absoluteString ?? "-")") debugPrint("[Matomo service] Opt out: \\(tracker.isOptedOut)") } // MARK: - Methods func logScreen(name: String, path: String) { guard !tracker.isOptedOut else { return } guard let trackerUrl = tracker.contentBase?.absoluteString else { return } let urlString = URL(string: "\\(trackerUrl)" + "/" + "\\(path)" + "iOS") tracker.track( view: [name], url: urlString ) } func logEvent( name: String, action: String, category: String, params: [String: Any]? ) { guard !tracker.isOptedOut else { return } tracker.track( eventWithCategory: category, action: action, name: name, number: nil, url: nil ) } } // MARK: - Firebase class FirebaseAnalyticsManager: AnalyticsManagerProtocol { func logScreen(name: String, path: String) { var parameters = [ AnalyticsParameterScreenName: name ] Analytics.logEvent( AnalyticsEventScreenView, parameters: parameters ) } func logEvent( name: String, action: String, category: String, params: [String: Any]? ) { var parameters: [String:Any] = [ "action": action, "category": category, ] if let supplementaryParameters = params { parameters.merge(supplementaryParameters) { (origin, new) -> Any in return origin } } Analytics.logEvent( name, parameters: parameters ) } } // MARK: - Manager class AnalyticsManager { static var shared = AnalyticsManager() // MARK: - Properties var managers: [AnalyticsManagerProtocol] = [] private var isEnabled: Bool = true // MARK: - Methods func setAnalyticsEnabled(_ enable: Bool) { isEnabled = enable } func configure(siteId: String, url: String) { managers.append( MatomoAnalyticsManager( siteId: siteId, url: url ) ) managers.append(FirebaseAnalyticsManager()) } private func logScreen(name: String, path: String) { guard isEnabled else { return } managers.forEach { manager in manager.logScreen(name: name, path: path) } } private func logEvent( name: String, action: String, category: String, params: [String: Any]? ) { guard isEnabled else { return } managers.forEach { manager in manager.logEvent( name: name, action: action, category: category, params: params ) } } // MARK: - section_one func logScreenS1DefOne() { logScreen( name: "s1 def one", path: "s1_def_one/" ) } func logEventS1DefTwo() { logEvent( name: "s1 def two", action: "test", category: "test", params: [] ) } // MARK: - section_two func logScreenS2DefOne() { logScreen( name: "s2 def one", path: "s2_def_one/" ) } } """ if extensionContent != expect { print(prettyFirstDifferenceBetweenStrings(s1: extensionContent, s2: expect)) } XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest()) } }