Skip to content

Commit

Permalink
feat(isValidCoordinate): add new matcher function
Browse files Browse the repository at this point in the history
This combines the functionality of `isValid2DCoordinate` and `isValid3DCoordinate`. Tests for this
matcher are included with tests for the individual 2D and 3D matchers.

Resolves: #4
  • Loading branch information
M-Scott-Lassiter committed May 24, 2022
1 parent ea42aed commit d7e5b70
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 79 deletions.
29 changes: 29 additions & 0 deletions src/core/coordinates/validCoordinate.js
@@ -0,0 +1,29 @@
const { valid2DCoordinate } = require('./valid2DCoordinate')
const { valid3DCoordinate } = require('./valid3DCoordinate')

/**
* A helper function used to verify a coordinate has appropriate longitude, latitude, and altitude values.
*
* @memberof Core.Coordinates
* @param {GeoJSON2DCoordinate|GeoJSON3DCoordinate} coordinate A WGS-84 array of [longitude, latitude] or [longitude, latitude, alititude]
* @returns {boolean} True if a valid 3D GeoJSON coordinate. If invalid, it will throw an error.
// * @throws {Error} Input must be an array of only three elments
// * @throws {Error} Altitude value must be numeric
*/
function validCoordinate(coordinate) {
if (!Array.isArray(coordinate) || coordinate.length < 2 || coordinate.length > 3) {
throw new Error('Input must be an array of either two or three elments.')
}

if (coordinate.length === 2) {
valid2DCoordinate(coordinate)
}

if (coordinate.length === 3) {
valid3DCoordinate(coordinate)
}

return true
}

exports.validCoordinate = validCoordinate
1 change: 1 addition & 0 deletions src/index.js
Expand Up @@ -56,6 +56,7 @@ exports.isValid2DCoordinate =
require('./matchers/coordinates/isValid2DCoordinate').isValid2DCoordinate
exports.isValid3DCoordinate =
require('./matchers/coordinates/isValid3DCoordinate').isValid3DCoordinate
exports.isValidCoordinate = require('./matchers/coordinates/isValidCoordinate').isValidCoordinate
// Features

// Geometries
Expand Down
5 changes: 3 additions & 2 deletions src/matchers/coordinates/isValid2DCoordinate.js
Expand Up @@ -12,8 +12,9 @@ const { valid2DCoordinate } = require('../../core/coordinates/valid2DCoordinate'
* expect([22, 45.733]).isValid2DCoordinate()
* expect([180, 90]).isValid2DCoordinate()
* @example
* expect([22, 100.56]).not.isValid2DCoordinate()
* expect([22, 45.733, 0]).not.isValid2DCoordinate()
* expect([22, 100.56]).not.isValid2DCoordinate() // Latitude out of range
* expect([22, 45.733, 0]).not.isValid2DCoordinate() //3D coordinate
* // Nested Arrays
* expect([[22, 45.733, 0]]).not.isValid2DCoordinate()
* expect([[22, 45.733], [180, 90]]).not.isValid2DCoordinate()
*/
Expand Down
6 changes: 4 additions & 2 deletions src/matchers/coordinates/isValid3DCoordinate.js
Expand Up @@ -15,8 +15,10 @@ const { valid3DCoordinate } = require('../../core/coordinates/valid3DCoordinate'
* expect([22, 45.733, 20]).isValid3DCoordinate()
* expect([180, 90, -10000]).isValid3DCoordinate()
* @example
* expect([22, 100.56]).not.isValid3DCoordinate()
* expect([22, 45.733, '0']).not.isValid3DCoordinate()
* expect([22, 100.56, 0]).not.isValid3DCoordinate() // Latitude out of range
* expect([22, 45.733]).isValid3DCoordinate() // 2D coordinate
* expect([22, 45.733, '0']).not.isValid3DCoordinate() // Non-numeric altitude
* // Nested Arrays
* expect([[22, 45.733, 0]]).not.isValid3DCoordinate()
* expect([[22, 45.733, 0], [180, 90, 0]]).not.isValid3DCoordinate()
*/
Expand Down
62 changes: 62 additions & 0 deletions src/matchers/coordinates/isValidCoordinate.js
@@ -0,0 +1,62 @@
const { validCoordinate } = require('../../core/coordinates/validCoordinate')

// eslint-disable-next-line jsdoc/require-returns
/**
* Verifies a two or three element coordinate meets WGS-84 and GeoJSON validity requirements.
*
* @memberof Coordinates
* @param {number[]} coordinateArray A two or three element array of numbers in format
* [longitude, latitude] or [longitude, latitude, altitude].
*
* Longitude must be between -180 to 180.
* Latitude must be between -90 to 90.
* Altitude must be a number between -Infinity to Infinity.
* The standard does not specify units altitude represents (i.e. meters, feet, etc.).
* @example
* expect([22, 45.733]).isValidCoordinate()
* expect([180, 90]).isValidCoordinate()
* expect([22, 45.733, 20]).isValidCoordinate()
* expect([180, 90, -10000]).isValidCoordinate()
* @example
* expect([220, 56]).not.isValidCoordinate() // Longitude out of range
* expect([22, 45.733, '0']).not.isValidCoordinate()
* // Nested Arrays
* expect([[22, 45.733, 0]]).not.isValidCoordinate()
* expect([[22, 45.733, 0], [180, 90, 0]]).not.isValidCoordinate()
*/
function isValidCoordinate(coordinateArray) {
const { printReceived, matcherHint } = this.utils
const passMessage =
// eslint-disable-next-line prefer-template
matcherHint('.not.isValidCoordinate', '[longitude, latitude, (altitude)]', '') +
'\n\n' +
`Expected input to not be a two or three element array with longitude between (-90 to 90),
latitude between (-180 to 180), and (if a 3D coordinate) numeric altitude.\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('.isValidCoordinate', '[longitude, latitude, (altitude)]', '') +
'\n\n' +
`${errorMessage}\n\n` +
`Received: ${printReceived(coordinateArray)}`
)
}

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

exports.isValidCoordinate = isValidCoordinate
35 changes: 35 additions & 0 deletions tests/coordinates/__snapshots__/isValidCoordinate.test.js.snap
@@ -0,0 +1,35 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Error Snapshot Testing. Throws error: expect([0, 0, 0]).isValidCoordinate 1`] = `
"expect([longitude, latitude, (altitude)]).isValidCoordinate()
Latitude must be a number between -90 and 90.
Received: [0, 95, 0]"
`;

exports[`Error Snapshot Testing. Throws error: expect([0, 0, 0]).not.isValidCoordinate 1`] = `
"expect([longitude, latitude, (altitude)]).not.isValidCoordinate()
Expected input to not be a two or three element array with longitude between (-90 to 90),
latitude between (-180 to 180), and (if a 3D coordinate) numeric altitude.
Received: [0, 0, 0]"
`;

exports[`Error Snapshot Testing. Throws error: expect([0, 0]).isValidCoordinate 1`] = `
"expect([longitude, latitude, (altitude)]).isValidCoordinate()
Latitude must be a number between -90 and 90.
Received: [0, 95]"
`;

exports[`Error Snapshot Testing. Throws error: expect([0, 0]).not.isValidCoordinate 1`] = `
"expect([longitude, latitude, (altitude)]).not.isValidCoordinate()
Expected input to not be a two or three element array with longitude between (-90 to 90),
latitude between (-180 to 180), and (if a 3D coordinate) numeric altitude.
Received: [0, 0]"
`;
70 changes: 39 additions & 31 deletions tests/coordinates/isValid2DCoordinate.test.js
@@ -1,33 +1,33 @@
const goodCoordinates = [
[0, 0],
[102.0, 0.5],
[172.0, -15],
[-10.9, 77],
[-152.0, -33.33333]
[[0, 0]],
[[102.0, 0.5]],
[[172.0, -15]],
[[-10.9, 77]],
[[-152.0, -33.33333]]
]
const goodBoundaryCoordinates = [
[180, 0],
[-180, 0],
[0, 90],
[0, -90],
[180, 90],
[180, -90],
[-180, 90],
[-180, -90]
[[180, 0]],
[[-180, 0]],
[[0, 90]],
[[0, -90]],
[[180, 90]],
[[180, -90]],
[[-180, 90]],
[[-180, -90]]
]
const coordinatesOutOfRange = [
[0, 90.0000001],
[0, -90.0000001],
[0, 900000],
[0, -900000],
[180.0000001, 0],
[-180.0000001, 0],
[1800000, 0],
[-1800000, 0],
[181, 91],
[181, -91],
[-181, 91],
[-181, -91]
[[0, 90.0000001]],
[[0, -90.0000001]],
[[0, 900000]],
[[0, -900000]],
[[180.0000001, 0]],
[[-180.0000001, 0]],
[[1800000, 0]],
[[-1800000, 0]],
[[181, 91]],
[[181, -91]],
[[-181, 91]],
[[-181, -91]]
]
const invalidInputValues = [
undefined,
Expand All @@ -47,14 +47,16 @@ const invalidInputValues = [
]
describe('Valid Use Cases', () => {
describe('Expect to pass with good coordinates:', () => {
test.each([...goodCoordinates])('expect([%p, %p])', (longitude, latitude) => {
expect([longitude, latitude]).isValid2DCoordinate()
test.each([...goodCoordinates])('expect(%p)', (coordinate) => {
expect(coordinate).isValid2DCoordinate()
expect(coordinate).isValidCoordinate()
})
})

describe('Expect to pass with good boundary coordinates:', () => {
test.each([...goodBoundaryCoordinates])('expect([%p, %p])', (longitude, latitude) => {
expect([longitude, latitude]).isValid2DCoordinate()
test.each([...goodBoundaryCoordinates])('expect(%p)', (coordinate) => {
expect(coordinate).isValid2DCoordinate()
expect(coordinate).isValidCoordinate()
})
})
})
Expand All @@ -63,6 +65,7 @@ describe('Inalid Use Cases', () => {
describe('Expect to fail with bad inputs:', () => {
test.each([...invalidInputValues])('expect(%p)', (badInput) => {
expect(badInput).not.isValid2DCoordinate()
expect(badInput).not.isValidCoordinate()
})
})

Expand All @@ -76,27 +79,31 @@ describe('Inalid Use Cases', () => {
})

describe('Expect to fail with out of range coordinate:', () => {
test.each([...coordinatesOutOfRange])('expect([%p, %p])', (longitude, latitude) => {
expect([longitude, latitude]).not.isValid2DCoordinate()
test.each([...coordinatesOutOfRange])('expect(%p)', (coordinate) => {
expect(coordinate).not.isValid2DCoordinate()
expect(coordinate).not.isValidCoordinate()
})
})

describe('Passing Bad Individual Coordinate Values', () => {
describe('Expect to fail with bad longitude value:', () => {
test.each([...invalidInputValues])('expect([%p, 0])', (longitude) => {
expect([longitude, 0]).not.isValid2DCoordinate()
expect([longitude, 0]).not.isValidCoordinate()
})
})

describe('Expect to fail with bad latitude value:', () => {
test.each([...invalidInputValues])('expect([0, %p])', (latitude) => {
expect([0, latitude]).not.isValid2DCoordinate()
expect([0, latitude]).not.isValidCoordinate()
})
})

describe('Expect to fail with bad values for both:', () => {
test.each([...invalidInputValues])('expect(<val>, <val>), <val> = %p', (input) => {
expect([input, input]).not.isValid2DCoordinate()
expect([input, input]).not.isValidCoordinate()
})
})
})
Expand All @@ -109,6 +116,7 @@ describe('Inalid Use Cases', () => {
]
test.each([[testArray], [[testArray]], [[[testArray]]]])('expect(%p)', (badInput) => {
expect([badInput]).not.isValid2DCoordinate()
expect([badInput]).not.isValidCoordinate()
})
})
})
Expand Down

0 comments on commit d7e5b70

Please sign in to comment.