diff --git a/frontend/src/components/projectDetail/index.js b/frontend/src/components/projectDetail/index.js index 54a7e83193..eb74a9e12a 100644 --- a/frontend/src/components/projectDetail/index.js +++ b/frontend/src/components/projectDetail/index.js @@ -370,6 +370,21 @@ export const ProjectDetail = (props) => { ); }; +const GeometryPropType = PropTypes.shape({ + type: PropTypes.oneOf(['Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon', 'GeometryCollection']), + coordinates: PropTypes.array, + geometries: PropTypes.array +}); +const FeaturePropType = PropTypes.shape({ + type: PropTypes.oneOf(['Feature']), + geometry: GeometryPropType, + properties: PropTypes.object, +}); +const FeatureCollectionPropType = PropTypes.shape({ + type: PropTypes.oneOf(['FeatureCollection']), + features: PropTypes.arrayOf(FeaturePropType).isRequired +}); + ProjectDetail.propTypes = { project: PropTypes.shape({ projectId: PropTypes.number, @@ -393,10 +408,11 @@ ProjectDetailMap.propTypes = { areaOfInterest: PropTypes.object, priorityAreas: PropTypes.arrayOf(PropTypes.object), }).isRequired, - tasks: PropTypes.arrayOf(PropTypes.object), + // Tasks are a GeoJSON FeatureCollection + tasks: FeatureCollectionPropType, navigate: PropTypes.func, type: PropTypes.string, - tasksError: PropTypes.string, + tasksError: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), projectLoading: PropTypes.bool, }; @@ -406,7 +422,7 @@ ProjectDetailLeft.propTypes = { shortDescription: PropTypes.string, }), projectId: PropTypes.number, - tasks: PropTypes.arrayOf(PropTypes.object), + tasks: FeatureCollectionPropType, }).isRequired, contributors: PropTypes.arrayOf(PropTypes.object), className: PropTypes.string, diff --git a/frontend/src/network/tests/mockData/projects.js b/frontend/src/network/tests/mockData/projects.js index bd63833d01..c6b676662c 100644 --- a/frontend/src/network/tests/mockData/projects.js +++ b/frontend/src/network/tests/mockData/projects.js @@ -323,6 +323,7 @@ export const taskDetail = (taskId) => ({ export const projectComments = { chat: [ { + id: 1, message: "

@happy_me we do want 'significant' roads that lead to houses. Rule of thumb I use for picking classification is the usage over condition/what it looks like. If it's the main 'path' to one or maybe several homes, I would pick service; even if a vehicle can't drive it, that can be reflected with additional tags, but the road still functions as access to the home(s).

", pictureUrl: @@ -331,6 +332,7 @@ export const projectComments = { username: 'helnershingthapa', }, { + id: 2, message: '

hello world

', pictureUrl: 'https://www.openstreetmap.org/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBNXQ2Q3c9PSIsImV4cCI6bnVsbCwicHVyIjoiYmxvYl9pZCJ9fQ==--fe41f1b2a5d6cf492a7133f15c81f105dec06ff7/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBPZ2h3Ym1jNkZISmxjMmw2WlY5MGIxOXNhVzFwZEZzSGFXbHBhUT09IiwiZXhwIjpudWxsLCJwdXIiOiJ2YXJpYXRpb24ifX0=--058ac785867b32287d598a314311e2253bd879a3/unnamed.webp', @@ -338,6 +340,7 @@ export const projectComments = { username: 'helnershingthapa', }, { + id: 3, message: '

asdadadasdasdasd

', pictureUrl: 'https://www.openstreetmap.org/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBeFJheFE9PSIsImV4cCI6bnVsbCwicHVyIjoiYmxvYl9pZCJ9fQ==--a765e2377a288bccae85da6604300251d9de6d39/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJYW5CbkJqb0dSVlE2RkhKbGMybDZaVjkwYjE5c2FXMXBkRnNIYVdscGFRPT0iLCJleHAiOm51bGwsInB1ciI6InZhcmlhdGlvbiJ9fQ==--1d22b8d446683a272d1a9ff04340453ca7c374b4/bitmoji.jpg', @@ -345,6 +348,7 @@ export const projectComments = { username: 'Hel Nershing Thapa', }, { + id: 4, message: '

test of \ncode block\nhmmm\npreview showed it as a block\nand monospace font\nbut not indented

', pictureUrl: @@ -353,6 +357,7 @@ export const projectComments = { username: 'wireguy', }, { + id: 5, message: '

this is a code\nblock\nshould it\nbe indented\nby 4 space?\nminor...

', pictureUrl: diff --git a/frontend/src/views/tests/project.test.js b/frontend/src/views/tests/project.test.js index 8c0e6fd602..7b02149495 100644 --- a/frontend/src/views/tests/project.test.js +++ b/frontend/src/views/tests/project.test.js @@ -221,27 +221,13 @@ describe('Project Detail Page', () => { Line: () => null, })); - it('should render component details', async () => { - act(() => { - store.dispatch({ type: 'SET_LOCALE', locale: 'es-AR' }); - }); - renderWithRouter( - - - - - , - ); - await waitFor(() => { - expect(screen.getByText(/sample project/i)).toBeInTheDocument(); - expect(screen.getByText(/hello world/i)).toBeInTheDocument(); - }); - }); - - it('should display private project error message', async () => { - setupFaultyHandlers(); + /** + * Set up a ProjectDetailPage given an initial entry; this avoids issues where there is no project id. + * @param {Array} initialEntries The initial entries. This should be in the form of `[projects/:id]`. + */ + function setup(initialEntries) { render( - + { , ); + } + + it('should render component details', async () => { + act(() => { + store.dispatch({ type: 'SET_LOCALE', locale: 'es-AR' }); + }); + setup(['/projects/123']); + await waitFor(() => { + expect(screen.getByText(/sample project/i)).toBeInTheDocument(); + expect(screen.getByText(/hello world/i)).toBeInTheDocument(); + }); + }); + + it('should display private project error message', async () => { + setupFaultyHandlers(); + setup(['/projects/123']); await waitFor(() => expect(