Skip to content

Commit

Permalink
feat(toBeAnyGeometry): add new matcher
Browse files Browse the repository at this point in the history
Verifies an object meets validity requirements for one of the six basic GeoJSON geometry types:
Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon

Resolves: #15
  • Loading branch information
M-Scott-Lassiter committed May 30, 2022
1 parent b828396 commit ed7c3eb
Show file tree
Hide file tree
Showing 16 changed files with 478 additions and 26 deletions.
1 change: 1 addition & 0 deletions .cz-config.js
Expand Up @@ -7,6 +7,7 @@ const coordinateMatchers = [
{ name: 'isValid3DCoordinate' },
{ name: 'isValidBoundingBox' },
{ name: 'isValidCoordinate' },
{ name: 'toBeAnyGeometry' },
{ name: 'toBeLineStringGeometry' },
{ name: 'toBeMultiLineStringGeometry' },
{ name: 'toBeMultiPointGeometry' },
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -161,7 +161,7 @@ _1.0.0_
- [x] toBeMultiLineStringGeometry
- [x] toBePolygonGeometry
- [x] toBeMultiPolygonGeometry
- [ ] toBeAnyGeometry
- [x] toBeAnyGeometry

---

Expand Down
1 change: 1 addition & 0 deletions src/core.js
Expand Up @@ -13,6 +13,7 @@ exports.coordinates = {
}

exports.geometries = {
anyGeometry: require('./core/geometries/anyGeometry'),
lineStringGeometry: require('./core/geometries/lineStringGeometry'),
multiLineStringGeometry: require('./core/geometries/multiLineStringGeometry'),
multiPointGeometry: require('./core/geometries/multiPointGeometry'),
Expand Down
104 changes: 104 additions & 0 deletions src/core/geometries/anyGeometry.js
@@ -0,0 +1,104 @@
const { pointGeometry } = require('./pointGeometry')
const { multiPointGeometry } = require('./multiPointGeometry')
const { lineStringGeometry } = require('./lineStringGeometry')
const { multiLineStringGeometry } = require('./multiLineStringGeometry')
const { polygonGeometry } = require('./polygonGeometry')
const { multiPolygonGeometry } = require('./multiPolygonGeometry')

/**
* Verifies an object meets validity requirements for one of the six basic GeoJSON geometry types:
* Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon
*
* @memberof Core.Geometries
* @see https://github.com/M-Scott-Lassiter/jest-geojson/issues/15
* @param {object} geometryObject A WGS-84 array of [longitude, latitude] or [longitude, latitude, alititude]
* @returns {boolean} True if a valid GeoJSON geometry object. If invalid, it will throw an error.
* @throws {Error} Input must be either a valid Point, MultiPoint, LineString, MultiLineString, Polygon, or MultiPolygon
* @example
point = {
"type": "Point",
"coordinates": [100.0, 0.0]
}
lineString = {
"type": "LineString",
"coordinates": [
[
[180.0, 40.0],
[180.0, 50.0],
[170.0, 50.0],
[170.0, 40.0],
[180.0, 40.0]
]
]
}
polygon = {
"type": "Polygon",
"coordinates": [
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0]
]
]
}
feature = {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [102.0, 0.5]
}
}
console.log(anyGeometry(point)) // true
console.log(anyGeometry(lineString)) // true
console.log(anyGeometry(polygon)) // true
console.log(anyGeometry(feature)) // throws error
*/
function anyGeometry(geometryObject) {
// if (
// typeof geometryObject !== 'object' ||
// Array.isArray(geometryObject) ||
// geometryObject === null
// ) {
// throw new Error('Argument must be a GeoJSON Geometry object.')
// }

if (geometryObject?.type === 'Point') {
pointGeometry(geometryObject)
return true
}

if (geometryObject?.type === 'MultiPoint') {
multiPointGeometry(geometryObject)
return true
}

if (geometryObject?.type === 'LineString') {
lineStringGeometry(geometryObject)
return true
}

if (geometryObject?.type === 'MultiLineString') {
multiLineStringGeometry(geometryObject)
return true
}

if (geometryObject?.type === 'Polygon') {
polygonGeometry(geometryObject)
return true
}

if (geometryObject?.type === 'MultiPolygon') {
multiPolygonGeometry(geometryObject)
return true
}

throw new Error(
'Object must be either a valid Point, MultiPoint, LineString, MultiLineString, Polygon, or MultiPolygon'
)
}

exports.anyGeometry = anyGeometry
1 change: 1 addition & 0 deletions src/matchers.js
Expand Up @@ -22,6 +22,7 @@ exports.coordinates = {

// Geometries
exports.geometries = {
toBeAnyGeometry: require('./matchers/geometries/toBeAnyGeometry').toBeAnyGeometry,
toBeLineStringGeometry: require('./matchers/geometries/toBeLineStringGeometry')
.toBeLineStringGeometry,
toBeMultiLineStringGeometry: require('./matchers/geometries/toBeMultiLineStringGeometry')
Expand Down
90 changes: 90 additions & 0 deletions src/matchers/geometries/toBeAnyGeometry.js
@@ -0,0 +1,90 @@
const { anyGeometry } = require('../../core/geometries/anyGeometry')

// eslint-disable-next-line jsdoc/require-returns
/**
* Verifies an object meets validity requirements for one of the six basic GeoJSON geometry types:
* Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon
*
* @memberof Matchers.Geometries
* @see https://github.com/M-Scott-Lassiter/jest-geojson/issues/15
* @param {object} geometryObject any GeoJSON Geometry object
* @example
point = {
"type": "Point",
"coordinates": [100.0, 0.0]
}
lineString = {
"type": "LineString",
"coordinates": [
[
[180.0, 40.0],
[180.0, 50.0],
[170.0, 50.0],
[170.0, 40.0],
[180.0, 40.0]
]
]
}
polygon = {
"type": "Polygon",
"coordinates": [
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0]
]
]
}
feature = {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [102.0, 0.5]
}
}
expect(point).toBeAnyGeometry()
expect(lineString).toBeAnyGeometry()
expect(polygon).toBeAnyGeometry()
expect(feature).not.toBeAnyGeometry()
expect([322, -34.549, 0]).not.toBeAnyGeometry()
expect({coordinates: [22, -34.549, 22]}).not.toBeAnyGeometry()
*/
function toBeAnyGeometry(geometryObject) {
const { printReceived, matcherHint } = this.utils
const passMessage =
// eslint-disable-next-line prefer-template
matcherHint('.not.toBeAnyGeometry', 'GeometryObject', '') +
'\n\n' +
`Expected input to not be a valid GeoJSON geometry object.\n\n` +
`Received: ${printReceived(geometryObject)}`

/**
* Combines a custom error message with built in Jest tools to provide a more descriptive error
* meessage to the end user.
*
* @param {string} errorMessage Error message text to return to the user
* @returns {string} Concatenated Jest test result string
*/
function failMessage(errorMessage) {
return (
// eslint-disable-next-line prefer-template, no-unused-expressions
matcherHint('.toBeAnyGeometry', 'GeometryObject', '') +
'\n\n' +
`${errorMessage}\n\n` +
`Received: ${printReceived(geometryObject)}`
)
}

try {
anyGeometry(geometryObject)
} catch (err) {
return { pass: false, message: () => failMessage(err.message) }
}
return { pass: true, message: () => passMessage }
}

exports.toBeAnyGeometry = toBeAnyGeometry
4 changes: 4 additions & 0 deletions tests/core.test.js
Expand Up @@ -31,6 +31,10 @@ describe('Coordinate Functions Exported', () => {
})

describe('Geometry Functions Exported', () => {
test('anyGeometry', () => {
expect('anyGeometry' in core.geometries).toBeTruthy()
})

test('lineStringGeometry', () => {
expect('lineStringGeometry' in core.geometries).toBeTruthy()
})
Expand Down
17 changes: 17 additions & 0 deletions tests/geometries/__snapshots__/toBeAnyGeometry.test.js.snap
@@ -0,0 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Error Snapshot Testing. Throws error: expect([0, 0, 0]).toBeAnyGeometry 1`] = `
"expect(GeometryObject).toBeAnyGeometry()
Object must be either a valid Point, MultiPoint, LineString, MultiLineString, Polygon, or MultiPolygon
Received: false"
`;

exports[`Error Snapshot Testing. Throws error: expect({type: 'Point', coordinates: [0, 0]}).not.toBeAnyGeometry 1`] = `
"expect(GeometryObject).not.toBeAnyGeometry()
Expected input to not be a valid GeoJSON geometry object.
Received: {\\"coordinates\\": [0, 0], \\"type\\": \\"Point\\"}"
`;
104 changes: 104 additions & 0 deletions tests/geometries/toBeAnyGeometry.test.js
@@ -0,0 +1,104 @@
// This matcher works on all the same geometry cases.
// Accordingly, those specific tests check that this function also work.
// This test suite also checks it fails with types GeometryCollection, Feature, and FeatureCollection.
// Finally, it tests the unique snapshots.

describe('Invalid Use Cases', () => {
test('Expect to fail with GeometryCollection', () => {
const geometryCollection = {
type: 'GeometryCollection',
geometries: [
{
type: 'Point',
coordinates: [100.0, 0.0]
},
{
type: 'LineString',
coordinates: [
[101.0, 0.0],
[102.0, 1.0]
]
}
]
}
expect(geometryCollection).not.toBeAnyGeometry()
})

test('Expect to fail with Feature', () => {
const feature = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [102.0, 0.5]
}
}
expect(feature).not.toBeAnyGeometry()
})

test('Expect to fail with FeatureCollection', () => {
const featureCollection = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [102.0, 0.5]
},
properties: {
prop0: 'value0'
}
},
{
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [
[102.0, 0.0],
[103.0, 1.0],
[104.0, 0.0],
[105.0, 1.0]
]
},
properties: {
prop0: 'value0',
prop1: 0.0
}
},
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0]
]
]
},
properties: {
prop0: 'value0',
prop1: {
this: 'that'
}
}
}
]
}
expect(featureCollection).not.toBeAnyGeometry()
})
})

describe('Error Snapshot Testing. Throws error:', () => {
test(`expect({type: 'Point', coordinates: [0, 0]}).not.toBeAnyGeometry`, () => {
expect(() =>
expect({ type: 'Point', coordinates: [0, 0] }).not.toBeAnyGeometry()
).toThrowErrorMatchingSnapshot()
})
test('expect([0, 0, 0]).toBeAnyGeometry', () => {
expect(() => expect(false).toBeAnyGeometry()).toThrowErrorMatchingSnapshot()
})
})

0 comments on commit ed7c3eb

Please sign in to comment.