|
1 | | -import { isISBN } from 'validator'; |
| 1 | +import { isEAN, isISBN } from 'validator'; |
2 | 2 | import { describe, expect, it } from 'vitest'; |
3 | 3 | import { faker } from '../../src'; |
4 | 4 | import { seededTests } from '../support/seeded-runs'; |
5 | 5 | import { times } from './../support/times'; |
6 | 6 |
|
7 | 7 | const NON_SEEDED_BASED_RUN = 5; |
8 | 8 |
|
| 9 | +/** |
| 10 | + * Helper function to verify UPC check digit |
| 11 | + * |
| 12 | + * @param upc The UPC string to verify. |
| 13 | + */ |
| 14 | +function verifyUPCCheckDigit(upc: string): boolean { |
| 15 | + if (!/^\d{12}$/.test(upc)) { |
| 16 | + return false; |
| 17 | + } |
| 18 | + |
| 19 | + return isEAN(`0${upc}`); |
| 20 | +} |
| 21 | + |
9 | 22 | describe('commerce', () => { |
10 | 23 | seededTests(faker, 'commerce', (t) => { |
11 | 24 | t.itEach( |
@@ -46,6 +59,14 @@ describe('commerce', () => { |
46 | 59 | }) |
47 | 60 | .it('with space separators', { separator: ' ' }); |
48 | 61 | }); |
| 62 | + |
| 63 | + t.describe('upc', (t) => { |
| 64 | + t.it('noArgs') |
| 65 | + .it('with empty prefix', { prefix: '' }) |
| 66 | + .it('with single digit prefix', { prefix: '0' }) |
| 67 | + .it('with 5 digit prefix', { prefix: '01234' }) |
| 68 | + .it('with 11 digit prefix', { prefix: '01234567890' }); |
| 69 | + }); |
49 | 70 | }); |
50 | 71 |
|
51 | 72 | describe.each(times(NON_SEEDED_BASED_RUN).map(() => faker.seed()))( |
@@ -247,6 +268,161 @@ describe('commerce', () => { |
247 | 268 | expect(isbn).toSatisfy((isbn: string) => isISBN(isbn, 13)); |
248 | 269 | }); |
249 | 270 | }); |
| 271 | + |
| 272 | + describe(`upc()`, () => { |
| 273 | + it('should return a 12-digit UPC-A string when not passing arguments', () => { |
| 274 | + const upc = faker.commerce.upc(); |
| 275 | + |
| 276 | + expect(upc).toBeTruthy(); |
| 277 | + expect(upc).toBeTypeOf('string'); |
| 278 | + expect(upc, 'UPC should be exactly 12 digits').toHaveLength(12); |
| 279 | + expect(upc, 'UPC should contain only digits').toMatch(/^\d{12}$/); |
| 280 | + expect( |
| 281 | + verifyUPCCheckDigit(upc), |
| 282 | + 'UPC check digit should be valid' |
| 283 | + ).toBe(true); |
| 284 | + }); |
| 285 | + |
| 286 | + it('should return a 12-digit UPC-A string with empty prefix', () => { |
| 287 | + const upc = faker.commerce.upc({ prefix: '' }); |
| 288 | + |
| 289 | + expect(upc).toBeTruthy(); |
| 290 | + expect(upc).toBeTypeOf('string'); |
| 291 | + expect(upc, 'UPC should be exactly 12 digits').toHaveLength(12); |
| 292 | + expect(upc, 'UPC should contain only digits').toMatch(/^\d{12}$/); |
| 293 | + expect( |
| 294 | + verifyUPCCheckDigit(upc), |
| 295 | + 'UPC check digit should be valid' |
| 296 | + ).toBe(true); |
| 297 | + }); |
| 298 | + |
| 299 | + it('should return a 12-digit UPC-A string with single digit prefix', () => { |
| 300 | + const prefix = '0'; |
| 301 | + const upc = faker.commerce.upc({ prefix }); |
| 302 | + |
| 303 | + expect(upc).toBeTruthy(); |
| 304 | + expect(upc).toBeTypeOf('string'); |
| 305 | + expect(upc, 'UPC should be exactly 12 digits').toHaveLength(12); |
| 306 | + expect(upc, 'UPC should contain only digits').toMatch(/^\d{12}$/); |
| 307 | + expect( |
| 308 | + upc.startsWith(prefix), |
| 309 | + 'UPC should start with the provided prefix' |
| 310 | + ).toBe(true); |
| 311 | + expect( |
| 312 | + verifyUPCCheckDigit(upc), |
| 313 | + 'UPC check digit should be valid' |
| 314 | + ).toBe(true); |
| 315 | + }); |
| 316 | + |
| 317 | + it('should return a 12-digit UPC-A string with 5-digit prefix', () => { |
| 318 | + const prefix = '01234'; |
| 319 | + const upc = faker.commerce.upc({ prefix }); |
| 320 | + |
| 321 | + expect(upc).toBeTruthy(); |
| 322 | + expect(upc).toBeTypeOf('string'); |
| 323 | + expect(upc, 'UPC should be exactly 12 digits').toHaveLength(12); |
| 324 | + expect(upc, 'UPC should contain only digits').toMatch(/^\d{12}$/); |
| 325 | + expect( |
| 326 | + upc.startsWith(prefix), |
| 327 | + 'UPC should start with the provided prefix' |
| 328 | + ).toBe(true); |
| 329 | + expect( |
| 330 | + verifyUPCCheckDigit(upc), |
| 331 | + 'UPC check digit should be valid' |
| 332 | + ).toBe(true); |
| 333 | + }); |
| 334 | + |
| 335 | + it('should return a 12-digit UPC-A string with 11-digit prefix', () => { |
| 336 | + const prefix = '01234567890'; |
| 337 | + const upc = faker.commerce.upc({ prefix }); |
| 338 | + |
| 339 | + expect(upc).toBe('012345678905'); |
| 340 | + }); |
| 341 | + |
| 342 | + it('should handle prefix with leading zeros', () => { |
| 343 | + const prefix = '00000'; |
| 344 | + const upc = faker.commerce.upc({ prefix }); |
| 345 | + |
| 346 | + expect(upc).toBeTruthy(); |
| 347 | + expect(upc, 'UPC should be exactly 12 digits').toHaveLength(12); |
| 348 | + expect(upc.startsWith(prefix)).toBe(true); |
| 349 | + expect(verifyUPCCheckDigit(upc)).toBe(true); |
| 350 | + }); |
| 351 | + |
| 352 | + it('should generate valid UPCs with various prefix lengths', () => { |
| 353 | + const prefixLengths = [0, 1, 2, 5, 8, 11]; |
| 354 | + |
| 355 | + for (const length of prefixLengths) { |
| 356 | + const prefix = length > 0 ? '0'.repeat(length) : ''; |
| 357 | + const upc = faker.commerce.upc({ prefix }); |
| 358 | + |
| 359 | + expect( |
| 360 | + upc, |
| 361 | + `UPC with prefix length ${length} should be 12 digits` |
| 362 | + ).toHaveLength(12); |
| 363 | + expect( |
| 364 | + verifyUPCCheckDigit(upc), |
| 365 | + `UPC with prefix length ${length} should have valid check digit` |
| 366 | + ).toBe(true); |
| 367 | + if (prefix) { |
| 368 | + expect( |
| 369 | + upc.startsWith(prefix), |
| 370 | + `UPC should start with prefix of length ${length}` |
| 371 | + ).toBe(true); |
| 372 | + } |
| 373 | + } |
| 374 | + }); |
| 375 | + |
| 376 | + it('should throw FakerError when prefix contains non-digit characters', () => { |
| 377 | + expect(() => { |
| 378 | + faker.commerce.upc({ prefix: 'abc' }); |
| 379 | + }).toThrow('Prefix must contain only numeric digits'); |
| 380 | + |
| 381 | + expect(() => { |
| 382 | + faker.commerce.upc({ prefix: '123abc' }); |
| 383 | + }).toThrow('Prefix must contain only numeric digits'); |
| 384 | + |
| 385 | + expect(() => { |
| 386 | + faker.commerce.upc({ prefix: '12-34' }); |
| 387 | + }).toThrow('Prefix must contain only numeric digits'); |
| 388 | + |
| 389 | + expect(() => { |
| 390 | + faker.commerce.upc({ prefix: ' 123' }); |
| 391 | + }).toThrow('Prefix must contain only numeric digits'); |
| 392 | + }); |
| 393 | + |
| 394 | + it('should throw FakerError when prefix is longer than 11 digits', () => { |
| 395 | + expect(() => { |
| 396 | + faker.commerce.upc({ prefix: '012345678901' }); |
| 397 | + }).toThrow('Prefix must be at most 11 numeric digits'); |
| 398 | + |
| 399 | + expect(() => { |
| 400 | + faker.commerce.upc({ prefix: '012345678901234' }); |
| 401 | + }).toThrow('Prefix must be at most 11 numeric digits'); |
| 402 | + }); |
| 403 | + |
| 404 | + it('should throw FakerError with correct error message for invalid prefix types', () => { |
| 405 | + expect(() => { |
| 406 | + faker.commerce.upc({ prefix: '12a' }); |
| 407 | + }).toThrow('Prefix must contain only numeric digits'); |
| 408 | + |
| 409 | + expect(() => { |
| 410 | + faker.commerce.upc({ prefix: '012345678901' }); |
| 411 | + }).toThrow('Prefix must be at most 11 numeric digits'); |
| 412 | + }); |
| 413 | + |
| 414 | + it('should generate valid UPCs that pass check digit validation for multiple calls', () => { |
| 415 | + const results = faker.helpers.multiple(() => faker.commerce.upc(), { |
| 416 | + count: 100, |
| 417 | + }); |
| 418 | + |
| 419 | + for (const upc of results) { |
| 420 | + expect(upc).toHaveLength(12); |
| 421 | + expect(upc).toMatch(/^\d{12}$/); |
| 422 | + expect(verifyUPCCheckDigit(upc)).toBe(true); |
| 423 | + } |
| 424 | + }); |
| 425 | + }); |
250 | 426 | } |
251 | 427 | ); |
252 | 428 | }); |
0 commit comments