-
Notifications
You must be signed in to change notification settings - Fork 427
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): move bundlesPerspective to usePerspective hook, sort rele…
…ases (#7682)
- Loading branch information
1 parent
d4db504
commit 81c5fc3
Showing
19 changed files
with
391 additions
and
143 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
232 changes: 232 additions & 0 deletions
232
packages/sanity/src/core/releases/hooks/__tests__/utils.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
import {describe, expect, it} from 'vitest' | ||
|
||
import {RELEASE_DOCUMENT_TYPE} from '../../../store/release/constants' | ||
import {type ReleaseDocument} from '../../../store/release/types' | ||
import {createReleaseId} from '../../util/createReleaseId' | ||
import {getBundleIdFromReleaseId} from '../../util/getBundleIdFromReleaseId' | ||
import {getReleasesPerspective, sortReleases} from '../utils' | ||
|
||
function createReleaseMock( | ||
value: Partial< | ||
Omit<ReleaseDocument, 'metadata'> & { | ||
metadata: Partial<ReleaseDocument['metadata']> | ||
} | ||
>, | ||
): ReleaseDocument { | ||
const id = value._id || createReleaseId() | ||
const name = getBundleIdFromReleaseId(id) | ||
return { | ||
_id: id, | ||
_type: RELEASE_DOCUMENT_TYPE, | ||
_createdAt: new Date().toISOString(), | ||
_updatedAt: new Date().toISOString(), | ||
name: getBundleIdFromReleaseId(id), | ||
createdBy: 'snty1', | ||
state: 'active', | ||
...value, | ||
metadata: { | ||
title: `Release ${name}`, | ||
releaseType: 'asap', | ||
...value.metadata, | ||
}, | ||
} | ||
} | ||
describe('sortReleases()', () => { | ||
it('should return the asap releases ordered by createdAt', () => { | ||
const releases: ReleaseDocument[] = [ | ||
createReleaseMock({ | ||
_id: '_.releases.asap1', | ||
_createdAt: '2024-10-24T00:00:00Z', | ||
metadata: { | ||
releaseType: 'asap', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: '_.releases.asap2', | ||
_createdAt: '2024-10-25T00:00:00Z', | ||
metadata: { | ||
releaseType: 'asap', | ||
}, | ||
}), | ||
] | ||
const sorted = sortReleases(releases) | ||
const expectedOrder = ['asap2', 'asap1'] | ||
expectedOrder.forEach((expectedName, idx) => { | ||
expect(sorted[idx].name).toBe(expectedName) | ||
}) | ||
}) | ||
it('should return the scheduled releases ordered by intendedPublishAt or publishAt', () => { | ||
const releases: ReleaseDocument[] = [ | ||
createReleaseMock({ | ||
_id: '_.releases.future2', | ||
metadata: { | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2024-11-25T00:00:00Z', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: '_.releases.future1', | ||
metadata: { | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2024-11-23T00:00:00Z', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: '_.releases.future4', | ||
state: 'scheduled', | ||
publishAt: '2024-11-31T00:00:00Z', | ||
metadata: { | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2024-10-20T00:00:00Z', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: '_.releases.future3', | ||
state: 'scheduled', | ||
publishAt: '2024-11-26T00:00:00Z', | ||
metadata: { | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2024-11-22T00:00:00Z', | ||
}, | ||
}), | ||
] | ||
const sorted = sortReleases(releases) | ||
const expectedOrder = ['future4', 'future3', 'future2', 'future1'] | ||
expectedOrder.forEach((expectedName, idx) => { | ||
expect(sorted[idx].name).toBe(expectedName) | ||
}) | ||
}) | ||
it('should return the undecided releases ordered by createdAt', () => { | ||
const releases: ReleaseDocument[] = [ | ||
createReleaseMock({ | ||
_id: '_.releases.undecided1', | ||
_createdAt: '2024-10-25T00:00:00Z', | ||
metadata: { | ||
releaseType: 'undecided', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: '_.releases.undecided2', | ||
_createdAt: '2024-10-26T00:00:00Z', | ||
metadata: { | ||
releaseType: 'undecided', | ||
}, | ||
}), | ||
] | ||
const sorted = sortReleases(releases) | ||
const expectedOrder = ['undecided2', 'undecided1'] | ||
expectedOrder.forEach((expectedName, idx) => { | ||
expect(sorted[idx].name).toBe(expectedName) | ||
}) | ||
}) | ||
it("should gracefully combine all release types, and sort them by 'undecided', 'scheduled', 'asap'", () => { | ||
const releases = [ | ||
createReleaseMock({ | ||
_id: '_.releases.asap2', | ||
_createdAt: '2024-10-25T00:00:00Z', | ||
metadata: { | ||
releaseType: 'asap', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: '_.releases.asap1', | ||
_createdAt: '2024-10-24T00:00:00Z', | ||
metadata: { | ||
releaseType: 'asap', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: '_.releases.undecided2', | ||
_createdAt: '2024-10-26T00:00:00Z', | ||
metadata: { | ||
releaseType: 'undecided', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: '_.releases.future4', | ||
state: 'scheduled', | ||
publishAt: '2024-11-31T00:00:00Z', | ||
metadata: { | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2024-10-20T00:00:00Z', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: '_.releases.future1', | ||
metadata: { | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2024-11-23T00:00:00Z', | ||
}, | ||
}), | ||
] | ||
const sorted = sortReleases(releases) | ||
const expectedOrder = ['undecided2', 'future4', 'future1', 'asap2', 'asap1'] | ||
expectedOrder.forEach((expectedName, idx) => { | ||
expect(sorted[idx].name).toBe(expectedName) | ||
}) | ||
}) | ||
}) | ||
|
||
describe('getReleasesPerspective()', () => { | ||
const releases = [ | ||
createReleaseMock({ | ||
_id: '_.releases.asap2', | ||
_createdAt: '2024-10-25T00:00:00Z', | ||
metadata: { | ||
releaseType: 'asap', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: '_.releases.asap1', | ||
_createdAt: '2024-10-24T00:00:00Z', | ||
metadata: { | ||
releaseType: 'asap', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: '_.releases.undecided2', | ||
_createdAt: '2024-10-26T00:00:00Z', | ||
metadata: { | ||
releaseType: 'undecided', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: '_.releases.future4', | ||
state: 'scheduled', | ||
publishAt: '2024-11-31T00:00:00Z', | ||
metadata: { | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2024-10-20T00:00:00Z', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: '_.releases.future1', | ||
metadata: { | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2024-11-23T00:00:00Z', | ||
}, | ||
}), | ||
] | ||
// Define your test cases with the expected outcomes | ||
const testCases = [ | ||
{perspective: 'bundle.asap1', excluded: [], expected: ['asap1', 'drafts']}, | ||
{perspective: 'bundle.asap2', excluded: [], expected: ['asap2', 'asap1', 'drafts']}, | ||
{ | ||
perspective: 'bundle.undecided2', | ||
excluded: [], | ||
expected: ['undecided2', 'future4', 'future1', 'asap2', 'asap1', 'drafts'], | ||
}, | ||
{ | ||
perspective: 'bundle.undecided2', | ||
excluded: ['future1', 'drafts'], | ||
expected: ['undecided2', 'future4', 'asap2', 'asap1'], | ||
}, | ||
] | ||
it.each(testCases)( | ||
'should return the correct release stack for %s', | ||
({perspective, excluded, expected}) => { | ||
const result = getReleasesPerspective({releases, perspective, excluded}) | ||
expect(result).toEqual(expected) | ||
}, | ||
) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import {type ReleaseDocument} from '../../store/release/types' | ||
import {DRAFTS_FOLDER} from '../../util/draftUtils' | ||
import {resolveBundlePerspective} from '../../util/resolvePerspective' | ||
import {getBundleIdFromReleaseId} from '../util/getBundleIdFromReleaseId' | ||
|
||
export function sortReleases(releases: ReleaseDocument[] = []): ReleaseDocument[] { | ||
// The order should always be: | ||
// [undecided (sortByCreatedAt), scheduled(sortBy publishAt || metadata.intendedPublishAt), asap(sortByCreatedAt)] | ||
return [...releases].sort((a, b) => { | ||
// undecided are always first, then by createdAt descending | ||
if (a.metadata.releaseType === 'undecided' && b.metadata.releaseType !== 'undecided') { | ||
return -1 | ||
} | ||
if (a.metadata.releaseType !== 'undecided' && b.metadata.releaseType === 'undecided') { | ||
return 1 | ||
} | ||
if (a.metadata.releaseType === 'undecided' && b.metadata.releaseType === 'undecided') { | ||
// Sort by createdAt | ||
return new Date(b._createdAt).getTime() - new Date(a._createdAt).getTime() | ||
} | ||
|
||
// Scheduled are always at the middle, then by publishAt descending | ||
if (a.metadata.releaseType === 'scheduled' && b.metadata.releaseType === 'scheduled') { | ||
const aPublishAt = a.publishAt || a.metadata.intendedPublishAt | ||
if (!aPublishAt) { | ||
return 1 | ||
} | ||
const bPublishAt = b.publishAt || b.metadata.intendedPublishAt | ||
if (!bPublishAt) { | ||
return -1 | ||
} | ||
return new Date(bPublishAt).getTime() - new Date(aPublishAt).getTime() | ||
} | ||
|
||
// ASAP are always last, then by createdAt descending | ||
if (a.metadata.releaseType === 'asap' && b.metadata.releaseType !== 'asap') { | ||
return 1 | ||
} | ||
if (a.metadata.releaseType !== 'asap' && b.metadata.releaseType === 'asap') { | ||
return -1 | ||
} | ||
if (a.metadata.releaseType === 'asap' && b.metadata.releaseType === 'asap') { | ||
// Sort by createdAt | ||
return new Date(b._createdAt).getTime() - new Date(a._createdAt).getTime() | ||
} | ||
|
||
return 0 | ||
}) | ||
} | ||
|
||
export function getReleasesPerspective({ | ||
releases, | ||
perspective, | ||
excluded, | ||
}: { | ||
releases: ReleaseDocument[] | ||
perspective: string | undefined // Includes the bundle.<releaseName> or 'published' | ||
excluded: string[] | ||
}): string[] { | ||
if (!perspective?.startsWith('bundle.')) { | ||
return [] | ||
} | ||
const perspectiveId = resolveBundlePerspective(perspective) | ||
if (!perspectiveId) { | ||
return [] | ||
} | ||
|
||
const sorted = sortReleases(releases).map((release) => getBundleIdFromReleaseId(release._id)) | ||
const selectedIndex = sorted.indexOf(perspectiveId) | ||
if (selectedIndex === -1) { | ||
return [] | ||
} | ||
return sorted | ||
.slice(selectedIndex) | ||
.concat(DRAFTS_FOLDER) | ||
.filter((name) => !excluded.includes(name)) | ||
} |
Oops, something went wrong.