@contentful/field-editor-shared 2.14.0 → 2.16.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.
@@ -1,32 +1,32 @@
1
1
  import { renderHook } from '@testing-library/react';
2
2
  import { useReleaseStatus } from './useReleaseStatus';
3
- const buildEntry = (status, id = 'entry-1')=>({
4
- sys: {
5
- id,
6
- type: 'Entry',
7
- fieldStatus: {
8
- '*': {
9
- 'en-US': status
10
- }
11
- },
12
- version: 3,
13
- publishedVersion: status === 'published' ? 2 : undefined
14
- }
15
- });
16
- const buildAsset = (status, id = 'asset-1')=>({
17
- sys: {
18
- id,
19
- type: 'Asset',
20
- fieldStatus: {
21
- '*': {
22
- 'en-US': status
3
+ const createEntityBuilder = (entityType, defaultId)=>{
4
+ let currentId = defaultId;
5
+ return {
6
+ withId (id) {
7
+ currentId = id;
8
+ return this;
9
+ },
10
+ withStatus (status) {
11
+ return {
12
+ sys: {
13
+ id: currentId,
14
+ type: entityType,
15
+ fieldStatus: {
16
+ '*': {
17
+ 'en-US': status
18
+ }
19
+ },
20
+ version: 3,
21
+ publishedVersion: status === 'published' ? 2 : undefined
23
22
  }
24
- },
25
- version: 3,
26
- publishedVersion: status === 'published' ? 2 : undefined
23
+ };
27
24
  }
28
- });
29
- const createEntryBasedReleaseEntity = ({ entityId = 'entry-1', action = 'publish', entityType = 'Entry' })=>({
25
+ };
26
+ };
27
+ const entryBuilder = ()=>createEntityBuilder('Entry', 'entry-1');
28
+ const assetBuilder = ()=>createEntityBuilder('Asset', 'asset-1');
29
+ const createEntryBasedReleaseEntity = ({ entityId, action = 'publish', entityType })=>({
30
30
  entity: {
31
31
  sys: {
32
32
  type: 'Link',
@@ -36,7 +36,7 @@ const createEntryBasedReleaseEntity = ({ entityId = 'entry-1', action = 'publish
36
36
  },
37
37
  action
38
38
  });
39
- const createEntryBasedRelease = ({ entityId, action, entityType = 'Entry' } = {})=>({
39
+ const createEntryBasedRelease = ({ entityId, action, entityType })=>({
40
40
  title: 'Release 1',
41
41
  sys: {
42
42
  id: 'release-1',
@@ -53,7 +53,7 @@ const createEntryBasedRelease = ({ entityId, action, entityType = 'Entry' } = {}
53
53
  ]
54
54
  }
55
55
  });
56
- const createLocaleBasedReleaseEntity = ({ entityId = 'entry-1', verb = 'add', entityType = 'Entry' })=>({
56
+ const createLocaleBasedReleaseEntity = ({ entityId, verb = 'add', entityType })=>({
57
57
  entity: {
58
58
  sys: {
59
59
  type: 'Link',
@@ -69,7 +69,7 @@ const createLocaleBasedReleaseEntity = ({ entityId = 'entry-1', verb = 'add', en
69
69
  }
70
70
  }
71
71
  });
72
- const createLocaleBasedRelease = ({ entityId, verb, entityType = 'Entry' } = {})=>({
72
+ const createLocaleBasedRelease = ({ entityId, verb, entityType })=>({
73
73
  title: 'Release 1',
74
74
  sys: {
75
75
  id: 'release-1',
@@ -86,224 +86,505 @@ const createLocaleBasedRelease = ({ entityId, verb, entityType = 'Entry' } = {})
86
86
  ]
87
87
  }
88
88
  });
89
+ const createDefaultLocales = ()=>[
90
+ {
91
+ code: 'en-US'
92
+ }
93
+ ];
94
+ const expectLocaleStatus = (result, localeCode, expected)=>{
95
+ expect(result.releaseStatusMap.get(localeCode)).toEqual(expected);
96
+ };
97
+ const expectEntityStatus = (result, expectedStatus)=>{
98
+ expect(result.releaseEntityStatus).toBe(expectedStatus);
99
+ };
89
100
  const ENTITY_TYPES = [
90
101
  'Entry',
91
102
  'Asset'
92
103
  ];
93
104
  describe('useReleaseStatus', ()=>{
94
- beforeEach(()=>{
95
- jest.clearAllMocks();
105
+ const locales = createDefaultLocales();
106
+ describe('Guard clauses and invalid inputs', ()=>{
107
+ it('returns empty map when entity is undefined', ()=>{
108
+ const { result } = renderHook(()=>useReleaseStatus({
109
+ locales,
110
+ release: createEntryBasedRelease({
111
+ entityId: 'entry-1',
112
+ entityType: 'Entry'
113
+ }),
114
+ entity: undefined
115
+ }));
116
+ expect(result.current.releaseStatusMap.size).toBe(0);
117
+ });
118
+ it('returns empty map when release is undefined', ()=>{
119
+ const { result } = renderHook(()=>useReleaseStatus({
120
+ locales,
121
+ entity: entryBuilder().withStatus('published'),
122
+ release: undefined
123
+ }));
124
+ expect(result.current.releaseStatusMap.size).toBe(0);
125
+ });
126
+ it('returns empty map when release has no schemaVersion', ()=>{
127
+ const invalidRelease = {
128
+ title: 'Release 1',
129
+ sys: {
130
+ id: 'release-1',
131
+ type: 'Release'
132
+ },
133
+ entities: {
134
+ items: []
135
+ }
136
+ };
137
+ const { result } = renderHook(()=>useReleaseStatus({
138
+ locales,
139
+ entity: entryBuilder().withStatus('published'),
140
+ release: invalidRelease
141
+ }));
142
+ expect(result.current.releaseStatusMap.size).toBe(0);
143
+ });
144
+ it('returns empty map when release schema is not v2', ()=>{
145
+ const oldSchemaRelease = {
146
+ title: 'Release 1',
147
+ sys: {
148
+ id: 'release-1',
149
+ type: 'Release',
150
+ schemaVersion: 'Release.v1'
151
+ },
152
+ entities: {
153
+ items: []
154
+ }
155
+ };
156
+ const { result } = renderHook(()=>useReleaseStatus({
157
+ locales,
158
+ entity: entryBuilder().withStatus('published'),
159
+ release: oldSchemaRelease
160
+ }));
161
+ expect(result.current.releaseStatusMap.size).toBe(0);
162
+ });
96
163
  });
97
- ENTITY_TYPES.forEach((entityType)=>{
98
- const entityId = entityType === 'Entry' ? 'entry-1' : 'asset-1';
99
- const baseParams = {
100
- locales: [
101
- {
164
+ describe('Edge case: Missing previousEntityOnTimeline', ()=>{
165
+ it('defaults to "Remains draft" when unpublishing without previous state (entry-based)', ()=>{
166
+ const { result } = renderHook(()=>useReleaseStatus({
167
+ locales,
168
+ previousEntityOnTimeline: undefined,
169
+ release: createEntryBasedRelease({
170
+ entityId: 'entry-1',
171
+ entityType: 'Entry',
172
+ action: 'unpublish'
173
+ }),
174
+ entity: entryBuilder().withStatus('draft')
175
+ }));
176
+ expectLocaleStatus(result.current, 'en-US', {
177
+ variant: 'warning',
178
+ status: 'remainsDraft',
179
+ label: 'Remains draft',
180
+ locale: {
102
181
  code: 'en-US'
103
182
  }
104
- ]
105
- };
106
- function buildEntity(status) {
107
- return entityType === 'Entry' ? buildEntry(status) : buildAsset(status);
108
- }
109
- describe(`${entityType} with entry based publishing`, ()=>{
110
- it('returns Will publish status when active release has publish action', ()=>{
111
- const { result } = renderHook(()=>useReleaseStatus({
112
- ...baseParams,
113
- previousEntityOnTimeline: buildEntity('draft'),
114
- release: createEntryBasedRelease({
115
- entityId,
116
- entityType
117
- }),
118
- entity: buildEntity('published')
119
- }));
120
- expect(result.current.releaseStatusMap.get('en-US')).toEqual({
121
- variant: 'positive',
122
- status: 'willPublish',
123
- label: 'Will publish',
124
- locale: {
125
- code: 'en-US'
126
- }
127
- });
128
183
  });
129
- it('returns Becomes draft status when previous version has published locales and active version has unpublish action', ()=>{
130
- const { result } = renderHook(()=>useReleaseStatus({
131
- ...baseParams,
132
- previousEntityOnTimeline: buildEntity('published'),
133
- release: createEntryBasedRelease({
134
- action: 'unpublish',
135
- entityId,
136
- entityType
137
- }),
138
- entity: buildEntity('draft')
139
- }));
140
- expect(result.current.releaseStatusMap.get('en-US')).toEqual({
141
- variant: 'warning',
142
- status: 'becomesDraft',
143
- label: 'Becomes draft',
144
- locale: {
145
- code: 'en-US'
146
- }
147
- });
184
+ });
185
+ it('defaults to "Remains draft" when removing locale without previous state (locale-based)', ()=>{
186
+ const { result } = renderHook(()=>useReleaseStatus({
187
+ locales,
188
+ previousEntityOnTimeline: undefined,
189
+ release: createLocaleBasedRelease({
190
+ entityId: 'entry-1',
191
+ entityType: 'Entry',
192
+ verb: 'remove'
193
+ }),
194
+ entity: entryBuilder().withStatus('draft')
195
+ }));
196
+ expectLocaleStatus(result.current, 'en-US', {
197
+ variant: 'warning',
198
+ status: 'remainsDraft',
199
+ label: 'Remains draft',
200
+ locale: {
201
+ code: 'en-US'
202
+ }
148
203
  });
149
- it('returns Remains draft status when previous version has draft locales and active version has unpublish action', ()=>{
150
- const { result } = renderHook(()=>useReleaseStatus({
151
- ...baseParams,
152
- previousEntityOnTimeline: buildEntity('draft'),
153
- release: createEntryBasedRelease({
154
- action: 'unpublish',
155
- entityId,
156
- entityType
157
- }),
158
- entity: buildEntity('draft')
159
- }));
160
- expect(result.current.releaseStatusMap.get('en-US')).toEqual({
161
- variant: 'warning',
162
- status: 'remainsDraft',
163
- label: 'Remains draft',
164
- locale: {
165
- code: 'en-US'
166
- }
167
- });
204
+ });
205
+ });
206
+ describe('Changed status handling', ()=>{
207
+ const buildChangedEntry = ()=>({
208
+ sys: {
209
+ id: 'entry-1',
210
+ type: 'Entry',
211
+ fieldStatus: {
212
+ '*': {
213
+ 'en-US': 'changed'
214
+ }
215
+ },
216
+ version: 5,
217
+ publishedVersion: 2
218
+ }
168
219
  });
169
- it('returns Not in release status when entity is not in the release', ()=>{
170
- const { result } = renderHook(()=>useReleaseStatus({
171
- ...baseParams,
172
- release: createEntryBasedRelease({
173
- entityId: entityType === 'Entry' ? 'entry-2' : 'asset-2',
174
- action: 'publish',
175
- entityType
176
- }),
177
- entity: buildEntity('draft')
178
- }));
179
- expect(result.current.releaseStatusMap.get('en-US')).toEqual({
180
- variant: 'secondary',
181
- status: 'notInRelease',
182
- label: 'Not in release',
183
- locale: {
184
- code: 'en-US'
185
- }
186
- });
220
+ it('treats "changed" as published-like when unpublishing (entry-based)', ()=>{
221
+ const { result } = renderHook(()=>useReleaseStatus({
222
+ locales,
223
+ previousEntityOnTimeline: buildChangedEntry(),
224
+ release: createEntryBasedRelease({
225
+ entityId: 'entry-1',
226
+ entityType: 'Entry',
227
+ action: 'unpublish'
228
+ }),
229
+ entity: entryBuilder().withStatus('draft')
230
+ }));
231
+ expectLocaleStatus(result.current, 'en-US', {
232
+ variant: 'warning',
233
+ status: 'becomesDraft',
234
+ label: 'Becomes draft',
235
+ locale: {
236
+ code: 'en-US'
237
+ }
187
238
  });
188
- it('returns published when not in release but already published', ()=>{
189
- const { result } = renderHook(()=>useReleaseStatus({
190
- ...baseParams,
191
- release: createEntryBasedRelease({
192
- entityId: entityType === 'Entry' ? 'entry-2' : 'asset-2',
193
- action: 'publish',
194
- entityType
195
- }),
196
- entity: buildEntity('published')
197
- }));
198
- expect(result.current.releaseStatusMap.get('en-US')).toEqual({
199
- variant: 'positive',
200
- status: 'published',
201
- label: 'Published',
202
- locale: {
203
- code: 'en-US'
204
- }
205
- });
206
- expect(result.current.releaseEntityStatus).toBe('published');
239
+ });
240
+ it('treats "changed" as published-like when removing locale (locale-based)', ()=>{
241
+ const { result } = renderHook(()=>useReleaseStatus({
242
+ locales,
243
+ previousEntityOnTimeline: buildChangedEntry(),
244
+ release: createLocaleBasedRelease({
245
+ entityId: 'entry-1',
246
+ entityType: 'Entry',
247
+ verb: 'remove'
248
+ }),
249
+ entity: entryBuilder().withStatus('draft')
250
+ }));
251
+ expectLocaleStatus(result.current, 'en-US', {
252
+ variant: 'warning',
253
+ status: 'becomesDraft',
254
+ label: 'Becomes draft',
255
+ locale: {
256
+ code: 'en-US'
257
+ }
207
258
  });
208
259
  });
209
- describe(`${entityType} with locale based publishing`, ()=>{
210
- it('returns Will publish status when active release has publish action', ()=>{
211
- const { result } = renderHook(()=>useReleaseStatus({
212
- ...baseParams,
213
- previousEntityOnTimeline: buildEntity('draft'),
214
- release: createLocaleBasedRelease({
215
- entityId,
216
- entityType
217
- }),
218
- entity: buildEntity('published')
219
- }));
220
- expect(result.current.releaseStatusMap.get('en-US')).toEqual({
221
- variant: 'positive',
222
- status: 'willPublish',
223
- label: 'Will publish',
224
- locale: {
225
- code: 'en-US'
226
- }
227
- });
260
+ it('shows "Published" for changed reference not in release', ()=>{
261
+ const { result } = renderHook(()=>useReleaseStatus({
262
+ locales,
263
+ release: createEntryBasedRelease({
264
+ entityId: 'entry-2',
265
+ entityType: 'Entry',
266
+ action: 'publish'
267
+ }),
268
+ entity: buildChangedEntry(),
269
+ isReference: true
270
+ }));
271
+ expectLocaleStatus(result.current, 'en-US', {
272
+ variant: 'positive',
273
+ status: 'published',
274
+ label: 'Published',
275
+ locale: {
276
+ code: 'en-US'
277
+ }
228
278
  });
229
- it('returns Becomes draft status when previous version has published locales and active version has unpublish action', ()=>{
230
- const { result } = renderHook(()=>useReleaseStatus({
231
- ...baseParams,
232
- previousEntityOnTimeline: buildEntity('published'),
233
- release: createLocaleBasedRelease({
234
- verb: 'remove',
235
- entityId,
236
- entityType
237
- }),
238
- entity: buildEntity('draft')
239
- }));
240
- expect(result.current.releaseStatusMap.get('en-US')).toEqual({
241
- variant: 'warning',
242
- status: 'becomesDraft',
243
- label: 'Becomes draft',
244
- locale: {
245
- code: 'en-US'
246
- }
247
- });
279
+ });
280
+ });
281
+ describe('Multi-locale scenarios', ()=>{
282
+ const multiLocales = [
283
+ {
284
+ code: 'en-US'
285
+ },
286
+ {
287
+ code: 'de-DE'
288
+ }
289
+ ];
290
+ const buildMultiLocaleEntry = (enStatus, deStatus)=>({
291
+ sys: {
292
+ id: 'entry-1',
293
+ type: 'Entry',
294
+ fieldStatus: {
295
+ '*': {
296
+ 'en-US': enStatus,
297
+ 'de-DE': deStatus
298
+ }
299
+ },
300
+ version: 3,
301
+ publishedVersion: enStatus === 'published' || deStatus === 'published' ? 2 : undefined
302
+ }
248
303
  });
249
- it('returns Remains draft status when previous version has draft locales and active version has unpublish action', ()=>{
250
- const { result } = renderHook(()=>useReleaseStatus({
251
- ...baseParams,
252
- previousEntityOnTimeline: buildEntity('draft'),
253
- release: createLocaleBasedRelease({
254
- verb: 'remove',
255
- entityId,
256
- entityType
257
- }),
258
- entity: buildEntity('draft')
259
- }));
260
- expect(result.current.releaseStatusMap.get('en-US')).toEqual({
261
- variant: 'warning',
262
- status: 'remainsDraft',
263
- label: 'Remains draft',
264
- locale: {
265
- code: 'en-US'
266
- }
267
- });
304
+ it('creates status for each locale', ()=>{
305
+ const { result } = renderHook(()=>useReleaseStatus({
306
+ locales: multiLocales,
307
+ release: createEntryBasedRelease({
308
+ entityId: 'entry-1',
309
+ entityType: 'Entry',
310
+ action: 'publish'
311
+ }),
312
+ entity: buildMultiLocaleEntry('draft', 'draft')
313
+ }));
314
+ expect(result.current.releaseStatusMap.size).toBe(2);
315
+ expect(result.current.releaseStatusMap.has('en-US')).toBe(true);
316
+ expect(result.current.releaseStatusMap.has('de-DE')).toBe(true);
317
+ });
318
+ it('aggregates to "published" when any locale is published', ()=>{
319
+ const publishedEntity = buildMultiLocaleEntry('published', 'draft');
320
+ const { result } = renderHook(()=>useReleaseStatus({
321
+ locales: multiLocales,
322
+ release: createEntryBasedRelease({
323
+ entityId: 'entry-2',
324
+ entityType: 'Entry',
325
+ action: 'publish'
326
+ }),
327
+ entity: publishedEntity,
328
+ isReference: true
329
+ }));
330
+ expectEntityStatus(result.current, 'published');
331
+ });
332
+ it('aggregates to "willPublish" when any locale will publish', ()=>{
333
+ const { result } = renderHook(()=>useReleaseStatus({
334
+ locales: multiLocales,
335
+ release: createEntryBasedRelease({
336
+ entityId: 'entry-1',
337
+ entityType: 'Entry',
338
+ action: 'publish'
339
+ }),
340
+ entity: buildMultiLocaleEntry('draft', 'draft')
341
+ }));
342
+ expectEntityStatus(result.current, 'willPublish');
343
+ });
344
+ it('handles locale not in add/remove arrays', ()=>{
345
+ const releaseWithDifferentLocale = {
346
+ title: 'Release 1',
347
+ sys: {
348
+ id: 'release-1',
349
+ type: 'Release',
350
+ schemaVersion: 'Release.v2'
351
+ },
352
+ entities: {
353
+ items: [
354
+ {
355
+ entity: {
356
+ sys: {
357
+ type: 'Link',
358
+ linkType: 'Entry',
359
+ id: 'entry-1'
360
+ }
361
+ },
362
+ add: {
363
+ fields: {
364
+ '*': [
365
+ 'de-DE'
366
+ ]
367
+ }
368
+ }
369
+ }
370
+ ]
371
+ }
372
+ };
373
+ const { result } = renderHook(()=>useReleaseStatus({
374
+ locales,
375
+ release: releaseWithDifferentLocale,
376
+ entity: entryBuilder().withStatus('draft')
377
+ }));
378
+ expectLocaleStatus(result.current, 'en-US', {
379
+ variant: 'warning',
380
+ status: 'remainsDraft',
381
+ label: 'Remains draft',
382
+ locale: {
383
+ code: 'en-US'
384
+ }
268
385
  });
269
- it('returns Not in release status when entry is in draft state and not in the release', ()=>{
270
- const { result } = renderHook(()=>useReleaseStatus({
271
- ...baseParams,
272
- release: createLocaleBasedRelease({
273
- entityId: entityType === 'Entry' ? 'entry-2' : 'asset-2',
274
- entityType,
275
- verb: 'add'
276
- }),
277
- entity: buildEntity('draft')
278
- }));
279
- expect(result.current.releaseStatusMap.get('en-US')).toEqual({
280
- variant: 'secondary',
281
- status: 'notInRelease',
282
- label: 'Not in release',
283
- locale: {
284
- code: 'en-US'
285
- }
386
+ });
387
+ });
388
+ const testPublishingScenarios = (config)=>{
389
+ const { isReference, publishingModel } = config;
390
+ const locales = createDefaultLocales();
391
+ ENTITY_TYPES.forEach((entityType)=>{
392
+ const defaultEntityId = entityType === 'Entry' ? 'entry-1' : 'asset-1';
393
+ const differentEntityId = entityType === 'Entry' ? 'entry-2' : 'asset-2';
394
+ const buildEntity = (status)=>{
395
+ const builder = entityType === 'Entry' ? entryBuilder() : assetBuilder();
396
+ return builder.withStatus(status);
397
+ };
398
+ const createRelease = (options)=>{
399
+ return publishingModel === 'entry-based' ? createEntryBasedRelease({
400
+ entityId: options.entityId,
401
+ entityType,
402
+ action: options.action || 'publish'
403
+ }) : createLocaleBasedRelease({
404
+ entityId: options.entityId,
405
+ entityType,
406
+ verb: options.verb || 'add'
286
407
  });
287
- });
288
- it('returns published status when entry is in published state and not in the release', ()=>{
289
- const { result } = renderHook(()=>useReleaseStatus({
290
- ...baseParams,
291
- release: createLocaleBasedRelease({
292
- entityId: entityType === 'Entry' ? 'entry-2' : 'asset-2',
293
- entityType,
294
- verb: 'add'
295
- }),
296
- entity: buildEntity('published')
297
- }));
298
- expect(result.current.releaseStatusMap.get('en-US')).toEqual({
299
- variant: 'positive',
300
- status: 'published',
301
- label: 'Published',
302
- locale: {
303
- code: 'en-US'
304
- }
408
+ };
409
+ const publishAction = publishingModel === 'entry-based' ? 'publish' : 'add';
410
+ const unpublishAction = publishingModel === 'entry-based' ? 'unpublish' : 'remove';
411
+ describe(`${entityType}`, ()=>{
412
+ it(`[${publishAction}] shows "Will publish" when entity transitions draft → published`, ()=>{
413
+ const { result } = renderHook(()=>useReleaseStatus({
414
+ locales,
415
+ previousEntityOnTimeline: buildEntity('draft'),
416
+ release: createRelease({
417
+ entityId: defaultEntityId,
418
+ action: 'publish',
419
+ verb: 'add'
420
+ }),
421
+ entity: buildEntity('published'),
422
+ isReference
423
+ }));
424
+ expectLocaleStatus(result.current, 'en-US', {
425
+ variant: 'positive',
426
+ status: 'willPublish',
427
+ label: 'Will publish',
428
+ locale: {
429
+ code: 'en-US'
430
+ }
431
+ });
305
432
  });
306
- expect(result.current.releaseEntityStatus).toBe('published');
433
+ it(`[${unpublishAction}] shows "Becomes draft" when entity transitions published → draft`, ()=>{
434
+ const { result } = renderHook(()=>useReleaseStatus({
435
+ locales,
436
+ previousEntityOnTimeline: buildEntity('published'),
437
+ release: createRelease({
438
+ entityId: defaultEntityId,
439
+ action: 'unpublish',
440
+ verb: 'remove'
441
+ }),
442
+ entity: buildEntity('draft'),
443
+ isReference
444
+ }));
445
+ expectLocaleStatus(result.current, 'en-US', {
446
+ variant: 'warning',
447
+ status: 'becomesDraft',
448
+ label: 'Becomes draft',
449
+ locale: {
450
+ code: 'en-US'
451
+ }
452
+ });
453
+ });
454
+ it(`[${unpublishAction}] shows "Remains draft" when entity stays draft → draft`, ()=>{
455
+ const { result } = renderHook(()=>useReleaseStatus({
456
+ locales,
457
+ previousEntityOnTimeline: buildEntity('draft'),
458
+ release: createRelease({
459
+ entityId: defaultEntityId,
460
+ action: 'unpublish',
461
+ verb: 'remove'
462
+ }),
463
+ entity: buildEntity('draft'),
464
+ isReference
465
+ }));
466
+ expectLocaleStatus(result.current, 'en-US', {
467
+ variant: 'warning',
468
+ status: 'remainsDraft',
469
+ label: 'Remains draft',
470
+ locale: {
471
+ code: 'en-US'
472
+ }
473
+ });
474
+ });
475
+ if (isReference) {
476
+ it('[not in release] shows "Published" when entity is published (returns actual state)', ()=>{
477
+ const { result } = renderHook(()=>useReleaseStatus({
478
+ locales,
479
+ release: createRelease({
480
+ entityId: differentEntityId,
481
+ action: 'publish',
482
+ verb: 'add'
483
+ }),
484
+ entity: buildEntity('published'),
485
+ isReference
486
+ }));
487
+ expectLocaleStatus(result.current, 'en-US', {
488
+ variant: 'positive',
489
+ status: 'published',
490
+ label: 'Published',
491
+ locale: {
492
+ code: 'en-US'
493
+ }
494
+ });
495
+ expectEntityStatus(result.current, 'published');
496
+ });
497
+ it('[not in release] shows "Not in release" when entity is draft (returns actual state)', ()=>{
498
+ const { result } = renderHook(()=>useReleaseStatus({
499
+ locales,
500
+ release: createRelease({
501
+ entityId: differentEntityId,
502
+ action: 'publish',
503
+ verb: 'add'
504
+ }),
505
+ entity: buildEntity('draft'),
506
+ isReference
507
+ }));
508
+ expectLocaleStatus(result.current, 'en-US', {
509
+ variant: 'secondary',
510
+ status: 'notInRelease',
511
+ label: 'Not in release',
512
+ locale: {
513
+ code: 'en-US'
514
+ }
515
+ });
516
+ });
517
+ } else {
518
+ it('[not in release] returns "notInRelease" even when published (ignores actual state)', ()=>{
519
+ const { result } = renderHook(()=>useReleaseStatus({
520
+ locales,
521
+ release: createRelease({
522
+ entityId: differentEntityId,
523
+ action: 'publish',
524
+ verb: 'add'
525
+ }),
526
+ entity: buildEntity('published'),
527
+ isReference
528
+ }));
529
+ expectLocaleStatus(result.current, 'en-US', {
530
+ variant: 'secondary',
531
+ status: 'notInRelease',
532
+ label: 'Not in release',
533
+ locale: {
534
+ code: 'en-US'
535
+ }
536
+ });
537
+ expectEntityStatus(result.current, 'notInRelease');
538
+ });
539
+ it('[not in release] returns "notInRelease" when draft (consistent regardless of state)', ()=>{
540
+ const { result } = renderHook(()=>useReleaseStatus({
541
+ locales,
542
+ release: createRelease({
543
+ entityId: differentEntityId,
544
+ action: 'publish',
545
+ verb: 'add'
546
+ }),
547
+ entity: buildEntity('draft'),
548
+ isReference
549
+ }));
550
+ expectLocaleStatus(result.current, 'en-US', {
551
+ variant: 'secondary',
552
+ status: 'notInRelease',
553
+ label: 'Not in release',
554
+ locale: {
555
+ code: 'en-US'
556
+ }
557
+ });
558
+ });
559
+ }
560
+ });
561
+ });
562
+ };
563
+ describe('When entity is a reference (isReference: true)', ()=>{
564
+ describe('Entry-based publishing', ()=>{
565
+ testPublishingScenarios({
566
+ isReference: true,
567
+ publishingModel: 'entry-based'
568
+ });
569
+ });
570
+ describe('Locale-based publishing', ()=>{
571
+ testPublishingScenarios({
572
+ isReference: true,
573
+ publishingModel: 'locale-based'
574
+ });
575
+ });
576
+ });
577
+ describe('When entity is NOT a reference (isReference: false)', ()=>{
578
+ describe('Entry-based publishing', ()=>{
579
+ testPublishingScenarios({
580
+ isReference: false,
581
+ publishingModel: 'entry-based'
582
+ });
583
+ });
584
+ describe('Locale-based publishing', ()=>{
585
+ testPublishingScenarios({
586
+ isReference: false,
587
+ publishingModel: 'locale-based'
307
588
  });
308
589
  });
309
590
  });