From 4bb549a0f36725cfccbbf0d3d77cf205562bad1d Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Fri, 3 Nov 2023 12:43:09 -0300 Subject: [PATCH 01/29] feat(string): adds support for generating ULID --- src/modules/string/index.ts | 15 ++++++++++ .../modules/__snapshots__/string.spec.ts.snap | 30 +++++++++++++++++++ test/modules/string.spec.ts | 10 +++++++ 3 files changed, 55 insertions(+) diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index a370c386c11..b5f897c51c1 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -686,6 +686,21 @@ export class StringModule { .replace(/y/g, () => this.faker.number.hex({ min: 0x8, max: 0xb })); } + /** + * Returns a ULID ([Universally Unique Lexicographically Sortable Identifier](https://github.com/ulid/spec)). + * + * @example + * faker.string.ulid() // '01ARZ3NDEKTSV4RRFFQ69G5FAV' + * + * @since 8.2.0 + */ + ulid(): string { + return ( + this.fromCharacters('012', 1) + + this.fromCharacters('0123456789ABCDEFGHJKMNPQRSTVWXYZ', 25) + ); + } + /** * Generates a [Nano ID](https://github.com/ai/nanoid). * diff --git a/test/modules/__snapshots__/string.spec.ts.snap b/test/modules/__snapshots__/string.spec.ts.snap index cb8487dae59..dd1a2f57d9f 100644 --- a/test/modules/__snapshots__/string.spec.ts.snap +++ b/test/modules/__snapshots__/string.spec.ts.snap @@ -164,6 +164,16 @@ exports[`string > 42 > symbol > with length parameter 5`] = `"!\\"~\\\\_"`; exports[`string > 42 > symbol > with length range 1`] = `"^}&\\\\]>>%/%$\\"/\`"`; +exports[`string > 42 > ulid 1`] = `"1SY5QRKK4E431EVAK4PM01ZQTY"`; + +exports[`string > 42 > ulid 2`] = `"005Z5K9KG0D09GKC419ZB7E2SK"`; + +exports[`string > 42 > ulid 3`] = `"0CGZJE1VKN5E20YYYJSC9037N7"`; + +exports[`string > 42 > ulid 4`] = `"1N3KFT15XC85NR9DG6HJ51ZTRE"`; + +exports[`string > 42 > ulid 5`] = `"2CWXKQXA2J6G1YAVCQ8HTJBY8K"`; + exports[`string > 42 > uuid 1`] = `"5cf2bc99-2721-407d-8592-ba00fbdf302f"`; exports[`string > 42 > uuid 2`] = `"94980604-8962-404f-a537-1c9368f970d9"`; @@ -338,6 +348,16 @@ exports[`string > 1211 > symbol > with length parameter 5`] = `"]@?\\\\_"`; exports[`string > 1211 > symbol > with length range 1`] = `"/{](%~\\"@&@'].,&[\\\\_!]"`; +exports[`string > 1211 > ulid 1`] = `"2EWR74Z1N5N6RDB5PQT0RNMQTX"`; + +exports[`string > 1211 > ulid 2`] = `"19VZVZ6EJW64BKZER0C9X0HDZ5"`; + +exports[`string > 1211 > ulid 3`] = `"0J95PCGXBZV9CJ8C9J52H7P741"`; + +exports[`string > 1211 > ulid 4`] = `"2VFM76DD6KVDJJF21MPH5KPF84"`; + +exports[`string > 1211 > ulid 5`] = `"2NGPEF64KHVE3GVEE1CJFPJ2SW"`; + exports[`string > 1211 > uuid 1`] = `"e7ec32f0-a2a3-4c65-b2bb-d0caabde64df"`; exports[`string > 1211 > uuid 2`] = `"f379e325-9f7c-4064-be08-6f23942b68e5"`; @@ -512,6 +532,16 @@ exports[`string > 1337 > symbol > with length parameter 5`] = `"-$?,%"`; exports[`string > 1337 > symbol > with length range 1`] = `"<&') 1337 > ulid 1`] = `"0H568HE1A7GJ8AZ8Q93SC3MB4Q"`; + +exports[`string > 1337 > ulid 2`] = `"25EVSSS0BEDCJGRB6F9VNCFE54"`; + +exports[`string > 1337 > ulid 3`] = `"1T6WHXTK5YYWDTGPGP0TQKZN5E"`; + +exports[`string > 1337 > ulid 4`] = `"0DBMPS0NBV1RSYB1PGFNZQTHK8"`; + +exports[`string > 1337 > ulid 5`] = `"1TZQ8V092KY8ZWF9AGQ40PRWNH"`; + exports[`string > 1337 > uuid 1`] = `"48234870-5389-445f-b4b4-1c61a52bf27d"`; exports[`string > 1337 > uuid 2`] = `"cc057669-8c53-474d-ba67-7226d3e8ed92"`; diff --git a/test/modules/string.spec.ts b/test/modules/string.spec.ts index a4f7ecef0c5..dabb593f1a7 100644 --- a/test/modules/string.spec.ts +++ b/test/modules/string.spec.ts @@ -113,6 +113,8 @@ describe('string', () => { t.itRepeated('uuid', 5); + t.itRepeated('ulid', 5); + t.describe('nanoid', (t) => { t.itRepeated('noArgs', 5) .it('with length parameter', 30) @@ -756,6 +758,14 @@ describe('string', () => { }); }); + describe(`ulid`, () => { + it('generates a valid ULID', () => { + const ulid = faker.string.ulid(); + const regex = /^[0-2][0-9A-HJKMNP-TV-Z]{25}$/; + expect(ulid).toMatch(regex); + }); + }); + describe(`nanoid`, () => { it('generates a valid Nano ID', () => { const id = faker.string.nanoid(); From 9ba1bbe998e4258981a0ae8dd6ebe98e4a32560c Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Mon, 18 Mar 2024 20:20:48 -0300 Subject: [PATCH 02/29] fix(string): update ulid values (#2648) --- .../modules/__snapshots__/string.spec.ts.snap | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/modules/__snapshots__/string.spec.ts.snap b/test/modules/__snapshots__/string.spec.ts.snap index afb695cd0ea..42dbbc3cc73 100644 --- a/test/modules/__snapshots__/string.spec.ts.snap +++ b/test/modules/__snapshots__/string.spec.ts.snap @@ -164,15 +164,15 @@ exports[`string > 42 > symbol > with length parameter 5`] = `">%*,/"`; exports[`string > 42 > symbol > with length range 1`] = `"}\\>%%"\`>[!~_'&"`; -exports[`string > 42 > ulid 1`] = `"1SY5QRKK4E431EVAK4PM01ZQTY"`; +exports[`string > 42 > ulid 1`] = `"1YQK441VKP0ZT6559GD9K49BES"`; -exports[`string > 42 > ulid 2`] = `"005Z5K9KG0D09GKC419ZB7E2SK"`; +exports[`string > 42 > ulid 2`] = `"0GJ1K52YYS93NE3F1X8N9GH5ZR"`; -exports[`string > 42 > ulid 3`] = `"0CGZJE1VKN5E20YYYJSC9037N7"`; +exports[`string > 42 > ulid 3`] = `"2WKX261AC8TB8H4S2ZR60TPQR2"`; -exports[`string > 42 > ulid 4`] = `"1N3KFT15XC85NR9DG6HJ51ZTRE"`; +exports[`string > 42 > ulid 4`] = `"13VKA29AQMWF3PRHRFGD031MAG"`; -exports[`string > 42 > ulid 5`] = `"2CWXKQXA2J6G1YAVCQ8HTJBY8K"`; +exports[`string > 42 > ulid 5`] = `"27DR7295XSMVS5WHSWA37DTV0G"`; exports[`string > 42 > uuid 1`] = `"5fb9220d-9b0f-4d32-a248-6492457c3890"`; @@ -348,15 +348,15 @@ exports[`string > 1211 > symbol > with length parameter 5`] = `"~]-|<"`; exports[`string > 1211 > symbol > with length range 1`] = `"{(~@@],[_]?_.\`\`'=',~"`; -exports[`string > 1211 > ulid 1`] = `"2EWR74Z1N5N6RDB5PQT0RNMQTX"`; +exports[`string > 1211 > ulid 1`] = `"2W7ZNNRBPTRMTDVV6J6BZRCXHZ"`; -exports[`string > 1211 > ulid 2`] = `"19VZVZ6EJW64BKZER0C9X0HDZ5"`; +exports[`string > 1211 > ulid 2`] = `"09PGBVC895HP4PF7D6VJF1P5P8"`; -exports[`string > 1211 > ulid 3`] = `"0J95PCGXBZV9CJ8C9J52H7P741"`; +exports[`string > 1211 > ulid 3`] = `"2GE6KV3VECFJSHXZTV7PTAW0HR"`; -exports[`string > 1211 > ulid 4`] = `"2VFM76DD6KVDJJF21MPH5KPF84"`; +exports[`string > 1211 > ulid 4`] = `"128486XZD6KJJKTMTRVXHAHEBT"`; -exports[`string > 1211 > ulid 5`] = `"2NGPEF64KHVE3GVEE1CJFPJ2SW"`; +exports[`string > 1211 > ulid 5`] = `"0Y371XDXKYZBKRW4GPQ8TX4P2D"`; exports[`string > 1211 > uuid 1`] = `"ee3faac5-bdca-4d6d-9d39-35fc6e8f34b8"`; @@ -532,15 +532,15 @@ exports[`string > 1337 > symbol > with length parameter 5`] = `"]'*@:"`; exports[`string > 1337 > symbol > with length range 1`] = `"&)/+;)~\\$-?%"`; -exports[`string > 1337 > ulid 1`] = `"0H568HE1A7GJ8AZ8Q93SC3MB4Q"`; +exports[`string > 1337 > ulid 1`] = `"058EAG8ZQ3CM4ZESSBDJR69NF5"`; -exports[`string > 1337 > ulid 2`] = `"25EVSSS0BEDCJGRB6F9VNCFE54"`; +exports[`string > 1337 > ulid 2`] = `"16HT5YDGG0QZ54BP0B1SBPFZTK"`; -exports[`string > 1337 > ulid 3`] = `"1T6WHXTK5YYWDTGPGP0TQKZN5E"`; +exports[`string > 1337 > ulid 3`] = `"1Z802YZFAQ0RN6NX5XAP8G5EEC"`; -exports[`string > 1337 > ulid 4`] = `"0DBMPS0NBV1RSYB1PGFNZQTHK8"`; +exports[`string > 1337 > ulid 4`] = `"2FQSBXPWDEWMH7YHR7DCG4N07Q"`; -exports[`string > 1337 > ulid 5`] = `"1TZQ8V092KY8ZWF9AGQ40PRWNH"`; +exports[`string > 1337 > ulid 5`] = `"2KDVZ9CPB1BF1XQR31847NFXJ5"`; exports[`string > 1337 > uuid 1`] = `"4247584f-b16a-42f7-8cc5-69c34a72638d"`; From 6e7a967e14079146f64e6710996fb662c32284a7 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Tue, 19 Mar 2024 20:12:05 -0300 Subject: [PATCH 03/29] feat(string): updates ulid implementation using refDate --- src/modules/string/index.ts | 44 +++++++++++++++-- .../modules/__snapshots__/string.spec.ts.snap | 48 +++++++++++++------ test/modules/string.spec.ts | 11 ++++- 3 files changed, 84 insertions(+), 19 deletions(-) diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index 92974e4a68c..f9bfc8d8301 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -707,15 +707,53 @@ export class StringModule extends SimpleModuleBase { /** * Returns a ULID ([Universally Unique Lexicographically Sortable Identifier](https://github.com/ulid/spec)). * + * @param options The optional options object. + * @param options.refDate The date to use as reference point for the newly generated date. Defaults to `faker.defaultRefDate()`. + * * @example * faker.string.ulid() // '01ARZ3NDEKTSV4RRFFQ69G5FAV' * * @since 8.2.0 */ - ulid(): string { + ulid( + options: { + /** + * The date to use as reference point for the newly generated ULID encoded timestamp. + * + * @default faker.defaultRefDate() + */ + refDate?: string | Date | number; + } = {} + ): string { + const encodingCharacters = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford's Base32 - Excludes I, L, O, and U which may be confused with numbers + const encodingLength = encodingCharacters.length; + const encodeTime = (now: number) => { + let mod; + let len = 10; + let str = ''; + for (; len > 0; len--) { + mod = now % encodingLength; + str = encodingCharacters.charAt(mod) + str; + now = (now - mod) / encodingLength; + } + + return str; + }; + + const { refDate } = options; + let date = refDate; + + if (date == null) { + date = this.faker.defaultRefDate(); + } + + date = new Date(date); + if (Number.isNaN(date.valueOf())) { + date = this.faker.defaultRefDate(); + } + return ( - this.fromCharacters('012', 1) + - this.fromCharacters('0123456789ABCDEFGHJKMNPQRSTVWXYZ', 25) + encodeTime(date.getTime()) + this.fromCharacters(encodingCharacters, 16) ); } diff --git a/test/modules/__snapshots__/string.spec.ts.snap b/test/modules/__snapshots__/string.spec.ts.snap index 42dbbc3cc73..eeb6e8754dc 100644 --- a/test/modules/__snapshots__/string.spec.ts.snap +++ b/test/modules/__snapshots__/string.spec.ts.snap @@ -164,15 +164,21 @@ exports[`string > 42 > symbol > with length parameter 5`] = `">%*,/"`; exports[`string > 42 > symbol > with length range 1`] = `"}\\>%%"\`>[!~_'&"`; -exports[`string > 42 > ulid 1`] = `"1YQK441VKP0ZT6559GD9K49BES"`; +exports[`string > 42 > ulid > noArgs 1`] = `"01HSCFQK6RBYQK441VKP0ZT655"`; -exports[`string > 42 > ulid 2`] = `"0GJ1K52YYS93NE3F1X8N9GH5ZR"`; +exports[`string > 42 > ulid > noArgs 2`] = `"01HSCFQK6R9GD9K49BES6GJ1K5"`; -exports[`string > 42 > ulid 3`] = `"2WKX261AC8TB8H4S2ZR60TPQR2"`; +exports[`string > 42 > ulid > noArgs 3`] = `"01HSCFQK6S2YYS93NE3F1X8N9G"`; -exports[`string > 42 > ulid 4`] = `"13VKA29AQMWF3PRHRFGD031MAG"`; +exports[`string > 42 > ulid > noArgs 4`] = `"01HSCFQK6SH5ZRYWKX261AC8TB"`; -exports[`string > 42 > ulid 5`] = `"27DR7295XSMVS5WHSWA37DTV0G"`; +exports[`string > 42 > ulid > noArgs 5`] = `"01HSCFQK6S8H4S2ZR60TPQR2B3"`; + +exports[`string > 42 > ulid > with only Date refDate 1`] = `"01EZ2S259ZBYQK441VKP0ZT655"`; + +exports[`string > 42 > ulid > with only number refDate 1`] = `"01EZ2S259ZBYQK441VKP0ZT655"`; + +exports[`string > 42 > ulid > with only string refDate 1`] = `"01EZ2S259ZBYQK441VKP0ZT655"`; exports[`string > 42 > uuid 1`] = `"5fb9220d-9b0f-4d32-a248-6492457c3890"`; @@ -348,15 +354,21 @@ exports[`string > 1211 > symbol > with length parameter 5`] = `"~]-|<"`; exports[`string > 1211 > symbol > with length range 1`] = `"{(~@@],[_]?_.\`\`'=',~"`; -exports[`string > 1211 > ulid 1`] = `"2W7ZNNRBPTRMTDVV6J6BZRCXHZ"`; +exports[`string > 1211 > ulid > noArgs 1`] = `"01HSCFQK7SXW7ZNNRBPTRMTDVV"`; + +exports[`string > 1211 > ulid > noArgs 2`] = `"01HSCFQK7S6J6BZRCXHZ79PGBV"`; + +exports[`string > 1211 > ulid > noArgs 3`] = `"01HSCFQK7SC895HP4PF7D6VJF1"`; -exports[`string > 1211 > ulid 2`] = `"09PGBVC895HP4PF7D6VJF1P5P8"`; +exports[`string > 1211 > ulid > noArgs 4`] = `"01HSCFQK7SP5P8RGE6KV3VECFJ"`; -exports[`string > 1211 > ulid 3`] = `"2GE6KV3VECFJSHXZTV7PTAW0HR"`; +exports[`string > 1211 > ulid > noArgs 5`] = `"01HSCFQK7SSHXZTV7PTAW0HRE2"`; -exports[`string > 1211 > ulid 4`] = `"128486XZD6KJJKTMTRVXHAHEBT"`; +exports[`string > 1211 > ulid > with only Date refDate 1`] = `"01EZ2S259ZXW7ZNNRBPTRMTDVV"`; -exports[`string > 1211 > ulid 5`] = `"0Y371XDXKYZBKRW4GPQ8TX4P2D"`; +exports[`string > 1211 > ulid > with only number refDate 1`] = `"01EZ2S259ZXW7ZNNRBPTRMTDVV"`; + +exports[`string > 1211 > ulid > with only string refDate 1`] = `"01EZ2S259ZXW7ZNNRBPTRMTDVV"`; exports[`string > 1211 > uuid 1`] = `"ee3faac5-bdca-4d6d-9d39-35fc6e8f34b8"`; @@ -532,15 +544,21 @@ exports[`string > 1337 > symbol > with length parameter 5`] = `"]'*@:"`; exports[`string > 1337 > symbol > with length range 1`] = `"&)/+;)~\\$-?%"`; -exports[`string > 1337 > ulid 1`] = `"058EAG8ZQ3CM4ZESSBDJR69NF5"`; +exports[`string > 1337 > ulid > noArgs 1`] = `"01HSCFQK89858EAG8ZQ3CM4ZES"`; + +exports[`string > 1337 > ulid > noArgs 2`] = `"01HSCFQK89SBDJR69NF5D6HT5Y"`; + +exports[`string > 1337 > ulid > noArgs 3`] = `"01HSCFQK89DGG0QZ54BP0B1SBP"`; + +exports[`string > 1337 > ulid > noArgs 4`] = `"01HSCFQK89FZTKJZ802YZFAQ0R"`; -exports[`string > 1337 > ulid 2`] = `"16HT5YDGG0QZ54BP0B1SBPFZTK"`; +exports[`string > 1337 > ulid > noArgs 5`] = `"01HSCFQK89N6NX5XAP8G5EECSF"`; -exports[`string > 1337 > ulid 3`] = `"1Z802YZFAQ0RN6NX5XAP8G5EEC"`; +exports[`string > 1337 > ulid > with only Date refDate 1`] = `"01EZ2S259Z858EAG8ZQ3CM4ZES"`; -exports[`string > 1337 > ulid 4`] = `"2FQSBXPWDEWMH7YHR7DCG4N07Q"`; +exports[`string > 1337 > ulid > with only number refDate 1`] = `"01EZ2S259Z858EAG8ZQ3CM4ZES"`; -exports[`string > 1337 > ulid 5`] = `"2KDVZ9CPB1BF1XQR31847NFXJ5"`; +exports[`string > 1337 > ulid > with only string refDate 1`] = `"01EZ2S259Z858EAG8ZQ3CM4ZES"`; exports[`string > 1337 > uuid 1`] = `"4247584f-b16a-42f7-8cc5-69c34a72638d"`; diff --git a/test/modules/string.spec.ts b/test/modules/string.spec.ts index 422ab687996..e2e5ad5b372 100644 --- a/test/modules/string.spec.ts +++ b/test/modules/string.spec.ts @@ -113,7 +113,16 @@ describe('string', () => { t.itRepeated('uuid', 5); - t.itRepeated('ulid', 5); + t.describe('ulid', (t) => { + const ulidRefDate = '2021-02-21T17:09:15.711Z'; + + t.itRepeated('noArgs', 5) + .it('with only string refDate', { refDate: ulidRefDate }) + .it('with only Date refDate', { refDate: new Date(ulidRefDate) }) + .it('with only number refDate', { + refDate: new Date(ulidRefDate).getTime(), + }); + }); t.describe('nanoid', (t) => { t.itRepeated('noArgs', 5) From 702061d0752d317a6b2b4fa2cd4103e911f13787 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Tue, 19 Mar 2024 20:14:18 -0300 Subject: [PATCH 04/29] feat(string): updates ulid refDate documentation --- src/modules/string/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index f9bfc8d8301..bd8283e9dc5 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -708,7 +708,7 @@ export class StringModule extends SimpleModuleBase { * Returns a ULID ([Universally Unique Lexicographically Sortable Identifier](https://github.com/ulid/spec)). * * @param options The optional options object. - * @param options.refDate The date to use as reference point for the newly generated date. Defaults to `faker.defaultRefDate()`. + * @param options.refDate The date to use as reference point for the newly generated ULID encoded timestamp. Defaults to `faker.defaultRefDate()`. * * @example * faker.string.ulid() // '01ARZ3NDEKTSV4RRFFQ69G5FAV' From 6fcba2c43d8c88c2d9c8ae470204faf8b8d9ae55 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Wed, 20 Mar 2024 10:45:52 -0300 Subject: [PATCH 05/29] fix(string): updates ulid regex to match all possible ULID values --- test/modules/string.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/modules/string.spec.ts b/test/modules/string.spec.ts index e2e5ad5b372..ccb6bde0640 100644 --- a/test/modules/string.spec.ts +++ b/test/modules/string.spec.ts @@ -764,7 +764,7 @@ describe('string', () => { describe(`ulid`, () => { it('generates a valid ULID', () => { const ulid = faker.string.ulid(); - const regex = /^[0-2][0-9A-HJKMNP-TV-Z]{25}$/; + const regex = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/; expect(ulid).toMatch(regex); }); }); From 23bbf60712fe4e6df405df84912a4036a3cd3263 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Thu, 4 Jul 2024 16:00:18 -0300 Subject: [PATCH 06/29] fix(string): uses up-to-date standard on handling date errors and updates ulid tests --- src/modules/string/index.ts | 17 +++---- .../modules/__snapshots__/string.spec.ts.snap | 48 ++++--------------- test/modules/string.spec.ts | 15 ++++-- 3 files changed, 26 insertions(+), 54 deletions(-) diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index bd8283e9dc5..c10d1751364 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -740,20 +740,15 @@ export class StringModule extends SimpleModuleBase { return str; }; - const { refDate } = options; - let date = refDate; - - if (date == null) { - date = this.faker.defaultRefDate(); - } - - date = new Date(date); - if (Number.isNaN(date.valueOf())) { - date = this.faker.defaultRefDate(); + const { refDate = this.faker.defaultRefDate() } = options; + const converted = new Date(refDate); + if (Number.isNaN(converted.valueOf())) { + throw new FakerError(`Invalid ULID refDate: ${refDate.toString()}`); } return ( - encodeTime(date.getTime()) + this.fromCharacters(encodingCharacters, 16) + encodeTime(converted.getTime()) + + this.fromCharacters(encodingCharacters, 16) ); } diff --git a/test/modules/__snapshots__/string.spec.ts.snap b/test/modules/__snapshots__/string.spec.ts.snap index eeb6e8754dc..099c21963ce 100644 --- a/test/modules/__snapshots__/string.spec.ts.snap +++ b/test/modules/__snapshots__/string.spec.ts.snap @@ -164,21 +164,11 @@ exports[`string > 42 > symbol > with length parameter 5`] = `">%*,/"`; exports[`string > 42 > symbol > with length range 1`] = `"}\\>%%"\`>[!~_'&"`; -exports[`string > 42 > ulid > noArgs 1`] = `"01HSCFQK6RBYQK441VKP0ZT655"`; +exports[`string > 42 > ulid > with Date refDate 1`] = `"01EZ2S259ZBYQK441VKP0ZT655"`; -exports[`string > 42 > ulid > noArgs 2`] = `"01HSCFQK6R9GD9K49BES6GJ1K5"`; +exports[`string > 42 > ulid > with number refDate 1`] = `"01EZ2S259ZBYQK441VKP0ZT655"`; -exports[`string > 42 > ulid > noArgs 3`] = `"01HSCFQK6S2YYS93NE3F1X8N9G"`; - -exports[`string > 42 > ulid > noArgs 4`] = `"01HSCFQK6SH5ZRYWKX261AC8TB"`; - -exports[`string > 42 > ulid > noArgs 5`] = `"01HSCFQK6S8H4S2ZR60TPQR2B3"`; - -exports[`string > 42 > ulid > with only Date refDate 1`] = `"01EZ2S259ZBYQK441VKP0ZT655"`; - -exports[`string > 42 > ulid > with only number refDate 1`] = `"01EZ2S259ZBYQK441VKP0ZT655"`; - -exports[`string > 42 > ulid > with only string refDate 1`] = `"01EZ2S259ZBYQK441VKP0ZT655"`; +exports[`string > 42 > ulid > with string refDate 1`] = `"01EZ2S259ZBYQK441VKP0ZT655"`; exports[`string > 42 > uuid 1`] = `"5fb9220d-9b0f-4d32-a248-6492457c3890"`; @@ -354,21 +344,11 @@ exports[`string > 1211 > symbol > with length parameter 5`] = `"~]-|<"`; exports[`string > 1211 > symbol > with length range 1`] = `"{(~@@],[_]?_.\`\`'=',~"`; -exports[`string > 1211 > ulid > noArgs 1`] = `"01HSCFQK7SXW7ZNNRBPTRMTDVV"`; - -exports[`string > 1211 > ulid > noArgs 2`] = `"01HSCFQK7S6J6BZRCXHZ79PGBV"`; - -exports[`string > 1211 > ulid > noArgs 3`] = `"01HSCFQK7SC895HP4PF7D6VJF1"`; - -exports[`string > 1211 > ulid > noArgs 4`] = `"01HSCFQK7SP5P8RGE6KV3VECFJ"`; +exports[`string > 1211 > ulid > with Date refDate 1`] = `"01EZ2S259ZXW7ZNNRBPTRMTDVV"`; -exports[`string > 1211 > ulid > noArgs 5`] = `"01HSCFQK7SSHXZTV7PTAW0HRE2"`; +exports[`string > 1211 > ulid > with number refDate 1`] = `"01EZ2S259ZXW7ZNNRBPTRMTDVV"`; -exports[`string > 1211 > ulid > with only Date refDate 1`] = `"01EZ2S259ZXW7ZNNRBPTRMTDVV"`; - -exports[`string > 1211 > ulid > with only number refDate 1`] = `"01EZ2S259ZXW7ZNNRBPTRMTDVV"`; - -exports[`string > 1211 > ulid > with only string refDate 1`] = `"01EZ2S259ZXW7ZNNRBPTRMTDVV"`; +exports[`string > 1211 > ulid > with string refDate 1`] = `"01EZ2S259ZXW7ZNNRBPTRMTDVV"`; exports[`string > 1211 > uuid 1`] = `"ee3faac5-bdca-4d6d-9d39-35fc6e8f34b8"`; @@ -544,21 +524,11 @@ exports[`string > 1337 > symbol > with length parameter 5`] = `"]'*@:"`; exports[`string > 1337 > symbol > with length range 1`] = `"&)/+;)~\\$-?%"`; -exports[`string > 1337 > ulid > noArgs 1`] = `"01HSCFQK89858EAG8ZQ3CM4ZES"`; - -exports[`string > 1337 > ulid > noArgs 2`] = `"01HSCFQK89SBDJR69NF5D6HT5Y"`; - -exports[`string > 1337 > ulid > noArgs 3`] = `"01HSCFQK89DGG0QZ54BP0B1SBP"`; - -exports[`string > 1337 > ulid > noArgs 4`] = `"01HSCFQK89FZTKJZ802YZFAQ0R"`; - -exports[`string > 1337 > ulid > noArgs 5`] = `"01HSCFQK89N6NX5XAP8G5EECSF"`; - -exports[`string > 1337 > ulid > with only Date refDate 1`] = `"01EZ2S259Z858EAG8ZQ3CM4ZES"`; +exports[`string > 1337 > ulid > with Date refDate 1`] = `"01EZ2S259Z858EAG8ZQ3CM4ZES"`; -exports[`string > 1337 > ulid > with only number refDate 1`] = `"01EZ2S259Z858EAG8ZQ3CM4ZES"`; +exports[`string > 1337 > ulid > with number refDate 1`] = `"01EZ2S259Z858EAG8ZQ3CM4ZES"`; -exports[`string > 1337 > ulid > with only string refDate 1`] = `"01EZ2S259Z858EAG8ZQ3CM4ZES"`; +exports[`string > 1337 > ulid > with string refDate 1`] = `"01EZ2S259Z858EAG8ZQ3CM4ZES"`; exports[`string > 1337 > uuid 1`] = `"4247584f-b16a-42f7-8cc5-69c34a72638d"`; diff --git a/test/modules/string.spec.ts b/test/modules/string.spec.ts index ccb6bde0640..7e383b5dab4 100644 --- a/test/modules/string.spec.ts +++ b/test/modules/string.spec.ts @@ -116,10 +116,9 @@ describe('string', () => { t.describe('ulid', (t) => { const ulidRefDate = '2021-02-21T17:09:15.711Z'; - t.itRepeated('noArgs', 5) - .it('with only string refDate', { refDate: ulidRefDate }) - .it('with only Date refDate', { refDate: new Date(ulidRefDate) }) - .it('with only number refDate', { + t.it('with string refDate', { refDate: ulidRefDate }) + .it('with Date refDate', { refDate: new Date(ulidRefDate) }) + .it('with number refDate', { refDate: new Date(ulidRefDate).getTime(), }); }); @@ -762,6 +761,14 @@ describe('string', () => { }); describe(`ulid`, () => { + it.each(['invalid', Number.NaN, new Date(Number.NaN)] as const)( + 'should reject invalid refDates %s', + (refDate) => { + expect(() => faker.string.ulid({ refDate })).toThrow( + new FakerError(`Invalid ULID refDate: ${refDate.toString()}`) + ); + } + ); it('generates a valid ULID', () => { const ulid = faker.string.ulid(); const regex = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/; From 1ca84cc006ebf0bcb3d7308701c54f82c6c419a0 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Fri, 5 Jul 2024 11:55:58 -0300 Subject: [PATCH 07/29] Update src/modules/string/index.ts Co-authored-by: ST-DDT --- src/modules/string/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index c10d1751364..fa536d7552b 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -713,7 +713,7 @@ export class StringModule extends SimpleModuleBase { * @example * faker.string.ulid() // '01ARZ3NDEKTSV4RRFFQ69G5FAV' * - * @since 8.2.0 + * @since 9.1.0 */ ulid( options: { From e762041f93c987b3cf5cb90811491ce371a6da74 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Fri, 5 Jul 2024 11:56:50 -0300 Subject: [PATCH 08/29] feat(internal): move 'toDate' function into an internal helper and updates references --- src/internal/date.ts | 19 +++++++++++++++++++ src/modules/date/index.ts | 19 +------------------ src/modules/string/index.ts | 6 ++---- test/modules/string.spec.ts | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 src/internal/date.ts diff --git a/src/internal/date.ts b/src/internal/date.ts new file mode 100644 index 00000000000..b6f9406274c --- /dev/null +++ b/src/internal/date.ts @@ -0,0 +1,19 @@ +import { FakerError } from "../errors/faker-error"; + +/** + * Converts a date passed as a `string`, `number` or `Date` to a valid `Date` object. + * + * @param date The date to convert. + * @param name The reference name used for error messages. Defaults to `'refDate'`. + * + * @throws If the given date is invalid. + */ +export function toDate(date: string | Date | number, name: string = 'refDate'): Date { + const converted = new Date(date); + + if (Number.isNaN(converted.valueOf())) { + throw new FakerError(`Invalid ${name} date: ${date.toString()}`); + } + + return converted; +} diff --git a/src/modules/date/index.ts b/src/modules/date/index.ts index f2eac1cf80e..761d54a3b31 100644 --- a/src/modules/date/index.ts +++ b/src/modules/date/index.ts @@ -3,24 +3,7 @@ import type { DateEntryDefinition } from '../../definitions'; import { FakerError } from '../../errors/faker-error'; import { SimpleModuleBase } from '../../internal/module-base'; import { assertLocaleData } from '../../locale-proxy'; - -/** - * Converts a date passed as a `string`, `number` or `Date` to a valid `Date` object. - * - * @param date The date to convert. - * @param name The reference name used for error messages. Defaults to `'refDate'`. - * - * @throws If the given date is invalid. - */ -function toDate(date: string | Date | number, name: string = 'refDate'): Date { - const converted = new Date(date); - - if (Number.isNaN(converted.valueOf())) { - throw new FakerError(`Invalid ${name} date: ${date.toString()}`); - } - - return converted; -} +import { toDate } from "../../internal/date"; /** * Module to generate dates (without methods requiring localized data). diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index c10d1751364..e2d59e3f6b0 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -1,6 +1,7 @@ import { FakerError } from '../../errors/faker-error'; import { SimpleModuleBase } from '../../internal/module-base'; import type { LiteralUnion } from '../../utils/types'; +import { toDate } from "../../internal/date"; export type Casing = 'upper' | 'lower' | 'mixed'; @@ -741,10 +742,7 @@ export class StringModule extends SimpleModuleBase { }; const { refDate = this.faker.defaultRefDate() } = options; - const converted = new Date(refDate); - if (Number.isNaN(converted.valueOf())) { - throw new FakerError(`Invalid ULID refDate: ${refDate.toString()}`); - } + const converted = toDate(refDate); return ( encodeTime(converted.getTime()) + diff --git a/test/modules/string.spec.ts b/test/modules/string.spec.ts index 7e383b5dab4..427267e6e43 100644 --- a/test/modules/string.spec.ts +++ b/test/modules/string.spec.ts @@ -765,7 +765,7 @@ describe('string', () => { 'should reject invalid refDates %s', (refDate) => { expect(() => faker.string.ulid({ refDate })).toThrow( - new FakerError(`Invalid ULID refDate: ${refDate.toString()}`) + new FakerError(`Invalid refDate date: ${refDate.toString()}`) ); } ); From 66158c306d8390a4518cbe75bd2f485a7e20a545 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Fri, 5 Jul 2024 12:19:42 -0300 Subject: [PATCH 09/29] feat(string): adds ulid example with refDate --- src/modules/string/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index faf15dea228..e4fc6b2c1ea 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -713,6 +713,7 @@ export class StringModule extends SimpleModuleBase { * * @example * faker.string.ulid() // '01ARZ3NDEKTSV4RRFFQ69G5FAV' + * faker.string.ulid({refDate: '2020-01-01T00:00:00.000Z'}) // '01DXF6DT00CX9QNNW7PNXQ3YR8' (first 10 digits will repeat for a same given date, as it represents encoded timestamp) * * @since 9.1.0 */ From 8d16f2b71eeb44e8fa9d67e1702e97dddb60b332 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Fri, 5 Jul 2024 12:42:46 -0300 Subject: [PATCH 10/29] feat(string): extract base32 methods into its own file --- src/internal/base32.ts | 20 ++++++++++++++++++++ src/internal/date.ts | 2 +- src/modules/string/index.ts | 25 ++++--------------------- 3 files changed, 25 insertions(+), 22 deletions(-) create mode 100644 src/internal/base32.ts diff --git a/src/internal/base32.ts b/src/internal/base32.ts new file mode 100644 index 00000000000..4c9b4f83292 --- /dev/null +++ b/src/internal/base32.ts @@ -0,0 +1,20 @@ +export const reducedBase32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford's Base32 - Excludes I, L, O, and U which may be confused with numbers + +/** + * Encodes a Date into 10 characters base32 string. + * @param date the Date to encode + */ +export const encodeDate = (date: Date): string => { + let now = date.getTime(); + + let mod; + let len = 10; + let str = ''; + for (; len > 0; len--) { + mod = now % reducedBase32.length; + str = reducedBase32.charAt(mod) + str; + now = (now - mod) / reducedBase32.length; + } + + return str; +}; diff --git a/src/internal/date.ts b/src/internal/date.ts index b6f9406274c..95962a74f35 100644 --- a/src/internal/date.ts +++ b/src/internal/date.ts @@ -1,4 +1,4 @@ -import { FakerError } from "../errors/faker-error"; +import { FakerError } from '../errors/faker-error'; /** * Converts a date passed as a `string`, `number` or `Date` to a valid `Date` object. diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index e4fc6b2c1ea..eb243c8072c 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -1,7 +1,8 @@ import { FakerError } from '../../errors/faker-error'; +import { encodeDate, reducedBase32 } from '../../internal/base32'; +import { toDate } from '../../internal/date'; import { SimpleModuleBase } from '../../internal/module-base'; import type { LiteralUnion } from '../../utils/types'; -import { toDate } from "../../internal/date"; export type Casing = 'upper' | 'lower' | 'mixed'; @@ -727,28 +728,10 @@ export class StringModule extends SimpleModuleBase { refDate?: string | Date | number; } = {} ): string { - const encodingCharacters = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford's Base32 - Excludes I, L, O, and U which may be confused with numbers - const encodingLength = encodingCharacters.length; - const encodeTime = (now: number) => { - let mod; - let len = 10; - let str = ''; - for (; len > 0; len--) { - mod = now % encodingLength; - str = encodingCharacters.charAt(mod) + str; - now = (now - mod) / encodingLength; - } - - return str; - }; - const { refDate = this.faker.defaultRefDate() } = options; - const converted = toDate(refDate); + const date = toDate(refDate); - return ( - encodeTime(converted.getTime()) + - this.fromCharacters(encodingCharacters, 16) - ); + return encodeDate(date) + this.fromCharacters(reducedBase32, 16); } /** From 9c53fb5226772c4e67a31b318801b7b12bd582e0 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Fri, 5 Jul 2024 12:53:35 -0300 Subject: [PATCH 11/29] feat(internal): adds tests for toDate --- test/internal/date.spec.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test/internal/date.spec.ts diff --git a/test/internal/date.spec.ts b/test/internal/date.spec.ts new file mode 100644 index 00000000000..eca2f846f74 --- /dev/null +++ b/test/internal/date.spec.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest'; +import { toDate } from '../../src/internal/date'; + +describe('toDate()', () => { + it('should convert a string date to a valid Date object', () => { + const dateString = '2024-07-05'; + expect(toDate(dateString)).toEqual(new Date(dateString)); + }); + + it('should convert a string datetime to a valid Date object', () => { + const timestamp = '2024-07-05T15:49:19+0000'; + expect(toDate(timestamp)).toEqual(new Date(timestamp)); + }); + + it('should throw a FakerError for an invalid date string', () => { + const timestamp = 'aaaa-07-05T15:49:19+0000'; + expect(() => toDate(timestamp)).toThrow( + `Invalid refDate date: ${timestamp}` + ); + }); +}); From ccadadf569c1406a85d92b996071575c9198c59c Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Fri, 5 Jul 2024 12:56:29 -0300 Subject: [PATCH 12/29] feat(internal): adds tests for base32 --- test/internal/base32.spec.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 test/internal/base32.spec.ts diff --git a/test/internal/base32.spec.ts b/test/internal/base32.spec.ts new file mode 100644 index 00000000000..f0c47d3868a --- /dev/null +++ b/test/internal/base32.spec.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from 'vitest'; +import { encodeDate } from "../../src/internal/base32"; + +describe('encodeDate()', ()=>{ + it('encodes current date correctly', () => { + const date = new Date('2023-04-01T00:00:00Z'); + const encoded = encodeDate(date); + expect(encoded).toHaveLength(10); + }); + + it('encodes epoch start date correctly', () => { + const date = new Date('1970-01-01T00:00:00Z'); + const encoded = encodeDate(date); + expect(encoded).toBe('0000000000'); + }); + + it('returns different encodings for dates one millisecond apart', () => { + const date1 = new Date('2023-04-01T00:00:00.000Z'); + const date2 = new Date('2023-04-01T00:00:00.001Z'); + const encoded1 = encodeDate(date1); + const encoded2 = encodeDate(date2); + expect(encoded1).not.toBe(encoded2); + }); + + it('encodes same date consistently', () => { + const date = new Date('2023-04-01T00:00:00Z'); + const encoded1 = encodeDate(date); + const encoded2 = encodeDate(date); + expect(encoded1).toBe(encoded2); + }); +}) From 43141fbf988468623d69106844f36815b89aba66 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Fri, 5 Jul 2024 12:59:43 -0300 Subject: [PATCH 13/29] feat(string): fix typo on ulid jsdoc --- src/modules/string/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index eb243c8072c..5e20023641f 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -714,7 +714,7 @@ export class StringModule extends SimpleModuleBase { * * @example * faker.string.ulid() // '01ARZ3NDEKTSV4RRFFQ69G5FAV' - * faker.string.ulid({refDate: '2020-01-01T00:00:00.000Z'}) // '01DXF6DT00CX9QNNW7PNXQ3YR8' (first 10 digits will repeat for a same given date, as it represents encoded timestamp) + * faker.string.ulid({refDate: '2020-01-01T00:00:00.000Z'}) // '01DXF6DT00CX9QNNW7PNXQ3YR8' (first 10 characters will repeat for a same given date, as it represents encoded timestamp) * * @since 9.1.0 */ From 56c57ea37cb2c24ee3bd1883a8adda6c7fa14c20 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Fri, 5 Jul 2024 20:11:38 -0300 Subject: [PATCH 14/29] feat(string): fix linting errors --- src/internal/date.ts | 5 ++++- src/modules/date/index.ts | 2 +- test/internal/base32.spec.ts | 6 +++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/internal/date.ts b/src/internal/date.ts index 95962a74f35..40751c2d997 100644 --- a/src/internal/date.ts +++ b/src/internal/date.ts @@ -8,7 +8,10 @@ import { FakerError } from '../errors/faker-error'; * * @throws If the given date is invalid. */ -export function toDate(date: string | Date | number, name: string = 'refDate'): Date { +export function toDate( + date: string | Date | number, + name: string = 'refDate' +): Date { const converted = new Date(date); if (Number.isNaN(converted.valueOf())) { diff --git a/src/modules/date/index.ts b/src/modules/date/index.ts index 761d54a3b31..7256cfc7b1e 100644 --- a/src/modules/date/index.ts +++ b/src/modules/date/index.ts @@ -1,9 +1,9 @@ import type { Faker } from '../..'; import type { DateEntryDefinition } from '../../definitions'; import { FakerError } from '../../errors/faker-error'; +import { toDate } from '../../internal/date'; import { SimpleModuleBase } from '../../internal/module-base'; import { assertLocaleData } from '../../locale-proxy'; -import { toDate } from "../../internal/date"; /** * Module to generate dates (without methods requiring localized data). diff --git a/test/internal/base32.spec.ts b/test/internal/base32.spec.ts index f0c47d3868a..55998929f89 100644 --- a/test/internal/base32.spec.ts +++ b/test/internal/base32.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; -import { encodeDate } from "../../src/internal/base32"; +import { encodeDate } from '../../src/internal/base32'; -describe('encodeDate()', ()=>{ +describe('encodeDate()', () => { it('encodes current date correctly', () => { const date = new Date('2023-04-01T00:00:00Z'); const encoded = encodeDate(date); @@ -28,4 +28,4 @@ describe('encodeDate()', ()=>{ const encoded2 = encodeDate(date); expect(encoded1).toBe(encoded2); }); -}) +}); From 54096d73e2618dcb7192b259fec5331850c69857 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Fri, 5 Jul 2024 20:13:50 -0300 Subject: [PATCH 15/29] Update src/internal/base32.ts Co-authored-by: ST-DDT --- src/internal/base32.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/internal/base32.ts b/src/internal/base32.ts index 4c9b4f83292..8b70d45ff11 100644 --- a/src/internal/base32.ts +++ b/src/internal/base32.ts @@ -2,6 +2,7 @@ export const reducedBase32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford's /** * Encodes a Date into 10 characters base32 string. + * * @param date the Date to encode */ export const encodeDate = (date: Date): string => { From 1c4d39dea74addd121f712c9065aa79b7e1811b7 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Fri, 5 Jul 2024 20:15:15 -0300 Subject: [PATCH 16/29] fix(string): fix ulid comment --- src/modules/string/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index 5e20023641f..cab7bd0b357 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -710,11 +710,11 @@ export class StringModule extends SimpleModuleBase { * Returns a ULID ([Universally Unique Lexicographically Sortable Identifier](https://github.com/ulid/spec)). * * @param options The optional options object. - * @param options.refDate The date to use as reference point for the newly generated ULID encoded timestamp. Defaults to `faker.defaultRefDate()`. + * @param options.refDate The date to use as reference point for the newly generated ULID encoded timestamp. Defaults to `faker.defaultRefDate()`. The first 10 characters will repeat for the same given date, as it represents encoded timestamp. * * @example * faker.string.ulid() // '01ARZ3NDEKTSV4RRFFQ69G5FAV' - * faker.string.ulid({refDate: '2020-01-01T00:00:00.000Z'}) // '01DXF6DT00CX9QNNW7PNXQ3YR8' (first 10 characters will repeat for a same given date, as it represents encoded timestamp) + * faker.string.ulid({refDate: '2020-01-01T00:00:00.000Z'}) // '01DXF6DT00CX9QNNW7PNXQ3YR8' * * @since 9.1.0 */ From 0a824fab055ccfdfe7bdfcb9680948c938e71ccd Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Fri, 5 Jul 2024 20:15:54 -0300 Subject: [PATCH 17/29] Update src/modules/string/index.ts Co-authored-by: ST-DDT --- src/modules/string/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index 5e20023641f..617a05ea177 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -710,7 +710,7 @@ export class StringModule extends SimpleModuleBase { * Returns a ULID ([Universally Unique Lexicographically Sortable Identifier](https://github.com/ulid/spec)). * * @param options The optional options object. - * @param options.refDate The date to use as reference point for the newly generated ULID encoded timestamp. Defaults to `faker.defaultRefDate()`. + * @param options.refDate The timestamp to encode into the ULID. Defaults to `faker.defaultRefDate()`. * * @example * faker.string.ulid() // '01ARZ3NDEKTSV4RRFFQ69G5FAV' From 2f5aa421ef0492fc6452ff17c95e6d0a46358aa1 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Fri, 5 Jul 2024 20:28:27 -0300 Subject: [PATCH 18/29] fix(string): adds test to match base32 characters and toMatchSnapshot() --- test/internal/__snapshots__/base32.spec.ts.snap | 3 +++ test/internal/base32.spec.ts | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 test/internal/__snapshots__/base32.spec.ts.snap diff --git a/test/internal/__snapshots__/base32.spec.ts.snap b/test/internal/__snapshots__/base32.spec.ts.snap new file mode 100644 index 00000000000..60a24e6f365 --- /dev/null +++ b/test/internal/__snapshots__/base32.spec.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`encodeDate() > encodes current date correctly 1`] = `"01GWX1T800"`; diff --git a/test/internal/base32.spec.ts b/test/internal/base32.spec.ts index 55998929f89..1287dc00a46 100644 --- a/test/internal/base32.spec.ts +++ b/test/internal/base32.spec.ts @@ -1,11 +1,14 @@ import { describe, expect, it } from 'vitest'; -import { encodeDate } from '../../src/internal/base32'; +import { encodeDate, reducedBase32 } from '../../src/internal/base32'; describe('encodeDate()', () => { it('encodes current date correctly', () => { const date = new Date('2023-04-01T00:00:00Z'); const encoded = encodeDate(date); expect(encoded).toHaveLength(10); + expect(encoded).toMatchSnapshot(); + for (const char of encoded) + expect(reducedBase32.includes(char)).toBeTruthy(); }); it('encodes epoch start date correctly', () => { From 34316a624821df931645cabf82017baf70d8ce62 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Mon, 8 Jul 2024 08:56:45 -0300 Subject: [PATCH 19/29] Update src/internal/base32.ts Co-authored-by: ST-DDT --- src/internal/base32.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/base32.ts b/src/internal/base32.ts index 8b70d45ff11..e12710c9e5b 100644 --- a/src/internal/base32.ts +++ b/src/internal/base32.ts @@ -3,7 +3,7 @@ export const reducedBase32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford's /** * Encodes a Date into 10 characters base32 string. * - * @param date the Date to encode + * @param date The Date to encode. */ export const encodeDate = (date: Date): string => { let now = date.getTime(); From c3b86979c2f907dca574ba28908195b828394349 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Mon, 8 Jul 2024 08:58:54 -0300 Subject: [PATCH 20/29] Update src/internal/base32.ts Co-authored-by: ST-DDT --- src/internal/base32.ts | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/internal/base32.ts b/src/internal/base32.ts index e12710c9e5b..0df6e458dee 100644 --- a/src/internal/base32.ts +++ b/src/internal/base32.ts @@ -5,17 +5,14 @@ export const reducedBase32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford's * * @param date The Date to encode. */ -export const encodeDate = (date: Date): string => { - let now = date.getTime(); - - let mod; - let len = 10; - let str = ''; - for (; len > 0; len--) { - mod = now % reducedBase32.length; - str = reducedBase32.charAt(mod) + str; - now = (now - mod) / reducedBase32.length; +export function encodeDate(date: Date): string { + let value = date.valueOf(); + let result = ''; + for (let len = 10; len > 0; len--) { + const mod = value % 32; + result = reducedBase32[mod] + result; + value = (value - mod) / 32; } - return str; -}; + return result; +} From ec1fd5be8f2de957cf4fab3534f6a442f2c6906d Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Mon, 8 Jul 2024 08:59:41 -0300 Subject: [PATCH 21/29] Update test/internal/base32.spec.ts Co-authored-by: ST-DDT --- test/internal/base32.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/internal/base32.spec.ts b/test/internal/base32.spec.ts index 1287dc00a46..2b490f79477 100644 --- a/test/internal/base32.spec.ts +++ b/test/internal/base32.spec.ts @@ -7,8 +7,9 @@ describe('encodeDate()', () => { const encoded = encodeDate(date); expect(encoded).toHaveLength(10); expect(encoded).toMatchSnapshot(); - for (const char of encoded) - expect(reducedBase32.includes(char)).toBeTruthy(); + for (const char of encoded) { + expect(reducedBase32).toContain(char); + } }); it('encodes epoch start date correctly', () => { From 7e6ba0a97296694a147689000bedfb4620ffbce9 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Mon, 8 Jul 2024 09:03:35 -0300 Subject: [PATCH 22/29] fix(string): refactoring and lint fixes --- src/internal/base32.ts | 9 ++++++--- src/modules/string/index.ts | 4 ++-- test/internal/base32.spec.ts | 16 ++++++++-------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/internal/base32.ts b/src/internal/base32.ts index 0df6e458dee..f3e15bb43de 100644 --- a/src/internal/base32.ts +++ b/src/internal/base32.ts @@ -1,16 +1,19 @@ -export const reducedBase32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford's Base32 - Excludes I, L, O, and U which may be confused with numbers +/** + * Crockford's Base32 - Excludes I, L, O, and U which may be confused with numbers + */ +export const CROCKFORDS_BASE32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; /** * Encodes a Date into 10 characters base32 string. * * @param date The Date to encode. */ -export function encodeDate(date: Date): string { +export function dateToBase32(date: Date): string { let value = date.valueOf(); let result = ''; for (let len = 10; len > 0; len--) { const mod = value % 32; - result = reducedBase32[mod] + result; + result = CROCKFORDS_BASE32[mod] + result; value = (value - mod) / 32; } diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index 6c7156a0a9c..158e71d736c 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -1,5 +1,5 @@ import { FakerError } from '../../errors/faker-error'; -import { encodeDate, reducedBase32 } from '../../internal/base32'; +import { CROCKFORDS_BASE32, dateToBase32 } from '../../internal/base32'; import { toDate } from '../../internal/date'; import { SimpleModuleBase } from '../../internal/module-base'; import type { LiteralUnion } from '../../utils/types'; @@ -731,7 +731,7 @@ export class StringModule extends SimpleModuleBase { const { refDate = this.faker.defaultRefDate() } = options; const date = toDate(refDate); - return encodeDate(date) + this.fromCharacters(reducedBase32, 16); + return dateToBase32(date) + this.fromCharacters(CROCKFORDS_BASE32, 16); } /** diff --git a/test/internal/base32.spec.ts b/test/internal/base32.spec.ts index 1287dc00a46..a615cd220cc 100644 --- a/test/internal/base32.spec.ts +++ b/test/internal/base32.spec.ts @@ -1,34 +1,34 @@ import { describe, expect, it } from 'vitest'; -import { encodeDate, reducedBase32 } from '../../src/internal/base32'; +import { CROCKFORDS_BASE32, dateToBase32 } from '../../src/internal/base32'; describe('encodeDate()', () => { it('encodes current date correctly', () => { const date = new Date('2023-04-01T00:00:00Z'); - const encoded = encodeDate(date); + const encoded = dateToBase32(date); expect(encoded).toHaveLength(10); expect(encoded).toMatchSnapshot(); for (const char of encoded) - expect(reducedBase32.includes(char)).toBeTruthy(); + expect(CROCKFORDS_BASE32.includes(char)).toBeTruthy(); }); it('encodes epoch start date correctly', () => { const date = new Date('1970-01-01T00:00:00Z'); - const encoded = encodeDate(date); + const encoded = dateToBase32(date); expect(encoded).toBe('0000000000'); }); it('returns different encodings for dates one millisecond apart', () => { const date1 = new Date('2023-04-01T00:00:00.000Z'); const date2 = new Date('2023-04-01T00:00:00.001Z'); - const encoded1 = encodeDate(date1); - const encoded2 = encodeDate(date2); + const encoded1 = dateToBase32(date1); + const encoded2 = dateToBase32(date2); expect(encoded1).not.toBe(encoded2); }); it('encodes same date consistently', () => { const date = new Date('2023-04-01T00:00:00Z'); - const encoded1 = encodeDate(date); - const encoded2 = encodeDate(date); + const encoded1 = dateToBase32(date); + const encoded2 = dateToBase32(date); expect(encoded1).toBe(encoded2); }); }); From d1d3d2381487d2303baf07d665995617c7cc1e9b Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Mon, 8 Jul 2024 11:28:54 -0300 Subject: [PATCH 23/29] Update src/modules/string/index.ts Co-authored-by: ST-DDT --- src/modules/string/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index 158e71d736c..b8ed4737c7f 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -722,6 +722,7 @@ export class StringModule extends SimpleModuleBase { options: { /** * The date to use as reference point for the newly generated ULID encoded timestamp. + * The encoded timestamp is represented by the first 10 characters of the result. * * @default faker.defaultRefDate() */ From 1d318b4cd3bd972e5c758888e7462bdf115ac4d0 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Mon, 8 Jul 2024 11:29:44 -0300 Subject: [PATCH 24/29] Update src/modules/string/index.ts Co-authored-by: ST-DDT --- src/modules/string/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index b8ed4737c7f..57407d3cb46 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -710,7 +710,9 @@ export class StringModule extends SimpleModuleBase { * Returns a ULID ([Universally Unique Lexicographically Sortable Identifier](https://github.com/ulid/spec)). * * @param options The optional options object. - * @param options.refDate The timestamp to encode into the ULID. Defaults to `faker.defaultRefDate()`. The first 10 characters will repeat for the same given date, as it represents encoded timestamp. + * @param options.refDate The timestamp to encode into the ULID. + * The encoded timestamp is represented by the first 10 characters of the result. + * Defaults to `faker.defaultRefDate()`. * * @example * faker.string.ulid() // '01ARZ3NDEKTSV4RRFFQ69G5FAV' From c7080e17e3bdb3ee203c7de234a8046f20e09421 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Mon, 8 Jul 2024 16:08:55 -0300 Subject: [PATCH 25/29] Update test/internal/base32.spec.ts Co-authored-by: DivisionByZero --- test/internal/base32.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/internal/base32.spec.ts b/test/internal/base32.spec.ts index a86c732dc07..c87ffd61698 100644 --- a/test/internal/base32.spec.ts +++ b/test/internal/base32.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { CROCKFORDS_BASE32, dateToBase32 } from '../../src/internal/base32'; -describe('encodeDate()', () => { +describe('dateToBase32()', () => { it('encodes current date correctly', () => { const date = new Date('2023-04-01T00:00:00Z'); const encoded = dateToBase32(date); From 52c28725c36f3695d708ba7a646f031923d90853 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Mon, 8 Jul 2024 16:09:09 -0300 Subject: [PATCH 26/29] Update src/modules/string/index.ts Co-authored-by: DivisionByZero --- src/modules/string/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index 57407d3cb46..e2d8366ddbe 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -716,7 +716,7 @@ export class StringModule extends SimpleModuleBase { * * @example * faker.string.ulid() // '01ARZ3NDEKTSV4RRFFQ69G5FAV' - * faker.string.ulid({refDate: '2020-01-01T00:00:00.000Z'}) // '01DXF6DT00CX9QNNW7PNXQ3YR8' + * faker.string.ulid({ refDate: '2020-01-01T00:00:00.000Z' }) // '01DXF6DT00CX9QNNW7PNXQ3YR8' * * @since 9.1.0 */ From af7376d77bca1f88817b599b4fb44765be7063c1 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Mon, 8 Jul 2024 16:10:02 -0300 Subject: [PATCH 27/29] Update test/internal/date.spec.ts Co-authored-by: DivisionByZero --- test/internal/date.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/internal/date.spec.ts b/test/internal/date.spec.ts index eca2f846f74..ec7475f50ef 100644 --- a/test/internal/date.spec.ts +++ b/test/internal/date.spec.ts @@ -15,7 +15,7 @@ describe('toDate()', () => { it('should throw a FakerError for an invalid date string', () => { const timestamp = 'aaaa-07-05T15:49:19+0000'; expect(() => toDate(timestamp)).toThrow( - `Invalid refDate date: ${timestamp}` + new FakerError(`Invalid refDate date: ${timestamp}`) ); }); }); From 1d63041baf5d1e3b8029f645939ba863e77a3069 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Mon, 8 Jul 2024 16:10:08 -0300 Subject: [PATCH 28/29] Update test/modules/string.spec.ts Co-authored-by: DivisionByZero --- test/modules/string.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/modules/string.spec.ts b/test/modules/string.spec.ts index 427267e6e43..7b3be347749 100644 --- a/test/modules/string.spec.ts +++ b/test/modules/string.spec.ts @@ -769,6 +769,7 @@ describe('string', () => { ); } ); + it('generates a valid ULID', () => { const ulid = faker.string.ulid(); const regex = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/; From b02d751b11204e3aaaf190e51284d5bf2166df82 Mon Sep 17 00:00:00 2001 From: Bruno Leite Date: Mon, 8 Jul 2024 16:14:21 -0300 Subject: [PATCH 29/29] fix(string): import fix and test snapshot --- test/internal/__snapshots__/base32.spec.ts.snap | 2 +- test/internal/date.spec.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/test/internal/__snapshots__/base32.spec.ts.snap b/test/internal/__snapshots__/base32.spec.ts.snap index 60a24e6f365..d05f1586bfd 100644 --- a/test/internal/__snapshots__/base32.spec.ts.snap +++ b/test/internal/__snapshots__/base32.spec.ts.snap @@ -1,3 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`encodeDate() > encodes current date correctly 1`] = `"01GWX1T800"`; +exports[`dateToBase32() > encodes current date correctly 1`] = `"01GWX1T800"`; diff --git a/test/internal/date.spec.ts b/test/internal/date.spec.ts index ec7475f50ef..453dae792e8 100644 --- a/test/internal/date.spec.ts +++ b/test/internal/date.spec.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; +import { FakerError } from '../../src'; import { toDate } from '../../src/internal/date'; describe('toDate()', () => {