Commit 4b03eeb5 authored by Miguel Rincon's avatar Miguel Rincon

Fetch runner type in runner details using graphql

This change adds an simple GraphQL query to the runner
details page to load the runner type asynchronously.
parent 0d801ac4
<script>
import { GlBadge } from '@gitlab/ui';
import { s__ } from '~/locale';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '../constants';
const badge = {
[INSTANCE_TYPE]: {
variant: 'success',
text: s__('Runners|shared'),
},
[GROUP_TYPE]: {
variant: 'success',
text: s__('Runners|group'),
},
[PROJECT_TYPE]: {
variant: 'info',
text: s__('Runners|specific'),
},
};
export default {
components: {
GlBadge,
},
props: {
type: {
type: String,
required: true,
},
},
computed: {
variant() {
return badge[this.type]?.variant;
},
text() {
return badge[this.type]?.text;
},
},
};
</script>
<template>
<gl-badge v-if="text" :variant="variant" v-bind="$attrs">
{{ text }}
</gl-badge>
</template>
import { s__ } from '~/locale';
export const I18N_DETAILS_TITLE = s__('Runners|Runner #%{runner_id}');
export const RUNNER_ENTITY_TYPE = 'Ci::Runner';
// CiRunnerType
export const INSTANCE_TYPE = 'INSTANCE_TYPE';
export const GROUP_TYPE = 'GROUP_TYPE';
export const PROJECT_TYPE = 'PROJECT_TYPE';
query getRunner($id: CiRunnerID!) {
runner(id: $id) {
id
runnerType
}
}
import { s__ } from '~/locale';
export const I18N_TITLE = s__('Runners|Runner #%{runner_id}');
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import RunnerDetailsApp from './runner_details_app.vue'; import RunnerDetailsApp from './runner_details_app.vue';
export const initRunnerDetail = (selector = '#js-runner-detail') => { Vue.use(VueApollo);
export const initRunnerDetail = (selector = '#js-runner-details') => {
const el = document.querySelector(selector); const el = document.querySelector(selector);
if (!el) { if (!el) {
...@@ -10,8 +14,18 @@ export const initRunnerDetail = (selector = '#js-runner-detail') => { ...@@ -10,8 +14,18 @@ export const initRunnerDetail = (selector = '#js-runner-detail') => {
const { runnerId } = el.dataset; const { runnerId } = el.dataset;
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(
{},
{
assumeImmutableResults: true,
},
),
});
return new Vue({ return new Vue({
el, el,
apolloProvider,
render(h) { render(h) {
return h(RunnerDetailsApp, { return h(RunnerDetailsApp, {
props: { props: {
......
<script> <script>
import { I18N_TITLE } from './constants'; import { convertToGraphQLId } from '~/graphql_shared/utils';
import RunnerTypeBadge from '../components/runner_type_badge.vue';
import { I18N_DETAILS_TITLE, RUNNER_ENTITY_TYPE } from '../constants';
import getRunnerQuery from '../graphql/get_runner.query.graphql';
export default { export default {
components: {
RunnerTypeBadge,
},
i18n: { i18n: {
I18N_TITLE, I18N_DETAILS_TITLE,
}, },
props: { props: {
runnerId: { runnerId: {
...@@ -11,10 +17,27 @@ export default { ...@@ -11,10 +17,27 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
runner: {},
};
},
apollo: {
runner: {
query: getRunnerQuery,
variables() {
return {
id: convertToGraphQLId(RUNNER_ENTITY_TYPE, this.runnerId),
};
},
},
},
}; };
</script> </script>
<template> <template>
<h2 class="page-title"> <h2 class="page-title">
{{ sprintf($options.i18n.I18N_TITLE, { runner_id: runnerId }) }} {{ sprintf($options.i18n.I18N_DETAILS_TITLE, { runner_id: runnerId }) }}
<runner-type-badge v-if="runner.runnerType" :type="runner.runnerType" />
</h2> </h2>
</template> </template>
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- add_to_breadcrumbs _('Runners'), admin_runners_path - add_to_breadcrumbs _('Runners'), admin_runners_path
- if Feature.enabled?(:runner_detailed_view_vue_ui, current_user, default_enabled: :yaml) - if Feature.enabled?(:runner_detailed_view_vue_ui, current_user, default_enabled: :yaml)
#js-runner-detail{ data: {runner_id: @runner.id} } #js-runner-details{ data: {runner_id: @runner.id} }
- else - else
%h2.page-title %h2.page-title
= s_('Runners|Runner #%{runner_id}' % { runner_id: @runner.id }) = s_('Runners|Runner #%{runner_id}' % { runner_id: @runner.id })
......
import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';
describe('RunnerTypeBadge', () => {
let wrapper;
const findBadge = () => wrapper.findComponent(GlBadge);
const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMount(RunnerTypeBadge, {
propsData: {
...props,
},
});
};
afterEach(() => {
wrapper.destroy();
});
it.each`
type | text | variant
${INSTANCE_TYPE} | ${'shared'} | ${'success'}
${GROUP_TYPE} | ${'group'} | ${'success'}
${PROJECT_TYPE} | ${'specific'} | ${'info'}
`('displays $type runner with as "$text" with a $variant variant ', ({ type, text, variant }) => {
createComponent({ props: { type } });
expect(findBadge().text()).toBe(text);
expect(findBadge().props('variant')).toBe(variant);
});
it('does not display a badge when type is unknown', () => {
createComponent({ props: { type: 'AN_UNKNOWN_VALUE' } });
expect(findBadge().exists()).toBe(false);
});
});
import { shallowMount } from '@vue/test-utils';
import RunnerDetailsApp from '~/runner/runner_details/runner_details_app.vue';
const mockRunnerId = '55';
describe('RunnerDetailsApp', () => {
let wrapper;
const createComponent = (props) => {
wrapper = shallowMount(RunnerDetailsApp, {
propsData: {
runnerId: mockRunnerId,
...props,
},
});
};
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('displays the runner id', () => {
expect(wrapper.text()).toContain('Runner #55');
});
});
import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue';
import { INSTANCE_TYPE } from '~/runner/constants';
import getRunnerQuery from '~/runner/graphql/get_runner.query.graphql';
import RunnerDetailsApp from '~/runner/runner_details/runner_details_app.vue';
const mockRunnerId = '55';
const localVue = createLocalVue();
localVue.use(VueApollo);
describe('RunnerDetailsApp', () => {
let wrapper;
let mockRunnerQuery;
const findRunnerTypeBadge = () => wrapper.findComponent(RunnerTypeBadge);
const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
const handlers = [[getRunnerQuery, mockRunnerQuery]];
wrapper = mountFn(RunnerDetailsApp, {
localVue,
apolloProvider: createMockApollo(handlers),
propsData: {
runnerId: mockRunnerId,
...props,
},
});
return waitForPromises();
};
beforeEach(async () => {
mockRunnerQuery = jest.fn().mockResolvedValue({
data: {
runner: {
id: `gid://gitlab/Ci::Runner/${mockRunnerId}`,
runnerType: INSTANCE_TYPE,
__typename: 'CiRunner',
},
},
});
});
afterEach(() => {
mockRunnerQuery.mockReset();
wrapper.destroy();
});
it('expect GraphQL ID to be requested', async () => {
await createComponentWithApollo();
expect(mockRunnerQuery).toHaveBeenCalledWith({ id: `gid://gitlab/Ci::Runner/${mockRunnerId}` });
});
it('displays the runner id', async () => {
await createComponentWithApollo();
expect(wrapper.text()).toContain('Runner #55');
});
it('displays the runner type', async () => {
await createComponentWithApollo({ mountFn: mount });
expect(findRunnerTypeBadge().text()).toBe('shared');
});
});
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