diff options
Diffstat (limited to 'spec/frontend/clusters/agents')
3 files changed, 213 insertions, 0 deletions
diff --git a/spec/frontend/clusters/agents/components/agent_integration_status_row_spec.js b/spec/frontend/clusters/agents/components/agent_integration_status_row_spec.js new file mode 100644 index 00000000000..2af64191a88 --- /dev/null +++ b/spec/frontend/clusters/agents/components/agent_integration_status_row_spec.js @@ -0,0 +1,96 @@ +import { GlLink, GlIcon, GlBadge } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import AgentIntegrationStatusRow from '~/clusters/agents/components/agent_integration_status_row.vue'; + +const defaultProps = { + text: 'Default integration status', +}; + +describe('IntegrationStatus', () => { + let wrapper; + + const createWrapper = ({ props = {}, glFeatures = {} } = {}) => { + wrapper = shallowMount(AgentIntegrationStatusRow, { + propsData: { + ...defaultProps, + ...props, + }, + provide: { + glFeatures, + }, + }); + }; + + const findLink = () => wrapper.findComponent(GlLink); + const findIcon = () => wrapper.findComponent(GlIcon); + const findBadge = () => wrapper.findComponent(GlBadge); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('icon', () => { + const icon = 'status-success'; + const iconClass = 'text-success-500'; + it.each` + props | iconName | iconClassName + ${{ icon, iconClass }} | ${icon} | ${iconClass} + ${{ icon }} | ${icon} | ${'text-info'} + ${{ iconClass }} | ${'information'} | ${iconClass} + ${null} | ${'information'} | ${'text-info'} + `('displays correct icon when props are $props', ({ props, iconName, iconClassName }) => { + createWrapper({ props }); + + expect(findIcon().props('name')).toBe(iconName); + expect(findIcon().attributes('class')).toContain(iconClassName); + }); + }); + + describe('helpUrl', () => { + it('displays a link with the correct help url when provided in props', () => { + const props = { + helpUrl: 'help-page-path', + }; + createWrapper({ props }); + + expect(findLink().attributes('href')).toBe(props.helpUrl); + expect(findLink().text()).toBe(defaultProps.text); + }); + + it("displays the text without a link when it's not provided", () => { + createWrapper(); + + expect(findLink().exists()).toBe(false); + expect(wrapper.text()).toBe(defaultProps.text); + }); + }); + + describe('badge', () => { + it('does not display premium feature badge when featureName is not provided', () => { + createWrapper(); + + expect(findBadge().exists()).toBe(false); + }); + + it('does not display premium feature badge when featureName is provided and is available for the project', () => { + const props = { featureName: 'feature' }; + const glFeatures = { feature: true }; + createWrapper({ props, glFeatures }); + + expect(findBadge().exists()).toBe(false); + }); + + it('displays premium feature badge when featureName is provided and is not available for the project', () => { + const props = { featureName: 'feature' }; + const glFeatures = { feature: false }; + createWrapper({ props, glFeatures }); + + expect(findBadge().props()).toMatchObject({ + icon: 'license', + variant: 'tier', + size: 'md', + }); + expect(findBadge().text()).toBe(wrapper.vm.$options.i18n.premiumTitle); + }); + }); +}); diff --git a/spec/frontend/clusters/agents/components/integration_status_spec.js b/spec/frontend/clusters/agents/components/integration_status_spec.js new file mode 100644 index 00000000000..36f0e622452 --- /dev/null +++ b/spec/frontend/clusters/agents/components/integration_status_spec.js @@ -0,0 +1,111 @@ +import { GlCollapse, GlButton, GlIcon } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import IntegrationStatus from '~/clusters/agents/components/integration_status.vue'; +import AgentIntegrationStatusRow from '~/clusters/agents/components/agent_integration_status_row.vue'; +import { ACTIVE_CONNECTION_TIME } from '~/clusters_list/constants'; +import { + INTEGRATION_STATUS_VALID_TOKEN, + INTEGRATION_STATUS_NO_TOKEN, + INTEGRATION_STATUS_RESTRICTED_CI_CD, +} from '~/clusters/agents/constants'; + +const connectedTimeNow = new Date(); +const connectedTimeInactive = new Date(connectedTimeNow.getTime() - ACTIVE_CONNECTION_TIME); + +describe('IntegrationStatus', () => { + let wrapper; + + const createWrapper = (tokens = []) => { + wrapper = shallowMountExtended(IntegrationStatus, { + propsData: { tokens }, + }); + }; + + const findCollapseButton = () => wrapper.findComponent(GlButton); + const findCollapse = () => wrapper.findComponent(GlCollapse); + const findStatusIcon = () => wrapper.findComponent(GlIcon); + const findAgentStatus = () => wrapper.findByTestId('agent-status'); + const findAgentIntegrationStatusRows = () => wrapper.findAllComponents(AgentIntegrationStatusRow); + + afterEach(() => { + wrapper.destroy(); + }); + + it.each` + lastUsedAt | status | iconName + ${null} | ${'Never connected'} | ${'status-neutral'} + ${connectedTimeNow} | ${'Connected'} | ${'status-success'} + ${connectedTimeInactive} | ${'Not connected'} | ${'status-alert'} + `( + 'displays correct text and icon when agent connection status is "$status"', + ({ lastUsedAt, status, iconName }) => { + const tokens = [{ lastUsedAt }]; + createWrapper(tokens); + + expect(findStatusIcon().props('name')).toBe(iconName); + expect(findAgentStatus().text()).toBe(status); + }, + ); + + describe('default', () => { + beforeEach(() => { + createWrapper(); + }); + + it('shows the collapse toggle button', () => { + expect(findCollapseButton().text()).toBe(wrapper.vm.$options.i18n.title); + expect(findCollapseButton().attributes()).toMatchObject({ + variant: 'link', + icon: 'chevron-right', + size: 'small', + }); + }); + + it('sets collapse component as invisible by default', () => { + expect(findCollapse().props('visible')).toBeUndefined(); + }); + }); + + describe('when user clicks collapse toggle', () => { + beforeEach(() => { + createWrapper(); + findCollapseButton().vm.$emit('click'); + }); + + it('changes the collapse button icon', () => { + expect(findCollapseButton().props('icon')).toBe('chevron-down'); + }); + + it('sets collapse component as visible', () => { + expect(findCollapse().attributes('visible')).toBe('true'); + }); + }); + + describe('integration status details', () => { + it.each` + agentStatus | tokens | integrationStatuses + ${'active'} | ${[{ lastUsedAt: connectedTimeNow }]} | ${[INTEGRATION_STATUS_VALID_TOKEN, INTEGRATION_STATUS_RESTRICTED_CI_CD]} + ${'inactive'} | ${[{ lastUsedAt: connectedTimeInactive }]} | ${[INTEGRATION_STATUS_RESTRICTED_CI_CD]} + ${'inactive'} | ${[]} | ${[INTEGRATION_STATUS_NO_TOKEN, INTEGRATION_STATUS_RESTRICTED_CI_CD]} + ${'unused'} | ${[{ lastUsedAt: null }]} | ${[INTEGRATION_STATUS_RESTRICTED_CI_CD]} + ${'unused'} | ${[]} | ${[INTEGRATION_STATUS_NO_TOKEN, INTEGRATION_STATUS_RESTRICTED_CI_CD]} + `( + 'displays AgentIntegrationStatusRow component with correct properties when agent status is $agentStatus and agent has $tokens.length tokens', + ({ tokens, integrationStatuses }) => { + createWrapper(tokens); + + expect(findAgentIntegrationStatusRows().length).toBe(integrationStatuses.length); + + integrationStatuses.forEach((integrationStatus, index) => { + expect(findAgentIntegrationStatusRows().at(index).props()).toMatchObject({ + icon: integrationStatus.icon, + iconClass: integrationStatus.iconClass, + text: integrationStatus.text, + helpUrl: integrationStatus.helpUrl || null, + featureName: integrationStatus.featureName || null, + }); + }); + }, + ); + }); +}); diff --git a/spec/frontend/clusters/agents/components/show_spec.js b/spec/frontend/clusters/agents/components/show_spec.js index f2f073544e3..efa85136b17 100644 --- a/spec/frontend/clusters/agents/components/show_spec.js +++ b/spec/frontend/clusters/agents/components/show_spec.js @@ -6,6 +6,7 @@ import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import ClusterAgentShow from '~/clusters/agents/components/show.vue'; import TokenTable from '~/clusters/agents/components/token_table.vue'; import ActivityEvents from '~/clusters/agents/components/activity_events_list.vue'; +import IntegrationStatus from '~/clusters/agents/components/integration_status.vue'; import getAgentQuery from '~/clusters/agents/graphql/queries/get_cluster_agent.query.graphql'; import { useFakeDate } from 'helpers/fake_date'; import createMockApollo from 'helpers/mock_apollo_helper'; @@ -76,6 +77,7 @@ describe('ClusterAgentShow', () => { const findTokenCount = () => wrapper.findByTestId('cluster-agent-token-count').text(); const findEESecurityTabSlot = () => wrapper.findByTestId('ee-security-tab'); const findActivity = () => wrapper.findComponent(ActivityEvents); + const findIntegrationStatus = () => wrapper.findComponent(IntegrationStatus); afterEach(() => { wrapper.destroy(); @@ -107,6 +109,10 @@ describe('ClusterAgentShow', () => { expect(findCreatedText()).toMatchInterpolatedText('Created by user-1 2 days ago'); }); + it('displays agent integration status section', () => { + expect(findIntegrationStatus().exists()).toBe(true); + }); + it('displays token count', () => { expect(findTokenCount()).toMatchInterpolatedText( `${ClusterAgentShow.i18n.tokens} ${defaultClusterAgent.tokens.count}`, |