@aidc-toolkit/gs1 0.0.1

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.
@@ -0,0 +1,1033 @@
1
+ import { I18NEnvironment, i18nInit } from "@aidc-toolkit/core";
2
+ import { CharacterSetCreator, Exclusion, IterationHelper, NUMERIC_CREATOR } from "@aidc-toolkit/utility";
3
+ import { describe, expect, test } from "vitest";
4
+ import {
5
+ AI39_CREATOR,
6
+ AI82_CREATOR,
7
+ CharacterSet,
8
+ CPID_VALIDATOR,
9
+ GCN_VALIDATOR,
10
+ GDTI_VALIDATOR,
11
+ GIAI_VALIDATOR,
12
+ GINC_VALIDATOR,
13
+ GLN_VALIDATOR,
14
+ GMN_VALIDATOR,
15
+ GRAI_VALIDATOR,
16
+ GSIN_VALIDATOR,
17
+ GSRN_VALIDATOR,
18
+ GTIN12_VALIDATOR,
19
+ GTIN13_VALIDATOR,
20
+ GTIN8_VALIDATOR, GTIN_VALIDATORS,
21
+ GTINCreator,
22
+ GTINLevel,
23
+ GTINType,
24
+ type GTINValidator,
25
+ hasValidCheckCharacterPair,
26
+ hasValidCheckDigit,
27
+ IdentificationKeyType,
28
+ type IdentificationKeyValidator,
29
+ LeaderType,
30
+ type NonGTINNumericIdentificationKeyCreator,
31
+ type NonGTINNumericIdentificationKeyValidator,
32
+ type NonNumericIdentificationKeyCreator,
33
+ type NonNumericIdentificationKeyValidator,
34
+ type NumericIdentificationKeyCreator,
35
+ type NumericIdentificationKeyValidator,
36
+ PrefixManager,
37
+ PrefixType,
38
+ type SerializableNumericIdentificationKeyCreator,
39
+ type SerializableNumericIdentificationKeyValidator,
40
+ SSCC_VALIDATOR
41
+ } from "../src/index.js";
42
+
43
+ await i18nInit(I18NEnvironment.CLI, true);
44
+
45
+ // eslint-disable-next-line jsdoc/require-jsdoc
46
+ function creatorFor(characterSet: CharacterSet): CharacterSetCreator {
47
+ let creator: CharacterSetCreator;
48
+
49
+ switch (characterSet) {
50
+ case CharacterSet.Numeric:
51
+ creator = NUMERIC_CREATOR;
52
+ break;
53
+
54
+ case CharacterSet.AI82:
55
+ creator = AI82_CREATOR;
56
+ break;
57
+
58
+ case CharacterSet.AI39:
59
+ creator = AI39_CREATOR;
60
+ break;
61
+ }
62
+
63
+ return creator;
64
+ }
65
+
66
+ // eslint-disable-next-line jsdoc/require-jsdoc
67
+ function validateIdentificationKeyValidator(creator: IdentificationKeyValidator, identificationKeyType: IdentificationKeyType, prefixType: PrefixType, length: number): void {
68
+ expect(creator.identificationKeyType).toBe(identificationKeyType);
69
+ expect(creator.prefixType).toBe(prefixType);
70
+ expect(creator.length).toBe(length);
71
+ }
72
+
73
+ // eslint-disable-next-line jsdoc/require-jsdoc
74
+ function validateNumericIdentificationKeyValidator(validator: NumericIdentificationKeyValidator, isCreator: boolean, identificationKeyType: IdentificationKeyType, prefixType: PrefixType, length: number, leaderType: LeaderType): void {
75
+ validateIdentificationKeyValidator(validator, identificationKeyType, prefixType, length);
76
+
77
+ expect(validator.leaderType).toBe(leaderType);
78
+ expect(validator.referenceCharacterSet).toBe(CharacterSet.Numeric);
79
+ expect(validator.referenceValidator).toBe(NUMERIC_CREATOR);
80
+
81
+ if (isCreator) {
82
+ expect((validator as NumericIdentificationKeyCreator).referenceCreator).toBe(NUMERIC_CREATOR);
83
+ }
84
+ }
85
+
86
+ // eslint-disable-next-line jsdoc/require-jsdoc
87
+ function validateGTINValidator(validator: GTINValidator, isCreator: boolean, gtinType: GTINType): void {
88
+ let prefixType: PrefixType;
89
+
90
+ switch (gtinType) {
91
+ case GTINType.GTIN13:
92
+ prefixType = PrefixType.GS1CompanyPrefix;
93
+ break;
94
+
95
+ case GTINType.GTIN12:
96
+ prefixType = PrefixType.UPCCompanyPrefix;
97
+ break;
98
+
99
+ case GTINType.GTIN8:
100
+ prefixType = PrefixType.GS18Prefix;
101
+ break;
102
+
103
+ default:
104
+ throw new Error("Not supported");
105
+ }
106
+
107
+ validateNumericIdentificationKeyValidator(validator, isCreator, IdentificationKeyType.GTIN, prefixType, gtinType, LeaderType.IndicatorDigit);
108
+
109
+ expect(validator.gtinType).toBe(gtinType);
110
+ }
111
+
112
+ // eslint-disable-next-line jsdoc/require-jsdoc
113
+ function validateNonGTINNumericIdentificationKeyValidator<T extends NonGTINNumericIdentificationKeyValidator>(validator: T, isCreator: boolean, identificationKeyType: IdentificationKeyType, length: number, leaderType: LeaderType): void {
114
+ validateNumericIdentificationKeyValidator(validator, isCreator, identificationKeyType, PrefixType.GS1CompanyPrefix, length, leaderType);
115
+ }
116
+
117
+ // eslint-disable-next-line jsdoc/require-jsdoc
118
+ function validateSerializableNumericIdentificationKeyValidator(validator: SerializableNumericIdentificationKeyValidator, isCreator: boolean, identificationKeyType: IdentificationKeyType, length: number, leaderType: LeaderType, serialLength: number, serialCharacterSet: CharacterSet): void {
119
+ validateNonGTINNumericIdentificationKeyValidator(validator, isCreator, identificationKeyType, length, leaderType);
120
+
121
+ const serialCreator = creatorFor(serialCharacterSet);
122
+
123
+ expect(validator.serialComponentLength).toBe(serialLength);
124
+ expect(validator.serialComponentCharacterSet).toBe(serialCharacterSet);
125
+ expect(validator.serialComponentValidator).toBe(serialCreator);
126
+
127
+ if (isCreator) {
128
+ expect((validator as SerializableNumericIdentificationKeyCreator).serialComponentCreator).toBe(serialCreator);
129
+ }
130
+ }
131
+
132
+ // eslint-disable-next-line jsdoc/require-jsdoc
133
+ function validateNonNumericIdentificationKeyValidator(validator: NonNumericIdentificationKeyValidator, isCreator: boolean, identificationKeyType: IdentificationKeyType, length: number, referenceCharacterSet: CharacterSet, requiresCheckCharacterPair: boolean): void {
134
+ validateIdentificationKeyValidator(validator, identificationKeyType, PrefixType.GS1CompanyPrefix, length);
135
+
136
+ const referenceCreator = creatorFor(referenceCharacterSet);
137
+
138
+ expect(validator.referenceCharacterSet).toBe(referenceCharacterSet);
139
+ expect(validator.referenceValidator).toBe(referenceCreator);
140
+ expect(validator.requiresCheckCharacterPair).toBe(requiresCheckCharacterPair);
141
+
142
+ if (isCreator) {
143
+ expect((validator as NonNumericIdentificationKeyCreator).referenceCreator).toBe(referenceCreator);
144
+ }
145
+ }
146
+
147
+ describe("Validators", () => {
148
+ test("GTIN", () => {
149
+ expect(GTIN_VALIDATORS[PrefixType.GS1CompanyPrefix]).toBe(GTIN13_VALIDATOR);
150
+ expect(GTIN_VALIDATORS[PrefixType.UPCCompanyPrefix]).toBe(GTIN12_VALIDATOR);
151
+ expect(GTIN_VALIDATORS[PrefixType.GS18Prefix]).toBe(GTIN8_VALIDATOR);
152
+ });
153
+
154
+ test("Structure", () => {
155
+ validateGTINValidator(GTIN13_VALIDATOR, false, GTINType.GTIN13);
156
+ validateGTINValidator(GTIN12_VALIDATOR, false, GTINType.GTIN12);
157
+ validateGTINValidator(GTIN8_VALIDATOR, false, GTINType.GTIN8);
158
+ validateNonGTINNumericIdentificationKeyValidator(GLN_VALIDATOR, false, IdentificationKeyType.GLN, 13, LeaderType.None);
159
+ validateNonGTINNumericIdentificationKeyValidator(SSCC_VALIDATOR, false, IdentificationKeyType.SSCC, 18, LeaderType.ExtensionDigit);
160
+ validateSerializableNumericIdentificationKeyValidator(GRAI_VALIDATOR, false, IdentificationKeyType.GRAI, 13, LeaderType.None, 16, CharacterSet.AI82);
161
+ validateNonNumericIdentificationKeyValidator(GIAI_VALIDATOR, false, IdentificationKeyType.GIAI, 30, CharacterSet.AI82, false);
162
+ validateNonGTINNumericIdentificationKeyValidator(GSRN_VALIDATOR, false, IdentificationKeyType.GSRN, 18, LeaderType.None);
163
+ validateSerializableNumericIdentificationKeyValidator(GDTI_VALIDATOR, false, IdentificationKeyType.GDTI, 13, LeaderType.None, 17, CharacterSet.AI82);
164
+ validateNonNumericIdentificationKeyValidator(GINC_VALIDATOR, false, IdentificationKeyType.GINC, 30, CharacterSet.AI82, false);
165
+ validateNonGTINNumericIdentificationKeyValidator(GSIN_VALIDATOR, false, IdentificationKeyType.GSIN, 17, LeaderType.None);
166
+ validateSerializableNumericIdentificationKeyValidator(GCN_VALIDATOR, false, IdentificationKeyType.GCN, 13, LeaderType.None, 12, CharacterSet.Numeric);
167
+ validateNonNumericIdentificationKeyValidator(CPID_VALIDATOR, false, IdentificationKeyType.CPID, 30, CharacterSet.AI39, false);
168
+ validateNonNumericIdentificationKeyValidator(GMN_VALIDATOR, false, IdentificationKeyType.GMN, 25, CharacterSet.AI82, true);
169
+ });
170
+ });
171
+
172
+ // eslint-disable-next-line jsdoc/require-jsdoc
173
+ function validateIdentificationKeyCreators(prefixManager: PrefixManager): void {
174
+ let gtinType: GTINType;
175
+
176
+ switch (prefixManager.prefixType) {
177
+ case PrefixType.GS1CompanyPrefix:
178
+ expect(prefixManager.prefix).toBe(prefixManager.gs1CompanyPrefix);
179
+ gtinType = GTINType.GTIN13;
180
+ break;
181
+
182
+ case PrefixType.UPCCompanyPrefix:
183
+ expect(prefixManager.prefix).toBe(prefixManager.upcCompanyPrefix);
184
+ gtinType = GTINType.GTIN12;
185
+ break;
186
+
187
+ case PrefixType.GS18Prefix:
188
+ expect(prefixManager.prefix).toBe(prefixManager.gs18Prefix);
189
+ gtinType = GTINType.GTIN8;
190
+ break;
191
+ }
192
+
193
+ validateGTINValidator(prefixManager.gtinCreator, true, gtinType);
194
+
195
+ if (prefixManager.prefixType !== PrefixType.GS18Prefix) {
196
+ validateNonGTINNumericIdentificationKeyValidator(prefixManager.glnCreator, true, IdentificationKeyType.GLN, 13, LeaderType.None);
197
+ validateNonGTINNumericIdentificationKeyValidator(prefixManager.ssccCreator, true, IdentificationKeyType.SSCC, 18, LeaderType.ExtensionDigit);
198
+ validateSerializableNumericIdentificationKeyValidator(prefixManager.graiCreator, true, IdentificationKeyType.GRAI, 13, LeaderType.None, 16, CharacterSet.AI82);
199
+ validateNonNumericIdentificationKeyValidator(prefixManager.giaiCreator, true, IdentificationKeyType.GIAI, 30, CharacterSet.AI82, false);
200
+ validateNonGTINNumericIdentificationKeyValidator(prefixManager.gsrnCreator, true, IdentificationKeyType.GSRN, 18, LeaderType.None);
201
+ validateSerializableNumericIdentificationKeyValidator(prefixManager.gdtiCreator, true, IdentificationKeyType.GDTI, 13, LeaderType.None, 17, CharacterSet.AI82);
202
+ validateNonNumericIdentificationKeyValidator(prefixManager.gincCreator, true, IdentificationKeyType.GINC, 30, CharacterSet.AI82, false);
203
+ validateNonGTINNumericIdentificationKeyValidator(prefixManager.gsinCreator, true, IdentificationKeyType.GSIN, 17, LeaderType.None);
204
+ validateSerializableNumericIdentificationKeyValidator(prefixManager.gcnCreator, true, IdentificationKeyType.GCN, 13, LeaderType.None, 12, CharacterSet.Numeric);
205
+ validateNonNumericIdentificationKeyValidator(prefixManager.cpidCreator, true, IdentificationKeyType.CPID, 30, CharacterSet.AI39, false);
206
+ validateNonNumericIdentificationKeyValidator(prefixManager.gmnCreator, true, IdentificationKeyType.GMN, 25, CharacterSet.AI82, true);
207
+ } else {
208
+ expect(() => prefixManager.glnCreator).toThrow("GLN not supported by GS1-8 Prefix");
209
+ expect(() => prefixManager.ssccCreator).toThrow("SSCC not supported by GS1-8 Prefix");
210
+ expect(() => prefixManager.graiCreator).toThrow("GRAI not supported by GS1-8 Prefix");
211
+ expect(() => prefixManager.giaiCreator).toThrow("GIAI not supported by GS1-8 Prefix");
212
+ expect(() => prefixManager.gsrnCreator).toThrow("GSRN not supported by GS1-8 Prefix");
213
+ expect(() => prefixManager.gdtiCreator).toThrow("GDTI not supported by GS1-8 Prefix");
214
+ expect(() => prefixManager.gincCreator).toThrow("GINC not supported by GS1-8 Prefix");
215
+ expect(() => prefixManager.gsinCreator).toThrow("GSIN not supported by GS1-8 Prefix");
216
+ expect(() => prefixManager.gcnCreator).toThrow("GCN not supported by GS1-8 Prefix");
217
+ expect(() => prefixManager.cpidCreator).toThrow("CPID not supported by GS1-8 Prefix");
218
+ expect(() => prefixManager.gmnCreator).toThrow("GMN not supported by GS1-8 Prefix");
219
+ }
220
+ }
221
+
222
+ describe("Prefix manager", () => {
223
+ let prefixManager: PrefixManager;
224
+
225
+ // eslint-disable-next-line jsdoc/require-jsdoc
226
+ function validateGTINStartsWithPrefix(length: number): void {
227
+ expect(prefixManager.gtinCreator.length).toBe(length);
228
+
229
+ const gtin = prefixManager.gtinCreator.create(0);
230
+
231
+ expect(gtin.startsWith(prefixManager.prefix)).toBe(true);
232
+ expect(gtin.length).toBe(length);
233
+
234
+ const gtin14 = prefixManager.gtinCreator.createGTIN14("5", 0);
235
+
236
+ expect(gtin14.startsWith("5" + prefixManager.gs1CompanyPrefix)).toBe(true);
237
+ expect(gtin14.length).toBe(14);
238
+ }
239
+
240
+ // eslint-disable-next-line jsdoc/require-jsdoc
241
+ function validateNonGTINStartsWithGS1CompanyPrefix(): void {
242
+ const gln = prefixManager.glnCreator.create(0);
243
+
244
+ expect(gln.startsWith(prefixManager.gs1CompanyPrefix)).toBe(true);
245
+ expect(gln.length).toBe(prefixManager.glnCreator.length);
246
+
247
+ const grai = prefixManager.graiCreator.createSerialized(0, "1234");
248
+
249
+ expect(grai.startsWith(prefixManager.gs1CompanyPrefix)).toBe(true);
250
+ expect(grai.length).toBe(prefixManager.graiCreator.length + 4);
251
+
252
+ const giai = prefixManager.giaiCreator.create("1234");
253
+
254
+ expect(giai.startsWith(prefixManager.gs1CompanyPrefix)).toBe(true);
255
+ expect(giai.length).toBe(prefixManager.gs1CompanyPrefix.length + 4);
256
+ }
257
+
258
+ test("Prefix equivalence", () => {
259
+ expect(PrefixManager.get(PrefixType.GS1CompanyPrefix, "9521234")).toBe(PrefixManager.get(PrefixType.GS1CompanyPrefix, "9521234"));
260
+
261
+ expect(PrefixManager.get(PrefixType.UPCCompanyPrefix, "614141")).toBe(PrefixManager.get(PrefixType.GS1CompanyPrefix, "0614141"));
262
+
263
+ expect(PrefixManager.get(PrefixType.GS18Prefix, "952")).toBe(PrefixManager.get(PrefixType.GS1CompanyPrefix, "00000952"));
264
+ });
265
+
266
+ test("GS1 Company Prefix 9521234", () => {
267
+ prefixManager = PrefixManager.get(PrefixType.GS1CompanyPrefix, "9521234");
268
+
269
+ expect(prefixManager.prefixType).toBe(PrefixType.GS1CompanyPrefix);
270
+ expect(prefixManager.prefix).toBe("9521234");
271
+ expect(prefixManager.gs1CompanyPrefix).toBe(prefixManager.prefix);
272
+ expect(prefixManager.upcCompanyPrefix).toBeUndefined();
273
+ expect(prefixManager.gs18Prefix).toBeUndefined();
274
+
275
+ validateGTINStartsWithPrefix(13);
276
+ validateNonGTINStartsWithGS1CompanyPrefix();
277
+
278
+ validateIdentificationKeyCreators(prefixManager);
279
+ });
280
+
281
+ test("U.P.C. Company Prefix 614141", () => {
282
+ prefixManager = PrefixManager.get(PrefixType.GS1CompanyPrefix, "0614141");
283
+
284
+ expect(prefixManager.prefixType).toBe(PrefixType.UPCCompanyPrefix);
285
+ expect(prefixManager.prefix).toBe("614141");
286
+ expect(prefixManager.gs1CompanyPrefix).toBe("0" + prefixManager.prefix);
287
+ expect(prefixManager.upcCompanyPrefix).toBe(prefixManager.prefix);
288
+ expect(prefixManager.gs18Prefix).toBeUndefined();
289
+
290
+ validateGTINStartsWithPrefix(12);
291
+ validateNonGTINStartsWithGS1CompanyPrefix();
292
+
293
+ validateIdentificationKeyCreators(prefixManager);
294
+ });
295
+
296
+ test("GS1-8 Prefix 952", () => {
297
+ prefixManager = PrefixManager.get(PrefixType.GS1CompanyPrefix, "00000952");
298
+
299
+ expect(prefixManager.prefixType).toBe(PrefixType.GS18Prefix);
300
+ expect(prefixManager.prefix).toBe("952");
301
+ expect(prefixManager.gs1CompanyPrefix).toBe("00000" + prefixManager.prefix);
302
+ expect(prefixManager.upcCompanyPrefix).toBeUndefined();
303
+ expect(prefixManager.gs18Prefix).toBe(prefixManager.prefix);
304
+
305
+ validateGTINStartsWithPrefix(8);
306
+
307
+ validateIdentificationKeyCreators(prefixManager);
308
+ });
309
+
310
+ test("Prefix validation", () => {
311
+ expect(() => PrefixManager.get(PrefixType.GS1CompanyPrefix, "952")).toThrow("Length 3 of GS1 Company Prefix must be greater than or equal to 4");
312
+ expect(() => PrefixManager.get(PrefixType.GS1CompanyPrefix, "9520")).not.toThrow(RangeError);
313
+ expect(() => PrefixManager.get(PrefixType.GS1CompanyPrefix, "952123456789")).not.toThrow(RangeError);
314
+ expect(() => PrefixManager.get(PrefixType.GS1CompanyPrefix, "9521234567890")).toThrow("Length 13 of GS1 Company Prefix must be less than or equal to 12");
315
+
316
+ expect(() => PrefixManager.get(PrefixType.GS1CompanyPrefix, "952123A56789")).toThrow("Invalid character 'A' at position 7 of GS1 Company Prefix");
317
+
318
+ expect(() => PrefixManager.get(PrefixType.UPCCompanyPrefix, "61414")).toThrow("Length 5 of U.P.C. Company Prefix must be greater than or equal to 6");
319
+ expect(() => PrefixManager.get(PrefixType.UPCCompanyPrefix, "614141")).not.toThrow(RangeError);
320
+ expect(() => PrefixManager.get(PrefixType.UPCCompanyPrefix, "61414112345")).not.toThrow(RangeError);
321
+ expect(() => PrefixManager.get(PrefixType.UPCCompanyPrefix, "614141123456")).toThrow("Length 12 of U.P.C. Company Prefix must be less than or equal to 11");
322
+ expect(() => PrefixManager.get(PrefixType.UPCCompanyPrefix, "000614")).not.toThrow(RangeError);
323
+ expect(() => PrefixManager.get(PrefixType.UPCCompanyPrefix, "000061")).toThrow("U.P.C. Company Prefix can't start with \"0000\"");
324
+
325
+ expect(() => PrefixManager.get(PrefixType.GS1CompanyPrefix, "00000952")).not.toThrow(RangeError);
326
+ expect(() => PrefixManager.get(PrefixType.GS1CompanyPrefix, "000000952")).toThrow("GS1 Company Prefix can't start with \"000000\"");
327
+
328
+ expect(() => PrefixManager.get(PrefixType.GS18Prefix, "952")).not.toThrow(RangeError);
329
+ expect(() => PrefixManager.get(PrefixType.GS18Prefix, "0952")).toThrow("GS1-8 Prefix can't start with \"0\"");
330
+ });
331
+ });
332
+
333
+ describe("Sparse creation", () => {
334
+ const prefixManager = PrefixManager.get(PrefixType.GS1CompanyPrefix, "9521234");
335
+
336
+ const gtin1 = prefixManager.gtinCreator.create(0, true);
337
+ const gln1 = prefixManager.glnCreator.create(0, true);
338
+
339
+ test("Same length 1, not equal", () => {
340
+ expect(gln1.length).toBe(gtin1.length);
341
+ expect(gln1).not.toBe(gtin1);
342
+ });
343
+
344
+ prefixManager.tweakFactor = 123456;
345
+
346
+ const gtin2 = prefixManager.gtinCreator.create(0, true);
347
+ const gln2 = prefixManager.glnCreator.create(0, true);
348
+
349
+ test("Same length 2, not equal", () => {
350
+ expect(gln2.length).toBe(gtin2.length);
351
+ expect(gln2).not.toBe(gtin2);
352
+ });
353
+
354
+ test("Same types 1 and 2, not equal", () => {
355
+ expect(gtin2).not.toBe(gtin1);
356
+ expect(gln2).not.toBe(gln1);
357
+ });
358
+
359
+ prefixManager.resetTweakFactor();
360
+
361
+ const gtin3 = prefixManager.gtinCreator.create(0, true);
362
+ const gln3 = prefixManager.glnCreator.create(0, true);
363
+
364
+ test("Same length 3, not equal", () => {
365
+ expect(gln3.length).toBe(gtin3.length);
366
+ expect(gln3).not.toBe(gtin3);
367
+ });
368
+
369
+ test("Same types 1 and 3, equal", () => {
370
+ expect(gtin3).toBe(gtin1);
371
+ expect(gln3).toBe(gln1);
372
+ });
373
+
374
+ prefixManager.tweakFactor = 0;
375
+
376
+ const sparseGTINs = Array.from(prefixManager.gtinCreator.createSequence(0, 10, true));
377
+ const straightGTINs = Array.from(prefixManager.gtinCreator.createSequence(0, 10));
378
+
379
+ test("Tweak factor 0", () => {
380
+ expect(sparseGTINs).toStrictEqual(straightGTINs);
381
+ });
382
+
383
+ prefixManager.resetTweakFactor();
384
+ });
385
+
386
+ // eslint-disable-next-line jsdoc/require-jsdoc
387
+ function testIdentificationKeyCreatorCallback(callback?: () => void): void {
388
+ if (callback !== undefined) {
389
+ callback();
390
+ }
391
+ }
392
+
393
+ // eslint-disable-next-line jsdoc/require-jsdoc
394
+ function testNumericIdentificationKeyCreator(creator: NumericIdentificationKeyCreator, preTestCallback?: () => void, postTestCallback?: () => void): void {
395
+ describe(creator.identificationKeyType === IdentificationKeyType.GTIN ? `${creator.identificationKeyType}-${creator.length}` : creator.identificationKeyType, () => {
396
+ testIdentificationKeyCreatorCallback(preTestCallback);
397
+
398
+ const prefix = creator.prefix;
399
+ const prefixLength = prefix.length;
400
+ const hasExtensionDigit = creator.leaderType === LeaderType.ExtensionDigit;
401
+ const prefixSubstringStart = Number(hasExtensionDigit);
402
+ const prefixSubstringEnd = prefixSubstringStart + prefixLength;
403
+ const referenceLength = creator.length - prefixLength - 1;
404
+ const referenceCount = Number(CharacterSetCreator.powerOf10(referenceLength));
405
+ const referenceSubstringStart = prefixSubstringEnd;
406
+ const referenceSubstringEnd = referenceSubstringStart + referenceLength - prefixSubstringStart;
407
+
408
+ // eslint-disable-next-line jsdoc/require-jsdoc
409
+ function validate(identificationKey: string, index: number, sparse: boolean): void {
410
+ expect(() => {
411
+ creator.validate(identificationKey);
412
+ }).not.toThrow(RangeError);
413
+ expect(identificationKey).toBe(creator.create(index, sparse));
414
+
415
+ expect(identificationKey.length).toBe(creator.length);
416
+ expect(identificationKey.substring(prefixSubstringStart, prefixSubstringEnd)).toBe(prefix);
417
+ expect(hasValidCheckDigit(identificationKey)).toBe(true);
418
+ }
419
+
420
+ test("Straight", () => {
421
+ expect(creator.referenceLength).toBe(referenceLength);
422
+ expect(creator.capacity).toBe(Number(CharacterSetCreator.powerOf10(referenceLength)));
423
+
424
+ const sequenceIterator = creator.createSequence(0, referenceCount)[Symbol.iterator]();
425
+
426
+ let allCount = 0;
427
+
428
+ IterationHelper.from(creator.createAll()).forEach((identificationKey, index) => {
429
+ validate(identificationKey, index, false);
430
+
431
+ expect(Number((hasExtensionDigit ? identificationKey.charAt(0) : "") + identificationKey.substring(referenceSubstringStart, referenceSubstringEnd))).toBe(index);
432
+ expect(sequenceIterator.next().value).toBe(identificationKey);
433
+
434
+ allCount++;
435
+ });
436
+
437
+ expect(allCount).toBe(referenceCount);
438
+ expect(sequenceIterator.next().value).toBeUndefined();
439
+
440
+ const randomValues = new Array<number>();
441
+ const identificationKeys = new Array<string>();
442
+
443
+ for (let i = 0; i < 1000; i++) {
444
+ const randomValue = Math.floor(Math.random() * creator.capacity);
445
+
446
+ randomValues.push(randomValue);
447
+ identificationKeys.push(creator.create(randomValue));
448
+ }
449
+
450
+ expect(Array.from(creator.createMultiple(randomValues))).toStrictEqual(identificationKeys);
451
+ });
452
+
453
+ test("Sparse", () => {
454
+ const sparseReferenceCount = Math.min(referenceCount, 1000);
455
+
456
+ // Reference count of 1 is neither sequential nor sparse so treat it as sparse.
457
+ let sequential = sparseReferenceCount !== 1;
458
+
459
+ const sequenceSet = new Set<string>();
460
+
461
+ let sequenceCount = 0;
462
+
463
+ IterationHelper.from(creator.createSequence(0, sparseReferenceCount, true)).forEach((identificationKey, index) => {
464
+ validate(identificationKey, index, true);
465
+
466
+ sequential = sequential && Number((hasExtensionDigit ? identificationKey.charAt(0) : "") + identificationKey.substring(referenceSubstringStart, referenceSubstringEnd)) === index;
467
+
468
+ expect(sequenceSet.has(identificationKey)).toBe(false);
469
+ sequenceSet.add(identificationKey);
470
+
471
+ sequenceCount++;
472
+ });
473
+
474
+ expect(sequential).toBe(false);
475
+ expect(sequenceCount).toBe(sparseReferenceCount);
476
+
477
+ const randomValues = new Array<number>();
478
+ const identificationKeys = new Array<string>();
479
+
480
+ for (let i = 0; i < 1000; i++) {
481
+ const randomValue = Math.floor(Math.random() * creator.capacity);
482
+
483
+ randomValues.push(randomValue);
484
+ identificationKeys.push(creator.create(randomValue, true));
485
+ }
486
+
487
+ expect(Array.from(creator.createMultiple(randomValues, true))).toStrictEqual(identificationKeys);
488
+ });
489
+
490
+ test("Validation position", () => {
491
+ const identificationKey = creator.create(0);
492
+
493
+ const badIdentificationKey1 = `${identificationKey.substring(0, identificationKey.length - 2)}O${identificationKey.substring(identificationKey.length - 1)}`;
494
+
495
+ expect(badIdentificationKey1.length).toBe(creator.length);
496
+ expect(() => {
497
+ creator.validate(badIdentificationKey1);
498
+ }).toThrow(`Invalid character 'O' at position ${creator.length - 1}`);
499
+
500
+ const badIdentificationKey2 = `${identificationKey.substring(0, 2)}O${identificationKey.substring(3)}`;
501
+
502
+ expect(badIdentificationKey2.length).toBe(creator.length);
503
+ expect(() => {
504
+ creator.validate(badIdentificationKey2);
505
+ }).toThrow("Invalid character 'O' at position 3");
506
+ });
507
+
508
+ testIdentificationKeyCreatorCallback(postTestCallback);
509
+ });
510
+ }
511
+
512
+ // eslint-disable-next-line jsdoc/require-jsdoc
513
+ function testGTINCreator(creator: GTINCreator): void {
514
+ testNumericIdentificationKeyCreator(creator, () => {
515
+ test("Length", () => {
516
+ switch (creator.prefixType) {
517
+ case PrefixType.GS1CompanyPrefix:
518
+ expect(creator.length).toBe(13);
519
+ break;
520
+
521
+ case PrefixType.UPCCompanyPrefix:
522
+ expect(creator.length).toBe(12);
523
+ break;
524
+
525
+ case PrefixType.GS18Prefix:
526
+ expect(creator.length).toBe(8);
527
+ break;
528
+ }
529
+ });
530
+ }, () => {
531
+ const gs1CompanyPrefix = creator.prefixManager.gs1CompanyPrefix;
532
+ const prefix = creator.prefix;
533
+ const prefixLength = prefix.length;
534
+ const prefixSubstringStart = 14 - creator.length;
535
+ const prefixSubstringEnd = prefixSubstringStart + prefixLength;
536
+ const referenceLength = creator.length - prefixLength - 1;
537
+ const referenceCount = Number(CharacterSetCreator.powerOf10(referenceLength));
538
+ const referenceSubstringStart = prefixSubstringEnd;
539
+ const referenceSubstringEnd = referenceSubstringStart + referenceLength;
540
+
541
+ // eslint-disable-next-line jsdoc/require-jsdoc
542
+ function validate(gtin: string, index: number, sparse: boolean): void {
543
+ expect(() => {
544
+ GTINCreator.validateGTIN14(gtin);
545
+ }).not.toThrow(RangeError);
546
+ expect(gtin).toBe(creator.createGTIN14("5", index, sparse));
547
+
548
+ expect(gtin.length).toBe(14);
549
+ expect(gtin.charAt(0)).toBe("5");
550
+ expect(gtin.substring(1, prefixSubstringEnd)).toBe(gs1CompanyPrefix);
551
+ expect(gtin.substring(prefixSubstringStart, prefixSubstringEnd)).toBe(prefix);
552
+ expect(hasValidCheckDigit(gtin)).toBe(true);
553
+ }
554
+
555
+ test("GTIN-14 straight", () => {
556
+ let sequenceCount = 0;
557
+
558
+ IterationHelper.from(creator.createGTIN14Sequence("5", 0, referenceCount)).forEach((gtin, index) => {
559
+ expect(Number(gtin.substring(referenceSubstringStart, referenceSubstringEnd))).toBe(index);
560
+
561
+ validate(gtin, index, false);
562
+
563
+ sequenceCount++;
564
+ });
565
+
566
+ expect(sequenceCount).toBe(referenceCount);
567
+
568
+ const randomValues = new Array<number>();
569
+ const identificationKeys = new Array<string>();
570
+
571
+ for (let i = 0; i < 1000; i++) {
572
+ const randomValue = Math.floor(Math.random() * creator.capacity);
573
+
574
+ randomValues.push(randomValue);
575
+ identificationKeys.push(creator.createGTIN14("5", randomValue));
576
+ }
577
+
578
+ expect(Array.from(creator.createGTIN14Multiple("5", randomValues))).toStrictEqual(identificationKeys);
579
+ });
580
+
581
+ test("GTIN-14 sparse", () => {
582
+ const sparseReferenceCount = Math.min(referenceCount, 1000);
583
+
584
+ // Reference count of 1 is neither sequential nor sparse so treat it as sparse.
585
+ let sequential = sparseReferenceCount !== 1;
586
+
587
+ const sequenceSet = new Set<string>();
588
+
589
+ let sequenceCount = 0;
590
+
591
+ IterationHelper.from(creator.createGTIN14Sequence("5", 0, sparseReferenceCount, true)).forEach((gtin, index) => {
592
+ sequential = sequential && Number(gtin.substring(referenceSubstringStart, referenceSubstringEnd)) === index;
593
+
594
+ validate(gtin, index, true);
595
+
596
+ expect(sequenceSet.has(gtin)).toBe(false);
597
+ sequenceSet.add(gtin);
598
+
599
+ sequenceCount++;
600
+ });
601
+
602
+ expect(sequential).toBe(false);
603
+ expect(sequenceCount).toBe(sparseReferenceCount);
604
+
605
+ const randomValues = new Array<number>();
606
+ const identificationKeys = new Array<string>();
607
+
608
+ for (let i = 0; i < 1000; i++) {
609
+ const randomValue = Math.floor(Math.random() * creator.capacity);
610
+
611
+ randomValues.push(randomValue);
612
+ identificationKeys.push(creator.createGTIN14("5", randomValue, true));
613
+ }
614
+
615
+ expect(Array.from(creator.createGTIN14Multiple("5", randomValues, true))).toStrictEqual(identificationKeys);
616
+ });
617
+
618
+ if (creator.gtinType === GTINType.GTIN12) {
619
+ test("Zero-suppress GTIN-12 rule 1", () => {
620
+ expect(GTINCreator.zeroSuppress("012345000058")).toBe("01234558");
621
+ expect(GTINCreator.zeroSuppress("012345000065")).toBe("01234565");
622
+ expect(GTINCreator.zeroSuppress("012345000072")).toBe("01234572");
623
+ expect(GTINCreator.zeroSuppress("012345000089")).toBe("01234589");
624
+ expect(GTINCreator.zeroSuppress("012345000096")).toBe("01234596");
625
+ });
626
+
627
+ test("Zero-suppress GTIN-12 rule 2", () => {
628
+ expect(GTINCreator.zeroSuppress("045670000080")).toBe("04567840");
629
+ });
630
+
631
+ test("Zero-suppress GTIN-12 rule 3", () => {
632
+ expect(GTINCreator.zeroSuppress("034000005673")).toBe("03456703");
633
+ expect(GTINCreator.zeroSuppress("034100005672")).toBe("03456712");
634
+ expect(GTINCreator.zeroSuppress("034200005671")).toBe("03456721");
635
+ });
636
+
637
+ test("Zero-suppress GTIN-12 rule 4", () => {
638
+ expect(GTINCreator.zeroSuppress("098300000752")).toBe("09837532");
639
+ expect(GTINCreator.zeroSuppress("098400000751")).toBe("09847531");
640
+ expect(GTINCreator.zeroSuppress("098500000750")).toBe("09857530");
641
+ expect(GTINCreator.zeroSuppress("098600000759")).toBe("09867539");
642
+ expect(GTINCreator.zeroSuppress("098700000758")).toBe("09877538");
643
+ expect(GTINCreator.zeroSuppress("098800000757")).toBe("09887537");
644
+ expect(GTINCreator.zeroSuppress("098900000756")).toBe("09897536");
645
+ });
646
+
647
+ test("Non-zero-suppressible GTIN-12 rule 1", () => {
648
+ expect(() => GTINCreator.zeroSuppress("012345100055")).toThrow("GTIN-12 not zero-suppressible");
649
+ expect(() => GTINCreator.zeroSuppress("012345010057")).toThrow("GTIN-12 not zero-suppressible");
650
+ expect(() => GTINCreator.zeroSuppress("012345001055")).toThrow("GTIN-12 not zero-suppressible");
651
+ expect(() => GTINCreator.zeroSuppress("012345000157")).toThrow("GTIN-12 not zero-suppressible");
652
+ expect(() => GTINCreator.zeroSuppress("012345000041")).toThrow("GTIN-12 not zero-suppressible");
653
+ expect(() => GTINCreator.zeroSuppress("012345000003")).toThrow("GTIN-12 not zero-suppressible");
654
+ });
655
+
656
+ test("Non-zero-suppressible GTIN-12 rule 2", () => {
657
+ expect(() => GTINCreator.zeroSuppress("045670100087")).toThrow("GTIN-12 not zero-suppressible");
658
+ expect(() => GTINCreator.zeroSuppress("045670010089")).toThrow("GTIN-12 not zero-suppressible");
659
+ expect(() => GTINCreator.zeroSuppress("045670001087")).toThrow("GTIN-12 not zero-suppressible");
660
+ expect(() => GTINCreator.zeroSuppress("045670000189")).toThrow("GTIN-12 not zero-suppressible");
661
+ });
662
+
663
+ test("Non-zero-suppressible GTIN-12 rule 3", () => {
664
+ expect(() => GTINCreator.zeroSuppress("034010005670")).toThrow("GTIN-12 not zero-suppressible");
665
+ expect(() => GTINCreator.zeroSuppress("034001005672")).toThrow("GTIN-12 not zero-suppressible");
666
+ expect(() => GTINCreator.zeroSuppress("034000105670")).toThrow("GTIN-12 not zero-suppressible");
667
+ expect(() => GTINCreator.zeroSuppress("034000015672")).toThrow("GTIN-12 not zero-suppressible");
668
+ });
669
+
670
+ test("Non-zero-suppressible GTIN-12 rule 4", () => {
671
+ expect(() => GTINCreator.zeroSuppress("098310000759")).toThrow("GTIN-12 not zero-suppressible");
672
+ expect(() => GTINCreator.zeroSuppress("098301000751")).toThrow("GTIN-12 not zero-suppressible");
673
+ expect(() => GTINCreator.zeroSuppress("098300100759")).toThrow("GTIN-12 not zero-suppressible");
674
+ expect(() => GTINCreator.zeroSuppress("098300010751")).toThrow("GTIN-12 not zero-suppressible");
675
+ expect(() => GTINCreator.zeroSuppress("098300001759")).toThrow("GTIN-12 not zero-suppressible");
676
+ });
677
+
678
+ test("Zero-suppress other error", () => {
679
+ expect(() => GTINCreator.zeroSuppress("0012345000059")).toThrow("GTIN must be 12 digits long");
680
+ expect(() => GTINCreator.zeroSuppress("012345000059")).toThrow("Invalid check digit");
681
+ expect(() => GTINCreator.zeroSuppress("112345000055")).toThrow("GTIN-12 not zero-suppressible");
682
+ });
683
+
684
+ test("Zero-expand GTIN-12 rule 1", () => {
685
+ expect(GTINCreator.zeroExpand("01234558")).toBe("012345000058");
686
+ expect(GTINCreator.zeroExpand("01234565")).toBe("012345000065");
687
+ expect(GTINCreator.zeroExpand("01234572")).toBe("012345000072");
688
+ expect(GTINCreator.zeroExpand("01234589")).toBe("012345000089");
689
+ expect(GTINCreator.zeroExpand("01234596")).toBe("012345000096");
690
+ expect(() => GTINCreator.zeroExpand("00000154")).toThrow("U.P.C. Company Prefix can't start with \"0000\"");
691
+ });
692
+
693
+ test("Zero-expand GTIN-12 rule 2", () => {
694
+ expect(GTINCreator.zeroExpand("04567840")).toBe("045670000080");
695
+ expect(() => GTINCreator.zeroExpand("00001047")).toThrow("U.P.C. Company Prefix can't start with \"0000\"");
696
+ });
697
+
698
+ test("Zero-expand GTIN-12 rule 3", () => {
699
+ expect(GTINCreator.zeroExpand("03456703")).toBe("034000005673");
700
+ expect(GTINCreator.zeroExpand("03456712")).toBe("034100005672");
701
+ expect(GTINCreator.zeroExpand("03456721")).toBe("034200005671");
702
+ expect(() => GTINCreator.zeroExpand("00000028")).not.toThrow(RangeError);
703
+ });
704
+
705
+ test("Zero-expand GTIN-12 rule 4", () => {
706
+ expect(GTINCreator.zeroExpand("09837532")).toBe("098300000752");
707
+ expect(GTINCreator.zeroExpand("09847531")).toBe("098400000751");
708
+ expect(GTINCreator.zeroExpand("09857530")).toBe("098500000750");
709
+ expect(GTINCreator.zeroExpand("09867539")).toBe("098600000759");
710
+ expect(GTINCreator.zeroExpand("09877538")).toBe("098700000758");
711
+ expect(GTINCreator.zeroExpand("09887537")).toBe("098800000757");
712
+ expect(GTINCreator.zeroExpand("09897536")).toBe("098900000756");
713
+ expect(() => GTINCreator.zeroExpand("00030037")).not.toThrow(RangeError);
714
+ });
715
+
716
+ test("Zero-expand error", () => {
717
+ expect(() => GTINCreator.zeroExpand("001234505")).toThrow("Length 9 must be less than or equal to 8");
718
+ expect(() => GTINCreator.zeroExpand("01234506")).toThrow("Invalid check digit");
719
+ expect(() => GTINCreator.zeroExpand("11234506")).toThrow("Invalid zero-suppressed GTIN-12");
720
+ expect(() => GTINCreator.zeroExpand("09800037")).toThrow("Invalid zero-suppressed GTIN-12");
721
+ expect(() => GTINCreator.zeroExpand("09800047")).toThrow("Invalid zero-suppressed GTIN-12");
722
+ expect(() => GTINCreator.zeroExpand("09800052")).toThrow("Invalid zero-suppressed GTIN-12");
723
+ });
724
+ }
725
+
726
+ test("GTIN-14", () => {
727
+ const gtin = creator.create(0, true);
728
+
729
+ expect(gtin.length).toBe(creator.length);
730
+
731
+ let gtin14 = GTINCreator.convertToGTIN14("0", gtin);
732
+
733
+ expect(gtin14.length).toBe(14);
734
+ expect(GTINCreator.normalize(gtin14)).toBe(gtin);
735
+
736
+ gtin14 = GTINCreator.convertToGTIN14("1", gtin);
737
+
738
+ expect(gtin14.length).toBe(14);
739
+ expect(GTINCreator.normalize(gtin14)).not.toBe(gtin);
740
+
741
+ gtin14 = GTINCreator.convertToGTIN14("2", gtin14);
742
+
743
+ expect(gtin14.length).toBe(14);
744
+ expect(GTINCreator.normalize(gtin14)).not.toBe(gtin);
745
+ });
746
+ });
747
+ }
748
+
749
+ // eslint-disable-next-line jsdoc/require-jsdoc
750
+ function testGTINValidationAndNormalization(): void {
751
+ describe("GTIN validation and normalization", () => {
752
+ test("Validation", () => {
753
+ expect(() => {
754
+ GTINCreator.validateAny("9521873000122", GTINLevel.Any);
755
+ }).not.toThrow(RangeError);
756
+ expect(() => {
757
+ GTINCreator.validateAny("19521873000129", GTINLevel.Any);
758
+ }).not.toThrow(RangeError);
759
+ expect(() => {
760
+ GTINCreator.validateAny("9521873000160", GTINLevel.Any);
761
+ }).not.toThrow(RangeError);
762
+ expect(() => {
763
+ GTINCreator.validateAny("95216843", GTINLevel.Any);
764
+ }).not.toThrow(RangeError);
765
+ expect(() => {
766
+ GTINCreator.validateAny("95217800031", GTINLevel.Any);
767
+ }).toThrow("GTIN must be 13, 12, 8, or 14 digits long");
768
+ expect(() => {
769
+ GTINCreator.validateAny("614141773985", GTINLevel.Any);
770
+ }).not.toThrow(RangeError);
771
+ expect(() => {
772
+ GTINCreator.validateAny("614141773991", GTINLevel.Any);
773
+ }).toThrow("Invalid check digit");
774
+ expect(() => {
775
+ GTINCreator.validateAny("09867539", GTINLevel.Any);
776
+ }).not.toThrow(RangeError);
777
+ expect(() => {
778
+ GTINCreator.validateAny("09800037", GTINLevel.Any);
779
+ }).toThrow("Invalid zero-suppressed GTIN-12");
780
+ expect(() => {
781
+ GTINCreator.validateAny("9521873000122", GTINLevel.RetailConsumer);
782
+ }).not.toThrow(RangeError);
783
+ expect(() => {
784
+ GTINCreator.validateAny("19521873000129", GTINLevel.RetailConsumer);
785
+ }).toThrow("GTIN not supported at retail consumer trade item level");
786
+ expect(() => {
787
+ GTINCreator.validateAny("9521873000160", GTINLevel.RetailConsumer);
788
+ }).not.toThrow(RangeError);
789
+ expect(() => {
790
+ GTINCreator.validateAny("95216843", GTINLevel.RetailConsumer);
791
+ }).not.toThrow(RangeError);
792
+ expect(() => {
793
+ GTINCreator.validateAny("95217800031", GTINLevel.RetailConsumer);
794
+ }).toThrow("GTIN must be 13, 12, 8, or 14 digits long");
795
+ expect(() => {
796
+ GTINCreator.validateAny("614141773985", GTINLevel.RetailConsumer);
797
+ }).not.toThrow(RangeError);
798
+ expect(() => {
799
+ GTINCreator.validateAny("0614141773985", GTINLevel.RetailConsumer);
800
+ }).toThrow("GTIN-13 at retail consumer trade item level can't start with zero");
801
+ expect(() => {
802
+ GTINCreator.validateAny("614141773991", GTINLevel.RetailConsumer);
803
+ }).toThrow("Invalid check digit");
804
+ expect(() => {
805
+ GTINCreator.validateAny("09867539", GTINLevel.RetailConsumer);
806
+ }).not.toThrow(RangeError);
807
+ expect(() => {
808
+ GTINCreator.validateAny("09800037", GTINLevel.RetailConsumer);
809
+ }).toThrow("Invalid zero-suppressed GTIN-12");
810
+ expect(() => {
811
+ GTINCreator.validateAny("9521873000122", GTINLevel.OtherThanRetailConsumer);
812
+ }).not.toThrow(RangeError);
813
+ expect(() => {
814
+ GTINCreator.validateAny("19521873000129", GTINLevel.OtherThanRetailConsumer);
815
+ }).not.toThrow(RangeError);
816
+ expect(() => {
817
+ GTINCreator.validateAny("9521873000160", GTINLevel.OtherThanRetailConsumer);
818
+ }).not.toThrow(RangeError);
819
+ expect(() => {
820
+ GTINCreator.validateAny("95216843", GTINLevel.OtherThanRetailConsumer);
821
+ }).toThrow("GTIN not supported at other than retail consumer trade item level");
822
+ expect(() => {
823
+ GTINCreator.validateAny("95217800031", GTINLevel.OtherThanRetailConsumer);
824
+ }).toThrow("GTIN must be 13, 12, 8, or 14 digits long");
825
+ expect(() => {
826
+ GTINCreator.validateAny("614141773985", GTINLevel.OtherThanRetailConsumer);
827
+ }).not.toThrow(RangeError);
828
+ expect(() => {
829
+ GTINCreator.validateAny("614141773991", GTINLevel.OtherThanRetailConsumer);
830
+ }).toThrow("Invalid check digit");
831
+ expect(() => {
832
+ GTINCreator.validateAny("09867539", GTINLevel.OtherThanRetailConsumer);
833
+ }).toThrow("GTIN not supported at other than retail consumer trade item level");
834
+ expect(() => {
835
+ GTINCreator.validateAny("09800037", GTINLevel.OtherThanRetailConsumer);
836
+ }).toThrow("Invalid zero-suppressed GTIN-12");
837
+ });
838
+
839
+ test("Normalization", () => {
840
+ // GTIN-14.
841
+ expect(GTINCreator.normalize("09526543219996")).toBe("9526543219996");
842
+ expect(GTINCreator.normalize("00614141009992")).toBe("614141009992");
843
+ expect(() => GTINCreator.normalize("00000001234505")).toThrow("Invalid zero-suppressed GTIN-12 as GTIN-14");
844
+ expect(GTINCreator.normalize("00000095209999")).toBe("95209999");
845
+ expect(GTINCreator.normalize("49526543219994")).toBe("49526543219994");
846
+
847
+ // GTIN-13.
848
+ expect(GTINCreator.normalize("9526543219996")).toBe("9526543219996");
849
+ expect(GTINCreator.normalize("0614141009992")).toBe("614141009992");
850
+ expect(() => GTINCreator.normalize("0000001234505")).toThrow("Invalid zero-suppressed GTIN-12 as GTIN-13");
851
+ expect(GTINCreator.normalize("0000095209999")).toBe("95209999");
852
+
853
+ // GTIN-12.
854
+ expect(GTINCreator.normalize("614141009992")).toBe("614141009992");
855
+ expect(GTINCreator.normalize("01234505")).toBe("012000003455");
856
+ expect(() => GTINCreator.normalize("09800037")).toThrow("Invalid zero-suppressed GTIN-12");
857
+
858
+ // GTIN-8.
859
+ expect(GTINCreator.normalize("95209999")).toBe("95209999");
860
+ });
861
+ });
862
+ }
863
+
864
+ // eslint-disable-next-line jsdoc/require-jsdoc
865
+ function testNonGTINNumericIdentificationKeyCreator(creator: NonGTINNumericIdentificationKeyCreator, preTestCallback?: () => void, postTestCallback?: () => void): void {
866
+ testNumericIdentificationKeyCreator(creator, preTestCallback, postTestCallback);
867
+ }
868
+
869
+ // eslint-disable-next-line jsdoc/require-jsdoc
870
+ function testSerializableNumericIdentificationKeyCreator(creator: SerializableNumericIdentificationKeyCreator): void {
871
+ testNonGTINNumericIdentificationKeyCreator(creator, undefined, () => {
872
+ test("Serialization", () => {
873
+ const identificationKey = creator.create(0, true);
874
+ const serial = "12345678";
875
+ const serializedIdentificationKey = identificationKey + serial;
876
+ const serials = [serial, "23456789", "34567890", "456789012"];
877
+ const serializedIdentificationKeys = serials.map(serial => identificationKey + serial);
878
+
879
+ expect(creator.createSerialized(0, serial, true)).toBe(serializedIdentificationKey);
880
+ expect(creator.concatenate(identificationKey, serial)).toBe(serializedIdentificationKey);
881
+ expect(Array.from(creator.createMultipleSerialized(0, serials, true))).toStrictEqual(serializedIdentificationKeys);
882
+ expect(Array.from(creator.concatenateMultiple(identificationKey, serials))).toStrictEqual(serializedIdentificationKeys);
883
+
884
+ const fullLengthSerial = "0".repeat(creator.serialComponentLength);
885
+ const fullLengthPlusOneSerial = fullLengthSerial + "0";
886
+ const fullLengthPlusOneSerialErrorMessage = `Length ${creator.serialComponentLength + 1} of serial component must be less than or equal to ${creator.serialComponentLength}`;
887
+
888
+ expect(() => creator.createSerialized(0, fullLengthSerial, true)).not.toThrow(RangeError);
889
+ expect(() => creator.concatenate(identificationKey, fullLengthSerial)).not.toThrow(RangeError);
890
+ expect(() => Array.from(creator.createMultipleSerialized(0, [...serials, fullLengthSerial], true))).not.toThrow(RangeError);
891
+ expect(() => Array.from(creator.concatenateMultiple(identificationKey, [...serials, fullLengthSerial]))).not.toThrow(RangeError);
892
+ expect(() => creator.createSerialized(0, fullLengthPlusOneSerial, true)).toThrow(fullLengthPlusOneSerialErrorMessage);
893
+ expect(() => creator.concatenate(identificationKey, fullLengthPlusOneSerial)).toThrow(fullLengthPlusOneSerialErrorMessage);
894
+ expect(() => Array.from(creator.createMultipleSerialized(0, [...serials, fullLengthPlusOneSerial], true))).toThrow(fullLengthPlusOneSerialErrorMessage);
895
+ expect(() => Array.from(creator.concatenateMultiple(identificationKey, [...serials, fullLengthPlusOneSerial]))).toThrow(fullLengthPlusOneSerialErrorMessage);
896
+
897
+ let invalidSerial: string;
898
+
899
+ switch (creator.serialComponentCharacterSet) {
900
+ case CharacterSet.Numeric:
901
+ invalidSerial = "1234A5678";
902
+ break;
903
+
904
+ case CharacterSet.AI82:
905
+ invalidSerial = "ABCD~1234";
906
+ break;
907
+
908
+ case CharacterSet.AI39:
909
+ invalidSerial = "ABCD%1234";
910
+ break;
911
+ }
912
+
913
+ const invalidSerialErrorMessage = `Invalid character '${invalidSerial.charAt(4)}' at position 5 of serial component`;
914
+
915
+ expect(() => creator.createSerialized(0, invalidSerial, true)).toThrow(invalidSerialErrorMessage);
916
+ expect(() => creator.concatenate(identificationKey, invalidSerial)).toThrow(invalidSerialErrorMessage);
917
+ expect(() => Array.from(creator.createMultipleSerialized(0, [...serials, invalidSerial], true))).toThrow(invalidSerialErrorMessage);
918
+ expect(() => Array.from(creator.concatenateMultiple(identificationKey, [...serials, invalidSerial]))).toThrow(invalidSerialErrorMessage);
919
+ });
920
+ });
921
+ }
922
+
923
+ const TEST_REFERENCE_LENGTH = 2;
924
+
925
+ // eslint-disable-next-line jsdoc/require-jsdoc
926
+ function testNonNumericIdentificationKeyCreator<T extends NonNumericIdentificationKeyCreator>(creator: T): void {
927
+ describe(creator.identificationKeyType, () => {
928
+ const prefix = creator.prefix;
929
+ const prefixLength = prefix.length;
930
+ const referenceLength = creator.length - prefixLength - 2 * Number(creator.requiresCheckCharacterPair);
931
+ const referenceCount = creator.referenceCreator.characterSetSize ** TEST_REFERENCE_LENGTH;
932
+ const referenceSubstringStart = prefixLength;
933
+ const referenceSubstringEnd = prefixLength + TEST_REFERENCE_LENGTH;
934
+
935
+ test("Straight", () => {
936
+ expect(creator.referenceLength).toBe(referenceLength);
937
+
938
+ let sequenceCount = 0;
939
+
940
+ IterationHelper.from(creator.createMultiple(creator.referenceCreator.createSequence(TEST_REFERENCE_LENGTH, 0, referenceCount))).forEach((identificationKey, index) => {
941
+ expect(() => {
942
+ creator.validate(identificationKey);
943
+ }).not.toThrow(RangeError);
944
+
945
+ expect(Number(creator.referenceCreator.value(identificationKey.substring(referenceSubstringStart, referenceSubstringEnd)))).toBe(index);
946
+
947
+ expect(identificationKey.length).toBeLessThanOrEqual(creator.length);
948
+ expect(identificationKey.substring(0, prefixLength)).toBe(prefix);
949
+ expect(!creator.requiresCheckCharacterPair || hasValidCheckCharacterPair(identificationKey)).toBe(true);
950
+
951
+ expect(identificationKey).toBe(creator.referenceCreator.create(TEST_REFERENCE_LENGTH, index, Exclusion.None, undefined, reference => creator.create(reference)));
952
+
953
+ sequenceCount++;
954
+ });
955
+
956
+ expect(sequenceCount).toBe(referenceCount);
957
+ });
958
+
959
+ test("Sparse", () => {
960
+ let sequential = true;
961
+
962
+ let sequenceCount = 0;
963
+
964
+ IterationHelper.from(creator.createMultiple(creator.referenceCreator.createSequence(TEST_REFERENCE_LENGTH, 0, referenceCount, Exclusion.None, 123456n))).forEach((identificationKey, index) => {
965
+ expect(() => {
966
+ creator.validate(identificationKey);
967
+ }).not.toThrow(RangeError);
968
+
969
+ expect(Number(creator.referenceCreator.value(identificationKey.substring(referenceSubstringStart, referenceSubstringEnd), Exclusion.None, 123456n))).toBe(index);
970
+
971
+ sequential = sequential && Number(creator.referenceCreator.value(identificationKey.substring(referenceSubstringStart, referenceSubstringEnd))) === index;
972
+
973
+ expect(identificationKey.length).toBeLessThanOrEqual(creator.length);
974
+ expect(identificationKey.substring(0, prefixLength)).toBe(prefix);
975
+ expect(!creator.requiresCheckCharacterPair || hasValidCheckCharacterPair(identificationKey)).toBe(true);
976
+
977
+ expect(identificationKey).toBe(creator.referenceCreator.create(TEST_REFERENCE_LENGTH, index, Exclusion.None, 123456n, reference => creator.create(reference)));
978
+
979
+ sequenceCount++;
980
+ });
981
+
982
+ expect(sequential).toBe(false);
983
+ expect(sequenceCount).toBe(referenceCount);
984
+ });
985
+
986
+ test("Not all numeric", () => {
987
+ expect(() => {
988
+ creator.validate(creator.create("01234"), {
989
+ exclusion: Exclusion.AllNumeric
990
+ });
991
+ }).toThrow("Reference can't be all-numeric");
992
+
993
+ expect(() => {
994
+ creator.validate(creator.create("O1234"), {
995
+ exclusion: Exclusion.AllNumeric
996
+ });
997
+ }).not.toThrow(RangeError);
998
+ });
999
+ });
1000
+ }
1001
+
1002
+ let prefixManager: PrefixManager;
1003
+
1004
+ prefixManager = PrefixManager.get(PrefixType.GS1CompanyPrefix, "952123456");
1005
+
1006
+ testGTINCreator(prefixManager.gtinCreator);
1007
+
1008
+ prefixManager = PrefixManager.get(PrefixType.UPCCompanyPrefix, "61414112");
1009
+
1010
+ testGTINCreator(prefixManager.gtinCreator);
1011
+
1012
+ prefixManager = PrefixManager.get(PrefixType.GS18Prefix, "9521");
1013
+
1014
+ testGTINCreator(prefixManager.gtinCreator);
1015
+
1016
+ testGTINValidationAndNormalization();
1017
+
1018
+ prefixManager = PrefixManager.get(PrefixType.GS1CompanyPrefix, "952123456");
1019
+
1020
+ testNonGTINNumericIdentificationKeyCreator(prefixManager.glnCreator);
1021
+ testSerializableNumericIdentificationKeyCreator(prefixManager.graiCreator);
1022
+ testNonNumericIdentificationKeyCreator(prefixManager.giaiCreator);
1023
+ testSerializableNumericIdentificationKeyCreator(prefixManager.gdtiCreator);
1024
+ testNonNumericIdentificationKeyCreator(prefixManager.gincCreator);
1025
+ testSerializableNumericIdentificationKeyCreator(prefixManager.gcnCreator);
1026
+ testNonNumericIdentificationKeyCreator(prefixManager.cpidCreator);
1027
+ testNonNumericIdentificationKeyCreator(prefixManager.gmnCreator);
1028
+
1029
+ prefixManager = PrefixManager.get(PrefixType.GS1CompanyPrefix, "952123456789");
1030
+
1031
+ testNonGTINNumericIdentificationKeyCreator(prefixManager.ssccCreator);
1032
+ testNonGTINNumericIdentificationKeyCreator(prefixManager.gsrnCreator);
1033
+ testNonGTINNumericIdentificationKeyCreator(prefixManager.gsinCreator);