Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(CanvasToolbar): Add DSL Selector #1034

Merged
merged 1 commit into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { fireEvent, render } from '@testing-library/react';
import { ChangeDSLModal } from './ChangeDSLModal';

describe('ChangeDSLModal', () => {
it('should be hidden when isOpen is false', () => {
const wrapper = render(<ChangeDSLModal isOpen={false} onConfirm={jest.fn()} onCancel={jest.fn()} />);

expect(wrapper.queryByTestId('confirmation-modal')).not.toBeInTheDocument();
});

it('should be visible when isOpen is true', () => {
const wrapper = render(<ChangeDSLModal isOpen={true} onConfirm={jest.fn()} onCancel={jest.fn()} />);

expect(wrapper.queryByTestId('confirmation-modal')).toBeInTheDocument();
});

it('should call onConfirm when confirm button is clicked', () => {
const onConfirm = jest.fn();
const wrapper = render(<ChangeDSLModal isOpen={true} onConfirm={onConfirm} onCancel={jest.fn()} />);

fireEvent.click(wrapper.getByTestId('confirmation-modal-confirm'));

expect(onConfirm).toBeCalled();
});

it('should call onCancel when cancel button is clicked', () => {
const onCancel = jest.fn();
const wrapper = render(<ChangeDSLModal isOpen={true} onConfirm={jest.fn()} onCancel={onCancel} />);

fireEvent.click(wrapper.getByTestId('confirmation-modal-cancel'));

expect(onCancel).toBeCalled();
});

it('should call onCancel when close button is clicked', () => {
const onCancel = jest.fn();
const wrapper = render(<ChangeDSLModal isOpen={true} onConfirm={jest.fn()} onCancel={onCancel} />);

fireEvent.click(wrapper.getByLabelText('Close'));

expect(onCancel).toBeCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Button, Modal, ModalVariant } from '@patternfly/react-core';
import { FunctionComponent } from 'react';

interface ChangeDSLModalProps {
isOpen: boolean;
onConfirm: () => void;
onCancel: () => void;
}

export const ChangeDSLModal: FunctionComponent<ChangeDSLModalProps> = (props) => {
return (
<Modal
variant={ModalVariant.small}
title="Warning"
data-testid="confirmation-modal"
titleIconVariant="warning"
onClose={props.onCancel}
actions={[
<Button key="confirm" variant="primary" data-testid="confirmation-modal-confirm" onClick={props.onConfirm}>
Confirm
</Button>,
<Button key="cancel" variant="link" data-testid="confirmation-modal-cancel" onClick={props.onCancel}>
Cancel
</Button>,
]}
isOpen={props.isOpen}
>
<p>
This will remove any existing integration and you will lose your current work. Are you sure you would like to
proceed?
</p>
</Modal>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { act, fireEvent, render } from '@testing-library/react';
import { EntitiesContextResult } from '../../../../hooks';
import { KaotoSchemaDefinition } from '../../../../models';
import { SourceSchemaType, sourceSchemaConfig } from '../../../../models/camel';
import { EntitiesContext } from '../../../../providers/entities.provider';
import { SourceCodeApiContext } from '../../../../providers/source-code.provider';
import { DSLSelector } from './DSLSelector';

describe('DSLSelector.tsx', () => {
const config = sourceSchemaConfig;
config.config[SourceSchemaType.Integration].schema = {
schema: { name: 'Integration', description: 'desc' } as KaotoSchemaDefinition['schema'],
} as KaotoSchemaDefinition;
config.config[SourceSchemaType.Pipe].schema = {
schema: { name: 'Pipe', description: 'desc' } as KaotoSchemaDefinition['schema'],
} as KaotoSchemaDefinition;
config.config[SourceSchemaType.Kamelet].schema = {
schema: { name: 'Kamelet', description: 'desc' } as KaotoSchemaDefinition['schema'],
} as KaotoSchemaDefinition;
config.config[SourceSchemaType.KameletBinding].schema = {
name: 'kameletBinding',
schema: { description: 'desc' },
} as KaotoSchemaDefinition;
config.config[SourceSchemaType.Route].schema = {
schema: { name: 'route', description: 'desc' } as KaotoSchemaDefinition['schema'],
} as KaotoSchemaDefinition;

const renderWithContext = () => {
return render(
<SourceCodeApiContext.Provider
value={{
setCodeAndNotify: jest.fn(),
}}
>
<EntitiesContext.Provider
value={
{
currentSchemaType: SourceSchemaType.Integration,
} as unknown as EntitiesContextResult
}
>
<DSLSelector />
</EntitiesContext.Provider>
</SourceCodeApiContext.Provider>,
);
};

it('should render all of the types', async () => {
const wrapper = renderWithContext();
const trigger = await wrapper.findByTestId('dsl-list-dropdown');

/** Open Select */
act(() => {
fireEvent.click(trigger);
});

for (const name of ['Pipe', 'Camel Route']) {
const element = await wrapper.findByText(name);
expect(element).toBeInTheDocument();
}
});

it('should warn the user when adding a different type of flow', async () => {
const wrapper = renderWithContext();
const trigger = await wrapper.findByTestId('dsl-list-dropdown');

/** Open Select */
act(() => {
fireEvent.click(trigger);
});

/** Select an option */
act(() => {
const element = wrapper.getByText('Pipe');
fireEvent.click(element);
});

const modal = await wrapper.findByTestId('confirmation-modal');
expect(modal).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { FunctionComponent, PropsWithChildren, useCallback, useContext, useState } from 'react';
import { SourceSchemaType } from '../../../../models/camel';
import { FlowTemplateService } from '../../../../models/visualization/flows/support/flow-templates-service';
import { SourceCodeApiContext } from '../../../../providers';
import { ChangeDSLModal } from './ChangeDSLModal/ChangeDSLModal';
import { DSLSelectorToggle } from './DSLSelectorToggle/DSLSelectorToggle';

export const DSLSelector: FunctionComponent<PropsWithChildren> = () => {
const sourceCodeContextApi = useContext(SourceCodeApiContext);
const [isConfirmationModalOpen, setIsConfirmationModalOpen] = useState(false);
const [proposedFlowType, setProposedFlowType] = useState<SourceSchemaType>();

const checkBeforeAddNewFlow = useCallback((flowType: SourceSchemaType) => {
/**
* If it is not the same DSL, this operation might result in
* removing the existing flows, so then we warn the user first
*/
setProposedFlowType(flowType);
setIsConfirmationModalOpen(true);
}, []);

const onConfirm = useCallback(() => {
if (proposedFlowType) {
sourceCodeContextApi.setCodeAndNotify(FlowTemplateService.getFlowYamlTemplate(proposedFlowType));
setIsConfirmationModalOpen(false);
}
}, [proposedFlowType, sourceCodeContextApi]);

const onCancel = useCallback(() => {
setIsConfirmationModalOpen(false);
}, []);

return (
<>
<DSLSelectorToggle onSelect={checkBeforeAddNewFlow} />
<ChangeDSLModal isOpen={isConfirmationModalOpen} onConfirm={onConfirm} onCancel={onCancel} />
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { act, fireEvent, render, waitFor } from '@testing-library/react';
import { FunctionComponent } from 'react';
import { EntitiesContextResult } from '../../../../../hooks';
import { KaotoSchemaDefinition } from '../../../../../models';
import { SourceSchemaType, sourceSchemaConfig } from '../../../../../models/camel';
import { EntitiesContext } from '../../../../../providers/entities.provider';
import { DSLSelectorToggle } from './DSLSelectorToggle';

const config = sourceSchemaConfig;
config.config[SourceSchemaType.Pipe].schema = {
name: 'Pipe',
schema: { name: 'Pipe', description: 'desc' } as KaotoSchemaDefinition['schema'],
} as KaotoSchemaDefinition;
config.config[SourceSchemaType.Kamelet].schema = {
name: 'Kamelet',
schema: { name: 'Kamelet', description: 'desc' } as KaotoSchemaDefinition['schema'],
} as KaotoSchemaDefinition;
config.config[SourceSchemaType.Route].schema = {
name: 'route',
schema: { name: 'route', description: 'desc' } as KaotoSchemaDefinition['schema'],
} as KaotoSchemaDefinition;

describe('DSLSelectorToggle.tsx', () => {
let onSelect: () => void;
beforeEach(() => {
onSelect = jest.fn();
});

const DSLSelectorWithContext: FunctionComponent<{ currentSchemaType?: SourceSchemaType }> = (props) => {
const currentSchemaType = props.currentSchemaType ?? SourceSchemaType.Route;
return (
<EntitiesContext.Provider key={Date.now()} value={{ currentSchemaType } as unknown as EntitiesContextResult}>
<DSLSelectorToggle onSelect={onSelect} />
</EntitiesContext.Provider>
);
};

it('component renders', () => {
const wrapper = render(<DSLSelectorWithContext />);
const toggle = wrapper.queryByTestId('dsl-list-dropdown');
expect(toggle).toBeInTheDocument();
});

it('should call onSelect when clicking on the MenuToggleAction', async () => {
const wrapper = render(<DSLSelectorWithContext />);

/** Click on toggle */
const toggle = await wrapper.findByTestId('dsl-list-dropdown');
act(() => {
fireEvent.click(toggle);
});

/** Click on first element */
const element = await wrapper.findByText('Pipe');
act(() => {
fireEvent.click(element);
});

await waitFor(() => {
expect(onSelect).toHaveBeenCalled();
});
});

it('should disable the MenuToggleAction if the DSL is already selected', async () => {
const wrapper = render(<DSLSelectorWithContext currentSchemaType={SourceSchemaType.Route} />);

/** Click on toggle */
const toggle = await wrapper.findByTestId('dsl-list-dropdown');
act(() => {
fireEvent.click(toggle);
});

/** Click on first element */
const element = await wrapper.findByText('Camel Route');
// act(() => {
// fireEvent.click(element);
// });

waitFor(() => {
expect(element).toBeDisabled();
});
});

it('should toggle list of DSLs', async () => {
const wrapper = render(<DSLSelectorWithContext />);
const toggle = await wrapper.findByTestId('dsl-list-dropdown');

/** Click on toggle */
act(() => {
fireEvent.click(toggle);
});

const element = await wrapper.findByText('Pipe');
expect(element).toBeInTheDocument();

/** Close Select */
act(() => {
fireEvent.click(toggle);
});

waitFor(() => {
expect(element).not.toBeInTheDocument();
});
});

it('should show selected value', async () => {
const wrapper = render(<DSLSelectorWithContext />);
const toggle = await wrapper.findByTestId('dsl-list-dropdown');

/** Open Select */
act(() => {
fireEvent.click(toggle);
});

/** Click on first element */
act(() => {
const element = wrapper.getByText('Camel Route');
fireEvent.click(element);
});

/** Open Select again */
act(() => {
fireEvent.click(toggle);
});

const element = await wrapper.findByRole('option', { selected: true });
expect(element).toBeInTheDocument();
expect(element).toHaveTextContent('Camel Route');
});

it('should have selected DSL if provided', async () => {
const wrapper = render(<DSLSelectorWithContext />);
const toggle = await wrapper.findByTestId('dsl-list-dropdown');

/** Open Select */
act(() => {
fireEvent.click(toggle);
});

waitFor(() => {
const element = wrapper.queryByRole('option', { selected: true });
expect(element).toBeInTheDocument();
expect(element).toHaveTextContent('Pipe');
});
});

it('should close Select when pressing ESC', async () => {
const wrapper = render(<DSLSelectorWithContext />);
const toggle = await wrapper.findByTestId('dsl-list-dropdown');

/** Open Select */
act(() => {
fireEvent.click(toggle);
});

const menu = await wrapper.findByRole('listbox');

expect(menu).toBeInTheDocument();

/** Press Escape key to close the menu */
act(() => {
fireEvent.focus(menu);
fireEvent.keyDown(menu, { key: 'Escape', code: 'Escape', charCode: 27 });
});

waitFor(() => {
/** The close panel is an async process */
expect(menu).not.toBeInTheDocument();
});

waitFor(() => {
const element = wrapper.queryByRole('option', { selected: true });
expect(element).toBeInTheDocument();
expect(element).toHaveTextContent('Camel Route');
});
});
});
Loading
Loading