mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
Cb 5198 unit tests (#2674)
* CB-5198 adds errorOf unit test
* CB-5198 adds unit tests for metadata map
* CB-5198 open centered popup covered with unit tests
* CB-5198 removed unused utils helper
* CB-5198 adds unit tests for utf8 to base64 helper
* СB-5198 adds unit tests fro TempMap
* СB-5198 adds unit tests for GlobalConstants
* CB-5198 removes unused helper
* CB-5198 fix: unit tests for global consts
* СB-5198 adds unit tests for ILoadableState
* СB-5198 adds unit tests for CancellablePromise and cancellableTimeout
* CB-5198 unit tests cleanup
* CB-5198 unit tests cleanup
* CB-5198 updates TS references
* CB-5198 adds PR fixes
* CB-5186 cleanup
* CB-5198 adds correct window object mock for global consts tests
* Revert "CB-5198 updates TS references"
This reverts commit 9388477a79.
* CB-5198 fixes PR comments
* CB-5198 fixes PR comments
* CB-4760 fix: metadatamap return non observed values correctly
---------
Co-authored-by: Alexey <wrouds@gmail.com>
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { GlobalConstants } from './GlobalConstants';
|
||||
|
||||
jest.mock('./isValidUrl', () => ({
|
||||
isValidUrl: jest.fn().mockReturnValue(false),
|
||||
}));
|
||||
|
||||
jest.mock('./pathJoin', () => ({
|
||||
pathJoin: jest.fn((...args: string[]) => args.reduce((acc, arg) => acc + arg, '')),
|
||||
}));
|
||||
|
||||
describe('GlobalConstants', () => {
|
||||
let windowSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
windowSpy = jest.spyOn(window, 'window', 'get');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
windowSpy.mockRestore();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
(global as any)._DEV_ = true;
|
||||
(global as any)._VERSION_ = '1.0.0';
|
||||
(global as any)._ROOT_URI_ = '{ROOT_URI}';
|
||||
|
||||
windowSpy.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
location: {
|
||||
protocol: 'http:',
|
||||
host: 'localhost',
|
||||
},
|
||||
}) as any,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return correct dev value', () => {
|
||||
expect(GlobalConstants.dev).toBe(true);
|
||||
});
|
||||
|
||||
it('should return correct version value', () => {
|
||||
expect(GlobalConstants.version).toBe('1.0.0');
|
||||
});
|
||||
|
||||
it('should return correct protocol value', () => {
|
||||
expect(GlobalConstants.protocol).toBe('http:');
|
||||
});
|
||||
|
||||
it('should return correct host value', () => {
|
||||
expect(GlobalConstants.host).toBe('localhost');
|
||||
});
|
||||
|
||||
it('should return correct websocket protocol', () => {
|
||||
expect(GlobalConstants.wsProtocol).toBe('ws:');
|
||||
|
||||
windowSpy.mockImplementationOnce(
|
||||
() =>
|
||||
({
|
||||
location: {
|
||||
protocol: 'https:',
|
||||
},
|
||||
}) as any,
|
||||
);
|
||||
|
||||
expect(GlobalConstants.wsProtocol).toBe('wss:');
|
||||
});
|
||||
|
||||
it('should return correct rootURI value', () => {
|
||||
expect(GlobalConstants.rootURI).toBe('/');
|
||||
|
||||
(global as any)._ROOT_URI_ = 'http://localhost:8080';
|
||||
(require('./isValidUrl').isValidUrl as jest.Mock).mockReturnValueOnce(true);
|
||||
|
||||
expect(GlobalConstants.rootURI).toBe('/');
|
||||
|
||||
(global as any)._ROOT_URI_ = '/dbeaver';
|
||||
expect(GlobalConstants.rootURI).toBe('/dbeaver/');
|
||||
});
|
||||
|
||||
it('should return correct serviceURI value', () => {
|
||||
expect(GlobalConstants.serviceURI).toBe('/api');
|
||||
});
|
||||
|
||||
it('should return correct health check url', () => {
|
||||
expect(GlobalConstants.getHealthCheckUrl('http://localhost')).toBe('http://localhost/status');
|
||||
expect(GlobalConstants.getHealthCheckUrl('')).toBe('/status');
|
||||
});
|
||||
|
||||
it('should generate absolute root url', () => {
|
||||
expect(GlobalConstants.absoluteRootUrl('test/', 'test2')).toBe('/test/test2');
|
||||
});
|
||||
|
||||
it('should generate absolute service url', () => {
|
||||
expect(GlobalConstants.absoluteServiceUrl('/test/', 'test2')).toBe('/api/test/test2');
|
||||
});
|
||||
|
||||
it('should generate absoluteServiceHTTPUrl', () => {
|
||||
expect(GlobalConstants.absoluteServiceHTTPUrl('/test/', 'test2')).toBe('http://localhost/api/test/test2');
|
||||
});
|
||||
|
||||
it('should generate absoluteServiceWSUrl', () => {
|
||||
expect(GlobalConstants.absoluteServiceWSUrl('/test/', 'test2')).toBe('ws://localhost/api/test/test2');
|
||||
|
||||
windowSpy.mockImplementationOnce(
|
||||
() =>
|
||||
({
|
||||
location: {
|
||||
protocol: 'https:',
|
||||
host: 'localhost',
|
||||
},
|
||||
}) as any,
|
||||
);
|
||||
|
||||
expect(GlobalConstants.absoluteServiceWSUrl('/test/', 'test2')).toBe('wss://localhost/api/test/test2');
|
||||
});
|
||||
|
||||
it('should generate absolute url', () => {
|
||||
expect(GlobalConstants.absoluteUrl('test/', 'test2')).toBe('/test/test2');
|
||||
expect(GlobalConstants.absoluteUrl('platform:test/', 'test2')).toBe('/apiimagesplatform:test/test2');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { getFirstException, ILoadableState, isContainsException, isLoadableStateHasException } from './ILoadableState';
|
||||
|
||||
const getMockedLoadableState = (state: Partial<ILoadableState>): ILoadableState => ({
|
||||
exception: null,
|
||||
isLoading: () => false,
|
||||
isLoaded: () => false,
|
||||
isError: () => false,
|
||||
load: () => undefined,
|
||||
...state,
|
||||
});
|
||||
|
||||
describe('isContainsException', () => {
|
||||
it('should return true if exception is present', () => {
|
||||
expect(isContainsException(new Error())).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if exception is not present', () => {
|
||||
expect(isContainsException(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('should check array of exceptions', () => {
|
||||
expect(isContainsException([null, new Error()])).toBe(true);
|
||||
expect(isContainsException([null, null])).toBe(false);
|
||||
expect(isContainsException([])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFirstException', () => {
|
||||
it('should return first exception', () => {
|
||||
const error = new Error();
|
||||
const error2 = new Error('error2');
|
||||
|
||||
expect(getFirstException([null, error, error2])).toBe(error);
|
||||
expect(getFirstException([null, error])).toBe(error);
|
||||
expect(getFirstException([error])).toBe(error);
|
||||
expect(getFirstException(error)).toBe(error);
|
||||
});
|
||||
|
||||
it('should not get exception', () => {
|
||||
expect(getFirstException([null, null])).toBe(null);
|
||||
expect(getFirstException([null])).toBe(null);
|
||||
expect(getFirstException([])).toBe(null);
|
||||
expect(getFirstException(null)).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isLoadableStateHasException', () => {
|
||||
it('should return true if exception is present', () => {
|
||||
expect(
|
||||
isLoadableStateHasException(
|
||||
getMockedLoadableState({
|
||||
exception: [new Error(), new Error('error2')],
|
||||
isError() {
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
isLoadableStateHasException(
|
||||
getMockedLoadableState({
|
||||
exception: [new Error()],
|
||||
isError() {
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
isLoadableStateHasException(
|
||||
getMockedLoadableState({
|
||||
exception: new Error(),
|
||||
isError() {
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
isLoadableStateHasException(
|
||||
getMockedLoadableState({
|
||||
exception: [null, new Error()],
|
||||
isError() {
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if exception is not present', () => {
|
||||
expect(
|
||||
isLoadableStateHasException(
|
||||
getMockedLoadableState({
|
||||
exception: [],
|
||||
isError() {
|
||||
return false;
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
isLoadableStateHasException(
|
||||
getMockedLoadableState({
|
||||
exception: null,
|
||||
isError() {
|
||||
return false;
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
isLoadableStateHasException(
|
||||
getMockedLoadableState({
|
||||
exception: [null, null],
|
||||
isError() {
|
||||
return false;
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -21,10 +21,6 @@ export interface ILoadableState {
|
||||
cancel?: () => void;
|
||||
}
|
||||
|
||||
// export function composeLoadableState(...states: ILoadableState[]): ILoadableState {
|
||||
|
||||
// }
|
||||
|
||||
export function isLoadableStateHasException(state: ILoadableState): boolean {
|
||||
return isContainsException(state.exception);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { z } from 'zod';
|
||||
|
||||
import { MetadataMap } from './MetadataMap';
|
||||
|
||||
describe('MetadataMap', () => {
|
||||
it('should create an empty map', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
|
||||
expect(map.size).toBe(0);
|
||||
});
|
||||
|
||||
it('should sync items', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
const emptyMap = new MetadataMap<number, string>();
|
||||
|
||||
const data: [number, string][] = [
|
||||
[1, 'one'],
|
||||
[2, 'two'],
|
||||
[3, 'three'],
|
||||
[Infinity, 'infinity'],
|
||||
[NaN, 'nan'],
|
||||
];
|
||||
|
||||
map.sync(data);
|
||||
emptyMap.sync([]);
|
||||
|
||||
data.forEach(([key, value]) => {
|
||||
expect(map.get(key)).toBe(value);
|
||||
});
|
||||
|
||||
expect(emptyMap.size).toBe(0);
|
||||
});
|
||||
|
||||
it('should set items', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
map.set(1, 'one');
|
||||
map.set(2, 'two');
|
||||
map.set(3, 'three');
|
||||
map.set(Infinity, 'infinity');
|
||||
map.set(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 throw an error if validation failed and no default value getter', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
map.sync([
|
||||
[1, 'one'],
|
||||
[2, 'two'],
|
||||
[3, 'three'],
|
||||
]);
|
||||
|
||||
expect(() => map.get(1, undefined, z.object({}))).toThrow();
|
||||
});
|
||||
|
||||
it('should get function default value if validation failed', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
map.sync([
|
||||
[1, 'one'],
|
||||
[2, 'two'],
|
||||
[3, 'three'],
|
||||
]);
|
||||
|
||||
expect(map.get(1, key => `default ${key}`, z.object({}))).toBe('default 1');
|
||||
});
|
||||
|
||||
it('should return default value for non existing element using default getter in constructor', () => {
|
||||
const map = new MetadataMap<number, string>(key => `default ${key}`);
|
||||
|
||||
expect(map.get(1)).toBe('default 1');
|
||||
expect(map.get(Infinity)).toBe('default Infinity');
|
||||
expect(map.get(NaN)).toBe('default NaN');
|
||||
});
|
||||
|
||||
it('should return size', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
map.sync([
|
||||
[1, 'one'],
|
||||
[2, 'two'],
|
||||
[3, 'three'],
|
||||
]);
|
||||
|
||||
expect(map.size).toBe(3);
|
||||
});
|
||||
|
||||
it('should return iterator', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
const data: [number, string][] = [
|
||||
[1, 'one'],
|
||||
[2, 'two'],
|
||||
[3, 'three'],
|
||||
];
|
||||
map.sync(data);
|
||||
|
||||
for (const [key, value] of map) {
|
||||
expect(map.get(key)).toBe(value);
|
||||
}
|
||||
});
|
||||
|
||||
it('should iterate keys', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
const data: [number, string][] = [
|
||||
[1, 'one'],
|
||||
[2, 'two'],
|
||||
[3, 'three'],
|
||||
];
|
||||
const keys = data.map(([key]) => key);
|
||||
|
||||
map.sync(data);
|
||||
|
||||
for (const key of map.keys()) {
|
||||
expect(key).toBe(keys.shift());
|
||||
}
|
||||
});
|
||||
|
||||
it('should iterate values', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
const data: [number, string][] = [
|
||||
[1, 'one'],
|
||||
[2, 'two'],
|
||||
[3, 'three'],
|
||||
];
|
||||
const values = data.map(([, value]) => value);
|
||||
map.sync(data);
|
||||
|
||||
for (const value of map.values()) {
|
||||
expect(value).toBe(values.shift());
|
||||
}
|
||||
});
|
||||
|
||||
it('should iterate entries', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
const data: [number, string][] = [
|
||||
[1, 'one'],
|
||||
[2, 'two'],
|
||||
[3, 'three'],
|
||||
];
|
||||
map.sync(data);
|
||||
|
||||
for (const [key, value] of map.entries()) {
|
||||
expect([key, value]).toStrictEqual(data.shift());
|
||||
}
|
||||
});
|
||||
|
||||
it('should check if key exists', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
map.sync([
|
||||
[1, 'one'],
|
||||
[2, 'two'],
|
||||
[3, 'three'],
|
||||
]);
|
||||
|
||||
expect(map.has(1)).toBeTruthy();
|
||||
expect(map.has(2)).toBeTruthy();
|
||||
expect(map.has(3)).toBeTruthy();
|
||||
expect(map.has(4)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should clear', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
map.sync([
|
||||
[1, 'one'],
|
||||
[2, 'two'],
|
||||
[3, 'three'],
|
||||
]);
|
||||
|
||||
map.clear();
|
||||
|
||||
expect(map.size).toBe(0);
|
||||
});
|
||||
|
||||
it('should delete items', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
const data: [number, string][] = [
|
||||
[1, 'one'],
|
||||
[2, 'two'],
|
||||
[3, 'three'],
|
||||
];
|
||||
|
||||
map.sync(data);
|
||||
|
||||
map.delete(2);
|
||||
map.delete(4);
|
||||
data.splice(1, 1);
|
||||
|
||||
expect(map.has(2)).toBeFalsy();
|
||||
expect(Array.from(map)).toEqual(data);
|
||||
});
|
||||
|
||||
it('should forEach', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
map.sync([
|
||||
[1, 'one'],
|
||||
[2, 'two'],
|
||||
[3, 'three'],
|
||||
]);
|
||||
|
||||
map.forEach((value, key) => {
|
||||
expect(map.get(key)).toBe(value);
|
||||
});
|
||||
});
|
||||
|
||||
it('has to string tag', () => {
|
||||
const map = new MetadataMap<number, string>();
|
||||
|
||||
expect(map[Symbol.toStringTag]).toBe('MetadataMap');
|
||||
});
|
||||
});
|
||||
@@ -75,6 +75,8 @@ export class MetadataMap<TKey, TValue> implements Map<TKey, TValue> {
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO replace zod schema with just validation callback returning true/false.
|
||||
// In case we use something else than zod
|
||||
get(key: TKey, defaultValue?: DefaultValueGetter<TKey, TValue>, schema?: schema.AnyZodObject): TValue {
|
||||
const value = this.temp.get(key);
|
||||
let invalidate = !this.temp.has(key);
|
||||
@@ -95,7 +97,9 @@ export class MetadataMap<TKey, TValue> implements Map<TKey, TValue> {
|
||||
}
|
||||
|
||||
const value = provider(key, this);
|
||||
this.temp.set(key, observable(value as any));
|
||||
const isNotPrimitiveValue = typeof value === 'object' && value !== null;
|
||||
|
||||
this.temp.set(key, isNotPrimitiveValue ? observable(value) : value);
|
||||
}
|
||||
|
||||
return this.temp.get(key)!;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { CancellablePromise } from './CancellablePromise';
|
||||
import { PromiseCancelledError } from './PromiseCancelledError';
|
||||
|
||||
describe('CancellablePromise', () => {
|
||||
jest.mock('./PromiseCancelledError', () => ({
|
||||
PromiseCancelledError: jest.fn(),
|
||||
}));
|
||||
|
||||
it('cancels promise', async () => {
|
||||
const PromiseCancelledErrorMockInstance = new PromiseCancelledError();
|
||||
const promise = new CancellablePromise<void>(resolve => {
|
||||
const token = setTimeout(() => resolve(), 0);
|
||||
return () => {
|
||||
clearTimeout(token);
|
||||
};
|
||||
});
|
||||
|
||||
promise.cancel();
|
||||
|
||||
await expect(promise).rejects.toThrow(PromiseCancelledErrorMockInstance);
|
||||
});
|
||||
|
||||
it('should resolve promise', async () => {
|
||||
const promise = new CancellablePromise<number>(resolve => {
|
||||
const token = setTimeout(() => resolve(777), 0);
|
||||
return () => {
|
||||
clearTimeout(token);
|
||||
};
|
||||
});
|
||||
|
||||
await expect(promise).resolves.toBe(777);
|
||||
});
|
||||
|
||||
it('should reject promise', async () => {
|
||||
const error = new Error('test');
|
||||
const promise = new CancellablePromise<number>((resolve, reject) => {
|
||||
const token = setTimeout(() => reject(error), 0);
|
||||
return () => {
|
||||
clearTimeout(token);
|
||||
};
|
||||
});
|
||||
|
||||
await expect(promise).rejects.toThrow(error);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { cancellableTimeout } from './cancellableTimeout';
|
||||
|
||||
jest.mock('./CancellablePromise', () => ({
|
||||
CancellablePromise: jest.fn().mockImplementation(() => ({
|
||||
cancel: jest.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
describe('cancellableTimeout', () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
it('resolves after the specified timeout', async () => {
|
||||
const timeout = 0;
|
||||
const start = Date.now();
|
||||
|
||||
const promise = cancellableTimeout(timeout);
|
||||
|
||||
await promise;
|
||||
|
||||
jest.advanceTimersByTime(timeout);
|
||||
|
||||
expect(Date.now() - start).toBe(timeout);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { CancellablePromise } from './CancellablePromise';
|
||||
|
||||
export function cancellableTimeout(timeout: number): CancellablePromise<void> {
|
||||
return new CancellablePromise<void>(resolve => {
|
||||
const token = setTimeout(() => resolve(), timeout);
|
||||
return () => {
|
||||
clearTimeout(token);
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { CancellablePromise } from './CancellablePromise';
|
||||
|
||||
type PromiseExecutor<T> = (resolve: (value: T) => void, reject: (reason?: any) => void) => void;
|
||||
|
||||
/**
|
||||
* wait timeout milliseconds then start to execute promise.
|
||||
* During the timeout the execution of promise can be cancelled.
|
||||
*
|
||||
* @param executor
|
||||
* @param timeout
|
||||
*/
|
||||
export function deferPromise<T>(executor: PromiseExecutor<T>, timeout: number): CancellablePromise<T> {
|
||||
return new CancellablePromise<T>((resolve, reject) => {
|
||||
const token = setTimeout(() => executor(resolve, reject), timeout);
|
||||
return () => {
|
||||
clearTimeout(token);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function cancellableTimeout(timeout: number): CancellablePromise<void> {
|
||||
return new CancellablePromise<void>(resolve => {
|
||||
const token = setTimeout(() => resolve(), timeout);
|
||||
return () => {
|
||||
clearTimeout(token);
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { TempMap } from './TempMap';
|
||||
|
||||
describe('TempMap', () => {
|
||||
it('should create empty map', () => {
|
||||
const target = new Map();
|
||||
const map = new TempMap<string, string>(target);
|
||||
|
||||
expect(map.size).toBe(0);
|
||||
expect(target.size).toBe(0);
|
||||
expect(Array.from(map.entries()).length).toBe(0);
|
||||
});
|
||||
|
||||
it('should set and get values', () => {
|
||||
const map = new TempMap<string, string>(new Map());
|
||||
|
||||
map.set('test', 'test value');
|
||||
map.set('test2', 'test value2');
|
||||
|
||||
expect(map.get('test')).toBe('test value');
|
||||
expect(map.get('test2')).toBe('test value2');
|
||||
});
|
||||
|
||||
it('should delete values', () => {
|
||||
const map = new TempMap<string, string>(new Map());
|
||||
|
||||
map.set('test', 'test value');
|
||||
map.delete('test');
|
||||
|
||||
expect(map.get('test')).toBeUndefined();
|
||||
expect(map.isDeleted('test')).toBe(true);
|
||||
});
|
||||
|
||||
it('should clear values', () => {
|
||||
const map = new TempMap<string, string>(new Map());
|
||||
|
||||
map.set('test', 'test value');
|
||||
map.set('test2', 'test value2');
|
||||
map.clear();
|
||||
|
||||
expect(map.get('test')).toBeUndefined();
|
||||
expect(map.get('test2')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should delete deleted values after map being cleared', () => {
|
||||
const map = new TempMap<string, string>(new Map());
|
||||
|
||||
map.set('test', 'test value');
|
||||
map.delete('test');
|
||||
expect(map.isDeleted('test')).toBe(true);
|
||||
map.clear();
|
||||
|
||||
expect(map.get('test')).toBeUndefined();
|
||||
expect(map.isDeleted('test')).toBe(false);
|
||||
});
|
||||
|
||||
it('should remove item from deleted map if it was set again', () => {
|
||||
const map = new TempMap<string, string>(new Map());
|
||||
|
||||
map.set('test', 'test value');
|
||||
map.delete('test');
|
||||
|
||||
expect(map.isDeleted('test')).toBe(true);
|
||||
|
||||
map.set('test', 'test value2');
|
||||
|
||||
expect(map.get('test')).toBe('test value2');
|
||||
expect(map.isDeleted('test')).toBe(false);
|
||||
});
|
||||
|
||||
it('should have for each items', () => {
|
||||
const map = new TempMap<string, string>(new Map());
|
||||
|
||||
map.set('test', 'test value');
|
||||
map.set('test2', 'test value2');
|
||||
|
||||
const values: string[] = [];
|
||||
const keys: string[] = [];
|
||||
map.forEach((value, key) => {
|
||||
values.push(value);
|
||||
keys.push(key);
|
||||
});
|
||||
|
||||
expect(values).toEqual(['test value', 'test value2']);
|
||||
expect(keys).toEqual(['test', 'test2']);
|
||||
});
|
||||
|
||||
it('should for each items and not include deleted', () => {
|
||||
const map = new TempMap<string, string>(new Map());
|
||||
|
||||
map.set('test', 'test value');
|
||||
map.set('test2', 'test value2');
|
||||
map.delete('test');
|
||||
|
||||
const values: string[] = [];
|
||||
const keys: string[] = [];
|
||||
map.forEach((value, key) => {
|
||||
values.push(value);
|
||||
keys.push(key);
|
||||
});
|
||||
|
||||
expect(values).toEqual(['test value2']);
|
||||
expect(keys).toEqual(['test2']);
|
||||
expect(map.isDeleted('test')).toBe(true);
|
||||
});
|
||||
|
||||
it('should check if key exists in the map', () => {
|
||||
const map = new TempMap<string, string>(new Map());
|
||||
|
||||
map.set('test', 'test value');
|
||||
map.set('test2', 'test value2');
|
||||
map.delete('test');
|
||||
|
||||
expect(map.has('test')).toBe(false);
|
||||
expect(map.has('test2')).toBe(true);
|
||||
});
|
||||
|
||||
it('should get keys', () => {
|
||||
const map = new TempMap<string, string>(new Map());
|
||||
|
||||
map.set('test', 'test value');
|
||||
map.set('test2', 'test value2');
|
||||
|
||||
expect(Array.from(map.keys())).toEqual(['test', 'test2']);
|
||||
});
|
||||
|
||||
it('should get values', () => {
|
||||
const map = new TempMap<string, string>(new Map());
|
||||
|
||||
map.set('test', 'test value');
|
||||
map.set('test2', 'test value2');
|
||||
|
||||
expect(Array.from(map.values())).toEqual(['test value', 'test value2']);
|
||||
});
|
||||
|
||||
it('should get entries', () => {
|
||||
const map = new TempMap<string, string>(new Map());
|
||||
|
||||
map.set('test', 'test value');
|
||||
map.set('test2', 'test value2');
|
||||
|
||||
expect(Array.from(map.entries())).toEqual([
|
||||
['test', 'test value'],
|
||||
['test2', 'test value2'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should get size', () => {
|
||||
const map = new TempMap<string, string>(new Map());
|
||||
|
||||
map.set('test', 'test value');
|
||||
map.set('test2', 'test value2');
|
||||
|
||||
expect(map.size).toBe(2);
|
||||
});
|
||||
|
||||
it('should only delete target value', () => {
|
||||
const target = new Map();
|
||||
const map = new TempMap<string, string>(target);
|
||||
|
||||
map.set('test', 'test value');
|
||||
target.delete('test');
|
||||
|
||||
expect(map.get('test')).toBe('test value');
|
||||
expect(target.get('test')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should set target value and also be available in temp map', () => {
|
||||
const target = new Map();
|
||||
const map = new TempMap<string, string>(target);
|
||||
|
||||
target.set('test', 'test value2');
|
||||
|
||||
expect(map.get('test')).toBe('test value2');
|
||||
expect(target.get('test')).toBe('test value2');
|
||||
});
|
||||
|
||||
it('should combine keys from target and temp map', () => {
|
||||
const target = new Map();
|
||||
const map = new TempMap<string, string>(target);
|
||||
|
||||
target.set('test', 'test value2');
|
||||
map.set('test2', 'test value');
|
||||
|
||||
expect(Array.from(map.keys())).toEqual(['test', 'test2']);
|
||||
});
|
||||
|
||||
it('should combine entries from target and temp map', () => {
|
||||
const target = new Map();
|
||||
const map = new TempMap<string, string>(target);
|
||||
|
||||
target.set('test', 'test value2');
|
||||
map.set('test2', 'test value');
|
||||
|
||||
expect(Array.from(map.entries())).toEqual([
|
||||
['test', 'test value2'],
|
||||
['test2', 'test value'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should combine values from target and temp map', () => {
|
||||
const target = new Map();
|
||||
const map = new TempMap<string, string>(target);
|
||||
|
||||
target.set('test', 'test value2');
|
||||
map.set('test2', 'test value');
|
||||
|
||||
expect(Array.from(map.values())).toEqual(['test value2', 'test value']);
|
||||
});
|
||||
|
||||
it('should has target value in temp map', () => {
|
||||
const target = new Map();
|
||||
const map = new TempMap<string, string>(target);
|
||||
|
||||
target.set('test', 'test value2');
|
||||
|
||||
expect(map.has('test')).toBe(true);
|
||||
});
|
||||
|
||||
it('should be iterable', () => {
|
||||
const target = new Map();
|
||||
const map = new TempMap<string, string>(target);
|
||||
|
||||
target.set('test', 'test value2');
|
||||
map.set('test2', 'test value');
|
||||
|
||||
const values: string[] = [];
|
||||
const keys: string[] = [];
|
||||
for (const [key, value] of map) {
|
||||
values.push(value);
|
||||
keys.push(key);
|
||||
}
|
||||
|
||||
expect(values).toEqual(['test value2', 'test value']);
|
||||
expect(keys).toEqual(['test', 'test2']);
|
||||
});
|
||||
|
||||
it('should not get deleted keys', () => {
|
||||
const target = new Map();
|
||||
const map = new TempMap<string, string>(target);
|
||||
|
||||
map.set('test', 'test value2');
|
||||
map.set('test2', 'test value');
|
||||
map.delete('test2');
|
||||
|
||||
expect(Array.from(map.keys())).toEqual(['test']);
|
||||
});
|
||||
|
||||
it('should not get deleted values', () => {
|
||||
const target = new Map();
|
||||
const map = new TempMap<string, string>(target);
|
||||
|
||||
map.set('test', 'test value2');
|
||||
map.set('test2', 'test value');
|
||||
map.delete('test2');
|
||||
|
||||
expect(Array.from(map.values())).toEqual(['test value2']);
|
||||
});
|
||||
|
||||
it('should not get deleted entries', () => {
|
||||
const target = new Map();
|
||||
const map = new TempMap<string, string>(target);
|
||||
|
||||
map.set('test', 'test value2');
|
||||
map.set('test2', 'test value');
|
||||
map.delete('test2');
|
||||
|
||||
expect(Array.from(map.entries())).toEqual([['test', 'test value2']]);
|
||||
});
|
||||
|
||||
it('should has toStringTag', () => {
|
||||
const target = new Map();
|
||||
const map = new TempMap<string, string>(target);
|
||||
|
||||
expect(map[Symbol.toStringTag]).toBe('TempMap');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { errorOf } from './errorOf';
|
||||
|
||||
describe('errorOf', () => {
|
||||
it('should return error of the specified type', () => {
|
||||
class TestError extends Error {}
|
||||
|
||||
const error = new TestError('test');
|
||||
const result = errorOf(error, TestError);
|
||||
|
||||
expect(result).toBe(error);
|
||||
expect(result).toBeInstanceOf(TestError);
|
||||
expect(result?.message).toBe('test');
|
||||
});
|
||||
|
||||
it('should return error of the specified type from the cause', () => {
|
||||
class TestError extends Error {}
|
||||
class AnotherError extends Error {
|
||||
cause: Error;
|
||||
|
||||
constructor(message: string, cause: Error) {
|
||||
super(message);
|
||||
this.cause = cause;
|
||||
}
|
||||
}
|
||||
|
||||
const error = new TestError('test');
|
||||
const testError = new AnotherError('another', error);
|
||||
const result = errorOf(testError, TestError);
|
||||
|
||||
expect(result).toBeInstanceOf(TestError);
|
||||
});
|
||||
|
||||
it('should return undefined if error is not of the specified type', () => {
|
||||
const error = new Error('test');
|
||||
const result = errorOf(error, TypeError);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return undefined if error is not an instance of Error', () => {
|
||||
expect(errorOf({ message: 'test' }, Error)).toBeUndefined();
|
||||
expect(errorOf(undefined, Error)).toBeUndefined();
|
||||
expect(errorOf(null, Error)).toBeUndefined();
|
||||
expect(errorOf(0, Error)).toBeUndefined();
|
||||
expect(errorOf('', Error)).toBeUndefined();
|
||||
expect(errorOf(true, Error)).toBeUndefined();
|
||||
expect(errorOf(Symbol(), Error)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
export * from './Promises/CancellablePromise';
|
||||
export * from './Promises/deferPromise';
|
||||
export * from './Promises/cancellableTimeout';
|
||||
export * from './Promises/Deferred';
|
||||
export * from './Promises/PromiseCancelledError';
|
||||
export * from './Promises/PromiseExecutor';
|
||||
@@ -72,7 +72,6 @@ export * from './createPath';
|
||||
export * from './debounce';
|
||||
export * from './schema';
|
||||
export * from './schemaValidationError';
|
||||
export * from './setByPath';
|
||||
export * from './svgToDataUri';
|
||||
export * from './TempMap';
|
||||
export * from './textToHex';
|
||||
@@ -80,7 +79,6 @@ export * from './uriToBlob';
|
||||
export * from './utf8ToBase64';
|
||||
export * from './createLastPromiseGetter';
|
||||
export * from './removeMetadataFromDataURL';
|
||||
export * from './renamePathName';
|
||||
export * from './removeLineBreak';
|
||||
export * from './replaceSubstring';
|
||||
export * from './formatNumber';
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { openCenteredPopup } from './openCenteredPopup';
|
||||
|
||||
describe('openCenteredPopup', () => {
|
||||
let windowSpy: jest.SpyInstance;
|
||||
const params = {
|
||||
url: 'http://localhost:3000',
|
||||
target: 'target',
|
||||
width: 500,
|
||||
height: 500,
|
||||
features: 'features',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
windowSpy = jest.spyOn(window, 'window', 'get');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
windowSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should open centered popup', () => {
|
||||
const windowMock = {
|
||||
top: {
|
||||
outerWidth: 1000,
|
||||
screenY: 100,
|
||||
screenX: 100,
|
||||
outerHeight: 1000,
|
||||
},
|
||||
screen: {
|
||||
availWidth: 1000,
|
||||
},
|
||||
open: jest.fn(),
|
||||
};
|
||||
|
||||
windowSpy.mockImplementation(() => windowMock);
|
||||
|
||||
const { url, target, width, height, features } = params;
|
||||
|
||||
const result = openCenteredPopup({ url, target, width, height, features });
|
||||
|
||||
expect(result).toBe(windowMock.open());
|
||||
expect(windowMock.open).toHaveBeenCalledWith(
|
||||
url,
|
||||
target,
|
||||
`toolbar=no, menubar=no, width=${width}, height=${height}, top=350, left=350${features}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return null if window.top is null', () => {
|
||||
const { url, target, width, height, features } = params;
|
||||
|
||||
windowSpy.mockImplementation(() => ({
|
||||
top: null,
|
||||
}));
|
||||
|
||||
const result = openCenteredPopup({ url, target, width, height, features });
|
||||
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { createPath } from './createPath';
|
||||
import { getPathParent } from './getPathParent';
|
||||
|
||||
export function renamePathName(path: string, name: string): string {
|
||||
return createPath(getPathParent(path), name);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
export function setByPath(object: any, path: string, value: any) {
|
||||
const parts = path.split('.');
|
||||
const last = parts.pop();
|
||||
|
||||
if (!last) {
|
||||
return;
|
||||
}
|
||||
|
||||
let current = object;
|
||||
|
||||
for (const part of parts) {
|
||||
if (current[part] === undefined) {
|
||||
current[part] = {};
|
||||
}
|
||||
current = current[part];
|
||||
}
|
||||
|
||||
current[last] = value;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { utf8ToBase64 } from './utf8ToBase64';
|
||||
|
||||
describe('utf8ToBase64', () => {
|
||||
it('should convert utf8 to base64', () => {
|
||||
expect(utf8ToBase64('test')).toBe('dGVzdA==');
|
||||
expect(utf8ToBase64('')).toBe('');
|
||||
expect(utf8ToBase64('test test')).toBe('dGVzdCB0ZXN0');
|
||||
});
|
||||
|
||||
it('should convert utf8 to base64 with special characters', () => {
|
||||
expect(utf8ToBase64('test % test')).toBe('dGVzdCAlIHRlc3Q=');
|
||||
expect(utf8ToBase64('%')).toBe('JQ==');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user