fix: Tags -> Anlytics

This commit is contained in:
2023-12-08 11:29:29 +01:00
parent 09c153ba65
commit 3fc2fd9bac
21 changed files with 930 additions and 550 deletions

View File

@ -0,0 +1,120 @@
//
// AnalyticsDefinitionTests.swift
//
//
// Created by Loris Perret on 06/12/2023.
//
import Foundation
import XCTest
@testable import ResgenSwift
final class AnalyticsDefinitionTests: XCTestCase {
// MARK: - Matching tags
func testMatchingAnalyticss() {
// Given
let definition = AnalyticsDefinition(id: "definition_name", name: "", type: .screen)
definition.tags = ["ios","iosonly","notranslation"]
// When
let match1 = definition.hasOneOrMoreMatchingTags(inputTags: ["ios"])
let match2 = definition.hasOneOrMoreMatchingTags(inputTags: ["iosonly"])
let match3 = definition.hasOneOrMoreMatchingTags(inputTags: ["notranslation"])
// Expect
XCTAssertTrue(match1)
XCTAssertTrue(match2)
XCTAssertTrue(match3)
}
func testNotMatchingAnalyticss() {
// Given
let definition = AnalyticsDefinition(id: "definition_name", name: "", type: .screen)
definition.tags = ["ios","iosonly","notranslation"]
// When
let match1 = definition.hasOneOrMoreMatchingTags(inputTags: ["droid"])
let match2 = definition.hasOneOrMoreMatchingTags(inputTags: ["droidonly"])
let match3 = definition.hasOneOrMoreMatchingTags(inputTags: ["azerty"])
// Expect
XCTAssertFalse(match1)
XCTAssertFalse(match2)
XCTAssertFalse(match3)
}
// MARK: - Raw properties
func testGeneratedRawPropertyScreen() {
// Given
let definition = AnalyticsDefinition(id: "definition_name", name: "Ecran un", type: .screen)
// When
let propertyScreen = definition.getProperty()
// Expect
let expectScreen = """
func logScreenEcranUn() {
logScreen(name: "Ecran un", path: "ecran_un/")
}
"""
XCTAssertEqual(propertyScreen.adaptForXCTest(), expectScreen.adaptForXCTest())
}
func testGeneratedRawPropertyEvent() {
// Given
let definition = AnalyticsDefinition(id: "definition_name", name: "Ecran un", type: .screen)
// When
let propertyEvent = definition.getProperty()
// Expect
let expectEvent = """
func logEventEcranUn() {
logEvent(name: "Ecran un")
}
"""
XCTAssertEqual(propertyEvent.adaptForXCTest(), expectEvent.adaptForXCTest())
}
func testGeneratedRawStaticPropertyScreen() {
// Given
let definition = AnalyticsDefinition(id: "definition_name", name: "Ecran un", type: .screen)
// When
let propertyScreen = definition.getStaticProperty()
// Expect
let expectScreen = """
static func logScreenEcranUn() {
logScreen(name: "Ecran un", path: "ecran_un/")
}
"""
XCTAssertEqual(propertyScreen.adaptForXCTest(), expectScreen.adaptForXCTest())
}
func testGeneratedRawStaticPropertyEvent() {
// Given
let definition = AnalyticsDefinition(id: "definition_name", name: "Ecran un", type: .screen)
// When
let propertyEvent = definition.getStaticProperty()
// Expect
let expectEvent = """
static func logEventEcranUn() {
logEvent(name: "Ecran un")
}
"""
XCTAssertEqual(propertyEvent.adaptForXCTest(), expectEvent.adaptForXCTest())
}
}

View File

@ -0,0 +1,451 @@
//
// 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, name: String, type: AnalyticsDefinition.TagType, tags: [String]) -> AnalyticsDefinition {
let definition = AnalyticsDefinition(id: id, name: name, type: type)
definition.tags = tags
return definition
}
func testGeneratedExtensionContentFirebase() {
// Given
let sectionOne = AnalyticsCategory(id: "section_one")
sectionOne.definitions = [
getAnalyticsDefinition(id: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: AnalyticsDefinition.TagType.screen, tags: ["ios", "iosonly"]),
getAnalyticsDefinition(id: "s1_def_two", path: "s1_def_two/", name: "s1 def two", type: AnalyticsDefinition.TagType.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: AnalyticsDefinition.TagType.screen, tags: ["ios","iosonly"]),
getAnalyticsDefinition(id: "s2_def_two", path: "s2_def_two/", name: "s2 def two", type: AnalyticsDefinition.TagType.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: AnalyticsDefinition.TagType.screen, tags: ["droid","droidonly"]),
getAnalyticsDefinition(id: "s3_def_two", path: "s3_def_two/", name: "s3 def two", type: AnalyticsDefinition.TagType.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 UIKit
import Firebase
// MARK: - Protocol
protocol AnalyticsManagerProtocol {
func logScreen(name: String, path: String)
func logEvent(name: String)
}
// MARK: - Firebase
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
func logScreen(name: String, path: String) {
Analytics.logEvent(AnalyticsEventScreenView, parameters: [AnalyticsParameterScreenName: name])
}
func logEvent(name: String) {
var parameters = [
AnalyticsParameterValue: name
]
Analytics.logEvent(AnalyticsEventSelectContent, 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) {
guard isEnabled else { return }
managers.forEach { manager in
manager.logEvent(name: name)
}
}
// MARK: - section_one
func logScreenS1DefOne() {
logScreen(name: "s1 def one", path: "s1_def_one/")
}
func logEventS1DefTwo() {
logEvent(name: "s1 def two")
}
// 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 testGeneratedExtensionContentMatomo() {
// Given
let sectionOne = AnalyticsCategory(id: "section_one")
sectionOne.definitions = [
getAnalyticsDefinition(id: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: AnalyticsDefinition.TagType.screen, tags: ["ios", "iosonly"]),
getAnalyticsDefinition(id: "s1_def_two", path: "s1_def_two/", name: "s1 def two", type: AnalyticsDefinition.TagType.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: AnalyticsDefinition.TagType.screen, tags: ["ios","iosonly"]),
getAnalyticsDefinition(id: "s2_def_two", path: "s2_def_two/", name: "s2 def two", type: AnalyticsDefinition.TagType.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: AnalyticsDefinition.TagType.screen, tags: ["droid","droidonly"]),
getAnalyticsDefinition(id: "s3_def_two", path: "s3_def_two/", name: "s3 def two", type: AnalyticsDefinition.TagType.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 UIKit
import MatomoTracker
// MARK: - Protocol
protocol AnalyticsManagerProtocol {
func logScreen(name: String, path: String)
func logEvent(name: String)
}
// 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) {
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) {
guard isEnabled else { return }
managers.forEach { manager in
manager.logEvent(name: name)
}
}
// MARK: - section_one
func logScreenS1DefOne() {
logScreen(name: "s1 def one", path: "s1_def_one/")
}
func logEventS1DefTwo() {
logEvent(name: "s1 def two")
}
// 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: AnalyticsDefinition.TagType.screen, tags: ["ios", "iosonly"]),
getAnalyticsDefinition(id: "s1_def_two", path: "s1_def_two/", name: "s1 def two", type: AnalyticsDefinition.TagType.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: AnalyticsDefinition.TagType.screen, tags: ["ios","iosonly"]),
getAnalyticsDefinition(id: "s2_def_two", path: "s2_def_two/", name: "s2 def two", type: AnalyticsDefinition.TagType.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: AnalyticsDefinition.TagType.screen, tags: ["droid","droidonly"]),
getAnalyticsDefinition(id: "s3_def_two", path: "s3_def_two/", name: "s3 def two", type: AnalyticsDefinition.TagType.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 UIKit
import MatomoTracker
import Firebase
// MARK: - Protocol
protocol AnalyticsManagerProtocol {
func logScreen(name: String, path: String)
func logEvent(name: String)
}
// 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) {
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) {
Analytics.logEvent(AnalyticsEventScreenView, parameters: [AnalyticsParameterScreenName: name])
}
func logEvent(name: String) {
var parameters = [
AnalyticsParameterValue: name
]
Analytics.logEvent(AnalyticsEventSelectContent, 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) {
guard isEnabled else { return }
managers.forEach { manager in
manager.logEvent(name: name)
}
}
// MARK: - section_one
func logScreenS1DefOne() {
logScreen(name: "s1 def one", path: "s1_def_one/")
}
func logEventS1DefTwo() {
logEvent(name: "s1 def two")
}
// 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())
}
}

View File

@ -0,0 +1,72 @@
//
// AnalyticsSectionTests.swift
//
//
// Created by Loris Perret on 06/12/2023.
//
import Foundation
import XCTest
@testable import ResgenSwift
final class AnalyticsSectionTests: XCTestCase {
// MARK: - Matching tags
func testMatchingAnalytics() {
// Given
let section = AnalyticsCategory(id: "section_name")
section.definitions = [
{
let def = AnalyticsDefinition(id: "definition_name", name: "", type: .screen)
def.tags = ["ios","iosonly"]
return def
}(),
{
let def = AnalyticsDefinition(id: "definition_name_two", name: "", type: .screen)
def.tags = ["droid","droidonly"]
return def
}()
]
// When
let match1 = section.hasOneOrMoreMatchingTags(tags: ["ios"])
let match2 = section.hasOneOrMoreMatchingTags(tags: ["iosonly"])
let match3 = section.hasOneOrMoreMatchingTags(tags: ["droid"])
let match4 = section.hasOneOrMoreMatchingTags(tags: ["droidonly"])
// Expect
XCTAssertTrue(match1)
XCTAssertTrue(match2)
XCTAssertTrue(match3)
XCTAssertTrue(match4)
}
func testNotMatchingAnalytics() {
// Given
let section = AnalyticsCategory(id: "section_name")
section.definitions = [
{
let def = AnalyticsDefinition(id: "definition_name", name: "", type: .screen)
def.tags = ["ios","iosonly"]
return def
}(),
{
let def = AnalyticsDefinition(id: "definition_name_two", name: "", type: .screen)
def.tags = ["droid","droidonly"]
return def
}()
]
// When
let match1 = section.hasOneOrMoreMatchingTags(tags: ["web"])
let match2 = section.hasOneOrMoreMatchingTags(tags: ["webonly"])
let match3 = section.hasOneOrMoreMatchingTags(tags: ["azerty"])
// Expect
XCTAssertFalse(match1)
XCTAssertFalse(match2)
XCTAssertFalse(match3)
}
}

View File

@ -0,0 +1,135 @@
//
// File.swift
//
//
// Created by Loris Perret on 06/12/2023.
//
import Foundation
/// Find first differing character between two strings
///
/// :param: s1 First String
/// :param: s2 Second String
///
/// :returns: .DifferenceAtIndex(i) or .NoDifference
public func firstDifferenceBetweenStrings(s1: NSString, s2: NSString) -> FirstDifferenceResult {
let len1 = s1.length
let len2 = s2.length
let lenMin = min(len1, len2)
for i in 0..<lenMin {
if s1.character(at: i) != s2.character(at: i) {
return .DifferenceAtIndex(i)
}
}
if len1 < len2 {
return .DifferenceAtIndex(len1)
}
if len2 < len1 {
return .DifferenceAtIndex(len2)
}
return .NoDifference
}
/// Create a formatted String representation of difference between strings
///
/// :param: s1 First string
/// :param: s2 Second string
///
/// :returns: a string, possibly containing significant whitespace and newlines
public func prettyFirstDifferenceBetweenStrings(s1: String, s2: String) -> String {
let firstDifferenceResult = firstDifferenceBetweenStrings(s1: s1 as NSString, s2: s2 as NSString)
return prettyDescriptionOfFirstDifferenceResult(firstDifferenceResult: firstDifferenceResult, s1: s1 as NSString, s2: s2 as NSString) as String
}
/// Create a formatted String representation of a FirstDifferenceResult for two strings
///
/// :param: firstDifferenceResult FirstDifferenceResult
/// :param: s1 First string used in generation of firstDifferenceResult
/// :param: s2 Second string used in generation of firstDifferenceResult
///
/// :returns: a printable string, possibly containing significant whitespace and newlines
public func prettyDescriptionOfFirstDifferenceResult(firstDifferenceResult: FirstDifferenceResult, s1: NSString, s2: NSString) -> NSString {
func diffString(index: Int, s1: NSString, s2: NSString) -> NSString {
let markerArrow = "\u{2b06}" // ""
let ellipsis = "\u{2026}" // ""
/// Given a string and a range, return a string representing that substring.
///
/// If the range starts at a position other than 0, an ellipsis
/// will be included at the beginning.
///
/// If the range ends before the actual end of the string,
/// an ellipsis is added at the end.
func windowSubstring(s: NSString, range: NSRange) -> String {
let validRange = NSMakeRange(range.location, min(range.length, s.length - range.location))
let substring = s.substring(with: validRange)
let prefix = range.location > 0 ? ellipsis : ""
let suffix = (s.length - range.location > range.length) ? ellipsis : ""
return "\(prefix)\(substring)\(suffix)"
}
// Show this many characters before and after the first difference
let windowPrefixLength = 10
let windowSuffixLength = 10
let windowLength = windowPrefixLength + 1 + windowSuffixLength
let windowIndex = max(index - windowPrefixLength, 0)
let windowRange = NSMakeRange(windowIndex, windowLength)
let sub1 = windowSubstring(s: s1, range: windowRange)
let sub2 = windowSubstring(s: s2, range: windowRange)
let markerPosition = min(windowSuffixLength, index) + (windowIndex > 0 ? 1 : 0)
let markerPrefix = String(repeating: " " as Character, count: markerPosition)
let markerLine = "\(markerPrefix)\(markerArrow)"
return "Difference at index \(index):\n\(sub1)\n\(sub2)\n\(markerLine)" as NSString
}
switch firstDifferenceResult {
case .NoDifference: return "No difference"
case .DifferenceAtIndex(let index): return diffString(index: index, s1: s1, s2: s2)
}
}
/// Result type for firstDifferenceBetweenStrings()
public enum FirstDifferenceResult {
/// Strings are identical
case NoDifference
/// Strings differ at the specified index.
///
/// This could mean that characters at the specified index are different,
/// or that one string is longer than the other
case DifferenceAtIndex(Int)
}
extension FirstDifferenceResult {
/// Textual representation of a FirstDifferenceResult
public var description: String {
switch self {
case .NoDifference:
return "NoDifference"
case .DifferenceAtIndex(let index):
return "DifferenceAtIndex(\(index))"
}
}
/// Textual representation of a FirstDifferenceResult for debugging purposes
public var debugDescription: String {
return self.description
}
}