Skip to content

Commit

Permalink
feat(helpers): fake from array (#1453)
Browse files Browse the repository at this point in the history
  • Loading branch information
ST-DDT committed Dec 23, 2022
1 parent 4ce8e98 commit 75a31f6
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 39 deletions.
4 changes: 1 addition & 3 deletions src/modules/company/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,9 @@ export class CompanyModule {
* @since 7.4.0
*/
name(): string {
const pattern = this.faker.helpers.arrayElement(
return this.faker.helpers.fake(
this.faker.definitions.company.name_patterns
);

return this.faker.helpers.fake(pattern);
}

/**
Expand Down
130 changes: 117 additions & 13 deletions src/modules/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ export class HelpersModule {
* It checks the given string for placeholders and replaces them by calling faker methods:
*
* ```js
* const hello = faker.helpers.fake('Hi, my name is {{person.firstName}} {{person.lastName}}!')
* const hello = faker.helpers.fake('Hi, my name is {{person.firstName}} {{person.lastName}}!');
* ```
*
* This would use the `faker.person.firstName()` and `faker.person.lastName()` method to resolve the placeholders respectively.
Expand All @@ -515,18 +515,18 @@ export class HelpersModule {
* and if that isn't possible, we will fall back to string:
*
* ```js
* const message = faker.helpers.fake('You can call me at {{phone.number(+!# !## #### #####!)}}.')
* const message = faker.helpers.fake('You can call me at {{phone.number(+!# !## #### #####!)}}.');
* ```
*
* It is also possible to use multiple parameters (comma separated).
*
* ```js
* const message = faker.helpers.fake('Your pin is {{string.numeric(4, {"allowLeadingZeros": true})}}.')
* const message = faker.helpers.fake('Your pin is {{string.numeric(4, {"allowLeadingZeros": true})}}.');
* ```
*
* It is also NOT possible to use any non-faker methods or plain javascript in such templates.
* It is also NOT possible to use any non-faker methods or plain javascript in such patterns.
*
* @param str The template string that will get interpolated. Must not be empty.
* @param pattern The pattern string that will get interpolated. Must not be empty.
*
* @see faker.helpers.mustache() to use custom functions for resolution.
*
Expand All @@ -541,24 +541,127 @@ export class HelpersModule {
*
* @since 7.4.0
*/
fake(str: string): string {
fake(pattern: string): string;
/**
* Generator for combining faker methods based on an array containing static string inputs.
*
* Note: We recommend using string template literals instead of `fake()`,
* which are faster and strongly typed (if you are using TypeScript),
* e.g. ``const address = `${faker.location.zipCode()} ${faker.location.city()}`;``
*
* This method is useful if you have to build a random string from a static, non-executable source
* (e.g. string coming from a user, stored in a database or a file).
*
* It checks the given string for placeholders and replaces them by calling faker methods:
*
* ```js
* const hello = faker.helpers.fake(['Hi, my name is {{person.firstName}} {{person.lastName}}!']);
* ```
*
* This would use the `faker.person.firstName()` and `faker.person.lastName()` method to resolve the placeholders respectively.
*
* It is also possible to provide parameters. At first, they will be parsed as json,
* and if that isn't possible, it will fall back to string:
*
* ```js
* const message = faker.helpers.fake([
* 'You can call me at {{phone.number(+!# !## #### #####!)}}.',
* 'My email is {{internet.email}}.',
* ]);
* ```
*
* It is also possible to use multiple parameters (comma separated).
*
* ```js
* const message = faker.helpers.fake(['Your pin is {{string.numeric(4, {"allowLeadingZeros": true})}}.']);
* ```
*
* It is also NOT possible to use any non-faker methods or plain javascript in such patterns.
*
* @param patterns The array to select a pattern from, that will then get interpolated. Must not be empty.
*
* @see faker.helpers.mustache() to use custom functions for resolution.
*
* @example
* faker.helpers.fake(['A: {{person.firstName}}', 'B: {{person.lastName}}']) // 'A: Barry'
*
* @since 8.0.0
*/
fake(patterns: string[]): string;
/**
* Generator for combining faker methods based on a static string input or an array of static string inputs.
*
* Note: We recommend using string template literals instead of `fake()`,
* which are faster and strongly typed (if you are using TypeScript),
* e.g. ``const address = `${faker.location.zipCode()} ${faker.location.city()}`;``
*
* This method is useful if you have to build a random string from a static, non-executable source
* (e.g. string coming from a user, stored in a database or a file).
*
* It checks the given string for placeholders and replaces them by calling faker methods:
*
* ```js
* const hello = faker.helpers.fake('Hi, my name is {{person.firstName}} {{person.lastName}}!');
* ```
*
* This would use the `faker.person.firstName()` and `faker.person.lastName()` method to resolve the placeholders respectively.
*
* It is also possible to provide parameters. At first, they will be parsed as json,
* and if that isn't possible, it will fall back to string:
*
* ```js
* const message = faker.helpers.fake('You can call me at {{phone.number(+!# !## #### #####!)}}.');
* ```
*
* It is also possible to use multiple parameters (comma separated).
*
* ```js
* const message = faker.helpers.fake('Your pin is {{string.numeric(4, {"allowLeadingZeros": true})}}.');
* ```
*
* It is also NOT possible to use any non-faker methods or plain javascript in such patterns.
*
* @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.
*
* @see faker.helpers.mustache() to use custom functions for resolution.
*
* @example
* faker.helpers.fake('{{person.lastName}}') // 'Barrows'
* faker.helpers.fake('{{person.lastName}}, {{person.firstName}} {{person.suffix}}') // 'Durgan, Noe MD'
* faker.helpers.fake('This is static test.') // 'This is static test.'
* faker.helpers.fake('Good Morning {{person.firstName}}!') // 'Good Morning Estelle!'
* faker.helpers.fake('You can call me at {{phone.number(!## ### #####!)}}.') // 'You can call me at 202 555 973722.'
* faker.helpers.fake('I flipped the coin and got: {{helpers.arrayElement(["heads", "tails"])}}') // 'I flipped the coin and got: tails'
* faker.helpers.fake(['A: {{person.firstName}}', 'B: {{person.lastName}}']) // 'A: Barry'
*
* @since 7.4.0
*/
fake(pattern: string | string[]): string;
fake(pattern: string | string[]): string {
if (Array.isArray(pattern)) {
pattern = this.arrayElement(pattern);
// TODO @ST-DDT 2022-10-15: Remove this check after we fail in `arrayElement` when the array is empty
if (pattern == null) {
throw new FakerError('Array of pattern strings cannot be empty.');
}
}
// if incoming str parameter is not provided, return error message
if (typeof str !== 'string' || str.length === 0) {
throw new FakerError('string parameter is required!');
if (pattern.length === 0) {
throw new FakerError('Pattern string cannot be empty.');
}

// find first matching {{ and }}
const start = str.search(/{{[a-z]/);
const end = str.indexOf('}}', start);
const start = pattern.search(/{{[a-z]/);
const end = pattern.indexOf('}}', start);

// if no {{ and }} is found, we are done
if (start === -1 || end === -1) {
return str;
return pattern;
}

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

// extract method parameters
Expand Down Expand Up @@ -615,7 +718,8 @@ export class HelpersModule {

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

if (res === '') {
return '';
Expand Down
10 changes: 2 additions & 8 deletions src/modules/location/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,7 @@ export class LocationModule {
* @since 8.0.0
*/
city(): string {
const pattern = this.faker.helpers.arrayElement(
this.faker.definitions.location.city
);
return this.faker.helpers.fake(pattern);
return this.faker.helpers.fake(this.faker.definitions.location.city);
}

/**
Expand Down Expand Up @@ -121,10 +118,7 @@ export class LocationModule {
* @since 8.0.0
*/
street(): string {
const format = this.faker.helpers.arrayElement(
this.faker.definitions.location.street
);
return this.faker.helpers.fake(format);
return this.faker.helpers.fake(this.faker.definitions.location.street);
}

/**
Expand Down
24 changes: 18 additions & 6 deletions test/__snapshots__/helpers.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ exports[`helpers > 42 > arrayElements > with array and count 1`] = `
]
`;

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

exports[`helpers > 42 > fake > with plain string 1`] = `"my test string"`;
exports[`helpers > 42 > fake > with a static template 1`] = `"my test string"`;

exports[`helpers > 42 > fake > with multiple dynamic templates 1`] = `"Sandy"`;

exports[`helpers > 42 > fake > with multiple static templates 1`] = `"B"`;

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

Expand Down Expand Up @@ -208,9 +212,13 @@ exports[`helpers > 1211 > arrayElements > with array and count 1`] = `
]
`;

exports[`helpers > 1211 > fake > with args 1`] = `"my string: wKti5-}$_/"`;
exports[`helpers > 1211 > fake > with a dynamic template 1`] = `"my string: wKti5-}$_/"`;

exports[`helpers > 1211 > fake > with a static template 1`] = `"my test string"`;

exports[`helpers > 1211 > fake > with plain string 1`] = `"my test string"`;
exports[`helpers > 1211 > fake > with multiple dynamic templates 1`] = `"La Crosse"`;

exports[`helpers > 1211 > fake > with multiple static templates 1`] = `"C"`;

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

Expand Down Expand Up @@ -383,9 +391,13 @@ exports[`helpers > 1337 > arrayElements > with array and count 1`] = `
]
`;

exports[`helpers > 1337 > fake > with args 1`] = `"my string: 9U/4:SK$>6"`;
exports[`helpers > 1337 > fake > with a dynamic template 1`] = `"my string: 9U/4:SK$>6"`;

exports[`helpers > 1337 > fake > with a static template 1`] = `"my test string"`;

exports[`helpers > 1337 > fake > with multiple dynamic templates 1`] = `"U/4:SK$>6Q"`;

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

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

Expand Down
42 changes: 33 additions & 9 deletions test/helpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,14 @@ describe('helpers', () => {
});

t.describe('fake', (t) => {
t.it('with plain string', 'my test string').it(
'with args',
'my string: {{datatype.string}}'
);
t.it('with a static template', 'my test string')
.it('with a dynamic template', 'my string: {{string.sample}}')
.it('with multiple static templates', ['A', 'B', 'C'])
.it('with multiple dynamic templates', [
'{{string.sample}}',
'{{location.city_name}}',
'{{location.cityName}}',
]);
});

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

it('does not allow undefined parameters', () => {
expect(() =>
// @ts-expect-error: The parameter is required
faker.helpers.fake()
).toThrowError(new FakerError('string parameter is required!'));
it('does not allow empty string parameters', () => {
expect(() => faker.helpers.fake('')).toThrowError(
new FakerError('Pattern string cannot be empty.')
);
});

it('does not allow empty array parameters', () => {
expect(() => faker.helpers.fake([])).toThrowError(
new FakerError('Array of pattern strings cannot be empty.')
);
});

it('does not allow invalid module name', () => {
Expand Down Expand Up @@ -655,6 +664,21 @@ describe('helpers', () => {
);
});

it('should be able to pass multiple static templates', () => {
expect(['A', 'B', 'C']).toContain(
faker.helpers.fake(['A', 'B', 'C'])
);
});

it('should be able to pass multiple dynamic templates', () => {
expect(faker.definitions.location.city_name).toContain(
faker.helpers.fake([
'{{location.city_name}}',
'{{location.cityName}}',
])
);
});

it('should be able to handle only {{ brackets', () => {
expect(faker.helpers.fake('{{hello')).toBe('{{hello');
expect(faker.helpers.fake('hello{{')).toBe('hello{{');
Expand Down

0 comments on commit 75a31f6

Please sign in to comment.