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:
Diffstat (limited to 'spec/frontend/kubernetes_dashboard')
-rw-r--r--spec/frontend/kubernetes_dashboard/components/workload_table_spec.js11
-rw-r--r--spec/frontend/kubernetes_dashboard/graphql/mock_data.js246
-rw-r--r--spec/frontend/kubernetes_dashboard/graphql/resolvers/kubernetes_spec.js254
-rw-r--r--spec/frontend/kubernetes_dashboard/helpers/k8s_integration_helper_spec.js80
-rw-r--r--spec/frontend/kubernetes_dashboard/pages/cron_jobs_page_spec.js102
-rw-r--r--spec/frontend/kubernetes_dashboard/pages/jobs_page_spec.js102
-rw-r--r--spec/frontend/kubernetes_dashboard/pages/services_page_spec.js104
7 files changed, 891 insertions, 8 deletions
diff --git a/spec/frontend/kubernetes_dashboard/components/workload_table_spec.js b/spec/frontend/kubernetes_dashboard/components/workload_table_spec.js
index 369b8f32c2d..e873da07a2a 100644
--- a/spec/frontend/kubernetes_dashboard/components/workload_table_spec.js
+++ b/spec/frontend/kubernetes_dashboard/components/workload_table_spec.js
@@ -1,7 +1,7 @@
import { mount } from '@vue/test-utils';
import { GlTable, GlBadge, GlPagination } from '@gitlab/ui';
import WorkloadTable from '~/kubernetes_dashboard/components/workload_table.vue';
-import { TABLE_HEADING_CLASSES, PAGE_SIZE } from '~/kubernetes_dashboard/constants';
+import { PAGE_SIZE } from '~/kubernetes_dashboard/constants';
import { mockPodsTableItems } from '../graphql/mock_data';
let wrapper;
@@ -26,25 +26,24 @@ describe('Workload table component', () => {
{
key: 'name',
label: 'Name',
- thClass: TABLE_HEADING_CLASSES,
sortable: true,
+ tdClass: 'gl-md-w-half gl-lg-w-40p gl-word-break-word',
},
{
key: 'status',
label: 'Status',
- thClass: TABLE_HEADING_CLASSES,
sortable: true,
+ tdClass: 'gl-md-w-15',
},
{
key: 'namespace',
label: 'Namespace',
- thClass: TABLE_HEADING_CLASSES,
sortable: true,
+ tdClass: 'gl-md-w-30p gl-lg-w-40p gl-word-break-word',
},
{
key: 'age',
label: 'Age',
- thClass: TABLE_HEADING_CLASSES,
sortable: true,
},
];
@@ -57,13 +56,11 @@ describe('Workload table component', () => {
{
key: 'field-1',
label: 'Field-1',
- thClass: TABLE_HEADING_CLASSES,
sortable: true,
},
{
key: 'field-2',
label: 'Field-2',
- thClass: TABLE_HEADING_CLASSES,
sortable: true,
},
];
diff --git a/spec/frontend/kubernetes_dashboard/graphql/mock_data.js b/spec/frontend/kubernetes_dashboard/graphql/mock_data.js
index 674425a5bc9..8f733d382b2 100644
--- a/spec/frontend/kubernetes_dashboard/graphql/mock_data.js
+++ b/spec/frontend/kubernetes_dashboard/graphql/mock_data.js
@@ -351,3 +351,249 @@ export const mockDaemonSetsTableItems = [
];
export const k8sDaemonSetsMock = [readyDaemonSet, failedDaemonSet];
+
+const completedJob = {
+ status: { failed: 0, succeeded: 1 },
+ spec: { completions: 1 },
+ metadata: {
+ name: 'job-1',
+ namespace: 'default',
+ creationTimestamp: '2023-07-31T11:50:17Z',
+ labels: {},
+ annotations: {},
+ },
+};
+
+const failedJob = {
+ status: { failed: 1, succeeded: 1 },
+ spec: { completions: 2 },
+ metadata: {
+ name: 'job-2',
+ namespace: 'default',
+ creationTimestamp: '2023-11-21T11:50:59Z',
+ labels: {},
+ annotations: {},
+ },
+};
+
+const anotherFailedJob = {
+ status: { failed: 0, succeeded: 1 },
+ spec: { completions: 2 },
+ metadata: {
+ name: 'job-3',
+ namespace: 'default',
+ creationTimestamp: '2023-11-21T11:50:59Z',
+ labels: {},
+ annotations: {},
+ },
+};
+
+export const mockJobsStats = [
+ {
+ title: 'Completed',
+ value: 1,
+ },
+ {
+ title: 'Failed',
+ value: 2,
+ },
+];
+
+export const mockJobsTableItems = [
+ {
+ name: 'job-1',
+ namespace: 'default',
+ status: 'Completed',
+ age: '114d',
+ labels: {},
+ annotations: {},
+ kind: 'Job',
+ },
+ {
+ name: 'job-2',
+ namespace: 'default',
+ status: 'Failed',
+ age: '1d',
+ labels: {},
+ annotations: {},
+ kind: 'Job',
+ },
+ {
+ name: 'job-3',
+ namespace: 'default',
+ status: 'Failed',
+ age: '1d',
+ labels: {},
+ annotations: {},
+ kind: 'Job',
+ },
+];
+
+export const k8sJobsMock = [completedJob, failedJob, anotherFailedJob];
+
+const readyCronJob = {
+ status: { active: 0, lastScheduleTime: '2023-07-31T11:50:17Z' },
+ spec: { suspend: 0 },
+ metadata: {
+ name: 'cronJob-1',
+ namespace: 'default',
+ creationTimestamp: '2023-07-31T11:50:17Z',
+ labels: {},
+ annotations: {},
+ },
+};
+
+const suspendedCronJob = {
+ status: { active: 0, lastScheduleTime: null },
+ spec: { suspend: 1 },
+ metadata: {
+ name: 'cronJob-2',
+ namespace: 'default',
+ creationTimestamp: '2023-11-21T11:50:59Z',
+ labels: {},
+ annotations: {},
+ },
+};
+
+const failedCronJob = {
+ status: { active: 1, lastScheduleTime: null },
+ spec: { suspend: 0 },
+ metadata: {
+ name: 'cronJob-3',
+ namespace: 'default',
+ creationTimestamp: '2023-11-21T11:50:59Z',
+ labels: {},
+ annotations: {},
+ },
+};
+
+export const mockCronJobsStats = [
+ {
+ title: 'Ready',
+ value: 1,
+ },
+ {
+ title: 'Failed',
+ value: 1,
+ },
+ {
+ title: 'Suspended',
+ value: 1,
+ },
+];
+
+export const mockCronJobsTableItems = [
+ {
+ name: 'cronJob-1',
+ namespace: 'default',
+ status: 'Ready',
+ age: '114d',
+ labels: {},
+ annotations: {},
+ kind: 'CronJob',
+ },
+ {
+ name: 'cronJob-2',
+ namespace: 'default',
+ status: 'Suspended',
+ age: '1d',
+ labels: {},
+ annotations: {},
+ kind: 'CronJob',
+ },
+ {
+ name: 'cronJob-3',
+ namespace: 'default',
+ status: 'Failed',
+ age: '1d',
+ labels: {},
+ annotations: {},
+ kind: 'CronJob',
+ },
+];
+
+export const k8sCronJobsMock = [readyCronJob, suspendedCronJob, failedCronJob];
+
+export const k8sServicesMock = [
+ {
+ metadata: {
+ name: 'my-first-service',
+ namespace: 'default',
+ creationTimestamp: '2023-07-31T11:50:17Z',
+ labels: {},
+ annotations: {},
+ },
+ spec: {
+ ports: [
+ {
+ name: 'https',
+ protocol: 'TCP',
+ port: 443,
+ targetPort: 8443,
+ },
+ ],
+ clusterIP: '10.96.0.1',
+ externalIP: '-',
+ type: 'ClusterIP',
+ },
+ },
+ {
+ metadata: {
+ name: 'my-second-service',
+ namespace: 'default',
+ creationTimestamp: '2023-11-21T11:50:59Z',
+ labels: {},
+ annotations: {},
+ },
+ spec: {
+ ports: [
+ {
+ name: 'http',
+ protocol: 'TCP',
+ appProtocol: 'http',
+ port: 80,
+ targetPort: 'http',
+ nodePort: 31989,
+ },
+ {
+ name: 'https',
+ protocol: 'TCP',
+ appProtocol: 'https',
+ port: 443,
+ targetPort: 'https',
+ nodePort: 32679,
+ },
+ ],
+ clusterIP: '10.105.219.238',
+ externalIP: '-',
+ type: 'NodePort',
+ },
+ },
+];
+
+export const mockServicesTableItems = [
+ {
+ name: 'my-first-service',
+ namespace: 'default',
+ type: 'ClusterIP',
+ clusterIP: '10.96.0.1',
+ externalIP: '-',
+ ports: '443/TCP',
+ age: '114d',
+ labels: {},
+ annotations: {},
+ kind: 'Service',
+ },
+ {
+ name: 'my-second-service',
+ namespace: 'default',
+ type: 'NodePort',
+ clusterIP: '10.105.219.238',
+ externalIP: '-',
+ ports: '80:31989/TCP, 443:32679/TCP',
+ age: '1d',
+ labels: {},
+ annotations: {},
+ kind: 'Service',
+ },
+];
diff --git a/spec/frontend/kubernetes_dashboard/graphql/resolvers/kubernetes_spec.js b/spec/frontend/kubernetes_dashboard/graphql/resolvers/kubernetes_spec.js
index 516d91af947..01e2c3d2716 100644
--- a/spec/frontend/kubernetes_dashboard/graphql/resolvers/kubernetes_spec.js
+++ b/spec/frontend/kubernetes_dashboard/graphql/resolvers/kubernetes_spec.js
@@ -1,16 +1,22 @@
-import { CoreV1Api, WatchApi, AppsV1Api } from '@gitlab/cluster-client';
+import { CoreV1Api, WatchApi, AppsV1Api, BatchV1Api } from '@gitlab/cluster-client';
import { resolvers } from '~/kubernetes_dashboard/graphql/resolvers';
import k8sDashboardPodsQuery from '~/kubernetes_dashboard/graphql/queries/k8s_dashboard_pods.query.graphql';
import k8sDashboardDeploymentsQuery from '~/kubernetes_dashboard/graphql/queries/k8s_dashboard_deployments.query.graphql';
import k8sDashboardStatefulSetsQuery from '~/kubernetes_dashboard/graphql/queries/k8s_dashboard_stateful_sets.query.graphql';
import k8sDashboardReplicaSetsQuery from '~/kubernetes_dashboard/graphql/queries/k8s_dashboard_replica_sets.query.graphql';
import k8sDashboardDaemonSetsQuery from '~/kubernetes_dashboard/graphql/queries/k8s_dashboard_daemon_sets.query.graphql';
+import k8sDashboardJobsQuery from '~/kubernetes_dashboard/graphql/queries/k8s_dashboard_jobs.query.graphql';
+import k8sDashboardCronJobsQuery from '~/kubernetes_dashboard/graphql/queries/k8s_dashboard_cron_jobs.query.graphql';
+import k8sDashboardServicesQuery from '~/kubernetes_dashboard/graphql/queries/k8s_dashboard_services.query.graphql';
import {
k8sPodsMock,
k8sDeploymentsMock,
k8sStatefulSetsMock,
k8sReplicaSetsMock,
k8sDaemonSetsMock,
+ k8sJobsMock,
+ k8sCronJobsMock,
+ k8sServicesMock,
} from '../mock_data';
describe('~/frontend/environments/graphql/resolvers', () => {
@@ -456,4 +462,250 @@ describe('~/frontend/environments/graphql/resolvers', () => {
).rejects.toThrow('API error');
});
});
+
+ describe('k8sJobs', () => {
+ const client = { writeQuery: jest.fn() };
+
+ const mockWatcher = WatchApi.prototype;
+ const mockJobsListWatcherFn = jest.fn().mockImplementation(() => {
+ return Promise.resolve(mockWatcher);
+ });
+
+ const mockOnDataFn = jest.fn().mockImplementation((eventName, callback) => {
+ if (eventName === 'data') {
+ callback([]);
+ }
+ });
+
+ const mockJobsListFn = jest.fn().mockImplementation(() => {
+ return Promise.resolve({
+ items: k8sJobsMock,
+ });
+ });
+
+ const mockAllJobsListFn = jest.fn().mockImplementation(mockJobsListFn);
+
+ describe('when the Jobs data is present', () => {
+ beforeEach(() => {
+ jest
+ .spyOn(BatchV1Api.prototype, 'listBatchV1JobForAllNamespaces')
+ .mockImplementation(mockAllJobsListFn);
+ jest.spyOn(mockWatcher, 'subscribeToStream').mockImplementation(mockJobsListWatcherFn);
+ jest.spyOn(mockWatcher, 'on').mockImplementation(mockOnDataFn);
+ });
+
+ it('should request all Jobs from the cluster_client library and watch the events', async () => {
+ const Jobs = await mockResolvers.Query.k8sJobs(
+ null,
+ {
+ configuration,
+ },
+ { client },
+ );
+
+ expect(mockAllJobsListFn).toHaveBeenCalled();
+ expect(mockJobsListWatcherFn).toHaveBeenCalled();
+
+ expect(Jobs).toEqual(k8sJobsMock);
+ });
+
+ it('should update cache with the new data when received from the library', async () => {
+ await mockResolvers.Query.k8sJobs(null, { configuration, namespace: '' }, { client });
+
+ expect(client.writeQuery).toHaveBeenCalledWith({
+ query: k8sDashboardJobsQuery,
+ variables: { configuration, namespace: '' },
+ data: { k8sJobs: [] },
+ });
+ });
+ });
+
+ it('should not watch Jobs from the cluster_client library when the Jobs data is not present', async () => {
+ jest.spyOn(BatchV1Api.prototype, 'listBatchV1JobForAllNamespaces').mockImplementation(
+ jest.fn().mockImplementation(() => {
+ return Promise.resolve({
+ items: [],
+ });
+ }),
+ );
+
+ await mockResolvers.Query.k8sJobs(null, { configuration }, { client });
+
+ expect(mockJobsListWatcherFn).not.toHaveBeenCalled();
+ });
+
+ it('should throw an error if the API call fails', async () => {
+ jest
+ .spyOn(BatchV1Api.prototype, 'listBatchV1JobForAllNamespaces')
+ .mockRejectedValue(new Error('API error'));
+
+ await expect(
+ mockResolvers.Query.k8sJobs(null, { configuration }, { client }),
+ ).rejects.toThrow('API error');
+ });
+ });
+
+ describe('k8sCronJobs', () => {
+ const client = { writeQuery: jest.fn() };
+
+ const mockWatcher = WatchApi.prototype;
+ const mockCronJobsListWatcherFn = jest.fn().mockImplementation(() => {
+ return Promise.resolve(mockWatcher);
+ });
+
+ const mockOnDataFn = jest.fn().mockImplementation((eventName, callback) => {
+ if (eventName === 'data') {
+ callback([]);
+ }
+ });
+
+ const mockCronJobsListFn = jest.fn().mockImplementation(() => {
+ return Promise.resolve({
+ items: k8sCronJobsMock,
+ });
+ });
+
+ const mockAllCronJobsListFn = jest.fn().mockImplementation(mockCronJobsListFn);
+
+ describe('when the CronJobs data is present', () => {
+ beforeEach(() => {
+ jest
+ .spyOn(BatchV1Api.prototype, 'listBatchV1CronJobForAllNamespaces')
+ .mockImplementation(mockAllCronJobsListFn);
+ jest.spyOn(mockWatcher, 'subscribeToStream').mockImplementation(mockCronJobsListWatcherFn);
+ jest.spyOn(mockWatcher, 'on').mockImplementation(mockOnDataFn);
+ });
+
+ it('should request all CronJobs from the cluster_client library and watch the events', async () => {
+ const CronJobs = await mockResolvers.Query.k8sCronJobs(
+ null,
+ {
+ configuration,
+ },
+ { client },
+ );
+
+ expect(mockAllCronJobsListFn).toHaveBeenCalled();
+ expect(mockCronJobsListWatcherFn).toHaveBeenCalled();
+
+ expect(CronJobs).toEqual(k8sCronJobsMock);
+ });
+
+ it('should update cache with the new data when received from the library', async () => {
+ await mockResolvers.Query.k8sCronJobs(null, { configuration, namespace: '' }, { client });
+
+ expect(client.writeQuery).toHaveBeenCalledWith({
+ query: k8sDashboardCronJobsQuery,
+ variables: { configuration, namespace: '' },
+ data: { k8sCronJobs: [] },
+ });
+ });
+ });
+
+ it('should not watch CronJobs from the cluster_client library when the CronJobs data is not present', async () => {
+ jest.spyOn(BatchV1Api.prototype, 'listBatchV1CronJobForAllNamespaces').mockImplementation(
+ jest.fn().mockImplementation(() => {
+ return Promise.resolve({
+ items: [],
+ });
+ }),
+ );
+
+ await mockResolvers.Query.k8sCronJobs(null, { configuration }, { client });
+
+ expect(mockCronJobsListWatcherFn).not.toHaveBeenCalled();
+ });
+
+ it('should throw an error if the API call fails', async () => {
+ jest
+ .spyOn(BatchV1Api.prototype, 'listBatchV1CronJobForAllNamespaces')
+ .mockRejectedValue(new Error('API error'));
+
+ await expect(
+ mockResolvers.Query.k8sCronJobs(null, { configuration }, { client }),
+ ).rejects.toThrow('API error');
+ });
+ });
+
+ describe('k8sServices', () => {
+ const client = { writeQuery: jest.fn() };
+
+ const mockWatcher = WatchApi.prototype;
+ const mockServicesListWatcherFn = jest.fn().mockImplementation(() => {
+ return Promise.resolve(mockWatcher);
+ });
+
+ const mockOnDataFn = jest.fn().mockImplementation((eventName, callback) => {
+ if (eventName === 'data') {
+ callback([]);
+ }
+ });
+
+ const mockServicesListFn = jest.fn().mockImplementation(() => {
+ return Promise.resolve({
+ items: k8sServicesMock,
+ });
+ });
+
+ const mockAllServicesListFn = jest.fn().mockImplementation(mockServicesListFn);
+
+ describe('when the Services data is present', () => {
+ beforeEach(() => {
+ jest
+ .spyOn(CoreV1Api.prototype, 'listCoreV1ServiceForAllNamespaces')
+ .mockImplementation(mockAllServicesListFn);
+ jest.spyOn(mockWatcher, 'subscribeToStream').mockImplementation(mockServicesListWatcherFn);
+ jest.spyOn(mockWatcher, 'on').mockImplementation(mockOnDataFn);
+ });
+
+ it('should request all Services from the cluster_client library and watch the events', async () => {
+ const Services = await mockResolvers.Query.k8sServices(
+ null,
+ {
+ configuration,
+ },
+ { client },
+ );
+
+ expect(mockAllServicesListFn).toHaveBeenCalled();
+ expect(mockServicesListWatcherFn).toHaveBeenCalled();
+
+ expect(Services).toEqual(k8sServicesMock);
+ });
+
+ it('should update cache with the new data when received from the library', async () => {
+ await mockResolvers.Query.k8sServices(null, { configuration, namespace: '' }, { client });
+
+ expect(client.writeQuery).toHaveBeenCalledWith({
+ query: k8sDashboardServicesQuery,
+ variables: { configuration, namespace: '' },
+ data: { k8sServices: [] },
+ });
+ });
+ });
+
+ it('should not watch Services from the cluster_client library when the Services data is not present', async () => {
+ jest.spyOn(CoreV1Api.prototype, 'listCoreV1ServiceForAllNamespaces').mockImplementation(
+ jest.fn().mockImplementation(() => {
+ return Promise.resolve({
+ items: [],
+ });
+ }),
+ );
+
+ await mockResolvers.Query.k8sServices(null, { configuration }, { client });
+
+ expect(mockServicesListWatcherFn).not.toHaveBeenCalled();
+ });
+
+ it('should throw an error if the API call fails', async () => {
+ jest
+ .spyOn(CoreV1Api.prototype, 'listCoreV1ServiceForAllNamespaces')
+ .mockRejectedValue(new Error('API error'));
+
+ await expect(
+ mockResolvers.Query.k8sServices(null, { configuration }, { client }),
+ ).rejects.toThrow('API error');
+ });
+ });
});
diff --git a/spec/frontend/kubernetes_dashboard/helpers/k8s_integration_helper_spec.js b/spec/frontend/kubernetes_dashboard/helpers/k8s_integration_helper_spec.js
index 2892d657aea..1fd89e67e79 100644
--- a/spec/frontend/kubernetes_dashboard/helpers/k8s_integration_helper_spec.js
+++ b/spec/frontend/kubernetes_dashboard/helpers/k8s_integration_helper_spec.js
@@ -3,6 +3,9 @@ import {
calculateDeploymentStatus,
calculateStatefulSetStatus,
calculateDaemonSetStatus,
+ calculateJobStatus,
+ calculateCronJobStatus,
+ generateServicePortsString,
} from '~/kubernetes_dashboard/helpers/k8s_integration_helper';
import { useFakeDate } from 'helpers/fake_date';
@@ -90,4 +93,81 @@ describe('k8s_integration_helper', () => {
expect(calculateDaemonSetStatus(item)).toBe(expected);
});
});
+
+ describe('calculateJobStatus', () => {
+ const completed = {
+ status: { failed: 0, succeeded: 2 },
+ spec: { completions: 2 },
+ };
+ const failed = {
+ status: { failed: 1, succeeded: 1 },
+ spec: { completions: 2 },
+ };
+ const anotherFailed = {
+ status: { failed: 0, succeeded: 1 },
+ spec: { completions: 2 },
+ };
+
+ it.each`
+ condition | item | expected
+ ${'there are no failed and succeeded amount is equal to completions number'} | ${completed} | ${'Completed'}
+ ${'there are some failed statuses'} | ${failed} | ${'Failed'}
+ ${'there are some failed and succeeded amount is not equal to completions number'} | ${anotherFailed} | ${'Failed'}
+ `('returns status as $expected when $condition', ({ item, expected }) => {
+ expect(calculateJobStatus(item)).toBe(expected);
+ });
+ });
+
+ describe('calculateCronJobStatus', () => {
+ const ready = {
+ status: { active: 0, lastScheduleTime: '2023-11-21T11:50:59Z' },
+ spec: { suspend: 0 },
+ };
+ const failed = {
+ status: { active: 1, lastScheduleTime: null },
+ spec: { suspend: 0 },
+ };
+ const suspended = {
+ status: { active: 0, lastScheduleTime: '2023-11-21T11:50:59Z' },
+ spec: { suspend: 1 },
+ };
+
+ it.each`
+ condition | item | expected
+ ${'there are no active and the lastScheduleTime is present'} | ${ready} | ${'Ready'}
+ ${'there are some active and the lastScheduleTime is not present'} | ${failed} | ${'Failed'}
+ ${'there are some suspend in the spec'} | ${suspended} | ${'Suspended'}
+ `('returns status as $expected when $condition', ({ item, expected }) => {
+ expect(calculateCronJobStatus(item)).toBe(expected);
+ });
+ });
+
+ describe('generateServicePortsString', () => {
+ const port = '8080';
+ const protocol = 'TCP';
+ const nodePort = '31732';
+
+ it('returns empty string if no ports provided', () => {
+ expect(generateServicePortsString([])).toBe('');
+ });
+
+ it('returns port and protocol when provided', () => {
+ expect(generateServicePortsString([{ port, protocol }])).toBe(`${port}/${protocol}`);
+ });
+
+ it('returns port, protocol and nodePort when provided', () => {
+ expect(generateServicePortsString([{ port, protocol, nodePort }])).toBe(
+ `${port}:${nodePort}/${protocol}`,
+ );
+ });
+
+ it('returns joined strings of ports if multiple are provided', () => {
+ expect(
+ generateServicePortsString([
+ { port, protocol },
+ { port, protocol, nodePort },
+ ]),
+ ).toBe(`${port}/${protocol}, ${port}:${nodePort}/${protocol}`);
+ });
+ });
});
diff --git a/spec/frontend/kubernetes_dashboard/pages/cron_jobs_page_spec.js b/spec/frontend/kubernetes_dashboard/pages/cron_jobs_page_spec.js
new file mode 100644
index 00000000000..3d5eadf920a
--- /dev/null
+++ b/spec/frontend/kubernetes_dashboard/pages/cron_jobs_page_spec.js
@@ -0,0 +1,102 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { shallowMount } from '@vue/test-utils';
+import waitForPromises from 'helpers/wait_for_promises';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import CronJobsPage from '~/kubernetes_dashboard/pages/cron_jobs_page.vue';
+import WorkloadLayout from '~/kubernetes_dashboard/components/workload_layout.vue';
+import { useFakeDate } from 'helpers/fake_date';
+import { k8sCronJobsMock, mockCronJobsStats, mockCronJobsTableItems } from '../graphql/mock_data';
+
+Vue.use(VueApollo);
+
+describe('Kubernetes dashboard cronJobs page', () => {
+ let wrapper;
+
+ const configuration = {
+ basePath: 'kas/tunnel/url',
+ baseOptions: {
+ headers: { 'GitLab-Agent-Id': '1' },
+ },
+ };
+
+ const findWorkloadLayout = () => wrapper.findComponent(WorkloadLayout);
+
+ const createApolloProvider = () => {
+ const mockResolvers = {
+ Query: {
+ k8sCronJobs: jest.fn().mockReturnValue(k8sCronJobsMock),
+ },
+ };
+
+ return createMockApollo([], mockResolvers);
+ };
+
+ const createWrapper = (apolloProvider = createApolloProvider()) => {
+ wrapper = shallowMount(CronJobsPage, {
+ provide: { configuration },
+ apolloProvider,
+ });
+ };
+
+ describe('mounted', () => {
+ it('renders WorkloadLayout component', () => {
+ createWrapper();
+
+ expect(findWorkloadLayout().exists()).toBe(true);
+ });
+
+ it('sets loading prop for the WorkloadLayout', () => {
+ createWrapper();
+
+ expect(findWorkloadLayout().props('loading')).toBe(true);
+ });
+
+ it('removes loading prop from the WorkloadLayout when the list of cronJobs loaded', async () => {
+ createWrapper();
+ await waitForPromises();
+
+ expect(findWorkloadLayout().props('loading')).toBe(false);
+ });
+ });
+
+ describe('when gets cronJobs data', () => {
+ useFakeDate(2023, 10, 23, 10, 10);
+
+ it('sets correct stats object for the WorkloadLayout', async () => {
+ createWrapper();
+ await waitForPromises();
+
+ expect(findWorkloadLayout().props('stats')).toEqual(mockCronJobsStats);
+ });
+
+ it('sets correct table items object for the WorkloadLayout', async () => {
+ createWrapper();
+ await waitForPromises();
+
+ expect(findWorkloadLayout().props('items')).toMatchObject(mockCronJobsTableItems);
+ });
+ });
+
+ 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: {
+ k8sCronJobs: jest.fn().mockRejectedValueOnce(error),
+ },
+ };
+
+ return createMockApollo([], mockResolvers);
+ };
+
+ beforeEach(async () => {
+ createWrapper(createErroredApolloProvider());
+ await waitForPromises();
+ });
+
+ it('sets errorMessage prop for the WorkloadLayout', () => {
+ expect(findWorkloadLayout().props('errorMessage')).toBe(error.message);
+ });
+ });
+});
diff --git a/spec/frontend/kubernetes_dashboard/pages/jobs_page_spec.js b/spec/frontend/kubernetes_dashboard/pages/jobs_page_spec.js
new file mode 100644
index 00000000000..a7148ae2394
--- /dev/null
+++ b/spec/frontend/kubernetes_dashboard/pages/jobs_page_spec.js
@@ -0,0 +1,102 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { shallowMount } from '@vue/test-utils';
+import waitForPromises from 'helpers/wait_for_promises';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import JobsPage from '~/kubernetes_dashboard/pages/jobs_page.vue';
+import WorkloadLayout from '~/kubernetes_dashboard/components/workload_layout.vue';
+import { useFakeDate } from 'helpers/fake_date';
+import { k8sJobsMock, mockJobsStats, mockJobsTableItems } from '../graphql/mock_data';
+
+Vue.use(VueApollo);
+
+describe('Kubernetes dashboard jobs page', () => {
+ let wrapper;
+
+ const configuration = {
+ basePath: 'kas/tunnel/url',
+ baseOptions: {
+ headers: { 'GitLab-Agent-Id': '1' },
+ },
+ };
+
+ const findWorkloadLayout = () => wrapper.findComponent(WorkloadLayout);
+
+ const createApolloProvider = () => {
+ const mockResolvers = {
+ Query: {
+ k8sJobs: jest.fn().mockReturnValue(k8sJobsMock),
+ },
+ };
+
+ return createMockApollo([], mockResolvers);
+ };
+
+ const createWrapper = (apolloProvider = createApolloProvider()) => {
+ wrapper = shallowMount(JobsPage, {
+ provide: { configuration },
+ apolloProvider,
+ });
+ };
+
+ describe('mounted', () => {
+ it('renders WorkloadLayout component', () => {
+ createWrapper();
+
+ expect(findWorkloadLayout().exists()).toBe(true);
+ });
+
+ it('sets loading prop for the WorkloadLayout', () => {
+ createWrapper();
+
+ expect(findWorkloadLayout().props('loading')).toBe(true);
+ });
+
+ it('removes loading prop from the WorkloadLayout when the list of jobs loaded', async () => {
+ createWrapper();
+ await waitForPromises();
+
+ expect(findWorkloadLayout().props('loading')).toBe(false);
+ });
+ });
+
+ describe('when gets jobs data', () => {
+ useFakeDate(2023, 10, 23, 10, 10);
+
+ it('sets correct stats object for the WorkloadLayout', async () => {
+ createWrapper();
+ await waitForPromises();
+
+ expect(findWorkloadLayout().props('stats')).toEqual(mockJobsStats);
+ });
+
+ it('sets correct table items object for the WorkloadLayout', async () => {
+ createWrapper();
+ await waitForPromises();
+
+ expect(findWorkloadLayout().props('items')).toMatchObject(mockJobsTableItems);
+ });
+ });
+
+ 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: {
+ k8sJobs: jest.fn().mockRejectedValueOnce(error),
+ },
+ };
+
+ return createMockApollo([], mockResolvers);
+ };
+
+ beforeEach(async () => {
+ createWrapper(createErroredApolloProvider());
+ await waitForPromises();
+ });
+
+ it('sets errorMessage prop for the WorkloadLayout', () => {
+ expect(findWorkloadLayout().props('errorMessage')).toBe(error.message);
+ });
+ });
+});
diff --git a/spec/frontend/kubernetes_dashboard/pages/services_page_spec.js b/spec/frontend/kubernetes_dashboard/pages/services_page_spec.js
new file mode 100644
index 00000000000..c76f4330cd6
--- /dev/null
+++ b/spec/frontend/kubernetes_dashboard/pages/services_page_spec.js
@@ -0,0 +1,104 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { shallowMount } from '@vue/test-utils';
+import waitForPromises from 'helpers/wait_for_promises';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import ServicesPage from '~/kubernetes_dashboard/pages/services_page.vue';
+import WorkloadLayout from '~/kubernetes_dashboard/components/workload_layout.vue';
+import { SERVICES_TABLE_FIELDS } from '~/kubernetes_dashboard/constants';
+import { useFakeDate } from 'helpers/fake_date';
+import { k8sServicesMock, mockServicesTableItems } from '../graphql/mock_data';
+
+Vue.use(VueApollo);
+
+describe('Kubernetes dashboard services page', () => {
+ let wrapper;
+
+ const configuration = {
+ basePath: 'kas/tunnel/url',
+ baseOptions: {
+ headers: { 'GitLab-Agent-Id': '1' },
+ },
+ };
+
+ const findWorkloadLayout = () => wrapper.findComponent(WorkloadLayout);
+
+ const createApolloProvider = () => {
+ const mockResolvers = {
+ Query: {
+ k8sServices: jest.fn().mockReturnValue(k8sServicesMock),
+ },
+ };
+
+ return createMockApollo([], mockResolvers);
+ };
+
+ const createWrapper = (apolloProvider = createApolloProvider()) => {
+ wrapper = shallowMount(ServicesPage, {
+ provide: { configuration },
+ apolloProvider,
+ });
+ };
+
+ describe('mounted', () => {
+ it('renders WorkloadLayout component', () => {
+ createWrapper();
+
+ expect(findWorkloadLayout().exists()).toBe(true);
+ });
+
+ it('sets loading prop for the WorkloadLayout', () => {
+ createWrapper();
+
+ expect(findWorkloadLayout().props('loading')).toBe(true);
+ });
+
+ it('removes loading prop from the WorkloadLayout when the list of services loaded', async () => {
+ createWrapper();
+ await waitForPromises();
+
+ expect(findWorkloadLayout().props('loading')).toBe(false);
+ });
+ });
+
+ describe('when gets services data', () => {
+ useFakeDate(2023, 10, 23, 10, 10);
+
+ it('sets correct stats object for the WorkloadLayout', async () => {
+ createWrapper();
+ await waitForPromises();
+
+ expect(findWorkloadLayout().props('stats')).toEqual([]);
+ });
+
+ it('sets correct table items object for the WorkloadLayout', async () => {
+ createWrapper();
+ await waitForPromises();
+
+ expect(findWorkloadLayout().props('items')).toMatchObject(mockServicesTableItems);
+ expect(findWorkloadLayout().props('fields')).toMatchObject(SERVICES_TABLE_FIELDS);
+ });
+ });
+
+ 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: {
+ k8sServices: jest.fn().mockRejectedValueOnce(error),
+ },
+ };
+
+ return createMockApollo([], mockResolvers);
+ };
+
+ beforeEach(async () => {
+ createWrapper(createErroredApolloProvider());
+ await waitForPromises();
+ });
+
+ it('sets errorMessage prop for the WorkloadLayout', () => {
+ expect(findWorkloadLayout().props('errorMessage')).toBe(error.message);
+ });
+ });
+});