diff options
Diffstat (limited to 'spec/frontend/clusters/agents')
-rw-r--r-- | spec/frontend/clusters/agents/components/create_token_button_spec.js | 257 | ||||
-rw-r--r-- | spec/frontend/clusters/agents/components/token_table_spec.js | 43 |
2 files changed, 288 insertions, 12 deletions
diff --git a/spec/frontend/clusters/agents/components/create_token_button_spec.js b/spec/frontend/clusters/agents/components/create_token_button_spec.js new file mode 100644 index 00000000000..b9a3a851e57 --- /dev/null +++ b/spec/frontend/clusters/agents/components/create_token_button_spec.js @@ -0,0 +1,257 @@ +import { GlButton, GlTooltip, GlModal, GlFormInput, GlFormTextarea, GlAlert } from '@gitlab/ui'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { mockTracking } from 'helpers/tracking_helper'; +import { + EVENT_LABEL_MODAL, + EVENT_ACTIONS_OPEN, + TOKEN_NAME_LIMIT, + TOKEN_STATUS_ACTIVE, + MAX_LIST_COUNT, +} from '~/clusters/agents/constants'; +import createNewAgentToken from '~/clusters/agents/graphql/mutations/create_new_agent_token.mutation.graphql'; +import getClusterAgentQuery from '~/clusters/agents/graphql/queries/get_cluster_agent.query.graphql'; +import AgentToken from '~/clusters_list/components/agent_token.vue'; +import CreateTokenButton from '~/clusters/agents/components/create_token_button.vue'; +import { + clusterAgentToken, + getTokenResponse, + createAgentTokenErrorResponse, +} from '../../mock_data'; + +Vue.use(VueApollo); + +describe('CreateTokenButton', () => { + let wrapper; + let apolloProvider; + let trackingSpy; + let createResponse; + + const clusterAgentId = 'cluster-agent-id'; + const cursor = { + first: MAX_LIST_COUNT, + last: null, + }; + const agentName = 'cluster-agent'; + const projectPath = 'path/to/project'; + + const defaultProvide = { + agentName, + projectPath, + canAdminCluster: true, + }; + const propsData = { + clusterAgentId, + cursor, + }; + + const findModal = () => wrapper.findComponent(GlModal); + const findBtn = () => wrapper.findComponent(GlButton); + const findInput = () => wrapper.findComponent(GlFormInput); + const findTextarea = () => wrapper.findComponent(GlFormTextarea); + const findAlert = () => wrapper.findComponent(GlAlert); + const findTooltip = () => wrapper.findComponent(GlTooltip); + const findAgentInstructions = () => findModal().findComponent(AgentToken); + const findButtonByVariant = (variant) => + findModal() + .findAll(GlButton) + .wrappers.find((button) => button.props('variant') === variant); + const findActionButton = () => findButtonByVariant('confirm'); + const findCancelButton = () => wrapper.findByTestId('agent-token-close-button'); + + const expectDisabledAttribute = (element, disabled) => { + if (disabled) { + expect(element.attributes('disabled')).toBe('true'); + } else { + expect(element.attributes('disabled')).toBeUndefined(); + } + }; + + const createMockApolloProvider = ({ mutationResponse }) => { + createResponse = jest.fn().mockResolvedValue(mutationResponse); + + return createMockApollo([[createNewAgentToken, createResponse]]); + }; + + const writeQuery = () => { + apolloProvider.clients.defaultClient.cache.writeQuery({ + query: getClusterAgentQuery, + data: getTokenResponse.data, + variables: { + agentName, + projectPath, + tokenStatus: TOKEN_STATUS_ACTIVE, + ...cursor, + }, + }); + }; + + const createWrapper = async ({ provideData = {} } = {}) => { + wrapper = shallowMountExtended(CreateTokenButton, { + apolloProvider, + provide: { + ...defaultProvide, + ...provideData, + }, + propsData, + stubs: { + GlModal, + GlTooltip, + }, + }); + wrapper.vm.$refs.modal.hide = jest.fn(); + + trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + }; + + const mockCreatedResponse = (mutationResponse) => { + apolloProvider = createMockApolloProvider({ mutationResponse }); + writeQuery(); + + createWrapper(); + + findInput().vm.$emit('input', 'new-token'); + findTextarea().vm.$emit('input', 'new-token-description'); + findActionButton().vm.$emit('click'); + + return waitForPromises(); + }; + + beforeEach(() => { + createWrapper(); + }); + + afterEach(() => { + wrapper.destroy(); + apolloProvider = null; + createResponse = null; + }); + + describe('create agent token action', () => { + it('displays create agent token button', () => { + expect(findBtn().text()).toBe('Create token'); + }); + + describe('when user cannot create token', () => { + beforeEach(() => { + createWrapper({ provideData: { canAdminCluster: false } }); + }); + + it('disabled the button', () => { + expect(findBtn().attributes('disabled')).toBe('true'); + }); + + it('shows a disabled tooltip', () => { + expect(findTooltip().attributes('title')).toBe( + 'Requires a Maintainer or greater role to perform these actions', + ); + }); + }); + + describe('when user can create a token and clicks the button', () => { + beforeEach(() => { + findBtn().vm.$emit('click'); + }); + + it('displays a token creation modal', () => { + expect(findModal().isVisible()).toBe(true); + }); + + describe('initial state', () => { + it('renders an input for the token name', () => { + expect(findInput().exists()).toBe(true); + expectDisabledAttribute(findInput(), false); + expect(findInput().attributes('max-length')).toBe(TOKEN_NAME_LIMIT.toString()); + }); + + it('renders a textarea for the token description', () => { + expect(findTextarea().exists()).toBe(true); + expectDisabledAttribute(findTextarea(), false); + }); + + it('renders a cancel button', () => { + expect(findCancelButton().isVisible()).toBe(true); + expectDisabledAttribute(findCancelButton(), false); + }); + + it('renders a disabled next button', () => { + expect(findActionButton().text()).toBe('Create token'); + expectDisabledAttribute(findActionButton(), true); + }); + + it('sends tracking event for modal shown', () => { + findModal().vm.$emit('show'); + expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTIONS_OPEN, { + label: EVENT_LABEL_MODAL, + }); + }); + }); + + describe('when user inputs the token name', () => { + beforeEach(() => { + expectDisabledAttribute(findActionButton(), true); + findInput().vm.$emit('input', 'new-token'); + }); + + it('enables the next button', () => { + expectDisabledAttribute(findActionButton(), false); + }); + }); + + describe('when user clicks the create-token button', () => { + beforeEach(async () => { + const loadingResponse = new Promise(() => {}); + await mockCreatedResponse(loadingResponse); + + findInput().vm.$emit('input', 'new-token'); + findActionButton().vm.$emit('click'); + }); + + it('disables the create-token button', () => { + expectDisabledAttribute(findActionButton(), true); + }); + + it('hides the cancel button', () => { + expect(findCancelButton().exists()).toBe(false); + }); + }); + + describe('creating a new token', () => { + beforeEach(async () => { + await mockCreatedResponse(clusterAgentToken); + }); + + it('creates a token', () => { + expect(createResponse).toHaveBeenCalledWith({ + input: { clusterAgentId, name: 'new-token', description: 'new-token-description' }, + }); + }); + + it('shows agent instructions', () => { + expect(findAgentInstructions().exists()).toBe(true); + }); + + it('renders a close button', () => { + expect(findActionButton().isVisible()).toBe(true); + expect(findActionButton().text()).toBe('Close'); + expectDisabledAttribute(findActionButton(), false); + }); + }); + + describe('error creating a new token', () => { + beforeEach(async () => { + await mockCreatedResponse(createAgentTokenErrorResponse); + }); + + it('displays the error message', async () => { + expect(findAlert().text()).toBe( + createAgentTokenErrorResponse.data.clusterAgentTokenCreate.errors[0], + ); + }); + }); + }); + }); +}); diff --git a/spec/frontend/clusters/agents/components/token_table_spec.js b/spec/frontend/clusters/agents/components/token_table_spec.js index 47ff944dd84..f6baaf87fa4 100644 --- a/spec/frontend/clusters/agents/components/token_table_spec.js +++ b/spec/frontend/clusters/agents/components/token_table_spec.js @@ -1,8 +1,10 @@ -import { GlEmptyState, GlLink, GlTooltip, GlTruncate } from '@gitlab/ui'; +import { GlEmptyState, GlTooltip, GlTruncate } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import TokenTable from '~/clusters/agents/components/token_table.vue'; +import CreateTokenButton from '~/clusters/agents/components/create_token_button.vue'; import { useFakeDate } from 'helpers/fake_date'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import { MAX_LIST_COUNT } from '~/clusters/agents/constants'; describe('ClusterAgentTokenTable', () => { let wrapper; @@ -28,13 +30,26 @@ describe('ClusterAgentTokenTable', () => { name: 'token-2', }, ]; + const clusterAgentId = 'cluster-agent-id'; + const cursor = { + first: MAX_LIST_COUNT, + last: null, + }; + + const provide = { + agentName: 'cluster-agent', + projectPath: 'path/to/project', + canAdminCluster: true, + }; const createComponent = (tokens) => { - wrapper = extendedWrapper(mount(TokenTable, { propsData: { tokens } })); + wrapper = extendedWrapper( + mount(TokenTable, { propsData: { tokens, clusterAgentId, cursor }, provide }), + ); }; - const findEmptyState = () => wrapper.find(GlEmptyState); - const findLink = () => wrapper.find(GlLink); + const findEmptyState = () => wrapper.findComponent(GlEmptyState); + const findCreateTokenBtn = () => wrapper.findComponent(CreateTokenButton); beforeEach(() => { return createComponent(defaultTokens); @@ -44,11 +59,15 @@ describe('ClusterAgentTokenTable', () => { wrapper.destroy(); }); - it('displays a learn more link', () => { - const learnMoreLink = findLink(); + it('displays the create token button', () => { + expect(findCreateTokenBtn().exists()).toBe(true); + }); - expect(learnMoreLink.exists()).toBe(true); - expect(learnMoreLink.text()).toBe(TokenTable.i18n.learnMore); + it('passes the correct params to the create token component', () => { + expect(findCreateTokenBtn().props()).toMatchObject({ + clusterAgentId, + cursor, + }); }); it.each` @@ -56,7 +75,7 @@ describe('ClusterAgentTokenTable', () => { ${'token-1'} | ${0} ${'token-2'} | ${1} `('displays token name "$name" for line "$lineNumber"', ({ name, lineNumber }) => { - const tokens = wrapper.findAll('[data-testid="agent-token-name"]'); + const tokens = wrapper.findAllByTestId('agent-token-name'); const token = tokens.at(lineNumber); expect(token.text()).toBe(name); @@ -83,7 +102,7 @@ describe('ClusterAgentTokenTable', () => { `( 'displays created information "$createdText" for line "$lineNumber"', ({ createdText, lineNumber }) => { - const tokens = wrapper.findAll('[data-testid="agent-token-created-time"]'); + const tokens = wrapper.findAllByTestId('agent-token-created-time'); const token = tokens.at(lineNumber); expect(token.text()).toBe(createdText); @@ -97,7 +116,7 @@ describe('ClusterAgentTokenTable', () => { `( 'displays creator information "$createdBy" for line "$lineNumber"', ({ createdBy, lineNumber }) => { - const tokens = wrapper.findAll('[data-testid="agent-token-created-user"]'); + const tokens = wrapper.findAllByTestId('agent-token-created-user'); const token = tokens.at(lineNumber); expect(token.text()).toBe(createdBy); @@ -111,7 +130,7 @@ describe('ClusterAgentTokenTable', () => { `( 'displays description information "$description" for line "$lineNumber"', ({ description, truncatesText, hasTooltip, lineNumber }) => { - const tokens = wrapper.findAll('[data-testid="agent-token-description"]'); + const tokens = wrapper.findAllByTestId('agent-token-description'); const token = tokens.at(lineNumber); expect(token.text()).toContain(description); |