Merge pull request 'Added some tests + fix somebugs + allow default config to App when fetch is not available' (#17) from dev.tests into dev
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: https://git.siklos-chaneru.duckdns.org/Siklos/svg-layout-designer-react/pulls/17
This commit is contained in:
commit
dae2f20e76
7 changed files with 439 additions and 13 deletions
|
@ -1,5 +1,9 @@
|
|||
# SVG Layout Designer React
|
||||
|
||||
[](https://drone.siklos-chaneru.duckdns.org/Siklos/svg-layout-designer-react)
|
||||
|
||||
[](https://drone.siklos-chaneru.duckdns.org/Siklos/svg-layout-designer-react)
|
||||
|
||||
An svg layout designer.
|
||||
|
||||
# Getting Started
|
||||
|
|
60
src/App.tsx
60
src/App.tsx
|
@ -100,8 +100,39 @@ export class App extends React.Component<IAppProps> {
|
|||
historyCurrentStep: 0,
|
||||
isLoaded: true
|
||||
});
|
||||
}, (error) => { throw new Error(error); }
|
||||
);
|
||||
}, (error) => {
|
||||
// TODO: Implement an alert component
|
||||
console.warn('[NewEditor] Could not fetch resource from API. Returning default.', error);
|
||||
const MainContainer = new ContainerModel(
|
||||
null,
|
||||
{
|
||||
id: 'main',
|
||||
parentId: 'null',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: DEFAULT_CONFIG.MainContainer.Width,
|
||||
height: DEFAULT_CONFIG.MainContainer.Height,
|
||||
fillOpacity: DEFAULT_CONFIG.MainContainer.Style.fillOpacity,
|
||||
stroke: DEFAULT_CONFIG.MainContainer.Style.stroke,
|
||||
}
|
||||
);
|
||||
|
||||
// Save the configuration and the new MainContainer
|
||||
// and default the selected container to it
|
||||
this.setState({
|
||||
configuration: DEFAULT_CONFIG,
|
||||
history:
|
||||
[
|
||||
{
|
||||
MainContainer,
|
||||
SelectedContainer: MainContainer,
|
||||
TypeCounters: {}
|
||||
}
|
||||
],
|
||||
historyCurrentStep: 0,
|
||||
isLoaded: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public LoadEditor(files: FileList | null): void {
|
||||
|
@ -182,3 +213,28 @@ export async function fetchConfiguration(): Promise<Configuration> {
|
|||
xhr.send();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const DEFAULT_CONFIG: Configuration = {
|
||||
AvailableContainers: [
|
||||
{
|
||||
Type: 'Container',
|
||||
Width: 75,
|
||||
Height: 100,
|
||||
Style: {
|
||||
fillOpacity: 0,
|
||||
stroke: 'green'
|
||||
}
|
||||
}
|
||||
],
|
||||
AvailableSymbols: [],
|
||||
MainContainer: {
|
||||
Type: 'Container',
|
||||
Width: 2000,
|
||||
Height: 100,
|
||||
Style: {
|
||||
fillOpacity: 0,
|
||||
stroke: 'black'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
242
src/Components/ElementsSidebar/ElementsSidebar.test.tsx
Normal file
242
src/Components/ElementsSidebar/ElementsSidebar.test.tsx
Normal file
|
@ -0,0 +1,242 @@
|
|||
import { describe, test, expect, vi } from 'vitest';
|
||||
import * as React from 'react';
|
||||
import { fireEvent, render, screen } from '../../utils/test-utils';
|
||||
import { ElementsSidebar } from './ElementsSidebar';
|
||||
import { IContainerModel } from '../../Interfaces/ContainerModel';
|
||||
|
||||
describe.concurrent('Elements sidebar', () => {
|
||||
it('No elements', () => {
|
||||
render(<ElementsSidebar
|
||||
MainContainer={null}
|
||||
isOpen={true}
|
||||
isHistoryOpen={false}
|
||||
SelectedContainer={null}
|
||||
onClick={() => {}}
|
||||
onPropertyChange={() => {}}
|
||||
selectContainer={() => {}}
|
||||
/>);
|
||||
|
||||
expect(screen.getByText(/Elements/i));
|
||||
expect(screen.queryByText('id')).toBeNull();
|
||||
expect(screen.queryByText(/main/i)).toBeNull();
|
||||
});
|
||||
|
||||
it('With a MainContainer', () => {
|
||||
render(<ElementsSidebar
|
||||
MainContainer={{
|
||||
children: [],
|
||||
parent: null,
|
||||
properties: {
|
||||
id: 'main',
|
||||
parentId: null,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 2000,
|
||||
height: 100
|
||||
},
|
||||
userData: {}
|
||||
}}
|
||||
isOpen={true}
|
||||
isHistoryOpen={false}
|
||||
SelectedContainer={null}
|
||||
onClick={() => {}}
|
||||
onPropertyChange={() => {}}
|
||||
selectContainer={() => {}}
|
||||
/>);
|
||||
|
||||
expect(screen.getByText(/Elements/i));
|
||||
expect(screen.queryByText('id')).toBeNull();
|
||||
expect(screen.getByText(/main/i));
|
||||
});
|
||||
|
||||
it('With a selected MainContainer', () => {
|
||||
const MainContainer = {
|
||||
children: [],
|
||||
parent: null,
|
||||
properties: {
|
||||
id: 'main',
|
||||
parentId: '',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 2000,
|
||||
height: 100
|
||||
},
|
||||
userData: {}
|
||||
};
|
||||
|
||||
const { container } = render(<ElementsSidebar
|
||||
MainContainer={MainContainer}
|
||||
isOpen={true}
|
||||
isHistoryOpen={false}
|
||||
SelectedContainer={MainContainer}
|
||||
onClick={() => {}}
|
||||
onPropertyChange={() => {}}
|
||||
selectContainer={() => {}}
|
||||
/>);
|
||||
|
||||
expect(screen.getByText(/Elements/i));
|
||||
expect(screen.getByText(/main/i));
|
||||
expect(screen.queryByText('id')).toBeDefined();
|
||||
expect(screen.queryByText('parentId')).toBeDefined();
|
||||
expect(screen.queryByText('x')).toBeDefined();
|
||||
expect(screen.queryByText('y')).toBeDefined();
|
||||
expect(screen.queryByText('width')).toBeDefined();
|
||||
expect(screen.queryByText('height')).toBeDefined();
|
||||
const propertyId = container.querySelector('#property-id');
|
||||
const propertyParentId = container.querySelector('#property-parentId');
|
||||
const propertyX = container.querySelector('#property-x');
|
||||
const propertyY = container.querySelector('#property-y');
|
||||
const propertyWidth = container.querySelector('#property-width');
|
||||
const propertyHeight = container.querySelector('#property-height');
|
||||
expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString());
|
||||
expect(propertyParentId).toBeDefined();
|
||||
expect((propertyParentId as HTMLInputElement).value).toBe('');
|
||||
expect(propertyX).toBeDefined();
|
||||
expect((propertyX as HTMLInputElement).value).toBe(MainContainer.properties.x.toString());
|
||||
expect(propertyY).toBeDefined();
|
||||
expect((propertyY as HTMLInputElement).value).toBe(MainContainer.properties.y.toString());
|
||||
expect(propertyWidth).toBeDefined();
|
||||
expect((propertyWidth as HTMLInputElement).value).toBe(MainContainer.properties.width.toString());
|
||||
expect(propertyHeight).toBeDefined();
|
||||
expect((propertyHeight as HTMLInputElement).value).toBe(MainContainer.properties.height.toString());
|
||||
});
|
||||
|
||||
it('With multiple containers', () => {
|
||||
const children: IContainerModel[] = [];
|
||||
const MainContainer = {
|
||||
children,
|
||||
parent: null,
|
||||
properties: {
|
||||
id: 'main',
|
||||
parentId: '',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 2000,
|
||||
height: 100
|
||||
},
|
||||
userData: {}
|
||||
};
|
||||
|
||||
children.push(
|
||||
{
|
||||
children: [],
|
||||
parent: MainContainer,
|
||||
properties: {
|
||||
id: 'child-1',
|
||||
parentId: 'main',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
},
|
||||
userData: {}
|
||||
}
|
||||
);
|
||||
|
||||
children.push(
|
||||
{
|
||||
children: [],
|
||||
parent: MainContainer,
|
||||
properties: {
|
||||
id: 'child-2',
|
||||
parentId: 'main',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
},
|
||||
userData: {}
|
||||
}
|
||||
);
|
||||
|
||||
render(<ElementsSidebar
|
||||
MainContainer={MainContainer}
|
||||
isOpen={true}
|
||||
isHistoryOpen={false}
|
||||
SelectedContainer={MainContainer}
|
||||
onClick={() => {}}
|
||||
onPropertyChange={() => {}}
|
||||
selectContainer={() => {}}
|
||||
/>);
|
||||
|
||||
expect(screen.getByText(/Elements/i));
|
||||
expect(screen.queryByText('id')).toBeDefined();
|
||||
expect(screen.getByText(/main/i));
|
||||
expect(screen.getByText(/child-1/i));
|
||||
expect(screen.getByText(/child-2/i));
|
||||
});
|
||||
|
||||
it('With multiple containers, change selection', () => {
|
||||
const children: IContainerModel[] = [];
|
||||
const MainContainer: IContainerModel = {
|
||||
children,
|
||||
parent: null,
|
||||
properties: {
|
||||
id: 'main',
|
||||
parentId: '',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 2000,
|
||||
height: 100
|
||||
},
|
||||
userData: {}
|
||||
};
|
||||
|
||||
const child1Model: IContainerModel = {
|
||||
children: [],
|
||||
parent: MainContainer,
|
||||
properties: {
|
||||
id: 'child-1',
|
||||
parentId: 'main',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
},
|
||||
userData: {}
|
||||
};
|
||||
children.push(child1Model);
|
||||
|
||||
let SelectedContainer = MainContainer;
|
||||
const selectContainer = vi.fn((container: IContainerModel) => {
|
||||
SelectedContainer = container;
|
||||
});
|
||||
|
||||
const { container, rerender } = render(<ElementsSidebar
|
||||
MainContainer={MainContainer}
|
||||
isOpen={true}
|
||||
isHistoryOpen={false}
|
||||
SelectedContainer={SelectedContainer}
|
||||
onClick={() => {}}
|
||||
onPropertyChange={() => {}}
|
||||
selectContainer={selectContainer}
|
||||
/>);
|
||||
|
||||
expect(screen.getByText(/Elements/i));
|
||||
expect(screen.queryByText('id')).toBeDefined();
|
||||
expect(screen.getByText(/main/i));
|
||||
const child1 = screen.getByText(/child-1/i);
|
||||
expect(child1);
|
||||
const propertyId = container.querySelector('#property-id');
|
||||
const propertyParentId = container.querySelector('#property-parentId');
|
||||
expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString());
|
||||
expect((propertyParentId as HTMLInputElement).value).toBe('');
|
||||
|
||||
fireEvent.click(child1);
|
||||
|
||||
rerender(<ElementsSidebar
|
||||
MainContainer={MainContainer}
|
||||
isOpen={true}
|
||||
isHistoryOpen={false}
|
||||
SelectedContainer={SelectedContainer}
|
||||
onClick={() => {}}
|
||||
onPropertyChange={() => {}}
|
||||
selectContainer={selectContainer}
|
||||
/>);
|
||||
|
||||
expect((propertyId as HTMLInputElement).value === 'main').toBeFalsy();
|
||||
expect((propertyParentId as HTMLInputElement).value === '').toBeFalsy();
|
||||
expect((propertyId as HTMLInputElement).value).toBe(child1Model.properties.id.toString());
|
||||
expect((propertyParentId as HTMLInputElement).value).toBe(child1Model.properties.parentId?.toString());
|
||||
});
|
||||
});
|
82
src/Components/Properties/Properties.test.tsx
Normal file
82
src/Components/Properties/Properties.test.tsx
Normal file
|
@ -0,0 +1,82 @@
|
|||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import * as React from 'react';
|
||||
import { describe, it, vi } from 'vitest';
|
||||
import { Properties } from './Properties';
|
||||
|
||||
describe.concurrent('Properties', () => {
|
||||
it('No properties', () => {
|
||||
render(<Properties
|
||||
properties={undefined}
|
||||
onChange={() => {}}
|
||||
/>);
|
||||
|
||||
expect(screen.queryByText('id')).toBeNull();
|
||||
expect(screen.queryByText('parentId')).toBeNull();
|
||||
expect(screen.queryByText('x')).toBeNull();
|
||||
expect(screen.queryByText('y')).toBeNull();
|
||||
});
|
||||
|
||||
it('Some properties', () => {
|
||||
const prop = {
|
||||
id: 'stuff',
|
||||
parentId: 'parentId',
|
||||
x: 1,
|
||||
y: 1
|
||||
};
|
||||
|
||||
const handleChange = vi.fn((key, value) => {
|
||||
(prop as any)[key] = value;
|
||||
});
|
||||
|
||||
const { container, rerender } = render(<Properties
|
||||
properties={prop}
|
||||
onChange={handleChange}
|
||||
/>);
|
||||
|
||||
expect(screen.queryByText('id')).toBeDefined();
|
||||
expect(screen.queryByText('parentId')).toBeDefined();
|
||||
expect(screen.queryByText('x')).toBeDefined();
|
||||
expect(screen.queryByText('y')).toBeDefined();
|
||||
|
||||
let propertyId = container.querySelector('#property-id');
|
||||
let propertyParentId = container.querySelector('#property-parentId');
|
||||
let propertyX = container.querySelector('#property-x');
|
||||
let propertyY = container.querySelector('#property-y');
|
||||
expect(propertyId).toBeDefined();
|
||||
expect((propertyId as HTMLInputElement).value).toBe('stuff');
|
||||
expect(propertyParentId).toBeDefined();
|
||||
expect((propertyParentId as HTMLInputElement).value).toBe('parentId');
|
||||
expect(propertyX).toBeDefined();
|
||||
expect((propertyX as HTMLInputElement).value).toBe('1');
|
||||
expect(propertyY).toBeDefined();
|
||||
expect((propertyY as HTMLInputElement).value).toBe('1');
|
||||
|
||||
fireEvent.change(propertyId as Element, { target: { value: 'stuffed' } });
|
||||
fireEvent.change(propertyParentId as Element, { target: { value: 'parentedId' } });
|
||||
fireEvent.change(propertyX as Element, { target: { value: '2' } });
|
||||
fireEvent.change(propertyY as Element, { target: { value: '2' } });
|
||||
expect(handleChange).toBeCalledTimes(4);
|
||||
|
||||
expect(prop.id).toBe('stuffed');
|
||||
expect(prop.parentId).toBe('parentedId');
|
||||
expect(prop.x).toBe('2');
|
||||
expect(prop.y).toBe('2');
|
||||
rerender(<Properties
|
||||
properties={Object.assign({}, prop)}
|
||||
onChange={handleChange}
|
||||
/>);
|
||||
|
||||
propertyId = container.querySelector('#property-id');
|
||||
propertyParentId = container.querySelector('#property-parentId');
|
||||
propertyX = container.querySelector('#property-x');
|
||||
propertyY = container.querySelector('#property-y');
|
||||
expect(propertyId).toBeDefined();
|
||||
expect((propertyId as HTMLInputElement).value).toBe('stuffed');
|
||||
expect(propertyParentId).toBeDefined();
|
||||
expect((propertyParentId as HTMLInputElement).value).toBe('parentedId');
|
||||
expect(propertyX).toBeDefined();
|
||||
expect((propertyX as HTMLInputElement).value).toBe('2');
|
||||
expect(propertyY).toBeDefined();
|
||||
expect((propertyY as HTMLInputElement).value).toBe('2');
|
||||
});
|
||||
});
|
|
@ -1,19 +1,61 @@
|
|||
import * as React from 'react';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { render, screen } from '../../utils/test-utils';
|
||||
import { describe, test, expect, vi } from 'vitest';
|
||||
import { findByText, fireEvent, render, screen } from '../../utils/test-utils';
|
||||
import Sidebar from './Sidebar';
|
||||
|
||||
describe('Sidebar test', () => {
|
||||
test('Start empty', () => {
|
||||
describe.concurrent('Sidebar', () => {
|
||||
it('Start default', () => {
|
||||
const handleClick = vi.fn();
|
||||
render(
|
||||
<Sidebar
|
||||
componentOptions={[]}
|
||||
isOpen={true}
|
||||
onClick={() => {}}
|
||||
onClick={handleClick}
|
||||
buttonOnClick={() => {}}
|
||||
/>
|
||||
);
|
||||
const stuff = screen.queryByText(/stuff/i);
|
||||
const close = screen.getByText(/close/i);
|
||||
|
||||
expect(screen.getByText(/Components/i)).toBeDefined();
|
||||
expect(screen.getByText(/Components/i).classList.contains('left-0')).toBeDefined();
|
||||
expect(stuff).toBeNull();
|
||||
fireEvent.click(close);
|
||||
expect(handleClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('Start close', () => {
|
||||
render(<Sidebar
|
||||
componentOptions={[]}
|
||||
isOpen={false}
|
||||
onClick={() => {}}
|
||||
buttonOnClick={() => {}}
|
||||
/>);
|
||||
|
||||
const stuff = screen.queryByText(/stuff/i);
|
||||
expect(screen.getByText(/Components/i).classList.contains('-left-64')).toBeDefined();
|
||||
expect(stuff).toBeNull();
|
||||
});
|
||||
|
||||
it('With stuff', () => {
|
||||
const Type = 'stuff';
|
||||
const handleButtonClick = vi.fn();
|
||||
render(<Sidebar
|
||||
componentOptions={[
|
||||
{
|
||||
Type,
|
||||
Width: 30,
|
||||
Height: 30,
|
||||
Style: {}
|
||||
}
|
||||
]}
|
||||
isOpen={true}
|
||||
onClick={() => {}}
|
||||
buttonOnClick={handleButtonClick}
|
||||
/>);
|
||||
const stuff = screen.getByText(/stuff/i);
|
||||
|
||||
expect(stuff).toBeDefined();
|
||||
fireEvent.click(stuff);
|
||||
expect(handleButtonClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -117,7 +117,7 @@ export class UI extends React.PureComponent<IUIProps, IUIState> {
|
|||
☰ History
|
||||
</button>
|
||||
|
||||
<FloatingButton className={`fixed flex flex-col gap-2 items-center bottom-40 ${buttonRightOffsetClasses}`}>
|
||||
<FloatingButton className={`fixed z-10 flex flex-col gap-2 items-center bottom-40 ${buttonRightOffsetClasses}`}>
|
||||
<button
|
||||
className={'transition-all w-10 h-10 p-2 align-middle items-center justify-center rounded-full bg-blue-500 hover:bg-blue-800'}
|
||||
title='Export as JSON'
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { describe, test, expect } from 'vitest';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { fetchConfiguration } from '../App';
|
||||
|
||||
describe('API test', () => {
|
||||
test('Load environment', () => {
|
||||
describe.concurrent('API test', () => {
|
||||
it('Load environment', () => {
|
||||
const url = import.meta.env.VITE_API_URL;
|
||||
expect(url).toBe('http://localhost:5000');
|
||||
});
|
||||
|
||||
test('Fetch configuration', async() => {
|
||||
it('Fetch configuration', async() => {
|
||||
const configuration = await fetchConfiguration();
|
||||
expect(configuration.MainContainer).toBeDefined();
|
||||
expect(configuration.MainContainer.Height).toBeGreaterThan(0);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue