UNPKG

@eflexsystems/factory-bot

Version:

A factory library for Node.js and JavaScript inspired by factory_girl

236 lines (200 loc) 6.64 kB
import Factory from './Factory.js' import Sequence from './generators/Sequence.js' import Assoc from './generators/Assoc.js' import AssocAttrs from './generators/AssocAttrs.js' import AssocMany from './generators/AssocMany.js' import AssocAttrsMany from './generators/AssocAttrsMany.js' import ChanceGenerator from './generators/ChanceGenerator.js' import OneOf from './generators/OneOf.js' import DefaultAdapter from './adapters/DefaultAdapter.js' function wrapGenerator(generator) { return (...args) => () => generator.generate(...args) } export function generatorThunk(factoryGirl, SomeGenerator) { const generator = new SomeGenerator(factoryGirl) return wrapGenerator(generator) } export default class FactoryGirl { constructor(options = {}) { this.factories = {} this.options = {} this.adapters = {} this.created = new Set() this.assoc = generatorThunk(this, Assoc) this.assocMany = generatorThunk(this, AssocMany) this.assocAttrs = generatorThunk(this, AssocAttrs) this.assocAttrsMany = generatorThunk(this, AssocAttrsMany) this.seq = this.sequence = (...args) => generatorThunk(this, Sequence)(...args) this.resetSeq = this.resetSequence = id => { Sequence.reset(id) } const chance = new ChanceGenerator(this) this.chance = wrapGenerator(chance) this.chance.seed = value => { chance.seed(value) } this.oneOf = generatorThunk(this, OneOf) this.defaultAdapter = new DefaultAdapter() this.options = options } define(name, Model, initializer, options = {}) { if (this.getFactory(name, false)) { throw new Error(`Factory ${name} already defined`) } const factory = (this.factories[name] = new Factory( Model, initializer, options, )) return factory } remove(name) { delete this.factories[name] } extend(parent, name, childInitializer, options = {}) { if (this.getFactory(name, false)) { throw new Error(`Factory ${name} already defined`) } const parentFactory = this.getFactory(parent, true) const Model = options.model || parentFactory.Model const jointOptions = { ...parentFactory.options, ...options } let jointInitializer function resolveInitializer(initializer, buildOptions) { return typeof initializer === 'function' ? initializer(buildOptions) : initializer } if ( typeof parentFactory.initializer === 'function' || typeof childInitializer === 'function' ) { jointInitializer = function initializer(buildOptions = {}) { return Object.assign( {}, resolveInitializer(parentFactory.initializer, buildOptions), resolveInitializer(childInitializer, buildOptions), ) } } else { jointInitializer = Object.assign( {}, parentFactory.initializer, childInitializer, ) } const factory = (this.factories[name] = new Factory( Model, jointInitializer, jointOptions, )) return factory } attrs(name, attrs, buildOptions = {}) { return this.getFactory(name).attrs(attrs, buildOptions) } attrsSync(name, attrs, buildOptions = {}) { return this.getFactory(name).attrsSync(attrs, buildOptions) } async build(name, attrs = {}, buildOptions = {}) { const adapter = this.getAdapter(name) const model = await this.getFactory(name).build(adapter, attrs, buildOptions); if (this.options.afterBuild) { return await this.options.afterBuild(model, attrs, buildOptions); } return model; } async create(name, attrs, buildOptions = {}) { const adapter = this.getAdapter(name) const createdModel = await this.getFactory(name).create(adapter, attrs, buildOptions); const model = await this.addToCreatedList(adapter, createdModel); if (this.options.afterCreate) { return await this.options.afterCreate(model, attrs, buildOptions); } return model; } attrsMany(name, num, attrs, buildOptions = {}) { return this.getFactory(name).attrsMany(num, attrs, buildOptions) } attrsManySync(name, num, attrs, buildOptions = {}) { return this.getFactory(name).attrsManySync(num, attrs, buildOptions) } async buildMany(name, num, attrs, buildOptions = {}) { const adapter = this.getAdapter(name) const models = await this.getFactory(name).buildMany(adapter, num, attrs, buildOptions); if (this.options.afterBuild) { return await Promise.all( models.map((model, i) => this.options.afterBuild(model, attrs, buildOptions[i]), ), ); } return models; } async createMany(name, num, attrs, buildOptions = {}) { const adapter = this.getAdapter(name) let models = await this.getFactory(name).createMany(adapter, num, attrs, buildOptions); models = this.addToCreatedList(adapter, models); if (this.options.afterCreate) { return await Promise.all( models.map((model, i) => this.options.afterCreate(model, attrs, buildOptions?.[i]), ), ); } return models; } getFactory(name, throwError = true) { if (!this.factories[name] && throwError) { throw new Error(`Invalid factory '${name}' requested`) } return this.factories[name] } withOptions(options, merge = false) { this.options = merge ? { ...this.options, ...options } : options } getAdapter(factory) { return factory ? this.adapters[factory] || this.defaultAdapter : this.defaultAdapter } addToCreatedList(adapter, models) { if (!Array.isArray(models)) { this.created.add([adapter, models]) } else { // eslint-disable-next-line no-restricted-syntax for (const model of models) { this.created.add([adapter, model]) } } return models } cleanUp() { const createdArray = [] // eslint-disable-next-line no-restricted-syntax for (const c of this.created) { createdArray.unshift(c) } const promise = createdArray.reduce(async (prev, [adapter, model]) => { await prev if (model === null) { return } await adapter.destroy(model, model.constructor) }, Promise.resolve()) this.created.clear() this.resetSeq() return promise } setAdapter(adapter, factoryNames = null) { if (!factoryNames) { this.defaultAdapter = adapter } else { factoryNames = Array.isArray(factoryNames) ? factoryNames : [factoryNames] factoryNames.forEach(name => { this.adapters[name] = adapter }) } return adapter } }