[
{
"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"
}
]