Skip to content

Commit

Permalink
Add unit tests (#1013)
Browse files Browse the repository at this point in the history
* test(backend): add missing unit tests

Signed-off-by: Oleksii Kurinnyi <[email protected]>

* test(frontend): add missing unit tests

Signed-off-by: Oleksii Kurinnyi <[email protected]>

* test: remove codecov flag

---------

Signed-off-by: Oleksii Kurinnyi <[email protected]>
  • Loading branch information
akurinnoy authored Dec 4, 2023
1 parent 6ae861c commit d4ebc4a
Show file tree
Hide file tree
Showing 37 changed files with 905 additions and 12 deletions.
1 change: 0 additions & 1 deletion .github/workflows/codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,3 @@ jobs:
uses: codecov/codecov-action@v3
with:
files: ./packages/dashboard-frontend/coverage/lcov.info,./packages/dashboard-backend/coverage/lcov.info,./packages/common/coverage/lcov.info
flags: unittests
1 change: 1 addition & 0 deletions packages/dashboard-backend/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module.exports = {
'!src/localRun/**',
'!src/utils/**',
'!src/server.ts',
'!src/**/*.d.ts',
],
testEnvironment: 'node',
setupFilesAfterEnv: ['./jest.setup.js'],
Expand Down
20 changes: 20 additions & 0 deletions packages/dashboard-backend/src/models/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018-2023 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import * as models from '@/models';

describe('models', () => {
it('should export models', () => {
// this makes the coverage tool happy
expect(models).toBeDefined();
});
});
20 changes: 20 additions & 0 deletions packages/dashboard-backend/src/models/__tests__/restParams.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018-2023 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import * as restParams from '@/models/restParams';

describe('restParams', () => {
it('should export restParams', () => {
// this makes the coverage tool happy
expect(restParams).toBeDefined();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2018-2023 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import { api } from '@eclipse-che/common';

import { IWatcherService } from '@/devworkspaceClient';
import { ObjectsWatcher } from '@/services/ObjectsWatcher';

describe('ObjectsWatcher', () => {
const message = 'message';
const channel = api.webSocket.Channel.DEV_WORKSPACE;

const mockListener = jest.fn();
const mockParams = { param: 'value' };

const mockStopWatching = jest.fn();
const mockWatchInNamespace = jest
.fn()
.mockImplementation(listener => Promise.resolve(listener(message)));

let objectsWatcher: ObjectsWatcher<unknown>;

beforeEach(() => {
const apiService = {
stopWatching: () => mockStopWatching(),
watchInNamespace: (...args) => mockWatchInNamespace(...args),
} as IWatcherService;
objectsWatcher = new ObjectsWatcher(apiService, channel);
});

afterEach(() => {
jest.clearAllMocks();
});

test('start watching w/o observer', async () => {
await objectsWatcher.start('test', mockParams);

expect(mockWatchInNamespace).toHaveBeenCalledWith(expect.any(Function), mockParams);
});

test('watch changes with observer', async () => {
const observer = {
update: mockListener,
};
objectsWatcher.attach(observer);

await objectsWatcher.start('test', mockParams);

expect(mockListener).toHaveBeenCalledWith(channel, 'message');
expect(mockWatchInNamespace).toHaveBeenCalledWith(expect.any(Function), mockParams);
});

test('detach observer', async () => {
const observer = {
update: mockListener,
};
objectsWatcher.attach(observer);
objectsWatcher.detach();

await objectsWatcher.start('test', mockParams);

expect(mockListener).not.toHaveBeenCalled();
expect(mockWatchInNamespace).toHaveBeenCalledWith(expect.any(Function), mockParams);
});

test('stop watching', async () => {
objectsWatcher.stop();

expect(mockStopWatching).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2018-2023 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import { api } from '@eclipse-che/common';
import MockWebSocket from 'jest-websocket-mock';
import WS from 'ws';

import { SubscriptionManager } from '@/services/SubscriptionManager';
import { NotificationMessage } from '@/services/types/Observer';

describe('SubscriptionManager', () => {
const channel = api.webSocket.Channel.DEV_WORKSPACE;

const ws = new MockWebSocket('ws://localhost') as unknown as WS;
const spyWsSend = jest.spyOn(ws, 'send');

let subscriptionManager: SubscriptionManager;

beforeEach(() => {
subscriptionManager = new SubscriptionManager(ws);
});

afterEach(() => {
jest.clearAllMocks();
});

test('subscribe and update', () => {
subscriptionManager.subscribe(channel);

const message = {
eventPhase: api.webSocket.EventPhase.ADDED,
} as NotificationMessage;
subscriptionManager.update(channel, message);

expect(spyWsSend).toHaveBeenCalled();
});

test('unsubscribe all channels', () => {
subscriptionManager.subscribe(channel);
subscriptionManager.unsubscribeAll();

const message = {
eventPhase: api.webSocket.EventPhase.ADDED,
} as NotificationMessage;
subscriptionManager.update(channel, message);

expect(spyWsSend).not.toHaveBeenCalled();
});
});
88 changes: 88 additions & 0 deletions packages/dashboard-backend/src/services/__tests__/helpers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2018-2023 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import { authenticationHeaderSchema } from '@/constants/schemas';
import * as helpers from '@/services/helpers';

describe('helpers', () => {
test('delay', async () => {
jest.useFakeTimers();

const mockCallback = jest.fn();
const promise = helpers.delay(1000).then(mockCallback);

await jest.advanceTimersByTimeAsync(500);
expect(mockCallback).not.toHaveBeenCalled();

await jest.advanceTimersByTimeAsync(500);
expect(promise).resolves.toBeUndefined();
expect(mockCallback).toHaveBeenCalled();

jest.useRealTimers();
});

test('getSchema', () => {
const schema = helpers.getSchema({
tags: ['test'],
namespacedSchema: {
type: 'object',
properties: {
namespace: {
type: 'string',
},
},
},
body: {
type: 'object',
properties: {
name: {
type: 'string',
},
},
},
});

expect(schema).toEqual({
schema: {
headers: authenticationHeaderSchema,
namespacedSchema: {
properties: {
namespace: {
type: 'string',
},
},
type: 'object',
},
security: [
{
Authorization: '',
},
],
tags: ['test'],
body: {
type: 'object',
properties: {
name: {
type: 'string',
},
},
},
},
});
});

test('createFastifyError', () => {
const error = helpers.createFastifyError('test', '500');
expect(error).toBeInstanceOf(Error);
expect(error.statusCode).toEqual(500);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2018-2023 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import * as k8s from '@kubernetes/client-node';

import { isOpenShift } from '@/services/kubeclient/helpers';

const mockFindApi = jest.fn();
jest.mock('@/helpers/findApi', () => ({
findApi: jest.fn().mockImplementation(() => mockFindApi()),
}));

describe('isOpenShift', () => {
it('should return true if project.openshift.io API is available', async () => {
mockFindApi.mockResolvedValue(true);

const res = await isOpenShift({} as k8s.ApisApi);

expect(res).toBe(true);
});

it('should return false if project.openshift.io API is not available', async () => {
mockFindApi.mockResolvedValue(false);

const res = await isOpenShift({} as k8s.ApisApi);

expect(res).toBe(false);
});

it('should throw an error if findApi throws an error', async () => {
mockFindApi.mockRejectedValue(new Error('find-api-error'));

await expect(isOpenShift({} as k8s.ApisApi)).rejects.toThrow(
`Can't evaluate target platform: find-api-error`,
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const projectApiGroup = 'project.openshift.io';

export async function isOpenShift(apisApi: k8s.ApisApi): Promise<boolean> {
try {
return findApi(apisApi, projectApiGroup);
return await findApi(apisApi, projectApiGroup);
} catch (e) {
throw new Error(`Can't evaluate target platform: ${helpers.errors.getMessage(e)}`);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018-2023 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import * as Observer from '@/services/types/Observer';

describe('Observer', () => {
it('should export Subject', () => {
// this makes the coverage tool happy
expect(Observer).toBeDefined();
});
});
1 change: 1 addition & 0 deletions packages/dashboard-frontend/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ module.exports = {
'!src/index.tsx',
'!src/App.tsx',
'!src/Routes.tsx',
'!src/service-worker.ts',
],
coverageThreshold: {
global: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2018-2023 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import React from 'react';

import { Props } from '@/Layout/ErrorBoundary';

export const errorMessage = 'Error Boundary Message';

export class ErrorBoundary extends React.PureComponent<Props> {
public render(): React.ReactElement {
return (
<div>
Mock ErrorBoundary component
<button onClick={() => this.props.onError(errorMessage)}>onError</button>
{this.props.children}
</div>
);
}
}
Loading

0 comments on commit d4ebc4a

Please sign in to comment.