Commit c78144c0 authored by Jose Vargas's avatar Jose Vargas

Add install GitLab runner popup

This contains the necessary frontend
code for the installation instructions
modal for runners
parent 8bb6cc6f
import initFilteredSearch from '~/pages/search/init_filtered_search'; import initFilteredSearch from '~/pages/search/init_filtered_search';
import AdminRunnersFilteredSearchTokenKeys from '~/filtered_search/admin_runners_filtered_search_token_keys'; import AdminRunnersFilteredSearchTokenKeys from '~/filtered_search/admin_runners_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants'; import { FILTERED_SEARCH } from '~/pages/constants';
import { initInstallRunner } from '~/pages/shared/mount_runner_instructions';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({ initFilteredSearch({
...@@ -8,4 +9,6 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -8,4 +9,6 @@ document.addEventListener('DOMContentLoaded', () => {
filteredSearchTokenKeys: AdminRunnersFilteredSearchTokenKeys, filteredSearchTokenKeys: AdminRunnersFilteredSearchTokenKeys,
useDefaultState: true, useDefaultState: true,
}); });
initInstallRunner();
}); });
...@@ -4,6 +4,7 @@ import initFilteredSearch from '~/pages/search/init_filtered_search'; ...@@ -4,6 +4,7 @@ import initFilteredSearch from '~/pages/search/init_filtered_search';
import GroupRunnersFilteredSearchTokenKeys from '~/filtered_search/group_runners_filtered_search_token_keys'; import GroupRunnersFilteredSearchTokenKeys from '~/filtered_search/group_runners_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants'; import { FILTERED_SEARCH } from '~/pages/constants';
import initSharedRunnersForm from '~/group_settings/mount_shared_runners'; import initSharedRunnersForm from '~/group_settings/mount_shared_runners';
import { initInstallRunner } from '~/pages/shared/mount_runner_instructions';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels // Initialize expandable settings panels
...@@ -18,4 +19,5 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -18,4 +19,5 @@ document.addEventListener('DOMContentLoaded', () => {
initSharedRunnersForm(); initSharedRunnersForm();
initVariableList(); initVariableList();
initInstallRunner();
}); });
...@@ -4,6 +4,7 @@ import registrySettingsApp from '~/registry/settings/registry_settings_bundle'; ...@@ -4,6 +4,7 @@ import registrySettingsApp from '~/registry/settings/registry_settings_bundle';
import initVariableList from '~/ci_variable_list'; import initVariableList from '~/ci_variable_list';
import initDeployFreeze from '~/deploy_freeze'; import initDeployFreeze from '~/deploy_freeze';
import initSettingsPipelinesTriggers from '~/ci_settings_pipeline_triggers'; import initSettingsPipelinesTriggers from '~/ci_settings_pipeline_triggers';
import { initInstallRunner } from '~/pages/shared/mount_runner_instructions';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels // Initialize expandable settings panels
...@@ -32,4 +33,5 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -32,4 +33,5 @@ document.addEventListener('DOMContentLoaded', () => {
initDeployFreeze(); initDeployFreeze();
initSettingsPipelinesTriggers(); initSettingsPipelinesTriggers();
initInstallRunner();
}); });
import Vue from 'vue';
import InstallRunnerInstructions from '~/vue_shared/components/runner_instructions/runner_instructions.vue';
import { createStore } from '~/vue_shared/components/runner_instructions/store';
export function initInstallRunner() {
const installRunnerEl = document.getElementById('js-install-runner');
if (installRunnerEl) {
// eslint-disable-next-line no-new
new Vue({
el: installRunnerEl,
store: createStore({
...installRunnerEl.dataset,
}),
render(createElement) {
return createElement(InstallRunnerInstructions);
},
});
}
}
<script>
import {
GlButton,
GlModal,
GlModalDirective,
GlButtonGroup,
GlDropdown,
GlDropdownItem,
GlIcon,
} from '@gitlab/ui';
import { mapActions, mapState, mapGetters } from 'vuex';
import { __, s__ } from '~/locale';
export default {
components: {
GlButton,
GlButtonGroup,
GlDropdown,
GlDropdownItem,
GlModal,
GlIcon,
},
directives: {
GlModalDirective,
},
computed: {
...mapState('installRunnerPopup', [
'availablePlatforms',
'instructions',
'selectedArchitecture',
]),
...mapGetters('installRunnerPopup', [
'getSupportedArchitectures',
'instructionsEmpty',
'hasDownloadLocationsAvailable',
'getDownloadLocation',
]),
closeButton() {
return {
text: __('Close'),
attributes: [{ variant: 'default' }],
};
},
isArchitectureSelected() {
return this.selectedArchitecture !== '';
},
},
mounted() {
this.requestPlatforms();
},
methods: {
...mapActions('installRunnerPopup', [
'requestPlatforms',
'selectPlatform',
'startInstructionsRequest',
]),
},
modalId: 'installation-instructions-modal',
i18n: {
installARunner: __('Install a Runner'),
architecture: __('Architecture'),
downloadInstallBinary: s__('Runners|Download and Install Binary'),
downloadLatestBinary: s__('Runners|Download Latest Binary'),
registerRunner: s__('Runners|Register Runner'),
method: __('Method'),
},
};
</script>
<template>
<div>
<gl-button v-gl-modal-directive="$options.modalId" data-testid="show-modal-button">
{{ __('Show Runner installation instructions') }}
</gl-button>
<gl-modal
:modal-id="$options.modalId"
:title="$options.i18n.installARunner"
:action-secondary="closeButton"
>
<h5>{{ __('Environment') }}</h5>
<gl-button-group class="gl-mb-5">
<gl-button
v-for="(platform, key) in availablePlatforms"
:key="key"
data-testid="platform-button"
@click="selectPlatform(key)"
>
{{ platform.human_readable_name }}
</gl-button>
</gl-button-group>
<template v-if="hasDownloadLocationsAvailable">
<h5>
{{ $options.i18n.architecture }}
</h5>
<gl-dropdown class="gl-mb-5" :text="selectedArchitecture">
<gl-dropdown-item
v-for="(architecture, index) in getSupportedArchitectures"
:key="index"
data-testid="architecture-dropdown-item"
@click="startInstructionsRequest(architecture)"
>
{{ architecture }}
</gl-dropdown-item>
</gl-dropdown>
<div v-if="isArchitectureSelected" class="gl-display-flex gl-align-items-center gl-mb-5">
<h5>{{ $options.i18n.downloadInstallBinary }}</h5>
<gl-button
class="gl-ml-auto"
:href="getDownloadLocation"
data-testid="binary-download-button"
>
{{ $options.i18n.downloadLatestBinary }}
</gl-button>
</div>
</template>
<template v-if="!instructionsEmpty">
<div v-if="!instructionsEmpty" class="gl-display-flex">
<pre class="bg-light gl-flex-fill-1" data-testid="binary-instructions">
{{ instructions.install.trimStart() }}
</pre>
<gl-button
class="gl-align-self-start gl-ml-2 gl-mt-2"
category="tertiary"
variant="link"
:data-clipboard-text="instructions.install"
>
<gl-icon name="copy-to-clipboard" />
</gl-button>
</div>
<hr />
<h5 class="gl-mb-5">{{ $options.i18n.registerRunner }}</h5>
<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() }}
</pre>
<gl-button
class="gl-align-self-start gl-ml-2 gl-mt-2"
category="tertiary"
variant="link"
:data-clipboard-text="instructions.register"
>
<gl-icon name="copy-to-clipboard" />
</gl-button>
</div>
</template>
</gl-modal>
</div>
</template>
import axios from '~/lib/utils/axios_utils';
import statusCodes from '~/lib/utils/http_status';
import createFlash from '~/flash';
import { __ } from '~/locale';
import * as types from './mutation_types';
export const requestPlatforms = ({ state, commit, getters, dispatch }) => {
axios
.get(state.platformsPath)
.then(resp => {
if (resp.status === statusCodes.OK) {
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(() => createFlash({ message: __('An error has occurred') }));
};
export const requestPlatformsInstructions = ({ commit, state }) => {
let path = `${state.instructionsPath}?os=${state.selectedAvailablePlatform}`;
path =
state.selectedArchitecture !== ''
? `${path}&arch=${state.selectedArchitecture}`
: `${path}&arch=amd64`;
axios
.get(path)
.then(resp => {
if (resp.status === statusCodes.OK) {
commit(types.SET_INSTRUCTIONS, resp?.data);
}
})
.catch(() =>
createFlash({ message: __('An error has occurred fetching platform instructions') }),
);
};
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 hasDownloadLocationsAvailable = state => {
return state.availablePlatforms[state.selectedAvailablePlatform]?.download_locations;
};
export const getSupportedArchitectures = state => {
if (hasDownloadLocationsAvailable(state)) {
return Object.keys(
state.availablePlatforms[state.selectedAvailablePlatform]?.download_locations,
);
}
return [];
};
export const instructionsEmpty = state => {
return !Object.keys(state.instructions).length > 0;
};
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';
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;
},
};
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 || {},
});
...@@ -39,7 +39,9 @@ ...@@ -39,7 +39,9 @@
= render partial: 'ci/runner/how_to_setup_runner', = render partial: 'ci/runner/how_to_setup_runner',
locals: { registration_token: Gitlab::CurrentSettings.runners_registration_token, locals: { registration_token: Gitlab::CurrentSettings.runners_registration_token,
type: 'shared', type: 'shared',
reset_token_url: reset_registration_token_admin_application_settings_path } reset_token_url: reset_registration_token_admin_application_settings_path,
instructions_path: runner_setup_scripts_admin_runners_path,
platforms_path: runner_setup_platforms_path }
.row .row
.col-sm-9 .col-sm-9
......
...@@ -19,3 +19,5 @@ ...@@ -19,3 +19,5 @@
data: { confirm: _("Are you sure you want to reset registration token?") } data: { confirm: _("Are you sure you want to reset registration token?") }
%li %li
= _("Start the Runner!") = _("Start the Runner!")
#js-install-runner{ data: { instructions_path: instructions_path, platforms_path: platforms_path } }
...@@ -17,4 +17,6 @@ ...@@ -17,4 +17,6 @@
= render partial: 'ci/runner/how_to_setup_runner', = render partial: 'ci/runner/how_to_setup_runner',
locals: { registration_token: @group.runners_token, locals: { registration_token: @group.runners_token,
type: 'group', type: 'group',
reset_token_url: reset_registration_token_group_settings_ci_cd_path } 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 }
...@@ -9,7 +9,9 @@ ...@@ -9,7 +9,9 @@
= render partial: 'ci/runner/how_to_setup_runner', = render partial: 'ci/runner/how_to_setup_runner',
locals: { registration_token: @project.runners_token, locals: { registration_token: @project.runners_token,
type: 'specific', type: 'specific',
reset_token_url: reset_registration_token_namespace_project_settings_ci_cd_path } 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 }
- if @project_runners.any? - if @project_runners.any?
%h4.underlined-title= _('Runners activated for this project') %h4.underlined-title= _('Runners activated for this project')
......
---
title: Add install GitLab runner popup
merge_request: 42877
author:
type: added
...@@ -2810,6 +2810,9 @@ msgstr "" ...@@ -2810,6 +2810,9 @@ msgstr ""
msgid "An error has occurred" msgid "An error has occurred"
msgstr "" msgstr ""
msgid "An error has occurred fetching platform instructions"
msgstr ""
msgid "An error occured while making the changes: %{error}" msgid "An error occured while making the changes: %{error}"
msgstr "" msgstr ""
...@@ -3418,6 +3421,9 @@ msgstr "" ...@@ -3418,6 +3421,9 @@ msgstr ""
msgid "April" msgid "April"
msgstr "" msgstr ""
msgid "Architecture"
msgstr ""
msgid "Architecture not found for OS" msgid "Architecture not found for OS"
msgstr "" msgstr ""
...@@ -14167,6 +14173,9 @@ msgstr "" ...@@ -14167,6 +14173,9 @@ msgstr ""
msgid "Install Runner on Kubernetes" msgid "Install Runner on Kubernetes"
msgstr "" msgstr ""
msgid "Install a Runner"
msgstr ""
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}." msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr "" msgstr ""
...@@ -22911,6 +22920,12 @@ msgstr "" ...@@ -22911,6 +22920,12 @@ msgstr ""
msgid "Runners|Description" msgid "Runners|Description"
msgstr "" msgstr ""
msgid "Runners|Download Latest Binary"
msgstr ""
msgid "Runners|Download and Install Binary"
msgstr ""
msgid "Runners|Group" msgid "Runners|Group"
msgstr "" msgstr ""
...@@ -22938,6 +22953,9 @@ msgstr "" ...@@ -22938,6 +22953,9 @@ msgstr ""
msgid "Runners|Protected" msgid "Runners|Protected"
msgstr "" msgstr ""
msgid "Runners|Register Runner"
msgstr ""
msgid "Runners|Revision" msgid "Runners|Revision"
msgstr "" msgstr ""
...@@ -24342,6 +24360,9 @@ msgstr "" ...@@ -24342,6 +24360,9 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account." msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr "" msgstr ""
msgid "Show Runner installation instructions"
msgstr ""
msgid "Show all activity" msgid "Show all activity"
msgstr "" msgstr ""
......
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:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64',
arm:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm',
arm64:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm64',
},
install_script_template_path: 'lib/gitlab/ci/runner_instructions/templates/linux/install.sh',
runner_executable: 'sudo gitlab-runner',
},
osx: {
human_readable_name: 'macOS',
download_locations: {
amd64:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64',
},
install_script_template_path: 'lib/gitlab/ci/runner_instructions/templates/osx/install.sh',
runner_executable: 'sudo gitlab-runner',
},
windows: {
human_readable_name: 'Windows',
download_locations: {
'386':
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-386.exe',
amd64:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-amd64.exe',
},
install_script_template_path: 'lib/gitlab/ci/runner_instructions/templates/windows/install.ps1',
runner_executable: './gitlab-runner.exe',
},
docker: {
human_readable_name: 'Docker',
installation_instructions_url: 'https://docs.gitlab.com/runner/install/docker.html',
},
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',
};
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { shallowMount } from '@vue/test-utils';
import statusCodes from '~/lib/utils/http_status';
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';
describe('RunnerInstructions component', () => {
let wrapper;
let store;
let mock;
const findModalButton = () => wrapper.find('[data-testid="show-modal-button"]');
const findPlatformButtons = () => wrapper.findAll('[data-testid="platform-button"]');
const findArchitectureDropdownItems = () =>
wrapper.findAll('[data-testid="architecture-dropdown-item"]');
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,
});
wrapper = shallowMount(RunnerInstructions, { store });
setupStore();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('should show the "Show Runner installation instructions" button', () => {
const button = findModalButton();
expect(button.exists()).toBe(true);
expect(button.text()).toBe('Show Runner installation instructions');
});
it('should contain a number of platforms buttons', () => {
const buttons = findPlatformButtons();
expect(buttons).toHaveLength(Object.keys(mockPlatformsObject).length);
});
it('should contain a number of dropdown items for the architecture options', () => {
const dropdownItems = findArchitectureDropdownItems();
expect(dropdownItems).toHaveLength(
Object.keys(mockPlatformsObject.linux.download_locations).length,
);
});
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'),
);
expect(runner.text()).toEqual(
expect.stringContaining(
`sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash`,
),
);
expect(runner.text()).toEqual(
expect.stringContaining(
'sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner',
),
);
expect(runner.text()).toEqual(expect.stringContaining('sudo gitlab-runner start'));
});
it('should display the runner instructions for a selected architecture', () => {
const runner = findRunnerInstructionsSection();
expect(runner.text()).toEqual(expect.stringContaining(mockInstructions.register));
});
});
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, [], [], done);
});
});
});
describe('requestPlatforms', () => {
describe('successful request', () => {
beforeEach(() => {
state.platformsPath = '/platforms';
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' },
],
[],
done,
);
});
});
describe('unsuccessful request', () => {
beforeEach(() => {
state.platformsPath = '/instructions';
axiosMock.onGet(state.platformsPath).reply(500);
});
it('shows an error', done => {
testAction(actions.requestPlatforms, null, state, [], [], done);
});
});
});
describe('startInstructionsRequest', () => {
it('dispatches two actions', done => {
testAction(
actions.startInstructionsRequest,
'linux',
state,
[],
[
{ type: 'selectArchitecture', payload: 'linux' },
{ type: 'requestPlatformsInstructions' },
],
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);
});
});
});
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