Skip to content

Commit

Permalink
CB-5340 fixes unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sergeyteleshev committed Jul 17, 2024
1 parent c52a992 commit 437a3ee
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 124 deletions.
31 changes: 31 additions & 0 deletions webapp/packages/core-blocks/src/useControlledScroll.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,37 @@ describe('useControlledScroll', () => {
expect(true).toBe(true);
});

it('should change element and apply the current state to it', () => {
const { rerender } = renderHook(({ el, state }) => useControlledScroll(el, state), {
initialProps: { el: element, state: scrollState },
});

const newElement = document.createElement('div');

rerender({ el: newElement, state: scrollState });

jest.runAllTimers();

expect(newElement.scrollTop).toBe(0);
expect(newElement.scrollLeft).toBe(0);
});

it('should change element and state and apply the new state to the new element', () => {
const { rerender } = renderHook(({ el, state }) => useControlledScroll(el, state), {
initialProps: { el: element, state: scrollState },
});

const newElement = document.createElement('div');
const newState = { scrollTop: 75, scrollLeft: 25 };

rerender({ el: newElement, state: newState });

jest.runAllTimers();

expect(newElement.scrollTop).toBe(75);
expect(newElement.scrollLeft).toBe(25);
});

it('should remove event listener if element becomes null', () => {
const { rerender } = renderHook(({ el, state }) => useControlledScroll(el, state), {
initialProps: { el: element as HTMLDivElement | null, state: scrollState },
Expand Down
106 changes: 39 additions & 67 deletions webapp/packages/core-blocks/src/useObjectRef.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,21 @@
*/
import { renderHook } from '@testing-library/react';

import * as coreUtils from '@cloudbeaver/core-utils';

import { useObjectRef } from './useObjectRef';

jest.mock('@cloudbeaver/core-utils', () => ({
bindFunctions: jest.fn(),
}));

describe('useObjectRef', () => {
const bindFunctions = jest.spyOn(coreUtils, 'bindFunctions');

beforeAll(() => {
bindFunctions.mockClear();
});

test('should initialize', () => {
const { result } = renderHook(() =>
useObjectRef({
Expand All @@ -30,20 +42,8 @@ describe('useObjectRef', () => {
expect(result.current).toEqual({});
});

test('should change ref value', () => {
const { result } = renderHook(() =>
useObjectRef({
count: 0,
}),
);

result.current.count = 123;

expect(result.current.count).toBe(123);
});

test('should bind ref functions', () => {
const { result } = renderHook(() =>
renderHook(() =>
useObjectRef(
() => ({
count: 0,
Expand All @@ -55,76 +55,48 @@ describe('useObjectRef', () => {
['increment'],
),
);
const anotherContextObject = {
count: 123,
increment: result.current.increment,
};

result.current.increment();

expect(result.current.count).toBe(1);

anotherContextObject.increment();

expect(anotherContextObject.count).toBe(123);
expect(result.current.count).toBe(2);
expect(bindFunctions).toHaveBeenCalledTimes(1);
});

it('should not bind with empty bind array', () => {
const { result } = renderHook(() =>
useObjectRef(
() => ({
test('should update ref via initial state method', () => {
type TRef = { update: { count: number; increment?: (this: { count: number }) => void } };
const init = () => ({ count: 0 });

const { result } = renderHook(({ update }: TRef) => useObjectRef(init, update, ['increment']), {
initialProps: {
update: {
count: 0,
increment: function (this: { count: number }) {
increment: function () {
this.count++;
},
}),
false,
[],
),
);

expect(typeof result.current.increment).toBe('function');

const anotherContextObject = {
count: 123,
increment: result.current.increment,
};

anotherContextObject.increment();

expect(result.current.count).toBe(0);
expect(anotherContextObject.count).toBe(124);
});

test('should update ref', () => {
const { result, rerender } = renderHook(
({ update }: { update: { count: number; increment?: (this: { count: number }) => void } }) =>
useObjectRef(() => ({ count: 0 }), update, ['increment'] as const),
{
initialProps: {
update: {
count: 0,
},
},
},
);
});

expect(result.current.count).toBe(0);

result.current?.increment?.();

expect(result.current.count).toBe(0);
expect(result.current.count).toBe(1);
});

rerender({
update: {
count: 1,
increment: result.current.increment,
},
test('should update ref', () => {
type TRef = { update: { count: number } };
const init = () => ({ count: 0 });

const { result, rerender } = renderHook(({ update }: TRef) => useObjectRef(init, update), {
initialProps: {
update: {
count: 0,
},
} as TRef,
});

result.current?.increment?.();
expect(result.current.count).toBe(0);

expect(result.current.count).toBe(1);
rerender({ update: { count: 3 } });

expect(result.current.count).toBe(3);
});
});
12 changes: 2 additions & 10 deletions webapp/packages/core-blocks/src/useObjectRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
*/
import { useState } from 'react';

import { bindFunctions } from '@cloudbeaver/core-utils';

export function useObjectRef<T extends Record<any, any>>(init: () => T & ThisType<T>, update: false, bind?: Array<keyof T>): T;
export function useObjectRef<T extends Record<any, any>>(init: () => T & ThisType<T>): T;
export function useObjectRef<T extends Record<any, any>, U extends Record<any, any>>(
Expand Down Expand Up @@ -58,13 +60,3 @@ export function useObjectRef<T extends Record<any, any>, U extends Record<any, a

return ref;
}

function bindFunctions<T>(object: T, keys: Array<keyof T>): void {
for (const key of keys) {
const value = object[key];

if (typeof value === 'function') {
object[key] = value.bind(object);
}
}
}
71 changes: 36 additions & 35 deletions webapp/packages/core-blocks/src/useObservableRef.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,22 @@
import { renderHook } from '@testing-library/react';
import { action, computed, isObservable, observable, runInAction } from 'mobx';

import * as coreUtils from '@cloudbeaver/core-utils';

import { useObservableRef } from './useObservableRef';

jest.mock('@cloudbeaver/core-utils', () => ({
bindFunctions: jest.fn(),
}));

describe('useObservableRef', () => {
test('initializes with a function', () => {
const bindFunctions = jest.spyOn(coreUtils, 'bindFunctions');

beforeAll(() => {
bindFunctions.mockClear();
});

test('should initialize with a function', () => {
const init = () => ({ count: 0 });
const observed = { count: observable };

Expand All @@ -21,7 +33,7 @@ describe('useObservableRef', () => {
expect(isObservable(result.current)).toBe(true);
});

test('initializes with an object', () => {
test('should initialize with an object', () => {
const init = { count: 0 };
const observed = { count: observable };

Expand All @@ -31,21 +43,10 @@ describe('useObservableRef', () => {
expect(isObservable(result.current)).toBe(true);
});

test('updates the object', () => {
const init = () => ({ count: 0 });
const observed = { count: observable };
const update = { count: 1 };

const { result } = renderHook(() => useObservableRef(init, observed, update));

expect(result.current.count).toBe(1);
expect(isObservable(result.current)).toBe(true);
});

test('binds functions', () => {
test('should bind functions', () => {
const observed = { count: observable, increment: action };

const { result } = renderHook(() =>
renderHook(() =>
useObservableRef(
() => ({
count: 0,
Expand All @@ -59,16 +60,7 @@ describe('useObservableRef', () => {
),
);

expect(typeof result.current.increment).toBe('function');
result.current.increment();
expect(result.current.count).toBe(1);

const anotherContext = { count: 0, increment: result.current.increment };

anotherContext.increment();

expect(anotherContext.count).toBe(0);
expect(result.current.count).toBe(2);
expect(bindFunctions).toHaveBeenCalledTimes(1);
});

test('handles computed properties', () => {
Expand All @@ -89,7 +81,7 @@ describe('useObservableRef', () => {
expect(result.current.doubleCount).toBe(10);
});

test('handles partial updates', () => {
test('should merge update param to initial state', () => {
const init = () => ({ count: 0, text: 'hello' });
const observed = { count: observable, text: observable };
const update = { count: 1 };
Expand All @@ -101,7 +93,7 @@ describe('useObservableRef', () => {
expect(isObservable(result.current)).toBe(true);
});

test('handles update as bind array', () => {
test('should merge update to bind', () => {
const init = () => ({
count: 0,
increment: function (this: { count: number }) {
Expand All @@ -111,17 +103,26 @@ describe('useObservableRef', () => {
const observed = { count: observable, increment: action };
const update = ['increment'];

const { result } = renderHook(() => useObservableRef(init, observed, update));
renderHook(() => useObservableRef(init, observed, update));

expect(typeof result.current.increment).toBe('function');
result.current.increment();
expect(result.current.count).toBe(1);
expect(bindFunctions).toHaveBeenCalledTimes(2);
});

test('should update ref', () => {
interface Update {
count: number;
str?: string;
}
const init = () => ({ count: 0 }) as Update;
const observed = { count: observable };

const { result, rerender } = renderHook((update: Update) => useObservableRef(init, observed, update));

const anotherContext = { count: 0, increment: result.current.increment };
expect(result.current.count).toBe(0);

anotherContext.increment();
rerender({ count: 3, str: 'hello' });

expect(anotherContext.count).toBe(0);
expect(result.current.count).toBe(2);
expect(result.current.count).toBe(3);
expect(result.current.str).toBe('hello');
});
});
16 changes: 4 additions & 12 deletions webapp/packages/core-blocks/src/useObservableRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import { action, AnnotationsMap, makeObservable, runInAction, untracked } from 'mobx';
import { useLayoutEffect, useState } from 'react';

import { bindFunctions } from '@cloudbeaver/core-utils';

export function useObservableRef<T extends Record<any, any>>(
init: () => T & ThisType<T>,
observed: AnnotationsMap<T, never>,
Expand Down Expand Up @@ -70,12 +72,12 @@ export function useObservableRef<T extends Record<any, any>>(
update = undefined;
}

state = makeObservable(state, observed, { deep: false, name });

if (bind) {
bindFunctions(state, bind as []);
}

state = makeObservable(state, observed, { deep: false, name });

return state;
});

Expand All @@ -98,16 +100,6 @@ export function useObservableRef<T extends Record<any, any>>(
return state;
}

function bindFunctions<T>(object: T, keys: Array<keyof T>): void {
for (const key of keys) {
const value = object[key];

if (typeof value === 'function') {
object[key] = value.bind(object);
}
}
}

function assign(object: any, update: any): void {
for (const [key, value] of Object.entries(update)) {
if (!(key in object) || object[key] !== value) {
Expand Down
Loading

0 comments on commit 437a3ee

Please sign in to comment.