diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-15 00:13:24 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-15 00:13:24 +0300 |
commit | 6df3cf6b4acbfe83f7a19e40bbd1471af5d6ce95 (patch) | |
tree | ad732f3108fb02f1e8f95f13cc87edaedbb728b1 /spec/frontend/environments | |
parent | 5b62f8e3ee531f63ce3c49cae03e2a618ba51615 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/environments')
-rw-r--r-- | spec/frontend/environments/graphql/mock_data.js | 10 | ||||
-rw-r--r-- | spec/frontend/environments/graphql/resolvers_spec.js | 57 | ||||
-rw-r--r-- | spec/frontend/environments/kubernetes_overview_spec.js | 56 | ||||
-rw-r--r-- | spec/frontend/environments/kubernetes_pods_spec.js | 114 | ||||
-rw-r--r-- | spec/frontend/environments/mock_data.js | 3 | ||||
-rw-r--r-- | spec/frontend/environments/new_environment_item_spec.js | 10 |
6 files changed, 239 insertions, 11 deletions
diff --git a/spec/frontend/environments/graphql/mock_data.js b/spec/frontend/environments/graphql/mock_data.js index b5435990042..8d91ffe5ffc 100644 --- a/spec/frontend/environments/graphql/mock_data.js +++ b/spec/frontend/environments/graphql/mock_data.js @@ -801,6 +801,14 @@ export const resolvedDeploymentDetails = { export const agent = { project: 'agent-project', - id: '1', + id: 'gid://gitlab/ClusterAgent/1', name: 'agent-name', + kubernetesNamespace: 'agent-namespace', }; + +const runningPod = { status: { phase: 'Running' } }; +const pendingPod = { status: { phase: 'Pending' } }; +const succeededPod = { status: { phase: 'Succeeded' } }; +const failedPod = { status: { phase: 'Failed' } }; + +export const k8sPodsMock = [runningPod, runningPod, pendingPod, succeededPod, failedPod, failedPod]; diff --git a/spec/frontend/environments/graphql/resolvers_spec.js b/spec/frontend/environments/graphql/resolvers_spec.js index 2c223d3a1a7..c66844f5f24 100644 --- a/spec/frontend/environments/graphql/resolvers_spec.js +++ b/spec/frontend/environments/graphql/resolvers_spec.js @@ -1,4 +1,5 @@ import MockAdapter from 'axios-mock-adapter'; +import { CoreV1Api } from '@gitlab/cluster-client'; import { s__ } from '~/locale'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; @@ -17,6 +18,7 @@ import { resolvedEnvironment, folder, resolvedFolder, + k8sPodsMock, } from './mock_data'; const ENDPOINT = `${TEST_HOST}/environments`; @@ -143,6 +145,61 @@ describe('~/frontend/environments/graphql/resolvers', () => { expect(environmentFolder).toEqual(resolvedFolder); }); }); + describe('k8sPods', () => { + const namespace = 'default'; + const configuration = { + basePath: 'kas-proxy/', + baseOptions: { + headers: { 'GitLab-Agent-Id': '1' }, + }, + }; + + const mockPodsListFn = jest.fn().mockImplementation(() => { + return Promise.resolve({ + data: { + items: k8sPodsMock, + }, + }); + }); + + const mockNamespacedPodsListFn = jest.fn().mockImplementation(mockPodsListFn); + const mockAllPodsListFn = jest.fn().mockImplementation(mockPodsListFn); + + beforeEach(() => { + jest + .spyOn(CoreV1Api.prototype, 'listCoreV1NamespacedPod') + .mockImplementation(mockNamespacedPodsListFn); + jest + .spyOn(CoreV1Api.prototype, 'listCoreV1PodForAllNamespaces') + .mockImplementation(mockAllPodsListFn); + }); + + it('should request namespaced pods from the cluster_client library if namespace is specified', async () => { + const pods = await mockResolvers.Query.k8sPods(null, { configuration, namespace }); + + expect(mockNamespacedPodsListFn).toHaveBeenCalledWith(namespace); + expect(mockAllPodsListFn).not.toHaveBeenCalled(); + + expect(pods).toEqual(k8sPodsMock); + }); + it('should request all pods from the cluster_client library if namespace is not specified', async () => { + const pods = await mockResolvers.Query.k8sPods(null, { configuration, namespace: '' }); + + expect(mockAllPodsListFn).toHaveBeenCalled(); + expect(mockNamespacedPodsListFn).not.toHaveBeenCalled(); + + expect(pods).toEqual(k8sPodsMock); + }); + it('should throw an error if the API call fails', async () => { + jest + .spyOn(CoreV1Api.prototype, 'listCoreV1PodForAllNamespaces') + .mockRejectedValue(new Error('API error')); + + await expect(mockResolvers.Query.k8sPods(null, { configuration })).rejects.toThrow( + 'API error', + ); + }); + }); describe('stopEnvironment', () => { it('should post to the stop environment path', async () => { mock.onPost(ENDPOINT).reply(HTTP_STATUS_OK); diff --git a/spec/frontend/environments/kubernetes_overview_spec.js b/spec/frontend/environments/kubernetes_overview_spec.js index 8673c657760..1912fd4a82b 100644 --- a/spec/frontend/environments/kubernetes_overview_spec.js +++ b/spec/frontend/environments/kubernetes_overview_spec.js @@ -1,19 +1,28 @@ import { nextTick } from 'vue'; import { shallowMount } from '@vue/test-utils'; -import { GlCollapse, GlButton } from '@gitlab/ui'; +import { GlCollapse, GlButton, GlAlert } from '@gitlab/ui'; import KubernetesOverview from '~/environments/components/kubernetes_overview.vue'; import KubernetesAgentInfo from '~/environments/components/kubernetes_agent_info.vue'; - -const agent = { - project: 'agent-project', - id: '1', - name: 'agent-name', -}; +import KubernetesPods from '~/environments/components/kubernetes_pods.vue'; +import { agent } from './graphql/mock_data'; +import { mockKasTunnelUrl } from './mock_data'; const propsData = { agentId: agent.id, agentName: agent.name, agentProjectPath: agent.project, + namespace: agent.kubernetesNamespace, +}; + +const provide = { + kasTunnelUrl: mockKasTunnelUrl, +}; + +const configuration = { + basePath: provide.kasTunnelUrl.replace(/\/$/, ''), + baseOptions: { + headers: { 'GitLab-Agent-Id': '1' }, + }, }; describe('~/environments/components/kubernetes_overview.vue', () => { @@ -22,10 +31,13 @@ describe('~/environments/components/kubernetes_overview.vue', () => { const findCollapse = () => wrapper.findComponent(GlCollapse); const findCollapseButton = () => wrapper.findComponent(GlButton); const findAgentInfo = () => wrapper.findComponent(KubernetesAgentInfo); + const findKubernetesPods = () => wrapper.findComponent(KubernetesPods); + const findAlert = () => wrapper.findComponent(GlAlert); const createWrapper = () => { wrapper = shallowMount(KubernetesOverview, { propsData, + provide, }); }; @@ -57,6 +69,7 @@ describe('~/environments/components/kubernetes_overview.vue', () => { it("doesn't render components when the collapse is not visible", () => { expect(findAgentInfo().exists()).toBe(false); + expect(findKubernetesPods().exists()).toBe(false); }); it('opens on click', async () => { @@ -70,15 +83,40 @@ describe('~/environments/components/kubernetes_overview.vue', () => { }); describe('when section is expanded', () => { - it('renders kubernetes agent info', async () => { + beforeEach(() => { createWrapper(); - await toggleCollapse(); + toggleCollapse(); + }); + it('renders kubernetes agent info', () => { expect(findAgentInfo().props()).toEqual({ agentName: agent.name, agentId: agent.id, agentProjectPath: agent.project, }); }); + + it('renders kubernetes pods', () => { + expect(findKubernetesPods().props()).toEqual({ + namespace: agent.kubernetesNamespace, + configuration, + }); + }); + }); + + describe('on cluster error', () => { + beforeEach(() => { + createWrapper(); + toggleCollapse(); + }); + + it('shows alert with the error message', async () => { + const error = 'Error message from pods'; + + findKubernetesPods().vm.$emit('cluster-error', error); + await nextTick(); + + expect(findAlert().text()).toBe(error); + }); }); }); diff --git a/spec/frontend/environments/kubernetes_pods_spec.js b/spec/frontend/environments/kubernetes_pods_spec.js new file mode 100644 index 00000000000..137309d7853 --- /dev/null +++ b/spec/frontend/environments/kubernetes_pods_spec.js @@ -0,0 +1,114 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import { shallowMount } from '@vue/test-utils'; +import { GlLoadingIcon } from '@gitlab/ui'; +import { GlSingleStat } from '@gitlab/ui/dist/charts'; +import waitForPromises from 'helpers/wait_for_promises'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import KubernetesPods from '~/environments/components/kubernetes_pods.vue'; +import { mockKasTunnelUrl } from './mock_data'; +import { k8sPodsMock } from './graphql/mock_data'; + +Vue.use(VueApollo); + +describe('~/environments/components/kubernetes_pods.vue', () => { + let wrapper; + + const namespace = 'my-kubernetes-namespace'; + const configuration = { + basePath: mockKasTunnelUrl, + baseOptions: { + headers: { 'GitLab-Agent-Id': '1' }, + }, + }; + + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findAllStats = () => wrapper.findAllComponents(GlSingleStat); + const findSingleStat = (at) => findAllStats().at(at); + + const createApolloProvider = () => { + const mockResolvers = { + Query: { + k8sPods: jest.fn().mockReturnValue(k8sPodsMock), + }, + }; + + return createMockApollo([], mockResolvers); + }; + + const createWrapper = (apolloProvider = createApolloProvider()) => { + wrapper = shallowMount(KubernetesPods, { + propsData: { namespace, configuration }, + apolloProvider, + }); + }; + + describe('mounted', () => { + it('shows the loading icon', () => { + createWrapper(); + + expect(findLoadingIcon().exists()).toBe(true); + }); + + it('hides the loading icon when the list of pods loaded', async () => { + createWrapper(); + await waitForPromises(); + + expect(findLoadingIcon().exists()).toBe(false); + }); + }); + + describe('when gets pods data', () => { + it('renders stats', async () => { + createWrapper(); + await waitForPromises(); + + expect(findAllStats()).toHaveLength(4); + }); + + it.each` + count | title | index + ${2} | ${KubernetesPods.i18n.runningPods} | ${0} + ${1} | ${KubernetesPods.i18n.pendingPods} | ${1} + ${1} | ${KubernetesPods.i18n.succeededPods} | ${2} + ${2} | ${KubernetesPods.i18n.failedPods} | ${3} + `( + 'renders stat with title "$title" and count "$count" at index $index', + async ({ count, title, index }) => { + createWrapper(); + await waitForPromises(); + + expect(findSingleStat(index).props()).toMatchObject({ + value: count, + title, + }); + }, + ); + }); + + describe('when gets an error from the cluster_client API', () => { + const error = new Error('Error from the cluster_client API'); + const createErroredApolloProvider = () => { + const mockResolvers = { + Query: { + k8sPods: jest.fn().mockRejectedValueOnce(error), + }, + }; + + return createMockApollo([], mockResolvers); + }; + + beforeEach(async () => { + createWrapper(createErroredApolloProvider()); + await waitForPromises(); + }); + + it("doesn't show pods stats", () => { + expect(findAllStats()).toHaveLength(0); + }); + + it('emits an error message', () => { + expect(wrapper.emitted('cluster-error')).toMatchObject([[error]]); + }); + }); +}); diff --git a/spec/frontend/environments/mock_data.js b/spec/frontend/environments/mock_data.js index a6d67c26304..bd2c6b7c892 100644 --- a/spec/frontend/environments/mock_data.js +++ b/spec/frontend/environments/mock_data.js @@ -313,6 +313,8 @@ const createEnvironment = (data = {}) => ({ ...data, }); +const mockKasTunnelUrl = 'https://kas.gitlab.com/k8s-proxy'; + export { environment, environmentsList, @@ -321,4 +323,5 @@ export { tableData, deployBoardMockData, createEnvironment, + mockKasTunnelUrl, }; diff --git a/spec/frontend/environments/new_environment_item_spec.js b/spec/frontend/environments/new_environment_item_spec.js index 89a9ca725ba..b4f5263a151 100644 --- a/spec/frontend/environments/new_environment_item_spec.js +++ b/spec/frontend/environments/new_environment_item_spec.js @@ -12,6 +12,7 @@ import Deployment from '~/environments/components/deployment.vue'; import DeployBoardWrapper from '~/environments/components/deploy_board_wrapper.vue'; import KubernetesOverview from '~/environments/components/kubernetes_overview.vue'; import { resolvedEnvironment, rolloutStatus, agent } from './graphql/mock_data'; +import { mockKasTunnelUrl } from './mock_data'; Vue.use(VueApollo); @@ -26,7 +27,13 @@ describe('~/environments/components/new_environment_item.vue', () => { mountExtended(EnvironmentItem, { apolloProvider, propsData: { environment: resolvedEnvironment, ...propsData }, - provide: { helpPagePath: '/help', projectId: '1', projectPath: '/1', ...provideData }, + provide: { + helpPagePath: '/help', + projectId: '1', + projectPath: '/1', + kasTunnelUrl: mockKasTunnelUrl, + ...provideData, + }, stubs: { transition: stubTransition() }, }); @@ -536,6 +543,7 @@ describe('~/environments/components/new_environment_item.vue', () => { agentProjectPath: agent.project, agentName: agent.name, agentId: agent.id, + namespace: agent.kubernetesNamespace, }); }); |