Skip to content

Commit

Permalink
test: add test for resizable button and service
Browse files Browse the repository at this point in the history
Signed-off-by: tygao <[email protected]>
  • Loading branch information
raintygao committed Mar 4, 2024
1 parent 3a3e9e4 commit b38176d
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 13 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';

import { ResizableButton, MIN_SIDECAR_SIZE } from './resizable_button';
import { shallow } from 'enzyme';
import { SIDECAR_DOCKED_MODE } from '../sidecar_service';

const storeWindowEvents = () => {
const map: Record<string, Function> = {};
window.addEventListener = jest.fn().mockImplementation((event: string, cb) => {
map[event] = cb;
});
return map;
};

const DEFAULT_FLYOUT_SIZE = 460;
const props = {
dockedMode: SIDECAR_DOCKED_MODE.RIGHT,
onResize: jest.fn(),
flyoutSize: DEFAULT_FLYOUT_SIZE,
};

test('is rendered', () => {
const component = shallow(<ResizableButton {...props} />);
expect(component).toMatchSnapshot();
});

test('it should be horizontal when docked mode is right', () => {
const component = shallow(<ResizableButton {...props} />);
expect(component.hasClass('resizableButton--horizontal')).toBe(true);
expect(component.hasClass('resizableButton--vertical')).toBe(false);
expect(component).toMatchSnapshot();
});

test('it should be vertical when docked mode is takeover', () => {
const newProps = { ...props, dockedMode: SIDECAR_DOCKED_MODE.TAKEOVER };
const component = shallow(<ResizableButton {...newProps} />);
expect(component.hasClass('resizableButton--vertical')).toBe(true);
expect(component.hasClass('resizableButton--horizontal')).toBe(false);
expect(component).toMatchSnapshot();
});

test('it should emit onResize with new flyout size when drag and horizontal', () => {
const windowEvents = storeWindowEvents();

const onResize = jest.fn();
const newProps = { ...props, onResize };
const component = shallow(<ResizableButton {...newProps} />);
const resizer = component.find(`[data-test-subj~="resizableButton"]`).first();
expect(onResize).not.toHaveBeenCalled();
resizer.simulate('mousedown', { clientX: 0, pageX: 0, pageY: 0 });
windowEvents?.mousemove({ clientX: -1000, pageX: 0, pageY: 0 });
windowEvents?.mouseup();
const newSize = 1000 + DEFAULT_FLYOUT_SIZE;
expect(onResize).toHaveBeenCalledWith(newSize);
expect(component).toMatchSnapshot();
});

test('it should emit onResize with new flyout size when drag and vertical', () => {
const windowEvents = storeWindowEvents();

const onResize = jest.fn();
const newProps = { ...props, onResize, dockedMode: SIDECAR_DOCKED_MODE.TAKEOVER };
const component = shallow(<ResizableButton {...newProps} />);
const resizer = component.find(`[data-test-subj~="resizableButton"]`).first();
expect(onResize).not.toHaveBeenCalled();
resizer.simulate('mousedown', { clientY: 0, pageX: 0, pageY: 0 });
windowEvents?.mousemove({ clientY: -1000, pageX: 0, pageY: 0 });
windowEvents?.mouseup();
const newSize = 1000 + DEFAULT_FLYOUT_SIZE;
expect(onResize).toHaveBeenCalledWith(newSize);
expect(component).toMatchSnapshot();
});

test('it should emit onResize with min size when drag if new size is below the minimum', () => {
const windowEvents = storeWindowEvents();
const onResize = jest.fn();
const newProps = { ...props, onResize };
const component = shallow(<ResizableButton {...newProps} />);
const resizer = component.find(`[data-test-subj~="resizableButton"]`).first();
expect(onResize).not.toHaveBeenCalled();
resizer.simulate('mousedown', { clientX: 0, pageX: 0, pageY: 0 });
windowEvents?.mousemove({ clientX: 1000, pageX: 0, pageY: 0 });
windowEvents?.mouseup();
const newSize = MIN_SIDECAR_SIZE;
expect(onResize).toHaveBeenCalledWith(newSize);
expect(component).toMatchSnapshot();
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
import React, { useCallback, useRef } from 'react';
import classNames from 'classnames';
import './resizable_button.scss';
import { getPosition } from './helper';
import { ISidecarConfig, SIDECAR_DOCKED_MODE } from './sidecar_service';
import { getPosition } from '../helper';
import { ISidecarConfig, SIDECAR_DOCKED_MODE } from '../sidecar_service';

interface Props {
onResize: (size: number) => void;
flyoutSize: number;
dockedMode: ISidecarConfig['dockedMode'] | undefined;
}

const MIN_SIDECAR_SIZE = 350;
export const MIN_SIDECAR_SIZE = 350;

export const ResizableButton = ({ dockedMode, onResize, flyoutSize }: Props) => {
const isHorizontal = dockedMode !== SIDECAR_DOCKED_MODE.TAKEOVER;
Expand All @@ -40,7 +40,7 @@ export const ResizableButton = ({ dockedMode, onResize, flyoutSize }: Props) =>
};
const onMouseMove = (e: MouseEvent | TouchEvent) => {
let offset;
if (dockedMode === 'left') {
if (dockedMode === SIDECAR_DOCKED_MODE.LEFT) {
offset = getPosition(e, isHorizontal) - initialMouseXorY.current;
} else {
offset = initialMouseXorY.current - getPosition(e, isHorizontal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import React, { useCallback, useMemo } from 'react';
import useObservable from 'react-use/lib/useObservable';
import { BehaviorSubject } from 'rxjs';
import classNames from 'classnames';
import { I18nStart } from '../../i18n';
import { MountPoint } from '../../types';
import { MountWrapper } from '../../utils';
import './sidecar_service.scss';
import { I18nStart } from '../../../i18n';
import { MountPoint } from '../../../types';
import { MountWrapper } from '../../../utils';
import './sidecar.scss';
import { ResizableButton } from './resizable_button';
import { ISidecarConfig, OverlaySidecarOpenOptions } from './sidecar_service';
import { ISidecarConfig, OverlaySidecarOpenOptions } from '../sidecar_service';

interface Props {
sidecarConfig$: BehaviorSubject<ISidecarConfig | undefined>;
Expand Down
1 change: 0 additions & 1 deletion src/core/public/overlays/sidecar/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* SPDX-License-Identifier: Apache-2.0
*/

// import { MouseEvent, TouchEvent } from 'react';
import { ISidecarConfig } from './sidecar_service';

function isMouseEvent(
Expand Down
107 changes: 107 additions & 0 deletions src/core/public/overlays/sidecar/sidecar_service.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { mockReactDomRender, mockReactDomUnmount } from '../overlay.test.mocks';

import { mount } from 'enzyme';
import { i18nServiceMock } from '../../i18n/i18n_service.mock';
import {
SidecarService,
OverlaySidecarStart,
OverlaySidecarOpenOptions,
SIDECAR_DOCKED_MODE,
} from './sidecar_service';
import { OverlayRef } from '../types';

const i18nMock = i18nServiceMock.createStartContract();

beforeEach(() => {
mockReactDomRender.mockClear();
mockReactDomUnmount.mockClear();
});

const mountText = (text: string) => (container: HTMLElement) => {
const content = document.createElement('span');
content.textContent = text;
container.append(content);
return () => {};
};

const getServiceStart = () => {
const service = new SidecarService();
return service.start({ i18n: i18nMock, targetDomElement: document.createElement('div') });
};

describe('SidecarService', () => {
let sidecar: OverlaySidecarStart;
const options: OverlaySidecarOpenOptions = {
config: {
dockedMode: SIDECAR_DOCKED_MODE.RIGHT,
paddingSize: 460,
},
};
beforeEach(() => {
sidecar = getServiceStart();
});

describe('openSidecar()', () => {
it('renders a sidecar to the DOM', () => {
expect(mockReactDomRender).not.toHaveBeenCalled();
sidecar.open(mountText('Sidecar content'), options);
const content = mount(mockReactDomRender.mock.calls[0][0]);
expect(content.html()).toMatchSnapshot();
});
describe('with a currently active sidecar', () => {
let ref1: OverlayRef;
beforeEach(() => {
ref1 = sidecar.open(mountText('Sidecar content'), options);
});
it('replaces the current sidecar with a new one', () => {
sidecar.open(mountText('Sidecar content 2'), options);
expect(mockReactDomUnmount).toHaveBeenCalledTimes(1);
const modalContent = mount(mockReactDomRender.mock.calls[1][0]);
expect(modalContent.html()).toMatchSnapshot();
expect(() => ref1.close()).not.toThrowError();
expect(mockReactDomUnmount).toHaveBeenCalledTimes(1);
});
it('resolves onClose on the previous ref', async () => {
const onCloseComplete = jest.fn();
ref1.onClose.then(onCloseComplete);
sidecar.open(mountText('Sidecar content 2'), options);
await ref1.onClose;
expect(onCloseComplete).toBeCalledTimes(1);
});
});
});
describe('SidecarRef#close()', () => {
it('resolves the onClose Promise', async () => {
const ref = sidecar.open(mountText('Sidecar content'), options);

const onCloseComplete = jest.fn();
ref.onClose.then(onCloseComplete);
await ref.close();
await ref.close();
expect(onCloseComplete).toHaveBeenCalledTimes(1);
});
it('can be called multiple times on the same SidecarRef', async () => {
const ref = sidecar.open(mountText('Sidecar content'), options);
expect(mockReactDomUnmount).not.toHaveBeenCalled();
await ref.close();
expect(mockReactDomUnmount.mock.calls).toMatchSnapshot();
await ref.close();
expect(mockReactDomUnmount).toHaveBeenCalledTimes(1);
});
it("on a stale Sidecar doesn't affect the active sidecar", async () => {
const ref1 = sidecar.open(mountText('Sidecar content 1'), options);
const ref2 = sidecar.open(mountText('Sidecar content 2'), options);
const onCloseComplete = jest.fn();
ref2.onClose.then(onCloseComplete);
mockReactDomUnmount.mockClear();
await ref1.close();
expect(mockReactDomUnmount).toBeCalledTimes(0);
expect(onCloseComplete).toBeCalledTimes(0);
});
});
});
6 changes: 3 additions & 3 deletions src/core/public/overlays/sidecar/sidecar_service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { I18nStart } from '../../i18n';
import { MountPoint } from '../../types';
import './sidecar_service.scss';
import { OverlayRef } from '../types';
import { Sidecar } from './sidecar';
import { Sidecar } from './components/sidecar';
/**
* A SidecarRef is a reference to an opened sidecar panel. It offers methods to
* close the sidecar panel again. If you open a sidecar panel you should make
Expand Down Expand Up @@ -68,7 +67,7 @@ export interface OverlaySidecarStart {
* @param options {@link OverlaySidecarOpenOptions} - options for the sidecar
* @return {@link SidecarRef} A reference to the opened sidecar panel.
*/
open(mount: MountPoint, options?: OverlaySidecarOpenOptions): SidecarRef;
open(mount: MountPoint, options: OverlaySidecarOpenOptions): SidecarRef;

/**
* Override the default support config
Expand Down Expand Up @@ -147,6 +146,7 @@ export class SidecarService {
if (this.activeSidecar) {
setSidecarConfig({ paddingSize: 0 });
this.activeSidecar.close();
this.cleanupDom();
}

const sidecar = new SidecarRef();
Expand Down

0 comments on commit b38176d

Please sign in to comment.