Skip to content

Commit

Permalink
Cb 4633 unit tests for utils (#2357)
Browse files Browse the repository at this point in the history
* CB-4633 blobToBase64 unit tests

* СB-4633 importLazyComponent tests

* CB-4633 OrderedMap unit tests

* CB-4633 removed test time limit from blobToBase64 test

* CB-4633 adds to ordered map unit tests for edge cases

* CB-4633 importLazy moved to core-blocks

* CB-4633 adds update-ts-references

---------

Co-authored-by: s.teleshev <[email protected]>
  • Loading branch information
sergeyteleshev and s.teleshev authored Feb 7, 2024
1 parent 7eba02a commit d96b9e9
Show file tree
Hide file tree
Showing 32 changed files with 298 additions and 36 deletions.
2 changes: 1 addition & 1 deletion webapp/packages/core-app/src/BodyLazy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { importLazyComponent } from '@cloudbeaver/core-utils';
import { importLazyComponent } from '@cloudbeaver/core-blocks';

export const BodyLazy = importLazyComponent(() => import('./Body').then(m => m.Body));
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';

type Props = {
children: React.ReactNode;
};

type State = {
hasError: boolean;
error: any | null;
};
export default class ErrorBoundary extends React.Component<Props, State> {
constructor(props: any) {
super(props);
this.state = { hasError: false, error: null };
}

static getDerivedStateFromError(error: any) {
return { hasError: true, error };
}

render() {
if (this.state.hasError) {
return <h1>{this.state.error.message}</h1>;
}

return this.props.children;
}
}
43 changes: 43 additions & 0 deletions webapp/packages/core-blocks/src/importLazyComponent.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { render, screen, waitFor } from '@testing-library/react';
import React, { Suspense } from 'react';

import ErrorBoundary from './__custom__mocks__/ErrorBoundaryMock';
import { importLazyComponent } from './importLazyComponent';

describe('importLazyComponent', () => {
const fallback = 'Loading...';

it('should render the lazy component', async () => {
const loadedText = 'Lazy Component';
const mockComponent: React.FC<any> = () => <div>{loadedText}</div>;
const componentImporter = jest.fn(() => Promise.resolve(mockComponent));
const LazyComponent = importLazyComponent(componentImporter);
render(
<Suspense fallback={fallback}>
<LazyComponent />
</Suspense>,
);

expect(screen.getByText(fallback)).toBeTruthy();
await waitFor(() => expect(screen.getByText(loadedText)).toBeTruthy());
expect(componentImporter).toHaveBeenCalled();
});

it('should render the error boundary if rejects with an error', async () => {
const errorText = 'Error';
const componentImporter = jest.fn(() => Promise.reject(new Error(errorText)));
const LazyComponent = importLazyComponent(componentImporter as any);

render(
<ErrorBoundary>
<Suspense fallback={fallback}>
<LazyComponent />
</Suspense>
</ErrorBoundary>,
);

expect(screen.getByText(fallback)).toBeTruthy();
await waitFor(() => expect(screen.getByText(errorText)).toBeTruthy());
expect(componentImporter).toHaveBeenCalled();
});
});
1 change: 1 addition & 0 deletions webapp/packages/core-blocks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,4 @@ export * from './useUserData';
export * from './useMergeRefs';
export * from './usePasswordValidation';
export * from './manifest';
export * from './importLazyComponent';
2 changes: 0 additions & 2 deletions webapp/packages/core-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
"fast-deep-equal": "^3.1.3",
"md5": "^2.3.0",
"mobx": "^6.12.0",
"react": "^18.2.0",
"underscore": "^1.13.6",
"uuid": "^9.0.1",
"whatwg-mimetype": "^4.0.0",
Expand All @@ -35,7 +34,6 @@
"devDependencies": {
"@types/jest": "^29.5.10",
"@types/md5": "~2.3.5",
"@types/react": "^18.2.42",
"@types/underscore": "^1.11.15",
"@types/uuid": "~9.0.7",
"typescript": "^5.3.2"
Expand Down
170 changes: 170 additions & 0 deletions webapp/packages/core-utils/src/OrderedMap.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { OrderedMap } from './OrderedMap';

describe('OrderedMap', () => {
it('should add and get items', () => {
const map = new OrderedMap<number, string>();
map.add(1, 'one');
map.add(2, 'two');
map.add(3, 'three');
map.add(Infinity, 'infinity');
map.add(NaN, 'nan');

expect(map.get(1)).toBe('one');
expect(map.get(2)).toBe('two');
expect(map.get(3)).toBe('three');
expect(map.get(Infinity)).toBe('infinity');
expect(map.get(NaN)).toBe('nan');
});

it('should not get not exist item and return undefined', () => {
const map = new OrderedMap<number, string>();
map.add(1, 'one');
map.add(2, 'two');
map.add(3, 'three');

expect(map.get(4)).toBeUndefined();
expect(map.get(Infinity)).toBeUndefined();
expect(map.get(NaN)).toBeUndefined();
});

it('should addValue and get items', () => {
const toKey = (v: string) => v.length;
const map = new OrderedMap<number, string>(toKey);
map.addValue('one');
map.addValue('twoo');
map.addValue('three');

expect(map.get(3)).toBe('one');
expect(map.get(4)).toBe('twoo');
expect(map.get(5)).toBe('three');
});

it('should not override with addValue if it already exists', () => {
const toKey = (v: string) => v.length;
const map = new OrderedMap<number, string>(toKey);
map.addValue('one');
map.addValue('twoo');
map.addValue('three');
map.addValue('four');

expect(map.get(4)).toBe('twoo');
});

it('should not addValue if no key fn', () => {
const map = new OrderedMap<number, string>();
expect(() => map.addValue('one')).toThrow();
});

it('should be ordered', () => {
const map = new OrderedMap<number, string>();
map.add(3, 'three');
map.add(1, 'one');
map.add(2, 'two');

expect(map.keys).toEqual([3, 1, 2]);
expect(map.values).toEqual(['three', 'one', 'two']);
});

it('should has items', () => {
const map = new OrderedMap<number, string>();
map.add(1, 'one');
map.add(2, 'two');
map.add(3, 'three');
map.add(Infinity, 'infinity');
map.add(NaN, 'nan');

expect(map.has(1)).toBeTruthy();
expect(map.has(2)).toBeTruthy();
expect(map.has(3)).toBeTruthy();
expect(map.has(Infinity)).toBeTruthy();
expect(map.has(NaN)).toBeTruthy();
});

it('should not override items', () => {
const map = new OrderedMap<number, string>();
map.add(1, 'one');
map.add(1, 'two');
map.add(Infinity, 'infinity');
map.add(Infinity, 'infinity2');
map.add(NaN, 'nan');
map.add(NaN, 'nan2');

expect(map.get(1)).toBe('one');
expect(map.get(Infinity)).toBe('infinity');
expect(map.get(NaN)).toBe('nan');
});

it('should remove items', () => {
const map = new OrderedMap<number, string>();
map.add(1, 'one');
map.add(2, 'two');
map.add(3, 'three');

map.remove(2);

expect(map.get(2)).toBeUndefined();
expect(map.keys).toEqual([1, 3]);
expect(map.values).toEqual(['one', 'three']);
});

it('should not have non-existing items after removal', () => {
const map = new OrderedMap<number, string>();
map.add(1, 'one');
map.add(2, 'two');
map.add(3, 'three');

expect(map.has(4)).toBeFalsy();
expect(map.get(4)).toBeUndefined();
map.remove(4);
expect(map.has(4)).toBeFalsy();
expect(map.get(4)).toBeUndefined();
});

it('should remove all items', () => {
const map = new OrderedMap<number, string>();
map.add(1, 'one');
map.add(2, 'two');
map.add(3, 'three');

map.removeAll();

expect(map.keys).toEqual([]);
expect(map.values).toEqual([]);
});

it('should throw bulk update items if no toKey fn', () => {
const map = new OrderedMap<number, string>();

expect(() => map.bulkUpdate(['one', 'two', 'three'])).toThrow();
});

it('should bulk update items', () => {
const toKey = (v: string) => v.length;
const map = new OrderedMap<number, string>(toKey);
map.bulkUpdate(['o', 'tw', 'thr']);

expect(map.keys).toEqual([1, 2, 3]);
expect(map.values).toEqual(['o', 'tw', 'thr']);
});

it('should bulk rewrite items', () => {
const toKey = (v: string) => v.length;
const map = new OrderedMap<number, string>(toKey);
map.bulkUpdate(['o', 'tw', 'thr']);
map.bulkRewrite(['one', 'twoo', 'three']);

expect(map.keys).toEqual([3, 4, 5]);
expect(map.values).toEqual(['one', 'twoo', 'three']);
});

it('should sort items', () => {
const map = new OrderedMap<number, string>();
map.add(3, 'c');
map.add(1, 'a');
map.add(2, 'b');

map.sort((a, b) => (a > b ? 1 : -1));

expect(map.keys).toEqual([1, 2, 3]);
});
});
34 changes: 34 additions & 0 deletions webapp/packages/core-utils/src/blobToBase64.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { blobToBase64 } from './blobToBase64';

describe('blobToBase64', () => {
it('converts blob to base64', async () => {
const blob = new Blob(['test'], { type: 'text/plain' });
const base64 = await blobToBase64(blob);

expect(base64).toBe('data:text/plain;base64,dGVzdA==');
});

it('converts blob to base64 with slice', async () => {
const blob = new Blob(['this is a test with longer text']);
const base64 = await blobToBase64(blob, 20);

expect(base64).toBe('data:application/octet-stream;base64,dGhpcyBpcyBhIHRlc3Qgd2l0aCA=');
});

it('calls readAsDataURL', async () => {
const readAsDataURL = jest.fn(blob => Promise.resolve(blob));
Object.defineProperty(global, 'FileReader', {
writable: true,
value: jest.fn().mockImplementation(() => ({
readAsDataURL,
})),
});
jest.useFakeTimers();

const blob = new Blob(['test'], { type: 'text/plain' });

blobToBase64(blob);

expect(readAsDataURL).toHaveBeenCalledWith(blob);
});
});
1 change: 0 additions & 1 deletion webapp/packages/core-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export * from './Quadtree/index';

export * from './underscore';

export * from './importLazyComponent';
export * from './base64ToBlob';
export * from './blobToBase64';
export * from './base64ToHex';
Expand Down
2 changes: 1 addition & 1 deletion webapp/packages/plugin-codemirror6/src/EditorLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { importLazyComponent } from '@cloudbeaver/core-utils';
import { importLazyComponent } from '@cloudbeaver/core-blocks';

export const EditorLoader = importLazyComponent(() => import('./Editor').then(m => m.Editor));
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { importLazyComponent } from '@cloudbeaver/core-blocks';
import { createConnectionParam, DATA_CONTEXT_CONNECTION } from '@cloudbeaver/core-connections';
import { injectable } from '@cloudbeaver/core-di';
import { CommonDialogService } from '@cloudbeaver/core-dialogs';
import { LocalizationService } from '@cloudbeaver/core-localization';
import { DATA_CONTEXT_NAV_NODE, EObjectFeature } from '@cloudbeaver/core-navigation-tree';
import { EAdminPermission, SessionPermissionsResource } from '@cloudbeaver/core-root';
import { importLazyComponent, withTimestamp } from '@cloudbeaver/core-utils';
import { withTimestamp } from '@cloudbeaver/core-utils';
import { ACTION_EXPORT, ActionService, DATA_CONTEXT_MENU, DATA_CONTEXT_MENU_NESTED, menuExtractItems, MenuService } from '@cloudbeaver/core-view';
import {
DATA_CONTEXT_DV_DDM,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import { observer } from 'mobx-react-lite';
import { useCallback, useContext, useEffect, useRef } from 'react';

import { getComputed, IconOrImage, Loader, s, useS } from '@cloudbeaver/core-blocks';
import { importLazyComponent, isValidUrl } from '@cloudbeaver/core-utils';
import { getComputed, IconOrImage, importLazyComponent, Loader, s, useS } from '@cloudbeaver/core-blocks';
import { isValidUrl } from '@cloudbeaver/core-utils';
import type { IResultSetRowKey } from '@cloudbeaver/plugin-data-viewer';
import type { RenderCellProps } from '@cloudbeaver/plugin-react-data-grid';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { importLazyComponent } from '@cloudbeaver/core-blocks';
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { ExceptionsCatcherService } from '@cloudbeaver/core-events';
import { ResultDataFormat } from '@cloudbeaver/core-sdk';
import { importLazyComponent } from '@cloudbeaver/core-utils';
import { DataPresentationService } from '@cloudbeaver/plugin-data-viewer';

import { DataGridContextMenuCellEditingService } from './DataGrid/DataGridContextMenu/DataGridContextMenuCellEditingService';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { importLazyComponent } from '@cloudbeaver/core-blocks';
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { EObjectFeature, NavNodeInfoResource } from '@cloudbeaver/core-navigation-tree';
import { importLazyComponent } from '@cloudbeaver/core-utils';
import { NavNodeViewService } from '@cloudbeaver/plugin-navigation-tree';

import { DDLViewerFooterService } from './DdlViewer/DDLViewerFooterService';
Expand Down
2 changes: 1 addition & 1 deletion webapp/packages/plugin-gis-viewer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@
"peerDependencies": {},
"devDependencies": {
"@types/leaflet": "^1.9.8",
"@types/proj4": "^2.5.5",
"@types/react": "^18.2.42",
"@types/react-leaflet": "~3.0.0",
"@types/wellknown": "~0.5.8",
"@types/proj4": "^2.5.5",
"leaflet": "^1.9.4",
"typescript": "^5.3.2",
"typescript-plugin-css-modules": "^5.0.2"
Expand Down
Loading

0 comments on commit d96b9e9

Please sign in to comment.