diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index 85d29b9dcc2..d48f4542c30 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -532,6 +532,48 @@ export class StringModule { return RFC4122_TEMPLATE.replace(/[xy]/g, replacePlaceholders); } + /** + * Generates a [Nano ID](https://github.com/ai/nanoid). + * + * @param length Length of the generated string. Defaults to `21`. + * @param length.min The minimum length of the Nano ID to generate. + * @param length.max The maximum length of the Nano ID to generate. + * + * @example + * faker.string.nanoid() // ptL0KpX_yRMI98JFr6B3n + * faker.string.nanoid(10) // VsvwSdm_Am + * faker.string.nanoid({ min: 13, max: 37 }) // KIRsdEL9jxVgqhBDlm + * + * @since 8.0.0 + */ + nanoid(length: number | { min: number; max: number } = 21): string { + length = this.faker.helpers.rangeToNumber(length); + if (length <= 0) { + return ''; + } + + const generators = [ + { + value: () => this.alphanumeric(1), + // a-z is 26 characters + // this times 2 for upper & lower case is 52 + // add all numbers 0-9 (10 in total) you get 62 + weight: 62, + }, + { + value: () => this.faker.helpers.arrayElement(['_', '-']), + weight: 2, + }, + ]; + let result = ''; + while (result.length < length) { + const charGen = this.faker.helpers.weightedArrayElement(generators); + result += charGen(); + } + + return result; + } + /** * Returns a string containing only special characters. * diff --git a/test/__snapshots__/string.spec.ts.snap b/test/__snapshots__/string.spec.ts.snap index 0a6df4d9abc..6a3ccead4eb 100644 --- a/test/__snapshots__/string.spec.ts.snap +++ b/test/__snapshots__/string.spec.ts.snap @@ -78,6 +78,20 @@ exports[`string > 42 > hexadecimal > with length range 1`] = `"0xBE4ABdd39321aD" exports[`string > 42 > hexadecimal > with length, casing and empty prefix 1`] = `"8be4abd"`; +exports[`string > 42 > nanoid > noArgs 1`] = `"NbMBr6sk8E3-W0ZCB01wo"`; + +exports[`string > 42 > nanoid > noArgs 2`] = `"2Ye5CnYsRGr0Wyn0eeGBP"`; + +exports[`string > 42 > nanoid > noArgs 3`] = `"aobKqcz1-roVJkzwXQKxA"`; + +exports[`string > 42 > nanoid > noArgs 4`] = `"XBhia0_oi0cIMBVEUQr5m"`; + +exports[`string > 42 > nanoid > noArgs 5`] = `"FFAhynYQIef2I6rcTtyH8"`; + +exports[`string > 42 > nanoid > with length parameter 1`] = `"NbMBr6sk8E3-W0ZCB01wo2Ye5CnYsR"`; + +exports[`string > 42 > nanoid > with length range 1`] = `"WJB993RBH1YPdb_iwqiB8i"`; + exports[`string > 42 > numeric > noArgs 1`] = `"3"`; exports[`string > 42 > numeric > with allowLeadingZeros 1`] = `"4"`; @@ -226,6 +240,20 @@ exports[`string > 1211 > hexadecimal > with length range 1`] = `"0xaDB42F0e3f4A9 exports[`string > 1211 > hexadecimal > with length, casing and empty prefix 1`] = `"eadb42f"`; +exports[`string > 1211 > nanoid > noArgs 1`] = `"sM8_9dqaK0FJViZZsU9C_"`; + +exports[`string > 1211 > nanoid > noArgs 2`] = `"0i1q_zaoUZjAoz4ee2QDc"`; + +exports[`string > 1211 > nanoid > noArgs 3`] = `"qApz4EyCu9FHt9xrws3AI"`; + +exports[`string > 1211 > nanoid > noArgs 4`] = `"5SxX-57HFuA0KnZgRAyUr"`; + +exports[`string > 1211 > nanoid > noArgs 5`] = `"T_mrcROKCRDrUWF5dWr0k"`; + +exports[`string > 1211 > nanoid > with length parameter 1`] = `"sM8_9dqaK0FJViZZsU9C_0i1q_zaoU"`; + +exports[`string > 1211 > nanoid > with length range 1`] = `"TdZFGLlHOLEPqR-_AcmZLoWxYdiHwl-ngjay"`; + exports[`string > 1211 > numeric > noArgs 1`] = `"9"`; exports[`string > 1211 > numeric > with allowLeadingZeros 1`] = `"9"`; @@ -374,6 +402,20 @@ exports[`string > 1337 > hexadecimal > with length range 1`] = `"0xc346ba075bd5" exports[`string > 1337 > hexadecimal > with length, casing and empty prefix 1`] = `"5c346ba"`; +exports[`string > 1337 > nanoid > noArgs 1`] = `"ydx2eAk_jN5mJ_RM0snwm"`; + +exports[`string > 1337 > nanoid > noArgs 2`] = `"tRpr8OTVCXSOGGPC-spDN"`; + +exports[`string > 1337 > nanoid > noArgs 3`] = `"GQLX3wG-xgP-RiBh-hv9G"`; + +exports[`string > 1337 > nanoid > noArgs 4`] = `"TxLsFyNSRiYIsFStp3G0m"`; + +exports[`string > 1337 > nanoid > noArgs 5`] = `"AtcFYs1gw9vinZNdyEHwk"`; + +exports[`string > 1337 > nanoid > with length parameter 1`] = `"ydx2eAk_jN5mJ_RM0snwmtRpr8OTVC"`; + +exports[`string > 1337 > nanoid > with length range 1`] = `"9hsjwgYJ7nC7YrMNmpA"`; + exports[`string > 1337 > numeric > noArgs 1`] = `"2"`; exports[`string > 1337 > numeric > with allowLeadingZeros 1`] = `"3"`; diff --git a/test/string.spec.ts b/test/string.spec.ts index c9f82593904..1ad429ca127 100644 --- a/test/string.spec.ts +++ b/test/string.spec.ts @@ -98,6 +98,12 @@ describe('string', () => { t.itRepeated('uuid', 5); + t.describe('nanoid', (t) => { + t.itRepeated('noArgs', 5) + .it('with length parameter', 30) + .it('with length range', { min: 13, max: 37 }); + }); + t.describe('special', (t) => { t.it('noArgs') .itRepeated('with length parameter', 5, 5) @@ -657,6 +663,31 @@ describe('string', () => { }); }); + describe(`nanoid`, () => { + it('generates a valid Nano ID', () => { + const id = faker.string.nanoid(); + const regex = /^[0-9a-zA-Z_-]+$/; + expect(id).toMatch(regex); + }); + + it('should have a default length of 21', () => { + const id = faker.string.nanoid(); + expect(id).toHaveLength(21); + }); + + it('should return an empty string when length is negative', () => { + const id = faker.string.nanoid(-1); + expect(id).toBe(''); + }); + + it('should return string with a length within a given range', () => { + const actual = faker.string.nanoid({ min: 13, max: 37 }); + + expect(actual.length).toBeGreaterThanOrEqual(13); + expect(actual.length).toBeLessThanOrEqual(37); + }); + }); + describe('special', () => { it('should return a value of type string with default length of 1', () => { const actual = faker.string.special();