Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-01-20 12:16:11 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-01-20 12:16:11 +0300
commitedaa33dee2ff2f7ea3fac488d41558eb5f86d68c (patch)
tree11f143effbfeba52329fb7afbd05e6e2a3790241 /spec/frontend/clusters_list
parentd8a5691316400a0f7ec4f83832698f1988eb27c1 (diff)
Add latest changes from gitlab-org/gitlab@14-7-stable-eev14.7.0-rc42
Diffstat (limited to 'spec/frontend/clusters_list')
-rw-r--r--spec/frontend/clusters_list/components/agent_options_spec.js211
-rw-r--r--spec/frontend/clusters_list/components/agent_table_spec.js26
-rw-r--r--spec/frontend/clusters_list/components/clusters_spec.js2
-rw-r--r--spec/frontend/clusters_list/mocks/apollo.js12
4 files changed, 249 insertions, 2 deletions
diff --git a/spec/frontend/clusters_list/components/agent_options_spec.js b/spec/frontend/clusters_list/components/agent_options_spec.js
new file mode 100644
index 00000000000..05bab247816
--- /dev/null
+++ b/spec/frontend/clusters_list/components/agent_options_spec.js
@@ -0,0 +1,211 @@
+import { GlDropdown, GlDropdownItem, GlModal, GlFormInput } from '@gitlab/ui';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { ENTER_KEY } from '~/lib/utils/keys';
+import getAgentsQuery from '~/clusters_list/graphql/queries/get_agents.query.graphql';
+import deleteAgentMutation from '~/clusters_list/graphql/mutations/delete_agent.mutation.graphql';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import AgentOptions from '~/clusters_list/components/agent_options.vue';
+import { MAX_LIST_COUNT } from '~/clusters_list/constants';
+import { getAgentResponse, mockDeleteResponse, mockErrorDeleteResponse } from '../mocks/apollo';
+
+Vue.use(VueApollo);
+
+const projectPath = 'path/to/project';
+const defaultBranchName = 'default';
+const maxAgents = MAX_LIST_COUNT;
+const agent = {
+ id: 'agent-id',
+ name: 'agent-name',
+ webPath: 'agent-webPath',
+};
+
+describe('AgentOptions', () => {
+ let wrapper;
+ let toast;
+ let apolloProvider;
+ let deleteResponse;
+
+ const findModal = () => wrapper.findComponent(GlModal);
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findDeleteBtn = () => wrapper.findComponent(GlDropdownItem);
+ const findInput = () => wrapper.findComponent(GlFormInput);
+ const findPrimaryAction = () => findModal().props('actionPrimary');
+ const findPrimaryActionAttributes = (attr) => findPrimaryAction().attributes[0][attr];
+
+ const createMockApolloProvider = ({ mutationResponse }) => {
+ deleteResponse = jest.fn().mockResolvedValue(mutationResponse);
+
+ return createMockApollo([[deleteAgentMutation, deleteResponse]]);
+ };
+
+ const writeQuery = () => {
+ apolloProvider.clients.defaultClient.cache.writeQuery({
+ query: getAgentsQuery,
+ variables: {
+ projectPath,
+ defaultBranchName,
+ first: maxAgents,
+ last: null,
+ },
+ data: getAgentResponse.data,
+ });
+ };
+
+ const createWrapper = ({ mutationResponse = mockDeleteResponse } = {}) => {
+ apolloProvider = createMockApolloProvider({ mutationResponse });
+ const provide = {
+ projectPath,
+ };
+ const propsData = {
+ defaultBranchName,
+ maxAgents,
+ agent,
+ };
+
+ toast = jest.fn();
+
+ wrapper = shallowMountExtended(AgentOptions, {
+ apolloProvider,
+ provide,
+ propsData,
+ mocks: { $toast: { show: toast } },
+ stubs: { GlModal },
+ });
+ wrapper.vm.$refs.modal.hide = jest.fn();
+
+ writeQuery();
+ return wrapper.vm.$nextTick();
+ };
+
+ const submitAgentToDelete = async () => {
+ findDeleteBtn().vm.$emit('click');
+ findInput().vm.$emit('input', agent.name);
+ await findModal().vm.$emit('primary');
+ };
+
+ beforeEach(() => {
+ return createWrapper({});
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ apolloProvider = null;
+ deleteResponse = null;
+ toast = null;
+ });
+
+ describe('delete agent action', () => {
+ it('displays a delete button', () => {
+ expect(findDeleteBtn().text()).toBe('Delete agent');
+ });
+
+ describe('when clicking the delete button', () => {
+ beforeEach(() => {
+ findDeleteBtn().vm.$emit('click');
+ });
+
+ it('displays a delete confirmation modal', () => {
+ expect(findModal().isVisible()).toBe(true);
+ });
+ });
+
+ describe.each`
+ condition | agentName | isDisabled | mutationCalled
+ ${'the input with agent name is missing'} | ${''} | ${true} | ${false}
+ ${'the input with agent name is incorrect'} | ${'wrong-name'} | ${true} | ${false}
+ ${'the input with agent name is correct'} | ${agent.name} | ${false} | ${true}
+ `('when $condition', ({ agentName, isDisabled, mutationCalled }) => {
+ beforeEach(() => {
+ findDeleteBtn().vm.$emit('click');
+ findInput().vm.$emit('input', agentName);
+ });
+
+ it(`${isDisabled ? 'disables' : 'enables'} the modal primary button`, () => {
+ expect(findPrimaryActionAttributes('disabled')).toBe(isDisabled);
+ });
+
+ describe('when user clicks the modal primary button', () => {
+ beforeEach(async () => {
+ await findModal().vm.$emit('primary');
+ });
+
+ if (mutationCalled) {
+ it('calls the delete mutation', () => {
+ expect(deleteResponse).toHaveBeenCalledWith({ input: { id: agent.id } });
+ });
+ } else {
+ it("doesn't call the delete mutation", () => {
+ expect(deleteResponse).not.toHaveBeenCalled();
+ });
+ }
+ });
+
+ describe('when user presses the enter button', () => {
+ beforeEach(async () => {
+ await findInput().vm.$emit('keydown', new KeyboardEvent({ key: ENTER_KEY }));
+ });
+
+ if (mutationCalled) {
+ it('calls the delete mutation', () => {
+ expect(deleteResponse).toHaveBeenCalledWith({ input: { id: agent.id } });
+ });
+ } else {
+ it("doesn't call the delete mutation", () => {
+ expect(deleteResponse).not.toHaveBeenCalled();
+ });
+ }
+ });
+ });
+
+ describe('when agent was deleted successfully', () => {
+ beforeEach(async () => {
+ await submitAgentToDelete();
+ });
+
+ it('calls the toast action', () => {
+ expect(toast).toHaveBeenCalledWith(`${agent.name} successfully deleted`);
+ });
+ });
+ });
+
+ describe('when getting an error deleting agent', () => {
+ beforeEach(async () => {
+ await createWrapper({ mutationResponse: mockErrorDeleteResponse });
+
+ submitAgentToDelete();
+ });
+
+ it('displays the error message', () => {
+ expect(toast).toHaveBeenCalledWith('could not delete agent');
+ });
+ });
+
+ describe('when the delete modal was closed', () => {
+ beforeEach(async () => {
+ const loadingResponse = new Promise(() => {});
+ await createWrapper({ mutationResponse: loadingResponse });
+
+ submitAgentToDelete();
+ });
+
+ it('reenables the options dropdown', async () => {
+ expect(findPrimaryActionAttributes('loading')).toBe(true);
+ expect(findDropdown().attributes('disabled')).toBe('true');
+
+ await findModal().vm.$emit('hide');
+
+ expect(findPrimaryActionAttributes('loading')).toBe(false);
+ expect(findDropdown().attributes('disabled')).toBeUndefined();
+ });
+
+ it('clears the agent name input', async () => {
+ expect(findInput().attributes('value')).toBe(agent.name);
+
+ await findModal().vm.$emit('hide');
+
+ expect(findInput().attributes('value')).toBeUndefined();
+ });
+ });
+});
diff --git a/spec/frontend/clusters_list/components/agent_table_spec.js b/spec/frontend/clusters_list/components/agent_table_spec.js
index a6d76b069cf..887c17bb4ad 100644
--- a/spec/frontend/clusters_list/components/agent_table_spec.js
+++ b/spec/frontend/clusters_list/components/agent_table_spec.js
@@ -1,16 +1,22 @@
import { GlLink, GlIcon } from '@gitlab/ui';
import AgentTable from '~/clusters_list/components/agent_table.vue';
+import AgentOptions from '~/clusters_list/components/agent_options.vue';
import { ACTIVE_CONNECTION_TIME } from '~/clusters_list/constants';
import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { stubComponent } from 'helpers/stub_component';
import timeagoMixin from '~/vue_shared/mixins/timeago';
const connectedTimeNow = new Date();
const connectedTimeInactive = new Date(connectedTimeNow.getTime() - ACTIVE_CONNECTION_TIME);
+const provideData = {
+ projectPath: 'path/to/project',
+};
const propsData = {
agents: [
{
name: 'agent-1',
+ id: 'agent-1-id',
configFolder: {
webPath: '/agent/full/path',
},
@@ -21,6 +27,7 @@ const propsData = {
},
{
name: 'agent-2',
+ id: 'agent-2-id',
webPath: '/agent-2',
status: 'active',
lastContact: connectedTimeNow.getTime(),
@@ -34,6 +41,7 @@ const propsData = {
},
{
name: 'agent-3',
+ id: 'agent-3-id',
webPath: '/agent-3',
status: 'inactive',
lastContact: connectedTimeInactive.getTime(),
@@ -48,6 +56,10 @@ const propsData = {
],
};
+const AgentOptionsStub = stubComponent(AgentOptions, {
+ template: `<div></div>`,
+});
+
describe('AgentTable', () => {
let wrapper;
@@ -57,15 +69,21 @@ describe('AgentTable', () => {
const findLastContactText = (at) => wrapper.findAllByTestId('cluster-agent-last-contact').at(at);
const findConfiguration = (at) =>
wrapper.findAllByTestId('cluster-agent-configuration-link').at(at);
+ const findAgentOptions = () => wrapper.findAllComponents(AgentOptions);
beforeEach(() => {
- wrapper = mountExtended(AgentTable, { propsData });
+ wrapper = mountExtended(AgentTable, {
+ propsData,
+ provide: provideData,
+ stubs: {
+ AgentOptions: AgentOptionsStub,
+ },
+ });
});
afterEach(() => {
if (wrapper) {
wrapper.destroy();
- wrapper = null;
}
});
@@ -108,5 +126,9 @@ describe('AgentTable', () => {
expect(findLink.exists()).toBe(hasLink);
expect(findConfiguration(lineNumber).text()).toBe(agentPath);
});
+
+ it('displays actions menu for each agent', () => {
+ expect(findAgentOptions()).toHaveLength(3);
+ });
});
});
diff --git a/spec/frontend/clusters_list/components/clusters_spec.js b/spec/frontend/clusters_list/components/clusters_spec.js
index a34202c789d..9af25a534d8 100644
--- a/spec/frontend/clusters_list/components/clusters_spec.js
+++ b/spec/frontend/clusters_list/components/clusters_spec.js
@@ -272,6 +272,8 @@ describe('Clusters', () => {
describe('when updating currentPage', () => {
beforeEach(() => {
mockPollingApi(200, apiData, paginationHeader(totalSecondPage, perPage, 2));
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({ currentPage: 2 });
return axios.waitForAll();
});
diff --git a/spec/frontend/clusters_list/mocks/apollo.js b/spec/frontend/clusters_list/mocks/apollo.js
index 804f9834506..c4a31ed4394 100644
--- a/spec/frontend/clusters_list/mocks/apollo.js
+++ b/spec/frontend/clusters_list/mocks/apollo.js
@@ -75,3 +75,15 @@ export const getAgentResponse = {
},
},
};
+
+export const mockDeleteResponse = {
+ data: { clusterAgentDelete: { errors: [] } },
+};
+
+export const mockErrorDeleteResponse = {
+ data: {
+ clusterAgentDelete: {
+ errors: ['could not delete agent'],
+ },
+ },
+};