Skip to content

Commit

Permalink
feat(isValidBoundingBox): add new matcher
Browse files Browse the repository at this point in the history
Verifies a two or three dimensional bounding box meets WGS-84 and GeoJSON validity requirements.

Resolves: #8
  • Loading branch information
M-Scott-Lassiter committed May 25, 2022
1 parent 70bd43e commit 9a8b7ed
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 8 deletions.
1 change: 1 addition & 0 deletions .cz-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const coordinateMatchers = [
{ name: 'isValid2DCoordinate' },
{ name: 'isValid3DBoundingBox' },
{ name: 'isValid3DCoordinate' },
{ name: 'isValidBoundingBox' },
{ name: 'isValidCoordinate' }
]

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ _1.0.0_

- [x] isValid2DBoundingBox
- [x] isValid3DBoundingBox
- [ ] isValidBoundingBox
- [x] isValidBoundingBox

## Geometry Matchers

Expand Down
3 changes: 2 additions & 1 deletion src/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

exports.boundingBoxes = {
valid2DBoundingBox: require('./core/boundingBoxes/valid2DBoundingBox'),
valid3DBoundingBox: require('./core/boundingBoxes/valid3DBoundingBox')
valid3DBoundingBox: require('./core/boundingBoxes/valid3DBoundingBox'),
validBoundingBox: require('./core/boundingBoxes/validBoundingBox')
}

exports.coordinates = {
Expand Down
38 changes: 38 additions & 0 deletions src/core/boundingBoxes/validBoundingBox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const { valid2DBoundingBox } = require('./valid2DBoundingBox')
const { valid3DBoundingBox } = require('./valid3DBoundingBox')

/**
* Verifies either a two or three dimensional bounding box meets WGS-84 and GeoJSON validity requirements.
*
* @memberof Core.BoundingBoxes
* @see https://github.com/M-Scott-Lassiter/jest-geojson/issues/8
* @param {GeoJSON2DBBox|GeoJSON3DBBox} bboxArray A WGS-84 array of [west, south, east, north] or [west, south, depth, east, north, altitude].
*
* Longitude must be between -180 to 180.
* Latitude must be between -90 to 90.
* If present, depth and altitude must be a number between -Infinity to Infinity.
* The standard does not specify units altitude represents (i.e. meters, feet, etc.).
* @returns {boolean} True if a valid 2D or 3D GeoJSON bounding box. If invalid, it will throw an error.
* @throws {Error} Input must be an array of only four or six elments
*/
function validBoundingBox(bboxArray) {
if (!Array.isArray(bboxArray)) {
throw new Error('Input must be an array.')
}

if (bboxArray.length < 4 || bboxArray.length > 6 || bboxArray.length === 5) {
throw new Error('Input must be an array of either four or six elments.')
}

if (bboxArray.length === 4) {
valid2DBoundingBox(bboxArray)
}

if (bboxArray.length === 6) {
valid3DBoundingBox(bboxArray)
}

return true
}

exports.validBoundingBox = validBoundingBox
3 changes: 2 additions & 1 deletion src/matchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ exports.boundingBoxes = {
isValid2DBoundingBox: require('./matchers/boundingBoxes/isValid2DBoundingBox')
.isValid2DBoundingBox,
isValid3DBoundingBox: require('./matchers/boundingBoxes/isValid3DBoundingBox')
.isValid3DBoundingBox
.isValid3DBoundingBox,
isValidBoundingBox: require('./matchers/boundingBoxes/isValidBoundingBox').isValidBoundingBox
}
// Coordinates
exports.coordinates = {
Expand Down
67 changes: 67 additions & 0 deletions src/matchers/boundingBoxes/isValidBoundingBox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const { validBoundingBox } = require('../../core/boundingBoxes/validBoundingBox')

// eslint-disable-next-line jsdoc/require-returns
/**
* Verifies a two or three dimensional bounding box meets WGS-84 and GeoJSON validity requirements.
*
* @memberof BoundingBoxes
* @see https://github.com/M-Scott-Lassiter/jest-geojson/issues/8
* @param {number[]} coordinateArray A WGS-84 array of [west, south, east, north] or
* [west, south, depth, east, north, altitude].
*
* Longitude must be between -180 to 180.
* Latitude must be between -90 to 90.
* If present, depth and altitude must be a number between -Infinity to Infinity.
* The standard does not specify units altitude represents (i.e. meters, feet, etc.).
* @example
* expect([-20, 10, -10, 20]).isValidBoundingBox()
* expect([170, -20, -170, 20]).isValidBoundingBox()
* expect([-10, -20, -100, 20, 10, 0]).isValidBoundingBox()
* expect([170, -20, -22.5, 20, -170, 12345.678]).isValidBoundingBox()
* @example
* expect([-180.01, -10, -160, 10]).not.isValidBoundingBox() // Longitude out of range
* expect([-10, -10, "0", 10, 20, 0]).not.isValidBoundingBox() // Non-numeric value
*/
function isValidBoundingBox(coordinateArray) {
const { printReceived, matcherHint } = this.utils
const passMessage =
// eslint-disable-next-line prefer-template
matcherHint(
'.not.isValidBoundingBox',
'[west, south, (depth), east, north, (altitude)]',
''
) +
'\n\n' +
`Expected input to not be a four or six element bounding box array.\n\n` +
`Received: ${printReceived(coordinateArray)}`

/**
* 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(
'.isValidBoundingBox',
'[west, south, (depth), east, north, (altitude)]',
''
) +
'\n\n' +
`${errorMessage}\n\n` +
`Received: ${printReceived(coordinateArray)}`
)
}

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

exports.isValidBoundingBox = isValidBoundingBox
33 changes: 33 additions & 0 deletions tests/boundingBoxes/__snapshots__/isValidBoundingBox.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Error Snapshot Testing. Throws error: expect([0, -95, 0, 0, 0, 0]).isValidBoundingBox 1`] = `
"expect([west, south, (depth), east, north, (altitude)]).isValidBoundingBox()
Southern value must be a number between -90 and 90.
Received: [0, -95, 0, 0, 0, 0]"
`;

exports[`Error Snapshot Testing. Throws error: expect([0, 0, 0, 0, 0, 0]).not.isValidBoundingBox 1`] = `
"expect([west, south, (depth), east, north, (altitude)]).not.isValidBoundingBox()
Expected input to not be a four or six element bounding box array.
Received: [0, 0, 0, 0, 0, 0]"
`;

exports[`Error Snapshot Testing. Throws error: expect([0, 0, 0, 0]).not.isValidBoundingBox 1`] = `
"expect([west, south, (depth), east, north, (altitude)]).not.isValidBoundingBox()
Expected input to not be a four or six element bounding box array.
Received: [-20, 10, -10, 20]"
`;

exports[`Error Snapshot Testing. Throws error: expect([0, 0, 95, 0]).isValidBoundingBox 1`] = `
"expect([west, south, (depth), east, north, (altitude)]).isValidBoundingBox()
Northern value must be a number between -90 and 90.
Received: [0, 0, 0, 95]"
`;
17 changes: 15 additions & 2 deletions tests/boundingBoxes/isValid2DBoundingBox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ describe('Valid Use Cases', () => {
describe('Expect to pass with good coordinates:', () => {
test.each([...goodBBoxes])('expect(%p)', (bboxArray) => {
expect(bboxArray).isValid2DBoundingBox()
expect(bboxArray).isValidBoundingBox()
})
})

describe('Expect to pass with good boundary coordinates:', () => {
test.each([...goodBoundaryCoordinates])('expect(%p)', (bboxArray) => {
expect(bboxArray).isValid2DBoundingBox()
expect(bboxArray).isValidBoundingBox()
})
})
})
Expand All @@ -72,6 +74,7 @@ describe('Inalid Use Cases', () => {
describe('Expect to fail with bad inputs:', () => {
test.each([...invalidInputValues])('expect(%p)', (badInput) => {
expect(badInput).not.isValid2DBoundingBox()
expect(badInput).not.isValidBoundingBox()
})
})

Expand All @@ -92,50 +95,60 @@ describe('Inalid Use Cases', () => {
describe('Expect to fail with out of range coordinate:', () => {
test.each([...coordinatesOutOfRange])('expect(%p)', (coordinate) => {
expect(coordinate).not.isValid2DBoundingBox()
expect(coordinate).not.isValidBoundingBox()
})
})

describe('Expect to fail with illogical BBox:', () => {
test('Northern boundary less than southern: expect([-10, 20, 10, -20])', () => {
expect([-10, 20, 10, -20]).not.isValid2DBoundingBox()
const invalidBBox = [-10, 20, 10, -20]
expect(invalidBBox).not.isValid2DBoundingBox()
expect(invalidBBox).not.isValidBoundingBox()
})
})

describe('Passing Bad Individual Coordinate Values', () => {
describe('Expect to fail with bad western value:', () => {
test.each([...invalidInputValues])('expect([%p, -10, 10, 10])', (input) => {
expect([input, -10, 10, 10]).not.isValid2DBoundingBox()
expect([input, -10, 10, 10]).not.isValidBoundingBox()
})
})

describe('Expect to fail with bad southern value:', () => {
test.each([...invalidInputValues])('expect([-10, %p, 10, 10])', (input) => {
expect([-10, input, 10, 10]).not.isValid2DBoundingBox()
expect([-10, input, 10, 10]).not.isValidBoundingBox()
})
})

describe('Expect to fail with bad eastern value:', () => {
test.each([...invalidInputValues])('expect([-10, -10, %p, 10])', (input) => {
expect([-10, -10, input, 10]).not.isValid2DBoundingBox()
expect([-10, -10, input, 10]).not.isValidBoundingBox()
})
})

describe('Expect to fail with bad northern value:', () => {
test.each([...invalidInputValues])('expect([-10, -10, 10, %p])', (input) => {
expect([-10, -10, 10, input]).not.isValid2DBoundingBox()
expect([-10, -10, 10, input]).not.isValidBoundingBox()
})
})

describe('Expect to fail with bad inputs for all values:', () => {
test.each([...invalidInputValues])('expect([%p, %p, %p, %p])', (input) => {
expect([input, input, input, input]).not.isValid2DBoundingBox()
expect([input, input, input, input]).not.isValidBoundingBox()
})
})
})

describe('Expect to fail when BBox values are arrays of otherwise valid numbers:', () => {
test('expect([[-20], [10], [-10], [20]])', () => {
expect([[-20], [10], [-10], [20]]).not.isValid2DBoundingBox()
const nestedArrays = [[-20], [10], [-10], [20]]
expect(nestedArrays).not.isValid2DBoundingBox()
expect(nestedArrays).not.isValidBoundingBox()
})
})
})
Expand Down
23 changes: 20 additions & 3 deletions tests/boundingBoxes/isValid3DBoundingBox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,14 @@ describe('Valid Use Cases', () => {
describe('Expect to pass with good coordinates:', () => {
test.each([...goodBBoxes])('expect(%p)', (bboxArray) => {
expect(bboxArray).isValid3DBoundingBox()
expect(bboxArray).isValidBoundingBox()
})
})

describe('Expect to pass with good boundary coordinates:', () => {
test.each([...goodBoundaryCoordinates])('expect(%p)', (bboxArray) => {
expect(bboxArray).isValid3DBoundingBox()
expect(bboxArray).isValidBoundingBox()
})
})
})
Expand All @@ -91,6 +93,7 @@ describe('Inalid Use Cases', () => {
describe('Expect to fail with bad inputs:', () => {
test.each([...invalidInputValues])('expect(%p)', (badInput) => {
expect(badInput).not.isValid3DBoundingBox()
expect(badInput).not.isValidBoundingBox()
})
})

Expand All @@ -112,66 +115,80 @@ describe('Inalid Use Cases', () => {
describe('Expect to fail with out of range coordinate:', () => {
test.each([...coordinatesOutOfRange])('expect(%p)', (coordinate) => {
expect(coordinate).not.isValid3DBoundingBox()
expect(coordinate).not.isValidBoundingBox()
})
})

describe('Expect to fail with illogical BBox:', () => {
test('Northern boundary less than southern: expect([-10, 20, 0, 10, -20, 0])', () => {
expect([-10, 20, 0, 10, -20, 0]).not.isValid3DBoundingBox()
const illogicalBBox = [-10, 20, 0, 10, -20, 0]
expect(illogicalBBox).not.isValid3DBoundingBox()
expect(illogicalBBox).not.isValidBoundingBox()
})

test('Altitude less than depth: expect([-10, -20, 200, 20, 10, 150])', () => {
expect([-10, -20, 200, 20, 10, 150]).not.isValid3DBoundingBox()
const illogicalBBox = [-10, -20, 200, 20, 10, 150]
expect(illogicalBBox).not.isValid3DBoundingBox()
expect(illogicalBBox).not.isValidBoundingBox()
})
})

describe('Passing Bad Individual Coordinate Values', () => {
describe('Expect to fail with bad western value:', () => {
test.each([...invalidInputValues])('expect([%p, -10, 0, 10, 10, 0])', (input) => {
expect([input, -10, 0, 10, 10, 0]).not.isValid3DBoundingBox()
expect([input, -10, 0, 10, 10, 0]).not.isValidBoundingBox()
})
})

describe('Expect to fail with bad southern value:', () => {
test.each([...invalidInputValues])('expect([-10, %p, 0, 10, 10, 0])', (input) => {
expect([-10, input, 0, 10, 10, 0]).not.isValid3DBoundingBox()
expect([-10, input, 0, 10, 10, 0]).not.isValidBoundingBox()
})
})

describe('Expect to fail with bad depth value:', () => {
test.each([...invalidAltitudeValues])('expect([-10, -10, %p, 10, 10, 0])', (input) => {
expect([-10, -10, input, 10, 10, 0]).not.isValid3DBoundingBox()
expect([-10, -10, input, 10, 10, 0]).not.isValidBoundingBox()
})
})

describe('Expect to fail with bad eastern value:', () => {
test.each([...invalidInputValues])('expect([-10, -10, 0, %p, 10, 0])', (input) => {
expect([-10, -10, 0, input, 10, 0]).not.isValid3DBoundingBox()
expect([-10, -10, 0, input, 10, 0]).not.isValidBoundingBox()
})
})

describe('Expect to fail with bad northern value:', () => {
test.each([...invalidInputValues])('expect([-10, -10, 0, 10, %p, 0])', (input) => {
expect([-10, -10, 0, 10, input, 0]).not.isValid3DBoundingBox()
expect([-10, -10, 0, 10, input, 0]).not.isValidBoundingBox()
})
})

describe('Expect to fail with bad altitude value:', () => {
test.each([...invalidAltitudeValues])('expect([-10, -10, 0, 10, 10, %p])', (input) => {
expect([-10, -10, 0, 10, 10, input]).not.isValid3DBoundingBox()
expect([-10, -10, 0, 10, 10, input]).not.isValidBoundingBox()
})
})

describe('Expect to fail with bad inputs for all values:', () => {
test.each([...invalidInputValues])('expect([%p, %p, %p, %p, %p, %p])', (input) => {
expect([input, input, input, input, input, input]).not.isValid3DBoundingBox()
expect([input, input, input, input, input, input]).not.isValidBoundingBox()
})
})
})

describe('Expect to fail when BBox values are arrays of otherwise valid numbers:', () => {
test('expect([[-20], [10], [0], [-10], [20], [0]])', () => {
expect([[-20], [10], [0], [-10], [20], [0]]).not.isValid3DBoundingBox()
const nestedArrays = [[-20], [10], [0], [-10], [20], [0]]
expect(nestedArrays).not.isValid3DBoundingBox()
expect(nestedArrays).not.isValidBoundingBox()
})
})
})
Expand Down
Loading

0 comments on commit 9a8b7ed

Please sign in to comment.