Skip to content
This repository has been archived by the owner on Nov 27, 2023. It is now read-only.

Commit

Permalink
fix(viz): Use metadata.name as flow identifier
Browse files Browse the repository at this point in the history
Currently, the UI creates the flows IDs in two places:
1. Upon syncing the code through the useFlowsStore.setFlowsWrapper() method
2. Upon creating a new flow when using the New Flow button

For the .1, since the UI doesn't have a previous ID
because we're starting from the source code, the UI
creates an ID using the DSL name and the position index,
i.e. "Camel Route-1".

For the .2, the UI uses a service to generate an ID
with the word "route-####" while "####" it's a 4 digits
random number.

In this commit, the UI leverages the IFlowsWrapper.flows[0].metadata.name
property as an ID for each flow, this way, whenever a sync happens,
the IDs will be the same.

fixes: KaotoIO/vscode-kaoto#280
fixes: #1910
  • Loading branch information
lordrip committed Jun 27, 2023
1 parent e01f4ef commit 61f6e54
Show file tree
Hide file tree
Showing 24 changed files with 805 additions and 584 deletions.
3 changes: 1 addition & 2 deletions cypress/e2e/11-multi_flow/code_editor_multi_flow.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ spec:
cy.get('[data-testid="viz-step-set-header"]').should('be.visible');
});

// Blocked by - https://github.com/KaotoIO/kaoto-ui/issues/1910
it.skip('User adds step to the second route using code editor', () => {
it('User adds step to the second route using code editor', () => {
cy.uploadFixture('IntegrationMultiFlow.yaml');

cy.showAllRoutes();
Expand Down
4 changes: 2 additions & 2 deletions cypress/fixtures/IntegrationMultiFlow.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: camel.apache.org/v1
kind: Integration
metadata:
name: ''
name: 'Integration-1'
spec:
flows:
- from:
Expand All @@ -17,7 +17,7 @@ spec:
apiVersion: camel.apache.org/v1
kind: Integration
metadata:
name: ''
name: 'Integration-2'
spec:
flows:
- from:
Expand Down
5 changes: 2 additions & 3 deletions cypress/support/kaoto-ui-commands/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ Cypress.Commands.add('openCodeEditor', () => {
});

Cypress.Commands.add('editorAddText', (line, text) => {
const arr = text.split('\n');
Array.from({ length: arr.length }).forEach((_, i) => {
text.split('\n').forEach((lineToWrite, i) => {
cy.get('.code-editor')
.click()
.type('{pageUp}{pageUp}' + '{downArrow}'.repeat(line + i) + '{enter}{upArrow}' + arr[i], {
.type('{pageUp}{pageUp}' + '{downArrow}'.repeat(line + i) + '{enter}{upArrow}' + lineToWrite, {
delay: 1,
});
});
Expand Down
10 changes: 9 additions & 1 deletion setupTests.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { stepsCatalog } from './src/stubs';
import { capabilitiesStub, stepsCatalog } from './src/stubs';
import '@testing-library/jest-dom';
import { TextEncoder } from 'util';
import 'whatwg-fetch';
Expand All @@ -12,5 +12,13 @@ jest.mock('@kaoto/api', () => {
return {
...actual,
fetchCatalogSteps: jest.fn().mockResolvedValue(stepsCatalog),
fetchDefaultNamespace: jest.fn().mockResolvedValue({ namespace: 'default' }),
fetchDeployments: jest.fn().mockResolvedValue([]),
startDeployment: jest.fn().mockResolvedValue(''),
stopDeployment: jest.fn().mockResolvedValue(''),

fetchCapabilities: jest.fn().mockResolvedValue(capabilitiesStub),
fetchIntegrationSourceCode: jest.fn().mockResolvedValue(''),
fetchViews: jest.fn().mockResolvedValue([]),
};
});
8 changes: 6 additions & 2 deletions src/components/Flows/FlowsList.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { FlowsService } from '../../services/FlowsService';
import { FlowsList } from './FlowsList';
import { useFlowsStore, useVisualizationStore } from '@kaoto/store';
import { act, fireEvent, render } from '@testing-library/react';

describe('FlowsList.tsx', () => {
beforeEach(() => {
useFlowsStore.getState().deleteAllFlows();
useFlowsStore.getState().addNewFlow('Integration', 'route-1234');
useFlowsStore.getState().addNewFlow('Integration', 'route-4321');

jest.spyOn(FlowsService, 'getNewFlowId').mockReturnValueOnce('route-1234');
jest.spyOn(FlowsService, 'getNewFlowId').mockReturnValueOnce('route-4321');
useFlowsStore.getState().addNewFlow('Integration');
useFlowsStore.getState().addNewFlow('Integration');
});

test('should render the existing flows', async () => {
Expand Down
8 changes: 6 additions & 2 deletions src/components/Flows/FlowsMenu.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { FlowsService } from '../../services';
import { FlowsMenu } from './FlowsMenu';
import { useFlowsStore, useVisualizationStore } from '@kaoto/store';
import { act, fireEvent, render, waitFor } from '@testing-library/react';

describe('FlowsMenu.tsx', () => {
beforeEach(() => {
useFlowsStore.getState().deleteAllFlows();
useFlowsStore.getState().addNewFlow('Integration', 'route-1234');
useFlowsStore.getState().addNewFlow('Integration', 'route-4321');

jest.spyOn(FlowsService, 'getNewFlowId').mockReturnValueOnce('route-1234');
jest.spyOn(FlowsService, 'getNewFlowId').mockReturnValueOnce('route-4321');
useFlowsStore.getState().addNewFlow('Integration');
useFlowsStore.getState().addNewFlow('Integration');
});

test('should open the flows list when clicking the dropdown', async () => {
Expand Down
92 changes: 60 additions & 32 deletions src/components/KaotoToolbar.test.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,76 @@
import { KaotoToolbar } from './KaotoToolbar';
import { fetchDefaultNamespace } from '@kaoto/api';
import { AlertProvider } from '@kaoto/layout';
import { screen, render, fireEvent, act } from '@testing-library/react';
import { act, fireEvent, render } from '@testing-library/react';
import { useSettingsStore } from '@kaoto/store';

describe('KaotoToolbar.tsx', () => {
test('component renders correctly', () => {
render(
<AlertProvider>
<KaotoToolbar
leftDrawerExpanded
toggleCatalog={jest.fn()}
toggleCodeEditor={jest.fn()}
hideLeftPanel={jest.fn()}
/>
</AlertProvider>
test('component renders correctly', async () => {
const wrapper = await act(async () =>
render(
<AlertProvider>
<KaotoToolbar
leftDrawerExpanded
toggleCatalog={jest.fn()}
toggleCodeEditor={jest.fn()}
hideLeftPanel={jest.fn()}
/>
</AlertProvider>,
),
);
const element = screen.getByTestId('viz-toolbar');

const element = wrapper.getByTestId('viz-toolbar');
expect(element).toBeInTheDocument();
});

test('logo renders correctly', () => {
render(
<AlertProvider>
<KaotoToolbar
leftDrawerExpanded
toggleCatalog={jest.fn()}
toggleCodeEditor={jest.fn()}
hideLeftPanel={jest.fn()}
/>
</AlertProvider>
test('logo renders correctly', async () => {
const wrapper = await act(async () =>
render(
<AlertProvider>
<KaotoToolbar
leftDrawerExpanded
toggleCatalog={jest.fn()}
toggleCodeEditor={jest.fn()}
hideLeftPanel={jest.fn()}
/>
</AlertProvider>,
),
);
const element = screen.getByTestId('kaoto-logo');

const element = wrapper.getByTestId('kaoto-logo');
expect(element).toBeInTheDocument();
});

test('should fetch the default namespace', async () => {
await act(async () =>
render(
<AlertProvider>
<KaotoToolbar
leftDrawerExpanded
toggleCatalog={jest.fn()}
toggleCodeEditor={jest.fn()}
hideLeftPanel={jest.fn()}
/>
</AlertProvider>,
),
);

expect(fetchDefaultNamespace).toHaveBeenCalled();
expect(useSettingsStore.getState().settings.namespace).toBe('default');
});

test('open about modal', async () => {
const wrapper = render(
<AlertProvider>
<KaotoToolbar
leftDrawerExpanded
toggleCatalog={jest.fn()}
toggleCodeEditor={jest.fn()}
hideLeftPanel={jest.fn()}
/>
</AlertProvider>
const wrapper = await act(async () =>
render(
<AlertProvider>
<KaotoToolbar
leftDrawerExpanded
toggleCatalog={jest.fn()}
toggleCodeEditor={jest.fn()}
hideLeftPanel={jest.fn()}
/>
</AlertProvider>,
),
);

const kebabDropdownMenu = wrapper.getByTestId('toolbar-kebab-dropdown-toggle');
Expand Down
4 changes: 2 additions & 2 deletions src/components/KaotoToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const KaotoToolbar = ({
useEffect(() => {
fetchDefaultNamespace().then((data) => {
const namespace = data.namespace;
setSettings({ namespace});
setSettings({ namespace });
});
}, []);

Expand Down Expand Up @@ -394,7 +394,7 @@ export const KaotoToolbar = ({
setSourceCode('');

/** TODO: Check whether this configuration is required to be kept inside of settingsStore */
setSettings({ name: 'integration', namespace: 'default' });
setSettings({ namespace: 'default' });
setIsConfirmationModalOpen(false);
}}
isModalOpen={isConfirmationModalOpen}
Expand Down
52 changes: 31 additions & 21 deletions src/components/SettingsModal.test.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,49 @@
import { AlertProvider } from '../layout';
import { SettingsModal } from './SettingsModal';
import { fireEvent, screen } from '@testing-library/dom';
import { render } from '@testing-library/react';
import { fireEvent } from '@testing-library/dom';
import { act, render } from '@testing-library/react';

describe('SettingsModal.tsx', () => {
test('component renders if open', () => {
render(
<AlertProvider>
<SettingsModal handleCloseModal={jest.fn()} isModalOpen={true} />
</AlertProvider>
test('component renders if open', async () => {
const wrapper = await act(async () =>
render(
<AlertProvider>
<SettingsModal handleCloseModal={jest.fn()} isModalOpen={true} />
</AlertProvider>,
),
);
const element = screen.queryByTestId('settings-modal');
const element = wrapper.queryByTestId('settings-modal');
expect(element).toBeInTheDocument();
});

test('component does not render if closed', () => {
render(
<AlertProvider>
<SettingsModal handleCloseModal={jest.fn()} isModalOpen={false} />
</AlertProvider>
test('component does not render if closed', async () => {
const wrapper = await act(async () =>
render(
<AlertProvider>
<SettingsModal handleCloseModal={jest.fn()} isModalOpen={false} />
</AlertProvider>,
),
);
const element = screen.queryByTestId('settings-modal');
const element = wrapper.queryByTestId('settings-modal');
expect(element).not.toBeInTheDocument();
});

test('handleCloseModal is called', () => {
test('handleCloseModal is called', async () => {
const mockClose = jest.fn();

render(
<AlertProvider>
<SettingsModal handleCloseModal={mockClose} isModalOpen={true} />
</AlertProvider>
const wrapper = await act(async () =>
render(
<AlertProvider>
<SettingsModal handleCloseModal={mockClose} isModalOpen={true} />
</AlertProvider>,
),
);
const saveButton = screen.getByTestId('settings-modal--save');
fireEvent.click(saveButton);

await act(async () => {
const saveButton = wrapper.getByTestId('settings-modal--save');
fireEvent.click(saveButton);
});

expect(mockClose).toHaveBeenCalled();
});
});
20 changes: 12 additions & 8 deletions src/components/SourceCodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ export const SourceCodeEditor = (props: ISourceCodeEditor) => {
const editorRef = useRef<Parameters<EditorDidMount>[0] | null>(null);
const { sourceCode, setSourceCode } = useIntegrationSourceStore();
const syncedSourceCode = useIntegrationSourceStore((state) => state.syncedSourceCode, shallow);
const { schemaUri, settings, setSettings } = useSettingsStore(
const { schemaUri, currentDsl, capabilities, editorIsLightMode, setSettings } = useSettingsStore(
(state) => ({
schemaUri: state.settings.dsl.validationSchema
? RequestService.getApiURL() + state.settings.dsl.validationSchema
: '',
settings: state.settings,
currentDsl: state.settings.dsl.name,
capabilities: state.settings.capabilities,
editorIsLightMode: state.settings.editorIsLightMode,
setSettings: state.setSettings,
}),
shallow,
Expand Down Expand Up @@ -60,13 +62,15 @@ export const SourceCodeEditor = (props: ISourceCodeEditor) => {
*/
const handleChanges = (incomingData: string) => {
// update integration JSON state with changes
fetchIntegrationJson(incomingData, settings.dsl.name)
fetchIntegrationJson(incomingData, currentDsl)
.then((flowsWrapper) => {
let tmpInt = flowsWrapper.flows[0];
if (typeof tmpInt.metadata?.name === 'string' && tmpInt.metadata.name !== '') {
settings.name = tmpInt.metadata.name;
setSettings({ name: tmpInt.metadata.name });
const newDsl = flowsWrapper.flows?.[0].dsl ?? '';

if (newDsl !== currentDsl) {
const dsl = capabilities.find((dsl) => dsl.name === newDsl);
setSettings({ dsl });
}

setFlowsWrapper(flowsWrapper);
})
.catch((e) => {
Expand Down Expand Up @@ -182,7 +186,7 @@ export const SourceCodeEditor = (props: ISourceCodeEditor) => {
toolTipPosition="top"
customControls={[UndoButton, RedoButton, ClearButton, UpdateButton]}
isCopyEnabled
isDarkTheme={!settings.editorIsLightMode}
isDarkTheme={!editorIsLightMode}
isDownloadEnabled
isLanguageLabelVisible
allowFullScreen={true}
Expand Down
17 changes: 0 additions & 17 deletions src/components/Visualization.test.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,11 @@
import { AlertProvider } from '../layout';
import { capabilitiesStub } from '../stubs';
import { integrationJSONStub, stepsStub } from '../stubs/steps';
import { Visualization } from './Visualization';
import { fetchCapabilities, fetchIntegrationSourceCode, fetchViews } from '@kaoto/api';
import { RFState, useFlowsStore, useVisualizationStore } from '@kaoto/store';
import { IFlowsWrapper } from '@kaoto/types';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { act } from 'react-dom/test-utils';

jest.mock('@kaoto/api', () => {
const actual = jest.requireActual('@kaoto/api');

return {
...actual,
fetchViews: jest.fn(),
fetchCapabilities: jest.fn(),
fetchIntegrationSourceCode: jest.fn(),
};
});

beforeAll(() => {
// Setup ResizeObserver and offset* properties
// see: https://github.com/wbkd/react-flow/issues/716
Expand All @@ -45,10 +32,6 @@ beforeAll(() => {
});

(window.SVGElement as any).prototype.getBBox = () => ({ x: 0, y: 0, width: 0, height: 0 });

jest.mocked(fetchCapabilities).mockResolvedValue(capabilitiesStub);
jest.mocked(fetchIntegrationSourceCode).mockResolvedValue('');
jest.mocked(fetchViews).mockResolvedValue([]);
});

beforeEach(() => {
Expand Down
Loading

0 comments on commit 61f6e54

Please sign in to comment.