summaryrefslogtreecommitdiff
path: root/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/runner/components/cells/runner_actions_cell_spec.js')
-rw-r--r--spec/frontend/runner/components/cells/runner_actions_cell_spec.js234
1 files changed, 180 insertions, 54 deletions
diff --git a/spec/frontend/runner/components/cells/runner_actions_cell_spec.js b/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
index 12651a82a0c..95f7c38cafc 100644
--- a/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
+++ b/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
@@ -1,18 +1,30 @@
-import { shallowMount } from '@vue/test-utils';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+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 createFlash from '~/flash';
import RunnerActionCell from '~/runner/components/cells/runner_actions_cell.vue';
-import deleteRunnerMutation from '~/runner/graphql/delete_runner.mutation.graphql';
import getRunnersQuery from '~/runner/graphql/get_runners.query.graphql';
+import runnerDeleteMutation from '~/runner/graphql/runner_delete.mutation.graphql';
import runnerUpdateMutation from '~/runner/graphql/runner_update.mutation.graphql';
+import { captureException } from '~/runner/sentry_utils';
+import { runnerData } from '../../mock_data';
-const mockId = '1';
+const mockRunner = runnerData.data.runner;
const getRunnersQueryName = getRunnersQuery.definitions[0].name.value;
+const localVue = createLocalVue();
+localVue.use(VueApollo);
+
+jest.mock('~/flash');
+jest.mock('~/runner/sentry_utils');
+
describe('RunnerTypeCell', () => {
let wrapper;
- let mutate;
+ const runnerDeleteMutationHandler = jest.fn();
+ const runnerUpdateMutationHandler = jest.fn();
const findEditBtn = () => wrapper.findByTestId('edit-runner');
const findToggleActiveBtn = () => wrapper.findByTestId('toggle-active-runner');
@@ -23,26 +35,43 @@ describe('RunnerTypeCell', () => {
shallowMount(RunnerActionCell, {
propsData: {
runner: {
- id: `gid://gitlab/Ci::Runner/${mockId}`,
+ id: mockRunner.id,
active,
},
},
- mocks: {
- $apollo: {
- mutate,
- },
- },
+ localVue,
+ apolloProvider: createMockApollo([
+ [runnerDeleteMutation, runnerDeleteMutationHandler],
+ [runnerUpdateMutation, runnerUpdateMutationHandler],
+ ]),
...options,
}),
);
};
beforeEach(() => {
- mutate = jest.fn();
+ runnerDeleteMutationHandler.mockResolvedValue({
+ data: {
+ runnerDelete: {
+ errors: [],
+ },
+ },
+ });
+
+ runnerUpdateMutationHandler.mockResolvedValue({
+ data: {
+ runnerUpdate: {
+ runner: runnerData.data.runner,
+ errors: [],
+ },
+ },
+ });
});
afterEach(() => {
- mutate.mockReset();
+ runnerDeleteMutationHandler.mockReset();
+ runnerUpdateMutationHandler.mockReset();
+
wrapper.destroy();
});
@@ -58,17 +87,6 @@ describe('RunnerTypeCell', () => {
${'paused'} | ${'Resume'} | ${'play'} | ${false} | ${true}
`('When the runner is $state', ({ label, icon, isActive, newActiveValue }) => {
beforeEach(() => {
- mutate.mockResolvedValue({
- data: {
- runnerUpdate: {
- runner: {
- id: `gid://gitlab/Ci::Runner/1`,
- __typename: 'CiRunner',
- },
- },
- },
- });
-
createComponent({ active: isActive });
});
@@ -93,46 +111,93 @@ describe('RunnerTypeCell', () => {
});
describe(`When clicking on the ${icon} button`, () => {
- beforeEach(async () => {
+ it(`The apollo mutation to set active to ${newActiveValue} is called`, async () => {
+ expect(runnerUpdateMutationHandler).toHaveBeenCalledTimes(0);
+
await findToggleActiveBtn().vm.$emit('click');
- await waitForPromises();
- });
- it(`The apollo mutation to set active to ${newActiveValue} is called`, () => {
- expect(mutate).toHaveBeenCalledTimes(1);
- expect(mutate).toHaveBeenCalledWith({
- mutation: runnerUpdateMutation,
- variables: {
- input: {
- id: `gid://gitlab/Ci::Runner/${mockId}`,
- active: newActiveValue,
- },
+ expect(runnerUpdateMutationHandler).toHaveBeenCalledTimes(1);
+ expect(runnerUpdateMutationHandler).toHaveBeenCalledWith({
+ input: {
+ id: mockRunner.id,
+ active: newActiveValue,
},
});
});
- it('The button does not have a loading state', () => {
+ it('The button does not have a loading state after the mutation occurs', async () => {
+ await findToggleActiveBtn().vm.$emit('click');
+
+ expect(findToggleActiveBtn().props('loading')).toBe(true);
+
+ await waitForPromises();
+
expect(findToggleActiveBtn().props('loading')).toBe(false);
});
});
- });
- describe('When the user clicks a runner', () => {
- beforeEach(() => {
- createComponent();
+ describe('When update fails', () => {
+ describe('On a network error', () => {
+ const mockErrorMsg = 'Update error!';
+
+ beforeEach(async () => {
+ runnerUpdateMutationHandler.mockRejectedValueOnce(new Error(mockErrorMsg));
+
+ await findToggleActiveBtn().vm.$emit('click');
+ });
+
+ it('error is reported to sentry', () => {
+ expect(captureException).toHaveBeenCalledWith({
+ error: new Error(`Network error: ${mockErrorMsg}`),
+ component: 'RunnerActionsCell',
+ });
+ });
- mutate.mockResolvedValue({
- data: {
- runnerDelete: {
- runner: {
- id: `gid://gitlab/Ci::Runner/1`,
- __typename: 'CiRunner',
+ it('error is shown to the user', () => {
+ expect(createFlash).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('On a validation error', () => {
+ const mockErrorMsg = 'Runner not found!';
+ const mockErrorMsg2 = 'User not allowed!';
+
+ beforeEach(async () => {
+ runnerUpdateMutationHandler.mockResolvedValue({
+ data: {
+ runnerUpdate: {
+ runner: runnerData.data.runner,
+ errors: [mockErrorMsg, mockErrorMsg2],
+ },
},
- },
- },
+ });
+
+ await findToggleActiveBtn().vm.$emit('click');
+ });
+
+ it('error is reported to sentry', () => {
+ expect(captureException).toHaveBeenCalledWith({
+ error: new Error(`${mockErrorMsg} ${mockErrorMsg2}`),
+ component: 'RunnerActionsCell',
+ });
+ });
+
+ it('error is shown to the user', () => {
+ expect(createFlash).toHaveBeenCalledTimes(1);
+ });
});
+ });
+ });
+ describe('When the user clicks a runner', () => {
+ beforeEach(() => {
jest.spyOn(window, 'confirm');
+
+ createComponent();
+ });
+
+ afterEach(() => {
+ window.confirm.mockRestore();
});
describe('When the user confirms deletion', () => {
@@ -141,18 +206,28 @@ describe('RunnerTypeCell', () => {
await findDeleteBtn().vm.$emit('click');
});
- it('The user sees a confirmation alert', async () => {
+ it('The user sees a confirmation alert', () => {
expect(window.confirm).toHaveBeenCalledTimes(1);
expect(window.confirm).toHaveBeenCalledWith(expect.any(String));
});
it('The delete mutation is called correctly', () => {
- expect(mutate).toHaveBeenCalledTimes(1);
- expect(mutate).toHaveBeenCalledWith({
- mutation: deleteRunnerMutation,
+ expect(runnerDeleteMutationHandler).toHaveBeenCalledTimes(1);
+ expect(runnerDeleteMutationHandler).toHaveBeenCalledWith({
+ input: { id: mockRunner.id },
+ });
+ });
+
+ it('When delete mutation is called, current runners are refetched', async () => {
+ jest.spyOn(wrapper.vm.$apollo, 'mutate');
+
+ await findDeleteBtn().vm.$emit('click');
+
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
+ mutation: runnerDeleteMutation,
variables: {
input: {
- id: `gid://gitlab/Ci::Runner/${mockId}`,
+ id: mockRunner.id,
},
},
awaitRefetchQueries: true,
@@ -176,6 +251,57 @@ describe('RunnerTypeCell', () => {
expect(findDeleteBtn().attributes('title')).toBe('');
});
+
+ describe('When delete fails', () => {
+ describe('On a network error', () => {
+ const mockErrorMsg = 'Delete error!';
+
+ beforeEach(async () => {
+ runnerDeleteMutationHandler.mockRejectedValueOnce(new Error(mockErrorMsg));
+
+ await findDeleteBtn().vm.$emit('click');
+ });
+
+ it('error is reported to sentry', () => {
+ expect(captureException).toHaveBeenCalledWith({
+ error: new Error(`Network error: ${mockErrorMsg}`),
+ component: 'RunnerActionsCell',
+ });
+ });
+
+ it('error is shown to the user', () => {
+ expect(createFlash).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('On a validation error', () => {
+ const mockErrorMsg = 'Runner not found!';
+ const mockErrorMsg2 = 'User not allowed!';
+
+ beforeEach(async () => {
+ runnerDeleteMutationHandler.mockResolvedValue({
+ data: {
+ runnerDelete: {
+ errors: [mockErrorMsg, mockErrorMsg2],
+ },
+ },
+ });
+
+ await findDeleteBtn().vm.$emit('click');
+ });
+
+ it('error is reported to sentry', () => {
+ expect(captureException).toHaveBeenCalledWith({
+ error: new Error(`${mockErrorMsg} ${mockErrorMsg2}`),
+ component: 'RunnerActionsCell',
+ });
+ });
+
+ it('error is shown to the user', () => {
+ expect(createFlash).toHaveBeenCalledTimes(1);
+ });
+ });
+ });
});
describe('When the user does not confirm deletion', () => {
@@ -189,7 +315,7 @@ describe('RunnerTypeCell', () => {
});
it('The delete mutation is not called', () => {
- expect(mutate).toHaveBeenCalledTimes(0);
+ expect(runnerDeleteMutationHandler).toHaveBeenCalledTimes(0);
});
it('The delete button does not have a loading state', () => {