Skip to content

Commit

Permalink
feat(isValid3DCoordinate): add new matcher function
Browse files Browse the repository at this point in the history
This matcher verifies that a 3D GeoJSON coordinate is appropriately formatted and in range. It makes
use of existing functions from the isValid2DCoordinate matcher.

Resolves: #2
  • Loading branch information
M-Scott-Lassiter committed May 19, 2022
1 parent 7a2184a commit 0329231
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 2 deletions.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
{
"name": "jest-geojson",
"version": "0.1.0",
"version": "0.2.0",
"description": "Additional Jest matchers for GeoJSON",
"keywords": [
"jest",
"jest-tests",
"jest-geojson",
"geojson",
"matchers",
"jest-matchers",
"test",
"testing",
"assertions"
Expand Down
3 changes: 2 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
// Coordinates
exports.isValid2DCoordinate =
require('./matchers/coordinates/isValid2DCoordinate').isValid2DCoordinate

exports.isValid3DCoordinate =
require('./matchers/coordinates/isValid3DCoordinate').isValid3DCoordinate
// Features

// Geometries
Expand Down
76 changes: 76 additions & 0 deletions src/matchers/coordinates/isValid3DCoordinate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const { valid2D } = require('./isValid2DCoordinate')
/**
* A helper function used to verify a coordinate has appropriate longitude, latitude, and altitude values.
*
* @memberof Helpers
* @private
* @ignore
* @param {GeoJSON-Coordinate} coordinate A WGS-84 array of [longitude, latitude, alititude]
* @returns {boolean} True if a valid 3D GeoJSON coordinate, false otherwise
*/
function valid3D(coordinate) {
if (!Array.isArray(coordinate) || coordinate.length !== 3) {
return false
}

// The first two elements have to match the same validity requirements as a 2D coordinate.
// Reuse the logic from that function.
if (!valid2D([coordinate[0], coordinate[1]])) {
return false
}

if (typeof coordinate[2] !== 'number') {
return false
}

// eslint-disable-next-line no-self-compare
if (coordinate[2] !== coordinate[2]) {
// Accounts for NaN
return false
}
return true
}

/**
* Verifies a three element coordinate meets WGS-84 and GeoJSON validity requirements.
*
* @memberof Coordinates
* @param {number[]} coordinateArray A three element array of numbers in format [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 it represents (i.e. meters, feet, etc.).
* @returns {JestMatchingObject} Test passed or failed
* @example
* 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, 45.733, 0]]).not.isValid3DCoordinate()
* expect([[22, 45.733, 0], [180, 90, 0]]).not.isValid3DCoordinate()
*/
function isValid3DCoordinate(coordinateArray) {
const { printReceived, matcherHint } = this.utils
const passMessage =
// eslint-disable-next-line prefer-template
matcherHint('.not.isValid3DCoordinate', '[longitude, latitude]', '') +
'\n\n' +
`Expected input to not be a three element array with longitude between (-90 to 90) and latitude between (-180 to 180) and numeric altitude.\n\n` +
`Received: ${printReceived(coordinateArray)}`

const failMessage =
// eslint-disable-next-line prefer-template
matcherHint('.isValid3DCoordinate', '[longitude, latitude]', '') +
'\n\n' +
`Expected a three element array with longitude between (-90 to 90) and latitude between (-180 to 180) and numeric altitude.\n\n` +
`Received: ${printReceived(coordinateArray)}`

if (valid3D(coordinateArray)) {
return { pass: true, message: () => passMessage }
}
return { pass: false, message: () => failMessage }
}

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

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

exports[`Error Snapshot Testing. Throws error: expect([0, 0]).isValid3DCoordinate 1`] = `
"expect([longitude, latitude]).isValid3DCoordinate()
Expected a three element array with longitude between (-90 to 90) and latitude between (-180 to 180) and numeric altitude.
Received: [0, 0]"
`;
152 changes: 152 additions & 0 deletions tests/coordinates/isValid3DCoordinate.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
const goodCoordinates = [
[0, 0, 0],
[102.0, 0.5, 1000],
[172.0, -15, -1000],
[-10.9, 77, 5000],
[-152.0, -33.33333, -5000]
]
const goodBoundaryCoordinates = [
[180, 0, Infinity],
[-180, 0, Infinity],
[0, 90, Infinity],
[0, -90, Infinity],
[180, 90, -Infinity],
[180, -90, -Infinity],
[-180, 90, -Infinity],
[-180, -90, -Infinity]
]
const coordinatesOutOfRange = [
[0, 90.0000001, 0],
[0, -90.0000001, 0],
[0, 900000, 0],
[0, -900000, 0],
[180.0000001, 0, 0],
[-180.0000001, 0, 0],
[1800000, 0, 0],
[-1800000, 0, 0],
[181, 91, 0],
[181, -91, 0],
[-181, 91, 0],
[-181, -91, 0]
]
const invalidInputValues = [
undefined,
null,
true,
false,
200,
-200,
Infinity,
-Infinity,
NaN,
{ coordinates: [0, 0, 0] },
'',
'Random Coordinate',
'[0, 0, 0]',
'[[0, 0, 0], [0, 0, 0]]'
]
const InvalidAltitudeValues = [
undefined,
null,
true,
false,
NaN,
{ coordinates: [0, 0, 0] },
'',
'Random Coordinate',
'[0, 0, 0]',
'[[0, 0, 0], [0, 0, 0]]'
]

describe('Valid Use Cases', () => {
describe('Expect to pass with good coordinates:', () => {
test.each([...goodCoordinates])('expect([%p, %p, %p])', (longitude, latitude, altitude) => {
expect([longitude, latitude, altitude]).isValid3DCoordinate()
})
})

describe('Expect to pass with good boundary coordinates:', () => {
test.each([...goodBoundaryCoordinates])(
'expect([%p, %p, %p])',
(longitude, latitude, altitude) => {
expect([longitude, latitude, altitude]).isValid3DCoordinate()
}
)
})
})

describe('Invalid Use Cases', () => {
describe('Expect to fail with bad inputs:', () => {
test.each([...invalidInputValues])('expect(%p)', (badInput) => {
expect(badInput).not.isValid3DCoordinate()
})
})

describe('Expect to fail with incorrect number of array elements:', () => {
test.each([[[]], [[20]], [[20, 30]], [[20, 30, 0, 20, 30, 0, 20, 30, 0]]])(
'expect(%p)',
(badInput) => {
expect(badInput).not.isValid3DCoordinate()
}
)
})

describe('Expect to fail with out of range lon/lat coordinates:', () => {
test.each([...coordinatesOutOfRange])(
'expect([%p, %p, %p])',
(longitude, latitude, altitude) => {
expect([longitude, latitude, altitude]).not.isValid3DCoordinate()
}
)
})

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

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

describe('Expect to fail with bad altitude value:', () => {
test.each([...InvalidAltitudeValues])('expect([0, 0, %p])', (altitude) => {
expect([0, 0, altitude]).not.isValid3DCoordinate()
})
})

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

describe('Expect to fail when arrays are nested too deeply:', () => {
const testArray = [
[10, 20],
[2, 90],
[95, 5]
]
test.each([[testArray], [[testArray]], [[[testArray]]]])('expect(%p)', (badInput) => {
expect([badInput]).not.isValid2DCoordinate()
})
})
})

describe('Error Snapshot Testing. Throws error:', () => {
test('expect([0, 0, 0]).not.isValid3DCoordinate', () => {
expect(() => expect([0, 0, 0]).not.isValid3DCoordinate()).toThrowErrorMatchingSnapshot()
})

test('expect([0, 0]).isValid3DCoordinate', () => {
expect(() => expect([0, 0]).isValid3DCoordinate()).toThrowErrorMatchingSnapshot()
})
})

0 comments on commit 0329231

Please sign in to comment.