artillery
Version:
Cloud-scale load testing. https://www.artillery.io
157 lines (129 loc) • 4.83 kB
text/typescript
import { faker } from '@faker-js/faker';
// Modules that don't expose simple data-generation functions
const SKIPPED_MODULES = new Set(['helpers', 'definitions', 'rawDefinitions']);
// Deprecated falso-style aliases kept for backwards compatibility.
// Maps legacy `$rand*` names to their faker equivalents (flattened names).
// NOTE: configuration options are NOT translated - options must be provided
// using the faker option shape, under the faker function name.
const DEPRECATED_ALIASES = {
randEmail: 'internetEmail',
randFullName: 'personFullName',
randFirstName: 'personFirstName',
randLastName: 'personLastName',
randUserName: 'internetUsername',
randPassword: 'internetPassword',
randUuid: 'stringUuid',
randPhoneNumber: 'phoneNumber',
randCity: 'locationCity',
randCountry: 'locationCountry',
randStreetAddress: 'locationStreetAddress',
randZipCode: 'locationZipCode',
randCompanyName: 'companyName',
randNumber: 'numberInt',
randBoolean: 'datatypeBoolean',
randUrl: 'internetUrl',
randIp: 'internetIpv4',
randWord: 'loremWord',
randSentence: 'loremSentence',
randParagraph: 'loremParagraph',
randText: 'loremText',
randJobTitle: 'personJobTitle',
randColor: 'colorHuman',
randProductName: 'commerceProductName'
};
const flattenName = (moduleName, functionName) =>
`${moduleName}${functionName[0].toUpperCase()}${functionName.slice(1)}`;
// Builds a map of flattened function names (e.g. `internetEmail`) to
// zero/one-argument faker functions (e.g. `faker.internet.email`)
const buildFakerFunctionMap = () => {
const functions = {};
for (const moduleName of Object.keys(faker)) {
if (SKIPPED_MODULES.has(moduleName) || moduleName.startsWith('_')) {
continue;
}
const mod = faker[moduleName];
if (!mod || typeof mod !== 'object') {
continue;
}
// Walk the prototype chain: some faker modules inherit methods
// (e.g. DateModule extends SimpleDateModule)
let proto = Object.getPrototypeOf(mod);
while (proto && proto !== Object.prototype) {
for (const functionName of Object.getOwnPropertyNames(proto)) {
if (functionName === 'constructor' || functionName.startsWith('_')) {
continue;
}
if (typeof mod[functionName] !== 'function') {
continue;
}
// Only functions taking at most one argument are supported, as
// configuration is passed as a single (object) argument
if (mod[functionName].length > 1) {
continue;
}
const flatName = flattenName(moduleName, functionName);
if (!functions[flatName]) {
functions[flatName] = mod[functionName].bind(mod);
}
}
proto = Object.getPrototypeOf(proto);
}
}
return functions;
};
const fakerFunctionMap = buildFakerFunctionMap();
const getFakerFunctions = () => Object.keys(fakerFunctionMap);
const warnedAliases = new Set();
function ArtilleryPluginFakeData(script, events) {
this.script = script;
this.events = events;
const pluginConfig =
script.config['fake-data'] || script.config.plugins['fake-data'];
function fakeDataHandler(context, _ee, next) {
for (const [funcName, fakerFunc] of Object.entries(
fakerFunctionMap
) as [string, any][]) {
context.funcs[`$${funcName}`] = () => {
if (pluginConfig[funcName]) {
return fakerFunc(pluginConfig[funcName]);
}
return fakerFunc();
};
}
for (const [aliasName, funcName] of Object.entries(DEPRECATED_ALIASES)) {
context.funcs[`$${aliasName}`] = () => {
if (!warnedAliases.has(aliasName)) {
warnedAliases.add(aliasName);
console.warn(
`[fake-data] $${aliasName} is deprecated and will be removed in a future release. Use $${funcName} instead.`
);
}
return context.funcs[`$${funcName}`]();
};
}
next();
}
script.scenarios = script.scenarios.map((scenario) => {
scenario.beforeScenario = [].concat(scenario.beforeScenario || []);
scenario.beforeScenario.push('fakeDataHandler');
return scenario;
});
if (!script.config.processor) {
script.config.processor = {};
}
// In the main thread config.processor may still be an unresolved
// path (a string). Attaching functions to it was a silent no-op
// under sloppy mode; ES modules are strict, so guard explicitly.
// Workers load the processor into an object before plugins run.
const canAttach = typeof script.config.processor === 'object';
if (canAttach) {
script.config.processor.fakeDataHandler = fakeDataHandler;
}
return this;
}
const getDeprecatedAliases = () => ({ ...DEPRECATED_ALIASES });
export {
ArtilleryPluginFakeData as Plugin,
getFakerFunctions,
getDeprecatedAliases
};