[ { "name": "category A, monthly, 2026-27: 8% over the primary threshold, 15% over the secondary", "args": [ { "minor": 300000, "currency": "GBP" }, "A", "monthly", "2026-05-28", null ], "expect": { "employee": { "minor": 15616, "currency": "GBP" }, "employer": { "minor": 38745, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "above the UEL: exact method gives 277.17 and 759.94 where HMRC's printed tables give 277.16 and 759.90", "args": [ { "minor": 548329, "currency": "GBP" }, "A", "monthly", "2025-06-27", null ], "expect": { "employee": { "minor": 27717, "currency": "GBP" }, "employer": { "minor": 75994, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "a half penny rounds up, once, on the total: 251.285 becomes 251.29", "args": [ { "minor": 418925, "currency": "GBP" }, "A", "monthly", "2026-06-26", null ], "expect": { "employee": { "minor": 25129, "currency": "GBP" }, "employer": { "minor": 56584, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "below the lower earnings limit: no employee NI, but employer NI above the \u00a396 secondary threshold", "args": [ { "minor": 10000, "currency": "GBP" }, "A", "weekly", "2026-06-05", null ], "expect": { "employee": { "minor": 0, "currency": "GBP" }, "employer": { "minor": 60, "currency": "GBP" }, "lowerEarningsLimitReached": false } }, { "name": "exactly the lower earnings limit counts as reaching it", "args": [ { "minor": 12900, "currency": "GBP" }, "A", "weekly", "2026-06-05", null ], "expect": { "employee": { "minor": 0, "currency": "GBP" }, "employer": { "minor": 495, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "fortnightly uses twice the weekly thresholds", "args": [ { "minor": 100000, "currency": "GBP" }, "A", "fortnightly", "2026-06-05", null ], "expect": { "employee": { "minor": 4128, "currency": "GBP" }, "employer": { "minor": 12120, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "four-weekly uses four times the weekly thresholds", "args": [ { "minor": 200000, "currency": "GBP" }, "A", "four-weekly", "2026-06-05", null ], "expect": { "employee": { "minor": 8256, "currency": "GBP" }, "employer": { "minor": 24240, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "category B married women's reduced rate 1.85%", "args": [ { "minor": 200000, "currency": "GBP" }, "B", "monthly", "2026-07-28", null ], "expect": { "employee": { "minor": 1761, "currency": "GBP" }, "employer": { "minor": 23745, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "category C over State Pension age: no employee NI, employer still pays", "args": [ { "minor": 50000, "currency": "GBP" }, "C", "weekly", "2026-07-03", null ], "expect": { "employee": { "minor": 0, "currency": "GBP" }, "employer": { "minor": 6060, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "category M under 21: employer pays only above the upper secondary threshold", "args": [ { "minor": 500000, "currency": "GBP" }, "M", "monthly", "2026-07-28", null ], "expect": { "employee": { "minor": 26750, "currency": "GBP" }, "employer": { "minor": 12165, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "category F freeport: employer pays only above the \u00a325,000 freeport threshold", "args": [ { "minor": 300000, "currency": "GBP" }, "F", "monthly", "2026-07-28", null ], "expect": { "employee": { "minor": 15616, "currency": "GBP" }, "employer": { "minor": 13755, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "category H apprentice under 25 in 2024-25: no employer NI below the upper secondary threshold", "args": [ { "minor": 60000, "currency": "GBP" }, "H", "weekly", "2024-10-04", null ], "expect": { "employee": { "minor": 2864, "currency": "GBP" }, "employer": { "minor": 0, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "category J deferment in 2025-26: 2% main rate", "args": [ { "minor": 50000, "currency": "GBP" }, "J", "weekly", "2025-07-04", null ], "expect": { "employee": { "minor": 516, "currency": "GBP" }, "employer": { "minor": 6060, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "category Z in 2025-26: 2% employee, no employer NI below the UST", "args": [ { "minor": 300000, "currency": "GBP" }, "Z", "monthly", "2025-07-28", null ], "expect": { "employee": { "minor": 3904, "currency": "GBP" }, "employer": { "minor": 0, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "2024-25: 8% employee, 13.8% employer over a \u00a3758 monthly secondary threshold", "args": [ { "minor": 250000, "currency": "GBP" }, "A", "monthly", "2024-07-26", null ], "expect": { "employee": { "minor": 11616, "currency": "GBP" }, "employer": { "minor": 24040, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "2023-24 before 6 January 2024: 12% main rate", "args": [ { "minor": 250000, "currency": "GBP" }, "A", "monthly", "2023-12-28", null ], "expect": { "employee": { "minor": 17424, "currency": "GBP" }, "employer": { "minor": 24040, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "2023-24 from 6 January 2024: 10% main rate", "args": [ { "minor": 250000, "currency": "GBP" }, "A", "monthly", "2024-01-26", null ], "expect": { "employee": { "minor": 14520, "currency": "GBP" }, "employer": { "minor": 24040, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "the day the 2023-24 main rate fell: 10% to the weekly UEL, 2% above it", "args": [ { "minor": 100000, "currency": "GBP" }, "A", "weekly", "2024-01-06", null ], "expect": { "employee": { "minor": 7316, "currency": "GBP" }, "employer": { "minor": 11385, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "director annual method 2026-27: NI on the year to date less what was already paid", "args": [ { "minor": 2000000, "currency": "GBP" }, "A", "monthly", "2026-09-30", { "previousEarnings": { "minor": 1000000, "currency": "GBP" }, "previousEmployee": { "minor": 0, "currency": "GBP" }, "previousEmployer": { "minor": 75000, "currency": "GBP" } } ], "expect": { "employee": { "minor": 139440, "currency": "GBP" }, "employer": { "minor": 300000, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "director annual method 2023-24 uses the whole-year 11.5% rate", "args": [ { "minor": 6000000, "currency": "GBP" }, "A", "monthly", "2023-06-30", { "previousEarnings": { "minor": 0, "currency": "GBP" }, "previousEmployee": { "minor": 0, "currency": "GBP" }, "previousEmployer": { "minor": 0, "currency": "GBP" } } ], "expect": { "employee": { "minor": 453010, "currency": "GBP" }, "employer": { "minor": 702420, "currency": "GBP" }, "lowerEarningsLimitReached": true } }, { "name": "director below the annual thresholds pays nothing yet", "args": [ { "minor": 500000, "currency": "GBP" }, "A", "monthly", "2026-05-28", { "previousEarnings": { "minor": 0, "currency": "GBP" }, "previousEmployee": { "minor": 0, "currency": "GBP" }, "previousEmployer": { "minor": 0, "currency": "GBP" } } ], "expect": { "employee": { "minor": 0, "currency": "GBP" }, "employer": { "minor": 0, "currency": "GBP" }, "lowerEarningsLimitReached": false } }, { "name": "category N did not exist in 2023-24", "args": [ { "minor": 250000, "currency": "GBP" }, "N", "monthly", "2023-12-28", null ], "expectError": "no National Insurance rates for category N" }, { "name": "an unknown category letter is an error", "args": [ { "minor": 250000, "currency": "GBP" }, "Q", "monthly", "2026-05-28", null ], "expectError": "no National Insurance rates for category Q" }, { "name": "a date before the rules this package carries", "args": [ { "minor": 250000, "currency": "GBP" }, "A", "monthly", "2023-04-05", null ], "expectError": "no National Insurance thresholds" }, { "name": "negative earnings are refused", "args": [ { "minor": -100, "currency": "GBP" }, "A", "monthly", "2026-05-28", null ], "expectError": "earnings must not be negative" }, { "name": "another currency is refused", "args": [ { "minor": 100, "currency": "EUR" }, "A", "monthly", "2026-05-28", null ], "expectError": "must be in GBP" }, { "name": "an unknown frequency is refused", "args": [ { "minor": 100, "currency": "GBP" }, "A", "daily", "2026-05-28", null ], "expectError": "unknown pay frequency" }, { "name": "a trailing newline is not part of an ISO date", "args": [ { "minor": 300000, "currency": "GBP" }, "A", "monthly", "2026-05-28\n", null ], "expectError": "payDate must be an ISO date" }, { "name": "Arabic-Indic digits are not an ISO date", "args": [ { "minor": 300000, "currency": "GBP" }, "A", "monthly", "٢٠٢٦-٠٥-٢٨", null ], "expectError": "payDate must be an ISO date" } ]