Skip to content

Commit 75a31f6

Browse files
authored
feat(helpers): fake from array (#1453)
1 parent 4ce8e98 commit 75a31f6

5 files changed

Lines changed: 171 additions & 39 deletions

File tree

src/modules/company/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,9 @@ export class CompanyModule {
3636
* @since 7.4.0
3737
*/
3838
name(): string {
39-
const pattern = this.faker.helpers.arrayElement(
39+
return this.faker.helpers.fake(
4040
this.faker.definitions.company.name_patterns
4141
);
42-
43-
return this.faker.helpers.fake(pattern);
4442
}
4543

4644
/**

src/modules/helpers/index.ts

Lines changed: 117 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ export class HelpersModule {
506506
* It checks the given string for placeholders and replaces them by calling faker methods:
507507
*
508508
* ```js
509-
* const hello = faker.helpers.fake('Hi, my name is {{person.firstName}} {{person.lastName}}!')
509+
* const hello = faker.helpers.fake('Hi, my name is {{person.firstName}} {{person.lastName}}!');
510510
* ```
511511
*
512512
* This would use the `faker.person.firstName()` and `faker.person.lastName()` method to resolve the placeholders respectively.
@@ -515,18 +515,18 @@ export class HelpersModule {
515515
* and if that isn't possible, we will fall back to string:
516516
*
517517
* ```js
518-
* const message = faker.helpers.fake('You can call me at {{phone.number(+!# !## #### #####!)}}.')
518+
* const message = faker.helpers.fake('You can call me at {{phone.number(+!# !## #### #####!)}}.');
519519
* ```
520520
*
521521
* It is also possible to use multiple parameters (comma separated).
522522
*
523523
* ```js
524-
* const message = faker.helpers.fake('Your pin is {{string.numeric(4, {"allowLeadingZeros": true})}}.')
524+
* const message = faker.helpers.fake('Your pin is {{string.numeric(4, {"allowLeadingZeros": true})}}.');
525525
* ```
526526
*
527-
* It is also NOT possible to use any non-faker methods or plain javascript in such templates.
527+
* It is also NOT possible to use any non-faker methods or plain javascript in such patterns.
528528
*
529-
* @param str The template string that will get interpolated. Must not be empty.
529+
* @param pattern The pattern string that will get interpolated. Must not be empty.
530530
*
531531
* @see faker.helpers.mustache() to use custom functions for resolution.
532532
*
@@ -541,24 +541,127 @@ export class HelpersModule {
541541
*
542542
* @since 7.4.0
543543
*/
544-
fake(str: string): string {
544+
fake(pattern: string): string;
545+
/**
546+
* Generator for combining faker methods based on an array containing static string inputs.
547+
*
548+
* Note: We recommend using string template literals instead of `fake()`,
549+
* which are faster and strongly typed (if you are using TypeScript),
550+
* e.g. ``const address = `${faker.location.zipCode()} ${faker.location.city()}`;``
551+
*
552+
* This method is useful if you have to build a random string from a static, non-executable source
553+
* (e.g. string coming from a user, stored in a database or a file).
554+
*
555+
* It checks the given string for placeholders and replaces them by calling faker methods:
556+
*
557+
* ```js
558+
* const hello = faker.helpers.fake(['Hi, my name is {{person.firstName}} {{person.lastName}}!']);
559+
* ```
560+
*
561+
* This would use the `faker.person.firstName()` and `faker.person.lastName()` method to resolve the placeholders respectively.
562+
*
563+
* It is also possible to provide parameters. At first, they will be parsed as json,
564+
* and if that isn't possible, it will fall back to string:
565+
*
566+
* ```js
567+
* const message = faker.helpers.fake([
568+
* 'You can call me at {{phone.number(+!# !## #### #####!)}}.',
569+
* 'My email is {{internet.email}}.',
570+
* ]);
571+
* ```
572+
*
573+
* It is also possible to use multiple parameters (comma separated).
574+
*
575+
* ```js
576+
* const message = faker.helpers.fake(['Your pin is {{string.numeric(4, {"allowLeadingZeros": true})}}.']);
577+
* ```
578+
*
579+
* It is also NOT possible to use any non-faker methods or plain javascript in such patterns.
580+
*
581+
* @param patterns The array to select a pattern from, that will then get interpolated. Must not be empty.
582+
*
583+
* @see faker.helpers.mustache() to use custom functions for resolution.
584+
*
585+
* @example
586+
* faker.helpers.fake(['A: {{person.firstName}}', 'B: {{person.lastName}}']) // 'A: Barry'
587+
*
588+
* @since 8.0.0
589+
*/
590+
fake(patterns: string[]): string;
591+
/**
592+
* Generator for combining faker methods based on a static string input or an array of static string inputs.
593+
*
594+
* Note: We recommend using string template literals instead of `fake()`,
595+
* which are faster and strongly typed (if you are using TypeScript),
596+
* e.g. ``const address = `${faker.location.zipCode()} ${faker.location.city()}`;``
597+
*
598+
* This method is useful if you have to build a random string from a static, non-executable source
599+
* (e.g. string coming from a user, stored in a database or a file).
600+
*
601+
* It checks the given string for placeholders and replaces them by calling faker methods:
602+
*
603+
* ```js
604+
* const hello = faker.helpers.fake('Hi, my name is {{person.firstName}} {{person.lastName}}!');
605+
* ```
606+
*
607+
* This would use the `faker.person.firstName()` and `faker.person.lastName()` method to resolve the placeholders respectively.
608+
*
609+
* It is also possible to provide parameters. At first, they will be parsed as json,
610+
* and if that isn't possible, it will fall back to string:
611+
*
612+
* ```js
613+
* const message = faker.helpers.fake('You can call me at {{phone.number(+!# !## #### #####!)}}.');
614+
* ```
615+
*
616+
* It is also possible to use multiple parameters (comma separated).
617+
*
618+
* ```js
619+
* const message = faker.helpers.fake('Your pin is {{string.numeric(4, {"allowLeadingZeros": true})}}.');
620+
* ```
621+
*
622+
* It is also NOT possible to use any non-faker methods or plain javascript in such patterns.
623+
*
624+
* @param pattern The pattern string that will get interpolated. Must not be empty. If an array is passed, a random element will be picked and interpolated.
625+
*
626+
* @see faker.helpers.mustache() to use custom functions for resolution.
627+
*
628+
* @example
629+
* faker.helpers.fake('{{person.lastName}}') // 'Barrows'
630+
* faker.helpers.fake('{{person.lastName}}, {{person.firstName}} {{person.suffix}}') // 'Durgan, Noe MD'
631+
* faker.helpers.fake('This is static test.') // 'This is static test.'
632+
* faker.helpers.fake('Good Morning {{person.firstName}}!') // 'Good Morning Estelle!'
633+
* faker.helpers.fake('You can call me at {{phone.number(!## ### #####!)}}.') // 'You can call me at 202 555 973722.'
634+
* faker.helpers.fake('I flipped the coin and got: {{helpers.arrayElement(["heads", "tails"])}}') // 'I flipped the coin and got: tails'
635+
* faker.helpers.fake(['A: {{person.firstName}}', 'B: {{person.lastName}}']) // 'A: Barry'
636+
*
637+
* @since 7.4.0
638+
*/
639+
fake(pattern: string | string[]): string;
640+
fake(pattern: string | string[]): string {
641+
if (Array.isArray(pattern)) {
642+
pattern = this.arrayElement(pattern);
643+
// TODO @ST-DDT 2022-10-15: Remove this check after we fail in `arrayElement` when the array is empty
644+
if (pattern == null) {
645+
throw new FakerError('Array of pattern strings cannot be empty.');
646+
}
647+
}
545648
// if incoming str parameter is not provided, return error message
546-
if (typeof str !== 'string' || str.length === 0) {
547-
throw new FakerError('string parameter is required!');
649+
if (pattern.length === 0) {
650+
throw new FakerError('Pattern string cannot be empty.');
548651
}
549652

550653
// find first matching {{ and }}
551-
const start = str.search(/{{[a-z]/);
552-
const end = str.indexOf('}}', start);
654+
const start = pattern.search(/{{[a-z]/);
655+
const end = pattern.indexOf('}}', start);
553656

554657
// if no {{ and }} is found, we are done
555658
if (start === -1 || end === -1) {
556-
return str;
659+
return pattern;
557660
}
558661

559662
// extract method name from between the {{ }} that we found
560663
// for example: {{person.firstName}}
561-
const token = str.substring(start + 2, end + 2);
664+
const token = pattern.substring(start + 2, end + 2);
562665
let method = token.replace('}}', '').replace('{{', '');
563666

564667
// extract method parameters
@@ -615,7 +718,8 @@ export class HelpersModule {
615718

616719
// Replace the found tag with the returned fake value
617720
// We cannot use string.replace here because the result might contain evaluated characters
618-
const res = str.substring(0, start) + result + str.substring(end + 2);
721+
const res =
722+
pattern.substring(0, start) + result + pattern.substring(end + 2);
619723

620724
if (res === '') {
621725
return '';

src/modules/location/index.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,7 @@ export class LocationModule {
7575
* @since 8.0.0
7676
*/
7777
city(): string {
78-
const pattern = this.faker.helpers.arrayElement(
79-
this.faker.definitions.location.city
80-
);
81-
return this.faker.helpers.fake(pattern);
78+
return this.faker.helpers.fake(this.faker.definitions.location.city);
8279
}
8380

8481
/**
@@ -121,10 +118,7 @@ export class LocationModule {
121118
* @since 8.0.0
122119
*/
123120
street(): string {
124-
const format = this.faker.helpers.arrayElement(
125-
this.faker.definitions.location.street
126-
);
127-
return this.faker.helpers.fake(format);
121+
return this.faker.helpers.fake(this.faker.definitions.location.street);
128122
}
129123

130124
/**

test/__snapshots__/helpers.spec.ts.snap

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@ exports[`helpers > 42 > arrayElements > with array and count 1`] = `
2929
]
3030
`;
3131

32-
exports[`helpers > 42 > fake > with args 1`] = `"my string: Cky2eiXX/J"`;
32+
exports[`helpers > 42 > fake > with a dynamic template 1`] = `"my string: Cky2eiXX/J"`;
3333

34-
exports[`helpers > 42 > fake > with plain string 1`] = `"my test string"`;
34+
exports[`helpers > 42 > fake > with a static template 1`] = `"my test string"`;
35+
36+
exports[`helpers > 42 > fake > with multiple dynamic templates 1`] = `"Sandy"`;
37+
38+
exports[`helpers > 42 > fake > with multiple static templates 1`] = `"B"`;
3539

3640
exports[`helpers > 42 > maybe > with only value 1`] = `"Hello World!"`;
3741

@@ -208,9 +212,13 @@ exports[`helpers > 1211 > arrayElements > with array and count 1`] = `
208212
]
209213
`;
210214

211-
exports[`helpers > 1211 > fake > with args 1`] = `"my string: wKti5-}$_/"`;
215+
exports[`helpers > 1211 > fake > with a dynamic template 1`] = `"my string: wKti5-}$_/"`;
216+
217+
exports[`helpers > 1211 > fake > with a static template 1`] = `"my test string"`;
212218

213-
exports[`helpers > 1211 > fake > with plain string 1`] = `"my test string"`;
219+
exports[`helpers > 1211 > fake > with multiple dynamic templates 1`] = `"La Crosse"`;
220+
221+
exports[`helpers > 1211 > fake > with multiple static templates 1`] = `"C"`;
214222

215223
exports[`helpers > 1211 > maybe > with only value 1`] = `undefined`;
216224

@@ -383,9 +391,13 @@ exports[`helpers > 1337 > arrayElements > with array and count 1`] = `
383391
]
384392
`;
385393

386-
exports[`helpers > 1337 > fake > with args 1`] = `"my string: 9U/4:SK$>6"`;
394+
exports[`helpers > 1337 > fake > with a dynamic template 1`] = `"my string: 9U/4:SK$>6"`;
395+
396+
exports[`helpers > 1337 > fake > with a static template 1`] = `"my test string"`;
397+
398+
exports[`helpers > 1337 > fake > with multiple dynamic templates 1`] = `"U/4:SK$>6Q"`;
387399

388-
exports[`helpers > 1337 > fake > with plain string 1`] = `"my test string"`;
400+
exports[`helpers > 1337 > fake > with multiple static templates 1`] = `"A"`;
389401

390402
exports[`helpers > 1337 > maybe > with only value 1`] = `"Hello World!"`;
391403

test/helpers.spec.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,14 @@ describe('helpers', () => {
9696
});
9797

9898
t.describe('fake', (t) => {
99-
t.it('with plain string', 'my test string').it(
100-
'with args',
101-
'my string: {{datatype.string}}'
102-
);
99+
t.it('with a static template', 'my test string')
100+
.it('with a dynamic template', 'my string: {{string.sample}}')
101+
.it('with multiple static templates', ['A', 'B', 'C'])
102+
.it('with multiple dynamic templates', [
103+
'{{string.sample}}',
104+
'{{location.city_name}}',
105+
'{{location.cityName}}',
106+
]);
103107
});
104108

105109
t.describe('rangeToNumber', (t) => {
@@ -598,11 +602,16 @@ describe('helpers', () => {
598602
expect(actual).toMatch(/^\d{5}$/);
599603
});
600604

601-
it('does not allow undefined parameters', () => {
602-
expect(() =>
603-
// @ts-expect-error: The parameter is required
604-
faker.helpers.fake()
605-
).toThrowError(new FakerError('string parameter is required!'));
605+
it('does not allow empty string parameters', () => {
606+
expect(() => faker.helpers.fake('')).toThrowError(
607+
new FakerError('Pattern string cannot be empty.')
608+
);
609+
});
610+
611+
it('does not allow empty array parameters', () => {
612+
expect(() => faker.helpers.fake([])).toThrowError(
613+
new FakerError('Array of pattern strings cannot be empty.')
614+
);
606615
});
607616

608617
it('does not allow invalid module name', () => {
@@ -655,6 +664,21 @@ describe('helpers', () => {
655664
);
656665
});
657666

667+
it('should be able to pass multiple static templates', () => {
668+
expect(['A', 'B', 'C']).toContain(
669+
faker.helpers.fake(['A', 'B', 'C'])
670+
);
671+
});
672+
673+
it('should be able to pass multiple dynamic templates', () => {
674+
expect(faker.definitions.location.city_name).toContain(
675+
faker.helpers.fake([
676+
'{{location.city_name}}',
677+
'{{location.cityName}}',
678+
])
679+
);
680+
});
681+
658682
it('should be able to handle only {{ brackets', () => {
659683
expect(faker.helpers.fake('{{hello')).toBe('{{hello');
660684
expect(faker.helpers.fake('hello{{')).toBe('hello{{');

0 commit comments

Comments
 (0)