diff options
Diffstat (limited to 'spec/frontend/clusters_list')
5 files changed, 104 insertions, 78 deletions
diff --git a/spec/frontend/clusters_list/components/agent_token_spec.js b/spec/frontend/clusters_list/components/agent_token_spec.js index 8d3130b45a6..a92a03fedb6 100644 --- a/spec/frontend/clusters_list/components/agent_token_spec.js +++ b/spec/frontend/clusters_list/components/agent_token_spec.js @@ -1,7 +1,13 @@ -import { GlAlert, GlFormInputGroup } from '@gitlab/ui'; +import { GlAlert, GlFormInputGroup, GlSprintf, GlLink, GlIcon } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { sprintf } from '~/locale'; import AgentToken from '~/clusters_list/components/agent_token.vue'; -import { I18N_AGENT_TOKEN, INSTALL_AGENT_MODAL_ID } from '~/clusters_list/constants'; +import { + I18N_AGENT_TOKEN, + INSTALL_AGENT_MODAL_ID, + NAME_MAX_LENGTH, + HELM_VERSION_POLICY_URL, +} from '~/clusters_list/constants'; import { generateAgentRegistrationCommand } from '~/clusters_list/clusters_util'; import CodeBlock from '~/vue_shared/components/code_block.vue'; import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; @@ -19,15 +25,17 @@ describe('InstallAgentModal', () => { const findCodeBlock = () => wrapper.findComponent(CodeBlock); const findCopyButton = () => wrapper.findComponent(ModalCopyButton); const findInput = () => wrapper.findComponent(GlFormInputGroup); + const findHelmVersionPolicyLink = () => wrapper.findComponent(GlLink); + const findHelmExternalLinkIcon = () => wrapper.findComponent(GlIcon); - const createWrapper = () => { + const createWrapper = (newAgentName = agentName) => { const provide = { kasAddress, kasVersion, }; const propsData = { - agentName, + agentName: newAgentName, agentToken, modalId, }; @@ -35,6 +43,9 @@ describe('InstallAgentModal', () => { wrapper = shallowMountExtended(AgentToken, { provide, propsData, + stubs: { + GlSprintf, + }, }); }; @@ -52,6 +63,17 @@ describe('InstallAgentModal', () => { expect(wrapper.text()).toContain(I18N_AGENT_TOKEN.basicInstallBody); }); + it('shows Helm version policy text with an external link', () => { + expect(wrapper.text()).toContain( + sprintf(I18N_AGENT_TOKEN.helmVersionText, { linkStart: '', linkEnd: ' ' }), + ); + expect(findHelmVersionPolicyLink().attributes()).toMatchObject({ + href: HELM_VERSION_POLICY_URL, + target: '_blank', + }); + expect(findHelmExternalLinkIcon().props()).toMatchObject({ name: 'external-link', size: 12 }); + }); + it('shows advanced agent installation instructions', () => { expect(wrapper.text()).toContain(I18N_AGENT_TOKEN.advancedInstallTitle); }); @@ -79,9 +101,19 @@ describe('InstallAgentModal', () => { it('shows code block with agent installation command', () => { expect(findCodeBlock().props('code')).toContain(`helm upgrade --install ${agentName}`); + expect(findCodeBlock().props('code')).toContain(`--namespace gitlab-agent-${agentName}`); expect(findCodeBlock().props('code')).toContain(`--set config.token=${agentToken}`); expect(findCodeBlock().props('code')).toContain(`--set config.kasAddress=${kasAddress}`); expect(findCodeBlock().props('code')).toContain(`--set image.tag=v${kasVersion}`); }); + + it('truncates the namespace name if it exceeds the maximum length', () => { + const newAgentName = 'agent-name-that-is-too-long-and-needs-to-be-truncated-to-use'; + createWrapper(newAgentName); + + expect(findCodeBlock().props('code')).toContain( + `--namespace gitlab-agent-${newAgentName.substring(0, NAME_MAX_LENGTH)}`, + ); + }); }); }); diff --git a/spec/frontend/clusters_list/components/agents_spec.js b/spec/frontend/clusters_list/components/agents_spec.js index bff1e573dbd..2372ab30300 100644 --- a/spec/frontend/clusters_list/components/agents_spec.js +++ b/spec/frontend/clusters_list/components/agents_spec.js @@ -1,7 +1,7 @@ import { GlAlert, GlKeysetPagination, GlLoadingIcon, GlBanner } from '@gitlab/ui'; -import { createLocalVue, shallowMount } from '@vue/test-utils'; +import { shallowMount } from '@vue/test-utils'; import VueApollo from 'vue-apollo'; -import { nextTick } from 'vue'; +import Vue, { nextTick } from 'vue'; import AgentEmptyState from '~/clusters_list/components/agent_empty_state.vue'; import AgentTable from '~/clusters_list/components/agent_table.vue'; import Agents from '~/clusters_list/components/agents.vue'; @@ -12,10 +12,10 @@ import { } from '~/clusters_list/constants'; import getAgentsQuery from '~/clusters_list/graphql/queries/get_agents.query.graphql'; import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; -const localVue = createLocalVue(); -localVue.use(VueApollo); +Vue.use(VueApollo); describe('Agents', () => { let wrapper; @@ -34,9 +34,10 @@ describe('Agents', () => { pageInfo = null, trees = [], count = 0, + queryResponse = null, }) => { const provide = provideData; - const apolloQueryResponse = { + const queryResponseData = { data: { project: { id: '1', @@ -51,13 +52,12 @@ describe('Agents', () => { }, }, }; + const agentQueryResponse = + queryResponse || jest.fn().mockResolvedValue(queryResponseData, provide); - const apolloProvider = createMockApollo([ - [getAgentsQuery, jest.fn().mockResolvedValue(apolloQueryResponse, provide)], - ]); + const apolloProvider = createMockApollo([[getAgentsQuery, agentQueryResponse]]); wrapper = shallowMount(Agents, { - localVue, apolloProvider, propsData: { ...defaultProps, @@ -313,24 +313,11 @@ describe('Agents', () => { }); describe('when agents query is loading', () => { - const mocks = { - $apollo: { - queries: { - agents: { - loading: true, - }, - }, - }, - }; - - beforeEach(async () => { - wrapper = shallowMount(Agents, { - mocks, - propsData: defaultProps, - provide: provideData, + beforeEach(() => { + createWrapper({ + queryResponse: jest.fn().mockReturnValue(new Promise(() => {})), }); - - await nextTick(); + return waitForPromises(); }); it('displays a loading icon', () => { diff --git a/spec/frontend/clusters_list/components/available_agents_dropwdown_spec.js b/spec/frontend/clusters_list/components/available_agents_dropwdown_spec.js index 197735d3c77..02b455d0b61 100644 --- a/spec/frontend/clusters_list/components/available_agents_dropwdown_spec.js +++ b/spec/frontend/clusters_list/components/available_agents_dropwdown_spec.js @@ -1,34 +1,31 @@ -import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui'; +import { GlCollapsibleListbox, GlButton } from '@gitlab/ui'; +import { nextTick } from 'vue'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import { ENTER_KEY } from '~/lib/utils/keys'; import AvailableAgentsDropdown from '~/clusters_list/components/available_agents_dropdown.vue'; import { I18N_AVAILABLE_AGENTS_DROPDOWN } from '~/clusters_list/constants'; describe('AvailableAgentsDropdown', () => { let wrapper; + const configuredAgent = 'configured-agent'; + const searchAgentName = 'search-agent'; + const newAgentName = 'new-agent'; + const i18n = I18N_AVAILABLE_AGENTS_DROPDOWN; - const findDropdown = () => wrapper.findComponent(GlDropdown); - const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); - const findFirstAgentItem = () => findDropdownItems().at(0); - const findSearchInput = () => wrapper.findComponent(GlSearchBoxByType); - const findCreateButton = () => wrapper.findByTestId('create-config-button'); + const findDropdown = () => wrapper.findComponent(GlCollapsibleListbox); + const findCreateButton = () => wrapper.findComponent(GlButton); const createWrapper = ({ propsData }) => { wrapper = shallowMountExtended(AvailableAgentsDropdown, { propsData, - stubs: { GlDropdown }, + stubs: { GlCollapsibleListbox }, }); - wrapper.vm.$refs.dropdown.hide = jest.fn(); + wrapper.vm.$refs.dropdown.closeAndFocus = jest.fn(); }; - afterEach(() => { - wrapper.destroy(); - }); - describe('there are agents available', () => { const propsData = { - availableAgents: ['configured-agent', 'search-agent', 'test-agent'], + availableAgents: [configuredAgent, searchAgentName, 'test-agent'], isRegistering: false, }; @@ -37,91 +34,93 @@ describe('AvailableAgentsDropdown', () => { }); it('prompts to select an agent', () => { - expect(findDropdown().props('text')).toBe(i18n.selectAgent); + expect(findDropdown().props('toggleText')).toBe(i18n.selectAgent); }); describe('search agent', () => { it('renders search button', () => { - expect(findSearchInput().exists()).toBe(true); + expect(findDropdown().props('searchable')).toBe(true); }); it('renders all agents when search term is empty', () => { - expect(findDropdownItems()).toHaveLength(3); + expect(findDropdown().props('items')).toHaveLength(3); }); it('renders only the agent searched for when the search item exists', async () => { - await findSearchInput().vm.$emit('input', 'search-agent'); - - expect(findDropdownItems()).toHaveLength(1); - expect(findFirstAgentItem().text()).toBe('search-agent'); - }); + findDropdown().vm.$emit('search', searchAgentName); + await nextTick(); - it('renders create button when search started', async () => { - await findSearchInput().vm.$emit('input', 'new-agent'); - - expect(findCreateButton().exists()).toBe(true); + expect(findDropdown().props('items')).toMatchObject([ + { text: searchAgentName, value: searchAgentName }, + ]); }); - it("doesn't render create button when search item is found", async () => { - await findSearchInput().vm.$emit('input', 'search-agent'); - - expect(findCreateButton().exists()).toBe(false); + describe('create button', () => { + it.each` + condition | search | createButtonRendered + ${'is rendered'} | ${newAgentName} | ${true} + ${'is not rendered'} | ${''} | ${false} + ${'is not rendered'} | ${searchAgentName} | ${false} + `('$condition when search is "$search"', async ({ search, createButtonRendered }) => { + findDropdown().vm.$emit('search', search); + await nextTick(); + + expect(findCreateButton().exists()).toBe(createButtonRendered); + }); }); }); describe('select existing agent configuration', () => { beforeEach(() => { - findFirstAgentItem().vm.$emit('click'); + findDropdown().vm.$emit('select', configuredAgent); }); - it('emits agentSelected with the name of the clicked agent', () => { - expect(wrapper.emitted('agentSelected')).toEqual([['configured-agent']]); + it('emits `agentSelected` with the name of the clicked agent', () => { + expect(wrapper.emitted('agentSelected')).toEqual([[configuredAgent]]); }); it('marks the clicked item as selected', () => { - expect(findDropdown().props('text')).toBe('configured-agent'); - expect(findFirstAgentItem().props('isChecked')).toBe(true); + expect(findDropdown().props('toggleText')).toBe(configuredAgent); }); }); describe('create new agent configuration', () => { beforeEach(async () => { - await findSearchInput().vm.$emit('input', 'new-agent'); + findDropdown().vm.$emit('search', newAgentName); + await nextTick(); findCreateButton().vm.$emit('click'); }); it('emits agentSelected with the name of the clicked agent', () => { - expect(wrapper.emitted('agentSelected')).toEqual([['new-agent']]); + expect(wrapper.emitted('agentSelected')).toEqual([[newAgentName]]); }); it('marks the clicked item as selected', () => { - expect(findDropdown().props('text')).toBe('new-agent'); + expect(findDropdown().props('toggleText')).toBe(newAgentName); }); }); describe('click enter to register new agent without configuration', () => { beforeEach(async () => { - await findSearchInput().vm.$emit('input', 'new-agent'); - await findSearchInput().vm.$emit('keydown', new KeyboardEvent({ key: ENTER_KEY })); + const dropdown = findDropdown(); + dropdown.vm.$emit('search', newAgentName); + await nextTick(); + await dropdown.trigger('keydown.enter'); }); it('emits agentSelected with the name of the clicked agent', () => { - expect(wrapper.emitted('agentSelected')).toEqual([['new-agent']]); + expect(wrapper.emitted('agentSelected')).toEqual([[newAgentName]]); }); it('marks the clicked item as selected', () => { - expect(findDropdown().props('text')).toBe('new-agent'); - }); - - it('closes the dropdown', () => { - expect(wrapper.vm.$refs.dropdown.hide).toHaveBeenCalledTimes(1); + expect(findDropdown().props('toggleText')).toBe(newAgentName); }); }); }); describe('registration in progress', () => { const propsData = { - availableAgents: ['configured-agent'], + availableAgents: [configuredAgent], isRegistering: true, }; @@ -130,7 +129,7 @@ describe('AvailableAgentsDropdown', () => { }); it('updates the text in the dropdown', () => { - expect(findDropdown().props('text')).toBe(i18n.registeringAgent); + expect(findDropdown().props('toggleText')).toBe(i18n.registeringAgent); }); it('displays a loading icon', () => { diff --git a/spec/frontend/clusters_list/components/clusters_spec.js b/spec/frontend/clusters_list/components/clusters_spec.js index a3f42c1f161..e8e705a6384 100644 --- a/spec/frontend/clusters_list/components/clusters_spec.js +++ b/spec/frontend/clusters_list/components/clusters_spec.js @@ -61,6 +61,10 @@ describe('Clusters', () => { let captureException; beforeEach(() => { + jest.spyOn(Sentry, 'withScope').mockImplementation((fn) => { + const mockScope = { setTag: () => {} }; + fn(mockScope); + }); captureException = jest.spyOn(Sentry, 'captureException'); mock = new MockAdapter(axios); diff --git a/spec/frontend/clusters_list/store/actions_spec.js b/spec/frontend/clusters_list/store/actions_spec.js index 09b1f80ff9b..1deebf8b75a 100644 --- a/spec/frontend/clusters_list/store/actions_spec.js +++ b/spec/frontend/clusters_list/store/actions_spec.js @@ -17,6 +17,10 @@ describe('Clusters store actions', () => { describe('reportSentryError', () => { beforeEach(() => { + jest.spyOn(Sentry, 'withScope').mockImplementation((fn) => { + const mockScope = { setTag: () => {} }; + fn(mockScope); + }); captureException = jest.spyOn(Sentry, 'captureException'); }); |