Commit bead17ae authored by Miguel Rincon's avatar Miguel Rincon

Merge branch...

Merge branch '347250-add-a-total-number-of-jobs-that-a-runner-has-run-in-the-admin-view' into 'master'

Add a total number of jobs run by a runner

See merge request gitlab-org/gitlab!76211
parents b04d962c a8d84a77
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
import { GlTable, GlTooltipDirective, GlSkeletonLoader } from '@gitlab/ui'; import { GlTable, GlTooltipDirective, GlSkeletonLoader } from '@gitlab/ui';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { __, s__ } from '~/locale'; import { formatNumber, __, s__ } from '~/locale';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { RUNNER_JOB_COUNT_LIMIT } from '../constants';
import RunnerActionsCell from './cells/runner_actions_cell.vue'; import RunnerActionsCell from './cells/runner_actions_cell.vue';
import RunnerSummaryCell from './cells/runner_summary_cell.vue'; import RunnerSummaryCell from './cells/runner_summary_cell.vue';
import RunnerStatusCell from './cells/runner_status_cell.vue'; import RunnerStatusCell from './cells/runner_status_cell.vue';
...@@ -52,6 +53,12 @@ export default { ...@@ -52,6 +53,12 @@ export default {
}, },
}, },
methods: { methods: {
formatJobCount(jobCount) {
if (jobCount > RUNNER_JOB_COUNT_LIMIT) {
return `${formatNumber(RUNNER_JOB_COUNT_LIMIT)}+`;
}
return formatNumber(jobCount);
},
runnerTrAttr(runner) { runnerTrAttr(runner) {
if (runner) { if (runner) {
return { return {
...@@ -66,6 +73,7 @@ export default { ...@@ -66,6 +73,7 @@ export default {
tableField({ key: 'summary', label: s__('Runners|Runner ID'), thClasses: ['gl-lg-w-25p'] }), tableField({ key: 'summary', label: s__('Runners|Runner ID'), thClasses: ['gl-lg-w-25p'] }),
tableField({ key: 'version', label: __('Version') }), tableField({ key: 'version', label: __('Version') }),
tableField({ key: 'ipAddress', label: __('IP Address') }), tableField({ key: 'ipAddress', label: __('IP Address') }),
tableField({ key: 'jobCount', label: __('Jobs') }),
tableField({ key: 'tagList', label: __('Tags'), thClasses: ['gl-lg-w-25p'] }), tableField({ key: 'tagList', label: __('Tags'), thClasses: ['gl-lg-w-25p'] }),
tableField({ key: 'contactedAt', label: __('Last contact') }), tableField({ key: 'contactedAt', label: __('Last contact') }),
tableField({ key: 'actions', label: '' }), tableField({ key: 'actions', label: '' }),
...@@ -112,6 +120,10 @@ export default { ...@@ -112,6 +120,10 @@ export default {
</tooltip-on-truncate> </tooltip-on-truncate>
</template> </template>
<template #cell(jobCount)="{ item: { jobCount } }">
{{ formatJobCount(jobCount) }}
</template>
<template #cell(tagList)="{ item: { tagList } }"> <template #cell(tagList)="{ item: { tagList } }">
<runner-tags :tag-list="tagList" size="sm" /> <runner-tags :tag-list="tagList" size="sm" />
</template> </template>
......
import { s__ } from '~/locale'; import { s__ } from '~/locale';
export const RUNNER_PAGE_SIZE = 20; export const RUNNER_PAGE_SIZE = 20;
export const RUNNER_JOB_COUNT_LIMIT = 1000;
export const GROUP_RUNNER_COUNT_LIMIT = 1000; export const GROUP_RUNNER_COUNT_LIMIT = 1000;
export const I18N_FETCH_ERROR = s__('Runners|Something went wrong while fetching runner data.'); export const I18N_FETCH_ERROR = s__('Runners|Something went wrong while fetching runner data.');
......
...@@ -8,6 +8,7 @@ fragment RunnerNode on CiRunner { ...@@ -8,6 +8,7 @@ fragment RunnerNode on CiRunner {
ipAddress ipAddress
active active
locked locked
jobCount
tagList tagList
contactedAt contactedAt
status(legacyMode: null) status(legacyMode: null)
......
...@@ -59,6 +59,19 @@ RSpec.describe "Admin Runners" do ...@@ -59,6 +59,19 @@ RSpec.describe "Admin Runners" do
end end
end end
it 'shows a job count' do
runner = create(:ci_runner, :project, projects: [project])
create(:ci_build, runner: runner)
create(:ci_build, runner: runner)
visit admin_runners_path
within "[data-testid='runner-row-#{runner.id}'] [data-label='Jobs']" do
expect(page).to have_content '2'
end
end
describe 'delete runner' do describe 'delete runner' do
let!(:runner) { create(:ci_runner, description: 'runner-foo') } let!(:runner) { create(:ci_runner, description: 'runner-foo') }
......
...@@ -46,6 +46,7 @@ describe('RunnerList', () => { ...@@ -46,6 +46,7 @@ describe('RunnerList', () => {
'Runner ID', 'Runner ID',
'Version', 'Version',
'IP Address', 'IP Address',
'Jobs',
'Tags', 'Tags',
'Last contact', 'Last contact',
'', // actions has no label '', // actions has no label
...@@ -79,6 +80,7 @@ describe('RunnerList', () => { ...@@ -79,6 +80,7 @@ describe('RunnerList', () => {
// Other fields // Other fields
expect(findCell({ fieldKey: 'version' }).text()).toBe(version); expect(findCell({ fieldKey: 'version' }).text()).toBe(version);
expect(findCell({ fieldKey: 'ipAddress' }).text()).toBe(ipAddress); expect(findCell({ fieldKey: 'ipAddress' }).text()).toBe(ipAddress);
expect(findCell({ fieldKey: 'jobCount' }).text()).toBe('0');
expect(findCell({ fieldKey: 'tagList' }).text()).toBe(''); expect(findCell({ fieldKey: 'tagList' }).text()).toBe('');
expect(findCell({ fieldKey: 'contactedAt' }).text()).toEqual(expect.any(String)); expect(findCell({ fieldKey: 'contactedAt' }).text()).toEqual(expect.any(String));
...@@ -89,6 +91,42 @@ describe('RunnerList', () => { ...@@ -89,6 +91,42 @@ describe('RunnerList', () => {
expect(actions.findByTestId('toggle-active-runner').exists()).toBe(true); expect(actions.findByTestId('toggle-active-runner').exists()).toBe(true);
}); });
describe('Table data formatting', () => {
let mockRunnersCopy;
beforeEach(() => {
mockRunnersCopy = [
{
...mockRunners[0],
},
];
});
it('Formats job counts', () => {
mockRunnersCopy[0].jobCount = 1;
createComponent({ props: { runners: mockRunnersCopy } }, mount);
expect(findCell({ fieldKey: 'jobCount' }).text()).toBe('1');
});
it('Formats large job counts', () => {
mockRunnersCopy[0].jobCount = 1000;
createComponent({ props: { runners: mockRunnersCopy } }, mount);
expect(findCell({ fieldKey: 'jobCount' }).text()).toBe('1,000');
});
it('Formats large job counts with a plus symbol', () => {
mockRunnersCopy[0].jobCount = 1001;
createComponent({ props: { runners: mockRunnersCopy } }, mount);
expect(findCell({ fieldKey: 'jobCount' }).text()).toBe('1,000+');
});
});
it('Shows runner identifier', () => { it('Shows runner identifier', () => {
const { id, shortSha } = mockRunners[0]; const { id, shortSha } = mockRunners[0];
const numericId = getIdFromGraphQLId(id); const numericId = getIdFromGraphQLId(id);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment