diff --git a/src/apps/Map/MapState.ts b/src/apps/Map/MapState.ts index 1d0edbb1d..299675e5a 100644 --- a/src/apps/Map/MapState.ts +++ b/src/apps/Map/MapState.ts @@ -5,6 +5,7 @@ import { Dispatch } from 'redux'; import config from '~/config'; import { MapConfig } from './types'; +import api from '~/services/api/'; const UPDATE_HISTORY = 'Map/MapState/UPDATE_HISTORY'; const SET_ACTIVE_SECTION = 'Map/MapState/SET_ACTIVE_SECTION'; @@ -18,7 +19,7 @@ const SET_PLANNING_FILTER = 'Map/MapState/SET_PLANNING_FILTER'; const SET_POPUP_DATA = 'Map/MapState/SET_POPUP_DATA'; const SET_POPUP_LOCATION = 'Map/MapState/SET_POPUP_LOCATION'; const SET_POPUP_VISIBLE = 'Map/MapState/SET_POPUP_VISIBLE'; -const SET_PLANNING_DATA = 'Map/MapState/SET_PLANNING_DATA'; +export const SET_PLANNING_DATA = 'Map/MapState/SET_PLANNING_DATA'; const SET_ERROR = 'Map/MapState/SET_ERROR'; const UNSET_ERROR = 'Map/MapState/UNSET_ERROR'; @@ -231,7 +232,7 @@ export function setPopupVisible(isVisible: boolean): SetPopupVisible { return { type: SET_POPUP_VISIBLE, payload: { displayPopup: isVisible } }; } -interface LoadPlanningData { +export interface LoadPlanningData { type: typeof SET_PLANNING_DATA; payload: { planningData: any; @@ -244,9 +245,8 @@ export function loadPlanningData() { return false; } - const planningData = await ky - .get(`${config.apiUrl}/projects?page_size=500`, { timeout: 50000 }) - .json(); + const apiRoute = `projects?page_size=500`; + const planningData = await api.get(apiRoute); return dispatch({ type: SET_PLANNING_DATA, payload: { planningData } }); }; diff --git a/src/apps/Map/tests/MapState.unit.test.ts b/src/apps/Map/tests/MapState.unit.test.ts new file mode 100644 index 000000000..ad5977348 --- /dev/null +++ b/src/apps/Map/tests/MapState.unit.test.ts @@ -0,0 +1,64 @@ +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import { rest } from 'msw'; +import { + LoadPlanningData, + loadPlanningData, + MapState, + SET_PLANNING_DATA +} from '~/apps/Map/MapState'; +import config from '~/config'; +import { mswServer } from '../../../../jest/msw/mswServer'; +import { RootState } from '~/store'; +import planningsResponseFixture from './fixtures/planningsResponse.json'; + +describe('Plannings Map Unit tests', () => { + describe('loadPlanningData() thunk', () => { + const mockStore = configureMockStore([thunk]); + + it('does not dispatch any actions if plannings data has been fetched already', async () => { + /* ARRANGE: mock store with initial state */ + const preSeededMapState = { + planningData: true + } as MapState; // omitting non-relevant props + const preSeededRootState = { + MapState: preSeededMapState + } as RootState; + const store = mockStore(preSeededRootState); + + /* ACT: invoke loadPlanningsThunk */ + const loadPlanningsThunk = loadPlanningData(); + // @ts-ignore FIXME: properly type this + await store.dispatch(loadPlanningsThunk); + + /* ASSERT: no action should have been called */ + expect(store.getActions()).toHaveLength(0); + }); + + it(`dispatches ${SET_PLANNING_DATA} once the plannings JSON has been fetched successfully`, async () => { + /* ARRANGE: mock store, intercept request and mock response */ + const store = mockStore({ + MapState: {} as MapState + }); + + const response = planningsResponseFixture; + mswServer.use( + rest.get(`${config.apiUrl}/projects`, (_, res, ctx) => + res(ctx.json(response)) + ) + ); + + /* ACT: invoke thunk */ + const loadPlanningsThunk = loadPlanningData(); + // @ts-ignore FIXME: properly type this + await store.dispatch(loadPlanningsThunk); + + /* ASSERT: action containing the api response has been dispatched */ + const expectedAction: LoadPlanningData = { + type: SET_PLANNING_DATA, + payload: { planningData: response as any } + }; + expect(store.getActions()).toEqual([expectedAction]); + }); + }); +}); diff --git a/src/apps/Map/tests/fixtures/planningsResponse.json b/src/apps/Map/tests/fixtures/planningsResponse.json new file mode 100644 index 000000000..b87bbc5d4 --- /dev/null +++ b/src/apps/Map/tests/fixtures/planningsResponse.json @@ -0,0 +1,50 @@ +{ + "count": 245, + "next": "https://fixmyberlin.de/api/projects?page=2&page_size=1", + "previous": null, + "results": [ + { + "id": 4, + "url": "https://fixmyberlin.de/api/projects/4", + "project_key": "FK-020", + "title": "Neue Fahrradstraße", + "description": "Die Glogauer Straße ist als Zubringer zum Görlitzer Park und Durchfahrt zur Oberbaumbrücke eine stark befahrene Radstrecke. Die Straße soll daher als Fahrradstraße umgwidmet werden. Die Machbarkeit soll im Jahr 2019 geprüft und bei einem positivem Ergebnis voraussichtlich in 2019 umgesetzt werden.", + "short_description": "Die Glogauer Straße wird zu einer Fahrradstraße.", + "category": "bike street", + "street_name": "Glogauer Straße", + "borough": "Friedrichshain-Kreuzberg", + "side": 2, + "costs": null, + "draft_submitted": null, + "construction_started": null, + "construction_completed": "unbekannt", + "construction_completed_date": null, + "phase": "draft", + "responsible": "Bezirksamt Friedrichshain-Kreuzberg", + "external_url": null, + "cross_section": null, + "faq": [], + "geometry": { + "type": "LineString", + "coordinates": [ + [13.435653786621, 52.4916709861841], + [13.4357418335253, 52.4917627197358], + [13.4374143916796, 52.4934570093144], + [13.4389228394647, 52.4949934633041] + ] + }, + "center": { + "type": "Point", + "coordinates": [13.4374143916796, 52.4934570093144] + }, + "length": 431.0, + "photos": [ + { + "copyright": "Photo by Anthony Ginsbrook", + "src": "https://fmb-aws-bucket.s3.amazonaws.com/photos/Platzhalter_anthony-ginsbrook-225252-unsplash.jpg" + } + ], + "likes": 4 + } + ] +} diff --git a/src/services/api/shorthands.ts b/src/services/api/shorthands.ts index fabbf4c62..96922a0c7 100644 --- a/src/services/api/shorthands.ts +++ b/src/services/api/shorthands.ts @@ -1,4 +1,3 @@ -import { Options as KyOptions } from 'ky'; import { JSONValue, RequestOptions } from './types'; import request from './request';