-
-
Notifications
You must be signed in to change notification settings - Fork 923
/
faker.ts
259 lines (235 loc) · 8.64 KB
/
faker.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
import type { LocaleDefinition } from './definitions';
import { FakerError } from './errors/faker-error';
import { MersenneModule } from './internal/mersenne/mersenne';
import type { KnownLocale } from './locales';
import { AddressModule } from './modules/address';
import { AnimalModule } from './modules/animal';
import { ColorModule } from './modules/color';
import { CommerceModule } from './modules/commerce';
import { CompanyModule } from './modules/company';
import { DatabaseModule } from './modules/database';
import { DatatypeModule } from './modules/datatype';
import { DateModule } from './modules/date';
import { FinanceModule } from './modules/finance';
import { GitModule } from './modules/git';
import { HackerModule } from './modules/hacker';
import { HelpersModule } from './modules/helpers';
import { ImageModule } from './modules/image';
import { InternetModule } from './modules/internet';
import { LoremModule } from './modules/lorem';
import { MusicModule } from './modules/music';
import { NameModule } from './modules/name';
import { PhoneModule } from './modules/phone';
import { RandomModule } from './modules/random';
import { ScienceModule } from './modules/science';
import { SystemModule } from './modules/system';
import { VehicleModule } from './modules/vehicle';
import { WordModule } from './modules/word';
import type { LiteralUnion } from './utils/types';
export type UsableLocale = LiteralUnion<KnownLocale>;
export type UsedLocales = Partial<Record<UsableLocale, LocaleDefinition>>;
export interface FakerOptions {
locales: UsedLocales;
locale?: UsableLocale;
localeFallback?: UsableLocale;
}
const metadataKeys: ReadonlyArray<keyof LocaleDefinition> = [
'title',
'separator',
];
export class Faker {
locales: UsedLocales;
private _locale: UsableLocale;
private _localeFallback: UsableLocale;
get locale(): UsableLocale {
return this._locale;
}
set locale(locale: UsableLocale) {
if (!this.locales[locale]) {
throw new FakerError(
`Locale ${locale} is not supported. You might want to add the requested locale first to \`faker.locales\`.`
);
}
this._locale = locale;
}
get localeFallback(): UsableLocale {
return this._localeFallback;
}
set localeFallback(localeFallback: UsableLocale) {
if (!this.locales[localeFallback]) {
throw new FakerError(
`Locale ${localeFallback} is not supported. You might want to add the requested locale first to \`faker.locales\`.`
);
}
this._localeFallback = localeFallback;
}
readonly definitions: LocaleDefinition = this.initDefinitions();
/** @internal */
private readonly _mersenne: MersenneModule = new MersenneModule();
readonly random: RandomModule = new RandomModule(this);
readonly helpers: HelpersModule = new HelpersModule(this);
readonly datatype: DatatypeModule = new DatatypeModule(this);
readonly address: AddressModule = new AddressModule(this);
readonly animal: AnimalModule = new AnimalModule(this);
readonly color: ColorModule = new ColorModule(this);
readonly commerce: CommerceModule = new CommerceModule(this);
readonly company: CompanyModule = new CompanyModule(this);
readonly database: DatabaseModule = new DatabaseModule(this);
readonly date: DateModule = new DateModule(this);
readonly finance = new FinanceModule(this);
readonly git: GitModule = new GitModule(this);
readonly hacker: HackerModule = new HackerModule(this);
readonly image: ImageModule = new ImageModule(this);
readonly internet: InternetModule = new InternetModule(this);
readonly lorem: LoremModule = new LoremModule(this);
readonly music: MusicModule = new MusicModule(this);
readonly name: NameModule = new NameModule(this);
readonly phone: PhoneModule = new PhoneModule(this);
readonly science: ScienceModule = new ScienceModule(this);
readonly system: SystemModule = new SystemModule(this);
readonly vehicle: VehicleModule = new VehicleModule(this);
readonly word: WordModule = new WordModule(this);
constructor(opts: FakerOptions) {
if (!opts) {
throw new FakerError(
'Options with at least one entry in locales must be provided'
);
}
if (Object.keys(opts.locales ?? {}).length === 0) {
throw new FakerError(
'At least one entry in locales must be provided in the locales parameter'
);
}
this.locales = opts.locales;
this.locale = opts.locale || 'en';
this.localeFallback = opts.localeFallback || 'en';
}
/**
* Creates a Proxy based LocaleDefinition that virtually merges the locales.
*/
private initDefinitions(): LocaleDefinition {
// Returns the first LocaleDefinition[key] in any locale
const resolveBaseData = (key: keyof LocaleDefinition): unknown =>
this.locales[this.locale][key] ?? this.locales[this.localeFallback][key];
// Returns the first LocaleDefinition[module][entry] in any locale
const resolveModuleData = (
module: keyof LocaleDefinition,
entry: string
): unknown =>
this.locales[this.locale][module]?.[entry] ??
this.locales[this.localeFallback][module]?.[entry];
// Returns a proxy that can return the entries for a module (if it exists)
const moduleLoader = (
module: keyof LocaleDefinition
): Record<string, unknown> | undefined => {
if (resolveBaseData(module)) {
return new Proxy(
{},
{
get(target, entry: string): unknown {
return resolveModuleData(module, entry);
},
}
);
} else {
return undefined;
}
};
return new Proxy({} as LocaleDefinition, {
get(target: LocaleDefinition, module: string): unknown {
let result = target[module];
if (result) {
return result;
} else if (metadataKeys.includes(module)) {
return resolveBaseData(module);
} else {
result = moduleLoader(module);
target[module] = result;
return result;
}
},
});
}
/**
* Sets the seed or generates a new one.
*
* Please note that generated values are dependent on both the seed and the
* number of calls that have been made since it was set.
*
* This method is intended to allow for consistent values in a tests, so you
* might want to use hardcoded values as the seed.
*
* In addition to that it can be used for creating truly random tests
* (by passing no arguments), that still can be reproduced if needed,
* by logging the result and explicitly setting it if needed.
*
* @param seed The seed to use. Defaults to a random number.
* @returns The seed that was set.
*
* @example
* // Consistent values for tests:
* faker.seed(42)
* faker.datatype.number(10); // 4
* faker.datatype.number(10); // 8
*
* faker.seed(42)
* faker.datatype.number(10); // 4
* faker.datatype.number(10); // 8
*
* @example
* // Random but reproducible tests:
* // Simply log the seed, and if you need to reproduce it, insert the seed here
* console.log('Running test with seed:', faker.seed());
*/
seed(seed?: number): number;
/**
* Sets the seed array.
*
* Please note that generated values are dependent on both the seed and the
* number of calls that have been made since it was set.
*
* This method is intended to allow for consistent values in a tests, so you
* might want to use hardcoded values as the seed.
*
* In addition to that it can be used for creating truly random tests
* (by passing no arguments), that still can be reproduced if needed,
* by logging the result and explicitly setting it if needed.
*
* @param seedArray The seed array to use.
* @returns The seed array that was set.
*
* @example
* // Consistent values for tests:
* faker.seed([42, 13, 17])
* faker.datatype.number(10); // 4
* faker.datatype.number(10); // 8
*
* faker.seed([42, 13, 17])
* faker.datatype.number(10); // 4
* faker.datatype.number(10); // 8
*
* @example
* // Random but reproducible tests:
* // Simply log the seed, and if you need to reproduce it, insert the seed here
* console.log('Running test with seed:', faker.seed());
*/
seed(seedArray: number[]): number[];
seed(
seed: number | number[] = Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)
): number | number[] {
if (Array.isArray(seed) && seed.length) {
this._mersenne.seed_array(seed);
} else if (!Array.isArray(seed) && !isNaN(seed)) {
this._mersenne.seed(seed);
}
return seed;
}
/**
* Set Faker's locale
*
* @param locale The locale to set (e.g. `en` or `en_AU`, `en_AU_ocker`).
*/
setLocale(locale: UsableLocale): void {
this.locale = locale;
}
}