Commit 8b54a3f3 authored by Jose Vargas's avatar Jose Vargas

Move runner installation logic to GraphQL

This removes the Vuex logic and uses Apollo
instead for state management
parent 6409921e
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import InstallRunnerInstructions from '~/vue_shared/components/runner_instructions/runner_instructions.vue';
import { createStore } from '~/vue_shared/components/runner_instructions/store';
Vue.use(VueApollo);
export function initInstallRunner() {
const installRunnerEl = document.getElementById('js-install-runner');
const { projectPath, groupPath } = installRunnerEl.dataset || {};
if (installRunnerEl) {
const defaultClient = createDefaultClient();
const apolloProvider = new VueApollo({
defaultClient,
});
// eslint-disable-next-line no-new
new Vue({
el: installRunnerEl,
store: createStore({
...installRunnerEl.dataset,
}),
apolloProvider,
provide: {
projectPath,
groupPath,
},
render(createElement) {
return createElement(InstallRunnerInstructions);
},
......
query getProjectId($projectPath: ID!) {
project(fullPath: $projectPath) {
id
}
}
query getRunnerPlatforms {
runnerPlatforms {
nodes {
name
humanReadableName
architectures {
nodes {
name
downloadLocation
}
}
}
}
}
query runnerSetupInstructions($platform: String!, $architecture: String!, $projectId: ID!) {
runnerSetup(platform: $platform, architecture: $architecture, projectId: $projectId) {
installInstructions
registerInstructions
}
}
query runnerSetupInstructionsGroup($platform: String!, $architecture: String!) {
runnerSetup(platform: $platform, architecture: $architecture) {
installInstructions
registerInstructions
}
}
query runnerSetupInstructionsGroup($platform: String!, $architecture: String!, $groupId: ID!) {
runnerSetup(platform: $platform, architecture: $architecture, groupId: $groupId) {
installInstructions
registerInstructions
}
}
......@@ -9,8 +9,13 @@ import {
GlDropdownItem,
GlIcon,
} from '@gitlab/ui';
import { mapActions, mapState, mapGetters } from 'vuex';
import { __, s__ } from '~/locale';
import getRunnerPlatforms from './graphql/queries/get_runner_platforms.query.graphql';
import getRunnerSetupInstructions from './graphql/queries/get_runner_setup.query.graphql';
import getRunnerSetupInstructionsGroup from './graphql/queries/get_runner_setup_group.query.graphql';
import getRunnerSetupInstructionsAdmin from './graphql/queries/get_runner_setup_admin.query.graphql';
import getProjectId from './graphql/queries/get_project_id.query.graphql';
import getGroupId from './graphql/queries/get_group_id.query.graphql';
export default {
components: {
......@@ -25,39 +30,127 @@ export default {
directives: {
GlModalDirective,
},
inject: {
projectPath: {
default: '',
},
groupPath: {
default: '',
},
},
apollo: {
runnerPlatforms: {
query: getRunnerPlatforms,
update(data) {
return data?.runnerPlatforms?.nodes;
},
error() {
this.showAlert = true;
},
},
projectId: {
query: getProjectId,
variables() {
return {
projectPath: this.projectPath,
};
},
update(data) {
return data?.project?.id;
},
error() {
this.showAlert = true;
},
},
groupId: {
query: getGroupId,
variables() {
return {
groupPath: this.groupPath,
};
},
update(data) {
return data?.group?.id;
},
error() {
this.showAlert = true;
},
},
},
data() {
return {
showAlert: false,
selectedPlatformArchitectures: [],
selectedPlatform: {},
selectedArchitecture: {},
runnerPlatforms: [],
projectId: '',
groupId: '',
instructions: {},
};
},
computed: {
...mapState('installRunnerPopup', [
'availablePlatforms',
'instructions',
'selectedArchitecture',
'showAlert',
]),
...mapGetters('installRunnerPopup', [
'getSupportedArchitectures',
'instructionsEmpty',
'hasDownloadLocationsAvailable',
'getDownloadLocation',
]),
closeButton() {
return {
text: __('Close'),
attributes: [{ variant: 'default' }],
};
},
isArchitectureSelected() {
return this.selectedArchitecture !== '';
isPlatformSelected() {
return Object.keys(this.selectedPlatform).length > 0;
},
instructionsEmpty() {
return this.instructions == null || Object.keys(this.instructions).length === 0;
},
mounted() {
this.requestPlatforms();
},
methods: {
...mapActions('installRunnerPopup', [
'requestPlatforms',
'selectPlatform',
'startInstructionsRequest',
'toggleAlert',
]),
selectPlatform(name) {
this.selectedPlatform = this.runnerPlatforms.find(platform => platform.name === name);
this.selectedPlatformArchitectures = this.selectedPlatform?.architectures?.nodes;
[this.selectedArchitecture] = this.selectedPlatformArchitectures;
this.selectArchitecture(this.selectedArchitecture.name);
},
selectArchitecture(name) {
this.selectedArchitecture = this.selectedPlatformArchitectures.find(
architecture => architecture.name === name,
);
this.$apollo.addSmartQuery('instructions', {
variables() {
const vars = {
platform: this.selectedPlatform.name,
architecture: this.selectedArchitecture.name,
};
if (this.projectId) {
vars.projectId = this.projectId;
}
if (this.groupId) {
vars.groupId = this.groupId;
}
return vars;
},
query() {
if (this.projectId) {
return getRunnerSetupInstructions;
} else if (this.groupId) {
return getRunnerSetupInstructionsGroup;
}
return getRunnerSetupInstructionsAdmin;
},
update(data) {
return data?.runnerSetup;
},
error() {
this.showAlert = true;
},
});
},
toggleAlert(state) {
this.showAlert = state;
},
},
modalId: 'installation-instructions-modal',
i18n: {
......@@ -68,13 +161,14 @@ export default {
registerRunner: s__('Runners|Register Runner'),
method: __('Method'),
genericError: __('An error has occurred'),
instructions: __('Show Runner installation instructions'),
},
};
</script>
<template>
<div>
<gl-button v-gl-modal-directive="$options.modalId" data-testid="show-modal-button">
{{ __('Show Runner installation instructions') }}
{{ $options.i18n.instructions }}
</gl-button>
<gl-modal
:modal-id="$options.modalId"
......@@ -87,33 +181,34 @@ export default {
<h5>{{ __('Environment') }}</h5>
<gl-button-group class="gl-mb-5">
<gl-button
v-for="(platform, key) in availablePlatforms"
:key="key"
v-for="platform in runnerPlatforms"
:key="platform.name"
data-testid="platform-button"
@click="selectPlatform(key)"
@click="selectPlatform(platform.name)"
>
{{ platform.human_readable_name }}
{{ platform.humanReadableName }}
</gl-button>
</gl-button-group>
<template v-if="hasDownloadLocationsAvailable">
<template v-if="isPlatformSelected">
<h5>
{{ $options.i18n.architecture }}
</h5>
<gl-dropdown class="gl-mb-5" :text="selectedArchitecture">
<gl-dropdown class="gl-mb-5" :text="selectedArchitecture.name">
<gl-dropdown-item
v-for="(architecture, index) in getSupportedArchitectures"
:key="index"
v-for="architecture in selectedPlatformArchitectures"
:key="architecture.name"
data-testid="architecture-dropdown-item"
@click="startInstructionsRequest(architecture)"
@click="selectArchitecture(architecture.name)"
>
{{ architecture }}
{{ architecture.name }}
</gl-dropdown-item>
</gl-dropdown>
<div v-if="isArchitectureSelected" class="gl-display-flex gl-align-items-center gl-mb-5">
<div class="gl-display-flex gl-align-items-center gl-mb-5">
<h5>{{ $options.i18n.downloadInstallBinary }}</h5>
<gl-button
class="gl-ml-auto"
:href="getDownloadLocation"
:href="selectedArchitecture.downloadLocation"
download
data-testid="binary-download-button"
>
{{ $options.i18n.downloadLatestBinary }}
......@@ -123,13 +218,13 @@ export default {
<template v-if="!instructionsEmpty">
<div class="gl-display-flex">
<pre class="bg-light gl-flex-fill-1" data-testid="binary-instructions">
{{ instructions.install.trimStart() }}
{{ instructions.installInstructions }}
</pre>
<gl-button
class="gl-align-self-start gl-ml-2 gl-mt-2"
category="tertiary"
variant="link"
:data-clipboard-text="instructions.install"
:data-clipboard-text="instructions.installationInstructions"
>
<gl-icon name="copy-to-clipboard" />
</gl-button>
......@@ -140,13 +235,13 @@ export default {
<h5 class="gl-mb-5">{{ $options.i18n.method }}</h5>
<div class="gl-display-flex">
<pre class="bg-light gl-flex-fill-1" data-testid="runner-instructions">
{{ instructions.register.trim() }}
{{ instructions.registerInstructions }}
</pre>
<gl-button
class="gl-align-self-start gl-ml-2 gl-mt-2"
category="tertiary"
variant="link"
:data-clipboard-text="instructions.register"
:data-clipboard-text="instructions.registerInstructions"
>
<gl-icon name="copy-to-clipboard" />
</gl-button>
......
import axios from '~/lib/utils/axios_utils';
import * as types from './mutation_types';
export const requestPlatforms = ({ state, commit, getters, dispatch }) => {
axios
.get(state.platformsPath)
.then(resp => {
commit(types.SET_AVAILABLE_PLATFORMS, resp?.data);
// Select the first platform and architecture
const platform = Object.keys(resp.data)[0];
commit(types.SET_AVAILABLE_PLATFORM, platform);
dispatch('selectArchitecture', getters.getSupportedArchitectures[0]);
dispatch('requestPlatformsInstructions');
})
.catch(() => dispatch('toggleAlert', true));
};
export const requestPlatformsInstructions = ({ commit, state, dispatch }) => {
let path = `${state.instructionsPath}?os=${state.selectedAvailablePlatform}`;
path =
state.selectedArchitecture !== ''
? `${path}&arch=${state.selectedArchitecture}`
: `${path}&arch=amd64`;
axios
.get(path)
.then(resp => commit(types.SET_INSTRUCTIONS, resp?.data))
.catch(() => dispatch('toggleAlert', true));
};
export const startInstructionsRequest = ({ dispatch }, architecture) => {
dispatch('selectArchitecture', architecture);
dispatch('requestPlatformsInstructions');
};
export const selectPlatform = ({ commit, dispatch, getters }, platform) => {
commit(types.SET_AVAILABLE_PLATFORM, platform);
const architecture = getters.getSupportedArchitectures
? getters.getSupportedArchitectures[0]
: '';
dispatch('selectArchitecture', architecture);
dispatch('requestPlatformsInstructions');
};
export const selectArchitecture = ({ commit }, architecture) => {
commit(types.SET_ARCHITECTURE, architecture);
};
export const toggleAlert = ({ commit }, state) => {
commit(types.SET_SHOW_ALERT, state);
};
export const hasDownloadLocationsAvailable = state => {
return state.availablePlatforms[state.selectedAvailablePlatform]?.download_locations;
};
export const getSupportedArchitectures = state => {
return Object.keys(
state.availablePlatforms[state.selectedAvailablePlatform]?.download_locations || {},
);
};
export const instructionsEmpty = state => {
return !Object.keys(state.instructions).length;
};
export const getDownloadLocation = state => {
return state.availablePlatforms[state.selectedAvailablePlatform]?.download_locations[
state.selectedArchitecture
];
};
import Vue from 'vue';
import Vuex from 'vuex';
import createState from './state';
import * as actions from './actions';
import mutations from './mutations';
import * as getters from './getters';
Vue.use(Vuex);
export const createStore = initialState =>
new Vuex.Store({
modules: {
installRunnerPopup: {
namespaced: true,
state: createState(initialState),
actions,
mutations,
getters,
},
},
});
export const SET_AVAILABLE_PLATFORMS = 'SET_AVAILABLE_PLATFORMS';
export const SET_AVAILABLE_PLATFORM = 'SET_AVAILABLE_PLATFORM';
export const SET_ARCHITECTURE = 'SET_ARCHITECTURE';
export const SET_INSTRUCTIONS = 'SET_INSTRUCTIONS';
export const SET_SHOW_ALERT = 'SET_SHOW_ALERT';
import * as types from './mutation_types';
export default {
[types.SET_AVAILABLE_PLATFORMS](state, platforms) {
state.availablePlatforms = platforms;
},
[types.SET_AVAILABLE_PLATFORM](state, index) {
state.selectedAvailablePlatform = index;
},
[types.SET_ARCHITECTURE](state, index) {
state.selectedArchitecture = index;
},
[types.SET_INSTRUCTIONS](state, instructions) {
state.instructions = instructions;
},
[types.SET_SHOW_ALERT](state, show) {
state.showAlert = show;
},
};
export default (initialState = {}) => ({
instructionsPath: initialState.instructionsPath || '',
platformsPath: initialState.platformsPath || '',
availablePlatforms: initialState.availablePlatforms || {},
selectedAvailablePlatform: initialState.selectedAvailablePlatform || '', // index from the availablePlatforms array
selectedArchitecture: initialState.selectedArchitecture || '',
instructions: initialState.instructions || {},
showAlert: false,
});
......@@ -40,8 +40,8 @@
locals: { registration_token: Gitlab::CurrentSettings.runners_registration_token,
type: 'shared',
reset_token_url: reset_registration_token_admin_application_settings_path,
instructions_path: runner_setup_scripts_admin_runners_path,
platforms_path: runner_setup_platforms_path }
project_path: '',
group_path: '' }
.row
.col-sm-9
......
......@@ -20,4 +20,4 @@
%li
= _("Start the Runner!")
#js-install-runner{ data: { instructions_path: instructions_path, platforms_path: platforms_path } }
#js-install-runner{ data: { project_path: project_path, group_path: group_path } }
......@@ -18,5 +18,5 @@
locals: { registration_token: @group.runners_token,
type: 'group',
reset_token_url: reset_registration_token_group_settings_ci_cd_path,
instructions_path: runner_setup_scripts_group_settings_ci_cd_path(@group),
platforms_path: runner_setup_platforms_path }
project_path: '',
group_path: @group.path }
......@@ -10,8 +10,8 @@
locals: { registration_token: @project.runners_token,
type: 'specific',
reset_token_url: reset_registration_token_namespace_project_settings_ci_cd_path,
instructions_path: runner_setup_scripts_namespace_project_settings_ci_cd_path(project_id: @project, namespace_id: @project.namespace),
platforms_path: runner_setup_platforms_path }
project_path: @project.path_with_namespace,
group_path: '' }
- if @project_runners.any?
%h4.underlined-title= _('Runners activated for this project')
......
export const mockPlatformsObject = {
linux: {
human_readable_name: 'Linux',
download_locations: {
'386':
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386',
amd64:
export const mockGraphqlRunnerPlatforms = {
data: {
runnerPlatforms: {
nodes: [
{
name: 'linux',
humanReadableName: 'Linux',
architectures: {
nodes: [
{
name: 'amd64',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64',
arm:
__typename: 'RunnerArchitecture',
},
{
name: '386',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386',
__typename: 'RunnerArchitecture',
},
{
name: 'arm',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm',
arm64:
__typename: 'RunnerArchitecture',
},
{
name: 'arm64',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm64',
__typename: 'RunnerArchitecture',
},
],
__typename: 'RunnerArchitectureConnection',
},
install_script_template_path: 'lib/gitlab/ci/runner_instructions/templates/linux/install.sh',
runner_executable: 'sudo gitlab-runner',
__typename: 'RunnerPlatform',
},
osx: {
human_readable_name: 'macOS',
download_locations: {
amd64:
{
name: 'osx',
humanReadableName: 'macOS',
architectures: {
nodes: [
{
name: 'amd64',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64',
__typename: 'RunnerArchitecture',
},
install_script_template_path: 'lib/gitlab/ci/runner_instructions/templates/osx/install.sh',
runner_executable: 'sudo gitlab-runner',
],
__typename: 'RunnerArchitectureConnection',
},
windows: {
human_readable_name: 'Windows',
download_locations: {
'386':
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-386.exe',
amd64:
__typename: 'RunnerPlatform',
},
{
name: 'windows',
humanReadableName: 'Windows',
architectures: {
nodes: [
{
name: 'amd64',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-amd64.exe',
__typename: 'RunnerArchitecture',
},
{
name: '386',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-386.exe',
__typename: 'RunnerArchitecture',
},
],
__typename: 'RunnerArchitectureConnection',
},
install_script_template_path: 'lib/gitlab/ci/runner_instructions/templates/windows/install.ps1',
runner_executable: './gitlab-runner.exe',
__typename: 'RunnerPlatform',
},
docker: {
human_readable_name: 'Docker',
installation_instructions_url: 'https://docs.gitlab.com/runner/install/docker.html',
{
name: 'docker',
humanReadableName: 'Docker',
architectures: null,
__typename: 'RunnerPlatform',
},
{
name: 'kubernetes',
humanReadableName: 'Kubernetes',
architectures: null,
__typename: 'RunnerPlatform',
},
],
__typename: 'RunnerPlatformConnection',
},
},
};
export const mockGraphqlInstructions = {
data: {
runnerSetup: {
installInstructions:
"# Download the binary for your system\nsudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64\n\n# Give it permissions to execute\nsudo chmod +x /usr/local/bin/gitlab-runner\n\n# Create a GitLab CI user\nsudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash\n\n# Install and run as service\nsudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner\nsudo gitlab-runner start\n",
registerInstructions:
'sudo gitlab-runner register --url http://192.168.1.81:3000/ --registration-token GE5gsjeep_HAtBf9s3Yz',
__typename: 'RunnerSetup',
},
kubernetes: {
human_readable_name: 'Kubernetes',
installation_instructions_url: 'https://docs.gitlab.com/runner/install/kubernetes.html',
},
};
export const mockInstructions = {
install:
"# Download the binary for your system\nsudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386\n\n# Give it permissions to execute\nsudo chmod +x /usr/local/bin/gitlab-runner\n\n# Create a GitLab CI user\nsudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash\n\n# Install and run as service\nsudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner\nsudo gitlab-runner start\n",
register:
'sudo gitlab-runner register --url http://0.0.0.0:3000/ --registration-token GE5gsjeep_HAtBf9s3Yz',
export const mockGraphqlProjectId = {
data: {
project: {
id: 'gid://gitlab/Project/1',
},
},
};
export const mockGraphqlGroupId = {
data: {
group: null,
},
};
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { shallowMount } from '@vue/test-utils';
import statusCodes from '~/lib/utils/http_status';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'jest/helpers/mock_apollo_helper';
import RunnerInstructions from '~/vue_shared/components/runner_instructions/runner_instructions.vue';
import { createStore } from '~/vue_shared/components/runner_instructions/store/';
import * as types from '~/vue_shared/components/runner_instructions/store/mutation_types';
import { mockPlatformsObject, mockInstructions } from './mock_data';
const instructionsPath = '/instructions';
const platformsPath = '/platforms';
import getRunnerPlatforms from '~/vue_shared/components/runner_instructions/graphql/queries/get_runner_platforms.query.graphql';
import getProjectId from '~/vue_shared/components/runner_instructions/graphql/queries/get_project_id.query.graphql';
import getGroupId from '~/vue_shared/components/runner_instructions/graphql/queries/get_group_id.query.graphql';
import getRunnerSetupInstructions from '~/vue_shared/components/runner_instructions/graphql/queries/get_runner_setup.query.graphql';
import {
mockGraphqlRunnerPlatforms,
mockGraphqlProjectId,
mockGraphqlInstructions,
mockGraphqlGroupId,
} from './mock_data';
const projectPath = 'gitlab-org/gitlab';
const localVue = createLocalVue();
localVue.use(VueApollo);
describe('RunnerInstructions component', () => {
let wrapper;
let store;
let mock;
let fakeApollo;
const findModalButton = () => wrapper.find('[data-testid="show-modal-button"]');
const findPlatformButtons = () => wrapper.findAll('[data-testid="platform-button"]');
......@@ -23,31 +29,23 @@ describe('RunnerInstructions component', () => {
const findBinaryInstructionsSection = () => wrapper.find('[data-testid="binary-instructions"]');
const findRunnerInstructionsSection = () => wrapper.find('[data-testid="runner-instructions"]');
function setupStore() {
store.commit(`installRunnerPopup/${types.SET_AVAILABLE_PLATFORMS}`, mockPlatformsObject);
store.commit(`installRunnerPopup/${types.SET_AVAILABLE_PLATFORM}`, 'linux');
store.commit(`installRunnerPopup/${types.SET_ARCHITECTURE}`, '386');
store.commit(`installRunnerPopup/${types.SET_INSTRUCTIONS}`, mockInstructions);
}
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(platformsPath).reply(statusCodes.OK, mockPlatformsObject);
mock.onGet('/instructions?os=linux&arch=386').reply(statusCodes.OK, mockInstructions);
store = createStore({
instructionsPath,
platformsPath,
const requestHandlers = [
[getRunnerPlatforms, jest.fn().mockResolvedValue(mockGraphqlRunnerPlatforms)],
[getRunnerSetupInstructions, jest.fn().mockResolvedValue(mockGraphqlInstructions)],
[getProjectId, jest.fn().mockResolvedValue(mockGraphqlProjectId)],
[getGroupId, jest.fn().mockResolvedValue(mockGraphqlGroupId)],
];
fakeApollo = createMockApollo(requestHandlers);
wrapper = shallowMount(RunnerInstructions, {
provide: {
projectPath,
},
localVue,
apolloProvider: fakeApollo,
});
wrapper = shallowMount(RunnerInstructions, { store });
setupStore();
});
afterEach(() => {
......@@ -65,25 +63,35 @@ describe('RunnerInstructions component', () => {
it('should contain a number of platforms buttons', () => {
const buttons = findPlatformButtons();
expect(buttons).toHaveLength(Object.keys(mockPlatformsObject).length);
expect(buttons).toHaveLength(mockGraphqlRunnerPlatforms.data.runnerPlatforms.nodes.length);
});
it('should contain a number of dropdown items for the architecture options', () => {
const platformButton = findPlatformButtons().at(0);
platformButton.vm.$emit('click');
return wrapper.vm.$nextTick(() => {
const dropdownItems = findArchitectureDropdownItems();
expect(dropdownItems).toHaveLength(
Object.keys(mockPlatformsObject.linux.download_locations).length,
mockGraphqlRunnerPlatforms.data.runnerPlatforms.nodes[0].architectures.nodes.length,
);
});
});
it('should display the binary installation instructions for a selected architecture', async () => {
const platformButton = findPlatformButtons().at(0);
platformButton.vm.$emit('click');
await wrapper.vm.$nextTick();
const dropdownItem = findArchitectureDropdownItems().at(0);
dropdownItem.vm.$emit('click');
await wrapper.vm.$nextTick();
it('should display the binary installation instructions for a selected architecture', () => {
const runner = findBinaryInstructionsSection();
expect(runner.text()).toEqual(
expect.stringContaining(
'sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386',
),
);
expect(runner.text()).toEqual(
expect.stringContaining('sudo chmod +x /usr/local/bin/gitlab-runner'),
);
......@@ -100,9 +108,21 @@ describe('RunnerInstructions component', () => {
expect(runner.text()).toEqual(expect.stringContaining('sudo gitlab-runner start'));
});
it('should display the runner instructions for a selected architecture', () => {
it('should display the runner register instructions for a selected architecture', async () => {
const platformButton = findPlatformButtons().at(0);
platformButton.vm.$emit('click');
await wrapper.vm.$nextTick();
const dropdownItem = findArchitectureDropdownItems().at(0);
dropdownItem.vm.$emit('click');
await wrapper.vm.$nextTick();
const runner = findRunnerInstructionsSection();
expect(runner.text()).toEqual(expect.stringContaining(mockInstructions.register));
expect(runner.text()).toEqual(
expect.stringContaining(mockGraphqlInstructions.data.runnerSetup.registerInstructions),
);
});
});
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import axios from '~/lib/utils/axios_utils';
import statusCodes from '~/lib/utils/http_status';
import * as actions from '~/vue_shared/components/runner_instructions/store/actions';
import * as types from '~/vue_shared/components/runner_instructions/store/mutation_types';
import createState from '~/vue_shared/components/runner_instructions/store/state';
import { mockPlatformsObject, mockInstructions } from '../mock_data';
describe('Runner Instructions actions', () => {
let state;
let axiosMock;
beforeEach(() => {
state = createState();
axiosMock = new MockAdapter(axios);
});
afterEach(() => {
axiosMock.restore();
});
describe('selectPlatform', () => {
it('commits the SET_AVAILABLE_PLATFORM mutation and calls two actions', done => {
testAction(
actions.selectPlatform,
null,
state,
[{ type: types.SET_AVAILABLE_PLATFORM, payload: null }],
[{ type: 'selectArchitecture', payload: '' }, { type: 'requestPlatformsInstructions' }],
done,
);
});
});
describe('selectArchitecture', () => {
it('commits the SET_ARCHITECTURE mutation', done => {
testAction(
actions.selectArchitecture,
null,
state,
[{ type: types.SET_ARCHITECTURE, payload: null }],
[],
done,
);
});
});
describe('requestPlatformsInstructions', () => {
describe('successful request', () => {
beforeEach(() => {
state.instructionsPath = '/instructions';
state.selectedAvailablePlatform = 'linux';
state.selectedArchitecture = 'amd64';
axiosMock
.onGet(`${state.instructionsPath}?os=linux&arch=amd64`)
.reply(statusCodes.OK, mockInstructions);
});
it('commits the SET_INSTRUCTIONS mutation', done => {
testAction(
actions.requestPlatformsInstructions,
null,
state,
[{ type: types.SET_INSTRUCTIONS, payload: mockInstructions }],
[],
done,
);
});
});
describe('unsuccessful request', () => {
beforeEach(() => {
state.instructionsPath = '/instructions';
axiosMock.onGet(state.instructionsPath).reply(500);
});
it('shows an error', done => {
testAction(
actions.requestPlatformsInstructions,
null,
state,
[],
[{ type: 'toggleAlert', payload: true }],
done,
);
});
});
});
describe('requestPlatforms', () => {
describe('successful request', () => {
beforeEach(() => {
state.platformsPath = '/platforms';
state.availablePlatforms = mockPlatformsObject;
state.getSupportedArchitectures = ['linux', 'windows'];
axiosMock.onGet(state.platformsPath).reply(statusCodes.OK, mockPlatformsObject);
});
it('commits the SET_AVAILABLE_PLATFORMS mutation', done => {
testAction(
actions.requestPlatforms,
null,
state,
[
{ type: types.SET_AVAILABLE_PLATFORMS, payload: mockPlatformsObject },
{ type: types.SET_AVAILABLE_PLATFORM, payload: 'linux' },
],
[
{ type: 'selectArchitecture', payload: 'linux' },
{ type: 'requestPlatformsInstructions' },
],
done,
);
});
});
describe('unsuccessful request', () => {
beforeEach(() => {
state.platformsPath = '/instructions';
axiosMock.onGet(state.platformsPath).reply(500);
});
it('shows an error', done => {
testAction(
actions.requestPlatforms,
null,
state,
[],
[{ type: 'toggleAlert', payload: true }],
done,
);
});
});
});
describe('startInstructionsRequest', () => {
it('dispatches two actions', done => {
testAction(
actions.startInstructionsRequest,
'linux',
state,
[],
[
{ type: 'selectArchitecture', payload: 'linux' },
{ type: 'requestPlatformsInstructions' },
],
done,
);
});
});
describe('toggleAlert', () => {
it('commits the SET_SHOW_ALERT mutation', done => {
testAction(
actions.toggleAlert,
true,
state,
[{ type: types.SET_SHOW_ALERT, payload: true }],
[],
done,
);
});
});
});
import * as getters from '~/vue_shared/components/runner_instructions/store/getters';
import createState from '~/vue_shared/components/runner_instructions/store/state';
import { mockPlatformsObject, mockInstructions } from '../mock_data';
describe('Runner Instructions Store Getters', () => {
let state;
beforeEach(() => {
state = createState({
instructionsPath: '/instructions',
availablePlatforms: mockPlatformsObject,
selectedAvailablePlatform: 'linux',
selectedArchitecture: 'amd64',
instructions: mockInstructions,
});
});
describe('getSupportedArchitectures', () => {
let getSupportedArchitectures;
beforeEach(() => {
getSupportedArchitectures = getters.getSupportedArchitectures(state);
});
it('should the list of supported architectures', () => {
expect(getSupportedArchitectures).toHaveLength(
Object.keys(mockPlatformsObject.linux.download_locations).length,
);
expect(getSupportedArchitectures).toEqual(
Object.keys(mockPlatformsObject.linux.download_locations),
);
});
});
describe('hasDownloadLocationsAvailable', () => {
let hasDownloadLocationsAvailable;
beforeEach(() => {
hasDownloadLocationsAvailable = getters.hasDownloadLocationsAvailable(state);
});
it('should get the list of download locations for each architecture', () => {
expect(hasDownloadLocationsAvailable).toEqual(mockPlatformsObject.linux.download_locations);
});
});
describe('instructionsEmpty', () => {
let instructionsEmpty;
beforeEach(() => {
instructionsEmpty = getters.instructionsEmpty(state);
});
it('should return false if the instruction object is not empty', () => {
expect(instructionsEmpty).toBe(false);
});
});
describe('getDownloadLocation', () => {
let getDownloadLocation;
beforeEach(() => {
getDownloadLocation = getters.getDownloadLocation(state);
});
it('should return the download link for the selected platform and architecture', () => {
expect(getDownloadLocation).toBe(mockPlatformsObject.linux.download_locations.amd64);
});
});
});
import mutations from '~/vue_shared/components/runner_instructions/store/mutations';
import createState from '~/vue_shared/components/runner_instructions/store/state';
import { mockPlatformsObject, mockInstructions } from '../mock_data';
describe('Runner Instructions mutations', () => {
let localState;
beforeEach(() => {
localState = createState();
});
describe('SET_AVAILABLE_PLATFORMS', () => {
it('should set the availablePlatforms object', () => {
mutations.SET_AVAILABLE_PLATFORMS(localState, mockPlatformsObject);
expect(localState.availablePlatforms).toEqual(mockPlatformsObject);
});
});
describe('SET_AVAILABLE_PLATFORM', () => {
it('should set the selectedAvailablePlatform key', () => {
mutations.SET_AVAILABLE_PLATFORM(localState, 'linux');
expect(localState.selectedAvailablePlatform).toBe('linux');
});
});
describe('SET_ARCHITECTURE', () => {
it('should set the selectedArchitecture key', () => {
mutations.SET_ARCHITECTURE(localState, 'amd64');
expect(localState.selectedArchitecture).toBe('amd64');
});
});
describe('SET_INSTRUCTIONS', () => {
it('should set the instructions object', () => {
mutations.SET_INSTRUCTIONS(localState, mockInstructions);
expect(localState.instructions).toEqual(mockInstructions);
});
});
describe('SET_SHOW_ALERT', () => {
it('should set the showAlert boolean', () => {
mutations.SET_SHOW_ALERT(localState, true);
expect(localState.showAlert).toEqual(true);
});
});
});
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