diff --git a/models/actions/run_job.go b/models/actions/run_job.go
index 31f5f44a14..8096bfb6b5 100644
--- a/models/actions/run_job.go
+++ b/models/actions/run_job.go
@@ -6,15 +6,12 @@ package actions
import (
"context"
"fmt"
- "html/template"
"slices"
- "strings"
"time"
"forgejo.org/models/db"
"forgejo.org/modules/container"
"forgejo.org/modules/timeutil"
- "forgejo.org/modules/translation"
"forgejo.org/modules/util"
"code.forgejo.org/forgejo/runner/v12/act/jobparser"
@@ -237,27 +234,6 @@ func AggregateJobStatus(jobs []*ActionRunJob) Status {
}
}
-// StatusDiagnostics returns optional diagnostic information to display to the user derived from
-// ActionRunJob's current status. It should help the user understand in which state the
-// ActionRunJob is and why.
-func (job *ActionRunJob) StatusDiagnostics(lang translation.Locale) []template.HTML {
- diagnostics := []template.HTML{}
-
- switch job.Status {
- case StatusWaiting:
- joinedLabels := strings.Join(job.RunsOn, ", ")
- diagnostics = append(diagnostics, lang.TrPluralString(len(job.RunsOn), "actions.status.diagnostics.waiting", joinedLabels))
- default:
- diagnostics = append(diagnostics, template.HTML(job.Status.LocaleString(lang)))
- }
-
- if job.Run.NeedApproval {
- diagnostics = append(diagnostics, template.HTML(lang.TrString("actions.need_approval_desc")))
- }
-
- return diagnostics
-}
-
func (job *ActionRunJob) decodeWorkflowPayload() (*jobparser.SingleWorkflow, error) {
if job.workflowPayloadDecoded != nil {
return job.workflowPayloadDecoded, nil
diff --git a/models/actions/run_job_test.go b/models/actions/run_job_test.go
index acab662e60..9cba48705b 100644
--- a/models/actions/run_job_test.go
+++ b/models/actions/run_job_test.go
@@ -4,12 +4,10 @@ package actions
import (
"fmt"
- "html/template"
"testing"
"forgejo.org/models/db"
"forgejo.org/models/unittest"
- "forgejo.org/modules/translation"
"code.forgejo.org/forgejo/runner/v12/act/jobparser"
"github.com/stretchr/testify/assert"
@@ -74,82 +72,6 @@ func TestActionRunJob_HTMLURL(t *testing.T) {
}
}
-func TestActionRunJob_StatusDiagnostics(t *testing.T) {
- translation.InitLocales(t.Context())
- english := translation.NewLocale("en-US")
-
- tests := []struct {
- name string
- job ActionRunJob
- expected []template.HTML
- }{
- {
- name: "Unknown status",
- job: ActionRunJob{RunsOn: []string{"windows"}, Status: StatusUnknown, Run: &ActionRun{NeedApproval: false}},
- expected: []template.HTML{"Unknown"},
- },
- {
- name: "Waiting without labels",
- job: ActionRunJob{RunsOn: []string{}, Status: StatusWaiting, Run: &ActionRun{NeedApproval: false}},
- expected: []template.HTML{"Waiting for a runner with the following labels: "},
- },
- {
- name: "Waiting with one label",
- job: ActionRunJob{RunsOn: []string{"freebsd"}, Status: StatusWaiting, Run: &ActionRun{NeedApproval: false}},
- expected: []template.HTML{"Waiting for a runner with the following label: freebsd"},
- },
- {
- name: "Waiting with labels, no approval",
- job: ActionRunJob{RunsOn: []string{"docker", "ubuntu"}, Status: StatusWaiting, Run: &ActionRun{NeedApproval: false}},
- expected: []template.HTML{"Waiting for a runner with the following labels: docker, ubuntu"},
- },
- {
- name: "Waiting with labels, approval",
- job: ActionRunJob{RunsOn: []string{"docker", "ubuntu"}, Status: StatusWaiting, Run: &ActionRun{NeedApproval: true}},
- expected: []template.HTML{
- "Waiting for a runner with the following labels: docker, ubuntu",
- "Need approval to run workflows for fork pull request.",
- },
- },
- {
- name: "Running",
- job: ActionRunJob{RunsOn: []string{"debian"}, Status: StatusRunning, Run: &ActionRun{NeedApproval: false}},
- expected: []template.HTML{"Running"},
- },
- {
- name: "Success",
- job: ActionRunJob{RunsOn: []string{"debian"}, Status: StatusSuccess, Run: &ActionRun{NeedApproval: false}},
- expected: []template.HTML{"Success"},
- },
- {
- name: "Failure",
- job: ActionRunJob{RunsOn: []string{"debian"}, Status: StatusFailure, Run: &ActionRun{NeedApproval: false}},
- expected: []template.HTML{"Failure"},
- },
- {
- name: "Cancelled",
- job: ActionRunJob{RunsOn: []string{"debian"}, Status: StatusCancelled, Run: &ActionRun{NeedApproval: false}},
- expected: []template.HTML{"Canceled"},
- },
- {
- name: "Skipped",
- job: ActionRunJob{RunsOn: []string{"debian"}, Status: StatusSkipped, Run: &ActionRun{NeedApproval: false}},
- expected: []template.HTML{"Skipped"},
- },
- {
- name: "Blocked",
- job: ActionRunJob{RunsOn: []string{"debian"}, Status: StatusBlocked, Run: &ActionRun{NeedApproval: false}},
- expected: []template.HTML{"Blocked"},
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- assert.Equal(t, tt.expected, tt.job.StatusDiagnostics(english))
- })
- }
-}
-
func TestActionRunJob_IsIncompleteMatrix(t *testing.T) {
tests := []struct {
name string
diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go
index 8ba701bb28..9b36f7b8cb 100644
--- a/routers/web/repo/actions/view.go
+++ b/routers/web/repo/actions/view.go
@@ -32,6 +32,7 @@ import (
"forgejo.org/modules/storage"
"forgejo.org/modules/templates"
"forgejo.org/modules/timeutil"
+ "forgejo.org/modules/translation"
"forgejo.org/modules/util"
"forgejo.org/modules/web"
"forgejo.org/routers/common"
@@ -241,9 +242,10 @@ type ViewStepLogLine struct {
}
type TaskAttempt struct {
- Number int64 `json:"number"`
- Started template.HTML `json:"time_since_started_html"`
- Status string `json:"status"`
+ Number int64 `json:"number"`
+ Started template.HTML `json:"time_since_started_html"`
+ Status string `json:"status"`
+ StatusDiagnostics []template.HTML `json:"status_diagnostics"`
}
func ViewPost(ctx *app_context.Context) {
@@ -358,7 +360,7 @@ func getViewResponse(ctx *app_context.Context, req *ViewRequest, runIndex, jobIn
}
resp.State.CurrentJob.Title = current.Name
- resp.State.CurrentJob.Details = current.StatusDiagnostics(ctx.Locale)
+ resp.State.CurrentJob.Details = statusDiagnostics(current.Status, current, ctx.Locale)
resp.State.CurrentJob.Steps = make([]*ViewJobStep, 0) // marshal to '[]' instead of 'null' in json
resp.Logs.StepsLog = make([]*ViewStepLog, 0) // marshal to '[]' instead of 'null' in json
@@ -372,9 +374,10 @@ func getViewResponse(ctx *app_context.Context, req *ViewRequest, runIndex, jobIn
allAttempts := make([]*TaskAttempt, len(taskAttempts))
for i, actionTask := range taskAttempts {
allAttempts[i] = &TaskAttempt{
- Number: actionTask.Attempt,
- Started: templates.TimeSince(actionTask.Started),
- Status: actionTask.Status.String(),
+ Number: actionTask.Attempt,
+ Started: templates.TimeSince(actionTask.Started),
+ Status: actionTask.Status.String(),
+ StatusDiagnostics: statusDiagnostics(actionTask.Status, task.Job, ctx.Locale),
}
}
resp.State.CurrentJob.AllAttempts = allAttempts
@@ -968,3 +971,24 @@ func disableOrEnableWorkflowFile(ctx *app_context.Context, isEnable bool) {
url.QueryEscape(ctx.FormString("actor")), url.QueryEscape(ctx.FormString("status")))
ctx.JSONRedirect(redirectURL)
}
+
+// statusDiagnostics returns optional diagnostic information to display to the user. It should help the user understand
+// what the current Status means and whether an action needs to be performed, for example, approving a job.
+func statusDiagnostics(status actions_model.Status, job *actions_model.ActionRunJob, lang translation.Locale) []template.HTML {
+ // Initialize as empty container for it to be serialized to an empty JSON array, not `null`.
+ diagnostics := []template.HTML{}
+
+ switch status {
+ case actions_model.StatusWaiting:
+ joinedLabels := strings.Join(job.RunsOn, ", ")
+ diagnostics = append(diagnostics, lang.TrPluralString(len(job.RunsOn), "actions.status.diagnostics.waiting", joinedLabels))
+ default:
+ diagnostics = append(diagnostics, template.HTML(status.LocaleString(lang)))
+ }
+
+ if job.Run.NeedApproval {
+ diagnostics = append(diagnostics, template.HTML(lang.TrString("actions.need_approval_desc")))
+ }
+
+ return diagnostics
+}
diff --git a/routers/web/repo/actions/view_test.go b/routers/web/repo/actions/view_test.go
index dfdd11f225..6e15d11df4 100644
--- a/routers/web/repo/actions/view_test.go
+++ b/routers/web/repo/actions/view_test.go
@@ -13,6 +13,7 @@ import (
repo_model "forgejo.org/models/repo"
unittest_model "forgejo.org/models/unittest"
"forgejo.org/modules/json"
+ "forgejo.org/modules/translation"
"forgejo.org/modules/web"
"forgejo.org/services/contexttest"
@@ -20,7 +21,7 @@ import (
"github.com/stretchr/testify/require"
)
-func Test_getRunByID(t *testing.T) {
+func TestActionsViewGetRunByID(t *testing.T) {
unittest_model.PrepareTestEnv(t)
repo := unittest_model.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: 5, ID: 4})
@@ -61,7 +62,7 @@ func Test_getRunByID(t *testing.T) {
}
}
-func Test_artifactsFind(t *testing.T) {
+func TestActionsViewArtifactsFind(t *testing.T) {
unittest_model.PrepareTestEnv(t)
for _, testCase := range []struct {
@@ -93,7 +94,7 @@ func Test_artifactsFind(t *testing.T) {
}
}
-func Test_artifactsFindByNameOrID(t *testing.T) {
+func TestActionsViewArtifactsFindByNameOrID(t *testing.T) {
unittest_model.PrepareTestEnv(t)
for _, testCase := range []struct {
@@ -195,19 +196,22 @@ func baseExpectedViewResponse() *ViewResponse {
},
AllAttempts: []*TaskAttempt{
{
- Number: 3,
- Started: template.HTML("2023-05-09 12:48:48 +00:00"),
- Status: "running",
+ Number: 3,
+ Started: template.HTML("2023-05-09 12:48:48 +00:00"),
+ Status: "running",
+ StatusDiagnostics: []template.HTML{"actions.status.running"},
},
{
- Number: 2,
- Started: template.HTML("2023-05-09 12:48:48 +00:00"),
- Status: "success",
+ Number: 2,
+ Started: template.HTML("2023-05-09 12:48:48 +00:00"),
+ Status: "success",
+ StatusDiagnostics: []template.HTML{"actions.status.success"},
},
{
- Number: 1,
- Started: template.HTML("2023-05-09 12:48:48 +00:00"),
- Status: "success",
+ Number: 1,
+ Started: template.HTML("2023-05-09 12:48:48 +00:00"),
+ Status: "success",
+ StatusDiagnostics: []template.HTML{"actions.status.success"},
},
},
},
@@ -281,9 +285,10 @@ func TestActionsViewViewPost(t *testing.T) {
}
resp.State.CurrentJob.AllAttempts = []*TaskAttempt{
{
- Number: 1,
- Started: template.HTML("2023-05-09 12:48:48 +00:00"),
- Status: "success",
+ Number: 1,
+ Started: template.HTML("2023-05-09 12:48:48 +00:00"),
+ Status: "success",
+ StatusDiagnostics: []template.HTML{"actions.status.success"},
},
}
@@ -510,3 +515,91 @@ func TestActionsRerun(t *testing.T) {
})
}
}
+
+func TestActionsViewStatusDiagnostics(t *testing.T) {
+ translation.InitLocales(t.Context())
+ english := translation.NewLocale("en-US")
+
+ testCases := []struct {
+ name string
+ status actions_model.Status
+ job actions_model.ActionRunJob
+ expected []template.HTML
+ }{
+ {
+ name: "Unknown status",
+ status: actions_model.StatusUnknown,
+ job: actions_model.ActionRunJob{RunsOn: []string{"windows"}, Run: &actions_model.ActionRun{NeedApproval: false}},
+ expected: []template.HTML{"Unknown"},
+ },
+ {
+ name: "Waiting without labels",
+ status: actions_model.StatusWaiting,
+ job: actions_model.ActionRunJob{RunsOn: []string{}, Run: &actions_model.ActionRun{NeedApproval: false}},
+ expected: []template.HTML{"Waiting for a runner with the following labels: "},
+ },
+ {
+ name: "Waiting with one label",
+ status: actions_model.StatusWaiting,
+ job: actions_model.ActionRunJob{RunsOn: []string{"freebsd"}, Run: &actions_model.ActionRun{NeedApproval: false}},
+ expected: []template.HTML{"Waiting for a runner with the following label: freebsd"},
+ },
+ {
+ name: "Waiting with labels, no approval",
+ status: actions_model.StatusWaiting,
+ job: actions_model.ActionRunJob{RunsOn: []string{"docker", "ubuntu"}, Run: &actions_model.ActionRun{NeedApproval: false}},
+ expected: []template.HTML{"Waiting for a runner with the following labels: docker, ubuntu"},
+ },
+ {
+ name: "Waiting with labels, approval",
+ status: actions_model.StatusWaiting,
+ job: actions_model.ActionRunJob{RunsOn: []string{"docker", "ubuntu"}, Run: &actions_model.ActionRun{NeedApproval: true}},
+ expected: []template.HTML{
+ "Waiting for a runner with the following labels: docker, ubuntu",
+ "Need approval to run workflows for fork pull request.",
+ },
+ },
+ {
+ name: "Running",
+ status: actions_model.StatusRunning,
+ job: actions_model.ActionRunJob{RunsOn: []string{"debian"}, Run: &actions_model.ActionRun{NeedApproval: false}},
+ expected: []template.HTML{"Running"},
+ },
+ {
+ name: "Success",
+ status: actions_model.StatusSuccess,
+ job: actions_model.ActionRunJob{RunsOn: []string{"debian"}, Run: &actions_model.ActionRun{NeedApproval: false}},
+ expected: []template.HTML{"Success"},
+ },
+ {
+ name: "Failure",
+ status: actions_model.StatusFailure,
+ job: actions_model.ActionRunJob{RunsOn: []string{"debian"}, Run: &actions_model.ActionRun{NeedApproval: false}},
+ expected: []template.HTML{"Failure"},
+ },
+ {
+ name: "Cancelled",
+ status: actions_model.StatusCancelled,
+ job: actions_model.ActionRunJob{RunsOn: []string{"debian"}, Run: &actions_model.ActionRun{NeedApproval: false}},
+ expected: []template.HTML{"Canceled"},
+ },
+ {
+ name: "Skipped",
+ status: actions_model.StatusSkipped,
+ job: actions_model.ActionRunJob{RunsOn: []string{"debian"}, Run: &actions_model.ActionRun{NeedApproval: false}},
+ expected: []template.HTML{"Skipped"},
+ },
+ {
+ name: "Blocked",
+ status: actions_model.StatusBlocked,
+ job: actions_model.ActionRunJob{RunsOn: []string{"debian"}, Run: &actions_model.ActionRun{NeedApproval: false}},
+ expected: []template.HTML{"Blocked"},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ assert.Equal(t, testCase.expected, statusDiagnostics(testCase.status, &testCase.job, english))
+ })
+ }
+}
diff --git a/tests/integration/actions_view_test.go b/tests/integration/actions_view_test.go
index 8ccaed8f7b..b3f40f17ff 100644
--- a/tests/integration/actions_view_test.go
+++ b/tests/integration/actions_view_test.go
@@ -153,7 +153,7 @@ func TestActionViewsView(t *testing.T) {
re = regexp.MustCompile(pattern)
actualClean = re.ReplaceAllString(actualClean, `"time_since_started_html":"_time_"`)
- return assert.JSONEq(t, "{\"state\":{\"run\":{\"preExecutionError\":\"\",\"link\":\"/user5/repo4/actions/runs/187\",\"title\":\"update actions\",\"titleHTML\":\"update actions\",\"status\":\"success\",\"canCancel\":false,\"canApprove\":false,\"canRerun\":false,\"canDeleteArtifact\":false,\"done\":true,\"jobs\":[{\"id\":192,\"name\":\"job_2\",\"status\":\"success\",\"canRerun\":false,\"duration\":\"_duration_\"}],\"commit\":{\"localeCommit\":\"Commit\",\"localePushedBy\":\"pushed by\",\"localeWorkflow\":\"Workflow\",\"shortSHA\":\"c2d72f5484\",\"link\":\"/user5/repo4/commit/c2d72f548424103f01ee1dc02889c1e2bff816b0\",\"pusher\":{\"displayName\":\"user1\",\"link\":\"/user1\"},\"branch\":{\"name\":\"master\",\"link\":\"/user5/repo4/src/branch/master\",\"isDeleted\":false}}},\"currentJob\":{\"title\":\"job_2\",\"details\":[\"Success\"],\"steps\":[{\"summary\":\"Set up job\",\"duration\":\"_duration_\",\"status\":\"success\"},{\"summary\":\"Complete job\",\"duration\":\"_duration_\",\"status\":\"success\"}],\"allAttempts\":[{\"number\":3,\"time_since_started_html\":\"_time_\",\"status\":\"running\"},{\"number\":2,\"time_since_started_html\":\"_time_\",\"status\":\"success\"},{\"number\":1,\"time_since_started_html\":\"_time_\",\"status\":\"success\"}]}},\"logs\":{\"stepsLog\":[]}}\n", actualClean)
+ return assert.JSONEq(t, "{\"state\":{\"run\":{\"preExecutionError\":\"\",\"link\":\"/user5/repo4/actions/runs/187\",\"title\":\"update actions\",\"titleHTML\":\"update actions\",\"status\":\"success\",\"canCancel\":false,\"canApprove\":false,\"canRerun\":false,\"canDeleteArtifact\":false,\"done\":true,\"jobs\":[{\"id\":192,\"name\":\"job_2\",\"status\":\"success\",\"canRerun\":false,\"duration\":\"_duration_\"}],\"commit\":{\"localeCommit\":\"Commit\",\"localePushedBy\":\"pushed by\",\"localeWorkflow\":\"Workflow\",\"shortSHA\":\"c2d72f5484\",\"link\":\"/user5/repo4/commit/c2d72f548424103f01ee1dc02889c1e2bff816b0\",\"pusher\":{\"displayName\":\"user1\",\"link\":\"/user1\"},\"branch\":{\"name\":\"master\",\"link\":\"/user5/repo4/src/branch/master\",\"isDeleted\":false}}},\"currentJob\":{\"title\":\"job_2\",\"details\":[\"Success\"],\"steps\":[{\"summary\":\"Set up job\",\"duration\":\"_duration_\",\"status\":\"success\"},{\"summary\":\"Complete job\",\"duration\":\"_duration_\",\"status\":\"success\"}],\"allAttempts\":[{\"number\":3,\"time_since_started_html\":\"_time_\",\"status\":\"running\",\"status_diagnostics\":[\"Running\"]},{\"number\":2,\"time_since_started_html\":\"_time_\",\"status\":\"success\",\"status_diagnostics\":[\"Success\"]},{\"number\":1,\"time_since_started_html\":\"_time_\",\"status\":\"success\",\"status_diagnostics\":[\"Success\"]}]}},\"logs\":{\"stepsLog\":[]}}\n", actualClean)
})
htmlDoc.AssertAttrEqual(t, selector, "data-initial-artifacts-response", "{\"artifacts\":[{\"name\":\"multi-file-download\",\"size\":2048,\"status\":\"completed\"}]}\n")
}
diff --git a/web_src/js/components/RepoActionView.test.js b/web_src/js/components/RepoActionView.test.js
index 71ea39a9c2..472763541b 100644
--- a/web_src/js/components/RepoActionView.test.js
+++ b/web_src/js/components/RepoActionView.test.js
@@ -94,6 +94,7 @@ test('processes ##[group] and ##[endgroup]', async () => {
},
},
currentJob: {
+ title: 'Test',
steps: [
{
summary: 'Test Job',
@@ -101,7 +102,7 @@ test('processes ##[group] and ##[endgroup]', async () => {
status: 'success',
},
],
- allAttempts: [{number: 1, time_since_started_html: '', status: 'success'}],
+ allAttempts: [{number: 1, time_since_started_html: '', status: 'success', status_diagnostics: ['Success']}],
},
},
logs: {
@@ -186,6 +187,7 @@ test('load multiple steps on a finished action', async () => {
},
},
currentJob: {
+ title: 'test',
steps: [
{
summary: 'Test Step #1',
@@ -198,7 +200,7 @@ test('load multiple steps on a finished action', async () => {
status: 'success',
},
],
- allAttempts: [{number: 1, time_since_started_html: '', status: 'success'}],
+ allAttempts: [{number: 1, time_since_started_html: '', status: 'success', status_diagnostics: ['Success']}],
},
},
logs: {
@@ -231,6 +233,11 @@ test('load multiple steps on a finished action', async () => {
expect(wrapper.get('.job-step-section:nth-of-type(2) .job-log-line:nth-of-type(1) .log-msg').text()).toEqual('Step #2 Log #1');
expect(wrapper.get('.job-step-section:nth-of-type(2) .job-log-line:nth-of-type(2) .log-msg').text()).toEqual('Step #2 Log #2');
expect(wrapper.get('.job-step-section:nth-of-type(2) .job-log-line:nth-of-type(3) .log-msg').text()).toEqual('Step #2 Log #3');
+
+ // Attempt status
+ expect(wrapper.get('.job-info-header h3').text()).toEqual('test');
+ expect(wrapper.findAll('ul.job-info-header-detail li').length).toEqual(1);
+ expect(wrapper.get('ul.job-info-header-detail li:nth-child(1)').text()).toEqual('Success');
});
function configureForMultipleAttemptTests({viewHistorical}) {
@@ -246,6 +253,7 @@ function configureForMultipleAttemptTests({viewHistorical}) {
},
},
currentJob: {
+ title: 'test',
steps: [
{
summary: 'Test Job',
@@ -254,8 +262,8 @@ function configureForMultipleAttemptTests({viewHistorical}) {
},
],
allAttempts: [
- {number: 2, time_since_started_html: 'yesterday', status: 'success'},
- {number: 1, time_since_started_html: 'two days ago', status: 'failure'},
+ {number: 2, time_since_started_html: 'yesterday', status: 'success', status_diagnostics: ['Success']},
+ {number: 1, time_since_started_html: 'two days ago', status: 'failure', status_diagnostics: ['Failure']},
],
},
};
@@ -315,6 +323,11 @@ test('display baseline with most-recent attempt', async () => {
expect(wrapper.findAll('.job-attempt-dropdown').length).toEqual(1);
expect(wrapper.findAll('.job-attempt-dropdown .svg.octicon-check-circle-fill.text.green').length).toEqual(1);
expect(wrapper.get('.job-attempt-dropdown .ui.dropdown').text()).toEqual('Run attempt 2 yesterday');
+
+ // Attempt status
+ expect(wrapper.get('.job-info-header h3').text()).toEqual('test');
+ expect(wrapper.findAll('ul.job-info-header-detail li').length).toEqual(1);
+ expect(wrapper.get('ul.job-info-header-detail li:nth-child(1)').text()).toEqual('Success');
});
test('display reconfigured for historical attempt', async () => {
@@ -342,6 +355,11 @@ test('display reconfigured for historical attempt', async () => {
expect(wrapper.findAll('.job-attempt-dropdown').length).toEqual(1);
expect(wrapper.findAll('.job-attempt-dropdown .svg.octicon-x-circle-fill.text.red').length).toEqual(1);
expect(wrapper.get('.job-attempt-dropdown .ui.dropdown').text()).toEqual('Run attempt 1 two days ago');
+
+ // Attempt status
+ expect(wrapper.get('.job-info-header h3').text()).toEqual('test');
+ expect(wrapper.findAll('ul.job-info-header-detail li').length).toEqual(1);
+ expect(wrapper.get('ul.job-info-header-detail li:nth-child(1)').text()).toEqual('Failure');
});
test('historical attempt dropdown interactions', async () => {
@@ -476,6 +494,7 @@ test('artifacts download links', async () => {
},
},
currentJob: {
+ title: 'test',
steps: [
{
summary: 'Test Step #1',
@@ -483,7 +502,7 @@ test('artifacts download links', async () => {
status: 'success',
},
],
- allAttempts: [{number: 1, time_since_started_html: '', status: 'success'}],
+ allAttempts: [{number: 1, time_since_started_html: '', status: 'success', status_diagnostics: ['Success']}],
},
},
logs: {
@@ -628,6 +647,11 @@ test('view non-picked action run job', async () => {
expect(wrapper.get('.job-brief-list .job-brief-item:nth-of-type(1) .job-brief-name').text()).toEqual('check-1');
expect(wrapper.get('.job-brief-list .job-brief-item:nth-of-type(2) .job-brief-name').text()).toEqual('check-2');
expect(wrapper.get('.job-brief-list .job-brief-item:nth-of-type(3) .job-brief-name').text()).toEqual('check-3');
+
+ // Attempt status
+ expect(wrapper.get('.job-info-header h3').text()).toEqual('check-1');
+ expect(wrapper.findAll('ul.job-info-header-detail li').length).toEqual(1);
+ expect(wrapper.get('ul.job-info-header-detail li:nth-child(1)').text()).toEqual('waiting (locale)');
});
test('view without pre-execution error', async () => {
@@ -686,6 +710,7 @@ test('Offset index', async () => {
},
},
currentJob: {
+ title: 'test',
steps: [
{
summary: 'Test Job',
@@ -693,7 +718,7 @@ test('Offset index', async () => {
status: 'success',
},
],
- allAttempts: [{number: 1, time_since_started_html: '', status: 'success'}],
+ allAttempts: [{number: 1, time_since_started_html: '', status: 'success', status_diagnostics: ['Success']}],
},
},
logs: {
diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue
index 8d2d82d022..e79dae7078 100644
--- a/web_src/js/components/RepoActionView.vue
+++ b/web_src/js/components/RepoActionView.vue
@@ -123,7 +123,7 @@ export default {
// initial value here is configured so that currentingViewingMostRecentAttempt() -> true on the default `data()`, so that the
// initial render (before `loadJob`'s first execution is complete) doesn't display "You are viewing an
// out-of-date run..."
- allAttempts: new Array(parseInt(this.attemptNumber)).fill({index: 0, time_since_started_html: '', status: 'success'}),
+ allAttempts: new Array(parseInt(this.attemptNumber)).fill({index: 0, time_since_started_html: '', status: 'success', status_diagnostics: []}),
},
};
},
@@ -158,6 +158,7 @@ export default {
if (!this.currentJob.allAttempts) {
return fallback;
}
+
const attempt = this.currentJob.allAttempts.find((attempt) => attempt.number === this.viewingAttemptNumber);
return attempt || fallback;
},
@@ -181,6 +182,18 @@ export default {
return this.locale.viewingOutOfDateRun
.replace('%[1]s', this.viewingAttempt.time_since_started_html);
},
+
+ statusDiagnostics() {
+ if (!this.currentJob.allAttempts) {
+ return this.currentJob.details;
+ }
+
+ const useAttempt = this.currentJob.allAttempts.some((attempt) => attempt.number === this.viewingAttemptNumber);
+ if (useAttempt) {
+ return this.viewingAttempt.status_diagnostics;
+ }
+ return this.currentJob.details;
+ },
},
async mounted() {
@@ -616,7 +629,7 @@ export default {
{{ currentJob.title }}