@hy_ong/zod-kit 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +24 -0
- package/CLAUDE.md +64 -22
- package/dist/chunk-2SWEVDFZ.js +134 -0
- package/dist/chunk-32JI34CV.cjs +146 -0
- package/dist/chunk-42C5OHRK.js +71 -0
- package/dist/chunk-46VAH2BJ.js +160 -0
- package/dist/chunk-5JGTDL3Y.js +87 -0
- package/dist/chunk-5LEXCVLX.js +257 -0
- package/dist/chunk-6AAP4LPF.js +2606 -0
- package/dist/chunk-B4EZYZOK.cjs +215 -0
- package/dist/chunk-COYKBWTI.js +161 -0
- package/dist/chunk-DFJZ3NS2.cjs +151 -0
- package/dist/chunk-EDHT4LPO.js +118 -0
- package/dist/chunk-EGHL277K.cjs +165 -0
- package/dist/chunk-ERH4NIMU.cjs +69 -0
- package/dist/chunk-FM3EZ72O.js +165 -0
- package/dist/chunk-GJIRDBZJ.cjs +90 -0
- package/dist/chunk-H2XTEM4M.js +696 -0
- package/dist/chunk-HMSM6FFA.cjs +181 -0
- package/dist/chunk-HTEHINI7.cjs +177 -0
- package/dist/chunk-JOLSGZGN.cjs +696 -0
- package/dist/chunk-JXY7APBU.js +69 -0
- package/dist/chunk-K2UOY6TB.js +136 -0
- package/dist/chunk-KFOHKTFD.js +61 -0
- package/dist/chunk-L4HSIKTU.cjs +135 -0
- package/dist/chunk-LH7ZB4BK.js +124 -0
- package/dist/chunk-LL4ZWLGO.js +90 -0
- package/dist/chunk-M6MTP3NY.cjs +99 -0
- package/dist/chunk-MHJFYYGV.js +215 -0
- package/dist/chunk-MINMXGW3.js +135 -0
- package/dist/chunk-MM7IL2RG.js +181 -0
- package/dist/chunk-OPQJWHXN.cjs +301 -0
- package/dist/chunk-ORFHDJII.cjs +136 -0
- package/dist/chunk-ORVV4MCF.cjs +87 -0
- package/dist/chunk-QICQ6YEY.js +75 -0
- package/dist/chunk-RKUQREMW.js +127 -0
- package/dist/chunk-RO47DKQG.js +146 -0
- package/dist/chunk-RRPXIRTQ.cjs +257 -0
- package/dist/chunk-RYFG2GKM.cjs +118 -0
- package/dist/chunk-STNHTRG7.cjs +124 -0
- package/dist/chunk-TFGS34VD.cjs +71 -0
- package/dist/chunk-TQXDUMML.cjs +61 -0
- package/dist/chunk-UBK3VCVH.cjs +134 -0
- package/dist/chunk-UCOXAZJF.cjs +2606 -0
- package/dist/chunk-UQZKFAFX.js +130 -0
- package/dist/chunk-VB2KV2ZM.cjs +130 -0
- package/dist/chunk-WABKPFPK.js +151 -0
- package/dist/chunk-WDI4QJMQ.js +177 -0
- package/dist/chunk-YDH3L27K.cjs +127 -0
- package/dist/chunk-YIM3D2AD.js +99 -0
- package/dist/chunk-YPSEIDUR.cjs +160 -0
- package/dist/chunk-ZNJLWJX3.cjs +75 -0
- package/dist/chunk-ZTFCJCPO.cjs +161 -0
- package/dist/chunk-ZXUMK2RR.js +301 -0
- package/dist/common/boolean.cjs +7 -0
- package/dist/common/boolean.d.cts +119 -0
- package/dist/common/boolean.d.ts +119 -0
- package/dist/common/boolean.js +7 -0
- package/dist/common/color.cjs +9 -0
- package/dist/common/color.d.cts +26 -0
- package/dist/common/color.d.ts +26 -0
- package/dist/common/color.js +9 -0
- package/dist/common/coordinate.cjs +11 -0
- package/dist/common/coordinate.d.cts +23 -0
- package/dist/common/coordinate.d.ts +23 -0
- package/dist/common/coordinate.js +11 -0
- package/dist/common/credit-card.cjs +11 -0
- package/dist/common/credit-card.d.cts +22 -0
- package/dist/common/credit-card.d.ts +22 -0
- package/dist/common/credit-card.js +11 -0
- package/dist/common/date.cjs +7 -0
- package/dist/common/date.d.cts +174 -0
- package/dist/common/date.d.ts +174 -0
- package/dist/common/date.js +7 -0
- package/dist/common/datetime.cjs +15 -0
- package/dist/common/datetime.d.cts +301 -0
- package/dist/common/datetime.d.ts +301 -0
- package/dist/common/datetime.js +15 -0
- package/dist/common/email.cjs +7 -0
- package/dist/common/email.d.cts +149 -0
- package/dist/common/email.d.ts +149 -0
- package/dist/common/email.js +7 -0
- package/dist/common/file.cjs +7 -0
- package/dist/common/file.d.cts +178 -0
- package/dist/common/file.d.ts +178 -0
- package/dist/common/file.js +7 -0
- package/dist/common/id.cjs +13 -0
- package/dist/common/id.d.cts +288 -0
- package/dist/common/id.d.ts +288 -0
- package/dist/common/id.js +13 -0
- package/dist/common/ip.cjs +11 -0
- package/dist/common/ip.d.cts +25 -0
- package/dist/common/ip.d.ts +25 -0
- package/dist/common/ip.js +11 -0
- package/dist/common/number.cjs +7 -0
- package/dist/common/number.d.cts +167 -0
- package/dist/common/number.d.ts +167 -0
- package/dist/common/number.js +7 -0
- package/dist/common/password.cjs +7 -0
- package/dist/common/password.d.cts +192 -0
- package/dist/common/password.d.ts +192 -0
- package/dist/common/password.js +7 -0
- package/dist/common/text.cjs +7 -0
- package/dist/common/text.d.cts +156 -0
- package/dist/common/text.d.ts +156 -0
- package/dist/common/text.js +7 -0
- package/dist/common/time.cjs +15 -0
- package/dist/common/time.d.cts +268 -0
- package/dist/common/time.d.ts +268 -0
- package/dist/common/time.js +15 -0
- package/dist/common/url.cjs +7 -0
- package/dist/common/url.d.cts +196 -0
- package/dist/common/url.d.ts +196 -0
- package/dist/common/url.js +7 -0
- package/dist/config-CABSSvAp.d.cts +5 -0
- package/dist/config-CABSSvAp.d.ts +5 -0
- package/dist/index.cjs +180 -5255
- package/dist/index.d.cts +28 -3150
- package/dist/index.d.ts +28 -3150
- package/dist/index.js +135 -5131
- package/dist/taiwan/bank-account.cjs +11 -0
- package/dist/taiwan/bank-account.d.cts +22 -0
- package/dist/taiwan/bank-account.d.ts +22 -0
- package/dist/taiwan/bank-account.js +11 -0
- package/dist/taiwan/business-id.cjs +9 -0
- package/dist/taiwan/business-id.d.cts +133 -0
- package/dist/taiwan/business-id.d.ts +133 -0
- package/dist/taiwan/business-id.js +9 -0
- package/dist/taiwan/fax.cjs +9 -0
- package/dist/taiwan/fax.d.cts +157 -0
- package/dist/taiwan/fax.d.ts +157 -0
- package/dist/taiwan/fax.js +9 -0
- package/dist/taiwan/invoice.cjs +9 -0
- package/dist/taiwan/invoice.d.cts +17 -0
- package/dist/taiwan/invoice.d.ts +17 -0
- package/dist/taiwan/invoice.js +9 -0
- package/dist/taiwan/license-plate.cjs +9 -0
- package/dist/taiwan/license-plate.d.cts +19 -0
- package/dist/taiwan/license-plate.d.ts +19 -0
- package/dist/taiwan/license-plate.js +9 -0
- package/dist/taiwan/mobile.cjs +9 -0
- package/dist/taiwan/mobile.d.cts +146 -0
- package/dist/taiwan/mobile.d.ts +146 -0
- package/dist/taiwan/mobile.js +9 -0
- package/dist/taiwan/national-id.cjs +15 -0
- package/dist/taiwan/national-id.d.cts +214 -0
- package/dist/taiwan/national-id.d.ts +214 -0
- package/dist/taiwan/national-id.js +15 -0
- package/dist/taiwan/passport.cjs +9 -0
- package/dist/taiwan/passport.d.cts +19 -0
- package/dist/taiwan/passport.d.ts +19 -0
- package/dist/taiwan/passport.js +9 -0
- package/dist/taiwan/postal-code.cjs +17 -0
- package/dist/taiwan/postal-code.d.cts +237 -0
- package/dist/taiwan/postal-code.d.ts +237 -0
- package/dist/taiwan/postal-code.js +17 -0
- package/dist/taiwan/tel.cjs +9 -0
- package/dist/taiwan/tel.d.cts +162 -0
- package/dist/taiwan/tel.d.ts +162 -0
- package/dist/taiwan/tel.js +9 -0
- package/package.json +132 -6
- package/src/i18n/locales/en-GB.json +51 -0
- package/src/i18n/locales/en-US.json +52 -1
- package/src/i18n/locales/id-ID.json +51 -0
- package/src/i18n/locales/ja-JP.json +51 -0
- package/src/i18n/locales/ko-KR.json +51 -0
- package/src/i18n/locales/ms-MY.json +51 -0
- package/src/i18n/locales/th-TH.json +51 -0
- package/src/i18n/locales/vi-VN.json +51 -0
- package/src/i18n/locales/zh-CN.json +51 -0
- package/src/i18n/locales/zh-TW.json +51 -0
- package/src/index.ts +10 -2
- package/src/validators/common/color.ts +192 -0
- package/src/validators/common/coordinate.ts +159 -0
- package/src/validators/common/credit-card.ts +134 -0
- package/src/validators/common/id.ts +45 -3
- package/src/validators/common/ip.ts +210 -0
- package/src/validators/taiwan/bank-account.ts +176 -0
- package/src/validators/taiwan/invoice.ts +84 -0
- package/src/validators/taiwan/license-plate.ts +110 -0
- package/src/validators/taiwan/passport.ts +103 -0
- package/tests/common/color.test.ts +587 -0
- package/tests/common/coordinate.test.ts +345 -0
- package/tests/common/credit-card.test.ts +378 -0
- package/tests/common/id.test.ts +68 -3
- package/tests/common/ip.test.ts +419 -0
- package/tests/taiwan/bank-account.test.ts +286 -0
- package/tests/taiwan/invoice.test.ts +227 -0
- package/tests/taiwan/license-plate.test.ts +280 -0
- package/tests/taiwan/passport.test.ts +277 -0
- package/tsup.config.ts +36 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest"
|
|
2
|
+
import { twBankAccount, setLocale, validateTaiwanBankAccount, TAIWAN_BANK_CODES } from "../../src"
|
|
3
|
+
|
|
4
|
+
describe("Taiwan twBankAccount(true) validator", () => {
|
|
5
|
+
beforeEach(() => setLocale("en-US"))
|
|
6
|
+
|
|
7
|
+
describe("basic functionality", () => {
|
|
8
|
+
it("should validate combined bank code + account number", () => {
|
|
9
|
+
const schema = twBankAccount(true)
|
|
10
|
+
|
|
11
|
+
expect(schema.parse("004-1234567890")).toBe("004-1234567890")
|
|
12
|
+
expect(schema.parse("808-9876543210")).toBe("808-9876543210")
|
|
13
|
+
expect(schema.parse("822-1234567890123456")).toBe("822-1234567890123456")
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it("should validate account number only (10 digits)", () => {
|
|
17
|
+
const schema = twBankAccount(true)
|
|
18
|
+
|
|
19
|
+
expect(schema.parse("1234567890")).toBe("1234567890")
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it("should validate account number only (16 digits)", () => {
|
|
23
|
+
const schema = twBankAccount(true)
|
|
24
|
+
|
|
25
|
+
expect(schema.parse("1234567890123456")).toBe("1234567890123456")
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it("should validate account numbers with 10-16 digits", () => {
|
|
29
|
+
const schema = twBankAccount(true)
|
|
30
|
+
|
|
31
|
+
expect(schema.parse("1234567890")).toBe("1234567890") // 10 digits
|
|
32
|
+
expect(schema.parse("12345678901")).toBe("12345678901") // 11 digits
|
|
33
|
+
expect(schema.parse("123456789012")).toBe("123456789012") // 12 digits
|
|
34
|
+
expect(schema.parse("1234567890123")).toBe("1234567890123") // 13 digits
|
|
35
|
+
expect(schema.parse("12345678901234")).toBe("12345678901234") // 14 digits
|
|
36
|
+
expect(schema.parse("123456789012345")).toBe("123456789012345") // 15 digits
|
|
37
|
+
expect(schema.parse("1234567890123456")).toBe("1234567890123456") // 16 digits
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it("should reject invalid bank codes (not in list when validateBankCode=true)", () => {
|
|
41
|
+
const schema = twBankAccount(true)
|
|
42
|
+
|
|
43
|
+
expect(() => schema.parse("999-1234567890")).toThrow("Invalid bank code")
|
|
44
|
+
expect(() => schema.parse("000-1234567890")).toThrow("Invalid bank code")
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it("should reject non-numeric bank codes", () => {
|
|
48
|
+
const schema = twBankAccount(true)
|
|
49
|
+
|
|
50
|
+
expect(() => schema.parse("AB1-1234567890")).toThrow("Invalid bank code")
|
|
51
|
+
expect(() => schema.parse("A12-1234567890")).toThrow("Invalid bank code")
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it("should reject account numbers with fewer than 10 digits", () => {
|
|
55
|
+
const schema = twBankAccount(true)
|
|
56
|
+
|
|
57
|
+
expect(() => schema.parse("004-123456789")).toThrow("Invalid account number")
|
|
58
|
+
expect(() => schema.parse("123456789")).toThrow("Invalid account number")
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("should reject account numbers with more than 16 digits", () => {
|
|
62
|
+
const schema = twBankAccount(true)
|
|
63
|
+
|
|
64
|
+
expect(() => schema.parse("004-12345678901234567")).toThrow("Invalid account number")
|
|
65
|
+
expect(() => schema.parse("12345678901234567")).toThrow("Invalid account number")
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe("validateBankCode option", () => {
|
|
70
|
+
it("should allow any 3-digit bank code when validateBankCode is false", () => {
|
|
71
|
+
const schema = twBankAccount(true, { validateBankCode: false })
|
|
72
|
+
|
|
73
|
+
expect(schema.parse("999-1234567890")).toBe("999-1234567890")
|
|
74
|
+
expect(schema.parse("000-1234567890")).toBe("000-1234567890")
|
|
75
|
+
expect(schema.parse("123-1234567890")).toBe("123-1234567890")
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it("should still reject non-numeric bank codes when validateBankCode is false", () => {
|
|
79
|
+
const schema = twBankAccount(true, { validateBankCode: false })
|
|
80
|
+
|
|
81
|
+
expect(() => schema.parse("AB1-1234567890")).toThrow("Invalid bank code")
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe("bankCode option", () => {
|
|
86
|
+
it("should prepend bank code when value has no hyphen", () => {
|
|
87
|
+
const schema = twBankAccount(true, { bankCode: "004" })
|
|
88
|
+
|
|
89
|
+
expect(schema.parse("1234567890")).toBe("004-1234567890")
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it("should not prepend bank code when value already has a hyphen", () => {
|
|
93
|
+
const schema = twBankAccount(true, { bankCode: "004" })
|
|
94
|
+
|
|
95
|
+
expect(schema.parse("808-1234567890")).toBe("808-1234567890")
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe("utility function validateTaiwanBankAccount", () => {
|
|
100
|
+
it("should return true for valid bank account formats", () => {
|
|
101
|
+
expect(validateTaiwanBankAccount("004-1234567890")).toBe(true)
|
|
102
|
+
expect(validateTaiwanBankAccount("808-9876543210")).toBe(true)
|
|
103
|
+
expect(validateTaiwanBankAccount("1234567890")).toBe(true)
|
|
104
|
+
expect(validateTaiwanBankAccount("1234567890123456")).toBe(true)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it("should return false for invalid bank account formats", () => {
|
|
108
|
+
expect(validateTaiwanBankAccount("999-1234567890")).toBe(false) // invalid bank code
|
|
109
|
+
expect(validateTaiwanBankAccount("AB1-1234567890")).toBe(false) // non-numeric bank code
|
|
110
|
+
expect(validateTaiwanBankAccount("004-123456789")).toBe(false) // 9 digits
|
|
111
|
+
expect(validateTaiwanBankAccount("004-12345678901234567")).toBe(false) // 17 digits
|
|
112
|
+
expect(validateTaiwanBankAccount("")).toBe(false)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it("should respect validateBankCode parameter", () => {
|
|
116
|
+
expect(validateTaiwanBankAccount("999-1234567890", true)).toBe(false)
|
|
117
|
+
expect(validateTaiwanBankAccount("999-1234567890", false)).toBe(true)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it("should reject malformed hyphenated input", () => {
|
|
121
|
+
expect(validateTaiwanBankAccount("004-123-456")).toBe(false) // multiple hyphens
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
describe("TAIWAN_BANK_CODES", () => {
|
|
126
|
+
it("should contain known major bank codes", () => {
|
|
127
|
+
expect(TAIWAN_BANK_CODES["004"]).toBe("台灣銀行")
|
|
128
|
+
expect(TAIWAN_BANK_CODES["808"]).toBe("玉山")
|
|
129
|
+
expect(TAIWAN_BANK_CODES["822"]).toBe("中信")
|
|
130
|
+
expect(TAIWAN_BANK_CODES["700"]).toBe("中華郵政")
|
|
131
|
+
expect(TAIWAN_BANK_CODES["812"]).toBe("台新")
|
|
132
|
+
expect(TAIWAN_BANK_CODES["013"]).toBe("國泰世華")
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it("should not contain invalid bank codes", () => {
|
|
136
|
+
expect(TAIWAN_BANK_CODES["999"]).toBeUndefined()
|
|
137
|
+
expect(TAIWAN_BANK_CODES["000"]).toBeUndefined()
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it("should have string values for all entries", () => {
|
|
141
|
+
for (const [code, name] of Object.entries(TAIWAN_BANK_CODES)) {
|
|
142
|
+
expect(code).toMatch(/^\d{3}$/)
|
|
143
|
+
expect(typeof name).toBe("string")
|
|
144
|
+
expect(name.length).toBeGreaterThan(0)
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
describe("required/optional behavior", () => {
|
|
150
|
+
it("should handle required=true", () => {
|
|
151
|
+
const schema = twBankAccount(true)
|
|
152
|
+
|
|
153
|
+
expect(() => schema.parse("")).toThrow("Required")
|
|
154
|
+
expect(() => schema.parse(null)).toThrow()
|
|
155
|
+
expect(() => schema.parse(undefined)).toThrow()
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it("should handle required=false", () => {
|
|
159
|
+
const schema = twBankAccount(false)
|
|
160
|
+
|
|
161
|
+
expect(schema.parse("")).toBe(null)
|
|
162
|
+
expect(schema.parse(null)).toBe(null)
|
|
163
|
+
expect(schema.parse(undefined)).toBe(null)
|
|
164
|
+
expect(schema.parse("004-1234567890")).toBe("004-1234567890")
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it("should use default values", () => {
|
|
168
|
+
const requiredSchema = twBankAccount(true, { defaultValue: "004-1234567890" })
|
|
169
|
+
const optionalSchema = twBankAccount(false, { defaultValue: "004-1234567890" })
|
|
170
|
+
|
|
171
|
+
expect(requiredSchema.parse("")).toBe("004-1234567890")
|
|
172
|
+
expect(optionalSchema.parse("")).toBe("004-1234567890")
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
describe("transform function", () => {
|
|
177
|
+
it("should apply custom transform", () => {
|
|
178
|
+
const schema = twBankAccount(true, {
|
|
179
|
+
transform: (val) => val.replace(/\s+/g, ""),
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
expect(schema.parse("004 - 1234567890")).toBe("004-1234567890")
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it("should apply transform before validation", () => {
|
|
186
|
+
const schema = twBankAccount(true, {
|
|
187
|
+
transform: (val) => val.replace(/\s+/g, ""),
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
expect(schema.parse(" 004-1234567890 ")).toBe("004-1234567890")
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
describe("input preprocessing", () => {
|
|
195
|
+
it("should trim whitespace", () => {
|
|
196
|
+
const schema = twBankAccount(true)
|
|
197
|
+
|
|
198
|
+
expect(schema.parse(" 004-1234567890 ")).toBe("004-1234567890")
|
|
199
|
+
expect(schema.parse("\t1234567890\n")).toBe("1234567890")
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
it("should strip internal spaces", () => {
|
|
203
|
+
const schema = twBankAccount(true)
|
|
204
|
+
|
|
205
|
+
expect(schema.parse("004-12345 67890")).toBe("004-1234567890")
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
describe("i18n support", () => {
|
|
210
|
+
it("should use English messages by default", () => {
|
|
211
|
+
setLocale("en-US")
|
|
212
|
+
const schema = twBankAccount(true)
|
|
213
|
+
|
|
214
|
+
expect(() => schema.parse("")).toThrow("Required")
|
|
215
|
+
expect(() => schema.parse("999-1234567890")).toThrow("Invalid bank code")
|
|
216
|
+
expect(() => schema.parse("004-123456789")).toThrow("Invalid account number")
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it("should use Chinese messages when locale is zh-TW", () => {
|
|
220
|
+
setLocale("zh-TW")
|
|
221
|
+
const schema = twBankAccount(true)
|
|
222
|
+
|
|
223
|
+
expect(() => schema.parse("")).toThrow("必填")
|
|
224
|
+
expect(() => schema.parse("999-1234567890")).toThrow("無效的銀行代碼")
|
|
225
|
+
expect(() => schema.parse("004-123456789")).toThrow("無效的帳號號碼")
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it("should support custom i18n messages", () => {
|
|
229
|
+
const schema = twBankAccount(true, {
|
|
230
|
+
i18n: {
|
|
231
|
+
"en-US": {
|
|
232
|
+
required: "Bank account is required",
|
|
233
|
+
invalidBankCode: "Bad bank code",
|
|
234
|
+
invalidAccountNumber: "Bad account number",
|
|
235
|
+
},
|
|
236
|
+
"zh-TW": {
|
|
237
|
+
required: "請輸入銀行帳號",
|
|
238
|
+
invalidBankCode: "銀行代碼有誤",
|
|
239
|
+
invalidAccountNumber: "帳號有誤",
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
setLocale("en-US")
|
|
245
|
+
expect(() => schema.parse("")).toThrow("Bank account is required")
|
|
246
|
+
expect(() => schema.parse("999-1234567890")).toThrow("Bad bank code")
|
|
247
|
+
expect(() => schema.parse("004-123456789")).toThrow("Bad account number")
|
|
248
|
+
|
|
249
|
+
setLocale("zh-TW")
|
|
250
|
+
expect(() => schema.parse("")).toThrow("請輸入銀行帳號")
|
|
251
|
+
expect(() => schema.parse("999-1234567890")).toThrow("銀行代碼有誤")
|
|
252
|
+
expect(() => schema.parse("004-123456789")).toThrow("帳號有誤")
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
describe("edge cases", () => {
|
|
257
|
+
it("should handle empty and whitespace inputs", () => {
|
|
258
|
+
const schema = twBankAccount(true)
|
|
259
|
+
const optionalSchema = twBankAccount(false)
|
|
260
|
+
|
|
261
|
+
expect(() => schema.parse("")).toThrow("Required")
|
|
262
|
+
expect(() => schema.parse(" ")).toThrow("Required")
|
|
263
|
+
expect(() => schema.parse("\t\n")).toThrow("Required")
|
|
264
|
+
|
|
265
|
+
expect(optionalSchema.parse("")).toBe(null)
|
|
266
|
+
expect(optionalSchema.parse(" ")).toBe(null)
|
|
267
|
+
expect(optionalSchema.parse("\t\n")).toBe(null)
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it("should handle boundary account lengths", () => {
|
|
271
|
+
const schema = twBankAccount(true)
|
|
272
|
+
|
|
273
|
+
// Exactly 10 digits (minimum)
|
|
274
|
+
expect(schema.parse("1234567890")).toBe("1234567890")
|
|
275
|
+
// Exactly 16 digits (maximum)
|
|
276
|
+
expect(schema.parse("1234567890123456")).toBe("1234567890123456")
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it("should reject non-digit account numbers", () => {
|
|
280
|
+
const schema = twBankAccount(true)
|
|
281
|
+
|
|
282
|
+
expect(() => schema.parse("004-123456789A")).toThrow("Invalid account number")
|
|
283
|
+
expect(() => schema.parse("abcdefghij")).toThrow("Invalid account number")
|
|
284
|
+
})
|
|
285
|
+
})
|
|
286
|
+
})
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest"
|
|
2
|
+
import { twInvoice, setLocale, validateTaiwanInvoice } from "../../src"
|
|
3
|
+
|
|
4
|
+
describe("Taiwan twInvoice(true) validator", () => {
|
|
5
|
+
beforeEach(() => setLocale("en-US"))
|
|
6
|
+
|
|
7
|
+
describe("basic functionality", () => {
|
|
8
|
+
it("should validate correct Taiwan invoice numbers", () => {
|
|
9
|
+
const schema = twInvoice(true)
|
|
10
|
+
|
|
11
|
+
expect(schema.parse("AB12345678")).toBe("AB12345678")
|
|
12
|
+
expect(schema.parse("CD99887766")).toBe("CD99887766")
|
|
13
|
+
expect(schema.parse("ZZ00000000")).toBe("ZZ00000000")
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it("should auto-strip hyphens from input", () => {
|
|
17
|
+
const schema = twInvoice(true)
|
|
18
|
+
|
|
19
|
+
expect(schema.parse("CD-12345678")).toBe("CD12345678")
|
|
20
|
+
expect(schema.parse("AB-99887766")).toBe("AB99887766")
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it("should auto-uppercase lowercase input", () => {
|
|
24
|
+
const schema = twInvoice(true)
|
|
25
|
+
|
|
26
|
+
expect(schema.parse("ab12345678")).toBe("AB12345678")
|
|
27
|
+
expect(schema.parse("cd-12345678")).toBe("CD12345678")
|
|
28
|
+
expect(schema.parse("Ab12345678")).toBe("AB12345678")
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it("should reject input with only 1 letter", () => {
|
|
32
|
+
const schema = twInvoice(true)
|
|
33
|
+
|
|
34
|
+
expect(() => schema.parse("A123456789")).toThrow("Invalid Taiwan uniform invoice number")
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("should reject input with 3 letters", () => {
|
|
38
|
+
const schema = twInvoice(true)
|
|
39
|
+
|
|
40
|
+
expect(() => schema.parse("ABC1234567")).toThrow("Invalid Taiwan uniform invoice number")
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it("should reject input with 7 digits", () => {
|
|
44
|
+
const schema = twInvoice(true)
|
|
45
|
+
|
|
46
|
+
expect(() => schema.parse("AB1234567")).toThrow("Invalid Taiwan uniform invoice number")
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it("should reject input with 9 digits", () => {
|
|
50
|
+
const schema = twInvoice(true)
|
|
51
|
+
|
|
52
|
+
expect(() => schema.parse("AB123456789")).toThrow("Invalid Taiwan uniform invoice number")
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it("should reject digits before letters", () => {
|
|
56
|
+
const schema = twInvoice(true)
|
|
57
|
+
|
|
58
|
+
expect(() => schema.parse("12AB345678")).toThrow("Invalid Taiwan uniform invoice number")
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("should reject non-alphanumeric inputs", () => {
|
|
62
|
+
const schema = twInvoice(true)
|
|
63
|
+
|
|
64
|
+
expect(() => schema.parse("AB@#345678")).toThrow("Invalid Taiwan uniform invoice number")
|
|
65
|
+
expect(() => schema.parse("!!12345678")).toThrow("Invalid Taiwan uniform invoice number")
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe("utility function validateTaiwanInvoice", () => {
|
|
70
|
+
it("should return true for valid invoice numbers", () => {
|
|
71
|
+
expect(validateTaiwanInvoice("AB12345678")).toBe(true)
|
|
72
|
+
expect(validateTaiwanInvoice("CD99887766")).toBe(true)
|
|
73
|
+
expect(validateTaiwanInvoice("ZZ00000000")).toBe(true)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it("should handle hyphens in input", () => {
|
|
77
|
+
expect(validateTaiwanInvoice("AB-12345678")).toBe(true)
|
|
78
|
+
expect(validateTaiwanInvoice("CD-99887766")).toBe(true)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it("should return false for invalid invoice numbers", () => {
|
|
82
|
+
expect(validateTaiwanInvoice("A123456789")).toBe(false)
|
|
83
|
+
expect(validateTaiwanInvoice("ABC1234567")).toBe(false)
|
|
84
|
+
expect(validateTaiwanInvoice("AB1234567")).toBe(false)
|
|
85
|
+
expect(validateTaiwanInvoice("AB123456789")).toBe(false)
|
|
86
|
+
expect(validateTaiwanInvoice("12AB345678")).toBe(false)
|
|
87
|
+
expect(validateTaiwanInvoice("12345678")).toBe(false)
|
|
88
|
+
expect(validateTaiwanInvoice("")).toBe(false)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it("should be case-sensitive (lowercase returns false)", () => {
|
|
92
|
+
expect(validateTaiwanInvoice("ab12345678")).toBe(false)
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe("required/optional behavior", () => {
|
|
97
|
+
it("should handle required=true", () => {
|
|
98
|
+
const schema = twInvoice(true)
|
|
99
|
+
|
|
100
|
+
expect(() => schema.parse("")).toThrow("Required")
|
|
101
|
+
expect(() => schema.parse(null)).toThrow()
|
|
102
|
+
expect(() => schema.parse(undefined)).toThrow()
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it("should handle required=false", () => {
|
|
106
|
+
const schema = twInvoice(false)
|
|
107
|
+
|
|
108
|
+
expect(schema.parse("")).toBe(null)
|
|
109
|
+
expect(schema.parse(null)).toBe(null)
|
|
110
|
+
expect(schema.parse(undefined)).toBe(null)
|
|
111
|
+
expect(schema.parse("AB12345678")).toBe("AB12345678")
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it("should use default values", () => {
|
|
115
|
+
const requiredSchema = twInvoice(true, { defaultValue: "AB12345678" })
|
|
116
|
+
const optionalSchema = twInvoice(false, { defaultValue: "AB12345678" })
|
|
117
|
+
|
|
118
|
+
expect(requiredSchema.parse("")).toBe("AB12345678")
|
|
119
|
+
expect(optionalSchema.parse("")).toBe("AB12345678")
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
describe("transform function", () => {
|
|
124
|
+
it("should apply custom transform", () => {
|
|
125
|
+
const schema = twInvoice(true, {
|
|
126
|
+
transform: (val) => val.replace(/\s+/g, ""),
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
expect(schema.parse("AB 12345678")).toBe("AB12345678")
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it("should apply transform before validation", () => {
|
|
133
|
+
const schema = twInvoice(true, {
|
|
134
|
+
transform: (val) => val.replace(/\s+/g, ""),
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
expect(schema.parse(" AB12345678 ")).toBe("AB12345678")
|
|
138
|
+
expect(() => schema.parse("AB 1234 567")).toThrow("Invalid Taiwan uniform invoice number")
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
describe("input preprocessing", () => {
|
|
143
|
+
it("should handle string conversion", () => {
|
|
144
|
+
const schema = twInvoice(true)
|
|
145
|
+
|
|
146
|
+
// Pure digits have no letter prefix, so they fail validation
|
|
147
|
+
expect(() => schema.parse(12345678)).toThrow("Invalid Taiwan uniform invoice number")
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it("should trim whitespace", () => {
|
|
151
|
+
const schema = twInvoice(true)
|
|
152
|
+
|
|
153
|
+
expect(schema.parse(" AB12345678 ")).toBe("AB12345678")
|
|
154
|
+
expect(schema.parse("\tAB12345678\n")).toBe("AB12345678")
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
describe("i18n support", () => {
|
|
159
|
+
it("should use English messages by default", () => {
|
|
160
|
+
setLocale("en-US")
|
|
161
|
+
const schema = twInvoice(true)
|
|
162
|
+
|
|
163
|
+
expect(() => schema.parse("")).toThrow("Required")
|
|
164
|
+
expect(() => schema.parse("INVALID")).toThrow("Invalid Taiwan uniform invoice number")
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it("should use Chinese messages when locale is zh-TW", () => {
|
|
168
|
+
setLocale("zh-TW")
|
|
169
|
+
const schema = twInvoice(true)
|
|
170
|
+
|
|
171
|
+
expect(() => schema.parse("")).toThrow("必填")
|
|
172
|
+
expect(() => schema.parse("INVALID")).toThrow("無效的統一發票號碼")
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it("should support custom i18n messages", () => {
|
|
176
|
+
const schema = twInvoice(true, {
|
|
177
|
+
i18n: {
|
|
178
|
+
"en-US": {
|
|
179
|
+
required: "Invoice number is required",
|
|
180
|
+
invalid: "Invoice number is invalid",
|
|
181
|
+
},
|
|
182
|
+
"zh-TW": {
|
|
183
|
+
required: "請輸入發票號碼",
|
|
184
|
+
invalid: "發票號碼格式錯誤",
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
setLocale("en-US")
|
|
190
|
+
expect(() => schema.parse("")).toThrow("Invoice number is required")
|
|
191
|
+
expect(() => schema.parse("INVALID")).toThrow("Invoice number is invalid")
|
|
192
|
+
|
|
193
|
+
setLocale("zh-TW")
|
|
194
|
+
expect(() => schema.parse("")).toThrow("請輸入發票號碼")
|
|
195
|
+
expect(() => schema.parse("INVALID")).toThrow("發票號碼格式錯誤")
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
describe("edge cases", () => {
|
|
200
|
+
it("should handle empty and whitespace inputs", () => {
|
|
201
|
+
const schema = twInvoice(true)
|
|
202
|
+
const optionalSchema = twInvoice(false)
|
|
203
|
+
|
|
204
|
+
expect(() => schema.parse("")).toThrow("Required")
|
|
205
|
+
expect(() => schema.parse(" ")).toThrow("Required")
|
|
206
|
+
expect(() => schema.parse("\t\n")).toThrow("Required")
|
|
207
|
+
|
|
208
|
+
expect(optionalSchema.parse("")).toBe(null)
|
|
209
|
+
expect(optionalSchema.parse(" ")).toBe(null)
|
|
210
|
+
expect(optionalSchema.parse("\t\n")).toBe(null)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it("should handle boundary letter/digit combinations", () => {
|
|
214
|
+
const schema = twInvoice(true)
|
|
215
|
+
|
|
216
|
+
expect(schema.parse("AA00000000")).toBe("AA00000000")
|
|
217
|
+
expect(schema.parse("ZZ99999999")).toBe("ZZ99999999")
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it("should reject special characters mixed in", () => {
|
|
221
|
+
const schema = twInvoice(true)
|
|
222
|
+
|
|
223
|
+
expect(() => schema.parse("AB!2345678")).toThrow("Invalid Taiwan uniform invoice number")
|
|
224
|
+
expect(() => schema.parse("AB 2345678")).toThrow("Invalid Taiwan uniform invoice number")
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
})
|