Repository: sanity-io/sanity
Pull request #8979: test(e2e): add unarchive / unpublish tests to releases
Author: login: RitaDias
Created: 2025-03-19T09:56:36Z | Merged: 2025-03-21T07:48:34Z

### Description

Add e2e tests for archiving and publishing releases + reverting. Making sure that the types are as expected

ARCHIVED:
- [x] Scheduled: Archive -> Unarchive -> Scheduled release
- [x] Undecided: Schedule -> UnSchedule -> Archive -> Unarchive -> Scheduled release
- [x] Undecided: Archive -> Unarchive -> Undecided release
- [x] ASAP: Schedule -> Unscheduled -> Archive -> Unarchive -> Scheduled release
- [x] ASAP: Archive -> Unarchive -> ASAP release

PUBLISHED:
- [x] ASAP: Publish -> Revert -> ASAP release
- [x] ASAP: Schedule -> Wait -> Revert -> ASAP release
- [x] ASAP: Schedule -> Unsechdule -> Publish -> Revert -> ASAP release
- [x] Scheduled: Schedule -> Wait -> Revert -> ASAP release
- [x] Scheduled: Publish -> Revert -> ASAP release
- [x] Undecided: Schedule -> Wait -> Revert -> ASAP release
- [x] Undecided: Publish -> Revert -> ASAP release


### What to review

This is all tests with utils for e2e test for releases

### Testing

This is all tests 🥲 

### Notes for release

N/A


Diff:
diff --git a/packages/sanity/src/core/releases/tool/components/releaseCTAButtons/ReleaseRevertButton/ReleaseRevertButton.tsx b/packages/sanity/src/core/releases/tool/components/releaseCTAButtons/ReleaseRevertButton/ReleaseRevertButton.tsx
index eb233737e04f..c83fd8889348 100644
--- a/packages/sanity/src/core/releases/tool/components/releaseCTAButtons/ReleaseRevertButton/ReleaseRevertButton.tsx
+++ b/packages/sanity/src/core/releases/tool/components/releaseCTAButtons/ReleaseRevertButton/ReleaseRevertButton.tsx
@@ -103,6 +103,7 @@ const ConfirmReleaseDialog = ({
                         marginBottom: '0.5rem',
                         display: 'flex',
                       }}
+                      data-testid="revert-stage-success-link"
                     >
                       {t('toast.revert-stage.success-link')}
                     </Text>
@@ -281,6 +282,7 @@ export const ReleaseRevertButton = ({
          * @todo Permissions to create each schema type within the release (not implemented)
          */
         disabled={isPendingGuardResponse || !hasCreatePermission || disabled || mode === 'disabled'}
+        data-testid="revert-button"
       />
       {revertReleaseStatus !== 'idle' && (
         <ConfirmReleaseDialog
diff --git a/test/e2e/tests/releases/displayDocument/DisplayedDocument.spec.ts b/test/e2e/tests/releases/displayDocument/DisplayedDocument.spec.ts
index 4d1237ff8815..c2c442f8ab5f 100644
--- a/test/e2e/tests/releases/displayDocument/DisplayedDocument.spec.ts
+++ b/test/e2e/tests/releases/displayDocument/DisplayedDocument.spec.ts
@@ -19,11 +19,9 @@ import {
   createRelease,
   discardVersion,
   getRandomReleaseId,
+  skipIfBrowser,
 } from '../utils/methods'
 
-// skip firefox due to flakyness
-const SKIP_BROWSERS = ['firefox']
-
 // for before all to work with single worker and run only once per tests and describe
 // this is to avoid issues with multiple releases being created per test
 test.describe.configure({mode: 'serial'})
@@ -44,10 +42,6 @@ test.describe('displayedDocument', () => {
   let asapReleaseId: string
   let undecidedReleaseId: string
 
-  const skipIfBrowser = (browserName: string) => {
-    test.skip(SKIP_BROWSERS.includes(browserName), `Skip ${browserName} due to flakiness`)
-  }
-
   test.beforeAll(async ({sanityClient, _testContext, browserName}) => {
     skipIfBrowser(browserName)
     const dataset = sanityClient.config().dataset
diff --git a/test/e2e/tests/releases/revert/revertASAP.spec.ts b/test/e2e/tests/releases/revert/revertASAP.spec.ts
new file mode 100644
index 000000000000..4788df791636
--- /dev/null
+++ b/test/e2e/tests/releases/revert/revertASAP.spec.ts
@@ -0,0 +1,153 @@
+import {expect} from '@playwright/test'
+import {test} from '@sanity/test'
+
+import {speciesDocumentNameASAP} from '../utils/__fixtures__/documents'
+import {partialASAPReleaseMetadata} from '../utils/__fixtures__/releases'
+import {
+  archiveAndDeleteRelease,
+  createDocument,
+  createRelease,
+  deleteRelease,
+  getRandomReleaseId,
+  skipIfBrowser,
+} from '../utils/methods'
+import {
+  publishAndConfirmRelease,
+  publishAndConfirmReleaseMenu,
+  revertAndConfirmRelease,
+  scheduleAndConfirmReleaseMenu,
+  unscheduleAndConfirmRelease,
+} from '../utils/release-detail-ui-methods'
+
+test.describe('Revert ASAP', () => {
+  const asapReleaseIdTestOne: string = getRandomReleaseId()
+
+  test.beforeEach(async ({sanityClient, browserName, page, _testContext}) => {
+    skipIfBrowser(browserName)
+    test.slow()
+
+    const dataset = sanityClient.config().dataset
+
+    await createRelease({
+      sanityClient,
+      dataset,
+      releaseId: asapReleaseIdTestOne,
+      metadata: partialASAPReleaseMetadata,
+    })
+
+    const versionDocumentId = _testContext.getUniqueDocumentId()
+
+    await createDocument(sanityClient, {
+      ...speciesDocumentNameASAP,
+      name: 'published',
+      _id: `${versionDocumentId}`,
+    })
+
+    await createDocument(sanityClient, {
+      ...speciesDocumentNameASAP,
+      _id: `versions.${asapReleaseIdTestOne}.${versionDocumentId}`,
+    })
+
+    await page.goto(`test/releases/${asapReleaseIdTestOne}`)
+  })
+
+  test.afterEach(async ({sanityClient, browserName, page}) => {
+    skipIfBrowser(browserName)
+    const dataset = sanityClient.config().dataset
+    // used for the new releases that have been created from the revert
+    const currentPageReleaseId = page.url().split('/').pop()
+
+    await Promise.all(
+      [
+        deleteRelease({sanityClient, dataset, releaseId: asapReleaseIdTestOne}),
+        currentPageReleaseId
+          ? archiveAndDeleteRelease({sanityClient, dataset, releaseId: currentPageReleaseId})
+          : null,
+      ].filter(Boolean),
+    )
+  })
+
+  // Publish -> Revert -> ASAP release
+  test('publish ASAP release, when reverted, should be ASAP release', async ({page}) => {
+    await publishAndConfirmRelease({page})
+
+    // retention policy card
+    await expect(page.getByTestId('retention-policy-card')).toBeVisible()
+
+    await revertAndConfirmRelease({page})
+
+    await expect(page.getByTestId('revert-stage-success-link')).toBeVisible()
+    await page.getByTestId('revert-stage-success-link').click()
+
+    await expect(page.getByTestId('retention-policy-card')).not.toBeVisible()
+
+    await expect(
+      page.getByRole('button', {
+        name: `Reverting "${partialASAPReleaseMetadata.title}"`,
+        exact: true,
+      }),
+    ).toBeVisible()
+    await expect(page.getByTestId('release-type-picker')).toHaveText('ASAP')
+  })
+
+  // Schedule -> Wait -> Revert -> ASAP release
+  test('schedule ASAP release, wait for it to be published. When reverted, should be ASAP release', async ({
+    page,
+  }) => {
+    // schedule the release for (1 minute and 20 seconds) from now
+    // this seems to be the lowest interval that works without hanging / flaking
+    await scheduleAndConfirmReleaseMenu({
+      page,
+      date: new Date(new Date().setSeconds(new Date().getSeconds() + 80)),
+    })
+
+    // retention policy card
+    await expect(page.getByTestId('retention-policy-card')).toBeVisible()
+
+    await revertAndConfirmRelease({page})
+
+    await expect(page.getByTestId('revert-stage-success-link')).toBeVisible()
+    await page.getByTestId('revert-stage-success-link').click()
+
+    await expect(page.getByTestId('retention-policy-card')).not.toBeVisible()
+
+    await expect(
+      page.getByRole('button', {
+        name: `Reverting "${partialASAPReleaseMetadata.title}"`,
+        exact: true,
+      }),
+    ).toBeVisible()
+    await expect(page.getByTestId('release-type-picker')).toHaveText('ASAP')
+  })
+
+  // Schedule -> Unshedule -> Publish -> Revert -> ASAP release
+  test('schedule ASAP release, unschedule, publish. When reverted, should be ASAP release', async ({
+    page,
+  }) => {
+    await scheduleAndConfirmReleaseMenu({
+      page,
+      date: new Date(new Date().setMinutes(new Date().getMinutes() + 20)),
+    })
+
+    await unscheduleAndConfirmRelease({page})
+
+    await publishAndConfirmReleaseMenu({page})
+
+    // retention policy card
+    await expect(page.getByTestId('retention-policy-card')).toBeVisible()
+
+    await revertAndConfirmRelease({page})
+
+    // retention policy card
+    await expect(page.getByTestId('revert-stage-success-link')).toBeVisible()
+    await page.getByTestId('revert-stage-success-link').click()
+
+    await expect(
+      page.getByRole('button', {
+        name: `Reverting "${partialASAPReleaseMetadata.title}"`,
+        exact: true,
+      }),
+    ).toBeVisible()
+    await expect(page.getByTestId('release-type-picker')).toHaveText('ASAP')
+  })
+})
diff --git a/test/e2e/tests/releases/revert/revertSchedule.spec.ts b/test/e2e/tests/releases/revert/revertSchedule.spec.ts
new file mode 100644
index 000000000000..02f1a6fcb98d
--- /dev/null
+++ b/test/e2e/tests/releases/revert/revertSchedule.spec.ts
@@ -0,0 +1,126 @@
+import {expect} from '@playwright/test'
+import {test} from '@sanity/test'
+
+import {speciesDocumentNameASAP} from '../utils/__fixtures__/documents'
+import {partialScheduledReleaseMetadata} from '../utils/__fixtures__/releases'
+import {
+  archiveAndDeleteRelease,
+  createDocument,
+  createRelease,
+  deleteRelease,
+  getRandomReleaseId,
+  skipIfBrowser,
+} from '../utils/methods'
+import {
+  publishAndConfirmReleaseMenu,
+  revertAndConfirmRelease,
+  scheduleAndConfirmRelease,
+} from '../utils/release-detail-ui-methods'
+
+test.describe('Revert Scheduled', () => {
+  const scheduledReleaseIdTestOne: string = getRandomReleaseId()
+
+  test.beforeEach(async ({sanityClient, browserName, page, _testContext}) => {
+    skipIfBrowser(browserName)
+    test.slow()
+    const dataset = sanityClient.config().dataset
+
+    await createRelease({
+      sanityClient,
+      dataset,
+      releaseId: scheduledReleaseIdTestOne,
+      metadata: {
+        ...partialScheduledReleaseMetadata,
+        // schedule the release for (1 minute and 10 seconds) from now
+        // this seems to be the lowest interval that works without hanging / flaking
+        intendedPublishAt: new Date(
+          new Date().setSeconds(new Date().getSeconds() + 70),
+        ).toISOString(),
+      },
+    })
+
+    const versionDocumentId = _testContext.getUniqueDocumentId()
+
+    await createDocument(sanityClient, {
+      ...speciesDocumentNameASAP,
+      name: 'published',
+      _id: `${versionDocumentId}`,
+    })
+
+    await createDocument(sanityClient, {
+      ...speciesDocumentNameASAP,
+      _id: `versions.${scheduledReleaseIdTestOne}.${versionDocumentId}`,
+    })
+
+    await page.goto(`test/releases/${scheduledReleaseIdTestOne}`)
+  })
+
+  test.afterEach(async ({sanityClient, browserName, page}) => {
+    skipIfBrowser(browserName)
+    const dataset = sanityClient.config().dataset
+    // used for the new releases that have been created from the revert
+    const currentPageReleaseId = page.url().split('/').pop()
+
+    await Promise.all(
+      [
+        deleteRelease({sanityClient, dataset, releaseId: scheduledReleaseIdTestOne}),
+        currentPageReleaseId
+          ? archiveAndDeleteRelease({sanityClient, dataset, releaseId: currentPageReleaseId})
+          : null,
+      ].filter(Boolean),
+    )
+  })
+
+  // Schedule -> Wait -> Revert -> ASAP release
+  test('schedule Scheduled release, wait for it to be published. When reverted, should be ASAP release', async ({
+    page,
+  }) => {
+    // schedule the release for (1 minute and 10 seconds) from now
+    // this seems to be the lowest interval that works without hanging / flaking
+    await scheduleAndConfirmRelease({
+      page,
+      date: new Date(new Date().setSeconds(new Date().getSeconds() + 70)),
+    })
+
+    // retention policy card
+    await expect(page.getByTestId('retention-policy-card')).toBeVisible()
+
+    await revertAndConfirmRelease({page})
+
+    await expect(page.getByTestId('revert-stage-success-link')).toBeVisible()
+    await page.getByTestId('revert-stage-success-link').click()
+
+    await expect(page.getByTestId('retention-policy-card')).not.toBeVisible()
+
+    await expect(
+      page.getByRole('button', {
+        name: `Reverting "${partialScheduledReleaseMetadata.title}"`,
+        exact: true,
+      }),
+    ).toBeVisible()
+    await expect(page.getByTestId('release-type-picker')).toHaveText('ASAP')
+  })
+
+  // Publish -> Revert -> ASAP release
+  test('publish Scheduled release, when reverted, should be ASAP release', async ({page}) => {
+    await publishAndConfirmReleaseMenu({page})
+
+    // retention policy card
+    await expect(page.getByTestId('retention-policy-card')).toBeVisible()
+
+    await revertAndConfirmRelease({page})
+
+    await expect(page.getByTestId('revert-stage-success-link')).toBeVisible()
+    await page.getByTestId('revert-stage-success-link').click()
+
+    await expect(page.getByTestId('retention-policy-card')).not.toBeVisible()
+
+    await expect(
+      page.getByRole('button', {
+        name: `Reverting "${partialScheduledReleaseMetadata.title}"`,
+        exact: true,
+      }),
+    ).toBeVisible()
+    await expect(page.getByTestId('release-type-picker')).toHaveText('ASAP')
+  })
+})
diff --git a/test/e2e/tests/releases/revert/revertUndecided.spec.ts b/test/e2e/tests/releases/revert/revertUndecided.spec.ts
new file mode 100644
index 000000000000..e20ce3c932b2
--- /dev/null
+++ b/test/e2e/tests/releases/revert/revertUndecided.spec.ts
@@ -0,0 +1,119 @@
+import {expect} from '@playwright/test'
+import {test} from '@sanity/test'
+
+import {speciesDocumentNameASAP} from '../utils/__fixtures__/documents'
+import {partialUndecidedReleaseMetadata} from '../utils/__fixtures__/releases'
+import {
+  archiveAndDeleteRelease,
+  createDocument,
+  createRelease,
+  deleteRelease,
+  getRandomReleaseId,
+  skipIfBrowser,
+} from '../utils/methods'
+import {
+  publishAndConfirmReleaseMenu,
+  revertAndConfirmRelease,
+  scheduleAndConfirmReleaseMenu,
+} from '../utils/release-detail-ui-methods'
+
+test.describe('Revert Undecided', () => {
+  const undecidedReleaseIdTestOne: string = getRandomReleaseId()
+
+  test.beforeEach(async ({sanityClient, browserName, page, _testContext}) => {
+    skipIfBrowser(browserName)
+    test.slow()
+    const dataset = sanityClient.config().dataset
+
+    await createRelease({
+      sanityClient,
+      dataset,
+      releaseId: undecidedReleaseIdTestOne,
+      metadata: partialUndecidedReleaseMetadata,
+    })
+
+    const versionDocumentId = _testContext.getUniqueDocumentId()
+
+    await createDocument(sanityClient, {
+      ...speciesDocumentNameASAP,
+      name: 'published',
+      _id: `${versionDocumentId}`,
+    })
+
+    await createDocument(sanityClient, {
+      ...speciesDocumentNameASAP,
+      _id: `versions.${undecidedReleaseIdTestOne}.${versionDocumentId}`,
+    })
+
+    await page.goto(`test/releases/${undecidedReleaseIdTestOne}`)
+  })
+
+  test.afterEach(async ({sanityClient, browserName, page}) => {
+    skipIfBrowser(browserName)
+    const dataset = sanityClient.config().dataset
+    // used for the new releases that have been created from the revert
+    const currentPageReleaseId = page.url().split('/').pop()
+
+    await Promise.all(
+      [
+        deleteRelease({sanityClient, dataset, releaseId: undecidedReleaseIdTestOne}),
+        currentPageReleaseId
+          ? archiveAndDeleteRelease({sanityClient, dataset, releaseId: currentPageReleaseId})
+          : null,
+      ].filter(Boolean),
+    )
+  })
+
+  // Schedule -> Wait -> Revert -> ASAP release
+  test('schedule Undecided release, wait for it to be published. When reverted, should be ASAP release', async ({
+    page,
+  }) => {
+    // schedule the release for (1 minute and 10 seconds) from now
+    // this seems to be the lowest interval that works without hanging / flaking
+    await scheduleAndConfirmReleaseMenu({
+      page,
+      date: new Date(new Date().setSeconds(new Date().getSeconds() + 70)),
+    })
+
+    // retention policy card
+    await expect(page.getByTestId('retention-policy-card')).toBeVisible()
+
+    await revertAndConfirmRelease({page})
+
+    await expect(page.getByTestId('revert-stage-success-link')).toBeVisible()
+    await page.getByTestId('revert-stage-success-link').click()
+
+    await expect(page.getByTestId('retention-policy-card')).not.toBeVisible()
+
+    await expect(
+      page.getByRole('button', {
+        name: `Reverting "${partialUndecidedReleaseMetadata.title}"`,
+        exact: true,
+      }),
+    ).toBeVisible()
+    await expect(page.getByTestId('release-type-picker')).toHaveText('ASAP')
+  })
+
+  // Publish -> Revert -> ASAP release
+  test('publish Undecided release, when reverted, should be ASAP release', async ({page}) => {
+    await publishAndConfirmReleaseMenu({page})
+
+    // retention policy card
+    await expect(page.getByTestId('retention-policy-card')).toBeVisible()
+
+    await revertAndConfirmRelease({page})
+
+    await expect(page.getByTestId('revert-stage-success-link')).toBeVisible()
+    await page.getByTestId('revert-stage-success-link').click()
+
+    await expect(page.getByTestId('retention-policy-card')).not.toBeVisible()
+
+    await expect(
+      page.getByRole('button', {
+        name: `Reverting "${partialUndecidedReleaseMetadata.title}"`,
+        exact: true,
+      }),
+    ).toBeVisible()
+    await expect(page.getByTestId('release-type-picker')).toHaveText('ASAP')
+  })
+})
diff --git a/test/e2e/tests/releases/unarchive/unarchiveASAP.spec.ts b/test/e2e/tests/releases/unarchive/unarchiveASAP.spec.ts
new file mode 100644
index 000000000000..08c714a59451
--- /dev/null
+++ b/test/e2e/tests/releases/unarchive/unarchiveASAP.spec.ts
@@ -0,0 +1,92 @@
+import {expect} from '@playwright/test'
+import {test} from '@sanity/test'
+
+import {speciesDocumentNameASAP} from '../utils/__fixtures__/documents'
+import {partialASAPReleaseMetadata} from '../utils/__fixtures__/releases'
+import {
+  archiveAndDeleteRelease,
+  archiveRelease,
+  createDocument,
+  createRelease,
+  getRandomReleaseId,
+  skipIfBrowser,
+  unarchiveRelease,
+} from '../utils/methods'
+import {
+  scheduleAndConfirmReleaseMenu,
+  unscheduleAndConfirmRelease,
+} from '../utils/release-detail-ui-methods'
+
+test.describe('Unarchive ASAP', () => {
+  const asapReleaseIdTestOne: string = getRandomReleaseId()
+
+  test.beforeEach(async ({sanityClient, browserName, page, _testContext}) => {
+    skipIfBrowser(browserName)
+    test.slow()
+    const dataset = sanityClient.config().dataset
+
+    await createRelease({
+      sanityClient,
+      dataset,
+      releaseId: asapReleaseIdTestOne,
+      metadata: partialASAPReleaseMetadata,
+    })
+
+    const versionDocumentId = _testContext.getUniqueDocumentId()
+
+    await createDocument(sanityClient, {
+      ...speciesDocumentNameASAP,
+      _id: `versions.${asapReleaseIdTestOne}.${versionDocumentId}`,
+    })
+
+    await page.goto(`test/releases/${asapReleaseIdTestOne}`)
+  })
+
+  test.afterEach(async ({sanityClient, browserName}) => {
+    skipIfBrowser(browserName)
+    const dataset = sanityClient.config().dataset
+
+    await Promise.all([
+      archiveAndDeleteRelease({sanityClient, dataset, releaseId: asapReleaseIdTestOne}),
+    ])
+  })
+
+  // Archive -> Unarchive -> ASAP release
+  test('Initially ASAP release type should be ASAP type', async ({page, sanityClient}) => {
+    const dataset = sanityClient.config().dataset
+
+    await archiveRelease({sanityClient, dataset, releaseId: asapReleaseIdTestOne})
+    await expect(page.getByTestId('retention-policy-card')).toBeVisible()
+
+    await unarchiveRelease({sanityClient, dataset, releaseId: asapReleaseIdTestOne})
+    await expect(page.getByTestId('retention-policy-card')).not.toBeVisible()
+
+    await expect(page.getByTestId('release-type-picker')).toHaveText('ASAP')
+  })
+
+  // Once a release of any type of scheduled, the type should be changed to that, so when it's unarchived, the type should be scheduled
+  // ASAP: Schedule -> Unscheduled -> Archive -> Unarchive -> Scheduled release
+  test('Initially ASAP when set to a scheduled time, then archived and unarchived should be Scheduled release type', async ({
+    page,
+    sanityClient,
+  }) => {
+    const dataset = sanityClient.config().dataset
+
+    await scheduleAndConfirmReleaseMenu({
+      page,
+      date: new Date(new Date().setMinutes(new Date().getMinutes() + 20)),
+    })
+
+    await unscheduleAndConfirmRelease({page})
+
+    await archiveRelease({sanityClient, dataset, releaseId: asapReleaseIdTestOne})
+    await expect(page.getByTestId('retention-policy-card')).toBeVisible()
+
+    await unarchiveRelease({sanityClient, dataset, releaseId: asapReleaseIdTestOne})
+
+    // has unarchived
+    await expect(page.getByTestId('retention-policy-card')).not.toBeVisible()
+
+    await expect(page.getByTestId('release-type-picker')).not.toHaveText('ASAP')
+  })
+})
diff --git a/test/e2e/tests/releases/unarchive/unarchiveScheduled.spec.ts b/test/e2e/tests/releases/unarchive/unarchiveScheduled.spec.ts
new file mode 100644
index 000000000000..960a092878a0
--- /dev/null
+++ b/test/e2e/tests/releases/unarchive/unarchiveScheduled.spec.ts
@@ -0,0 +1,84 @@
+import {expect} from '@playwright/test'
+import {test} from '@sanity/test'
+
+import {speciesDocumentNameASAP} from '../utils/__fixtures__/documents'
+import {partialScheduledReleaseMetadata} from '../utils/__fixtures__/releases'
+import {
+  archiveAndDeleteRelease,
+  archiveRelease,
+  createDocument,
+  createRelease,
+  getRandomReleaseId,
+  skipIfBrowser,
+  unarchiveRelease,
+} from '../utils/methods'
+
+test.describe('Unarchive Scheduled', () => {
+  const scheduledReleaseIdTestOne: string = getRandomReleaseId()
+
+  test.beforeEach(async ({sanityClient, browserName}) => {
+    skipIfBrowser(browserName)
+    const dataset = sanityClient.config().dataset
+
+    await createRelease({
+      sanityClient,
+      dataset,
+      releaseId: scheduledReleaseIdTestOne,
+      metadata: partialScheduledReleaseMetadata,
+    })
+  })
+
+  test.afterEach(async ({sanityClient, browserName}) => {
+    skipIfBrowser(browserName)
+    const dataset = sanityClient.config().dataset
+
+    await Promise.all([
+      archiveAndDeleteRelease({sanityClient, dataset, releaseId: scheduledReleaseIdTestOne}),
+    ])
+  })
+
+  // Archive -> Unarchive -> Scheduled release
+  test('Initially scheduled release should be scheduled release type when unarchived', async ({
+    page,
+    sanityClient,
+    _testContext,
+    browserName,
+  }) => {
+    skipIfBrowser(browserName)
+    const dataset = sanityClient.config().dataset
+    const versionDocumentId = _testContext.getUniqueDocumentId()
+
+    await createDocument(sanityClient, {
+      ...speciesDocumentNameASAP,
+      _id: `versions.${scheduledReleaseIdTestOne}.${versionDocumentId}`,
+    })
+
+    await page.goto(`test/releases/${scheduledReleaseIdTestOne}`)
+
+    await archiveRelease({sanityClient, dataset, releaseId: scheduledReleaseIdTestOne})
+    await expect(page.getByTestId('retention-policy-card')).toBeVisible()
+
+    await unarchiveRelease({sanityClient, dataset, releaseId: scheduledReleaseIdTestOne})
+    await expect(page.getByTestId('retention-policy-card')).not.toBeVisible()
+
+    const formattedScheduledReleaseDate = new Date(
+      partialScheduledReleaseMetadata.intendedPublishAt,
+    ).toLocaleString('en-US', {
+      year: 'numeric',
+      month: 'short',
+      day: 'numeric',
+    }) // example Mar 20, 2025
+    const formattedScheduledReleaseTime = new Date(
+      partialScheduledReleaseMetadata.intendedPublishAt,
+    ).toLocaleString('en-US', {
+      hour: 'numeric',
+      minute: '2-digit',
+      second: '2-digit',
+      hour12: true,
+    }) // example 10:00:00 AM or 1:00:00 PM
+
+    await expect(page.getByTestId('release-type-picker')).toHaveText(
+      `${formattedScheduledReleaseDate}, ${formattedScheduledReleaseTime}`,
+    )
+  })
+})
diff --git a/test/e2e/tests/releases/unarchive/unarchiveUndecided.spec.ts b/test/e2e/tests/releases/unarchive/unarchiveUndecided.spec.ts
new file mode 100644
index 000000000000..5dcff3321ccf
--- /dev/null
+++ b/test/e2e/tests/releases/unarchive/unarchiveUndecided.spec.ts
@@ -0,0 +1,91 @@
+import {expect} from '@playwright/test'
+import {test} from '@sanity/test'
+
+import {speciesDocumentNameASAP} from '../utils/__fixtures__/documents'
+import {partialUndecidedReleaseMetadata} from '../utils/__fixtures__/releases'
+import {
+  archiveAndDeleteRelease,
+  archiveRelease,
+  createDocument,
+  createRelease,
+  getRandomReleaseId,
+  skipIfBrowser,
+  unarchiveRelease,
+} from '../utils/methods'
+import {
+  scheduleAndConfirmReleaseMenu,
+  unscheduleAndConfirmRelease,
+} from '../utils/release-detail-ui-methods'
+
+test.describe('Unarchive Undecided', () => {
+  const undecidedReleaseIdTestOne: string = getRandomReleaseId()
+
+  test.beforeEach(async ({sanityClient, browserName, page, _testContext}) => {
+    skipIfBrowser(browserName)
+    test.slow()
+    const dataset = sanityClient.config().dataset
+
+    await createRelease({
+      sanityClient,
+      dataset,
+      releaseId: undecidedReleaseIdTestOne,
+      metadata: partialUndecidedReleaseMetadata,
+    })
+
+    const versionDocumentId = _testContext.getUniqueDocumentId()
+
+    await createDocument(sanityClient, {
+      ...speciesDocumentNameASAP,
+      _id: `versions.${undecidedReleaseIdTestOne}.${versionDocumentId}`,
+    })
+
+    await page.goto(`test/releases/${undecidedReleaseIdTestOne}`)
+  })
+
+  test.afterEach(async ({sanityClient, browserName}) => {
+    skipIfBrowser(browserName)
+    const dataset = sanityClient.config().dataset
+
+    await Promise.all([
+      archiveAndDeleteRelease({sanityClient, dataset, releaseId: undecidedReleaseIdTestOne}),
+    ])
+  })
+
+  // Archive -> Unarchive -> Undecided release
+  test('Initially Undecided release type should be ASAP type', async ({page, sanityClient}) => {
+    const dataset = sanityClient.config().dataset
+    await archiveRelease({sanityClient, dataset, releaseId: undecidedReleaseIdTestOne})
+    await expect(page.getByTestId('retention-policy-card')).toBeVisible()
+
+    await unarchiveRelease({sanityClient, dataset, releaseId: undecidedReleaseIdTestOne})
+    await expect(page.getByTestId('retention-policy-card')).not.toBeVisible()
+
+    await expect(page.getByTestId('release-type-picker')).toHaveText('Undecided')
+  })
+
+  // Once a release of any type of scheduled, the type should be changed to that, so when it's unarchived, the type should be scheduled
+  // Schedule -> Unschedule -> Archive -> Unarchive -> Scheduled release
+  test('Initially Undecided when set to a scheduled time, then archived and unarchived should be Scheduled release type', async ({
+    page,
+    sanityClient,
+  }) => {
+    const dataset = sanityClient.config().dataset
+
+    await scheduleAndConfirmReleaseMenu({
+      page,
+      date: new Date(new Date().setMinutes(new Date().getMinutes() + 20)),
+    })
+
+    await unscheduleAndConfirmRelease({page})
+
+    await archiveRelease({sanityClient, dataset, releaseId: undecidedReleaseIdTestOne})
+    await expect(page.getByTestId('retention-policy-card')).toBeVisible()
+
+    await unarchiveRelease({sanityClient, dataset, releaseId: undecidedReleaseIdTestOne})
+
+    // has unarchived
+    await expect(page.getByTestId('retention-policy-card')).not.toBeVisible()
+
+    await expect(page.getByTestId('release-type-picker')).not.toHaveText('Undecided')
+  })
+})
diff --git a/test/e2e/tests/releases/utils/__fixtures__/releases.ts b/test/e2e/tests/releases/utils/__fixtures__/releases.ts
index 87e2f12e608c..ede3458d38c6 100644
--- a/test/e2e/tests/releases/utils/__fixtures__/releases.ts
+++ b/test/e2e/tests/releases/utils/__fixtures__/releases.ts
@@ -13,5 +13,7 @@ export const partialUndecidedReleaseMetadata = {
 export const partialScheduledReleaseMetadata = {
   title: 'Scheduled Release A',
   description: '',
-  releaseType: 'asap',
+  releaseType: 'scheduled',
+  // tomorrow
+  intendedPublishAt: new Date(new Date().setDate(new Date().getDate() + 1)).toISOString(),
 }
diff --git a/test/e2e/tests/releases/utils/methods.ts b/test/e2e/tests/releases/utils/methods.ts
index 76911db38778..07d6c0922205 100644
--- a/test/e2e/tests/releases/utils/methods.ts
+++ b/test/e2e/tests/releases/utils/methods.ts
@@ -1,9 +1,17 @@
+import test from '@playwright/test'
 import {type SanityClient, type SanityDocument} from '@sanity/client'
 
 export const CLIENT_OPTIONS = {
   apiVersion: 'v2025-02-19',
 }
 
+// skip firefox due to flakyness
+const SKIP_BROWSERS = ['firefox']
+
+export const skipIfBrowser = (browserName: string) => {
+  test.skip(SKIP_BROWSERS.includes(browserName), `Skip ${browserName} due to flakiness`)
+}
+
 export const createRelease = async ({
   sanityClient,
   dataset,
@@ -58,6 +66,34 @@ export const archiveRelease = async ({
   }
 }
 
+export const unarchiveRelease = async ({
+  sanityClient,
+  dataset,
+  releaseId,
+}: {
+  sanityClient: SanityClient
+  dataset: string | undefined
+  releaseId: string
+}) => {
+  try {
+    sanityClient.withConfig(CLIENT_OPTIONS).request({
+      uri: `/data/actions/${dataset}`,
+      method: 'POST',
+      body: {
+        actions: [
+          {
+            actionType: 'sanity.action.release.unarchive',
+            releaseId: releaseId,
+          },
+        ],
+      },
+    })
+  } catch (error) {
+    console.error('Error archiving release:', error)
+    throw error
+  }
+}
+
 export const deleteRelease = ({
   sanityClient,
   dataset,
diff --git a/test/e2e/tests/releases/utils/release-detail-ui-methods.ts b/test/e2e/tests/releases/utils/release-detail-ui-methods.ts
new file mode 100644
index 000000000000..e752b62e0e16
--- /dev/null
+++ b/test/e2e/tests/releases/utils/release-detail-ui-methods.ts
@@ -0,0 +1,129 @@
+import {expect, type Page} from '@playwright/test'
+
+/** This method follows the archiving and confirming from the release detail page */
+export const archiveAndConfirmRelease = async ({page}: {page: Page}) => {
+  await page.getByTestId('release-menu-button').click()
+  await expect(page.getByTestId('archive-release-menu-item')).toBeVisible()
+  await page.getByTestId('archive-release-menu-item').click()
+  await expect(page.getByTestId('confirm-archive-dialog')).toBeVisible()
+  await page.getByTestId('confirm-button').click()
+  await expect(page.getByTestId('confirm-archive-dialog')).not.toBeVisible()
+
+  await expect(page.getByTestId('retention-policy-card')).toBeVisible()
+}
+/** This method follows the unarchiving and confirming from the release detail page */
+export const unarchiveAndConfirmRelease = async ({page}: {page: Page}) => {
+  await page.getByTestId('release-menu-button').click()
+  await expect(page.getByTestId('unarchive-release-menu-item')).toBeVisible()
+  await page.getByTestId('unarchive-release-menu-item').click()
+  await expect(page.getByTestId('retention-policy-card')).not.toBeVisible()
+}
+
+/** This method follows the scheduling and confirming from the release detail page
+ *  date should be a date example:new Date(new Date().setMinutes(new Date().getMinutes() + 20)) - current date + 20 minutes
+ */
+export const scheduleAndConfirmReleaseMenu = async ({page, date}: {page: Page; date: Date}) => {
+  await expect(await page.getByTestId('release-menu-button')).toBeVisible()
+  await page.getByTestId('release-menu-button').click()
+  await expect(page.getByTestId('schedule-button-menu-item')).not.toBeDisabled()
+
+  await expect(page.getByTestId('schedule-button-menu-item')).toBeVisible()
+  await page.getByTestId('schedule-button-menu-item').click()
+  await expect(page.getByTestId('confirm-schedule-dialog')).toBeVisible()
+
+  await page.getByTestId('select-date-button').click()
+  await expect(page.getByTestId('date-input-dialog')).toBeVisible()
+  await page.getByRole('textbox', {name: 'Select time'}).click()
+
+  // sets release to happen in 20 minutes
+  const timeString = date.toLocaleTimeString('en-US', {
+    hour: '2-digit',
+    minute: '2-digit',
+    hour12: false,
+  })
+  await page.getByRole('textbox', {name: 'Select time'}).fill(timeString)
+  await page.getByTestId('date-input-dialog').click()
+  await page.getByTestId('select-date-button').click()
+  await expect(page.getByTestId('date-input-dialog')).not.toBeVisible()
+
+  await expect(page.getByTestId('confirm-button')).not.toBeDisabled()
+
+  await page.getByTestId('confirm-button').click()
+  await expect(page.getByTestId('confirm-schedule-dialog')).not.toBeVisible()
+  await expect(page.getByTestId('release-type-picker')).toBeDisabled()
+}
+
+/** This method follows the scheduling and confirming from the release detail page
+ *  date should be a date example:new Date(new Date().setMinutes(new Date().getMinutes() + 20)) - current date + 20 minutes
+ */
+export const scheduleAndConfirmRelease = async ({page, date}: {page: Page; date: Date}) => {
+  await expect(page.getByTestId('schedule-button')).not.toBeDisabled()
+  await page.getByTestId('schedule-button').click()
+
+  await expect(page.getByTestId('confirm-schedule-dialog')).toBeVisible()
+
+  await page.getByTestId('select-date-button').click()
+  await expect(page.getByTestId('date-input-dialog')).toBeVisible()
+  await page.getByRole('textbox', {name: 'Select time'}).click()
+
+  // sets release to happen in 20 minutes
+  const timeString = date.toLocaleTimeString('en-US', {
+    hour: '2-digit',
+    minute: '2-digit',
+    hour12: false,
+  })
+  await page.getByRole('textbox', {name: 'Select time'}).fill(timeString)
+  await page.getByTestId('date-input-dialog').click()
+  await page.getByTestId('select-date-button').click()
+  await expect(page.getByTestId('date-input-dialog')).not.toBeVisible()
+
+  await expect(page.getByTestId('confirm-button')).not.toBeDisabled()
+
+  await page.getByTestId('confirm-button').click()
+  await expect(page.getByTestId('confirm-schedule-dialog')).not.toBeVisible()
+  await expect(page.getByTestId('release-type-picker')).toBeDisabled()
+}
+
+/** This method follows the unscheduling and confirming from the release detail page */
+export const unscheduleAndConfirmRelease = async ({page}: {page: Page}) => {
+  await page.getByTestId('schedule-button').click()
+  await expect(page.locator('#confirm-unschedule-dialog')).toBeVisible()
+  await expect(page.getByTestId('confirm-button')).not.toBeDisabled()
+  await page.getByTestId('confirm-button').click()
+  await expect(page.locator('#confirm-unschedule-dialog')).not.toBeVisible()
+  await expect(page.getByTestId('release-type-picker')).not.toBeDisabled()
+}
+
+/** This method follows the publishing and confirming from the release detail page */
+export const publishAndConfirmRelease = async ({page}: {page: Page}) => {
+  await expect(page.getByTestId('publish-all-button')).toBeVisible()
+  await page.getByTestId('publish-all-button').click()
+  await expect(page.getByTestId('confirm-publish-dialog')).toBeVisible()
+  await page.getByTestId('confirm-button').click()
+  await expect(page.getByText('This release is published')).toBeVisible()
+}
+
+/** This method follows the publishing and confirming from the release detail page */
+export const publishAndConfirmReleaseMenu = async ({page}: {page: Page}) => {
+  await expect(await page.getByTestId('release-menu-button')).toBeVisible()
+  await page.getByTestId('release-menu-button').click()
+  await expect(page.getByTestId('publish-all-button-menu-item')).not.toBeDisabled()
+
+  await expect(page.getByTestId('publish-all-button-menu-item')).toBeVisible()
+  await page.getByTestId('publish-all-button-menu-item').click()
+
+  await expect(page.getByTestId('confirm-publish-dialog')).toBeVisible()
+  await page.getByTestId('confirm-button').click()
+  await expect(page.getByText('This release is published')).toBeVisible()
+}
+
+/** This method follows the reverting and confirming from the release detail page */
+export const revertAndConfirmRelease = async ({page}: {page: Page}) => {
+  await expect(await page.getByTestId('revert-button')).toBeVisible()
+  await expect(await page.getByTestId('revert-button')).not.toBeDisabled()
+  await page.getByTestId('revert-button').click()
+  await expect(page.locator('#confirm-revert-dialog')).toBeVisible()
+  await expect(page.getByTestId('confirm-button')).toBeVisible()
+  await page.getByTestId('confirm-button').click()
+  await expect(page.locator('#confirm-revert-dialog')).not.toBeVisible()
+}


Reviews:
user: login: juice49
body: This is great test coverage to land. Thanks so much, Rita! ✨
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T14:57:42Z
user: login: RitaDias
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T07:29:14Z
user: login: RitaDias
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T07:30:13Z
user: login: RitaDias
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T07:48:25Z

Review comments:
diff_hunk: @@ -0,0 +1,153 @@
+import {expect} from '@playwright/test'
+import {test} from '@sanity/test'
+
+import {speciesDocumentNameASAP} from '../utils/__fixtures__/documents'
+import {partialASAPReleaseMetadata} from '../utils/__fixtures__/releases'
+import {
+  archiveAndDeleteRelease,
+  createDocument,
+  createRelease,
+  deleteRelease,
+  getRandomReleaseId,
+  skipIfBrowser,
+} from '../utils/methods'
+import {
+  publishAndConfirmRelease,
+  publishAndConfirmReleaseMenu,
+  revertAndConfirmRelease,
+  scheduleAndConfirmReleaseMenu,
+  unscheduleAndConfirmRelease,
+} from '../utils/release-detail-ui-methods'
+
+test.describe('Revert ASAP', () => {
+  const asapReleaseIdTestOne: string = getRandomReleaseId()
+
+  test.beforeEach(async ({sanityClient, browserName, page, _testContext}) => {
+    skipIfBrowser(browserName)
+    test.slow()
+
+    const dataset = sanityClient.config().dataset
+
+    await createRelease({
+      sanityClient,
+      dataset,
+      releaseId: asapReleaseIdTestOne,
+      metadata: partialASAPReleaseMetadata,
+    })
+
+    const versionDocumentId = _testContext.getUniqueDocumentId()
+
+    await createDocument(sanityClient, {
path: test/e2e/tests/releases/revert/revertASAP.spec.ts
user: login: juice49
body: Could it be beneficial to parallelise the creation of these two documents? I'm not sure, but maybe the creation of the release can be parallelised, too?
created_at: 2025-03-20T14:39:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,119 @@
+import {expect} from '@playwright/test'
+import {test} from '@sanity/test'
+
+import {speciesDocumentNameASAP} from '../utils/__fixtures__/documents'
+import {partialUndecidedReleaseMetadata} from '../utils/__fixtures__/releases'
+import {
+  archiveAndDeleteRelease,
+  createDocument,
+  createRelease,
+  deleteRelease,
+  getRandomReleaseId,
+  skipIfBrowser,
+} from '../utils/methods'
+import {
+  publishAndConfirmReleaseMenu,
+  revertAndConfirmRelease,
+  scheduleAndConfirmReleaseMenu,
+} from '../utils/release-detail-ui-methods'
+
+test.describe('Revert Undecided', () => {
+  const undecidedReleaseIdTestOne: string = getRandomReleaseId()
+
+  test.beforeEach(async ({sanityClient, browserName, page, _testContext}) => {
+    skipIfBrowser(browserName)
+    test.slow()
+    const dataset = sanityClient.config().dataset
+
+    await createRelease({
+      sanityClient,
+      dataset,
+      releaseId: undecidedReleaseIdTestOne,
+      metadata: partialUndecidedReleaseMetadata,
+    })
+
+    const versionDocumentId = _testContext.getUniqueDocumentId()
+
+    await createDocument(sanityClient, {
path: test/e2e/tests/releases/revert/revertUndecided.spec.ts
user: login: juice49
body: Same thought about parallelisation.
created_at: 2025-03-20T14:50:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,153 @@
+import {expect} from '@playwright/test'
+import {test} from '@sanity/test'
+
+import {speciesDocumentNameASAP} from '../utils/__fixtures__/documents'
+import {partialASAPReleaseMetadata} from '../utils/__fixtures__/releases'
+import {
+  archiveAndDeleteRelease,
+  createDocument,
+  createRelease,
+  deleteRelease,
+  getRandomReleaseId,
+  skipIfBrowser,
+} from '../utils/methods'
+import {
+  publishAndConfirmRelease,
+  publishAndConfirmReleaseMenu,
+  revertAndConfirmRelease,
+  scheduleAndConfirmReleaseMenu,
+  unscheduleAndConfirmRelease,
+} from '../utils/release-detail-ui-methods'
+
+test.describe('Revert ASAP', () => {
+  const asapReleaseIdTestOne: string = getRandomReleaseId()
+
+  test.beforeEach(async ({sanityClient, browserName, page, _testContext}) => {
+    skipIfBrowser(browserName)
+    test.slow()
+
+    const dataset = sanityClient.config().dataset
+
+    await createRelease({
+      sanityClient,
+      dataset,
+      releaseId: asapReleaseIdTestOne,
+      metadata: partialASAPReleaseMetadata,
+    })
+
+    const versionDocumentId = _testContext.getUniqueDocumentId()
+
+    await createDocument(sanityClient, {
path: test/e2e/tests/releases/revert/revertASAP.spec.ts
user: login: RitaDias
body: I'll give it a shot, let me give it a go!
created_at: 2025-03-21T07:29:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,153 @@
+import {expect} from '@playwright/test'
+import {test} from '@sanity/test'
+
+import {speciesDocumentNameASAP} from '../utils/__fixtures__/documents'
+import {partialASAPReleaseMetadata} from '../utils/__fixtures__/releases'
+import {
+  archiveAndDeleteRelease,
+  createDocument,
+  createRelease,
+  deleteRelease,
+  getRandomReleaseId,
+  skipIfBrowser,
+} from '../utils/methods'
+import {
+  publishAndConfirmRelease,
+  publishAndConfirmReleaseMenu,
+  revertAndConfirmRelease,
+  scheduleAndConfirmReleaseMenu,
+  unscheduleAndConfirmRelease,
+} from '../utils/release-detail-ui-methods'
+
+test.describe('Revert ASAP', () => {
+  const asapReleaseIdTestOne: string = getRandomReleaseId()
+
+  test.beforeEach(async ({sanityClient, browserName, page, _testContext}) => {
+    skipIfBrowser(browserName)
+    test.slow()
+
+    const dataset = sanityClient.config().dataset
+
+    await createRelease({
+      sanityClient,
+      dataset,
+      releaseId: asapReleaseIdTestOne,
+      metadata: partialASAPReleaseMetadata,
+    })
+
+    const versionDocumentId = _testContext.getUniqueDocumentId()
+
+    await createDocument(sanityClient, {
path: test/e2e/tests/releases/revert/revertASAP.spec.ts
user: login: RitaDias
body: The release itself I don't think can be parallelised with the risk of flakiness since we _need_ the id and the release to be created to go to the page (a few lines down)
created_at: 2025-03-21T07:30:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,153 @@
+import {expect} from '@playwright/test'
+import {test} from '@sanity/test'
+
+import {speciesDocumentNameASAP} from '../utils/__fixtures__/documents'
+import {partialASAPReleaseMetadata} from '../utils/__fixtures__/releases'
+import {
+  archiveAndDeleteRelease,
+  createDocument,
+  createRelease,
+  deleteRelease,
+  getRandomReleaseId,
+  skipIfBrowser,
+} from '../utils/methods'
+import {
+  publishAndConfirmRelease,
+  publishAndConfirmReleaseMenu,
+  revertAndConfirmRelease,
+  scheduleAndConfirmReleaseMenu,
+  unscheduleAndConfirmRelease,
+} from '../utils/release-detail-ui-methods'
+
+test.describe('Revert ASAP', () => {
+  const asapReleaseIdTestOne: string = getRandomReleaseId()
+
+  test.beforeEach(async ({sanityClient, browserName, page, _testContext}) => {
+    skipIfBrowser(browserName)
+    test.slow()
+
+    const dataset = sanityClient.config().dataset
+
+    await createRelease({
+      sanityClient,
+      dataset,
+      releaseId: asapReleaseIdTestOne,
+      metadata: partialASAPReleaseMetadata,
+    })
+
+    const versionDocumentId = _testContext.getUniqueDocumentId()
+
+    await createDocument(sanityClient, {
path: test/e2e/tests/releases/revert/revertASAP.spec.ts
user: login: RitaDias
body: Hmm doing this has made the tests become flaky (running them locally), I would prefer to keep it as is since at least they were not flaky when I had them like this so I'm keeping as is 🤔 
created_at: 2025-03-21T07:48:25Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-03-19T09:56:41Z
body: [vc]: #y3PIFOOEuz9/H6MS3Njz/e+6HCk2ZRF1P5/AOJOWIvE=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ0ZXN0LXN0dWRpbyIsInJvb3REaXJlY3RvcnkiOm51bGwsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJ0ZXN0LXN0dWRpby1naXQtYWRkLWUyZS10by1hcmNoaXZlLXVuYXJjaGl2ZS5zYW5pdHkuZGV2In0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zYW5pdHktc2FuZGJveC90ZXN0LXN0dWRpby80NDhNaG5hcGg1eTJXWXpKVG1ybWZSQjV0RjVIIiwicHJldmlld1VybCI6InRlc3Qtc3R1ZGlvLWdpdC1hZGQtZTJlLXRvLWFyY2hpdmUtdW5hcmNoaXZlLnNhbml0eS5kZXYiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQifSx7Im5hbWUiOiJzdHVkaW8td29ya3Nob3AiLCJyb290RGlyZWN0b3J5IjpudWxsLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zYW5pdHktc2FuZGJveC9zdHVkaW8td29ya3Nob3AvQ1dzMnlYSEE4WHIzWW1COGNaa0ZGWXJxWlJBdiIsIm5leHRDb21taXRTdGF0dXMiOiJJR05PUkVEIiwicHJldmlld1VybCI6InN0dWRpby13b3Jrc2hvcC1naXQtYWRkLWUyZS10by1hcmNoaXZlLXVuYXJjaGl2ZS5zYW5pdHkuZGV2In0seyJuYW1lIjoicGVyZm9ybWFuY2Utc3R1ZGlvIiwicm9vdERpcmVjdG9yeSI6bnVsbCwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6InBlcmZvcm1hbmNlLXN0dWRpby1naXQtYWRkLWUyZS10by1hcmNoaXZlLXVuYXJjaGl2ZS5zYW5pdHkuZGV2In0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zYW5pdHktc2FuZGJveC9wZXJmb3JtYW5jZS1zdHVkaW8vNWNoVHVYUWo5eDRyUGFzSDE4Y1pWNzFwc3gzaiIsInByZXZpZXdVcmwiOiJwZXJmb3JtYW5jZS1zdHVkaW8tZ2l0LWFkZC1lMmUtdG8tYXJjaGl2ZS11bmFyY2hpdmUuc2FuaXR5LmRldiIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCJ9LHsibmFtZSI6InRlc3QtbmV4dC1zdHVkaW8iLCJyb290RGlyZWN0b3J5IjoiZGV2L3Rlc3QtbmV4dC1zdHVkaW8iLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zYW5pdHktc2FuZGJveC90ZXN0LW5leHQtc3R1ZGlvL0RZemlBWllMaHo0dnZ3S1M2RWgxOWEzQ0dYZXkiLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCJ9LHsibmFtZSI6InBhZ2UtYnVpbGRpbmctc3R1ZGlvIiwicm9vdERpcmVjdG9yeSI6ImRldi9wYWdlLWJ1aWxkaW5nLXN0dWRpbyIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJwYWdlLWJ1aWxkaW5nLXN0dWRpby1naXQtYWRkLWUyZS10by1hcmNoaXZlLXVuYXJjaGl2ZS5zYW5pdHkuZGV2In0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zYW5pdHktc2FuZGJveC9wYWdlLWJ1aWxkaW5nLXN0dWRpby80c3M5QUJWTkEyVmJhamtwTFV3VTdYUXZ6a0pyIiwicHJldmlld1VybCI6InBhZ2UtYnVpbGRpbmctc3R1ZGlvLWdpdC1hZGQtZTJlLXRvLWFyY2hpdmUtdW5hcmNoaXZlLnNhbml0eS5kZXYiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQifV19
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **page-building-studio** | ✅ Ready ([Inspect](https://vercel.com/sanity-sandbox/page-building-studio/4ss9ABVNA2VbajkpLUwU7XQvzkJr)) | [Visit Preview](https://page-building-studio-git-add-e2e-to-archive-unarchive.sanity.dev) | 💬 [**Add feedback**](https://vercel.live/open-feedback/page-building-studio-git-add-e2e-to-archive-unarchive.sanity.dev?via=pr-comment-feedback-link) | Mar 20, 2025 11:18am |
| **performance-studio** | ✅ Ready ([Inspect](https://vercel.com/sanity-sandbox/performance-studio/5chTuXQj9x4rPasH18cZV71psx3j)) | [Visit Preview](https://performance-studio-git-add-e2e-to-archive-unarchive.sanity.dev) | 💬 [**Add feedback**](https://vercel.live/open-feedback/performance-studio-git-add-e2e-to-archive-unarchive.sanity.dev?via=pr-comment-feedback-link) | Mar 20, 2025 11:18am |
| **test-studio** | ✅ Ready ([Inspect](https://vercel.com/sanity-sandbox/test-studio/448Mhnaph5y2WYzJTmrmfRB5tF5H)) | [Visit Preview](https://test-studio-git-add-e2e-to-archive-unarchive.sanity.dev) | 💬 [**Add feedback**](https://vercel.live/open-feedback/test-studio-git-add-e2e-to-archive-unarchive.sanity.dev?via=pr-comment-feedback-link) | Mar 20, 2025 11:18am |

<details><summary>2 Skipped Deployments</summary>

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **studio-workshop** | ⬜️ Ignored ([Inspect](https://vercel.com/sanity-sandbox/studio-workshop/CWs2yXHA8Xr3YmB8cZkFFYrqZRAv)) | [Visit Preview](https://studio-workshop-git-add-e2e-to-archive-unarchive.sanity.dev) |  | Mar 20, 2025 11:18am |
| **test-next-studio** | ⬜️ Ignored ([Inspect](https://vercel.com/sanity-sandbox/test-next-studio/DYziAZYLhz4vvwKS6Eh19a3CGXey)) |  |  | Mar 20, 2025 11:18am |
</details>






author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-19T09:57:24Z
body: No changes to documentation
<!-- thollander/actions-comment-pull-request "docs-report" -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-19T10:01:22Z
body: <h2>Coverage Report</h2>
<table> <thead> <tr> <th align="center">Status</th> <th align="left">Category</th> <th align="right">Percentage</th> <th align="right">Covered / Total</th> </tr> </thead> <tbody> <tr> <td align="center">🔵</td> <td align="left">Lines</td> <td align="right">42.98%</td> <td align="right">55546 / 129223</td> </tr> <tr> <td align="center">🔵</td> <td align="left">Statements</td> <td align="right">42.98%</td> <td align="right">55546 / 129223</td> </tr> <tr> <td align="center">🔵</td> <td align="left">Functions</td> <td align="right">47.43%</td> <td align="right">2783 / 5867</td> </tr> <tr> <td align="center">🔵</td> <td align="left">Branches</td> <td align="right">79.55%</td> <td align="right">10417 / 13094</td> </tr> </tbody> </table><details><summary>File Coverage</summary><table> <thead> <tr> <th align="left">File</th> <th align="right">Stmts</th> <th align="right">Branches</th> <th align="right">Functions</th> <th align="right">Lines</th> <th align="left">Uncovered Lines</th> </tr> </thead> <tbody> <tr> <td align="left" colspan="6"><b>Changed Files</b></td> </tr> <tr> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/ab299d00e1ef6f055133e27b1a650c2ff017fa04/packages/sanity/src/core/releases/tool/components/releaseCTAButtons/ReleaseRevertButton/ReleaseRevertButton.tsx">packages/sanity/src/core/releases/tool/components/releaseCTAButtons/ReleaseRevertButton/ReleaseRevertButton.tsx</a></td> <td align="right">18.45%</td> <td align="right">55.55%</td> <td align="right">50%</td> <td align="right">18.45%</td> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/ab299d00e1ef6f055133e27b1a650c2ff017fa04/packages/sanity/src/core/releases/tool/components/releaseCTAButtons/ReleaseRevertButton/ReleaseRevertButton.tsx#L34-L229" class="text-red">34-229</a>, <a href="https://github.com/sanity-io/sanity/blob/ab299d00e1ef6f055133e27b1a650c2ff017fa04/packages/sanity/src/core/releases/tool/components/releaseCTAButtons/ReleaseRevertButton/ReleaseRevertButton.tsx#L246-L248" class="text-red">246-248</a>, <a href="https://github.com/sanity-io/sanity/blob/ab299d00e1ef6f055133e27b1a650c2ff017fa04/packages/sanity/src/core/releases/tool/components/releaseCTAButtons/ReleaseRevertButton/ReleaseRevertButton.tsx#L268-L277" class="text-red">268-277</a>, <a href="https://github.com/sanity-io/sanity/blob/ab299d00e1ef6f055133e27b1a650c2ff017fa04/packages/sanity/src/core/releases/tool/components/releaseCTAButtons/ReleaseRevertButton/ReleaseRevertButton.tsx#L285-L295" class="text-red">285-295</a></td> </tr> </tbody> </table></details>
<em>Generated in workflow <a href=https://github.com/sanity-io/sanity/actions/runs/13967905386>#32568</a> for commit <a href="https://github.com/sanity-io/sanity/commit/ab299d00e1ef6f055133e27b1a650c2ff017fa04">ab299d0</a> by the <a href="https://github.com/davelosert/vitest-coverage-report-action">Vitest Coverage Report Action</a></em>

<!-- vitest-coverage-report-marker-root -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-19T10:09:55Z
body: ### ⚡️ Editor Performance Report

Updated Thu, 20 Mar 2025 11:19:08 GMT


| Benchmark | reference<br/><sup>latency of `sanity@latest`</sup> | experiment<br/><sup>latency of this branch</sup> | Δ (%)<br/><sup>latency difference</sup> | |
| :-- | :-- | :-- | :-- | --- |
| article (title) | 25.6 efps (39ms) | 23.8 efps (42ms) | +3ms (+7.7%) | ✅ |
| article (body) | 72.5 efps (14ms) | 81.3 efps (12ms) | -1ms (-/-%) | ✅ |
| article (string inside object) | 27.0 efps (37ms) | 23.8 efps (42ms) | +5ms (+13.5%) | ✅ |
| article (string inside array) | 24.1 efps (42ms) | 22.2 efps (45ms) | +4ms (+8.4%) | ✅ |
| recipe (name) | 47.6 efps (21ms) | 47.6 efps (21ms) | +0ms (-/-%) | ✅ |
| recipe (description) | 50.0 efps (20ms) | 52.6 efps (19ms) | -1ms (-5.0%) | ✅ |
| recipe (instructions) | 99.9+ efps (5ms) | 99.9+ efps (5ms) | +0ms (-/-%) | ✅ |
| synthetic (title) | 18.5 efps (54ms) | 18.9 efps (53ms) | -1ms (-1.9%) | ✅ |
| synthetic (string inside object) | 19.8 efps (51ms) | 19.2 efps (52ms) | +2ms (+3.0%) | ✅ |


> **efps** — editor "frames per second". The number of updates assumed to be possible within a second.
>
> Derived from input latency. `efps = 1000 / input_latency`

<details>

<summary><strong>Detailed information</strong></summary>

### 🏠 Reference result

The performance result of `sanity@latest`



| Benchmark | latency | p75 | p90 | p99 | blocking time | test duration |
| --------- | ------: | --: | --: | --: | ------------: | ------------: |
| article (title) | 39ms | 44ms | 49ms | 57ms | 258ms | 11.0s |
| article (body) | 14ms | 15ms | 16ms | 138ms | 174ms | 5.1s |
| article (string inside object) | 37ms | 39ms | 42ms | 182ms | 200ms | 7.0s |
| article (string inside array) | 42ms | 43ms | 47ms | 58ms | 201ms | 6.8s |
| recipe (name) | 21ms | 22ms | 26ms | 43ms | 2ms | 7.9s |
| recipe (description) | 20ms | 21ms | 22ms | 23ms | 0ms | 5.0s |
| recipe (instructions) | 5ms | 7ms | 8ms | 40ms | 0ms | 3.2s |
| synthetic (title) | 54ms | 58ms | 65ms | 242ms | 1066ms | 13.4s |
| synthetic (string inside object) | 51ms | 53ms | 57ms | 212ms | 640ms | 8.0s |


### 🧪 Experiment result

The performance result of this branch


| Benchmark | latency | p75 | p90 | p99 | blocking time | test duration |
| --------- | ------: | --: | --: | --: | ------------: | ------------: |
| article (title) | 42ms | 46ms | 66ms | 526ms | 660ms | 11.4s |
| article (body) | 12ms | 14ms | 19ms | 39ms | 56ms | 4.9s |
| article (string inside object) | 42ms | 43ms | 48ms | 52ms | 25ms | 6.9s |
| article (string inside array) | 45ms | 48ms | 52ms | 150ms | 369ms | 7.7s |
| recipe (name) | 21ms | 22ms | 24ms | 44ms | 0ms | 7.4s |
| recipe (description) | 19ms | 20ms | 21ms | 25ms | 0ms | 4.6s |
| recipe (instructions) | 5ms | 6ms | 7ms | 8ms | 0ms | 3.0s |
| synthetic (title) | 53ms | 55ms | 59ms | 215ms | 928ms | 13.3s |
| synthetic (string inside object) | 52ms | 53ms | 60ms | 530ms | 1521ms | 8.4s |


### 📚 Glossary

> #### column definitions
>
> - **benchmark** — the name of the test, e.g. "article", followed by the label of the field being measured, e.g. "(title)".
> - **latency** — the time between when a key was pressed and when it was rendered. derived from a set of samples. the median (p50) is shown to show the most common latency.
> - **p75** — the 75th percentile of the input latency in the test run. 75% of the sampled inputs in this benchmark were processed faster than this value. this provides insight into the upper range of typical performance.
> - **p90** — the 90th percentile of the input latency in the test run. 90% of the sampled inputs were faster than this. this metric helps identify slower interactions that occurred less frequently during the benchmark.
> - **p99** — the 99th percentile of the input latency in the test run. only 1% of sampled inputs were slower than this. this represents the worst-case scenarios encountered during the benchmark, useful for identifying potential performance outliers.
> - **blocking time** — the total time during which the main thread was blocked, preventing user input and UI updates. this metric helps identify performance bottlenecks that may cause the interface to feel unresponsive.
> - **test duration** — how long the test run took to complete.

</details>

<!-- thollander/actions-comment-pull-request "efps-report" -->
author_association: CONTRIBUTOR