Skip to content

Commit 5d859ad

Browse files
authored
Merge pull request #43 from flagship-io/preReleaseJuin24
Pre release 07/24
2 parents be84227 + 46c4264 commit 5d859ad

File tree

76 files changed

+1965
-1054
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1965
-1054
lines changed

.github/workflows/flagship_ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,25 @@ jobs:
1010
strategy:
1111
matrix:
1212
run-config:
13-
- { scheme: 'Flagship', destination: 'platform=iOS Simulator,name=iPhone 14', label: 'iOS'}
13+
- { scheme: 'Flagship', destination: 'platform=iOS Simulator,name=iPhone 15', label: 'iOS'}
1414
- { scheme: 'Flagship-tvOS', destination: 'platform=tvOS Simulator,name=Apple TV', label: 'tvOS' }
1515
- { scheme: 'Flagship-macOS', destination: 'platform=macOS', label: 'macOS'}
1616
- { scheme: 'Flagship-watchOS', destination: 'platform=watchOS Simulator,name=Apple Watch SE (40mm) (2nd generation)', label: 'watchOS'}
1717

1818

1919
steps:
2020
- name: Checkout Project
21-
uses: actions/checkout@v2
21+
uses: actions/checkout@v4
2222
- name: Building SDK for ${{ matrix.run-config['label'] }}
2323
run: xcodebuild clean build -project Flagship/Flagship.xcodeproj -scheme '${{ matrix.run-config['scheme'] }}' -destination '${{ matrix.run-config['destination'] }}'
2424
Testing:
2525
needs: Build_SDK
2626
runs-on: macos-latest
2727
steps:
2828
- name: Checkout Project
29-
uses: actions/checkout@v3
29+
uses: actions/checkout@v4
3030
- name: Test SDK Flagship
31-
run: xcodebuild test -project Flagship/Flagship.xcodeproj -scheme "Flagship" -destination "name=iPhone 14" -enableCodeCoverage YES
31+
run: xcodebuild test -project Flagship/Flagship.xcodeproj -scheme "Flagship" -destination "platform=iOS Simulator,name=iPhone 15" -enableCodeCoverage YES
3232
- name: Upload coverage to Codecov
3333
uses: codecov/[email protected]
3434
with:
File renamed without changes.

FlagShip.podspec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
Pod::Spec.new do |s|
1010
s.name = "FlagShip"
11-
s.version = "3.3.5"
11+
12+
s.version = "4.0.0"
1213
s.summary = "Flagship SDK"
1314

1415
# This description is used to generate tags and improve search results.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict/>
5+
</plist>
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//
2+
// FSAllFlagTest.swift
3+
// FlagshipTests
4+
//
5+
// Created by Adel Ferguen on 03/06/2024.
6+
// Copyright © 2024 FlagShip. All rights reserved.
7+
//
8+
9+
@testable import Flagship
10+
import XCTest
11+
12+
final class FSAllFlagTest: XCTestCase {
13+
var urlFakeSession: URLSession?
14+
15+
override func setUpWithError() throws {
16+
/// Configuration
17+
let configuration = URLSessionConfiguration.ephemeral
18+
/// Fake session
19+
configuration.protocolClasses = [MockURLProtocol.self]
20+
urlFakeSession = URLSession(configuration: configuration)
21+
Flagship.sharedInstance.start(envId: "gk87t3jggr10c6l6sdov", apiKey: "apiKey")
22+
do {
23+
guard let path = Bundle(for: type(of: self)).url(forResource: "decisionApi", withExtension: "json") else { return }
24+
let data = try Data(contentsOf: path, options: .alwaysMapped)
25+
MockURLProtocol.requestHandler = { _ in
26+
let response = HTTPURLResponse(url: URL(string: "ok")!, statusCode: 200, httpVersion: nil, headerFields: nil)!
27+
return (response, data)
28+
}
29+
} catch {
30+
print("---------------- FSAllFlagTest- Failed to load the mock api file ----------")
31+
}
32+
}
33+
34+
// Normal behaviour
35+
func testAbsoluteGetFlags() {
36+
// Create Visitor
37+
let userAllFlag: FSVisitor = Flagship.sharedInstance.newVisitor(visitorId: "userAllFlag", hasConsented: true).build()
38+
// Set fake session
39+
if let aUrlFakeSession = urlFakeSession {
40+
userAllFlag.configManager.decisionManager?.networkService.serviceSession = aUrlFakeSession
41+
}
42+
43+
let expecAllFlag = XCTestExpectation(description: "Get All Flags")
44+
// FetchFlags
45+
userAllFlag.fetchFlags {
46+
let collectionFlag = userAllFlag.getFlags()
47+
XCTAssertTrue(collectionFlag.count == 4) // should contain 4 flags
48+
XCTAssertFalse(collectionFlag.isEmpty) // Should not be empty
49+
XCTAssertTrue(collectionFlag.keys().count == 4) // Should be 4
50+
51+
// Apply filter
52+
let result = collectionFlag.filter { (key: String, _: FSFlag) in
53+
54+
key == "isMenuUpdateVisible"
55+
}
56+
XCTAssert(result.count == 1)
57+
// Apply iterator
58+
var index = 0
59+
collectionFlag.forEach { (_: String, _: FSFlag) in
60+
index += 1
61+
}
62+
XCTAssertTrue(index == 4)
63+
64+
// Test metadata for collection
65+
let metadataArray = collectionFlag.metadatas()
66+
XCTAssertEqual(metadataArray.count, 4)
67+
metadataArray.forEach { item in
68+
XCTAssertEqual(item.campaignId, "bvcdqksmicqghldq9agg")
69+
XCTAssertEqual(item.campaignName, "campaign_name")
70+
}
71+
// Test json representation
72+
let collectionString = collectionFlag.toJson()
73+
let data = Data(collectionString.utf8)
74+
do {
75+
// make sure this JSON is in the format we expect
76+
if let ConvertedArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
77+
try ConvertedArray.forEach { subDico in
78+
if subDico["key"] as? String == "btnTitle" {
79+
if let hexValue = subDico["hex"] as? String {
80+
let decodedHex = Hex.hexToStr(text: hexValue)
81+
let dataBis = Data(decodedHex.utf8)
82+
if let ConvertedHEx = try JSONSerialization.jsonObject(with: dataBis, options: []) as? [String: Any] {
83+
XCTAssertTrue(ConvertedHEx["v"] as? String == "Alpha_demoApp")
84+
}
85+
}
86+
}
87+
}
88+
}
89+
} catch let error as NSError {
90+
print("Failed to load: \(error.localizedDescription)")
91+
}
92+
93+
expecAllFlag.fulfill()
94+
}
95+
96+
wait(for: [expecAllFlag], timeout: 5.0)
97+
}
98+
99+
func testWithBadKeysGetFlags() {
100+
let expecAllFlag = XCTestExpectation(description: "Get All FlagsBadKey")
101+
102+
// Create Visitor
103+
let userAllFlagBis: FSVisitor = Flagship.sharedInstance.newVisitor(visitorId: "userAllFlag", hasConsented: true).build()
104+
// Set fake session
105+
if let aUrlFakeSession = urlFakeSession {
106+
userAllFlagBis.configManager.decisionManager?.networkService.serviceSession = aUrlFakeSession
107+
}
108+
109+
// FetchFlags
110+
userAllFlagBis.fetchFlags {
111+
let collectionFlag = userAllFlagBis.getFlags()
112+
let badFlag: FSFlag = collectionFlag["badKey"]
113+
XCTAssertTrue(badFlag.key == "badKey")
114+
XCTAssertNil(badFlag.defaultValue)
115+
XCTAssertNil(badFlag.strategy)
116+
XCTAssertTrue(badFlag.value(defaultValue: "dflValue") == "dflValue")
117+
XCTAssertFalse(badFlag.exists())
118+
XCTAssertEqual(badFlag.metadata().campaignId, "")
119+
XCTAssertEqual(collectionFlag.count, 4)
120+
121+
expecAllFlag.fulfill()
122+
}
123+
124+
wait(for: [expecAllFlag], timeout: 5.0)
125+
}
126+
127+
func testReadingFlag() {
128+
let expecAllFlag = XCTestExpectation(description: "Get All Flags")
129+
130+
// Create Visitor
131+
let userAllFlagTer: FSVisitor = Flagship.sharedInstance.newVisitor(visitorId: "userAllFlag", hasConsented: true).build()
132+
// Set fake session
133+
if let aUrlFakeSession = urlFakeSession {
134+
userAllFlagTer.configManager.decisionManager?.networkService.serviceSession = aUrlFakeSession
135+
}
136+
137+
// FetchFlags
138+
userAllFlagTer.fetchFlags {
139+
let readFlag = userAllFlagTer.getFlag(key: "btnTitle")
140+
// Should return a default value
141+
XCTAssertEqual(readFlag.value(defaultValue: 12), 12)
142+
// Should return a default value
143+
XCTAssertEqual(readFlag.value(defaultValue: 12.4), 12.4)
144+
// Should return a default value
145+
XCTAssertEqual(readFlag.value(defaultValue: false), false)
146+
// Should return a default value
147+
XCTAssertEqual(readFlag.value(defaultValue: [1, 2, 3]), [1, 2, 3])
148+
// Check reading value
149+
XCTAssertEqual(readFlag.value(defaultValue: "default"), "Alpha_demoApp")
150+
expecAllFlag.fulfill()
151+
}
152+
wait(for: [expecAllFlag], timeout: 5.0)
153+
}
154+
}

FlagShip/FlagShipTests/FSConsentStrategyTest.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,19 @@ class FSConsentStrategyTest: XCTestCase {
1515
func testVisitorWithConsent() {
1616
Flagship.sharedInstance.start(envId: "gk87t3jggr10c6l6sdob", apiKey: "apiKey")
1717

18-
let v1 = Flagship.sharedInstance.newVisitor("newUser").withContext(context: [:]).build()
18+
let v1 = Flagship.sharedInstance.newVisitor(visitorId: "newUser", hasConsented: true).withContext(context: [:]).build()
1919
XCTAssertTrue(v1.hasConsented)
2020

21-
let v2 = Flagship.sharedInstance.newVisitor("newUser").withContext(context: [:]).hasConsented(hasConsented: false).build()
21+
let v2 = Flagship.sharedInstance.newVisitor(visitorId: "newUser", hasConsented: false).withContext(context: [:]).build()
2222

2323

24-
let v3 = Flagship.sharedInstance.newVisitor("nc").withContext(context: [:]).hasConsented(hasConsented: false).build()
24+
let v3 = Flagship.sharedInstance.newVisitor(visitorId: "nc", hasConsented: false).withContext(context: [:]).build()
2525

2626

2727
// Send hit
2828
v3.strategy?.getStrategy().sendHit(FSPage("pageNC"))
2929
// Send consent
3030
v3.strategy?.getStrategy().sendHit(FSConsent(eventCategory: .User_Engagement, eventAction: "NC"))
31-
// Activate
32-
v3.strategy?.getStrategy().activate("NC")
3331
// cache + lookup*
3432
v3.strategy?.getStrategy().cacheVisitor()
3533
v3.strategy?.getStrategy().lookupHits()

FlagShip/FlagShipTests/FSDataUsageTrackingTest.swift

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,17 @@ final class FSDataUsageTrackingTest: XCTestCase {
4949
}
5050

5151
func testConsent() {
52-
let consentVisitor: FSVisitor = FSVisitorBuilder("consentUSer").hasConsented(hasConsented: false).build()
52+
let consentVisitor: FSVisitor = FSVisitorBuilder("consentUSer", false).build()
5353
FSDataUsageTracking.sharedInstance.configureWithVisitor(pVisitor: consentVisitor)
5454
XCTAssertFalse(FSDataUsageTracking.sharedInstance._hasConsented)
5555
consentVisitor.setConsent(hasConsented: true)
5656
XCTAssertTrue(FSDataUsageTracking.sharedInstance._hasConsented)
5757
}
5858

5959
func testCreateCriticalFieldsForVisitor() {
60-
let config: FlagshipConfig = FSConfigBuilder().withTimeout(3).withLogLevel(.ERROR).Bucketing().build()
60+
let config: FlagshipConfig = FSConfigBuilder().withTimeout(3000).withLogLevel(.ERROR).Bucketing().build()
6161

62-
let consentVisitor: FSVisitor = FSVisitorBuilder("userTest").hasConsented(hasConsented: true).withContext(context: ["trCtx": "valCtx"]).build()
62+
let consentVisitor: FSVisitor = FSVisitorBuilder("userTest", true).withContext(context: ["trCtx": "valCtx"]).build()
6363

6464
consentVisitor.configManager.flagshipConfig = config
6565
FSDataUsageTracking.sharedInstance.configureWithVisitor(pVisitor: consentVisitor)
@@ -68,18 +68,18 @@ final class FSDataUsageTrackingTest: XCTestCase {
6868
consentVisitor.currentFlags = ["flagTR": FSModification(aCampaign: camp, aVariation: variation, valueForFlag: 12)]
6969
let ret: [String: String] = FSDataUsageTracking.sharedInstance.createCriticalFieldsForVisitor(consentVisitor)
7070

71-
XCTAssertTrue(ret.keys.contains("visitor.flags.flagTR.key"))
72-
XCTAssertTrue(ret.keys.contains("visitor.flags.flagTR.value"))
73-
XCTAssertTrue((ret["visitor.flags.flagTR.key"] ?? "") == "flagTR")
74-
XCTAssertTrue((ret["visitor.flags.flagTR.value"] ?? "") == "12")
71+
XCTAssertTrue(ret.keys.contains("visitor.flags.[flagTR].key"))
72+
XCTAssertTrue(ret.keys.contains("visitor.flags.[flagTR].value"))
73+
XCTAssertTrue((ret["visitor.flags.[flagTR].key"] ?? "") == "flagTR")
74+
XCTAssertTrue((ret["visitor.flags.[flagTR].value"] ?? "") == "12")
7575
XCTAssertTrue((ret["sdk.config.mode"] ?? "") == "BUCKETING")
7676
XCTAssertTrue((ret["sdk.config.trackingManager.strategy"] ?? "") == "CONTINUOUS_CACHING")
77-
XCTAssertTrue((ret["visitor.context.trCtx"] ?? "") == "valCtx")
78-
XCTAssertTrue((ret["sdk.config.timeout"] ?? "") == "3.0")
77+
XCTAssertTrue((ret["visitor.context.[trCtx]"] ?? "") == "valCtx")
78+
XCTAssertTrue((ret["sdk.config.timeout"] ?? "") == "3000")
7979
}
8080

8181
func testCreateCrticalXpc() {
82-
let xpcVisitor: FSVisitor = FSVisitorBuilder("userXpcTest").hasConsented(hasConsented: true).withContext(context: ["trCtx": "valCtx"]).build()
82+
let xpcVisitor: FSVisitor = FSVisitorBuilder("userXpcTest", true).withContext(context: ["trCtx": "valCtx"]).build()
8383
xpcVisitor.configManager.flagshipConfig = FSConfigBuilder().build()
8484
xpcVisitor.authenticate(visitorId: "loggedId")
8585
FSDataUsageTracking.sharedInstance.configureWithVisitor(pVisitor: xpcVisitor)
@@ -94,11 +94,11 @@ final class FSDataUsageTrackingTest: XCTestCase {
9494

9595
func testStressTroubleshooting() {
9696
for i in 1 ... 100 {
97-
let testVisitor: FSVisitor = FSVisitorBuilder("userXpcTest\(i)").hasConsented(hasConsented: true).withContext(context: ["trCtx": "valCtx"]).build()
97+
let testVisitor: FSVisitor = FSVisitorBuilder("userXpcTest\(i)", true).withContext(context: ["trCtx": "valCtx"]).build()
9898
testVisitor.configManager.flagshipConfig = FSConfigBuilder().build()
99-
FSDataUsageTracking.sharedInstance.processTSFetching(v: testVisitor, campaigns: nil)
100-
FSDataUsageTracking.sharedInstance.proceesTSFlag(crticalPointLabel: CriticalPoints.GET_FLAG_VALUE_FLAG_NOT_FOUND, f: FSFlag("key", nil, "defaultValue", nil), v: testVisitor)
101-
FSDataUsageTracking.sharedInstance.proceesTSFlag(crticalPointLabel: CriticalPoints.GET_FLAG_VALUE_FLAG_NOT_FOUND, f: FSFlag("key", nil, "defaultValue", nil), v: nil)
99+
FSDataUsageTracking.sharedInstance.processTSFetching(v: testVisitor, campaigns: nil, fetchingDate: Date())
100+
FSDataUsageTracking.sharedInstance.proceesTSFlag(crticalPointLabel: CriticalPoints.GET_FLAG_VALUE_FLAG_NOT_FOUND, f: FSFlag("key", nil), v: testVisitor)
101+
FSDataUsageTracking.sharedInstance.proceesTSFlag(crticalPointLabel: CriticalPoints.GET_FLAG_VALUE_FLAG_NOT_FOUND, f: FSFlag("key", nil), v: nil)
102102
FSDataUsageTracking.sharedInstance.processTSHits(label: "label", visitor: testVisitor, hit: FSScreen("TRScreen\(i)"))
103103
FSDataUsageTracking.sharedInstance.processTSXPC(label: "label", visitor: testVisitor)
104104
FSDataUsageTracking.sharedInstance.processTSBucketingFile(HTTPURLResponse(url: URL(string: "testUrl")!, statusCode: 200, httpVersion: nil, headerFields: nil), URLRequest(url: URL(string: "testUrl")!), Data())
@@ -110,7 +110,7 @@ final class FSDataUsageTrackingTest: XCTestCase {
110110

111111
func testDeveloperUsage() {
112112
let config: FlagshipConfig = FSConfigBuilder().withDisableDeveloperUsageTracking(true).build()
113-
let devUsageVisitor: FSVisitor = FSVisitorBuilder("user23").hasConsented(hasConsented: true).withContext(context: ["trCtx": "valCtx"]).build()
113+
let devUsageVisitor: FSVisitor = FSVisitorBuilder("user23", true).withContext(context: ["trCtx": "valCtx"]).build()
114114
devUsageVisitor.configManager.flagshipConfig = FSConfigBuilder().build()
115115
devUsageVisitor.configManager.flagshipConfig = config
116116

@@ -124,7 +124,7 @@ final class FSDataUsageTrackingTest: XCTestCase {
124124

125125
func testTroubleshootingHit() {
126126
Flagship.sharedInstance.start(envId: "gk87t3jggr10c6l6sdob", apiKey: "trApiKey")
127-
Flagship.sharedInstance.newVisitor("truser").build()
127+
Flagship.sharedInstance.newVisitor(visitorId: "truser", hasConsented: true).build()
128128
let trHit = TroubleshootingHit(pVisitorId: "trId", pLabel: "trLabel", pSpeceficCustomFields: ["key1": "val1"])
129129
let bodyTr: [String: Any] = trHit.bodyTrack
130130
XCTAssertTrue(bodyTr["vid"] as? String == "trId")

0 commit comments

Comments
 (0)