aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeyla Jähnig <[email protected]>2022-08-29 13:10:45 +0200
committerGitHub <[email protected]>2022-08-29 11:10:45 +0000
commit7f8b8716ba69e24800e26d6c072c3076c01bfccf (patch)
tree909b28019e2d461ba5257ac91ea98ca1220b9a34
parentc2108fa5db889bb1455a5735934776bcf91fabac (diff)
downloadfaker-7f8b8716ba69e24800e26d6c072c3076c01bfccf.tar.xz
faker-7f8b8716ba69e24800e26d6c072c3076c01bfccf.zip
refactor(unique): move to helpers (#1298)
-rw-r--r--src/faker.ts2
-rw-r--r--src/modules/helpers/index.ts44
-rw-r--r--src/modules/helpers/unique.ts (renamed from src/modules/unique/unique.ts)4
-rw-r--r--src/modules/unique/index.ts26
-rw-r--r--test/__snapshots__/helpers.spec.ts.snap24
-rw-r--r--test/helpers.spec.ts159
-rw-r--r--test/unique.spec.ts84
7 files changed, 249 insertions, 94 deletions
diff --git a/src/faker.ts b/src/faker.ts
index c84d77a4..9534e373 100644
--- a/src/faker.ts
+++ b/src/faker.ts
@@ -77,7 +77,7 @@ export class Faker {
readonly definitions: LocaleDefinition = this.initDefinitions();
readonly fake: Fake['fake'] = new Fake(this).fake;
- readonly unique: Unique['unique'] = new Unique().unique;
+ readonly unique: Unique['unique'] = new Unique(this).unique;
readonly mersenne: Mersenne = new Mersenne();
readonly random: Random = new Random(this);
diff --git a/src/modules/helpers/index.ts b/src/modules/helpers/index.ts
index fac7e3e2..5228c605 100644
--- a/src/modules/helpers/index.ts
+++ b/src/modules/helpers/index.ts
@@ -2,6 +2,8 @@ import type { Faker } from '../..';
import { FakerError } from '../../errors/faker-error';
import { deprecated } from '../../internal/deprecated';
import { luhnCheckValue } from './luhn-check';
+import type { RecordKey } from './unique';
+import * as uniqueExec from './unique';
/**
* Module with various helper methods that transform the method input rather than returning values from locales.
@@ -590,4 +592,46 @@ export class Helpers {
// return the response recursively until we are done finding all tags
return this.fake(res);
}
+
+ /**
+ * Generates a unique result using the results of the given method.
+ * Used unique entries will be stored internally and filtered from subsequent calls.
+ *
+ * @template Method The type of the method to execute.
+ * @param method The method used to generate the values.
+ * @param args The arguments used to call the method.
+ * @param options The optional options used to configure this method.
+ * @param options.startTime This parameter does nothing.
+ * @param options.maxTime The time in milliseconds this method may take before throwing an error. Defaults to `50`.
+ * @param options.maxRetries The total number of attempts to try before throwing an error. Defaults to `50`.
+ * @param options.currentIterations This parameter does nothing.
+ * @param options.exclude The value or values that should be excluded/skipped. Defaults to `[]`.
+ * @param options.compare The function used to determine whether a value was already returned. Defaults to check the existence of the key.
+ * @param options.store The store of unique entries. Defaults to a global store.
+ *
+ * @example
+ * faker.helpers.unique(faker.name.firstName) // 'Corbin'
+ */
+ unique<Method extends (...parameters) => RecordKey>(
+ method: Method,
+ args?: Parameters<Method>,
+ options: {
+ startTime?: number;
+ maxTime?: number;
+ maxRetries?: number;
+ currentIterations?: number;
+ exclude?: RecordKey | RecordKey[];
+ compare?: (obj: Record<RecordKey, RecordKey>, key: RecordKey) => 0 | -1;
+ store?: Record<RecordKey, RecordKey>;
+ } = {}
+ ): ReturnType<Method> {
+ const { maxTime = 50, maxRetries = 50 } = options;
+ return uniqueExec.exec(method, args, {
+ ...options,
+ startTime: new Date().getTime(),
+ maxTime,
+ maxRetries,
+ currentIterations: 0,
+ });
+ }
}
diff --git a/src/modules/unique/unique.ts b/src/modules/helpers/unique.ts
index 70408faf..31fddec8 100644
--- a/src/modules/unique/unique.ts
+++ b/src/modules/helpers/unique.ts
@@ -4,7 +4,7 @@ export type RecordKey = string | number | symbol;
/**
* Global store of unique values.
- * This means that faker should *never* return duplicate values across all API methods when using `Faker.unique` without passing `options.store`.
+ * This means that faker should *never* return duplicate values across all API methods when using `Faker.helpers.unique` without passing `options.store`.
*/
const GLOBAL_UNIQUE_STORE: Record<RecordKey, RecordKey> = {};
@@ -60,7 +60,7 @@ total time: ${now - startTime}ms`
`${code} for uniqueness check.
May not be able to generate any more unique values with current settings.
-Try adjusting maxTime or maxRetries parameters for faker.unique().`
+Try adjusting maxTime or maxRetries parameters for faker.helpers.unique().`
);
}
diff --git a/src/modules/unique/index.ts b/src/modules/unique/index.ts
index d77004e4..6c02d6d8 100644
--- a/src/modules/unique/index.ts
+++ b/src/modules/unique/index.ts
@@ -1,11 +1,14 @@
-import type { RecordKey } from './unique';
-import * as uniqueExec from './unique';
+import type { Faker } from '../..';
+import { deprecated } from '../../internal/deprecated';
+import type { RecordKey } from '../helpers/unique';
/**
* Module to generate unique entries.
+ *
+ * @deprecated
*/
export class Unique {
- constructor() {
+ constructor(private readonly faker: Faker) {
// Bind `this` so namespaced is working correctly
for (const name of Object.getOwnPropertyNames(Unique.prototype)) {
if (
@@ -36,8 +39,12 @@ export class Unique {
* @param options.compare The function used to determine whether a value was already returned. Defaults to check the existence of the key.
* @param options.store The store of unique entries. Defaults to a global store.
*
+ * @see faker.helpers.unique()
+ *
* @example
* faker.unique(faker.name.firstName) // 'Corbin'
+ *
+ * @deprecated Use faker.helpers.unique() instead.
*/
unique<Method extends (...parameters) => RecordKey>(
method: Method,
@@ -52,13 +59,12 @@ export class Unique {
store?: Record<RecordKey, RecordKey>;
} = {}
): ReturnType<Method> {
- const { maxTime = 50, maxRetries = 50 } = options;
- return uniqueExec.exec(method, args, {
- ...options,
- startTime: new Date().getTime(),
- maxTime,
- maxRetries,
- currentIterations: 0,
+ deprecated({
+ deprecated: 'faker.unique()',
+ proposed: 'faker.helpers.unique()',
+ since: '7.5',
+ until: '8.0',
});
+ return this.faker.helpers.unique(method, args, options);
}
}
diff --git a/test/__snapshots__/helpers.spec.ts.snap b/test/__snapshots__/helpers.spec.ts.snap
index c79f19c8..0fa011fa 100644
--- a/test/__snapshots__/helpers.spec.ts.snap
+++ b/test/__snapshots__/helpers.spec.ts.snap
@@ -98,6 +98,14 @@ exports[`helpers > 42 > slugify > noArgs 1`] = `""`;
exports[`helpers > 42 > slugify > some string 1`] = `"hello-world"`;
+exports[`helpers > 42 > unique > with () => number 1`] = `37454`;
+
+exports[`helpers > 42 > unique > with () => number and args 1`] = `19`;
+
+exports[`helpers > 42 > unique > with customMethod 1`] = `"Test-188"`;
+
+exports[`helpers > 42 > unique > with customMethod and args 1`] = `"prefix-1-Test-188"`;
+
exports[`helpers > 42 > uniqueArray > with array 1`] = `
[
"H",
@@ -212,6 +220,14 @@ exports[`helpers > 1211 > slugify > noArgs 1`] = `""`;
exports[`helpers > 1211 > slugify > some string 1`] = `"hello-world"`;
+exports[`helpers > 1211 > unique > with () => number 1`] = `92852`;
+
+exports[`helpers > 1211 > unique > with () => number and args 1`] = `47`;
+
+exports[`helpers > 1211 > unique > with customMethod 1`] = `"Test-465"`;
+
+exports[`helpers > 1211 > unique > with customMethod and args 1`] = `"prefix-1-Test-465"`;
+
exports[`helpers > 1211 > uniqueArray > with array 1`] = `
[
"W",
@@ -316,6 +332,14 @@ exports[`helpers > 1337 > slugify > noArgs 1`] = `""`;
exports[`helpers > 1337 > slugify > some string 1`] = `"hello-world"`;
+exports[`helpers > 1337 > unique > with () => number 1`] = `26202`;
+
+exports[`helpers > 1337 > unique > with () => number and args 1`] = `13`;
+
+exports[`helpers > 1337 > unique > with customMethod 1`] = `"Test-132"`;
+
+exports[`helpers > 1337 > unique > with customMethod and args 1`] = `"prefix-1-Test-132"`;
+
exports[`helpers > 1337 > uniqueArray > with array 1`] = `
[
"o",
diff --git a/test/helpers.spec.ts b/test/helpers.spec.ts
index 61d742a7..ce8fb733 100644
--- a/test/helpers.spec.ts
+++ b/test/helpers.spec.ts
@@ -5,6 +5,13 @@ import { seededTests } from './support/seededRuns';
const NON_SEEDED_BASED_RUN = 5;
+function customUniqueMethod(prefix: string = ''): string {
+ const element = faker.helpers.arrayElement(
+ Array.from({ length: 500 }, (_, index) => `Test-${index + 1}`)
+ );
+ return `${prefix}${element}`;
+}
+
describe('helpers', () => {
afterEach(() => {
faker.locale = 'en';
@@ -93,6 +100,13 @@ describe('helpers', () => {
'my string: {{datatype.string}}'
);
});
+
+ t.describe('unique', (t) => {
+ t.it('with customMethod', customUniqueMethod)
+ .it('with customMethod and args', customUniqueMethod, ['prefix-1-'])
+ .it('with () => number', faker.datatype.number)
+ .it('with () => number and args', faker.datatype.number, [50]);
+ });
});
describe(`random seeded tests for seed ${faker.seed()}`, () => {
@@ -608,6 +622,151 @@ describe('helpers', () => {
delete (faker.random as any).special;
});
});
+
+ describe('unique()', () => {
+ it('should be possible to call a function with no arguments and return a result', () => {
+ const result = faker.helpers.unique(faker.internet.email);
+ expect(result).toBeTypeOf('string');
+ });
+
+ it('should be possible to call a function with arguments and return a result', () => {
+ const result = faker.helpers.unique(faker.internet.email, [
+ 'fName',
+ 'lName',
+ 'domain',
+ ]); // third argument is provider, or domain for email
+ expect(result).toMatch(/\@domain/);
+ });
+
+ it('should be possible to limit unique call by maxTime in ms', () => {
+ expect(() => {
+ faker.helpers.unique(faker.internet.protocol, [], {
+ maxTime: 1,
+ maxRetries: 9999,
+ exclude: ['https', 'http'],
+ });
+ }).toThrowError(
+ new FakerError(`Exceeded maxTime: 1 for uniqueness check.
+
+May not be able to generate any more unique values with current settings.
+Try adjusting maxTime or maxRetries parameters for faker.helpers.unique().`)
+ );
+ });
+
+ it('should be possible to limit unique call by maxRetries', () => {
+ expect(() => {
+ faker.helpers.unique(faker.internet.protocol, [], {
+ maxTime: 5000,
+ maxRetries: 5,
+ exclude: ['https', 'http'],
+ });
+ }).toThrowError(
+ new FakerError(`Exceeded maxRetries: 5 for uniqueness check.
+
+May not be able to generate any more unique values with current settings.
+Try adjusting maxTime or maxRetries parameters for faker.helpers.unique().`)
+ );
+ });
+
+ it('should throw a FakerError instance on error', () => {
+ expect(() => {
+ faker.helpers.unique(faker.internet.protocol, [], {
+ maxTime: 5000,
+ maxRetries: 5,
+ exclude: ['https', 'http'],
+ });
+ }).toThrowError(
+ new FakerError(`Exceeded maxRetries: 5 for uniqueness check.
+
+May not be able to generate any more unique values with current settings.
+Try adjusting maxTime or maxRetries parameters for faker.helpers.unique().`)
+ );
+ });
+ });
}
+
+ // This test can be only executed once, because the unique function has a global state.
+ // See: https://github.com/faker-js/faker/issues/371
+ describe('global unique()', () => {
+ it('should be possible to exclude results as array', () => {
+ const internetProtocol = () =>
+ faker.helpers.arrayElement(['https', 'http']);
+ const result = faker.helpers.unique(internetProtocol, [], {
+ exclude: ['https'],
+ });
+ expect(result).toBe('http');
+ });
+
+ it('no conflict', () => {
+ let i = 0;
+ const method = () => `no conflict: ${i++}`;
+ expect(faker.helpers.unique(method)).toBe('no conflict: 0');
+ expect(faker.helpers.unique(method)).toBe('no conflict: 1');
+ });
+
+ it('with conflict', () => {
+ const method = () => 'with conflict: 0';
+ expect(faker.helpers.unique(method)).toBe('with conflict: 0');
+ expect(() =>
+ faker.helpers.unique(method, [], {
+ maxRetries: 1,
+ })
+ ).toThrowError(
+ new FakerError(`Exceeded maxRetries: 1 for uniqueness check.
+
+May not be able to generate any more unique values with current settings.
+Try adjusting maxTime or maxRetries parameters for faker.helpers.unique().`)
+ );
+ });
+
+ it('should not mutate most of the input option properties', () => {
+ const method = () => 'options-mutate-test';
+
+ const startTime = new Date().getTime();
+ const maxTime = 49;
+ const maxRetries = 49;
+ const currentIterations = 0;
+ const exclude = [];
+ const compare = (obj, key) => (obj[key] === undefined ? -1 : 0);
+
+ const options = {
+ startTime,
+ maxTime,
+ maxRetries,
+ currentIterations,
+ exclude,
+ compare,
+ };
+
+ faker.helpers.unique(method, [], options);
+
+ expect(options.startTime).toBe(startTime);
+ expect(options.maxTime).toBe(maxTime);
+ expect(options.maxRetries).toBe(maxRetries);
+ // `options.currentIterations` is incremented in the `faker.helpers.unique` function.
+ expect(options.exclude).toBe(exclude);
+ expect(options.compare).toBe(compare);
+ });
+
+ it('should be possible to pass a user-specific store', () => {
+ const store = {};
+
+ const method = () => 'with conflict: 0';
+
+ expect(faker.helpers.unique(method, [], { store })).toBe(
+ 'with conflict: 0'
+ );
+ expect(store).toEqual({ 'with conflict: 0': 'with conflict: 0' });
+
+ expect(() => faker.helpers.unique(method, [], { store })).toThrow();
+
+ delete store['with conflict: 0'];
+
+ expect(faker.helpers.unique(method, [], { store })).toBe(
+ 'with conflict: 0'
+ );
+ expect(store).toEqual({ 'with conflict: 0': 'with conflict: 0' });
+ });
+ });
});
});
diff --git a/test/unique.spec.ts b/test/unique.spec.ts
index 9644cd67..e603939d 100644
--- a/test/unique.spec.ts
+++ b/test/unique.spec.ts
@@ -86,7 +86,7 @@ describe('unique', () => {
new FakerError(`Exceeded maxTime: 1 for uniqueness check.
May not be able to generate any more unique values with current settings.
-Try adjusting maxTime or maxRetries parameters for faker.unique().`)
+Try adjusting maxTime or maxRetries parameters for faker.helpers.unique().`)
);
});
@@ -101,7 +101,7 @@ Try adjusting maxTime or maxRetries parameters for faker.unique().`)
new FakerError(`Exceeded maxRetries: 5 for uniqueness check.
May not be able to generate any more unique values with current settings.
-Try adjusting maxTime or maxRetries parameters for faker.unique().`)
+Try adjusting maxTime or maxRetries parameters for faker.helpers.unique().`)
);
});
@@ -116,88 +116,10 @@ Try adjusting maxTime or maxRetries parameters for faker.unique().`)
new FakerError(`Exceeded maxRetries: 5 for uniqueness check.
May not be able to generate any more unique values with current settings.
-Try adjusting maxTime or maxRetries parameters for faker.unique().`)
+Try adjusting maxTime or maxRetries parameters for faker.helpers.unique().`)
);
});
});
}
});
-
- // This test can be only executed once, because the unique function has a global state.
- // See: https://github.com/faker-js/faker/issues/371
- it('should be possible to exclude results as array', () => {
- const internetProtocol = () =>
- faker.helpers.arrayElement(['https', 'http']);
- const result = faker.unique(internetProtocol, [], {
- exclude: ['https'],
- });
- expect(result).toBe('http');
- });
-
- it('no conflict', () => {
- let i = 0;
- const method = () => `no conflict: ${i++}`;
- expect(faker.unique(method)).toBe('no conflict: 0');
- expect(faker.unique(method)).toBe('no conflict: 1');
- });
-
- it('with conflict', () => {
- const method = () => 'with conflict: 0';
- expect(faker.unique(method)).toBe('with conflict: 0');
- expect(() =>
- faker.unique(method, [], {
- maxRetries: 1,
- })
- ).toThrowError(
- new FakerError(`Exceeded maxRetries: 1 for uniqueness check.
-
-May not be able to generate any more unique values with current settings.
-Try adjusting maxTime or maxRetries parameters for faker.unique().`)
- );
- });
-
- it('should not mutate most of the input option properties', () => {
- const method = () => 'options-mutate-test';
-
- const startTime = new Date().getTime();
- const maxTime = 49;
- const maxRetries = 49;
- const currentIterations = 0;
- const exclude = [];
- const compare = (obj, key) => (obj[key] === undefined ? -1 : 0);
-
- const options = {
- startTime,
- maxTime,
- maxRetries,
- currentIterations,
- exclude,
- compare,
- };
-
- faker.unique(method, [], options);
-
- expect(options.startTime).toBe(startTime);
- expect(options.maxTime).toBe(maxTime);
- expect(options.maxRetries).toBe(maxRetries);
- // `options.currentIterations` is incremented in the `faker.unique` function.
- expect(options.exclude).toBe(exclude);
- expect(options.compare).toBe(compare);
- });
-
- it('should be possible to pass a user-specific store', () => {
- const store = {};
-
- const method = () => 'with conflict: 0';
-
- expect(faker.unique(method, [], { store })).toBe('with conflict: 0');
- expect(store).toEqual({ 'with conflict: 0': 'with conflict: 0' });
-
- expect(() => faker.unique(method, [], { store })).toThrow();
-
- delete store['with conflict: 0'];
-
- expect(faker.unique(method, [], { store })).toBe('with conflict: 0');
- expect(store).toEqual({ 'with conflict: 0': 'with conflict: 0' });
- });
});