Added some tests + fix somebugs + allow default config to App when fetch is not available #17
7 changed files with 439 additions and 13 deletions
|
@ -1,5 +1,9 @@
|
||||||
# SVG Layout Designer React
|
# 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.
|
An svg layout designer.
|
||||||
|
|
||||||
# Getting Started
|
# Getting Started
|
||||||
|
|
60
src/App.tsx
60
src/App.tsx
|
@ -100,8 +100,39 @@ export class App extends React.Component<IAppProps> {
|
||||||
historyCurrentStep: 0,
|
historyCurrentStep: 0,
|
||||||
isLoaded: true
|
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 {
|
public LoadEditor(files: FileList | null): void {
|
||||||
|
@ -182,3 +213,28 @@ export async function fetchConfiguration(): Promise<Configuration> {
|
||||||
xhr.send();
|
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 * as React from 'react';
|
||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect, vi } from 'vitest';
|
||||||
import { render, screen } from '../../utils/test-utils';
|
import { findByText, fireEvent, render, screen } from '../../utils/test-utils';
|
||||||
import Sidebar from './Sidebar';
|
import Sidebar from './Sidebar';
|
||||||
|
|
||||||
describe('Sidebar test', () => {
|
describe.concurrent('Sidebar', () => {
|
||||||
test('Start empty', () => {
|
it('Start default', () => {
|
||||||
|
const handleClick = vi.fn();
|
||||||
render(
|
render(
|
||||||
<Sidebar
|
<Sidebar
|
||||||
componentOptions={[]}
|
componentOptions={[]}
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
onClick={() => {}}
|
onClick={handleClick}
|
||||||
buttonOnClick={() => {}}
|
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
|
☰ History
|
||||||
</button>
|
</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
|
<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'}
|
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'
|
title='Export as JSON'
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { describe, test, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
import { fetchConfiguration } from '../App';
|
import { fetchConfiguration } from '../App';
|
||||||
|
|
||||||
describe('API test', () => {
|
describe.concurrent('API test', () => {
|
||||||
test('Load environment', () => {
|
it('Load environment', () => {
|
||||||
const url = import.meta.env.VITE_API_URL;
|
const url = import.meta.env.VITE_API_URL;
|
||||||
expect(url).toBe('http://localhost:5000');
|
expect(url).toBe('http://localhost:5000');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Fetch configuration', async() => {
|
it('Fetch configuration', async() => {
|
||||||
const configuration = await fetchConfiguration();
|
const configuration = await fetchConfiguration();
|
||||||
expect(configuration.MainContainer).toBeDefined();
|
expect(configuration.MainContainer).toBeDefined();
|
||||||
expect(configuration.MainContainer.Height).toBeGreaterThan(0);
|
expect(configuration.MainContainer.Height).toBeGreaterThan(0);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue