@financial-times/x-teaser 18.0.0 → 18.2.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.
package/Props.d.ts CHANGED
@@ -31,6 +31,7 @@ export interface Features {
31
31
  showTitle?: boolean
32
32
  showStandfirst?: boolean
33
33
  showPremiumLabel?: boolean
34
+ showScoopLabel?: boolean
34
35
  showStatus?: boolean
35
36
  showImage?: boolean
36
37
  showHeadshot?: boolean
@@ -0,0 +1,312 @@
1
+ const { h } = require('@financial-times/x-engine')
2
+ const { mount } = require('@financial-times/x-test-utils/enzyme')
3
+
4
+ import * as LiveBlogStatus from '../src/LiveBlogStatus'
5
+ import * as ScoopLabel from '../src/ScoopLabel'
6
+ import * as PremiumLabel from '../src/PremiumLabel'
7
+ import * as AlwaysShowTimestamp from '../src/AlwaysShowTimestamp'
8
+ import * as RelativeTime from '../src/RelativeTime'
9
+ import * as TimeStamp from '../src/TimeStamp'
10
+
11
+ const LiveBlogStatusSpy = jest
12
+ .spyOn(LiveBlogStatus, 'default')
13
+ .mockReturnValue(<div className="live-blog-status">LiveBlogStatus</div>)
14
+ const ScoopLabelSpy = jest
15
+ .spyOn(ScoopLabel, 'default')
16
+ .mockReturnValue(<div className="scoop-label">ScoopLabel</div>)
17
+ const PremiumLabelSpy = jest
18
+ .spyOn(PremiumLabel, 'default')
19
+ .mockReturnValue(<div className="premium-label">PremiumLabel</div>)
20
+ const AlwaysShowTimestampSpy = jest
21
+ .spyOn(AlwaysShowTimestamp, 'default')
22
+ .mockReturnValue(<div className="always-show-timestamp">AlwaysShowTimestamp</div>)
23
+ const RelativeTimeSpy = jest
24
+ .spyOn(RelativeTime, 'default')
25
+ .mockReturnValue(<div className="relative-time">RelativeTimeSpy</div>)
26
+ const TimeStampSpy = jest
27
+ .spyOn(TimeStamp, 'default')
28
+ .mockReturnValue(<div className="timestamp">TimeStamp</div>)
29
+
30
+ import Status from '../src/Status'
31
+
32
+ describe('Status - Display Logic', () => {
33
+ let props
34
+
35
+ beforeEach(() => {
36
+ props = {
37
+ showStatus: true,
38
+ showPremiumLabel: true,
39
+ showScoopLabel: true,
40
+ status: 'inprogress',
41
+ indicators: { isScoop: true, accessLevel: 'premium' },
42
+ firstPublishedDate: '2025-10-01T00:00:00.000Z',
43
+ publishedDate: '2025-10-01T00:00:00.000Z',
44
+ useRelativeTimeIfToday: true,
45
+ useRelativeTime: true
46
+ }
47
+ })
48
+
49
+ afterEach(() => {
50
+ jest.clearAllMocks()
51
+ })
52
+
53
+ describe('LiveBlogStatus', () => {
54
+ it('renders only LiveBlogStatus when LiveBlogStatus props are present', () => {
55
+ mount(<Status {...props} />)
56
+
57
+ expect(LiveBlogStatusSpy).toHaveBeenCalledTimes(1)
58
+ expect(ScoopLabelSpy).not.toHaveBeenCalled()
59
+ expect(PremiumLabelSpy).not.toHaveBeenCalled()
60
+ expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
61
+ expect(RelativeTimeSpy).not.toHaveBeenCalled()
62
+ expect(TimeStampSpy).not.toHaveBeenCalled()
63
+ })
64
+
65
+ it('should not render LiveBlogStatus when status is not present', () => {
66
+ delete props.status
67
+ mount(<Status {...props} />)
68
+
69
+ expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
70
+ })
71
+
72
+ it('should not render LiveBlogStatus when showStatus is false', () => {
73
+ props.showStatus = false
74
+ mount(<Status {...props} />)
75
+
76
+ expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
77
+ })
78
+ })
79
+
80
+ describe('ScoopLabel', () => {
81
+ beforeEach(() => {
82
+ delete props.status
83
+ })
84
+
85
+ it('renders only ScoopLabel when higher-level render props are absent and ScoopLabel props are present', () => {
86
+ mount(<Status {...props} />)
87
+
88
+ expect(ScoopLabelSpy).toHaveBeenCalledTimes(1)
89
+ expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
90
+ expect(PremiumLabelSpy).not.toHaveBeenCalled()
91
+ expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
92
+ expect(RelativeTimeSpy).not.toHaveBeenCalled()
93
+ expect(TimeStampSpy).not.toHaveBeenCalled()
94
+ })
95
+
96
+ it('should not render ScoopLabel when showScoopLabel = false', () => {
97
+ props.showScoopLabel = false
98
+ mount(<Status {...props} />)
99
+
100
+ expect(ScoopLabelSpy).not.toHaveBeenCalled()
101
+ })
102
+
103
+ it('should not render ScoopLabel when props.indicators.isScoop = false', () => {
104
+ props.indicators.isScoop = false
105
+ mount(<Status {...props} />)
106
+
107
+ expect(ScoopLabelSpy).not.toHaveBeenCalled()
108
+ })
109
+
110
+ it('should not render ScoopLabel when firstPublishedDate is older than the cutoff date', () => {
111
+ props.firstPublishedDate = '2025-09-01T00:00:00.000Z'
112
+ mount(<Status {...props} />)
113
+
114
+ expect(ScoopLabelSpy).not.toHaveBeenCalled()
115
+ })
116
+ })
117
+
118
+ describe('PremiumLabel', () => {
119
+ beforeEach(() => {
120
+ delete props.status
121
+ props.showScoopLabel = false
122
+ })
123
+
124
+ it('renders only PremiumLabel when higher-level render props are absent and PremiumLabel props are present', () => {
125
+ mount(<Status {...props} />)
126
+
127
+ expect(PremiumLabelSpy).toHaveBeenCalledTimes(1)
128
+ expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
129
+ expect(ScoopLabelSpy).not.toHaveBeenCalled()
130
+ expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
131
+ expect(RelativeTimeSpy).not.toHaveBeenCalled()
132
+ expect(TimeStampSpy).not.toHaveBeenCalled()
133
+ })
134
+
135
+ it('should not render PremiumLabel when showPremiumLabel = false', () => {
136
+ props.showPremiumLabel = false
137
+ mount(<Status {...props} />)
138
+
139
+ expect(PremiumLabelSpy).not.toHaveBeenCalled()
140
+ })
141
+
142
+ it('should not render PremiumLabel when accessLevel is not premium', () => {
143
+ props.indicators.accessLevel = 'subscribed'
144
+ mount(<Status {...props} />)
145
+
146
+ expect(PremiumLabelSpy).not.toHaveBeenCalled()
147
+ })
148
+ })
149
+
150
+ describe('AlwaysShowTimestamp', () => {
151
+ beforeEach(() => {
152
+ delete props.status
153
+ props.showScoopLabel = false
154
+ props.showPremiumLabel = false
155
+ })
156
+
157
+ it('renders only AlwaysShowTimestamp when higher-level render props are absent and AlwaysShowTimestamp props are present', () => {
158
+ mount(<Status {...props} />)
159
+
160
+ expect(AlwaysShowTimestampSpy).toHaveBeenCalledTimes(1)
161
+ expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
162
+ expect(ScoopLabelSpy).not.toHaveBeenCalled()
163
+ expect(PremiumLabelSpy).not.toHaveBeenCalled()
164
+ expect(RelativeTimeSpy).not.toHaveBeenCalled()
165
+ expect(TimeStampSpy).not.toHaveBeenCalled()
166
+ })
167
+
168
+ it('should not render AlwaysShowTimestamp when showStatus = false', () => {
169
+ props.showStatus = false
170
+ mount(<Status {...props} />)
171
+
172
+ expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
173
+ })
174
+
175
+ it('should not render AlwaysShowTimestamp when publishedDate is not present', () => {
176
+ delete props.publishedDate
177
+ mount(<Status {...props} />)
178
+
179
+ expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
180
+ })
181
+
182
+ it('should not render AlwaysShowTimestamp when useRelativeTimeIfToday = false', () => {
183
+ props.useRelativeTimeIfToday = false
184
+ mount(<Status {...props} />)
185
+
186
+ expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
187
+ })
188
+ })
189
+
190
+ describe('RelativeTime', () => {
191
+ beforeEach(() => {
192
+ delete props.status
193
+ props.showScoopLabel = false
194
+ props.showPremiumLabel = false
195
+ props.useRelativeTimeIfToday = false
196
+ })
197
+
198
+ it('renders only RelativeTime when higher-level render props are absent and RelativeTime props are present', () => {
199
+ mount(<Status {...props} />)
200
+
201
+ expect(RelativeTimeSpy).toHaveBeenCalledTimes(1)
202
+ expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
203
+ expect(ScoopLabelSpy).not.toHaveBeenCalled()
204
+ expect(PremiumLabelSpy).not.toHaveBeenCalled()
205
+ expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
206
+ expect(TimeStampSpy).not.toHaveBeenCalled()
207
+ })
208
+
209
+ it('should not render RelativeTime when showStatus = false', () => {
210
+ props.showStatus = false
211
+ mount(<Status {...props} />)
212
+
213
+ expect(RelativeTimeSpy).not.toHaveBeenCalled()
214
+ })
215
+
216
+ it('should not render RelativeTime when publishedDate is not present', () => {
217
+ delete props.publishedDate
218
+ mount(<Status {...props} />)
219
+
220
+ expect(RelativeTimeSpy).not.toHaveBeenCalled()
221
+ })
222
+
223
+ it('should not render RelativeTime when useRelativeTime = false', () => {
224
+ props.useRelativeTime = false
225
+ mount(<Status {...props} />)
226
+
227
+ expect(RelativeTimeSpy).not.toHaveBeenCalled()
228
+ })
229
+ })
230
+
231
+ describe('TimeStamp', () => {
232
+ beforeEach(() => {
233
+ delete props.status
234
+ props.showScoopLabel = false
235
+ props.showPremiumLabel = false
236
+ props.useRelativeTimeIfToday = false
237
+ props.useRelativeTime = false
238
+ })
239
+
240
+ it('renders only TimeStamp when higher-level render props are absent and TimeStamp props are present', () => {
241
+ mount(<Status {...props} />)
242
+
243
+ expect(TimeStampSpy).toHaveBeenCalledTimes(1)
244
+ expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
245
+ expect(ScoopLabelSpy).not.toHaveBeenCalled()
246
+ expect(PremiumLabelSpy).not.toHaveBeenCalled()
247
+ expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
248
+ expect(RelativeTimeSpy).not.toHaveBeenCalled()
249
+ })
250
+
251
+ it('should not render TimeStamp when showStatus = false', () => {
252
+ props.showStatus = false
253
+ mount(<Status {...props} />)
254
+
255
+ expect(TimeStampSpy).not.toHaveBeenCalled()
256
+ })
257
+
258
+ it('should not render TimeStamp when publishedDate is not present', () => {
259
+ delete props.publishedDate
260
+ mount(<Status {...props} />)
261
+
262
+ expect(TimeStampSpy).not.toHaveBeenCalled()
263
+ })
264
+ })
265
+
266
+ describe('No Status rendered', () => {
267
+ it('should not render any Status - Case 1: all showXXX properties are set to false', () => {
268
+ props = {
269
+ showStatus: false,
270
+ showPremiumLabel: false,
271
+ showScoopLabel: false,
272
+ status: 'inprogress',
273
+ indicators: { isScoop: true, accessLevel: 'premium' },
274
+ firstPublishedDate: '2025-10-01T00:00:00.000Z',
275
+ publishedDate: '2025-10-01T00:00:00.000Z',
276
+ useRelativeTimeIfToday: true,
277
+ useRelativeTime: true
278
+ }
279
+ const subject = mount(<Status {...props} />)
280
+
281
+ expect(subject.isEmptyRender()).toBeTruthy()
282
+ expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
283
+ expect(ScoopLabelSpy).not.toHaveBeenCalled()
284
+ expect(PremiumLabelSpy).not.toHaveBeenCalled()
285
+ expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
286
+ expect(RelativeTimeSpy).not.toHaveBeenCalled()
287
+ expect(TimeStampSpy).not.toHaveBeenCalled()
288
+ })
289
+
290
+ it('should not render any Status - Case 2: showStatus = true but other properties are not present', () => {
291
+ // status & publishedDate are missing
292
+ props = {
293
+ showStatus: true,
294
+ showPremiumLabel: false,
295
+ showScoopLabel: false,
296
+ indicators: { isScoop: true, accessLevel: 'premium' },
297
+ firstPublishedDate: '2025-10-01T00:00:00.000Z',
298
+ useRelativeTimeIfToday: true,
299
+ useRelativeTime: true
300
+ }
301
+ const subject = mount(<Status {...props} />)
302
+
303
+ expect(subject.isEmptyRender()).toBeTruthy()
304
+ expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
305
+ expect(ScoopLabelSpy).not.toHaveBeenCalled()
306
+ expect(PremiumLabelSpy).not.toHaveBeenCalled()
307
+ expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
308
+ expect(RelativeTimeSpy).not.toHaveBeenCalled()
309
+ expect(TimeStampSpy).not.toHaveBeenCalled()
310
+ })
311
+ })
312
+ })