diff --git a/src/routePlanner/reducer.test.ts b/src/routePlanner/reducer.test.ts new file mode 100644 index 0000000..b5cbc19 --- /dev/null +++ b/src/routePlanner/reducer.test.ts @@ -0,0 +1,258 @@ +import t from 'tap'; +import { Marker, ReducerAction } from './types'; +import { createChangeStateMethods, markersReducer } from './reducer.js'; + +const createReducerWithState = () => { + let markers: Marker[] = []; + + const changeStateMethods = createChangeStateMethods((action) => { + markers = markersReducer(markers, action); + }); + + return { + ...changeStateMethods, + getMarkers: () => markers, + }; +}; + +const coordinates1 = { lat: 1, lng: 101 }; +const coordinates2 = { lat: 2, lng: 102 }; +const coordinates3 = { lat: 3, lng: 103 }; +const coordinates4 = { lat: 4, lng: 104 }; +const coordinates5 = { lat: 5, lng: 105 }; + +const createReducersWithFivePoints = () => { + const result = createReducerWithState(); + result.addMarker(coordinates1); + result.addMarker(coordinates2); + result.addMarker(coordinates3); + result.addMarker(coordinates4); + result.addMarker(coordinates5); + return result; +}; + +const markerPattern = { + key: String, + moveUp: Function, + moveDown: Function, + remove: Function, +}; + +void t.test('reducer', (t) => { + void t.test('throws an error for incorrect action type', (t) => { + // just to get to 100% coverage + t.throws(() => + markersReducer([], { type: 'unknown' } as unknown as ReducerAction), + ); + t.end(); + }); + t.end(); +}); + +void t.test('reducer methods', (t) => { + void t.test('create markers with correct labels', (t) => { + const reducers = createReducersWithFivePoints(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates1 }, + { ...markerPattern, label: '2', coordinates: coordinates2 }, + { ...markerPattern, label: '3', coordinates: coordinates3 }, + { ...markerPattern, label: '4', coordinates: coordinates4 }, + { ...markerPattern, label: '5', coordinates: coordinates5 }, + ]); + t.end(); + }); + + void t.test('remove first marker', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.getMarkers()[0]?.remove(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates2 }, + { ...markerPattern, label: '2', coordinates: coordinates3 }, + { ...markerPattern, label: '3', coordinates: coordinates4 }, + { ...markerPattern, label: '4', coordinates: coordinates5 }, + ]); + t.end(); + }); + + void t.test('remove middle marker', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.getMarkers()[2]?.remove(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates1 }, + { ...markerPattern, label: '2', coordinates: coordinates2 }, + { ...markerPattern, label: '3', coordinates: coordinates4 }, + { ...markerPattern, label: '4', coordinates: coordinates5 }, + ]); + t.end(); + }); + + void t.test('remove last marker', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.getMarkers()[4]?.remove(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates1 }, + { ...markerPattern, label: '2', coordinates: coordinates2 }, + { ...markerPattern, label: '3', coordinates: coordinates3 }, + { ...markerPattern, label: '4', coordinates: coordinates4 }, + ]); + t.end(); + }); + + void t.test('do not move first marker up', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.getMarkers()[0]?.moveUp(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates1 }, + { ...markerPattern, label: '2', coordinates: coordinates2 }, + { ...markerPattern, label: '3', coordinates: coordinates3 }, + { ...markerPattern, label: '4', coordinates: coordinates4 }, + { ...markerPattern, label: '5', coordinates: coordinates5 }, + ]); + t.end(); + }); + + void t.test('move first marker down', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.getMarkers()[0]?.moveDown(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates2 }, + { ...markerPattern, label: '2', coordinates: coordinates1 }, + { ...markerPattern, label: '3', coordinates: coordinates3 }, + { ...markerPattern, label: '4', coordinates: coordinates4 }, + { ...markerPattern, label: '5', coordinates: coordinates5 }, + ]); + t.end(); + }); + + void t.test('move second marker up', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.getMarkers()[1]?.moveUp(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates2 }, + { ...markerPattern, label: '2', coordinates: coordinates1 }, + { ...markerPattern, label: '3', coordinates: coordinates3 }, + { ...markerPattern, label: '4', coordinates: coordinates4 }, + { ...markerPattern, label: '5', coordinates: coordinates5 }, + ]); + t.end(); + }); + + void t.test('move second marker down', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.getMarkers()[1]?.moveDown(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates1 }, + { ...markerPattern, label: '2', coordinates: coordinates3 }, + { ...markerPattern, label: '3', coordinates: coordinates2 }, + { ...markerPattern, label: '4', coordinates: coordinates4 }, + { ...markerPattern, label: '5', coordinates: coordinates5 }, + ]); + t.end(); + }); + + void t.test('move middle marker up', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.getMarkers()[2]?.moveUp(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates1 }, + { ...markerPattern, label: '2', coordinates: coordinates3 }, + { ...markerPattern, label: '3', coordinates: coordinates2 }, + { ...markerPattern, label: '4', coordinates: coordinates4 }, + { ...markerPattern, label: '5', coordinates: coordinates5 }, + ]); + t.end(); + }); + + void t.test('move middle marker down', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.getMarkers()[2]?.moveDown(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates1 }, + { ...markerPattern, label: '2', coordinates: coordinates2 }, + { ...markerPattern, label: '3', coordinates: coordinates4 }, + { ...markerPattern, label: '4', coordinates: coordinates3 }, + { ...markerPattern, label: '5', coordinates: coordinates5 }, + ]); + t.end(); + }); + + void t.test('move penultimate marker up', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.getMarkers()[3]?.moveUp(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates1 }, + { ...markerPattern, label: '2', coordinates: coordinates2 }, + { ...markerPattern, label: '3', coordinates: coordinates4 }, + { ...markerPattern, label: '4', coordinates: coordinates3 }, + { ...markerPattern, label: '5', coordinates: coordinates5 }, + ]); + t.end(); + }); + + void t.test('move penultimate marker down', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.getMarkers()[3]?.moveDown(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates1 }, + { ...markerPattern, label: '2', coordinates: coordinates2 }, + { ...markerPattern, label: '3', coordinates: coordinates3 }, + { ...markerPattern, label: '4', coordinates: coordinates5 }, + { ...markerPattern, label: '5', coordinates: coordinates4 }, + ]); + t.end(); + }); + + void t.test('move last marker up', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.getMarkers()[4]?.moveUp(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates1 }, + { ...markerPattern, label: '2', coordinates: coordinates2 }, + { ...markerPattern, label: '3', coordinates: coordinates3 }, + { ...markerPattern, label: '4', coordinates: coordinates5 }, + { ...markerPattern, label: '5', coordinates: coordinates4 }, + ]); + t.end(); + }); + + void t.test('do not move last marker down', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.getMarkers()[4]?.moveDown(); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates1 }, + { ...markerPattern, label: '2', coordinates: coordinates2 }, + { ...markerPattern, label: '3', coordinates: coordinates3 }, + { ...markerPattern, label: '4', coordinates: coordinates4 }, + { ...markerPattern, label: '5', coordinates: coordinates5 }, + ]); + t.end(); + }); + + void t.test('drag second marker to fourth', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.reorderMarkers({ oldIndex: 1, newIndex: 3 }); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates1 }, + { ...markerPattern, label: '2', coordinates: coordinates3 }, + { ...markerPattern, label: '3', coordinates: coordinates4 }, + { ...markerPattern, label: '4', coordinates: coordinates2 }, + { ...markerPattern, label: '5', coordinates: coordinates5 }, + ]); + t.end(); + }); + + void t.test('drag fourth marker to second', (t) => { + const reducers = createReducersWithFivePoints(); + reducers.reorderMarkers({ oldIndex: 3, newIndex: 1 }); + t.matchOnlyStrict(reducers.getMarkers(), [ + { ...markerPattern, label: '1', coordinates: coordinates1 }, + { ...markerPattern, label: '2', coordinates: coordinates4 }, + { ...markerPattern, label: '3', coordinates: coordinates2 }, + { ...markerPattern, label: '4', coordinates: coordinates3 }, + { ...markerPattern, label: '5', coordinates: coordinates5 }, + ]); + t.end(); + }); + + t.end(); +}); diff --git a/src/routePlanner/reducer.ts b/src/routePlanner/reducer.ts index 29e5638..4533c04 100644 --- a/src/routePlanner/reducer.ts +++ b/src/routePlanner/reducer.ts @@ -1,8 +1,8 @@ import { nanoid } from 'nanoid'; -import { Marker, ReducerAction, ReorderMarkersParams } from './types'; -import { reorderElements } from '../shared/collections'; import { Dispatch } from 'preact/hooks'; +import { reorderElements } from '../shared/collections.js'; import { Coordinates } from '../shared/types'; +import { Marker, ReducerAction, ReorderMarkersParams } from './types'; const reindexMarkers = (markers: Marker[]) => markers.map((marker, i) => { diff --git a/src/shared/collections.test.ts b/src/shared/collections.test.ts index 1d2fd37..2a59454 100644 --- a/src/shared/collections.test.ts +++ b/src/shared/collections.test.ts @@ -7,7 +7,7 @@ void t.test('reorderElements', (t) => { newIndex: number, expected: string, ) => { - t.match( + t.matchOnlyStrict( reorderElements('abcde'.split(''), oldIndex, newIndex), expected.split(''), ); diff --git a/src/shared/types.ts b/src/shared/types.ts index ce5eba8..7593e6f 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -1,6 +1,7 @@ -import type { LatLng } from 'leaflet'; - -export type Coordinates = LatLng; +export type Coordinates = { + lat: number; + lng: number; +}; export type Waypoint = { coordinates: Coordinates;