mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-03-25 18:13:03 -04:00
Fixes #11036. This adds a link from a CI run to the file that its workflow was taken from. | Before | After | |---------|---------| |  |  | Before: * the `test.yml` link points to the list of other runs (`/org123/repo2/actions?workflow=test.yml`) After: * the `test.yml` link points to the workflow definition (`/org123/repo2/src/commit/55b048363c8cfa7d9e8b5cade5c75681bd0c7328/.forgejo/workflows/test.yml`) * the `all runs` link points to the list of other runs (`/org123/repo2/actions?workflow=test.yml`) I have tried to retain the existing link to the list of workflow runs (moving it to a separate link), but I am not sure if this link should be retained at all and if so how. ## Checklist ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [x] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change. - [ ] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change. <!--start release-notes-assistant--> ## Release notes <!--URL:https://codeberg.org/forgejo/forgejo--> - Features - [PR](https://codeberg.org/forgejo/forgejo/pulls/11216): <!--number 11216 --><!--line 0 --><!--description bGluayBDSSBqb2IgdG8gaXRzIGRlZmluaW5nIHdvcmtmbG93IGZpbGU=-->link CI job to its defining workflow file<!--description--> <!--end release-notes-assistant--> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11216 Reviewed-by: Andreas Ahlenstorf <aahlenst@noreply.codeberg.org> Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org> Co-authored-by: Antonin Delpeuch <antonin@delpeuch.eu> Co-committed-by: Antonin Delpeuch <antonin@delpeuch.eu>
610 lines
21 KiB
JavaScript
610 lines
21 KiB
JavaScript
import {mount, flushPromises} from '@vue/test-utils';
|
|
import {toAbsoluteUrl} from '../utils.js';
|
|
import RepoActionView from './RepoActionView.vue';
|
|
|
|
const testLocale = {
|
|
approve: 'Locale Approve',
|
|
cancel: 'Locale Cancel',
|
|
rerun: 'Locale Re-run',
|
|
artifactsTitle: 'artifactTitleHere',
|
|
areYouSure: '',
|
|
confirmDeleteArtifact: '',
|
|
rerun_all: '',
|
|
showTimeStamps: '',
|
|
showLogSeconds: '',
|
|
showFullScreen: '',
|
|
downloadLogs: '',
|
|
runAttemptLabel: 'Run attempt %[1]s %[2]s',
|
|
viewingOutOfDateRun: 'oh no, out of date since %[1]s give or take or so',
|
|
viewMostRecentRun: '',
|
|
preExecutionError: 'pre-execution error',
|
|
status: {
|
|
unknown: '',
|
|
waiting: '',
|
|
running: '',
|
|
success: '',
|
|
failure: '',
|
|
cancelled: '',
|
|
skipped: '',
|
|
blocked: '',
|
|
},
|
|
};
|
|
const minimalInitialJobData = {
|
|
state: {
|
|
run: {
|
|
status: 'success',
|
|
commit: {
|
|
pusher: {},
|
|
},
|
|
},
|
|
currentJob: {
|
|
steps: [
|
|
{
|
|
summary: 'Test Job',
|
|
duration: '1s',
|
|
status: 'success',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
logs: {
|
|
stepsLog: [],
|
|
},
|
|
};
|
|
const minimalInitialArtifactData = {
|
|
artifacts: [],
|
|
};
|
|
const defaultTestProps = {
|
|
actionsURL: 'https://example.com/example-org/example-repo/actions',
|
|
jobIndex: '1',
|
|
attemptNumber: '1',
|
|
runIndex: '10',
|
|
runID: '1001',
|
|
initialJobData: minimalInitialJobData,
|
|
initialArtifactData: minimalInitialArtifactData,
|
|
locale: testLocale,
|
|
workflowName: 'workflow name',
|
|
workflowURL: 'https://example.com/example-org/example-repo/actions?workflow=test.yml',
|
|
workflowSourceURL: 'https://example.com/example-org/example-repo/src/commit/023babec384/.forgejo/workflows/test.yml',
|
|
};
|
|
|
|
test('load multiple steps on a finished action', async () => {
|
|
Object.defineProperty(document.documentElement, 'lang', {value: 'en'});
|
|
vi.spyOn(global, 'fetch').mockImplementation((url, opts) => {
|
|
if (url.endsWith('/artifacts')) {
|
|
return Promise.resolve({
|
|
ok: true,
|
|
json: vi.fn().mockResolvedValue(
|
|
{
|
|
artifacts: [],
|
|
},
|
|
),
|
|
});
|
|
}
|
|
|
|
const postBody = JSON.parse(opts.body);
|
|
const stepsLog_value = [];
|
|
for (const cursor of postBody.logCursors) {
|
|
if (cursor.expanded) {
|
|
stepsLog_value.push(
|
|
{
|
|
step: cursor.step,
|
|
cursor: 0,
|
|
lines: [
|
|
{index: 1, message: `Step #${cursor.step + 1} Log #1`, timestamp: 0},
|
|
{index: 1, message: `Step #${cursor.step + 1} Log #2`, timestamp: 0},
|
|
{index: 1, message: `Step #${cursor.step + 1} Log #3`, timestamp: 0},
|
|
],
|
|
},
|
|
);
|
|
}
|
|
}
|
|
const jobs_value = {
|
|
state: {
|
|
run: {
|
|
status: 'success',
|
|
commit: {
|
|
pusher: {},
|
|
},
|
|
},
|
|
currentJob: {
|
|
title: 'test',
|
|
steps: [
|
|
{
|
|
summary: 'Test Step #1',
|
|
duration: '1s',
|
|
status: 'success',
|
|
},
|
|
{
|
|
summary: 'Test Step #2',
|
|
duration: '1s',
|
|
status: 'success',
|
|
},
|
|
],
|
|
allAttempts: [{number: 1, time_since_started_html: '', status: 'success', status_diagnostics: ['Success']}],
|
|
},
|
|
},
|
|
logs: {
|
|
stepsLog: opts.body?.includes('"cursor":null') ? stepsLog_value : [],
|
|
},
|
|
};
|
|
|
|
return Promise.resolve({
|
|
ok: true,
|
|
json: vi.fn().mockResolvedValue(
|
|
jobs_value,
|
|
),
|
|
});
|
|
});
|
|
|
|
const wrapper = mount(RepoActionView, {
|
|
props: defaultTestProps,
|
|
});
|
|
wrapper.vm.loadJob(); // simulate intermittent reload immediately so UI switches from minimalInitialJobData to the mock data from the test's fetch spy.
|
|
await flushPromises();
|
|
// Click on both steps to start their log loading in fast succession...
|
|
await wrapper.get('.job-step-section:nth-of-type(1) .job-step-summary').trigger('click');
|
|
await wrapper.get('.job-step-section:nth-of-type(2) .job-step-summary').trigger('click');
|
|
await flushPromises();
|
|
|
|
// Verify both step's logs were loaded
|
|
expect(wrapper.get('.job-step-section:nth-of-type(1) .job-log-line:nth-of-type(1) .log-msg').text()).toEqual('Step #1 Log #1');
|
|
expect(wrapper.get('.job-step-section:nth-of-type(1) .job-log-line:nth-of-type(2) .log-msg').text()).toEqual('Step #1 Log #2');
|
|
expect(wrapper.get('.job-step-section:nth-of-type(1) .job-log-line:nth-of-type(3) .log-msg').text()).toEqual('Step #1 Log #3');
|
|
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}) {
|
|
Object.defineProperty(document.documentElement, 'lang', {value: 'en'});
|
|
const myJobState = {
|
|
run: {
|
|
canApprove: true,
|
|
canCancel: true,
|
|
canRerun: true,
|
|
status: 'success',
|
|
commit: {
|
|
pusher: {},
|
|
},
|
|
},
|
|
currentJob: {
|
|
title: 'test',
|
|
steps: [
|
|
{
|
|
summary: 'Test Job',
|
|
duration: '1s',
|
|
status: 'success',
|
|
},
|
|
],
|
|
allAttempts: [
|
|
{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']},
|
|
],
|
|
},
|
|
};
|
|
vi.spyOn(global, 'fetch').mockImplementation((url, opts) => {
|
|
const artifacts_value = {
|
|
artifacts: [],
|
|
};
|
|
const stepsLog_value = [
|
|
{
|
|
step: 0,
|
|
cursor: 0,
|
|
lines: [],
|
|
},
|
|
];
|
|
const jobs_value = {
|
|
state: myJobState,
|
|
logs: {
|
|
stepsLog: opts.body?.includes('"cursor":null') ? stepsLog_value : [],
|
|
},
|
|
};
|
|
|
|
return Promise.resolve({
|
|
ok: true,
|
|
json: vi.fn().mockResolvedValue(
|
|
url.endsWith('/artifacts') ? artifacts_value : jobs_value,
|
|
),
|
|
});
|
|
});
|
|
|
|
const wrapper = mount(RepoActionView, {
|
|
props: {
|
|
...defaultTestProps,
|
|
runIndex: '123',
|
|
attemptNumber: viewHistorical ? '1' : '2',
|
|
actionsURL: toAbsoluteUrl('/user1/repo2/actions'),
|
|
initialJobData: {...minimalInitialJobData, state: myJobState},
|
|
},
|
|
});
|
|
return wrapper;
|
|
}
|
|
|
|
test('display baseline with most-recent attempt', async () => {
|
|
const wrapper = configureForMultipleAttemptTests({viewHistorical: false});
|
|
await flushPromises();
|
|
|
|
// Warning dialog for viewing an out-of-date attempt...
|
|
expect(wrapper.findAll('.job-out-of-date-warning').length).toEqual(0);
|
|
|
|
// Approve button should be visible; can't have all three at once but at least this verifies the inverse of the
|
|
// historical attempt test below.
|
|
expect(wrapper.findAll('button').filter((button) => button.text() === 'Locale Approve').length).toEqual(1);
|
|
|
|
// Job list will be visible...
|
|
expect(wrapper.findAll('.job-group-section').length).toEqual(1);
|
|
|
|
// Attempt selector dropdown...
|
|
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 () => {
|
|
const wrapper = configureForMultipleAttemptTests({viewHistorical: true});
|
|
await flushPromises();
|
|
|
|
// Warning dialog for viewing an out-of-date attempt...
|
|
expect(wrapper.findAll('.job-out-of-date-warning').length).toEqual(1);
|
|
expect(wrapper.get('.job-out-of-date-warning').text()).toEqual('oh no, out of date since two days ago give or take or so');
|
|
await wrapper.get('.job-out-of-date-warning button').trigger('click');
|
|
expect(window.location.href).toEqual(toAbsoluteUrl('/user1/repo2/actions/runs/123/jobs/1'));
|
|
// eslint-disable-next-line no-restricted-globals
|
|
history.back();
|
|
await flushPromises();
|
|
|
|
// Approve, Cancel, Re-run all buttons should all be suppressed...
|
|
expect(wrapper.findAll('button').filter((button) => button.text() === 'Locale Approve').length).toEqual(0);
|
|
expect(wrapper.findAll('button').filter((button) => button.text() === 'Locale Cancel').length).toEqual(0);
|
|
expect(wrapper.findAll('button').filter((button) => button.text() === 'Locale Re-run').length).toEqual(0);
|
|
|
|
// Job list will be suppressed...
|
|
expect(wrapper.findAll('.job-group-section').length).toEqual(0);
|
|
|
|
// Attempt selector dropdown...
|
|
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 () => {
|
|
const wrapper = configureForMultipleAttemptTests({viewHistorical: true});
|
|
await flushPromises();
|
|
|
|
// Check dropdown exists, but isn't expanded.
|
|
const attemptsNotExpanded = () => {
|
|
expect(wrapper.findAll('.job-attempt-dropdown').length).toEqual(1);
|
|
expect(wrapper.findAll('.job-attempt-dropdown .action-job-menu').length).toEqual(0, 'dropdown content not yet visible');
|
|
};
|
|
attemptsNotExpanded();
|
|
|
|
// Click on attempt dropdown
|
|
wrapper.get('.job-attempt-dropdown .ui.dropdown').trigger('click');
|
|
await flushPromises();
|
|
|
|
// Check dropdown is expanded and both options are displayed
|
|
const attemptsExpanded = () => {
|
|
expect(wrapper.findAll('.job-attempt-dropdown .action-job-menu').length).toEqual(1);
|
|
expect(wrapper.get('.job-attempt-dropdown .action-job-menu').isVisible()).toBe(true);
|
|
expect(wrapper.findAll('.job-attempt-dropdown .action-job-menu a').filter((a) => a.text() === 'Run attempt 2 yesterday').length).toEqual(1);
|
|
expect(wrapper.findAll('.job-attempt-dropdown .action-job-menu a').filter((a) => a.text() === 'Run attempt 1 two days ago').length).toEqual(1);
|
|
};
|
|
attemptsExpanded();
|
|
|
|
// Normally dismiss occurs on a body click event; simulate that by calling `closeDropdown()`
|
|
wrapper.vm.closeDropdown();
|
|
await flushPromises();
|
|
|
|
// Should return to not expanded.
|
|
attemptsNotExpanded();
|
|
|
|
// Click on the gear dropdown
|
|
wrapper.get('.job-gear-dropdown').trigger('click');
|
|
await flushPromises();
|
|
|
|
// Check that gear's menu is expanded, and attempt dropdown isn't.
|
|
expect(wrapper.findAll('.job-gear-dropdown .action-job-menu').length).toEqual(1);
|
|
expect(wrapper.get('.job-gear-dropdown .action-job-menu').isVisible()).toBe(true);
|
|
attemptsNotExpanded();
|
|
|
|
// Click on attempt dropdown
|
|
wrapper.get('.job-attempt-dropdown .ui.dropdown').trigger('click');
|
|
await flushPromises();
|
|
|
|
// Check that attempt dropdown expanded again, gear dropdown disappeared (mutually exclusive)
|
|
expect(wrapper.findAll('.job-gear-dropdown .action-job-menu').length).toEqual(0);
|
|
attemptsExpanded();
|
|
|
|
// Click on the other option in the dropdown to verify it navigates to the target attempt
|
|
wrapper.findAll('.job-attempt-dropdown .action-job-menu a').find((a) => a.text() === 'Run attempt 2 yesterday').trigger('click');
|
|
expect(window.location.href).toEqual(toAbsoluteUrl('/user1/repo2/actions/runs/123/jobs/1/attempt/2'));
|
|
});
|
|
|
|
test('run approval interaction', async () => {
|
|
const pullRequestLink = '/example-org/example-repo/pulls/456';
|
|
const wrapper = mount(RepoActionView, {
|
|
props: {
|
|
...defaultTestProps,
|
|
initialJobData: {
|
|
state: {
|
|
run: {
|
|
canApprove: true,
|
|
status: 'waiting',
|
|
commit: {
|
|
pusher: {},
|
|
branch: {
|
|
link: toAbsoluteUrl(pullRequestLink),
|
|
},
|
|
},
|
|
},
|
|
currentJob: {
|
|
steps: [
|
|
{
|
|
summary: 'Test Job',
|
|
duration: '1s',
|
|
status: 'success',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
logs: {
|
|
stepsLog: [],
|
|
},
|
|
},
|
|
},
|
|
});
|
|
await flushPromises();
|
|
const approve = wrapper.findAll('button').filter((button) => button.text() === 'Locale Approve');
|
|
expect(approve.length).toEqual(1);
|
|
approve[0].trigger('click');
|
|
expect(window.location.href).toEqual(toAbsoluteUrl(`${pullRequestLink}#pull-request-trust-panel`));
|
|
});
|
|
|
|
test('artifacts download links', async () => {
|
|
Object.defineProperty(document.documentElement, 'lang', {value: 'en'});
|
|
vi.spyOn(global, 'fetch').mockImplementation((url, opts) => {
|
|
if (url.endsWith('/artifacts')) {
|
|
return Promise.resolve({
|
|
ok: true,
|
|
json: vi.fn().mockResolvedValue(
|
|
{
|
|
artifacts: [
|
|
{name: 'artifactname1', size: 111, status: 'completed'},
|
|
{name: 'artifactname2', size: 222, status: 'expired'},
|
|
],
|
|
},
|
|
),
|
|
});
|
|
}
|
|
|
|
const postBody = JSON.parse(opts.body);
|
|
const stepsLog_value = [];
|
|
for (const cursor of postBody.logCursors) {
|
|
if (cursor.expanded) {
|
|
stepsLog_value.push(
|
|
{
|
|
step: cursor.step,
|
|
cursor: 0,
|
|
lines: [
|
|
{index: 1, message: `Step #${cursor.step + 1} Log #1`, timestamp: 0},
|
|
],
|
|
},
|
|
);
|
|
}
|
|
}
|
|
const jobs_value = {
|
|
state: {
|
|
run: {
|
|
status: 'success',
|
|
commit: {
|
|
pusher: {},
|
|
},
|
|
},
|
|
currentJob: {
|
|
title: 'test',
|
|
steps: [
|
|
{
|
|
summary: 'Test Step #1',
|
|
duration: '1s',
|
|
status: 'success',
|
|
},
|
|
],
|
|
allAttempts: [{number: 1, time_since_started_html: '', status: 'success', status_diagnostics: ['Success']}],
|
|
},
|
|
},
|
|
logs: {
|
|
stepsLog: opts.body?.includes('"cursor":null') ? stepsLog_value : [],
|
|
},
|
|
};
|
|
|
|
return Promise.resolve({
|
|
ok: true,
|
|
json: vi.fn().mockResolvedValue(
|
|
jobs_value,
|
|
),
|
|
});
|
|
});
|
|
|
|
const wrapper = mount(RepoActionView, {
|
|
props: defaultTestProps,
|
|
});
|
|
wrapper.vm.loadJob(); // simulate intermittent reload immediately so UI switches from minimalInitialJobData to the mock data from the test's fetch spy.
|
|
await flushPromises();
|
|
|
|
expect(wrapper.get('.job-artifacts .job-artifacts-title').text()).toEqual('artifactTitleHere');
|
|
expect(wrapper.get('.job-artifacts .job-artifacts-item:nth-of-type(1) .job-artifacts-link').attributes('href')).toEqual('https://example.com/example-org/example-repo/actions/runs/1001/artifacts/artifactname1');
|
|
expect(wrapper.get('.job-artifacts .job-artifacts-item:nth-of-type(2) .job-artifacts-link').attributes('href')).toEqual('https://example.com/example-org/example-repo/actions/runs/1001/artifacts/artifactname2');
|
|
});
|
|
|
|
test('initial load schedules refresh when job is not done', async () => {
|
|
Object.defineProperty(document.documentElement, 'lang', {value: 'en'});
|
|
vi.spyOn(global, 'fetch').mockImplementation((url, _opts) => {
|
|
return Promise.resolve({
|
|
ok: true,
|
|
json: vi.fn().mockResolvedValue(
|
|
url.endsWith('/artifacts') ? minimalInitialArtifactData : minimalInitialJobData,
|
|
),
|
|
});
|
|
});
|
|
|
|
// Provide a job that is "done" so that the component doesn't start incremental refresh...
|
|
{
|
|
const doneInitialJobData = structuredClone(minimalInitialJobData);
|
|
doneInitialJobData.state.run.done = true;
|
|
const wrapper = mount(RepoActionView, {
|
|
props: {
|
|
...defaultTestProps,
|
|
initialJobData: doneInitialJobData,
|
|
},
|
|
});
|
|
await flushPromises();
|
|
const container = wrapper.find('.action-view-container');
|
|
expect(container.exists()).toBe(true);
|
|
expect(container.classes()).not.toContain('interval-pending');
|
|
wrapper.unmount();
|
|
}
|
|
|
|
// Provide a job that is *not* "done" so that the component does start incremental refresh...
|
|
{
|
|
const runningInitialJobData = structuredClone(minimalInitialJobData);
|
|
runningInitialJobData.state.run.done = false;
|
|
const wrapper = mount(RepoActionView, {
|
|
props: defaultTestProps,
|
|
});
|
|
await flushPromises();
|
|
const container = wrapper.find('.action-view-container');
|
|
expect(container.exists()).toBe(true);
|
|
expect(container.classes()).toContain('interval-pending');
|
|
wrapper.unmount();
|
|
}
|
|
});
|
|
|
|
test('initial load data is used without calling fetch()', async () => {
|
|
Object.defineProperty(document.documentElement, 'lang', {value: 'en'});
|
|
const fetchSpy = vi.spyOn(global, 'fetch').mockImplementation((url, _opts) => {
|
|
return Promise.resolve({
|
|
ok: true,
|
|
json: vi.fn().mockResolvedValue(
|
|
url.endsWith('/artifacts') ? minimalInitialArtifactData : minimalInitialJobData,
|
|
),
|
|
});
|
|
});
|
|
|
|
mount(RepoActionView, {
|
|
props: defaultTestProps,
|
|
});
|
|
await flushPromises();
|
|
expect(fetchSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test('view non-picked action run job', async () => {
|
|
Object.defineProperty(document.documentElement, 'lang', {value: 'en'});
|
|
const wrapper = mount(RepoActionView, {
|
|
props: {
|
|
...defaultTestProps,
|
|
initialJobData: {
|
|
...minimalInitialJobData,
|
|
// Definitions here should match the same type of content as the related backend test,
|
|
// "TestActionsViewViewPost/attempt out-of-bounds on non-picked task", so that we're sure we can handle in the
|
|
// UI what the backend will deliver in this case.
|
|
state: {
|
|
run: {
|
|
done: false,
|
|
status: 'waiting',
|
|
commit: {
|
|
pusher: {},
|
|
},
|
|
jobs: [
|
|
{
|
|
id: 161,
|
|
name: 'check-1',
|
|
status: 'waiting',
|
|
canRerun: false,
|
|
duration: '0s',
|
|
},
|
|
{
|
|
id: 162,
|
|
name: 'check-2',
|
|
status: 'waiting',
|
|
canRerun: false,
|
|
duration: '0s',
|
|
},
|
|
{
|
|
id: 163,
|
|
name: 'check-3',
|
|
status: 'waiting',
|
|
canRerun: false,
|
|
duration: '0s',
|
|
},
|
|
],
|
|
},
|
|
currentJob: {
|
|
title: 'check-1',
|
|
details: ['waiting (locale)'], // locale-specific, not exact match to backend test
|
|
steps: [],
|
|
allAttempts: null,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
await flushPromises();
|
|
|
|
expect(wrapper.get('.job-info-header-detail li:first-child').text()).toEqual('waiting (locale)');
|
|
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 () => {
|
|
Object.defineProperty(document.documentElement, 'lang', {value: 'en'});
|
|
const wrapper = mount(RepoActionView, {
|
|
props: defaultTestProps,
|
|
});
|
|
await flushPromises();
|
|
expect(wrapper.find('.pre-execution-error').exists()).toBe(false);
|
|
});
|
|
|
|
test('view with pre-execution error', async () => {
|
|
Object.defineProperty(document.documentElement, 'lang', {value: 'en'});
|
|
const wrapper = mount(RepoActionView, {
|
|
props: {
|
|
...defaultTestProps,
|
|
initialJobData: {
|
|
...minimalInitialJobData,
|
|
state: {
|
|
...minimalInitialJobData.state,
|
|
run: {
|
|
...minimalInitialJobData.state.run,
|
|
preExecutionError: 'Oops, I dropped it.',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
await flushPromises();
|
|
const block = wrapper.find('.pre-execution-error');
|
|
expect(block.exists()).toBe(true);
|
|
expect(block.text()).toBe('pre-execution error Oops, I dropped it.');
|
|
});
|