-
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
- Loading branch information
1 parent
f57b720
commit 7f0ff00
Showing
17 changed files
with
378 additions
and
134 deletions.
There are no files selected for viewing
230 changes: 230 additions & 0 deletions
230
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,230 @@ | ||
import {createReleaseId, getBundleIdFromReleaseId, type ReleaseDocument} from 'sanity' | ||
import {describe, expect, it} from 'vitest' | ||
|
||
import {RELEASE_DOCUMENT_TYPE} from '../../../store/release/constants' | ||
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: 'system-tmp-releases.asap1', | ||
_createdAt: '2024-10-24T00:00:00Z', | ||
metadata: { | ||
releaseType: 'asap', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: 'system-tmp-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: 'system-tmp-releases.future2', | ||
metadata: { | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2024-11-25T00:00:00Z', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: 'system-tmp-releases.future1', | ||
metadata: { | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2024-11-23T00:00:00Z', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: 'system-tmp-releases.future4', | ||
state: 'scheduled', | ||
publishAt: '2024-11-31T00:00:00Z', | ||
metadata: { | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2024-10-20T00:00:00Z', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: 'system-tmp-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: 'system-tmp-releases.undecided1', | ||
_createdAt: '2024-10-25T00:00:00Z', | ||
metadata: { | ||
releaseType: 'undecided', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: 'system-tmp-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: 'system-tmp-releases.asap2', | ||
_createdAt: '2024-10-25T00:00:00Z', | ||
metadata: { | ||
releaseType: 'asap', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: 'system-tmp-releases.asap1', | ||
_createdAt: '2024-10-24T00:00:00Z', | ||
metadata: { | ||
releaseType: 'asap', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: 'system-tmp-releases.undecided2', | ||
_createdAt: '2024-10-26T00:00:00Z', | ||
metadata: { | ||
releaseType: 'undecided', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: 'system-tmp-releases.future4', | ||
state: 'scheduled', | ||
publishAt: '2024-11-31T00:00:00Z', | ||
metadata: { | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2024-10-20T00:00:00Z', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: 'system-tmp-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: 'system-tmp-releases.asap2', | ||
_createdAt: '2024-10-25T00:00:00Z', | ||
metadata: { | ||
releaseType: 'asap', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: 'system-tmp-releases.asap1', | ||
_createdAt: '2024-10-24T00:00:00Z', | ||
metadata: { | ||
releaseType: 'asap', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: 'system-tmp-releases.undecided2', | ||
_createdAt: '2024-10-26T00:00:00Z', | ||
metadata: { | ||
releaseType: 'undecided', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: 'system-tmp-releases.future4', | ||
state: 'scheduled', | ||
publishAt: '2024-11-31T00:00:00Z', | ||
metadata: { | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2024-10-20T00:00:00Z', | ||
}, | ||
}), | ||
createReleaseMock({ | ||
_id: 'system-tmp-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,78 @@ | ||
import {getBundleIdFromReleaseId} from 'sanity' | ||
|
||
import {type ReleaseDocument} from '../../store/release/types' | ||
import {DRAFTS_FOLDER} from '../../util/draftUtils' | ||
import {resolveBundlePerspective} from '../../util/resolvePerspective' | ||
|
||
export function sortReleases(releases: ReleaseDocument[]): ReleaseDocument[] { | ||
// The order should always be: | ||
// [undecided (sortByCreatedAt), scheduled(sortBy publishAt || metadata.intendedPublishAt), asap(sortByCreatedAt)] | ||
return releases.toSorted((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.