matchers/features/toHaveID.js

const { hasID } = require('../../core/features/hasID')

// eslint-disable-next-line jsdoc/require-returns
/**
 * Checks if a GeoJSON Feature has an ID. Passes if the Feature object has any ID (no argument provided), or
 * if the ID exactly matches the optional argument (single string, number, or RegExp provided), or any value
 * within an array of any combination of strings, numbers, or RegExp.
 *
 * The test fails if the object does not have an ID, or if it has an ID that does not match the SearchID.
 *
 * @memberof Matchers.Features
 * @see https://github.com/M-Scott-Lassiter/jest-geojson/issues/36
 * @param {object} featureObject any GeoJSON Feature object
 * @param {string|number|RegExp|string[]|number[]|RegExp[]} [SearchID] Specific value or array of possible values
 * to search for.
 * @example
 * const testFeature1 = {
 *     type: 'Feature',
 *     id: 'f1',
 *     geometry: {...},
 *     properties: {...}
 * }
 *
 * test('Feature Has an ID', () => {
 *     expect(testFeature1).toHaveID()
 *     expect(testFeature1).toHaveID([])
 *     expect(testFeature1).toHaveID('f1')
 *     expect(testFeature1).toHaveID([1, 'F', 'F12', /[a-z]+[0-9]+/])
 *
 *     expect(testFeature1).not.toHaveID('f2')
 *     expect(testFeature1).not.toHaveID([1, 'F', 'F12', /SomeID/])
 * })
 * @example
 * const testFeature2 = {
 *     type: 'Feature',
 *     geometry: {...},
 *     properties: {...}
 * }
 *
 * test('Feature Does not Have an ID', () => {
 *     expect(testFeature2).not.toHaveID()
 *     expect(testFeature2).not.toHaveID(2)
 * })
 */
function toHaveID(featureObject, SearchID) {
    const { printReceived, matcherHint } = this.utils
    const optionalIDMessage = () => {
        if (SearchID !== undefined && SearchID?.length !== 0) {
            return ` of ${SearchID}`
        }
        return ''
    }
    const passMessage =
        // eslint-disable-next-line prefer-template
        matcherHint('.not.toHaveID', 'FeatureObject', 'SearchID') +
        '\n\n' +
        `Expected input to not be a valid GeoJSON Feature object with ID` +
        optionalIDMessage() +
        `.\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('.toHaveID', 'FeatureObject', 'SearchID') +
            '\n\n' +
            `${errorMessage}\n\n` +
            `Received:  ${printReceived(featureObject)}`
        )
    }

    let idIsPresent
    // Provide error handling in case of invalid inputs to the matcher
    try {
        idIsPresent = hasID(featureObject, SearchID)
    } catch (err) {
        return { pass: false, message: () => failMessage(err.message) }
    }

    // Input was valid, now return either pass or fail
    if (idIsPresent) {
        return { pass: true, message: () => passMessage }
    }
    return { pass: false, message: () => failMessage(`Did not find the ID ${SearchID}.`) }
}

exports.toHaveID = toHaveID