All files / src/validation/address zipValidator.ts

94.73% Statements 18/19
88.46% Branches 23/26
100% Functions 5/5
94.73% Lines 18/19

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78                                                          1666x   1666x     1666x 1178x     614x       1666x 1238x   1238x 1178x           60x 60x 4x       2x   4x         56x 56x 56x               1666x    
import { VALIDATION_MESSAGES } from "@validation/messages";
import { US_ZIP_REGEX, getPostalRegexForCountry } from "@validation/regex";
import * as Yup from "yup";
 
/**
 * options:
 * - required: boolean (default false)
 * - countryField: string ("country" by default)
 * - usFormatOnly: boolean (force US format)
 * - nonUSValidator: Yup schema for non-US ZIPs
 * - message: optional custom error message
 */
export function buildZipSchema(
  options: {
    required?: boolean;
    countryField?: string;
    usFormatOnly?: boolean;
    nonUSValidator?: () => Yup.StringSchema;
    message?: string;
    allowNull?: boolean;
  } = {},
) {
  const {
    required = false,
    countryField = "country",
    usFormatOnly = false,
    nonUSValidator,
    message = VALIDATION_MESSAGES.INVALID_ZIP_SHORT,
    allowNull = true,
  } = options;
 
  let base = allowNull ? Yup.string().nullable() : Yup.string();
 
  /** Force US-only format (used in several variations) */
  const applyUSFormat = (schema: Yup.StringSchema) =>
    schema.test(
      "us-zip-format",
      message,
      (value) => !value || US_ZIP_REGEX.test(value),
    );
 
  /** Main country-based conditional */
  base = base.when(countryField, (country, schema) => {
    const isUS = String(country)?.toUpperCase() === "US";
 
    if (isUS || usFormatOnly) {
      return applyUSFormat(
        required ? schema.required(VALIDATION_MESSAGES.ZIP_REQUIRED) : schema,
      );
    }
 
    // Non-US: try country-specific regex first, then nonUSValidator, then bare fallback
    const countryRegex = getPostalRegexForCountry(String(country));
    if (countryRegex) {
      const withFormat = schema.test(
        "postal-format",
        message,
        (value: string | undefined | null) =>
          !value || countryRegex.test(value),
      );
      return required
        ? withFormat.required(VALIDATION_MESSAGES.ZIP_REQUIRED)
        : withFormat;
    }
 
    Eif (nonUSValidator) {
      const nonUS = nonUSValidator();
      return required
        ? nonUS.required(VALIDATION_MESSAGES.ZIP_REQUIRED)
        : nonUS;
    }
 
    return schema; // fallback — country not recognised, no format enforced
  });
 
  return base;
}