feat: users row actions "Cancel invite" on Pending rows and "Renew invite" on Expired rows

This commit is contained in:
fsylvain committed 2026-09-30 02:48:49 +02:00
1 parent 1ece778caf
commit 142325730d
14 files changed
+1156 -35

No files matched your search

+3 -1
View File
@@ -88,7 +88,9 @@ async function readScreen(name: string): Promise<Screen> {
if (!reference) {
throw new Error(`docs/design/screens/${name}.md has no visual reference line`)
}
const states = [...markdown.matchAll(/^### (.+)$/gm)].map((match) => match[1]!.trim())
const statesStart = markdown.search(/^## States$/m)
const statesMarkdown = statesStart === -1 ? markdown : markdown.slice(statesStart)
const states = [...statesMarkdown.matchAll(/^### (.+)$/gm)].map((match) => match[1]!.trim())
return { name, board: reference[1]!, group: reference[2]!, states }
}
+61 -4
View File
@@ -634,6 +634,33 @@ async function openInviteModal(page: Page) {
await page.mouse.move(0, 0)
}
function rowActionButton(page: Page, email: string, name: string) {
return page
.locator('[data-testid="user-row"], [data-testid="user-card"]')
.filter({ hasText: email })
.getByRole('button', { name, exact: true })
}
async function openCancelInviteDialog(page: Page) {
await rowActionButton(page, 'sam.oliveira@renew.example', 'Cancel invite').click()
await page.getByRole('alertdialog').waitFor()
await page.mouse.move(0, 0)
}
async function confirmCancelInvite(page: Page) {
await openCancelInviteDialog(page)
await page
.getByRole('alertdialog')
.getByRole('button', { name: /^Cancel(ling…| invite)$/ })
.click()
await page.mouse.move(0, 0)
}
async function renewPaulHuberInvite(page: Page) {
await rowActionButton(page, 'p.huber@gmail.com', 'Renew invite').click()
await page.mouse.move(0, 0)
}
function inviteWith(email: string, response?: ApiMock) {
return async (page: Page) => {
await openInviteModal(page)
@@ -845,10 +872,40 @@ export const visualStates: Record<string, Record<string, VisualState>> = {
Default: {
path: '/users',
api: usersApi(),
exception: {
maxPercent: 10.5,
reason: 'Row actions (Cancel invite, Renew invite) drawn on the board are out of scope for this slice',
approvedBy: 'user',
},
'Cancel invite: confirm': {
path: '/users',
api: usersApi(),
steps: openCancelInviteDialog,
},
'Cancel invite: sending': {
path: '/users',
api: usersApi({ 'DELETE /v1/user/invite/2': { status: 204, hang: true } }),
steps: confirmCancelInvite,
},
'Cancel invite: failed': {
path: '/users',
api: usersApi({ 'DELETE /v1/user/invite/2': { status: 500 } }),
steps: async (page) => {
await confirmCancelInvite(page)
await page.getByText("Couldn't cancel the invite").waitFor()
await page.mouse.move(0, 0)
},
},
'Cancel invite: done': { path: '/users', skip: 'clock-sensitive' },
'Renew invite: sending': {
path: '/users',
api: usersApi({ 'POST /v1/user/invite/1/renew': { status: 200, hang: true } }),
steps: renewPaulHuberInvite,
},
'Renew invite: done': { path: '/users', skip: 'clock-sensitive' },
'Renew invite: failed': {
path: '/users',
api: usersApi({ 'POST /v1/user/invite/1/renew': { status: 500 } }),
steps: async (page) => {
await renewPaulHuberInvite(page)
await page.getByText("Couldn't renew the invite").waitFor()
await page.mouse.move(0, 0)
},
},
Loading: {