cozy-client 60.30.6 → 60.32.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,807 @@
1
+ {
2
+ "$comment": "Shared contract vectors for io.cozy.banners. Every client implementing the doctype runs these and must produce `expected` from `input`. Published with the doctype in cozy-doctypes, alongside docs/io.cozy.banners.md.",
3
+ "now": "2026-07-22T12:00:00Z",
4
+ "expectedShape": "An ordered list of the banners a client displays, each reduced to the values that rendering depends on: bannerId, the severity and surface actually used after any fallback, and whether a call to action is shown.",
5
+ "cases": [
6
+ {
7
+ "name": "no documents renders nothing",
8
+ "input": [],
9
+ "expected": []
10
+ },
11
+ {
12
+ "name": "higher priority renders first",
13
+ "input": [
14
+ {
15
+ "_id": "doc001",
16
+ "_rev": "1-aaa",
17
+ "bannerId": "b.low",
18
+ "category": "quota",
19
+ "severity": "info",
20
+ "surface": "banner",
21
+ "text": "Message.",
22
+ "lang": "en",
23
+ "dismissible": true,
24
+ "dismissedAt": null,
25
+ "priority": 10,
26
+ "startsAt": "2026-07-01T00:00:00Z",
27
+ "endsAt": null,
28
+ "source": {
29
+ "trigger": "test",
30
+ "at": "2026-07-01T00:00:00Z"
31
+ },
32
+ "cozyMetadata": {
33
+ "createdAt": "2026-07-01T00:00:00Z",
34
+ "createdByApp": "stack",
35
+ "doctypeVersion": "1",
36
+ "metadataVersion": 1,
37
+ "updatedAt": "2026-07-01T00:00:00Z"
38
+ }
39
+ },
40
+ {
41
+ "_id": "doc002",
42
+ "_rev": "1-aaa",
43
+ "bannerId": "a.high",
44
+ "category": "quota",
45
+ "severity": "info",
46
+ "surface": "banner",
47
+ "text": "Message.",
48
+ "lang": "en",
49
+ "dismissible": true,
50
+ "dismissedAt": null,
51
+ "priority": 100,
52
+ "startsAt": "2026-07-01T00:00:00Z",
53
+ "endsAt": null,
54
+ "source": {
55
+ "trigger": "test",
56
+ "at": "2026-07-01T00:00:00Z"
57
+ },
58
+ "cozyMetadata": {
59
+ "createdAt": "2026-07-01T00:00:00Z",
60
+ "createdByApp": "stack",
61
+ "doctypeVersion": "1",
62
+ "metadataVersion": 1,
63
+ "updatedAt": "2026-07-01T00:00:00Z"
64
+ }
65
+ }
66
+ ],
67
+ "expected": [
68
+ {
69
+ "bannerId": "a.high",
70
+ "severity": "info",
71
+ "surface": "banner",
72
+ "cta": false,
73
+ "secondaryCta": false
74
+ },
75
+ {
76
+ "bannerId": "b.low",
77
+ "severity": "info",
78
+ "surface": "banner",
79
+ "cta": false,
80
+ "secondaryCta": false
81
+ }
82
+ ]
83
+ },
84
+ {
85
+ "name": "equal priority breaks on bannerId, compared by code unit rather than locale",
86
+ "input": [
87
+ {
88
+ "_id": "doc003",
89
+ "_rev": "1-aaa",
90
+ "bannerId": "a.lower",
91
+ "category": "quota",
92
+ "severity": "info",
93
+ "surface": "banner",
94
+ "text": "Message.",
95
+ "lang": "en",
96
+ "dismissible": true,
97
+ "dismissedAt": null,
98
+ "priority": 50,
99
+ "startsAt": "2026-07-01T00:00:00Z",
100
+ "endsAt": null,
101
+ "source": {
102
+ "trigger": "test",
103
+ "at": "2026-07-01T00:00:00Z"
104
+ },
105
+ "cozyMetadata": {
106
+ "createdAt": "2026-07-01T00:00:00Z",
107
+ "createdByApp": "stack",
108
+ "doctypeVersion": "1",
109
+ "metadataVersion": 1,
110
+ "updatedAt": "2026-07-01T00:00:00Z"
111
+ }
112
+ },
113
+ {
114
+ "_id": "doc004",
115
+ "_rev": "1-aaa",
116
+ "bannerId": "B.upper",
117
+ "category": "quota",
118
+ "severity": "info",
119
+ "surface": "banner",
120
+ "text": "Message.",
121
+ "lang": "en",
122
+ "dismissible": true,
123
+ "dismissedAt": null,
124
+ "priority": 50,
125
+ "startsAt": "2026-07-01T00:00:00Z",
126
+ "endsAt": null,
127
+ "source": {
128
+ "trigger": "test",
129
+ "at": "2026-07-01T00:00:00Z"
130
+ },
131
+ "cozyMetadata": {
132
+ "createdAt": "2026-07-01T00:00:00Z",
133
+ "createdByApp": "stack",
134
+ "doctypeVersion": "1",
135
+ "metadataVersion": 1,
136
+ "updatedAt": "2026-07-01T00:00:00Z"
137
+ }
138
+ }
139
+ ],
140
+ "expected": [
141
+ {
142
+ "bannerId": "B.upper",
143
+ "severity": "info",
144
+ "surface": "banner",
145
+ "cta": false,
146
+ "secondaryCta": false
147
+ },
148
+ {
149
+ "bannerId": "a.lower",
150
+ "severity": "info",
151
+ "surface": "banner",
152
+ "cta": false,
153
+ "secondaryCta": false
154
+ }
155
+ ]
156
+ },
157
+ {
158
+ "name": "a window that has not started is hidden",
159
+ "input": [
160
+ {
161
+ "_id": "doc005",
162
+ "_rev": "1-aaa",
163
+ "bannerId": "future",
164
+ "category": "quota",
165
+ "severity": "info",
166
+ "surface": "banner",
167
+ "text": "Message.",
168
+ "lang": "en",
169
+ "dismissible": true,
170
+ "dismissedAt": null,
171
+ "priority": 50,
172
+ "startsAt": "2026-07-23T00:00:00Z",
173
+ "endsAt": null,
174
+ "source": {
175
+ "trigger": "test",
176
+ "at": "2026-07-01T00:00:00Z"
177
+ },
178
+ "cozyMetadata": {
179
+ "createdAt": "2026-07-01T00:00:00Z",
180
+ "createdByApp": "stack",
181
+ "doctypeVersion": "1",
182
+ "metadataVersion": 1,
183
+ "updatedAt": "2026-07-01T00:00:00Z"
184
+ }
185
+ }
186
+ ],
187
+ "expected": []
188
+ },
189
+ {
190
+ "name": "startsAt is inclusive",
191
+ "input": [
192
+ {
193
+ "_id": "doc006",
194
+ "_rev": "1-aaa",
195
+ "bannerId": "starting",
196
+ "category": "quota",
197
+ "severity": "info",
198
+ "surface": "banner",
199
+ "text": "Message.",
200
+ "lang": "en",
201
+ "dismissible": true,
202
+ "dismissedAt": null,
203
+ "priority": 50,
204
+ "startsAt": "2026-07-22T12:00:00Z",
205
+ "endsAt": null,
206
+ "source": {
207
+ "trigger": "test",
208
+ "at": "2026-07-01T00:00:00Z"
209
+ },
210
+ "cozyMetadata": {
211
+ "createdAt": "2026-07-01T00:00:00Z",
212
+ "createdByApp": "stack",
213
+ "doctypeVersion": "1",
214
+ "metadataVersion": 1,
215
+ "updatedAt": "2026-07-01T00:00:00Z"
216
+ }
217
+ }
218
+ ],
219
+ "expected": [
220
+ {
221
+ "bannerId": "starting",
222
+ "severity": "info",
223
+ "surface": "banner",
224
+ "cta": false,
225
+ "secondaryCta": false
226
+ }
227
+ ]
228
+ },
229
+ {
230
+ "name": "endsAt is exclusive",
231
+ "input": [
232
+ {
233
+ "_id": "doc007",
234
+ "_rev": "1-aaa",
235
+ "bannerId": "ending",
236
+ "category": "quota",
237
+ "severity": "info",
238
+ "surface": "banner",
239
+ "text": "Message.",
240
+ "lang": "en",
241
+ "dismissible": true,
242
+ "dismissedAt": null,
243
+ "priority": 50,
244
+ "startsAt": "2026-07-01T00:00:00Z",
245
+ "endsAt": "2026-07-22T12:00:00Z",
246
+ "source": {
247
+ "trigger": "test",
248
+ "at": "2026-07-01T00:00:00Z"
249
+ },
250
+ "cozyMetadata": {
251
+ "createdAt": "2026-07-01T00:00:00Z",
252
+ "createdByApp": "stack",
253
+ "doctypeVersion": "1",
254
+ "metadataVersion": 1,
255
+ "updatedAt": "2026-07-01T00:00:00Z"
256
+ }
257
+ }
258
+ ],
259
+ "expected": []
260
+ },
261
+ {
262
+ "name": "a window still open is visible",
263
+ "input": [
264
+ {
265
+ "_id": "doc008",
266
+ "_rev": "1-aaa",
267
+ "bannerId": "open",
268
+ "category": "quota",
269
+ "severity": "info",
270
+ "surface": "banner",
271
+ "text": "Message.",
272
+ "lang": "en",
273
+ "dismissible": true,
274
+ "dismissedAt": null,
275
+ "priority": 50,
276
+ "startsAt": "2026-07-01T00:00:00Z",
277
+ "endsAt": "2026-07-23T00:00:00Z",
278
+ "source": {
279
+ "trigger": "test",
280
+ "at": "2026-07-01T00:00:00Z"
281
+ },
282
+ "cozyMetadata": {
283
+ "createdAt": "2026-07-01T00:00:00Z",
284
+ "createdByApp": "stack",
285
+ "doctypeVersion": "1",
286
+ "metadataVersion": 1,
287
+ "updatedAt": "2026-07-01T00:00:00Z"
288
+ }
289
+ }
290
+ ],
291
+ "expected": [
292
+ {
293
+ "bannerId": "open",
294
+ "severity": "info",
295
+ "surface": "banner",
296
+ "cta": false,
297
+ "secondaryCta": false
298
+ }
299
+ ]
300
+ },
301
+ {
302
+ "name": "a null endsAt is open ended",
303
+ "input": [
304
+ {
305
+ "_id": "doc009",
306
+ "_rev": "1-aaa",
307
+ "bannerId": "openended",
308
+ "category": "quota",
309
+ "severity": "info",
310
+ "surface": "banner",
311
+ "text": "Message.",
312
+ "lang": "en",
313
+ "dismissible": true,
314
+ "dismissedAt": null,
315
+ "priority": 50,
316
+ "startsAt": "2026-07-01T00:00:00Z",
317
+ "endsAt": null,
318
+ "source": {
319
+ "trigger": "test",
320
+ "at": "2026-07-01T00:00:00Z"
321
+ },
322
+ "cozyMetadata": {
323
+ "createdAt": "2026-07-01T00:00:00Z",
324
+ "createdByApp": "stack",
325
+ "doctypeVersion": "1",
326
+ "metadataVersion": 1,
327
+ "updatedAt": "2026-07-01T00:00:00Z"
328
+ }
329
+ }
330
+ ],
331
+ "expected": [
332
+ {
333
+ "bannerId": "openended",
334
+ "severity": "info",
335
+ "surface": "banner",
336
+ "cta": false,
337
+ "secondaryCta": false
338
+ }
339
+ ]
340
+ },
341
+ {
342
+ "name": "an absent endsAt means the same as null",
343
+ "input": [
344
+ {
345
+ "_id": "doc010",
346
+ "_rev": "1-aaa",
347
+ "bannerId": "absentend",
348
+ "category": "quota",
349
+ "severity": "info",
350
+ "surface": "banner",
351
+ "text": "Message.",
352
+ "lang": "en",
353
+ "dismissible": true,
354
+ "dismissedAt": null,
355
+ "priority": 50,
356
+ "startsAt": "2026-07-01T00:00:00Z",
357
+ "source": {
358
+ "trigger": "test",
359
+ "at": "2026-07-01T00:00:00Z"
360
+ },
361
+ "cozyMetadata": {
362
+ "createdAt": "2026-07-01T00:00:00Z",
363
+ "createdByApp": "stack",
364
+ "doctypeVersion": "1",
365
+ "metadataVersion": 1,
366
+ "updatedAt": "2026-07-01T00:00:00Z"
367
+ }
368
+ }
369
+ ],
370
+ "expected": [
371
+ {
372
+ "bannerId": "absentend",
373
+ "severity": "info",
374
+ "surface": "banner",
375
+ "cta": false,
376
+ "secondaryCta": false
377
+ }
378
+ ]
379
+ },
380
+ {
381
+ "name": "an unparseable startsAt hides the banner",
382
+ "input": [
383
+ {
384
+ "_id": "doc011",
385
+ "_rev": "1-aaa",
386
+ "bannerId": "badstart",
387
+ "category": "quota",
388
+ "severity": "info",
389
+ "surface": "banner",
390
+ "text": "Message.",
391
+ "lang": "en",
392
+ "dismissible": true,
393
+ "dismissedAt": null,
394
+ "priority": 50,
395
+ "startsAt": "not a date",
396
+ "endsAt": null,
397
+ "source": {
398
+ "trigger": "test",
399
+ "at": "2026-07-01T00:00:00Z"
400
+ },
401
+ "cozyMetadata": {
402
+ "createdAt": "2026-07-01T00:00:00Z",
403
+ "createdByApp": "stack",
404
+ "doctypeVersion": "1",
405
+ "metadataVersion": 1,
406
+ "updatedAt": "2026-07-01T00:00:00Z"
407
+ }
408
+ }
409
+ ],
410
+ "expected": []
411
+ },
412
+ {
413
+ "name": "an unparseable endsAt hides the banner",
414
+ "input": [
415
+ {
416
+ "_id": "doc012",
417
+ "_rev": "1-aaa",
418
+ "bannerId": "badend",
419
+ "category": "quota",
420
+ "severity": "info",
421
+ "surface": "banner",
422
+ "text": "Message.",
423
+ "lang": "en",
424
+ "dismissible": true,
425
+ "dismissedAt": null,
426
+ "priority": 50,
427
+ "startsAt": "2026-07-01T00:00:00Z",
428
+ "endsAt": "22/07/2026",
429
+ "source": {
430
+ "trigger": "test",
431
+ "at": "2026-07-01T00:00:00Z"
432
+ },
433
+ "cozyMetadata": {
434
+ "createdAt": "2026-07-01T00:00:00Z",
435
+ "createdByApp": "stack",
436
+ "doctypeVersion": "1",
437
+ "metadataVersion": 1,
438
+ "updatedAt": "2026-07-01T00:00:00Z"
439
+ }
440
+ }
441
+ ],
442
+ "expected": []
443
+ },
444
+ {
445
+ "name": "a dismissed banner is hidden",
446
+ "input": [
447
+ {
448
+ "_id": "doc013",
449
+ "_rev": "1-aaa",
450
+ "bannerId": "dismissed",
451
+ "category": "quota",
452
+ "severity": "info",
453
+ "surface": "banner",
454
+ "text": "Message.",
455
+ "lang": "en",
456
+ "dismissible": true,
457
+ "dismissedAt": "2026-07-21T08:00:00Z",
458
+ "priority": 50,
459
+ "startsAt": "2026-07-01T00:00:00Z",
460
+ "endsAt": null,
461
+ "source": {
462
+ "trigger": "test",
463
+ "at": "2026-07-01T00:00:00Z"
464
+ },
465
+ "cozyMetadata": {
466
+ "createdAt": "2026-07-01T00:00:00Z",
467
+ "createdByApp": "stack",
468
+ "doctypeVersion": "1",
469
+ "metadataVersion": 1,
470
+ "updatedAt": "2026-07-01T00:00:00Z"
471
+ }
472
+ }
473
+ ],
474
+ "expected": []
475
+ },
476
+ {
477
+ "name": "a dismissal recorded while dismissible wins over dismissible false",
478
+ "input": [
479
+ {
480
+ "_id": "doc014",
481
+ "_rev": "1-aaa",
482
+ "bannerId": "locked",
483
+ "category": "quota",
484
+ "severity": "info",
485
+ "surface": "banner",
486
+ "text": "Message.",
487
+ "lang": "en",
488
+ "dismissible": false,
489
+ "dismissedAt": "2026-07-21T08:00:00Z",
490
+ "priority": 50,
491
+ "startsAt": "2026-07-01T00:00:00Z",
492
+ "endsAt": null,
493
+ "source": {
494
+ "trigger": "test",
495
+ "at": "2026-07-01T00:00:00Z"
496
+ },
497
+ "cozyMetadata": {
498
+ "createdAt": "2026-07-01T00:00:00Z",
499
+ "createdByApp": "stack",
500
+ "doctypeVersion": "1",
501
+ "metadataVersion": 1,
502
+ "updatedAt": "2026-07-01T00:00:00Z"
503
+ }
504
+ }
505
+ ],
506
+ "expected": []
507
+ },
508
+ {
509
+ "name": "an unknown category still renders",
510
+ "input": [
511
+ {
512
+ "_id": "doc015",
513
+ "_rev": "1-aaa",
514
+ "bannerId": "unknowncat",
515
+ "category": "weather",
516
+ "severity": "info",
517
+ "surface": "banner",
518
+ "text": "Message.",
519
+ "lang": "en",
520
+ "dismissible": true,
521
+ "dismissedAt": null,
522
+ "priority": 50,
523
+ "startsAt": "2026-07-01T00:00:00Z",
524
+ "endsAt": null,
525
+ "source": {
526
+ "trigger": "test",
527
+ "at": "2026-07-01T00:00:00Z"
528
+ },
529
+ "cozyMetadata": {
530
+ "createdAt": "2026-07-01T00:00:00Z",
531
+ "createdByApp": "stack",
532
+ "doctypeVersion": "1",
533
+ "metadataVersion": 1,
534
+ "updatedAt": "2026-07-01T00:00:00Z"
535
+ }
536
+ }
537
+ ],
538
+ "expected": [
539
+ {
540
+ "bannerId": "unknowncat",
541
+ "severity": "info",
542
+ "surface": "banner",
543
+ "cta": false,
544
+ "secondaryCta": false
545
+ }
546
+ ]
547
+ },
548
+ {
549
+ "name": "an unknown severity renders as warning",
550
+ "input": [
551
+ {
552
+ "_id": "doc016",
553
+ "_rev": "1-aaa",
554
+ "bannerId": "unknownsev",
555
+ "category": "quota",
556
+ "severity": "critical",
557
+ "surface": "banner",
558
+ "text": "Message.",
559
+ "lang": "en",
560
+ "dismissible": true,
561
+ "dismissedAt": null,
562
+ "priority": 50,
563
+ "startsAt": "2026-07-01T00:00:00Z",
564
+ "endsAt": null,
565
+ "source": {
566
+ "trigger": "test",
567
+ "at": "2026-07-01T00:00:00Z"
568
+ },
569
+ "cozyMetadata": {
570
+ "createdAt": "2026-07-01T00:00:00Z",
571
+ "createdByApp": "stack",
572
+ "doctypeVersion": "1",
573
+ "metadataVersion": 1,
574
+ "updatedAt": "2026-07-01T00:00:00Z"
575
+ }
576
+ }
577
+ ],
578
+ "expected": [
579
+ {
580
+ "bannerId": "unknownsev",
581
+ "severity": "warning",
582
+ "surface": "banner",
583
+ "cta": false,
584
+ "secondaryCta": false
585
+ }
586
+ ]
587
+ },
588
+ {
589
+ "name": "an unknown surface renders as banner",
590
+ "input": [
591
+ {
592
+ "_id": "doc017",
593
+ "_rev": "1-aaa",
594
+ "bannerId": "unknownsurface",
595
+ "category": "quota",
596
+ "severity": "info",
597
+ "surface": "toast",
598
+ "text": "Message.",
599
+ "lang": "en",
600
+ "dismissible": true,
601
+ "dismissedAt": null,
602
+ "priority": 50,
603
+ "startsAt": "2026-07-01T00:00:00Z",
604
+ "endsAt": null,
605
+ "source": {
606
+ "trigger": "test",
607
+ "at": "2026-07-01T00:00:00Z"
608
+ },
609
+ "cozyMetadata": {
610
+ "createdAt": "2026-07-01T00:00:00Z",
611
+ "createdByApp": "stack",
612
+ "doctypeVersion": "1",
613
+ "metadataVersion": 1,
614
+ "updatedAt": "2026-07-01T00:00:00Z"
615
+ }
616
+ }
617
+ ],
618
+ "expected": [
619
+ {
620
+ "bannerId": "unknownsurface",
621
+ "severity": "info",
622
+ "surface": "banner",
623
+ "cta": false,
624
+ "secondaryCta": false
625
+ }
626
+ ]
627
+ },
628
+ {
629
+ "name": "an https call to action renders",
630
+ "input": [
631
+ {
632
+ "_id": "doc018",
633
+ "_rev": "1-aaa",
634
+ "bannerId": "withcta",
635
+ "category": "quota",
636
+ "severity": "info",
637
+ "surface": "banner",
638
+ "text": "Message.",
639
+ "lang": "en",
640
+ "cta": {
641
+ "label": "Upgrade",
642
+ "url": "https://example.org/plans"
643
+ },
644
+ "dismissible": true,
645
+ "dismissedAt": null,
646
+ "priority": 50,
647
+ "startsAt": "2026-07-01T00:00:00Z",
648
+ "endsAt": null,
649
+ "source": {
650
+ "trigger": "test",
651
+ "at": "2026-07-01T00:00:00Z"
652
+ },
653
+ "cozyMetadata": {
654
+ "createdAt": "2026-07-01T00:00:00Z",
655
+ "createdByApp": "stack",
656
+ "doctypeVersion": "1",
657
+ "metadataVersion": 1,
658
+ "updatedAt": "2026-07-01T00:00:00Z"
659
+ }
660
+ }
661
+ ],
662
+ "expected": [
663
+ {
664
+ "bannerId": "withcta",
665
+ "severity": "info",
666
+ "surface": "banner",
667
+ "cta": true,
668
+ "secondaryCta": false
669
+ }
670
+ ]
671
+ },
672
+ {
673
+ "name": "a null call to action renders no button",
674
+ "input": [
675
+ {
676
+ "_id": "doc020",
677
+ "_rev": "1-aaa",
678
+ "bannerId": "nullcta",
679
+ "category": "quota",
680
+ "severity": "info",
681
+ "surface": "banner",
682
+ "text": "Message.",
683
+ "lang": "en",
684
+ "cta": null,
685
+ "dismissible": true,
686
+ "dismissedAt": null,
687
+ "priority": 50,
688
+ "startsAt": "2026-07-01T00:00:00Z",
689
+ "endsAt": null,
690
+ "source": {
691
+ "trigger": "test",
692
+ "at": "2026-07-01T00:00:00Z"
693
+ },
694
+ "cozyMetadata": {
695
+ "createdAt": "2026-07-01T00:00:00Z",
696
+ "createdByApp": "stack",
697
+ "doctypeVersion": "1",
698
+ "metadataVersion": 1,
699
+ "updatedAt": "2026-07-01T00:00:00Z"
700
+ }
701
+ }
702
+ ],
703
+ "expected": [
704
+ {
705
+ "bannerId": "nullcta",
706
+ "severity": "info",
707
+ "surface": "banner",
708
+ "cta": false,
709
+ "secondaryCta": false
710
+ }
711
+ ]
712
+ },
713
+ {
714
+ "name": "a secondary call to action renders alongside the primary one",
715
+ "input": [
716
+ {
717
+ "_id": "doc019",
718
+ "_rev": "1-aaa",
719
+ "bannerId": "withsecondary",
720
+ "category": "billing",
721
+ "severity": "warning",
722
+ "surface": "modal",
723
+ "text": "Message.",
724
+ "lang": "en",
725
+ "cta": {
726
+ "label": "Update payment method",
727
+ "url": "https://example.org/billing"
728
+ },
729
+ "dismissible": false,
730
+ "dismissedAt": null,
731
+ "priority": 50,
732
+ "startsAt": "2026-07-01T00:00:00Z",
733
+ "endsAt": null,
734
+ "source": {
735
+ "trigger": "test",
736
+ "at": "2026-07-01T00:00:00Z"
737
+ },
738
+ "cozyMetadata": {
739
+ "createdAt": "2026-07-01T00:00:00Z",
740
+ "createdByApp": "stack",
741
+ "doctypeVersion": "1",
742
+ "metadataVersion": 1,
743
+ "updatedAt": "2026-07-01T00:00:00Z"
744
+ },
745
+ "secondaryCta": {
746
+ "label": "Contact support",
747
+ "url": "https://example.org/support"
748
+ }
749
+ }
750
+ ],
751
+ "expected": [
752
+ {
753
+ "bannerId": "withsecondary",
754
+ "severity": "warning",
755
+ "surface": "modal",
756
+ "cta": true,
757
+ "secondaryCta": true
758
+ }
759
+ ]
760
+ },
761
+ {
762
+ "name": "a secondary call to action without a primary one is ignored",
763
+ "input": [
764
+ {
765
+ "_id": "doc020",
766
+ "_rev": "1-aaa",
767
+ "bannerId": "orphansecondary",
768
+ "category": "billing",
769
+ "severity": "warning",
770
+ "surface": "modal",
771
+ "text": "Message.",
772
+ "lang": "en",
773
+ "cta": null,
774
+ "dismissible": true,
775
+ "dismissedAt": null,
776
+ "priority": 50,
777
+ "startsAt": "2026-07-01T00:00:00Z",
778
+ "endsAt": null,
779
+ "source": {
780
+ "trigger": "test",
781
+ "at": "2026-07-01T00:00:00Z"
782
+ },
783
+ "cozyMetadata": {
784
+ "createdAt": "2026-07-01T00:00:00Z",
785
+ "createdByApp": "stack",
786
+ "doctypeVersion": "1",
787
+ "metadataVersion": 1,
788
+ "updatedAt": "2026-07-01T00:00:00Z"
789
+ },
790
+ "secondaryCta": {
791
+ "label": "Contact support",
792
+ "url": "https://example.org/support"
793
+ }
794
+ }
795
+ ],
796
+ "expected": [
797
+ {
798
+ "bannerId": "orphansecondary",
799
+ "severity": "warning",
800
+ "surface": "modal",
801
+ "cta": false,
802
+ "secondaryCta": false
803
+ }
804
+ ]
805
+ }
806
+ ]
807
+ }