@admoai/sdk-scenarios 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,118 @@
1
+ {
2
+ "success": true,
3
+ "data": [
4
+ {
5
+ "placement": "preroll",
6
+ "creatives": [
7
+ {
8
+ "delivery": "json",
9
+ "contents": [
10
+ {
11
+ "key": "video_asset",
12
+ "type": "video",
13
+ "value": "https://cdn.admoai.com/media/volta-15s.mp4"
14
+ },
15
+ {
16
+ "key": "poster_image",
17
+ "type": "image",
18
+ "value": "https://cdn.admoai.com/media/volta-poster.jpg"
19
+ },
20
+ {
21
+ "key": "brand_logo",
22
+ "type": "image",
23
+ "value": "https://cdn.admoai.com/media/volta-logo.png"
24
+ },
25
+ {
26
+ "key": "headline",
27
+ "type": "text",
28
+ "value": "Two lattes, one price"
29
+ },
30
+ {
31
+ "key": "supporting_text",
32
+ "type": "text",
33
+ "value": "At every Volta in Santiago until Sunday."
34
+ },
35
+ {
36
+ "key": "cta_text",
37
+ "type": "text",
38
+ "value": "See the menu"
39
+ },
40
+ {
41
+ "key": "destination_url",
42
+ "type": "url",
43
+ "value": "https://volta.example/menu"
44
+ },
45
+ {
46
+ "key": "disclaimer",
47
+ "type": "text",
48
+ "value": "While stocks last."
49
+ }
50
+ ],
51
+ "advertiser": {
52
+ "name": "Volta Coffee",
53
+ "legalName": "Volta Coffee SpA",
54
+ "logoUrl": "https://cdn.admoai.com/media/volta-logo.png"
55
+ },
56
+ "template": {
57
+ "key": "standard_video",
58
+ "style": "default"
59
+ },
60
+ "tracking": {
61
+ "impressions": [
62
+ {
63
+ "key": "default",
64
+ "url": "https://api.admoai.com/v1/tracking?e=tok.standard_video.full.impression"
65
+ }
66
+ ],
67
+ "clicks": [
68
+ {
69
+ "key": "default",
70
+ "url": "https://api.admoai.com/v1/tracking?e=tok.standard_video.full.click"
71
+ }
72
+ ],
73
+ "custom": [],
74
+ "videoEvents": [
75
+ {
76
+ "key": "start",
77
+ "url": "https://api.admoai.com/v1/tracking?e=tok.standard_video.full.start"
78
+ },
79
+ {
80
+ "key": "first_quartile",
81
+ "url": "https://api.admoai.com/v1/tracking?e=tok.standard_video.full.first_quartile"
82
+ },
83
+ {
84
+ "key": "midpoint",
85
+ "url": "https://api.admoai.com/v1/tracking?e=tok.standard_video.full.midpoint"
86
+ },
87
+ {
88
+ "key": "third_quartile",
89
+ "url": "https://api.admoai.com/v1/tracking?e=tok.standard_video.full.third_quartile"
90
+ },
91
+ {
92
+ "key": "complete",
93
+ "url": "https://api.admoai.com/v1/tracking?e=tok.standard_video.full.complete"
94
+ }
95
+ ]
96
+ },
97
+ "metadata": {
98
+ "adId": "ad_01JDDR87YZYWJKANY5ZRAXWS3Q",
99
+ "creativeId": "creative_01JDGR1RTA3KV7KARB86J1DP84",
100
+ "advertiserId": "advertiser_01JDH315KXW0FP82VY8WMV97YE",
101
+ "placementId": "placement_01JDGX7Z9JHJR8PN9HZJPDNFWA",
102
+ "templateId": "template_01JDM27C055JQSS6R0DV2H5XPB",
103
+ "priority": "standard",
104
+ "language": "en",
105
+ "format": "video",
106
+ "effectiveStandardFormatKey": "standard_video",
107
+ "effectiveStandardFormatVersion": 1,
108
+ "templateSource": "admoai_standard",
109
+ "duration": 15,
110
+ "aspectRatio": "16:9"
111
+ }
112
+ }
113
+ ]
114
+ }
115
+ ],
116
+ "errors": null,
117
+ "warnings": null
118
+ }
@@ -0,0 +1,83 @@
1
+ {
2
+ "success": true,
3
+ "data": [
4
+ {
5
+ "placement": "preroll",
6
+ "creatives": [
7
+ {
8
+ "delivery": "json",
9
+ "contents": [
10
+ {
11
+ "key": "video_asset",
12
+ "type": "video",
13
+ "value": "https://cdn.admoai.com/media/volta-15s.mp4"
14
+ },
15
+ {
16
+ "key": "poster_image",
17
+ "type": "image",
18
+ "value": "https://cdn.admoai.com/media/volta-poster.jpg"
19
+ }
20
+ ],
21
+ "advertiser": {
22
+ "name": "Volta Coffee",
23
+ "legalName": "Volta Coffee SpA",
24
+ "logoUrl": "https://cdn.admoai.com/media/volta-logo.png"
25
+ },
26
+ "template": {
27
+ "key": "standard_video",
28
+ "style": "default"
29
+ },
30
+ "tracking": {
31
+ "impressions": [
32
+ {
33
+ "key": "default",
34
+ "url": "https://api.admoai.com/v1/tracking?e=tok.standard_video.minimal.impression"
35
+ }
36
+ ],
37
+ "clicks": [],
38
+ "custom": [],
39
+ "videoEvents": [
40
+ {
41
+ "key": "start",
42
+ "url": "https://api.admoai.com/v1/tracking?e=tok.standard_video.minimal.start"
43
+ },
44
+ {
45
+ "key": "first_quartile",
46
+ "url": "https://api.admoai.com/v1/tracking?e=tok.standard_video.minimal.first_quartile"
47
+ },
48
+ {
49
+ "key": "midpoint",
50
+ "url": "https://api.admoai.com/v1/tracking?e=tok.standard_video.minimal.midpoint"
51
+ },
52
+ {
53
+ "key": "third_quartile",
54
+ "url": "https://api.admoai.com/v1/tracking?e=tok.standard_video.minimal.third_quartile"
55
+ },
56
+ {
57
+ "key": "complete",
58
+ "url": "https://api.admoai.com/v1/tracking?e=tok.standard_video.minimal.complete"
59
+ }
60
+ ]
61
+ },
62
+ "metadata": {
63
+ "adId": "ad_01JDB2BVYVEG36SJ39ZVJ2S7FK",
64
+ "creativeId": "creative_01JDXWS60TJADDBKW1184GW4DE",
65
+ "advertiserId": "advertiser_01JDH315KXW0FP82VY8WMV97YE",
66
+ "placementId": "placement_01JDGX7Z9JHJR8PN9HZJPDNFWA",
67
+ "templateId": "template_01JDJ0JEE18TK4W82VSCT9NM1F",
68
+ "priority": "standard",
69
+ "language": "en",
70
+ "format": "video",
71
+ "effectiveStandardFormatKey": "standard_video",
72
+ "effectiveStandardFormatVersion": 1,
73
+ "templateSource": "admoai_standard",
74
+ "duration": 15,
75
+ "aspectRatio": "16:9"
76
+ }
77
+ }
78
+ ]
79
+ }
80
+ ],
81
+ "errors": null,
82
+ "warnings": null
83
+ }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@admoai/sdk-scenarios",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "The cross-SDK decision & tracking scenarios every AdMoai SDK asserts against a real decision-engine. Data only.",
5
5
  "license": "MIT",
6
6
  "files": [
7
7
  "scenarios.json",
8
8
  "scenarios.feature",
9
+ "fixtures/",
9
10
  "CHANGELOG.md",
10
11
  "README.md"
11
12
  ],
package/scenarios.feature CHANGED
@@ -1,11 +1,12 @@
1
1
  # GENERATED from scenarios.json — do not edit; regenerate with: go run ./tools/sdkfeaturegen
2
2
  #
3
- # scenarios.json is what the iOS, Android and Flutter manifest runners execute against a
4
- # real decision-engine. This is its readable rendering: what every AdMoai SDK promises a
5
- # publisher's app, in the same words for all three. CI regenerates it and fails on any
6
- # difference, so it cannot drift from what the runners execute.
3
+ # scenarios.json is what the iOS, Android and Flutter manifest runners execute: the live
4
+ # scenarios against a real decision-engine, the @fixture scenarios offline against the golden
5
+ # decision wire shipped in the package. This is its readable rendering: what every AdMoai SDK
6
+ # promises a publisher's app, in the same words for all three. CI regenerates it and fails on
7
+ # any difference, so it cannot drift from what the runners execute.
7
8
  # How it is published and kept fresh: docs/guides/sdk-ci.md, Section 7.
8
- # Manifest schemaVersion: 1
9
+ # Manifest schemaVersion: 3
9
10
 
10
11
  Feature: Cross-SDK decision & tracking contract
11
12
  Shared cross-SDK E2E scenario manifest. ONE definition, executed by a thin interpreter in each
@@ -15,7 +16,21 @@ Feature: Cross-SDK decision & tracking contract
15
16
  (log capture, concurrency, retry, TTL waits) stay hand-written per SDK. Mirrored
16
17
  byte-identically into all three repos; a checksum test fails the build if the copies diverge.
17
18
  Every expected value here was verified against a live local decision-engine before being
18
- written.
19
+ written. `fixtureScenarios` (schemaVersion 2) run offline: each decodes one golden
20
+ decision-wire file (ui/platform/lib/ad-formats/golden-wire in adhub, shipped in this package
21
+ under fixtures/golden-wire/) after optional `edits`, and asserts what the publisher's app
22
+ reads from the decoded creative. `contentKeys` is the exact set of plain content keys (slides
23
+ and matched points are asserted through their own fields); a key in `contentKeysAbsent` must
24
+ decode as absent (null), never as an empty string or a placeholder; a null in `metadata` means
25
+ absent. A runner that does not execute fixtureScenarios must refuse schemaVersion 2. A fixture
26
+ scenario may add `slideTracking` (package 1.3.0, schemaVersion 3; a runner that does not
27
+ execute it must refuse schemaVersion 3): `steps` the app takes on the decoded creative
28
+ (`slideView`/`slideClick` with a 1-based slide index, `newResponse`, `fireClick` with a key or
29
+ null for no key, `custom` with a key) and the beacons that must be dispatched: `beacons` is
30
+ the exact ordered list of first-party beacons, each named by the tracker it must be
31
+ (`slide`+`view`/`click`, or creative `click`/`custom`, with `response`), and
32
+ `thirdPartyBeacons` the exact multiset of third-party tracker ids dispatched (their order is
33
+ not asserted). An edit `at` `tracking` targets the creative's tracking object.
19
34
 
20
35
  @L @native
21
36
  Scenario: L1 — a normal native ad serves with complete, well-formed metadata
@@ -369,3 +384,360 @@ Feature: Cross-SDK decision & tracking contract
369
384
  Then each shown pin's view beacon reaches the engine exactly once per response, and no pin the app never showed sends one
370
385
  And every tap and every click sends its beacon, on every call
371
386
  And the engine accepts every beacon
387
+
388
+ @F @fixture
389
+ Scenario: F1 — a Native Display creative exposes its standard format, version and template source
390
+ Given the app decodes the golden card.full response
391
+ Then the response decodes
392
+ And the creative's effectiveStandardFormatKey is "card"
393
+ And the creative's effectiveStandardFormatVersion is 1
394
+ And the creative's templateSource is "admoai_standard"
395
+ And the template key is "card"
396
+ And the creative's contents are exactly "image", "brand_logo", "headline", "supporting_text", "cta_text", "destination_url"
397
+ And the click trackers are exactly "default"
398
+ And the creative has no custom event tracker
399
+
400
+ @F @fixture
401
+ Scenario: F2 — a Native Display creative leaves every optional field it was served without absent
402
+ Given the app decodes the golden card.minimal response
403
+ Then the response decodes
404
+ And the creative's effectiveStandardFormatKey is "card"
405
+ And the creative's effectiveStandardFormatVersion is 1
406
+ And the creative's templateSource is "admoai_standard"
407
+ And the template key is "card"
408
+ And the creative's contents are exactly "image", "destination_url"
409
+ And "brand_logo", "headline", "supporting_text", "cta_text" decode as absent, never as an empty string or a placeholder
410
+ And the click trackers are exactly "default"
411
+ And the creative has no custom event tracker
412
+
413
+ @F @fixture
414
+ Scenario: F3 — a template based on a standard keeps the standard's identity and exposes the operator's addition by key
415
+ Given the app decodes the golden card.derived response
416
+ Then the response decodes
417
+ And the creative's effectiveStandardFormatKey is "card"
418
+ And the creative's effectiveStandardFormatVersion is 1
419
+ And the creative's templateSource is "based_on_standard"
420
+ And the template key is "city_promo_display"
421
+ And the creative's contents are exactly "image", "headline", "destination_url", "promo_code"
422
+ And the click trackers are exactly "default"
423
+ And the custom event trackers are exactly "save_offer"
424
+
425
+ @F @fixture
426
+ Scenario: F4 — a Carousel exposes its slides in server order, each with its id, position, contents and own trackers, and no creative-level click
427
+ Given the app decodes the golden carousel.full response
428
+ Then the response decodes
429
+ And the creative's effectiveStandardFormatKey is "carousel"
430
+ And the creative's effectiveStandardFormatVersion is 1
431
+ And the creative's templateSource is "admoai_standard"
432
+ And the template key is "carousel"
433
+ And the creative's contents are exactly "brand_logo", "disclaimer"
434
+ And the creative has no click tracker
435
+ And the creative has no custom event tracker
436
+ And there are 3 slides in server order
437
+ And slide 1 is "slide_01JDY02N8MA2K2HKQ74989YS99" with contents "image", "headline", "supporting_text", "cta_text", "destination_url", view trackers "slide_view" and click trackers "default"
438
+ And slide 2 is "slide_01JD04H8GY801VF8Y0FHETP6XA" with contents "image", "headline", "supporting_text", "cta_text", "destination_url", view trackers "slide_view" and click trackers "default"
439
+ And slide 3 is "slide_01JDRGMFSTZXQ7ES1QBCF0DR2X" with contents "image", "headline", "supporting_text", "cta_text", "destination_url", view trackers "slide_view" and click trackers "default"
440
+
441
+ @F @fixture
442
+ Scenario: F5 — a minimal Carousel still exposes its slides and leaves the optional brand logo and disclaimer absent
443
+ Given the app decodes the golden carousel.minimal response
444
+ Then the response decodes
445
+ And the creative's effectiveStandardFormatKey is "carousel"
446
+ And the creative's effectiveStandardFormatVersion is 1
447
+ And the creative's templateSource is "admoai_standard"
448
+ And the template key is "carousel"
449
+ And the creative has no plain contents
450
+ And "brand_logo", "disclaimer" decode as absent, never as an empty string or a placeholder
451
+ And the creative has no click tracker
452
+ And the creative has no custom event tracker
453
+ And there are 2 slides in server order
454
+ And slide 1 is "slide_01JDZ725G8017BHW9RQJX7Y56S" with contents "image", "destination_url", view trackers "slide_view" and click trackers "default"
455
+ And slide 2 is "slide_01JD4GNH6TH0XPP5YWCMDKN029" with contents "image", "destination_url", view trackers "slide_view" and click trackers "default"
456
+
457
+ @F @fixture
458
+ Scenario: F6 — a Carousel based on a standard exposes its slides and the operator's addition
459
+ Given the app decodes the golden carousel.derived response
460
+ Then the response decodes
461
+ And the creative's effectiveStandardFormatKey is "carousel"
462
+ And the creative's effectiveStandardFormatVersion is 1
463
+ And the creative's templateSource is "based_on_standard"
464
+ And the template key is "city_carousel"
465
+ And the creative's contents are exactly "promo_code"
466
+ And the creative has no click tracker
467
+ And the creative has no custom event tracker
468
+ And there are 2 slides in server order
469
+ And slide 1 is "slide_01JDS1H8VEM0ABE2PQ0JY6S38W" with contents "image", "destination_url", view trackers "slide_view" and click trackers "default"
470
+ And slide 2 is "slide_01JDDGXJE0JDBY21Y9KEJDD54A" with contents "image", "destination_url", view trackers "slide_view" and click trackers "default"
471
+
472
+ @F @fixture
473
+ Scenario: F7 — a Tap-to-expand creative exposes expand and close as custom events, apart from its click
474
+ Given the app decodes the golden expandable_banner.full response
475
+ Then the response decodes
476
+ And the creative's effectiveStandardFormatKey is "expandable_banner"
477
+ And the creative's effectiveStandardFormatVersion is 1
478
+ And the creative's templateSource is "admoai_standard"
479
+ And the template key is "expandable_banner"
480
+ And the creative's contents are exactly "teaser_image", "teaser_headline", "expanded_image", "headline", "supporting_text", "cta_text", "destination_url", "disclaimer"
481
+ And the click trackers are exactly "default"
482
+ And the custom event trackers are exactly "expand", "close"
483
+
484
+ @F @fixture
485
+ Scenario: F8 — a minimal Tap-to-expand creative leaves its optional texts absent and keeps expand and close
486
+ Given the app decodes the golden expandable_banner.minimal response
487
+ Then the response decodes
488
+ And the creative's effectiveStandardFormatKey is "expandable_banner"
489
+ And the creative's effectiveStandardFormatVersion is 1
490
+ And the creative's templateSource is "admoai_standard"
491
+ And the template key is "expandable_banner"
492
+ And the creative's contents are exactly "teaser_image", "expanded_image", "destination_url"
493
+ And "teaser_headline", "headline", "supporting_text", "cta_text", "disclaimer" decode as absent, never as an empty string or a placeholder
494
+ And the click trackers are exactly "default"
495
+ And the custom event trackers are exactly "expand", "close"
496
+
497
+ @F @fixture
498
+ Scenario: F9 — a Branded Assets creative exposes asset tap as a custom event, apart from its click
499
+ Given the app decodes the golden branded_assets.full response
500
+ Then the response decodes
501
+ And the creative's effectiveStandardFormatKey is "branded_assets"
502
+ And the creative's effectiveStandardFormatVersion is 1
503
+ And the creative's templateSource is "admoai_standard"
504
+ And the template key is "branded_assets"
505
+ And the creative's contents are exactly "branded_image", "accessible_label", "destination_url"
506
+ And the click trackers are exactly "default"
507
+ And the custom event trackers are exactly "asset_tap"
508
+
509
+ @F @fixture
510
+ Scenario: F10 — a Branded Assets creative without a destination has no click, only the asset tap
511
+ Given the app decodes the golden branded_assets.minimal response
512
+ Then the response decodes
513
+ And the creative's effectiveStandardFormatKey is "branded_assets"
514
+ And the creative's effectiveStandardFormatVersion is 1
515
+ And the creative's templateSource is "admoai_standard"
516
+ And the template key is "branded_assets"
517
+ And the creative's contents are exactly "branded_image"
518
+ And "accessible_label", "destination_url" decode as absent, never as an empty string or a placeholder
519
+ And the creative has no click tracker
520
+ And the custom event trackers are exactly "asset_tap"
521
+
522
+ @F @fixture
523
+ Scenario: F11 — a Standard Video exposes its video events and click, and no skippable setting
524
+ Given the app decodes the golden standard_video.full response
525
+ Then the response decodes
526
+ And the creative's effectiveStandardFormatKey is "standard_video"
527
+ And the creative's effectiveStandardFormatVersion is 1
528
+ And the creative's templateSource is "admoai_standard"
529
+ And the template key is "standard_video"
530
+ And the creative's contents are exactly "video_asset", "poster_image", "brand_logo", "headline", "supporting_text", "cta_text", "destination_url", "disclaimer"
531
+ And the click trackers are exactly "default"
532
+ And the creative has no custom event tracker
533
+ And the video event trackers are exactly "start", "first_quartile", "midpoint", "third_quartile", "complete"
534
+ And the creative exposes no skippable setting
535
+
536
+ @F @fixture
537
+ Scenario: F12 — a Standard Video without a destination has no click action or click tracker
538
+ Given the app decodes the golden standard_video.minimal response
539
+ Then the response decodes
540
+ And the creative's effectiveStandardFormatKey is "standard_video"
541
+ And the creative's effectiveStandardFormatVersion is 1
542
+ And the creative's templateSource is "admoai_standard"
543
+ And the template key is "standard_video"
544
+ And the creative's contents are exactly "video_asset", "poster_image"
545
+ And "brand_logo", "headline", "supporting_text", "cta_text", "destination_url", "disclaimer" decode as absent, never as an empty string or a placeholder
546
+ And the creative has no click tracker
547
+ And the creative has no custom event tracker
548
+ And the video event trackers are exactly "start", "first_quartile", "midpoint", "third_quartile", "complete"
549
+ And the creative exposes no skippable setting
550
+
551
+ @F @fixture
552
+ Scenario: F13 — a Sponsored Pin creative keeps decoding its matched points as it does today
553
+ Given the app decodes the golden sponsored_pin_location.full response
554
+ Then the response decodes
555
+ And the creative's effectiveStandardFormatKey is "sponsored_pin_location"
556
+ And the creative's effectiveStandardFormatVersion is 1
557
+ And the creative's templateSource is "admoai_standard"
558
+ And the template key is "sponsored_pin_location"
559
+ And the creative's contents are exactly "pinImage", "image", "headline", "description", "ctaText", "defaultDestinationUrl"
560
+ And the click trackers are exactly "default"
561
+ And the creative has no custom event tracker
562
+ And the creative exposes 2 matched points
563
+
564
+ @F @fixture
565
+ Scenario: F14 — a creative served before the standards (gate off) decodes as today, with the three standard fields absent
566
+ The engine omits the three standard fields while is_standard_native_formats_enabled is off;
567
+ this is that wire. Absent means null, never an empty string or a placeholder (BR-SDK-01,
568
+ BR-SDK-09).
569
+
570
+ Given the app decodes the golden card.full response
571
+ And with the metadata field "effectiveStandardFormatKey" removed
572
+ And with the metadata field "effectiveStandardFormatVersion" removed
573
+ And with the metadata field "templateSource" removed
574
+ Then the response decodes
575
+ And the creative's effectiveStandardFormatKey is absent
576
+ And the creative's effectiveStandardFormatVersion is absent
577
+ And the creative's templateSource is absent
578
+ And the template key is "card"
579
+ And the creative's contents are exactly "image", "brand_logo", "headline", "supporting_text", "cta_text", "destination_url"
580
+ And the click trackers are exactly "default"
581
+ And the creative has no custom event tracker
582
+
583
+ @F @fixture
584
+ Scenario: F15 — fields the SDK does not know yet, at any level, never break decoding
585
+ An additive field the engine starts sending later must be ignored, not fail the response
586
+ (BR-SDK-01).
587
+
588
+ Given the app decodes the golden carousel.full response
589
+ And with an unknown response field "futureField" added
590
+ And with an unknown creative field "futureField" added
591
+ And with an unknown metadata field "futureField" added
592
+ And with an unknown content field "futureField" added on every content entry
593
+ And with an unknown slide field "futureField" added on every slide
594
+ Then the response decodes
595
+ And the creative's effectiveStandardFormatKey is "carousel"
596
+ And the creative's effectiveStandardFormatVersion is 1
597
+ And the creative's templateSource is "admoai_standard"
598
+ And the template key is "carousel"
599
+ And the creative's contents are exactly "brand_logo", "disclaimer"
600
+ And the creative has no click tracker
601
+ And the creative has no custom event tracker
602
+ And there are 3 slides in server order
603
+ And slide 1 is "slide_01JDY02N8MA2K2HKQ74989YS99" with contents "image", "headline", "supporting_text", "cta_text", "destination_url", view trackers "slide_view" and click trackers "default"
604
+ And slide 2 is "slide_01JD04H8GY801VF8Y0FHETP6XA" with contents "image", "headline", "supporting_text", "cta_text", "destination_url", view trackers "slide_view" and click trackers "default"
605
+ And slide 3 is "slide_01JDRGMFSTZXQ7ES1QBCF0DR2X" with contents "image", "headline", "supporting_text", "cta_text", "destination_url", view trackers "slide_view" and click trackers "default"
606
+
607
+ @F @fixture
608
+ Scenario: F16 — a slide's view beacon is sent once per served response, however often the slide is shown
609
+ Keyed on the signed view URL, never on the slideId: re-rendering or scrolling back to a
610
+ slide must not count it again (BR-SDK-04, Q19).
611
+
612
+ Given the app decodes the golden carousel.full response
613
+ And with third-party trackers "tpt_fixture_click_any", "tpt_fixture_click_default", "tpt_fixture_click_other", "tpt_fixture_impression"
614
+ When the app reports slide 1 as viewed
615
+ And the app reports slide 1 as viewed
616
+ And the app reports slide 2 as viewed
617
+ And the app reports slide 1 as viewed
618
+ Then the response decodes
619
+ And exactly these beacons are sent, in this order:
620
+ And slide 1's "slide_view" view beacon
621
+ And slide 2's "slide_view" view beacon
622
+ And no third-party tracker is sent
623
+
624
+ @F @fixture
625
+ Scenario: F17 — the same slide in a new decision response counts again
626
+ A new response carries new signed URLs for the same slides. The runner simulates it by
627
+ decoding the same golden file again with every first-party tracker URL suffixed
628
+ `&response=N` (N = 2, 3, ...); `response` in an expected beacon names which one (default 1).
629
+
630
+ Given the app decodes the golden carousel.full response
631
+ And with third-party trackers "tpt_fixture_click_any", "tpt_fixture_click_default", "tpt_fixture_click_other", "tpt_fixture_impression"
632
+ When the app reports slide 1 as viewed
633
+ And the app decodes a new decision response for the same ad
634
+ And the app reports slide 1 as viewed
635
+ And the app reports slide 1 as viewed
636
+ Then the response decodes
637
+ And exactly these beacons are sent, in this order:
638
+ And slide 1's "slide_view" view beacon
639
+ And slide 1's "slide_view" view beacon from response 2
640
+ And no third-party tracker is sent
641
+
642
+ @F @fixture
643
+ Scenario: F18 — a slide clicked before its view was sent sends the view, then the click, then the creative's applicable third-party click trackers once
644
+ Applicable means a click tracker matching any click, or the `default` click specifically; a
645
+ tracker for another click key and an impression tracker do not fire (BR-SDK-05, Q14, Q18).
646
+
647
+ Given the app decodes the golden carousel.full response
648
+ And with third-party trackers "tpt_fixture_click_any", "tpt_fixture_click_default", "tpt_fixture_click_other", "tpt_fixture_impression"
649
+ When the user clicks slide 2
650
+ Then the response decodes
651
+ And exactly these beacons are sent, in this order:
652
+ And slide 2's "slide_view" view beacon
653
+ And slide 2's "default" click beacon
654
+ And the third-party trackers sent are exactly "tpt_fixture_click_any", "tpt_fixture_click_default"
655
+
656
+ @F @fixture
657
+ Scenario: F19 — a slide clicked after its view sends only the click, on every click
658
+ Given the app decodes the golden carousel.full response
659
+ And with third-party trackers "tpt_fixture_click_any", "tpt_fixture_click_default", "tpt_fixture_click_other", "tpt_fixture_impression"
660
+ When the app reports slide 2 as viewed
661
+ And the user clicks slide 2
662
+ And the user clicks slide 2
663
+ Then the response decodes
664
+ And exactly these beacons are sent, in this order:
665
+ And slide 2's "slide_view" view beacon
666
+ And slide 2's "default" click beacon
667
+ And slide 2's "default" click beacon
668
+ And the third-party trackers sent are exactly "tpt_fixture_click_any", "tpt_fixture_click_default", "tpt_fixture_click_any", "tpt_fixture_click_default"
669
+
670
+ @F @fixture
671
+ Scenario: F20 — slide views never send third-party click trackers
672
+ Given the app decodes the golden carousel.full response
673
+ And with third-party trackers "tpt_fixture_click_any", "tpt_fixture_click_default", "tpt_fixture_click_other", "tpt_fixture_impression"
674
+ When the app reports slide 1 as viewed
675
+ And the app reports slide 2 as viewed
676
+ And the app reports slide 3 as viewed
677
+ Then the response decodes
678
+ And exactly these beacons are sent, in this order:
679
+ And slide 1's "slide_view" view beacon
680
+ And slide 2's "slide_view" view beacon
681
+ And slide 3's "slide_view" view beacon
682
+ And no third-party tracker is sent
683
+
684
+ @F @fixture
685
+ Scenario: F21 — a Carousel has no creative-level click: fireClick sends nothing, so slide clicks go through the slide helper
686
+ Publishers must not route a slide tap through fireClick (BR-SDK-03, Q9).
687
+
688
+ Given the app decodes the golden carousel.full response
689
+ And with third-party trackers "tpt_fixture_click_any", "tpt_fixture_click_default", "tpt_fixture_click_other", "tpt_fixture_impression"
690
+ When the app calls fireClick with no key
691
+ Then the response decodes
692
+ And no first-party beacon is sent
693
+ And no third-party tracker is sent
694
+
695
+ @F @fixture
696
+ Scenario: F22 — fireClick with no key sends the default click of a standard
697
+ Every standard seeds its click with key `default`, which is what fireClick sends without a
698
+ key (BR-SDK-06, Q12).
699
+
700
+ Given the app decodes the golden card.full response
701
+ And with third-party trackers "tpt_fixture_click_any", "tpt_fixture_click_default", "tpt_fixture_click_other", "tpt_fixture_impression"
702
+ When the app calls fireClick with no key
703
+ Then the response decodes
704
+ And exactly these beacons are sent, in this order:
705
+ And the creative's "default" click beacon
706
+ And the third-party trackers sent are exactly "tpt_fixture_click_any", "tpt_fixture_click_default"
707
+
708
+ @F @fixture
709
+ Scenario: F23 — expand and close are sent verbatim as custom events and are not clicks
710
+ The publisher calls them when the banner actually expands or closes; no click is sent and no
711
+ third-party click tracker fires (BR-SDK-07).
712
+
713
+ Given the app decodes the golden expandable_banner.full response
714
+ And with third-party trackers "tpt_fixture_click_any", "tpt_fixture_click_default", "tpt_fixture_click_other", "tpt_fixture_impression"
715
+ When the app sends the custom event "expand"
716
+ And the app sends the custom event "close"
717
+ Then the response decodes
718
+ And exactly these beacons are sent, in this order:
719
+ And the creative's "expand" custom beacon
720
+ And the creative's "close" custom beacon
721
+ And no third-party tracker is sent
722
+
723
+ @F @fixture
724
+ Scenario: F24 — an asset tap is sent verbatim as a custom event and is not a click
725
+ BR-SDK-07.
726
+
727
+ Given the app decodes the golden branded_assets.full response
728
+ And with third-party trackers "tpt_fixture_click_any", "tpt_fixture_click_default", "tpt_fixture_click_other", "tpt_fixture_impression"
729
+ When the app sends the custom event "asset_tap"
730
+ Then the response decodes
731
+ And exactly these beacons are sent, in this order:
732
+ And the creative's "asset_tap" custom beacon
733
+ And no third-party tracker is sent
734
+
735
+ @F @fixture
736
+ Scenario: F25 — a Standard Video without a destination sends no click
737
+ No destination means no click action and no click tracker (BR-SDK-08).
738
+
739
+ Given the app decodes the golden standard_video.minimal response
740
+ When the app calls fireClick with no key
741
+ Then the response decodes
742
+ And no first-party beacon is sent
743
+ And no third-party tracker is sent