-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Verifies an object is a valid GeoJSON Feature. In addition to the core function and matcher, this adds a new setup script for 'features', reorganizes the package.json test scripts, and refactors the good and bad geometry examples from toBeGeometryCollection.test.js into a separate data file for reuse. Type definitions updated and reorganized. Note: this matcher test has extensive snapshot testing to begin addressing #32. Resolves: #24
- Loading branch information
1 parent
5c041c6
commit 551aa7f
Showing
17 changed files
with
1,290 additions
and
262 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -166,7 +166,7 @@ _Future_ | |
|
||
_1.0.0_ | ||
|
||
- [ ] toBeFeature | ||
- [x] toBeFeature | ||
|
||
--- | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
const { anyGeometry } = require('../geometries/anyGeometry') | ||
const { pointGeometry } = require('../geometries/pointGeometry') | ||
const { multiPointGeometry } = require('../geometries/multiPointGeometry') | ||
const { lineStringGeometry } = require('../geometries/lineStringGeometry') | ||
const { multiLineStringGeometry } = require('../geometries/multiLineStringGeometry') | ||
const { polygonGeometry } = require('../geometries/polygonGeometry') | ||
const { multiPolygonGeometry } = require('../geometries/multiPolygonGeometry') | ||
const { validBoundingBox } = require('../boundingBoxes/validBoundingBox') | ||
|
||
/** | ||
* Verifies an object is a valid GeoJSON Feature. This object requires a "type" member that must | ||
* equal 'Feature', a "geometry" member that contains either one of the seven valid GeoJSON | ||
* geometry objects or an empty array, and a "properties" member that is either an object of any | ||
* composition or null. | ||
* | ||
* Foreign members are allowed with the exceptions thrown below. | ||
* If present, bounding boxes must be valid. | ||
* | ||
* @memberof Core.Features | ||
* @see https://github.com/M-Scott-Lassiter/jest-geojson/issues/24 | ||
* @param {object} featureObject a GeoJSON LineString Geometry object | ||
* @param {string} [geometryType] Specific type of geometry to search for | ||
* @returns {boolean} True if a valid GeoJSON Feature. If invalid, it will throw an error. | ||
* @throws {Error} Argument not an object | ||
* @throws {Error} Must have a type property with value 'Feature' | ||
* @throws {Error} Forbidden from having a property 'coordinates', 'geometries', 'properties', or 'features' | ||
* @throws {Error} Bounding box must be valid (if present) | ||
* @throws {Error} ID must be either a number or string (if present) | ||
* @example | ||
* const testFeature = { | ||
* "type": "Feature", | ||
* "bbox": [-10.0, -10.0, 10.0, 10.0], | ||
* "geometry": { | ||
* "type": "Polygon", | ||
* "coordinates": [ | ||
* [ | ||
* [-10.0, -10.0], | ||
* [10.0, -10.0], | ||
* [10.0, 10.0], | ||
* [-10.0, -10.0] | ||
* ] | ||
* ] | ||
* }, | ||
* "properties": { | ||
* "prop0": "value0", | ||
* "prop1": { | ||
* "this": "that" | ||
* } | ||
* } | ||
* } | ||
* const multiPoint = { | ||
* type: "MultiPoint", | ||
* coordinates: [ | ||
* [101.0, 0.0], | ||
* [102.0, 1.0] | ||
* ] | ||
* } | ||
* | ||
* const goodExample1 = feature(testFeature)) // true | ||
* const goodExample2 = feature(testFeature, 'Polygon')) // true | ||
* | ||
* const badExample1 = feature(multiPoint)) // throws error | ||
* const badExample2 = feature(testFeature, 'LineString')) // throws error | ||
* const badExample3 = feature(testFeature.geometry, 'Polygon')) // throws error | ||
*/ | ||
function feature(featureObject, geometryType) { | ||
if (featureObject.type !== 'Feature') { | ||
throw new Error(`Must have a type property with value 'Feature'`) | ||
} | ||
|
||
if ('coordinates' in featureObject) { | ||
throw new Error( | ||
`GeoJSON Feature objects are forbidden from having a property 'coordinates'.` | ||
) | ||
} | ||
|
||
if ('geometries' in featureObject) { | ||
throw new Error( | ||
`GeoJSON Feature objects are forbidden from having a property 'geometries'.` | ||
) | ||
} | ||
|
||
if ('features' in featureObject) { | ||
throw new Error(`GeoJSON Feature objects are forbidden from having a property 'features'.`) | ||
} | ||
|
||
if (!('properties' in featureObject)) { | ||
throw new Error(`GeoJSON Feature objects must have a property 'properties'.`) | ||
} | ||
|
||
if (typeof featureObject.properties !== 'object' || Array.isArray(featureObject.properties)) { | ||
throw new Error(`GeoJSON Feature properties must be either null or an object.`) | ||
} | ||
|
||
if (!('geometry' in featureObject)) { | ||
throw new Error(`GeoJSON Feature objects must have a property 'geometry'.`) | ||
} | ||
|
||
if (typeof featureObject.geometry !== 'object' || Array.isArray(featureObject.geometry)) { | ||
throw new Error(`GeoJSON Feature 'geometry' must be a valid GeoJSON geometry object.`) | ||
} | ||
|
||
if ('bbox' in featureObject) { | ||
validBoundingBox(featureObject.bbox) | ||
} | ||
|
||
if ('id' in featureObject) { | ||
if ( | ||
!(typeof featureObject.id === 'number' || typeof featureObject.id === 'string') || | ||
Number.isNaN(featureObject.id) | ||
) { | ||
throw new Error(`If present, ID must be either a number or string.`) | ||
} | ||
} | ||
|
||
// Guard clause; features are allowed to have null geometry. However, if the matcher explicitly calls | ||
// for a particular geometry type, null isn't an option. We have to check for that. | ||
if (featureObject.geometry === null && geometryType === undefined) { | ||
return true | ||
} | ||
|
||
// At this point, we have guaranteed there is a geometry here. Validate it with the core functions. | ||
if (geometryType === undefined) { | ||
anyGeometry(featureObject.geometry) | ||
} | ||
|
||
if (geometryType === 'Point') { | ||
pointGeometry(featureObject.geometry) | ||
} | ||
|
||
if (geometryType === 'MultiPoint') { | ||
multiPointGeometry(featureObject.geometry) | ||
} | ||
|
||
if (geometryType === 'LineString') { | ||
lineStringGeometry(featureObject.geometry) | ||
} | ||
|
||
if (geometryType === 'MultiLineString') { | ||
multiLineStringGeometry(featureObject.geometry) | ||
} | ||
|
||
if (geometryType === 'Polygon') { | ||
polygonGeometry(featureObject.geometry) | ||
} | ||
|
||
if (geometryType === 'MultiPolygon') { | ||
multiPolygonGeometry(featureObject.geometry) | ||
} | ||
|
||
return true | ||
} | ||
|
||
exports.feature = feature |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
const { feature } = require('../../core/features/feature') | ||
|
||
// eslint-disable-next-line jsdoc/require-returns | ||
/** | ||
* Verifies an object is a valid GeoJSON Feature. This object requires a "type" member that must | ||
* equal 'Feature', a "geometry" member that contains either one of the seven valid GeoJSON | ||
* geometry objects or an empty array, and a "properties" member that is either an object of any | ||
* composition or null. | ||
* | ||
* Foreign members are allowed with the exception of 'coordinates', 'geometries', 'properties', or 'features'. | ||
* If present, bounding boxes must be valid. | ||
* | ||
* @memberof Matchers.Features | ||
* @see https://github.com/M-Scott-Lassiter/jest-geojson/issues/24 | ||
* @param {object} featureObject any GeoJSON Feature object | ||
* @param {string} geometryType Optional string representing one of the seven GeoJSON geometry types | ||
* @example | ||
* const testFeature = { | ||
* "type": "Feature", | ||
* "bbox": [-10.0, -10.0, 10.0, 10.0], | ||
* "geometry": { | ||
* "type": "Polygon", | ||
* "coordinates": [ | ||
* [ | ||
* [-10.0, -10.0], | ||
* [10.0, -10.0], | ||
* [10.0, 10.0], | ||
* [-10.0, -10.0] | ||
* ] | ||
* ] | ||
* }, | ||
* "properties": { | ||
* "prop0": "value0", | ||
* "prop1": { | ||
* "this": "that" | ||
* } | ||
* } | ||
* } | ||
* | ||
* test('Object is valid GeoJSON Feature', () => { | ||
* expect(testFeature).toBeFeature() | ||
* expect(testFeature).toBeFeature('Polygon') | ||
* }) | ||
* @example | ||
* const multiPoint = { | ||
* type: "MultiPoint", | ||
* coordinates: [ | ||
* [101.0, 0.0], | ||
* [102.0, 1.0] | ||
* ] | ||
* } | ||
* | ||
* test('Object is NOT valid GeoJSON Geometry Object', () => { | ||
* expect(multiPoint).not.toBeFeature() | ||
* expect(testFeature).not.toBeFeature('LineString') | ||
* expect(testFeature.geometry).not.toBeFeature('Polygon') | ||
* }) | ||
*/ | ||
function toBeFeature(featureObject, geometryType) { | ||
const { printReceived, matcherHint } = this.utils | ||
const optionalTypeMessage = () => { | ||
if (geometryType !== undefined) { | ||
return ` with ${geometryType} geometry` | ||
} | ||
return '' | ||
} | ||
const passMessage = | ||
// eslint-disable-next-line prefer-template | ||
matcherHint('.not.toBeFeature', 'FeatureObject', 'GeometryType') + | ||
'\n\n' + | ||
`Expected input to not be a valid GeoJSON feature object` + | ||
optionalTypeMessage() + | ||
`.\n\n` + | ||
`Received: ${printReceived(featureObject)}` | ||
|
||
/** | ||
* 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('.toBeFeature', 'FeatureObject', 'GeometryType') + | ||
'\n\n' + | ||
`${errorMessage}\n\n` + | ||
`Received: ${printReceived(featureObject)}` | ||
) | ||
} | ||
|
||
try { | ||
feature(featureObject, geometryType) | ||
} catch (err) { | ||
return { pass: false, message: () => failMessage(err.message) } | ||
} | ||
return { pass: true, message: () => passMessage } | ||
} | ||
|
||
exports.toBeFeature = toBeFeature |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
const matchers = require('../matchers') | ||
const { throwJestRuntimeError } = require('./all') | ||
|
||
const jestExpect = global.expect | ||
|
||
if (jestExpect !== undefined) { | ||
expect.extend(matchers.features) | ||
} else { | ||
throwJestRuntimeError() | ||
} |
Oops, something went wrong.