From 1cd81146a9790be2574d8ddec78481cff1e09829 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Wed, 15 Apr 2026 20:25:23 +0200 Subject: [PATCH] fix: improve runner list and details view (#12113) - shrink runner list width (use icons, move details link to runner name) - add owner to runner details on admin view - #11516 removed a lot details which makes it much harder for an admin to find a specific runner --- ### admin list ![image](/attachments/7dd28e5b-6332-48b1-b545-2fc2b83e5368) ### admin org runner details ![image](/attachments/da972377-d401-41fe-8a17-d78824d6d714) ### admin repo runner ![image](/attachments/489e71c2-6087-4441-ad72-695ef0e04161) ### individual list ![image](/attachments/5618b962-0964-415f-a820-e673001f4007) ### individual runner details ![image](/attachments/5799c212-37d5-4047-965f-60952ee7c74c) ### tooltips for edit and delete ![image](/attachments/bcfb9358-0bf3-4d3f-a73c-66fb8e63eb67) ![image](/attachments/63b1c9e5-2fd3-4cc3-9f88-e0a1cf410769) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12113 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Michael Kriese Co-committed-by: Michael Kriese --- options/locale_next/locale_en-US.json | 3 - templates/shared/actions/runner_details.tmpl | 8 ++ templates/shared/actions/runner_list.tmpl | 16 ++- tests/e2e/runner-management.test.e2e.ts | 124 +++++++++++-------- 4 files changed, 85 insertions(+), 66 deletions(-) diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index 5581806e80..85a21ef28d 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -516,11 +516,8 @@ "actions.runners.labels": "Labels", "actions.runners.version": "Version", "actions.runners.last_online": "Last online time", - "actions.runners.list_runners.details_column": "Details", "actions.runners.list_runners.edit_column": "Edit", "actions.runners.list_runners.delete_column": "Delete", - "actions.runners.list_runners.details_button": "Details", - "actions.runners.list_runners.details_button_aria": "Show details of %s", "actions.runners.list_runners.delete_button": "Delete", "actions.runners.list_runners.delete_button_aria": "Delete %s", "actions.runners.list_runners.edit_button": "Edit", diff --git a/templates/shared/actions/runner_details.tmpl b/templates/shared/actions/runner_details.tmpl index f50e96482a..537dd766f6 100644 --- a/templates/shared/actions/runner_details.tmpl +++ b/templates/shared/actions/runner_details.tmpl @@ -23,6 +23,14 @@ {{.Runner.BelongsToOwnerType.LocaleString ctx.Locale}} + {{if and $.PageIsAdmin .Runner.BelongsToOwnerName}} +
+
{{.Runner.BelongsToOwnerType.LocaleString ctx.Locale}}
+
+ {{.Runner.BelongsToOwnerName}} +
+
+ {{end}}
{{ctx.Locale.Tr "actions.runners.labels"}}
diff --git a/templates/shared/actions/runner_list.tmpl b/templates/shared/actions/runner_list.tmpl index 88c354b7cf..bb5e292528 100644 --- a/templates/shared/actions/runner_list.tmpl +++ b/templates/shared/actions/runner_list.tmpl @@ -57,9 +57,6 @@ {{ctx.Locale.Tr "actions.runners.status"}} {{SortArrow "online" "offline" .SortType false}} - - {{ctx.Locale.Tr "actions.runners.list_runners.details_column"}} - {{ctx.Locale.Tr "actions.runners.list_runners.edit_column"}} @@ -72,7 +69,7 @@ {{range .Runners}} -
{{.Name}}
+
{{.UUID}}
@@ -86,6 +83,10 @@ {{.BelongsToOwnerType.LocaleString ctx.Locale}} + {{if and $.PageIsAdmin .BelongsToOwnerName}} +
+ {{.BelongsToOwnerName}} + {{end}}
@@ -107,17 +108,14 @@
- - {{ctx.Locale.Tr "actions.runners.list_runners.details_button"}} - {{if .Editable $.RunnerOwnerID $.RunnerRepoID}} - {{ctx.Locale.Tr "actions.runners.list_runners.edit_button"}} + {{svg "octicon-pencil"}} {{end}} {{if .Editable $.RunnerOwnerID $.RunnerRepoID}} - + {{end}} diff --git a/tests/e2e/runner-management.test.e2e.ts b/tests/e2e/runner-management.test.e2e.ts index 41808f72d1..1dba606a1b 100644 --- a/tests/e2e/runner-management.test.e2e.ts +++ b/tests/e2e/runner-management.test.e2e.ts @@ -30,39 +30,41 @@ test.describe('Runners of user2', () => { // We cannot assert the length of the table because it's influenced by global fixtures. It also changes depending on // the ordering of tests. - await expect(rows.nth(0)).toHaveAccessibleName('Name Labels Type Status Details Edit Delete'); + await expect(rows.nth(0)).toHaveAccessibleName('Name Labels Type Status Edit Delete'); await expect(page.locator('tbody tr:has-text("3a20ad8d-d5d6-4b7b-ba55-841ac8264c17")')).toMatchAriaSnapshot(` - - cell "runner-2 3a20ad8d-d5d6-4b7b-ba55-841ac8264c17" + - cell "runner-2 3a20ad8d-d5d6-4b7b-ba55-841ac8264c17": + - link "runner-2": + - /url: /user/settings/actions/runners/719932 - cell "docker" - cell "Individual" - cell "Offline" - - cell "Show details of runner-2" - cell "Edit runner-2" - cell "Delete runner-2" `); await expect(page.locator('tbody tr:has-text("1ef59b64-93b7-4ad4-ade4-21ca13db49c0")')).toMatchAriaSnapshot(` - - cell "runner-4 1ef59b64-93b7-4ad4-ade4-21ca13db49c0" + - cell "runner-4 1ef59b64-93b7-4ad4-ade4-21ca13db49c0": + - link "runner-4": + - /url: /user/settings/actions/runners/719934 - cell "docker" - cell "Global" - cell "Offline" - - cell "Show details of runner-4" - cell - cell `); - await page.getByRole('link', {name: 'Show details of runner-2', exact: true}).click(); + await page.getByRole('link', {name: 'runner-2', exact: true}).click(); await expect(page).toHaveTitle(/^Runner runner-2 .*/); await page.goto('/user/settings/actions/runners'); - await page.getByRole('link', {name: 'Show details of runner-4', exact: true}).click(); + await page.getByRole('link', {name: 'runner-4', exact: true}).click(); await expect(page).toHaveTitle(/^Runner runner-4 .*/); }); test('runner details with tasks of repositories owned by user', async ({page}) => { await page.goto('/user/settings/actions/runners'); - await page.getByRole('link', {name: 'Show details of runner-4', exact: true}).click(); + await page.getByRole('link', {name: 'runner-4', exact: true}).click(); await expect(page).toHaveTitle(/^Runner runner-4 .*/); await expect(page.getByRole('heading', {name: 'Runner runner-4'})).toBeVisible(); @@ -142,11 +144,12 @@ test.describe('Runners of user2', () => { await page.getByRole('link', {name: 'List of runners', exact: true}).click(); await expect(page.locator(`tbody tr:has-text("${runnerUUID}")`)).toMatchAriaSnapshot(` - - cell "runner-991301 ${runnerUUID}" + - cell "runner-991301 ${runnerUUID}": + - link "runner-991301": + - /url: /user/settings/actions/runners/\\d+/ - cell "" - cell "Individual" - cell "Offline" - - cell "Show details of runner-991301" - cell "Edit runner-991301" - cell "Delete runner-991301" `); @@ -287,67 +290,74 @@ test.describe('Global runners', () => { // We cannot assert the length of the table because it's influenced by global fixtures. It also changes depending on // the ordering of tests. - await expect(rows.nth(0)).toHaveAccessibleName('Name Labels Type Status Details Edit Delete'); + await expect(rows.nth(0)).toHaveAccessibleName('Name Labels Type Status Edit Delete'); await expect(page.locator('tbody tr:has-text("8f940b0b-32a2-479a-9d48-06ab8d8a0b90")')).toMatchAriaSnapshot(` - - cell "runner-1 8f940b0b-32a2-479a-9d48-06ab8d8a0b90" + - cell "runner-1 8f940b0b-32a2-479a-9d48-06ab8d8a0b90": + - link "runner-1": + - /url: /admin/actions/runners/719931 - cell "debian gpu" - - cell "Organization" + - cell "Organization org3" - cell "Offline" - - cell "Show details of runner-1" - cell "Edit runner-1" - cell "Delete runner-1" `); await expect(page.locator('tbody tr:has-text("3a20ad8d-d5d6-4b7b-ba55-841ac8264c17")')).toMatchAriaSnapshot(` - - cell "runner-2 3a20ad8d-d5d6-4b7b-ba55-841ac8264c17" + - cell "runner-2 3a20ad8d-d5d6-4b7b-ba55-841ac8264c17": + - link "runner-2": + - /url: /admin/actions/runners/719932 - cell "docker" - - cell "Individual" + - cell "Individual user2" - cell "Offline" - - cell "Show details of runner-2" - cell "Edit runner-2" - cell "Delete runner-2" `); await expect(page.locator('tbody tr:has-text("11c9a6da-0a92-46ea-a4f1-b6c98f8c781c")')).toMatchAriaSnapshot(` - - cell "runner-3 11c9a6da-0a92-46ea-a4f1-b6c98f8c781c" + - cell "runner-3 11c9a6da-0a92-46ea-a4f1-b6c98f8c781c": + - link "runner-3": + - /url: /admin/actions/runners/719933 - cell "fedora" - - cell "Organization" + - cell "Organization org17" - cell "Offline" - - cell "Show details of runner-3" - cell "Edit runner-3" - cell "Delete runner-3" `); await expect(page.locator('tbody tr:has-text("1ef59b64-93b7-4ad4-ade4-21ca13db49c0")')).toMatchAriaSnapshot(` - - cell "runner-4 1ef59b64-93b7-4ad4-ade4-21ca13db49c0" + - cell "runner-4 1ef59b64-93b7-4ad4-ade4-21ca13db49c0": + - link "runner-4": + - /url: /admin/actions/runners/719934 - cell "docker" - cell "Global" - cell "Offline" - - cell "Show details of runner-4" - cell "Edit runner-4" - cell "Delete runner-4" `); await expect(page.locator('tbody tr:has-text("69d29449-1de5-4d17-845d-e3ae11a04a1b")')).toMatchAriaSnapshot(` - - cell "runner-5 69d29449-1de5-4d17-845d-e3ae11a04a1b" + - cell "runner-5 69d29449-1de5-4d17-845d-e3ae11a04a1b": + - link "runner-5": + - /url: /admin/actions/runners/719935 - cell "debian" - - cell "Individual" + - cell "Individual user1" - cell "Offline" - - cell "Show details of runner-5" - cell "Edit runner-5" - cell "Delete runner-5" `); await expect(page.locator('tbody tr:has-text("9da25fbb-89a5-4520-a35a-d55fc94e4b76")')).toMatchAriaSnapshot(` - - cell "runner-6 9da25fbb-89a5-4520-a35a-d55fc94e4b76" + - cell "runner-6 9da25fbb-89a5-4520-a35a-d55fc94e4b76": + - link "runner-6": + - /url: /admin/actions/runners/719936 - cell "debian" - - cell "Repository" + - cell "Repository user2/test_workflows" - cell "Offline" - - cell "Show details of runner-6" - cell "Edit runner-6" - cell "Delete runner-6" `); await expect(page.locator('tbody tr:has-text("d935307e-1d2d-4b61-8885-bc8a1c52c269")')).toMatchAriaSnapshot(` - - cell "runner-7 d935307e-1d2d-4b61-8885-bc8a1c52c269" + - cell "runner-7 d935307e-1d2d-4b61-8885-bc8a1c52c269": + - link "runner-7": + - /url: /admin/actions/runners/719937 - cell "alpine" - - cell "Individual" + - cell "Individual user4" - cell "Offline" - - cell "Show details of runner-7" - cell "Edit runner-7" - cell "Delete runner-7" `); @@ -356,7 +366,7 @@ test.describe('Global runners', () => { test('runner details with all tasks visible on details page', async ({page}) => { await page.goto('/admin/actions/runners'); - await page.getByRole('link', {name: 'Show details of runner-4', exact: true}).click(); + await page.getByRole('link', {name: 'runner-4', exact: true}).click(); await expect(page).toHaveTitle(/^Runner runner-4 .*/); await expect(page.getByRole('heading', {name: 'Runner runner-4'})).toBeVisible(); @@ -437,11 +447,12 @@ test.describe('Global runners', () => { await page.getByRole('link', {name: 'List of runners', exact: true}).click(); await expect(page.locator(`tbody tr:has-text("${runnerUUID}")`)).toMatchAriaSnapshot(` - - cell "runner-473465 ${runnerUUID}" + - cell "runner-473465 ${runnerUUID}": + - link "runner-473465": + - /url: /admin/actions/runners/\\d+/ - cell "" - cell "Global" - cell "Offline" - - cell "Show details of runner-473465" - cell "Edit runner-473465" - cell "Delete runner-473465" `); @@ -546,22 +557,24 @@ test.describe('Organization runners', () => { // We cannot assert the length of the table because it's influenced by global fixtures. It also changes depending on // the ordering of tests. - await expect(rows.nth(0)).toHaveAccessibleName('Name Labels Type Status Details Edit Delete'); + await expect(rows.nth(0)).toHaveAccessibleName('Name Labels Type Status Edit Delete'); await expect(page.locator('tbody tr:has-text("8f940b0b-32a2-479a-9d48-06ab8d8a0b90")')).toMatchAriaSnapshot(` - - cell "runner-1 8f940b0b-32a2-479a-9d48-06ab8d8a0b90" + - cell "runner-1 8f940b0b-32a2-479a-9d48-06ab8d8a0b90": + - link "runner-1": + - /url: /org/org3/settings/actions/runners/719931 - cell "debian gpu" - cell "Organization" - cell "Offline" - - cell "Show details of runner-1" - cell "Edit runner-1" - cell "Delete runner-1" `); await expect(page.locator('tbody tr:has-text("1ef59b64-93b7-4ad4-ade4-21ca13db49c0")')).toMatchAriaSnapshot(` - - cell "runner-4 1ef59b64-93b7-4ad4-ade4-21ca13db49c0" + - cell "runner-4 1ef59b64-93b7-4ad4-ade4-21ca13db49c0": + - link "runner-4": + - /url: /org/org3/settings/actions/runners/719934 - cell "docker" - cell "Global" - cell "Offline" - - cell "Show details of runner-4" - cell - cell `); @@ -572,19 +585,19 @@ test.describe('Organization runners', () => { await expect(page.locator('tbody tr:has-text("d935307e-1d2d-4b61-8885-bc8a1c52c269")')).toBeHidden(); // Verify that details of usable runners are accessible. - await page.getByRole('link', {name: 'Show details of runner-1', exact: true}).click(); + await page.getByRole('link', {name: 'runner-1', exact: true}).click(); await expect(page).toHaveTitle(/^Runner runner-1 .*/); await page.goto('/org/org3/settings/actions/runners'); - await page.getByRole('link', {name: 'Show details of runner-4', exact: true}).click(); + await page.getByRole('link', {name: 'runner-4', exact: true}).click(); await expect(page).toHaveTitle(/^Runner runner-4 .*/); }); test('runner details with tasks of repositories owned by organization', async ({page}) => { await page.goto('/org/org3/settings/actions/runners'); - await page.getByRole('link', {name: 'Show details of runner-4', exact: true}).click(); + await page.getByRole('link', {name: 'runner-4', exact: true}).click(); await expect(page).toHaveTitle(/^Runner runner-4 .*/); await expect(page.getByRole('heading', {name: 'Runner runner-4'})).toBeVisible(); @@ -621,7 +634,7 @@ test.describe('Organization runners', () => { test('runner details with multiple pages of tasks', async ({page}) => { await page.goto('/org/org3/settings/actions/runners'); - await page.getByRole('link', {name: 'Show details of runner-1', exact: true}).click(); + await page.getByRole('link', {name: 'runner-1', exact: true}).click(); await expect(page).toHaveTitle(/^Runner runner-1 .*/); await expect(page.getByRole('heading', {name: 'Runner runner-1'})).toBeVisible(); @@ -664,31 +677,34 @@ test.describe('Repository runners', () => { // We cannot assert the length of the table because it's influenced by global fixtures. It also changes depending on // the ordering of tests. - await expect(rows.nth(0)).toHaveAccessibleName('Name Labels Type Status Details Edit Delete'); + await expect(rows.nth(0)).toHaveAccessibleName('Name Labels Type Status Edit Delete'); await expect(page.locator('tbody tr:has-text("3a20ad8d-d5d6-4b7b-ba55-841ac8264c17")')).toMatchAriaSnapshot(` - - cell "runner-2 3a20ad8d-d5d6-4b7b-ba55-841ac8264c17" + - cell "runner-2 3a20ad8d-d5d6-4b7b-ba55-841ac8264c17": + - link "runner-2": + - /url: /user2/test_workflows/settings/actions/runners/719932 - cell "docker" - cell "Individual" - cell "Offline" - - cell "Show details of runner-2" - cell - cell `); await expect(page.locator('tbody tr:has-text("1ef59b64-93b7-4ad4-ade4-21ca13db49c0")')).toMatchAriaSnapshot(` - - cell "runner-4 1ef59b64-93b7-4ad4-ade4-21ca13db49c0" + - cell "runner-4 1ef59b64-93b7-4ad4-ade4-21ca13db49c0": + - link "runner-4": + - /url: /user2/test_workflows/settings/actions/runners/719934 - cell "docker" - cell "Global" - cell "Offline" - - cell "Show details of runner-4" - cell - cell `); await expect(page.locator('tbody tr:has-text("9da25fbb-89a5-4520-a35a-d55fc94e4b76")')).toMatchAriaSnapshot(` - - cell "runner-6 9da25fbb-89a5-4520-a35a-d55fc94e4b76" + - cell "runner-6 9da25fbb-89a5-4520-a35a-d55fc94e4b76": + - link "runner-6": + - /url: /user2/test_workflows/settings/actions/runners/719936 - cell "debian" - cell "Repository" - cell "Offline" - - cell "Show details of runner-6" - cell "Edit runner-6" - cell "Delete runner-6" `); @@ -697,24 +713,24 @@ test.describe('Repository runners', () => { await expect(page.locator('tbody tr:has-text("d935307e-1d2d-4b61-8885-bc8a1c52c269")')).toBeHidden(); // Verify that details of usable runners are accessible. - await page.getByRole('link', {name: 'Show details of runner-2', exact: true}).click(); + await page.getByRole('link', {name: 'runner-2', exact: true}).click(); await expect(page).toHaveTitle(/^Runner runner-2 .*/); await page.goto('/user2/test_workflows/settings/actions/runners'); - await page.getByRole('link', {name: 'Show details of runner-4', exact: true}).click(); + await page.getByRole('link', {name: 'runner-4', exact: true}).click(); await expect(page).toHaveTitle(/^Runner runner-4 .*/); await page.goto('/user2/test_workflows/settings/actions/runners'); - await page.getByRole('link', {name: 'Show details of runner-6', exact: true}).click(); + await page.getByRole('link', {name: 'runner-6', exact: true}).click(); await expect(page).toHaveTitle(/^Runner runner-6 .*/); }); test('runner details with tasks of repository only', async ({page}) => { await page.goto('/user2/test_workflows/settings/actions/runners'); - await page.getByRole('link', {name: 'Show details of runner-4', exact: true}).click(); + await page.getByRole('link', {name: 'runner-4', exact: true}).click(); await expect(page).toHaveTitle(/^Runner runner-4 .*/); await expect(page.getByRole('heading', {name: 'Runner runner-4'})).toBeVisible();