feat: restart to apply update, privileged helper, rollback
This commit is contained in:
1 parent
70460e4170
commit
54e8fd5fed
63 files changed
+8635
-164
No files matched your search
+89
-9
@@ -187,6 +187,7 @@ function updateStatusBody(overrides: Record<string, unknown> = {}) {
|
||||
progress: null,
|
||||
error: null,
|
||||
started_at: null,
|
||||
finished_at: null,
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
@@ -248,6 +249,41 @@ function updateDownloading() {
|
||||
|
||||
const updateCheckHangs: ApiMock = { status: 200, hang: true }
|
||||
|
||||
const restartStartedAt = '2026-09-30T09:51:00'
|
||||
const restartFinishedAt = '2026-09-30T09:52:00'
|
||||
const restartMemoryKey = 'renewcms:update-restart'
|
||||
|
||||
function restartApi(status: unknown, restart: ApiMock): Record<string, ApiMock> {
|
||||
return { ...updateApi('owner', status), 'POST /v1/system/update/restart': restart }
|
||||
}
|
||||
|
||||
function restartAccepted(status: Record<string, unknown>): ApiMock {
|
||||
return { status: 202, body: { ...status, state: 'restarting', started_at: restartStartedAt } }
|
||||
}
|
||||
|
||||
async function openRestartDialog(page: Page) {
|
||||
await page.getByRole('button', { name: 'Restart', exact: true }).click()
|
||||
await page.getByRole('alertdialog').waitFor()
|
||||
}
|
||||
|
||||
function confirmRestart(label: string) {
|
||||
return async (page: Page) => {
|
||||
await openRestartDialog(page)
|
||||
await page.getByRole('button', { name: label, exact: true }).click()
|
||||
}
|
||||
}
|
||||
|
||||
async function rememberRestartAndReload(page: Page) {
|
||||
await page.evaluate(
|
||||
([key, startedAt]) =>
|
||||
sessionStorage.setItem(key!, JSON.stringify({ kind: 'update', started_at: startedAt })),
|
||||
[restartMemoryKey, restartStartedAt],
|
||||
)
|
||||
await page.reload()
|
||||
await page.getByText('Your site is back on v2.2.0-beta.').first().waitFor()
|
||||
await page.mouse.move(0, 0)
|
||||
}
|
||||
|
||||
function updateApi(
|
||||
role: 'owner' | 'admin',
|
||||
status: unknown,
|
||||
@@ -2089,12 +2125,42 @@ export const visualStates: Record<string, Record<string, VisualState>> = {
|
||||
Downloading: { path: '/settings', api: updateApi('owner', updateDownloading()) },
|
||||
Verifying: { path: '/settings', api: updateApi('owner', updateJob('verifying')) },
|
||||
'Ready to install': { path: '/settings', api: updateApi('owner', updateJob('ready')) },
|
||||
'Restart: confirm': { path: '/settings', skip: 'pending' },
|
||||
'Restart: request failed': { path: '/settings', skip: 'pending' },
|
||||
'Restarting to install': { path: '/settings', skip: 'pending' },
|
||||
'Restarting, taking longer': { path: '/settings', skip: 'pending' },
|
||||
'Update done': { path: '/settings', skip: 'pending' },
|
||||
'Update failed, rolled back': { path: '/settings', skip: 'pending' },
|
||||
'Restart: confirm': {
|
||||
path: '/settings',
|
||||
api: restartApi(updateJob('ready'), { status: 500 }),
|
||||
steps: openRestartDialog,
|
||||
},
|
||||
'Restart: request failed': {
|
||||
path: '/settings',
|
||||
api: restartApi(updateJob('ready'), { status: 500 }),
|
||||
steps: async (page) => {
|
||||
await confirmRestart('Restart now')(page)
|
||||
await page.getByText("Couldn't start the restart").waitFor()
|
||||
await page.mouse.move(0, 0)
|
||||
},
|
||||
},
|
||||
'Restarting to install': {
|
||||
path: '/settings',
|
||||
api: restartApi(updateJob('ready'), restartAccepted(updateJob('ready'))),
|
||||
steps: async (page) => {
|
||||
await confirmRestart('Restart now')(page)
|
||||
await page.getByText('Installing version 2.3.0').waitFor()
|
||||
},
|
||||
},
|
||||
'Restarting, taking longer': { path: '/settings', skip: 'clock-sensitive' },
|
||||
'Update done': { path: '/settings', skip: 'clock-sensitive' },
|
||||
'Update failed, rolled back': {
|
||||
path: '/settings',
|
||||
api: updateApi(
|
||||
'owner',
|
||||
updateJob('failed', {
|
||||
error: 'rolled_back',
|
||||
started_at: restartStartedAt,
|
||||
finished_at: restartFinishedAt,
|
||||
}),
|
||||
),
|
||||
steps: rememberRestartAndReload,
|
||||
},
|
||||
'Download failed': {
|
||||
path: '/settings',
|
||||
api: updateApi('owner', updateJob('failed', { error: 'download' })),
|
||||
@@ -2123,9 +2189,23 @@ export const visualStates: Record<string, Record<string, VisualState>> = {
|
||||
await page.mouse.move(0, 0)
|
||||
},
|
||||
},
|
||||
'Plain restart: confirm': { path: '/settings', skip: 'pending' },
|
||||
'Plain restart: restarting': { path: '/settings', skip: 'pending' },
|
||||
'Plain restart: done': { path: '/settings', skip: 'pending' },
|
||||
'Plain restart: confirm': {
|
||||
path: '/settings',
|
||||
api: restartApi(updateStatusBody({ can_apply: true }), { status: 500 }),
|
||||
steps: openRestartDialog,
|
||||
},
|
||||
'Plain restart: restarting': {
|
||||
path: '/settings',
|
||||
api: restartApi(
|
||||
updateStatusBody({ can_apply: true }),
|
||||
restartAccepted(updateStatusBody({ can_apply: true })),
|
||||
),
|
||||
steps: async (page) => {
|
||||
await confirmRestart('Restart site')(page)
|
||||
await page.getByText('Restarting your site').waitFor()
|
||||
},
|
||||
},
|
||||
'Plain restart: done': { path: '/settings', skip: 'clock-sensitive' },
|
||||
'Admin view': {
|
||||
path: '/settings',
|
||||
api: updateApi('admin', updateAvailable()),
|
||||
|
||||
Reference in new issue
Block a user