diff --git a/packages/rmf-dashboard-framework/src/components/tasks/types/custom-compose.test.tsx b/packages/rmf-dashboard-framework/src/components/tasks/types/custom-compose.test.tsx index 718a93d6f..e14c7e18f 100644 --- a/packages/rmf-dashboard-framework/src/components/tasks/types/custom-compose.test.tsx +++ b/packages/rmf-dashboard-framework/src/components/tasks/types/custom-compose.test.tsx @@ -1,7 +1,13 @@ import { fireEvent, render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import { CustomComposeTaskForm } from './custom-compose'; +import { + CustomComposeTaskDefinition, + CustomComposeTaskForm, + isCustomTaskDescriptionValid, + makeCustomComposeTaskBookingLabel, + makeCustomComposeTaskShortDescription, +} from './custom-compose'; describe('Custom compose task form', () => { it('Renders custom compose task form', async () => { @@ -28,4 +34,18 @@ describe('Custom compose task form', () => { fireEvent.change(textArea, { target: { value: validTaskDesc } }); expect(onValidate).toHaveBeenCalledWith(true); }); + + it('Validate description', () => { + expect(isCustomTaskDescriptionValid('invalid json')).not.toBeTruthy(); + expect(isCustomTaskDescriptionValid('{"valid": "json"}')).toBeTruthy(); + }); + + it('Booking label', () => { + const bookingLabel = makeCustomComposeTaskBookingLabel(); + expect(bookingLabel.task_definition_id).toBe(CustomComposeTaskDefinition.taskDefinitionId); + }); + + it('Short description', () => { + expect(makeCustomComposeTaskShortDescription('{"valid": "json"}')).toBe('{"valid": "json"}'); + }); }); diff --git a/packages/rmf-dashboard-framework/src/components/tasks/types/custom-compose.tsx b/packages/rmf-dashboard-framework/src/components/tasks/types/custom-compose.tsx index bd256ddf7..00a1b58a2 100644 --- a/packages/rmf-dashboard-framework/src/components/tasks/types/custom-compose.tsx +++ b/packages/rmf-dashboard-framework/src/components/tasks/types/custom-compose.tsx @@ -23,7 +23,7 @@ export function makeCustomComposeTaskShortDescription(desc: CustomComposeTaskDes return desc; } -const isCustomTaskDescriptionValid = (taskDescription: string): boolean => { +export const isCustomTaskDescriptionValid = (taskDescription: string): boolean => { if (taskDescription.length === 0) { return false; } diff --git a/packages/rmf-dashboard-framework/src/components/tasks/types/patrol.stories.tsx b/packages/rmf-dashboard-framework/src/components/tasks/types/patrol.stories.tsx new file mode 100644 index 000000000..01bd0b397 --- /dev/null +++ b/packages/rmf-dashboard-framework/src/components/tasks/types/patrol.stories.tsx @@ -0,0 +1,19 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { makeDefaultPatrolTaskDescription, PatrolTaskForm } from './patrol'; + +export default { + title: 'Tasks/PatrolTaskForm', + component: PatrolTaskForm, +} satisfies Meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + taskDesc: makeDefaultPatrolTaskDescription(), + patrolWaypoints: ['waypoint_1', 'waypoint_2', 'waypoint_3'], + onChange: () => {}, + onValidate: () => {}, + }, +}; diff --git a/packages/rmf-dashboard-framework/src/components/tasks/types/patrol.test.tsx b/packages/rmf-dashboard-framework/src/components/tasks/types/patrol.test.tsx new file mode 100644 index 000000000..1f670bb3d --- /dev/null +++ b/packages/rmf-dashboard-framework/src/components/tasks/types/patrol.test.tsx @@ -0,0 +1,89 @@ +import { fireEvent, render, screen, within } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import { + addPlaceToPatrolTaskDescription, + isPatrolTaskDescriptionValid, + makeDefaultPatrolTaskDescription, + makePatrolTaskBookingLabel, + makePatrolTaskShortDescription, + PatrolTaskDefinition, + PatrolTaskForm, +} from './patrol'; + +const mockWaypoints = ['waypoint_1', 'waypoint_2', 'waypoint_3']; + +describe('Patrol task form', () => { + it('PatrolTaskForm renders, changes and validates', async () => { + const onChange = vi.fn(); + const onValidate = vi.fn(); + + render( + , + ); + + const autocomplete = screen.getByTestId('place-name'); + const input = within(autocomplete).getByLabelText(/place name/i); + autocomplete.focus(); + fireEvent.change(input, { target: { value: 'a' } }); + fireEvent.keyDown(autocomplete, { key: 'ArrowDown' }); + fireEvent.keyDown(autocomplete, { key: 'Enter' }); + + expect(onChange).toHaveBeenCalled(); + expect(onValidate).toHaveBeenCalled(); + }); + + it('PatrolTaskForm renders and has places', async () => { + const onChange = vi.fn(); + const onValidate = vi.fn(); + + const desc = makeDefaultPatrolTaskDescription(); + const updatedDesc = addPlaceToPatrolTaskDescription(desc, 'waypoint_1'); + + const root = render( + , + ); + + expect(root.getByText(/waypoint_1/i)); + }); + + it('booking label', () => { + let desc = makeDefaultPatrolTaskDescription(); + desc = addPlaceToPatrolTaskDescription(desc, 'waypoint_1'); + let label = makePatrolTaskBookingLabel(desc); + expect(label.task_definition_id).toBe(PatrolTaskDefinition.taskDefinitionId); + expect(label.destination).toBe('waypoint_1'); + + desc = addPlaceToPatrolTaskDescription(desc, 'waypoint_2'); + label = makePatrolTaskBookingLabel(desc); + expect(label.task_definition_id).toBe(PatrolTaskDefinition.taskDefinitionId); + expect(label.destination).toBe('waypoint_2'); + }); + + it('validity', () => { + let desc = makeDefaultPatrolTaskDescription(); + expect(isPatrolTaskDescriptionValid(desc)).not.toBeTruthy(); + + desc = addPlaceToPatrolTaskDescription(desc, 'waypoint_1'); + expect(isPatrolTaskDescriptionValid(desc)).toBeTruthy(); + }); + + it('short description', () => { + let desc = makeDefaultPatrolTaskDescription(); + desc = addPlaceToPatrolTaskDescription(desc, 'waypoint_1'); + desc = addPlaceToPatrolTaskDescription(desc, 'waypoint_2'); + expect(makePatrolTaskShortDescription(desc, undefined)).toBe( + '[Patrol] [1] round/s, along [waypoint_1], [waypoint_2]', + ); + }); +}); diff --git a/packages/rmf-dashboard-framework/src/components/tasks/types/patrol.tsx b/packages/rmf-dashboard-framework/src/components/tasks/types/patrol.tsx index e69f70c2e..eb3cd20d8 100644 --- a/packages/rmf-dashboard-framework/src/components/tasks/types/patrol.tsx +++ b/packages/rmf-dashboard-framework/src/components/tasks/types/patrol.tsx @@ -76,6 +76,7 @@ interface PlaceListProps { function PlaceList({ places, onClick }: PlaceListProps) { const theme = useTheme(); + console.log(places); return ( ( onClick(index)}> @@ -104,6 +106,17 @@ function PlaceList({ places, onClick }: PlaceListProps) { ); } +export function addPlaceToPatrolTaskDescription( + taskDesc: PatrolTaskDescription, + place: string, +): PatrolTaskDescription { + const updatedTaskDesc = { + ...taskDesc, + places: taskDesc.places.concat(place).filter((el: string) => el), + }; + return updatedTaskDesc; +} + interface PatrolTaskFormProps { taskDesc: PatrolTaskDescription; patrolWaypoints: string[]; @@ -132,15 +145,12 @@ export function PatrolTaskForm({ - newValue !== null && - onInputChange({ - ...taskDesc, - places: taskDesc.places.concat(newValue).filter((el: string) => el), - }) + newValue !== null && onInputChange(addPlaceToPatrolTaskDescription(taskDesc, newValue)) } sx={{ '& .MuiOutlinedInput-root': {