Skip to content

Commit 49ae1b3

Browse files
authored
Merge pull request #681 from AppsFlyerSDK/development
Align dev to master for CI/CD workflows
2 parents b09f42d + 3005a62 commit 49ae1b3

103 files changed

Lines changed: 7665 additions & 367 deletions

File tree

Some content is hidden

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

.af-e2e/test-plan.json

Lines changed: 396 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,396 @@
1+
{
2+
"_meta": {
3+
"plan_id": "reactnative-e2e",
4+
"plugin": "reactnative",
5+
"version": "1.1.0",
6+
"description": "Pre-publish E2E test plan for the AppsFlyer React Native plugin. Runs against plugin source in example/. Covers six scenarios: cold launch, background deep link, foreground deep link, custom events, identity APIs, and consent/stop. Mapped to E2E-001..E2E-006 in appsflyer-mobile-plugin-tooling/contracts/e2e-test-contract.md.",
7+
"platforms": ["android", "ios"],
8+
"schema_version": "1.0.0",
9+
"tooling_contract_ref": "E2E-001, E2E-002, E2E-003, E2E-004, E2E-005, E2E-006"
10+
},
11+
12+
"config": {
13+
"android": {
14+
"package_name": "com.appsflyer.engagement",
15+
"activity": "com.appsflyer.qa.reactnative.MainActivity",
16+
"apk_path": "example/android/app/build/outputs/apk/debug/app-debug.apk",
17+
"build_cmd": "cd example/android && ./gradlew assembleDebug"
18+
},
19+
"ios": {
20+
"bundle_id": "com.appsflyer.qa.reactnative",
21+
"app_path": "example/ios/build/Build/Products/Debug-iphonesimulator/example.app",
22+
"build_cmd": "cd example/ios && xcodebuild build -workspace example.xcworkspace -scheme example -configuration Debug -destination 'platform=iOS Simulator,id=$IOS_SIMULATOR_UDID' -derivedDataPath build"
23+
}
24+
},
25+
26+
"phases": [
27+
{
28+
"id": "phase_1",
29+
"name": "Cold launch coverage",
30+
"scenario_ref": "E2E-001",
31+
"description": "Fresh install. Validate SDK startup, pre/post-start APIs, three auto-launched events with HTTP 200, install conversion data with is_first_launch=true, and onDeepLinking NOT_FOUND on clean launch.",
32+
"requires_fresh_install": true,
33+
"wait_after_launch_sec": 420,
34+
"checks": [
35+
{
36+
"id": "sdk_started",
37+
"description": "startSDK was called",
38+
"type": "log_contains",
39+
"pattern": "[AF_QA][startSDK] result:",
40+
"fail_action": "abort"
41+
},
42+
{
43+
"id": "is_first_launch_true",
44+
"description": "onInstallConversionData fires with is_first_launch=true",
45+
"type": "log_contains",
46+
"pattern": "[AF_QA][CALLBACK][onInstallConversionData]",
47+
"payload_check": {"field": "is_first_launch", "expected": "true"},
48+
"fail_action": "abort"
49+
},
50+
{
51+
"id": "pre_start_apis_complete",
52+
"description": "Pre-start auto APIs ran",
53+
"type": "log_contains",
54+
"pattern": "[AF_QA][AUTO_APIS] --- Pre-start auto APIs complete ---",
55+
"fail_action": "fail"
56+
},
57+
{
58+
"id": "post_start_apis_complete",
59+
"description": "Post-start auto APIs ran",
60+
"type": "log_contains",
61+
"pattern": "[AF_QA][AUTO_APIS] --- Post-start auto APIs complete ---",
62+
"fail_action": "fail"
63+
},
64+
{
65+
"id": "get_sdk_version",
66+
"description": "getSDKVersion returns a value",
67+
"type": "log_contains",
68+
"pattern": "[AF_QA][getSDKVersion] result:",
69+
"fail_action": "fail"
70+
},
71+
{
72+
"id": "get_appsflyer_uid",
73+
"description": "getAppsFlyerUID returns a value",
74+
"type": "log_contains",
75+
"pattern": "[AF_QA][getAppsFlyerUID] result:",
76+
"fail_action": "fail"
77+
},
78+
{
79+
"id": "event_af_demo_launch",
80+
"description": "af_demo_launch event fires successfully",
81+
"type": "log_contains",
82+
"pattern": "[AF_QA][logEvent(af_demo_launch)] result:",
83+
"fail_action": "fail"
84+
},
85+
{
86+
"id": "event_af_purchase",
87+
"description": "af_purchase event fires successfully",
88+
"type": "log_contains",
89+
"pattern": "[AF_QA][logEvent(af_purchase)] result:",
90+
"fail_action": "fail"
91+
},
92+
{
93+
"id": "event_af_content_view",
94+
"description": "af_content_view event fires successfully",
95+
"type": "log_contains",
96+
"pattern": "[AF_QA][logEvent(af_content_view)] result:",
97+
"fail_action": "fail"
98+
},
99+
{
100+
"id": "http_200_count",
101+
"description": "At least 3 HTTP 200 responses from AppsFlyer servers",
102+
"type": "count_matches",
103+
"pattern": "response code:200 OK|response_status=200",
104+
"minimum": 3,
105+
"fail_action": "fail"
106+
},
107+
{
108+
"id": "on_deep_linking_callback",
109+
"description": "onDeepLinking fires (NOT_FOUND expected on clean launch)",
110+
"type": "log_contains",
111+
"pattern": "[AF_QA][CALLBACK][onDeepLinking]",
112+
"fail_action": "fail"
113+
},
114+
{
115+
"id": "no_fatal_errors",
116+
"description": "No fatal exceptions or SDK errors in logs",
117+
"type": "absent",
118+
"patterns": ["Fatal Exception", "FATAL", "[AF_QA][startSDK] error:", "response code:4", "response code:5"],
119+
"fail_action": "fail"
120+
}
121+
]
122+
},
123+
124+
{
125+
"id": "phase_2",
126+
"name": "Background deep link",
127+
"scenario_ref": "E2E-002",
128+
"description": "App backgrounded after Phase 1, then deep link triggers re-entry. Verifies URL is delivered to native layer. onDeepLinking attribution (FOUND/value) requires real OneLink and is warn-only in CI. LAUNCH event receives HTTP 200.",
129+
"requires_fresh_install": false,
130+
"wait_after_trigger_sec": 15,
131+
"deep_link_url": "afqa-reactnative://deeplink?deep_link_value=qa_deeplink_bg&af_sub1=background_test&pid=testmedia&c=deeplink_test",
132+
"pre_actions": {
133+
"android": ["adb shell input keyevent KEYCODE_HOME", "sleep 2"],
134+
"ios": ["xcrun simctl terminate {{UDID}} {{BUNDLE_ID}}", "sleep 1"]
135+
},
136+
"trigger": {
137+
"android": "adb shell am start -W -a android.intent.action.VIEW -d \"{{DEEP_LINK_URL}}\"",
138+
"ios": "xcrun simctl launch {{UDID}} {{BUNDLE_ID}} -deepLinkURL \"{{DEEP_LINK_URL}}\""
139+
},
140+
"checks": [
141+
{
142+
"id": "deeplink_url_received",
143+
"description": "Deep link URL delivered to native app layer",
144+
"type": "log_contains",
145+
"pattern": "[AF_QA][DEEPLINK_NATIVE]",
146+
"fail_action": "fail"
147+
},
148+
{
149+
"id": "deeplink_callback_fired",
150+
"description": "onDeepLinking callback fires after deep link (any status)",
151+
"type": "log_contains",
152+
"pattern": "[AF_QA][CALLBACK][onDeepLinking]",
153+
"fail_action": "fail"
154+
},
155+
{
156+
"id": "deeplink_status_found",
157+
"description": "onDeepLinking fires with Status.FOUND (requires real OneLink attribution — expected to warn in CI)",
158+
"type": "log_contains",
159+
"pattern": "status=FOUND",
160+
"fail_action": "warn"
161+
},
162+
{
163+
"id": "deeplink_value_bg",
164+
"description": "deepLinkValue matches qa_deeplink_bg (requires server-side attribution — expected to warn in CI)",
165+
"type": "log_contains",
166+
"pattern": "deepLinkValue=qa_deeplink_bg",
167+
"fail_action": "warn"
168+
},
169+
{
170+
"id": "launch_http_200",
171+
"description": "LAUNCH event receives HTTP 200 after deep link re-entry",
172+
"type": "count_matches",
173+
"pattern": "response code:200 OK|response_status=200",
174+
"minimum": 1,
175+
"fail_action": "fail"
176+
},
177+
{
178+
"id": "no_fatal_errors",
179+
"description": "No fatal exceptions after deep link",
180+
"type": "absent",
181+
"patterns": ["Fatal Exception", "FATAL"],
182+
"fail_action": "fail"
183+
}
184+
]
185+
},
186+
187+
{
188+
"id": "phase_3",
189+
"name": "Foreground deep link",
190+
"scenario_ref": "E2E-003",
191+
"description": "Fresh install. App in foreground after SDK start. Brief launcher switch, then deep link. Verifies URL delivery and callback firing. Attribution checks (FOUND/value) are warn-only in CI. SDK start and conversion data are validated in Phase 1.",
192+
"requires_fresh_install": true,
193+
"wait_after_launch_sec": 420,
194+
"wait_after_trigger_sec": 15,
195+
"deep_link_url": "afqa-reactnative://deeplink?deep_link_value=qa_deeplink_fg&af_sub1=foreground_test&pid=testmedia&c=deeplink_test",
196+
"pre_actions": {
197+
"android": ["adb shell am start -a android.intent.action.MAIN -c android.intent.category.HOME", "sleep 1"],
198+
"ios": ["xcrun simctl terminate {{UDID}} {{BUNDLE_ID}}", "sleep 1"]
199+
},
200+
"trigger": {
201+
"android": "adb shell am start -W -a android.intent.action.VIEW -d \"{{DEEP_LINK_URL}}\"",
202+
"ios": "xcrun simctl launch {{UDID}} {{BUNDLE_ID}} -deepLinkURL \"{{DEEP_LINK_URL}}\""
203+
},
204+
"checks": [
205+
{
206+
"id": "deeplink_url_received",
207+
"description": "Deep link URL delivered to native app layer",
208+
"type": "log_contains",
209+
"pattern": "[AF_QA][DEEPLINK_NATIVE]",
210+
"fail_action": "fail"
211+
},
212+
{
213+
"id": "deeplink_callback_fired",
214+
"description": "onDeepLinking callback fires after deep link (any status)",
215+
"type": "log_contains",
216+
"pattern": "[AF_QA][CALLBACK][onDeepLinking]",
217+
"fail_action": "fail"
218+
},
219+
{
220+
"id": "deeplink_status_found",
221+
"description": "onDeepLinking fires with Status.FOUND (requires real OneLink attribution — expected to warn in CI)",
222+
"type": "log_contains",
223+
"pattern": "status=FOUND",
224+
"fail_action": "warn"
225+
},
226+
{
227+
"id": "deeplink_value_fg",
228+
"description": "deepLinkValue matches qa_deeplink_fg (requires server-side attribution — expected to warn in CI)",
229+
"type": "log_contains",
230+
"pattern": "deepLinkValue=qa_deeplink_fg",
231+
"fail_action": "warn"
232+
},
233+
{
234+
"id": "no_fatal_errors",
235+
"description": "No fatal exceptions",
236+
"type": "absent",
237+
"patterns": ["Fatal Exception", "FATAL"],
238+
"fail_action": "fail"
239+
}
240+
]
241+
},
242+
243+
{
244+
"id": "phase_4",
245+
"name": "Custom in-app event with parameters",
246+
"scenario_ref": "E2E-004",
247+
"description": "Trigger af_qa_custom_purchase with rich parameters (string, number, bool, nested map). Verify all parameter keys are serialized, HTTP 200 returned, and no logEvent errors.",
248+
"requires_fresh_install": false,
249+
"wait_after_launch_sec": 420,
250+
"checks": [
251+
{
252+
"id": "custom_event_logged",
253+
"description": "af_qa_custom_purchase logEvent fires",
254+
"type": "log_contains",
255+
"pattern": "[AF_QA][logEvent(af_qa_custom_purchase)]",
256+
"fail_action": "fail"
257+
},
258+
{
259+
"id": "params_complete",
260+
"description": "All 5 parameter keys present in the custom event log line",
261+
"type": "log_contains",
262+
"pattern": "[AF_QA][logEvent] name=af_qa_custom_purchase params=",
263+
"fail_action": "fail"
264+
},
265+
{
266+
"id": "http_200_event",
267+
"description": "HTTP 200 for the custom event",
268+
"type": "count_matches",
269+
"pattern": "response code:200 OK|response_status=200",
270+
"minimum": 1,
271+
"fail_action": "fail"
272+
},
273+
{
274+
"id": "no_logEvent_error",
275+
"description": "No logEvent errors for custom purchase",
276+
"type": "absent",
277+
"patterns": ["[AF_QA][logEvent(af_qa_custom_purchase)] error:"],
278+
"fail_action": "fail"
279+
},
280+
{
281+
"id": "no_fatal_errors",
282+
"description": "No fatal exceptions or process crashes",
283+
"type": "absent",
284+
"patterns": ["Fatal Exception", "FATAL"],
285+
"fail_action": "fail"
286+
}
287+
]
288+
},
289+
290+
{
291+
"id": "phase_5",
292+
"name": "Identity APIs round-trip",
293+
"scenario_ref": "E2E-005",
294+
"description": "Fresh install. Verify setCustomerUserId, setCurrencyCode, setAdditionalData propagate correctly. Identity-check event receives HTTP 200. is_first_launch=true still fires.",
295+
"requires_fresh_install": true,
296+
"wait_after_launch_sec": 420,
297+
"checks": [
298+
{
299+
"id": "customer_user_id_set",
300+
"description": "setCustomerUserId readback present",
301+
"type": "log_contains",
302+
"pattern": "[AF_QA][setCustomerUserId] result:",
303+
"fail_action": "fail"
304+
},
305+
{
306+
"id": "currency_code",
307+
"description": "setCurrencyCode readback present",
308+
"type": "log_contains",
309+
"pattern": "[AF_QA][setCurrencyCode] result:",
310+
"fail_action": "fail"
311+
},
312+
{
313+
"id": "additional_data",
314+
"description": "setAdditionalData readback present",
315+
"type": "log_contains",
316+
"pattern": "[AF_QA][setAdditionalData] result:",
317+
"fail_action": "fail"
318+
},
319+
{
320+
"id": "http_200_identity_event",
321+
"description": "HTTP 200 for the identity-check event",
322+
"type": "count_matches",
323+
"pattern": "response code:200 OK|response_status=200",
324+
"minimum": 1,
325+
"fail_action": "fail"
326+
},
327+
{
328+
"id": "is_first_launch_true",
329+
"description": "onInstallConversionData still fires with is_first_launch=true",
330+
"type": "log_contains",
331+
"pattern": "[AF_QA][CALLBACK][onInstallConversionData]",
332+
"payload_check": {"field": "is_first_launch", "expected": "true"},
333+
"fail_action": "fail"
334+
},
335+
{
336+
"id": "no_fatal_errors",
337+
"description": "No fatal exceptions or process crashes",
338+
"type": "absent",
339+
"patterns": ["Fatal Exception", "FATAL"],
340+
"fail_action": "fail"
341+
}
342+
]
343+
},
344+
345+
{
346+
"id": "phase_6",
347+
"name": "Consent / SDK stop toggle",
348+
"scenario_ref": "E2E-006",
349+
"description": "stop(true) suppresses outbound events. stop(false) resumes them. Verify suppressed event does NOT get HTTP 200, resumed event DOES get HTTP 200.",
350+
"requires_fresh_install": false,
351+
"wait_after_launch_sec": 420,
352+
"checks": [
353+
{
354+
"id": "stop_true",
355+
"description": "stop(true) readback present",
356+
"type": "log_contains",
357+
"pattern": "[AF_QA][stop] result: true",
358+
"fail_action": "fail"
359+
},
360+
{
361+
"id": "suppressed_event_no_result",
362+
"description": "Suppressed event does not get a success result while SDK is stopped",
363+
"type": "absent",
364+
"patterns": ["[AF_QA][logEvent(af_qa_suppressed)] result:"],
365+
"fail_action": "fail"
366+
},
367+
{
368+
"id": "stop_false",
369+
"description": "stop(false) readback present",
370+
"type": "log_contains",
371+
"pattern": "[AF_QA][stop] result: false",
372+
"fail_action": "fail"
373+
},
374+
{
375+
"id": "resumed_event_http_200",
376+
"description": "Resumed event fires and receives HTTP 200",
377+
"type": "log_contains",
378+
"pattern": "[AF_QA][logEvent(af_qa_resumed)] result:",
379+
"fail_action": "fail"
380+
},
381+
{
382+
"id": "no_fatal_errors",
383+
"description": "No fatal exceptions throughout stop/resume cycle",
384+
"type": "absent",
385+
"patterns": ["Fatal Exception", "FATAL"],
386+
"fail_action": "fail"
387+
}
388+
]
389+
}
390+
],
391+
392+
"report": {
393+
"output_dir": ".af-e2e/reports",
394+
"format": "json"
395+
}
396+
}

0 commit comments

Comments
 (0)