Skip to content

Commit

Permalink
Merge 6174f73 into 78911ad
Browse files Browse the repository at this point in the history
  • Loading branch information
faboulaws committed Sep 1, 2019
2 parents 78911ad + 6174f73 commit 727cb72
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 8 deletions.
59 changes: 56 additions & 3 deletions lib/mocker.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
const Chance = require('chance');
const set = require('lodash.set');
const get = require('lodash.get');
const flatten = require('flat');
const isPlainObject = require('lodash.isplainobject');
const ObjectId = require('bson-objectid');

const UNDEFINED_PROP_VIA_PARENT = Symbol('UndefinedPropFromParent');

const chance = new Chance();

function getOfType(of) {
Expand Down Expand Up @@ -117,8 +120,8 @@ const propertyParentIsSkipped = (path, options) => {
const shouldSkipProperty = (path, options) =>
propertyIsSkipped(path, options) || propertyParentIsSkipped(path, options);

function populateField(mockObject, schema, path, options, staticValue) {
const fieldOptions = options[path] || {};
function populateField(mockObject, schema, path, options, staticValue, indirectValues = {}) {
const fieldOptions = getPropertyByName(options, path) || getPropertyByPath(options, path) || {};
if (!shouldSkipProperty(path, options)) {
let value;
const pathDef = schema.path(path);
Expand All @@ -129,6 +132,8 @@ function populateField(mockObject, schema, path, options, staticValue) {
value = fieldOptions.value(mockObject);
} else if (fieldOptions.value) {
({ value } = fieldOptions);
} else if (indirectValues[path]) {
value = indirectValues[path];
} else {
const type = pathDef.instance.toLowerCase();
if (!generators[type]) {
Expand All @@ -142,18 +147,66 @@ function populateField(mockObject, schema, path, options, staticValue) {
}
}

const getOptionFromParentProperty = (path, options, optionName) => {
const pathProps = path.split('.');
if (pathProps.length > 1) {
let parentPath = '';
// eslint-disable-next-line no-restricted-syntax
for (const pathProp of pathProps) {
parentPath = parentPath.length === 0 ? pathProp : `${parentPath}.${pathProp}`;
const parentObject = getPropertyByPath(options, parentPath) ||
getPropertyByName(options, parentPath) || {};
if (parentObject[optionName]) {
return [parentPath, parentObject[optionName]];
}
}
}
return UNDEFINED_PROP_VIA_PARENT;
};

const setIndirectValues = (parentPath, rootValue, indirectVals) => {
const rootValues = flatten(rootValue);
Object.entries(rootValues).forEach(([keyPath, value]) => {
// eslint-disable-next-line no-param-reassign
indirectVals[`${parentPath}.${keyPath}`] = value;
});
return indirectVals;
};

const generate = (schema, options, staticFields) => {
const mockObject = {};
const fieldGeneration = [];
const delayedFieldGeneration = [];
const indirectValues = {};
const inderectValuesTasks = {};
schema.eachPath((path) => {
if (!path.includes('$*')) {
if (typeof get(options, `${path}.value`) === 'function' || typeof get(options, `['${path}'].value`, undefined) === 'function') {
delayedFieldGeneration.push(() =>
populateField(mockObject, schema, path, options, get(staticFields, path, undefined)));
} else {
const indirectVals = getOptionFromParentProperty(path, options, 'value');
if (indirectVals !== UNDEFINED_PROP_VIA_PARENT) {
const [parentPath, value] = indirectVals;
if (!inderectValuesTasks[parentPath]) {
if (typeof value === 'function') {
fieldGeneration.push(() => {
const rootValue = value(mockObject);
setIndirectValues(parentPath, rootValue, indirectValues);
});
} else {
const rootValue = value;
setIndirectValues(parentPath, rootValue, indirectValues);
}
inderectValuesTasks[parentPath] = true;
}
}

fieldGeneration.push(() =>
populateField(mockObject, schema, path, options, get(staticFields, path, undefined)));
populateField(
mockObject, schema, path, options,
get(staticFields, path, undefined), indirectValues,
));
}
}
});
Expand Down
4 changes: 1 addition & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"dependencies": {
"bson-objectid": "^1.3.0",
"chance": "^1.0.18",
"flat": "^4.1.0",
"lodash.get": "^4.4.2",
"lodash.isplainobject": "^4.0.6",
"lodash.merge": "^4.6.2",
Expand All @@ -57,7 +58,6 @@
"eslint": "^4.18.1",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.9.0",
"flat": "^4.1.0",
"husky": "^3.0.3",
"mocha": "^6.2.0",
"mocha-lcov-reporter": "^1.3.0",
Expand Down
68 changes: 67 additions & 1 deletion test/mocker.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,12 +237,42 @@ describe('mocker test', () => {
expect(mock.firstName).to.eql('blabla');
});

it('should use static value - at leaf', () => {
it('should use static value - at leaf - key as path', () => {
const theShema = new Schema({ root: { levelOne: { firstName: String, username: String, lastName: String } } });
const thingMocker = mocker(theShema, { 'root.levelOne.firstName': { value: 'blabla' } });
const mock = thingMocker.generate();
expect(mock.root.levelOne.firstName).to.eql('blabla');
});

it('should use static value - at leaf - nested key', () => {
const theShema = new Schema({ root: { levelOne: { firstName: String, username: String, lastName: String } } });
const thingMocker = mocker(theShema, { root: { levelOne: { firstName: { value: 'blabla' } } } });
const mock = thingMocker.generate();
expect(mock.root.levelOne.firstName).to.eql('blabla');
});

describe('root level property', () => {
it('should allow using value from parent property - root key', () => {
const theShema = new Schema({ root: { levelOne: { firstName: String, username: String, lastName: String } } });
const thingMocker = mocker(theShema, { root: { value: { levelOne: { firstName: 'blabla' } } } });
const mock = thingMocker.generate();
expect(mock.root.levelOne.firstName).to.eql('blabla');
});

it('should allow using value from parent property - sub level(levelOne) - nested option key', () => {
const theShema = new Schema({ root: { levelOne: { firstName: String, username: String, lastName: String } } });
const thingMocker = mocker(theShema, { root: { levelOne: { value: { firstName: 'blabla' } } } });
const mock = thingMocker.generate();
expect(mock.root.levelOne.firstName).to.eql('blabla');
});

it('should allow using value from parent property - sub level(levelOne) - path option key(root.levelOne)', () => {
const theShema = new Schema({ root: { levelOne: { firstName: String, username: String, lastName: String } } });
const thingMocker = mocker(theShema, { "root.levelOne": { value: { firstName: 'blabla' } } });
const mock = thingMocker.generate();
expect(mock.root.levelOne.firstName).to.eql('blabla');
});
});
});

describe('value()', () => {
Expand Down Expand Up @@ -271,6 +301,42 @@ describe('mocker test', () => {
expect(mock.user.info.firstName).to.eql('John');
expect(mock.user.info.username).to.eql('John.Doe');
});

it('should use value() function for property - nested property - nested option key', () => {
const theShema = new Schema({ user: { info: { firstName: String, username: String, lastName: String } } });
const thingMocker = mocker(theShema, unflatten({
'user.info.firstName': { value: () => 'John' },
'user.info.username': {
value: (object) => `${object.user.info.firstName}.${object.user.info.lastName}`
}
}));
const mock = thingMocker.generate({ user: { info: { lastName: 'Doe' } } });
expect(mock.user.info.firstName).to.eql('John');
expect(mock.user.info.username).to.eql('John.Doe');
});

describe('root level property', () => {
it('should allow using value from parent property - root key', () => {
const theShema = new Schema({ root: { levelOne: { firstName: String, username: String, lastName: String } } });
const thingMocker = mocker(theShema, { root: { value: () => ({ levelOne: { firstName: 'blabla' } }) } });
const mock = thingMocker.generate();
expect(mock.root.levelOne.firstName).to.eql('blabla');
});

it('should allow using value from parent property - sub level(levelOne) - nested option key', () => {
const theShema = new Schema({ root: { levelOne: { firstName: String, username: String, lastName: String } } });
const thingMocker = mocker(theShema, { root: { levelOne: { value: () => ({ firstName: 'blabla' }) } } });
const mock = thingMocker.generate();
expect(mock.root.levelOne.firstName).to.eql('blabla');
});

it('should allow using value from parent property - sub level(levelOne) - path option key(root.levelOne)', () => {
const theShema = new Schema({ root: { levelOne: { firstName: String, username: String, lastName: String } } });
const thingMocker = mocker(theShema, { "root.levelOne": { value: () => ({ firstName: 'blabla' }) } });
const mock = thingMocker.generate();
expect(mock.root.levelOne.firstName).to.eql('blabla');
});
});
});
});

Expand Down

0 comments on commit 727cb72

Please sign in to comment.