gitlab-org--gitlab-foss/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_s...

336 lines
10 KiB
JavaScript

import { GlAlert, GlModal, GlButton, GlLoadingIcon, GlSkeletonLoader } from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import getRunnerPlatformsQuery from '~/vue_shared/components/runner_instructions/graphql/queries/get_runner_platforms.query.graphql';
import getRunnerSetupInstructionsQuery from '~/vue_shared/components/runner_instructions/graphql/queries/get_runner_setup.query.graphql';
import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue';
import {
mockGraphqlRunnerPlatforms,
mockGraphqlInstructions,
mockGraphqlInstructionsWindows,
} from './mock_data';
Vue.use(VueApollo);
let resizeCallback;
const MockResizeObserver = {
bind(el, { value }) {
resizeCallback = value;
},
mockResize(size) {
bp.getBreakpointSize.mockReturnValue(size);
resizeCallback();
},
unbind() {
resizeCallback = null;
},
};
Vue.directive('gl-resize-observer', MockResizeObserver);
jest.mock('@gitlab/ui/dist/utils');
describe('RunnerInstructionsModal component', () => {
let wrapper;
let fakeApollo;
let runnerPlatformsHandler;
let runnerSetupInstructionsHandler;
const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
const findGlLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findAlert = () => wrapper.findComponent(GlAlert);
const findModal = () => wrapper.findComponent(GlModal);
const findPlatformButtonGroup = () => wrapper.findByTestId('platform-buttons');
const findPlatformButtons = () => findPlatformButtonGroup().findAllComponents(GlButton);
const findArchitectureDropdownItems = () => wrapper.findAllByTestId('architecture-dropdown-item');
const findBinaryDownloadButton = () => wrapper.findByTestId('binary-download-button');
const findBinaryInstructions = () => wrapper.findByTestId('binary-instructions');
const findRegisterCommand = () => wrapper.findByTestId('register-command');
const createComponent = ({ props, shown = true, ...options } = {}) => {
const requestHandlers = [
[getRunnerPlatformsQuery, runnerPlatformsHandler],
[getRunnerSetupInstructionsQuery, runnerSetupInstructionsHandler],
];
fakeApollo = createMockApollo(requestHandlers);
wrapper = extendedWrapper(
shallowMount(RunnerInstructionsModal, {
propsData: {
modalId: 'runner-instructions-modal',
registrationToken: 'MY_TOKEN',
...props,
},
apolloProvider: fakeApollo,
...options,
}),
);
// trigger open modal
if (shown) {
findModal().vm.$emit('shown');
}
};
beforeEach(async () => {
runnerPlatformsHandler = jest.fn().mockResolvedValue(mockGraphqlRunnerPlatforms);
runnerSetupInstructionsHandler = jest.fn().mockResolvedValue(mockGraphqlInstructions);
});
afterEach(() => {
wrapper.destroy();
});
describe('when the modal is shown', () => {
beforeEach(async () => {
createComponent();
await waitForPromises();
});
it('should not show alert', async () => {
expect(findAlert().exists()).toBe(false);
});
it('should contain a number of platforms buttons', () => {
expect(runnerPlatformsHandler).toHaveBeenCalledWith({});
const buttons = findPlatformButtons();
expect(buttons).toHaveLength(mockGraphqlRunnerPlatforms.data.runnerPlatforms.nodes.length);
});
it('should contain a number of dropdown items for the architecture options', () => {
expect(findArchitectureDropdownItems()).toHaveLength(
mockGraphqlRunnerPlatforms.data.runnerPlatforms.nodes[0].architectures.nodes.length,
);
});
describe('should display default instructions', () => {
const {
installInstructions,
registerInstructions,
} = mockGraphqlInstructions.data.runnerSetup;
it('runner instructions are requested', () => {
expect(runnerSetupInstructionsHandler).toHaveBeenCalledWith({
platform: 'linux',
architecture: 'amd64',
});
});
it('binary instructions are shown', async () => {
const instructions = findBinaryInstructions().text();
expect(instructions).toBe(installInstructions);
});
it('register command is shown with a replaced token', async () => {
const command = findRegisterCommand().text();
expect(command).toBe(
'sudo gitlab-runner register --url http://gdk.test:3000/ --registration-token MY_TOKEN',
);
});
describe('when a register token is not shown', () => {
beforeEach(async () => {
createComponent({ props: { registrationToken: undefined } });
await waitForPromises();
});
it('register command is shown without a defined registration token', () => {
const instructions = findRegisterCommand().text();
expect(instructions).toBe(registerInstructions);
});
});
describe('when providing a defaultPlatformName', () => {
beforeEach(async () => {
createComponent({ props: { defaultPlatformName: 'osx' } });
await waitForPromises();
});
it('runner instructions for the default selected platform are requested', () => {
expect(runnerSetupInstructionsHandler).toHaveBeenCalledWith({
platform: 'osx',
architecture: 'amd64',
});
});
it('sets the focus on the default selected platform', () => {
const findOsxPlatformButton = () => wrapper.find({ ref: 'osx' });
findOsxPlatformButton().element.focus = jest.fn();
findModal().vm.$emit('shown');
expect(findOsxPlatformButton().element.focus).toHaveBeenCalled();
});
});
});
describe('after a platform and architecture are selected', () => {
const windowsIndex = 2;
const { installInstructions } = mockGraphqlInstructionsWindows.data.runnerSetup;
beforeEach(async () => {
runnerSetupInstructionsHandler.mockResolvedValue(mockGraphqlInstructionsWindows);
findPlatformButtons().at(windowsIndex).vm.$emit('click');
await waitForPromises();
});
it('runner instructions are requested', () => {
expect(runnerSetupInstructionsHandler).toHaveBeenLastCalledWith({
platform: 'windows',
architecture: 'amd64',
});
});
it('architecture download link is updated', () => {
const architectures =
mockGraphqlRunnerPlatforms.data.runnerPlatforms.nodes[windowsIndex].architectures.nodes;
expect(findBinaryDownloadButton().attributes('href')).toBe(
architectures[0].downloadLocation,
);
});
it('other binary instructions are shown', () => {
const instructions = findBinaryInstructions().text();
expect(instructions).toBe(installInstructions);
});
it('register command is shown', () => {
const command = findRegisterCommand().text();
expect(command).toBe(
'./gitlab-runner.exe register --url http://gdk.test:3000/ --registration-token MY_TOKEN',
);
});
it('runner instructions are requested with another architecture', async () => {
findArchitectureDropdownItems().at(1).vm.$emit('click');
await waitForPromises();
expect(runnerSetupInstructionsHandler).toHaveBeenLastCalledWith({
platform: 'windows',
architecture: '386',
});
});
});
describe('when the modal resizes', () => {
it('to an xs viewport', async () => {
MockResizeObserver.mockResize('xs');
await nextTick();
expect(findPlatformButtonGroup().attributes('vertical')).toBeTruthy();
});
it('to a non-xs viewport', async () => {
MockResizeObserver.mockResize('sm');
await nextTick();
expect(findPlatformButtonGroup().props('vertical')).toBeFalsy();
});
});
});
describe('when the modal is not shown', () => {
beforeEach(async () => {
createComponent({ shown: false });
await waitForPromises();
});
it('does not fetch instructions', () => {
expect(runnerPlatformsHandler).not.toHaveBeenCalled();
expect(runnerSetupInstructionsHandler).not.toHaveBeenCalled();
});
});
describe('when apollo is loading', () => {
beforeEach(() => {
createComponent();
});
it('should show a skeleton loader', async () => {
expect(findSkeletonLoader().exists()).toBe(true);
expect(findGlLoadingIcon().exists()).toBe(false);
// wait on fetch of both `platforms` and `instructions`
await nextTick();
await nextTick();
expect(findGlLoadingIcon().exists()).toBe(true);
});
it('once loaded, should not show a loading state', async () => {
await waitForPromises();
expect(findSkeletonLoader().exists()).toBe(false);
expect(findGlLoadingIcon().exists()).toBe(false);
});
});
describe('when instructions cannot be loaded', () => {
beforeEach(async () => {
runnerSetupInstructionsHandler.mockRejectedValue();
createComponent();
await waitForPromises();
});
it('should show alert', () => {
expect(findAlert().exists()).toBe(true);
});
it('should not show instructions', () => {
expect(findBinaryInstructions().exists()).toBe(false);
expect(findRegisterCommand().exists()).toBe(false);
});
});
describe('GlModal API', () => {
const getGlModalStub = (methods) => {
return {
...GlModal,
methods: {
...GlModal.methods,
...methods,
},
};
};
describe('show()', () => {
let mockShow;
beforeEach(() => {
mockShow = jest.fn();
createComponent({
shown: false,
stubs: {
GlModal: getGlModalStub({ show: mockShow }),
},
});
});
it('delegates show()', () => {
wrapper.vm.show();
expect(mockShow).toHaveBeenCalledTimes(1);
});
});
});
});