Skip to content

Commit

Permalink
🤖 443 adding tests for gateways services and models (#496)
Browse files Browse the repository at this point in the history
  • Loading branch information
xoscar authored May 17, 2022
1 parent 1ebe755 commit 2c98fda
Show file tree
Hide file tree
Showing 52 changed files with 1,004 additions and 144 deletions.
8 changes: 5 additions & 3 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@
"scripts": {
"start": "craco start",
"build": "craco build",
"test:unit:watch": "craco test",
"test:unit": "craco test --watchAll=false --coverage",
"test:integration": "npm run cy:run",
"test:lint": "eslint ./src/**/*.{ts,tsx}",
"test:types": "tsc",
"test": "npm run test:lint && npm run test:types && npm run test:unit",
"lint": "npm run eslint && npm run types",
"types": "tsc",
"eslint": "eslint ./src/**/*.{ts,tsx}",
"test": "npm run test:unit",
"eject": "react-scripts eject",
"cy:open": "cypress open",
"cy:run": "cypress run",
Expand Down
7 changes: 4 additions & 3 deletions web/src/components/Analytics/useAnalytics.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {useContext, useMemo} from 'react';
import {useCallback, useContext} from 'react';
import {GA4ReactInterface} from 'ga-4-react/src/models/gtagModels';
import {Context} from './AnalyticsProvider';
import AnalyticsService, {Categories} from '../../services/Analytics/Analytics.service';
import AnalyticsService from '../../services/Analytics/Analytics.service';
import {Categories} from '../../constants/Analytics.constants';

export type TAnalyticsService<A> = {
isEnabled: boolean;
Expand All @@ -11,7 +12,7 @@ export type TAnalyticsService<A> = {

const useAnalytics = <A>(category: Categories = Categories.Home): TAnalyticsService<A> => {
const {instance, isEnabled} = useContext(Context);
const {event} = useMemo(() => AnalyticsService(category), [category]);
const event = useCallback((action, label) => AnalyticsService.event(category, action, label), [category]);

return {isEnabled, event, instance};
};
Expand Down
2 changes: 1 addition & 1 deletion web/src/components/DiagramSwitcher/DiagramSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {SupportedDiagrams} from '../Diagram/Diagram';
import SearchInput from '../SearchInput';
import * as S from './DiagramStories.styled';
import * as S from './DiagramSwitcher.styled';

interface IDiagramSwitcherProps {
onSearch(search: string): void;
Expand Down
18 changes: 18 additions & 0 deletions web/src/constants/Analytics.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export enum Categories {
Home = 'home',
Test = 'test',
Trace = 'trace',
TraceDetail = 'trace-detail',
TestResults = 'test-results',
SpanDetail = 'span-detail',
Assertion = 'assertion',
}

export enum Labels {
Button = 'button',
Link = 'link',
Modal = 'modal',
Table = 'table',
Form = 'form',
Tab = 'tab',
}
41 changes: 41 additions & 0 deletions web/src/gateways/__tests__/Assertion.gateway.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {endpoints} from '../../redux/apis/Test.api';
import AssertionGateway from '../Assertion.gateway';

const {createAssertion, getAssertions, updateAssertion} = endpoints;

jest.mock('../../redux/apis/Test.api', () => {
const initiate = jest.fn(() => Promise.resolve());

return {
endpoints: {
createAssertion: {initiate},
getAssertions: {initiate},
updateAssertion: {initiate},
},
};
});

describe('AssertionGateway', () => {
it('should execute the getAssertions function', async () => {
expect.assertions(1);
await AssertionGateway.get('testId');

expect(getAssertions.initiate).toBeCalledWith('testId');
});

it('should execute the createAssertion function', async () => {
expect.assertions(1);
const assertion = {assertionId: 'assertionId', selectors: []};
await AssertionGateway.create('testId', assertion);

expect(createAssertion.initiate).toBeCalledWith({testId: 'testId', assertion});
});

it('should execute the updateAssertion function', async () => {
expect.assertions(1);
const assertion = {assertionId: 'assertionId', selectors: []};
await AssertionGateway.update('testId', 'assertionId', assertion);

expect(updateAssertion.initiate).toBeCalledWith({testId: 'testId', assertionId: 'assertionId', assertion});
});
});
21 changes: 21 additions & 0 deletions web/src/gateways/__tests__/LocalStorage.gateway.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import LocalStorageGateway from '../LocalStorage.gateway';

const localStorage = LocalStorageGateway<{name: string}>('key');

describe('LocalStorageGateway', () => {
it('should set a value and retrieve it afterwards', async () => {
expect.assertions(1);
const item = {name: 'test'};
localStorage.set(item);

const value = localStorage.get();

expect(value).toEqual(item);
});

it('should return undefined as key is not found', async () => {
const value = localStorage.get('not-found');

expect(value).toEqual(undefined);
});
});
48 changes: 48 additions & 0 deletions web/src/gateways/__tests__/Test.gateway.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {endpoints} from '../../redux/apis/Test.api';
import TestGateway from '../Test.gateway';

const {createTest, getTestById, getTestList, runTest} = endpoints;

jest.mock('../../redux/apis/Test.api', () => {
const initiate = jest.fn(() => Promise.resolve());

return {
endpoints: {
createTest: {initiate},
getTestById: {initiate},
getTestList: {initiate},
runTest: {initiate},
},
};
});

describe('TestGateway', () => {
it('should execute the create function', async () => {
expect.assertions(1);
const test = {name: 'test', description: 'test'};
await TestGateway.create(test);

expect(createTest.initiate).toBeCalledWith(test);
});

it('should execute the getById function', async () => {
expect.assertions(1);
await TestGateway.getById('testId');

expect(getTestById.initiate).toBeCalledWith('testId');
});

it('should execute the getList function', async () => {
expect.assertions(1);
await TestGateway.getList();

expect(getTestList.initiate).toBeCalledWith();
});

it('should execute the runTest function', async () => {
expect.assertions(1);
await TestGateway.run('testId');

expect(runTest.initiate).toBeCalledWith('testId');
});
});
47 changes: 47 additions & 0 deletions web/src/gateways/__tests__/TestRunResult.gateway.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {endpoints} from '../../redux/apis/Test.api';
import TestRunResultGateway from '../TestRunResult.gateway';

const {getResultById, getResultList, updateResult} = endpoints;

jest.mock('../../redux/apis/Test.api', () => {
const initiate = jest.fn(() => Promise.resolve());

return {
endpoints: {
getResultById: {initiate},
getResultList: {initiate},
updateResult: {initiate},
},
};
});

describe('TestRunResultGateway', () => {
it('should execute the get function', async () => {
expect.assertions(1);
await TestRunResultGateway.get('testId');

expect(getResultList.initiate).toBeCalledWith({testId: 'testId', take: 25, skip: 0});
});

it('should execute the getById function', async () => {
expect.assertions(1);
await TestRunResultGateway.getById('testId', 'resultId');

expect(getResultById.initiate).toBeCalledWith({testId: 'testId', resultId: 'resultId'});
});

it('should execute the update function', async () => {
expect.assertions(1);
const testAssertionResult = {
assertionResultState: true,
assertionResult: [],
};
await TestRunResultGateway.update('testId', 'resultId', testAssertionResult);

expect(updateResult.initiate).toBeCalledWith({
testId: 'testId',
resultId: 'resultId',
assertionResult: testAssertionResult,
});
});
});
1 change: 0 additions & 1 deletion web/src/hooks/__tests__/useDAGChart.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ test('useDAGChart with filled span', () => {
id: '1',
data: {
attributes: {},
instrumentationLibrary: {name: 'fist', version: '1'},
type: SemanticGroupNames.Http,
duration: 10,
signature: [],
Expand Down
27 changes: 12 additions & 15 deletions web/src/models/SpanAttribute.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,24 @@ import {IRawSpanAttribute, ISpanAttribute} from '../types/SpanAttribute.types';

const spanAttributeTypeList = Object.values(SpanAttributeType);

const typeOfList = ['number', 'boolean', 'string'];

const getSpanAttributeValueType = (attribute: IRawSpanAttribute): SpanAttributeType =>
spanAttributeTypeList.find(type => {
const value = attribute.value[type];
if (typeof value === 'number') return true;
return !isEmpty(value);

return typeOfList.includes(typeof value) || !isEmpty(value);
}) || SpanAttributeType.stringValue;

const getSpanAttributeValue = (attribute: IRawSpanAttribute): string => {
const attributeType = getSpanAttributeValueType(attribute);
const value = attribute.value[attributeType];

if (!value) return '<Empty value>';
switch (attributeType) {
case SpanAttributeType.kvlistValue: {
return JSON.stringify(value);
}

default: {
return String(value);
}
}
const type = getSpanAttributeValueType(attribute);
const value = attribute.value[type];

if (['number', 'boolean'].includes(typeof value)) return String(value);

return (
(type === SpanAttributeType.kvlistValue && JSON.stringify(value)) || (value && String(value)) || '<Empty value>'
);
};

const SpanAttribute = (rawAttribute: IRawSpanAttribute): ISpanAttribute => {
Expand Down
7 changes: 5 additions & 2 deletions web/src/models/TestRunResult.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ const getExecutionTime = (createdAt?: string, completedAt?: string) => {
return differenceInSeconds(new Date(completedAt), new Date(createdAt)) + 1;
};

const getTestResultCount = (assertionResultList: TAssertionResultList = [], type: 'all' | 'passed' | 'failed' = 'all') => {
const getTestResultCount = (
assertionResultList: TAssertionResultList = [],
type: 'all' | 'passed' | 'failed' = 'all'
) => {
const spanAssertionList = assertionResultList.flatMap(({spanAssertionResults}) => spanAssertionResults);

if (type === 'all') return spanAssertionList.length;
Expand All @@ -32,7 +35,7 @@ const TestRunResult = (rawTestRunResult: IRawTestRunResult): ITestRunResult => {
return {
...rawTestRunResult,
trace: rawTestRunResult.trace ? Trace(rawTestRunResult.trace) : undefined,
totalAssertionCount: getTestResultCount(rawTestRunResult.assertionResult, 'all'),
totalAssertionCount: getTestResultCount(rawTestRunResult.assertionResult),
failedAssertionCount: getTestResultCount(rawTestRunResult.assertionResult, 'failed'),
passedAssertionCount: getTestResultCount(rawTestRunResult.assertionResult, 'passed'),
executionTime: getExecutionTime(rawTestRunResult.createdAt, rawTestRunResult.completedAt),
Expand Down
17 changes: 16 additions & 1 deletion web/src/models/__mocks__/Span.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,22 @@ const SpanMock: IMockFactory<ISpan, IRawSpan> = () => ({
kind: faker.random.word(),
startTimeUnixNano: faker.date.recent().toISOString(),
endTimeUnixNano: faker.date.recent().toISOString(),
attributes: [],
attributes: faker.datatype
.array(faker.datatype.number({min: 2, max: 10}))
.map(() => ({
key: faker.random.word(),
value: {
stringValue: faker.random.word(),
kvlistValue: {values: []},
},
}))
.concat({
key: 'service.name',
value: {
stringValue: 'mock',
kvlistValue: {values: []},
},
}),
status: {
code: faker.random.word(),
},
Expand Down
30 changes: 30 additions & 0 deletions web/src/models/__mocks__/TestRunResult.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import faker from '@faker-js/faker';
import {TestState} from '../../constants/TestRunResult.constants';
import {IMockFactory} from '../../types/Common.types';
import {IRawTestRunResult, ITestRunResult} from '../../types/TestRunResult.types';
import TestRunResult from '../TestRunResult.model';
import TraceMock from './Trace.mock';

const TestRunResultMock: IMockFactory<ITestRunResult, IRawTestRunResult> = () => ({
raw(data = {}) {
return {
resultId: faker.datatype.uuid(),
testId: faker.datatype.uuid(),
traceId: faker.datatype.uuid(),
spanId: faker.datatype.uuid(),
createdAt: faker.date.past().toISOString(),
completedAt: faker.date.past().toISOString(),
response: {},
trace: TraceMock.raw(),
state: TestState.FINISHED,
assertionResultState: true,
assertionResult: [],
...data,
};
},
model(data = {}) {
return TestRunResult(this.raw(data));
},
});

export default TestRunResultMock();
35 changes: 35 additions & 0 deletions web/src/models/__mocks__/Trace.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import faker from '@faker-js/faker';
import {IMockFactory} from '../../types/Common.types';
import {IRawTrace, ITrace} from '../../types/Trace.types';
import Trace from '../Trace.model';
import SpanMock from './Span.mock';

const TraceMock: IMockFactory<ITrace, IRawTrace> = () => ({
raw(data = {}) {
return {
description: faker.random.words(),
resourceSpans: [
{
resource: {
attributes: [],
},
instrumentationLibrarySpans: [
{
instrumentationLibrary: {
version: String(faker.datatype.number()),
name: faker.random.word(),
},
spans: faker.datatype.array(faker.datatype.number({min: 2, max: 10})).map(() => SpanMock.raw()),
},
],
},
],
...data,
};
},
model(data = {}) {
return Trace(this.raw(data));
},
});

export default TraceMock();
Loading

0 comments on commit 2c98fda

Please sign in to comment.