Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fake with multiple parameters #1459

Merged
merged 13 commits into from
Oct 23, 2022
26 changes: 13 additions & 13 deletions src/modules/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,10 +487,14 @@ 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(+!# !## #### #####!)}}.')
* ```
*
* Currently it is not possible to set more than a single parameter.
* 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 templates.
*
Expand All @@ -505,6 +509,7 @@ export class HelpersModule {
* 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('I rolled the dice and got: {{string.numeric(1, {"allowLeadingZeros": true})}}') // 'I rolled the dice and got: 6'
*
* @since 7.4.0
*/
Expand All @@ -529,7 +534,7 @@ export class HelpersModule {
let method = token.replace('}}', '').replace('{{', '');

// extract method parameters
const regExp = /\(([^)]+)\)/;
const regExp = /\(([^)]*)\)/;
const matches = regExp.exec(method);
let parameters = '';
if (matches) {
Expand All @@ -550,7 +555,7 @@ export class HelpersModule {
}

// Make method executable
let fn: (args?: unknown) => unknown;
let fn: (...args: unknown[]) => unknown;
if (typeof currentModuleOrMethod === 'function') {
fn = currentModuleOrMethod as (args?: unknown) => unknown;
} else if (Array.isArray(currentDefinitions)) {
Expand All @@ -568,22 +573,17 @@ export class HelpersModule {
// If parameters are populated here, they are always going to be of string type
// since we might actually be dealing with an object or array,
// we always attempt to the parse the incoming parameters into JSON
let params: unknown;
let params: unknown[];
// Note: we experience a small performance hit here due to JSON.parse try / catch
// If anyone actually needs to optimize this specific code path, please open a support issue on github
try {
params = JSON.parse(parameters);
params = JSON.parse(`[${parameters}]`);
} catch (err) {
// since JSON.parse threw an error, assume parameters was actually a string
params = parameters;
params = [parameters];
}

let result: string;
if (typeof params === 'string' && params.length === 0) {
result = String(fn());
} else {
result = String(fn(params));
}
const result = String(fn(...params));

// Replace the found tag with the returned fake value
// We cannot use string.replace here because the result might contain evaluated characters
Expand Down
48 changes: 34 additions & 14 deletions test/helpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,31 +488,51 @@ describe('helpers', () => {
});

describe('fake()', () => {
it('replaces a token with a random value for a method with no parameters', () => {
const name = faker.helpers.fake('{{phone.number}}');
expect(name).toMatch(/\d/);
it('replaces a token with a random value for a method without parentheses', () => {
const actual = faker.helpers.fake('{{string.numeric}}');
expect(actual).toMatch(/^\d$/);
});

it('replaces multiple tokens with random values for methods with no parameters', () => {
const name = faker.helpers.fake(
'{{helpers.arrayElement}}{{helpers.arrayElement}}{{helpers.arrayElement}}'
it('replaces multiple tokens with random values for methods without parentheses', () => {
const actual = faker.helpers.fake(
'{{string.numeric}}{{string.numeric}}{{string.numeric}}'
);
expect(name).toMatch(/[abc]{3}/);
expect(actual).toMatch(/^\d{3}$/);
});

it('replaces a token with a random value for a methods with a simple parameter', () => {
const random = faker.helpers.fake(
'{{helpers.slugify("Will This Work")}}'
);
expect(random).toBe('Will-This-Work');
it('replaces a token with a random value for a method with empty parentheses', () => {
const actual = faker.helpers.fake('{{string.numeric()}}');
expect(actual).toMatch(/^\d$/);
});

it('replaces a token with a random value for a method with an unquoted parameter', () => {
const random = faker.helpers.fake('{{helpers.slugify(This Works)}}');
expect(random).toBe('This-Works');
});

it('replaces a token with a random value for a method with a simple parameter', () => {
const actual = faker.helpers.fake('{{string.numeric(3)}}');
expect(actual).toMatch(/^\d{3}$/);
});

it('replaces a token with a random value for a method with an array parameter', () => {
const arr = ['one', 'two', 'three'];
const random = faker.helpers.fake(
const actual = faker.helpers.fake(
'{{helpers.arrayElement(["one", "two", "three"])}}'
);
expect(arr).toContain(random);
expect(arr).toContain(actual);
});

it('replaces a token with a random value for a method with an object parameter', () => {
const actual = faker.helpers.fake('{{random.alpha({"count": 3})}}');
expect(actual).toMatch(/^[a-z]{3}$/i);
});

it('replaces a token with a random value for a method with multiple parameters', () => {
const actual = faker.helpers.fake(
'{{string.numeric(5, {"allowLeadingZeros": true})}}'
);
expect(actual).toMatch(/^\d{5}$/);
});

it('does not allow undefined parameters', () => {
Expand Down