Skip to content

Commit

Permalink
Merge 8a0fb75 into 270511e
Browse files Browse the repository at this point in the history
  • Loading branch information
faboulaws committed Aug 31, 2019
2 parents 270511e + 8a0fb75 commit 2b7ffb2
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 57 deletions.
64 changes: 60 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,17 +131,73 @@ const mock = blogFactory.generate({
}
```

# Define options for nested properties
# Define options for nested properties

To define options for nested a property use the nested property path (property names sperated by a dot).
Example:
```js

``` js
const options = {
"meta.votes": { value: 0 }, // set value for 'votes' property under meta
"meta.favs": { skip: true } // skip value for 'favs' property under meta
"meta.votes": {
value: 0
}, // set value for 'votes' property under meta
"meta.favs": {
skip: true
} // skip value for 'favs' property under meta
}
```

# Skipping multiple nested properties

Multiple nested properties can be skipped from parent property.
Example:

```js
const accountSchema = new mongoose.Schema({
user: {
generalInfo: {
firstName: String,
lastName: String,
age: Number
},
address: {
postCode: String,
street: String
}
}
});
const accountFactory = factory(accountSchema, options);
```

To generate mocks without an addres define options as below

``` js
const options = {
'user.address': {
skip: true
}
}
```

or


``` js
const options = {
user: {
address: {
skip: true
}
}
}
```

then

```js
const mockObject = accountFactory.generate(options);
```

# Supported Types

* String
Expand Down
31 changes: 30 additions & 1 deletion lib/mocker.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,38 @@ const generators = {
},
};

const getPropertyByPath = (object, path) => get(object, path);

const getPropertyByName = (object, propName) => object[propName];

const propertyIsSkipped = (path, options) => {
const fieldOptions = getPropertyByName(options, path) || getPropertyByPath(options, path) || {};
return fieldOptions.skip === true;
};

const propertyParentIsSkipped = (path, options) => {
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.skip === true) {
return true;
}
}
}
return false;
};

const shouldSkipProperty = (path, options) =>
propertyIsSkipped(path, options) || propertyParentIsSkipped(path, options);

function populateField(mockObject, schema, path, options, staticValue) {
const fieldOptions = options[path] || {};
if (!fieldOptions.skip) {
if (!shouldSkipProperty(path, options)) {
let value;
const pathDef = schema.path(path);

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"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
181 changes: 129 additions & 52 deletions test/mocker.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const mongoose = require('mongoose');
const { expect } = require('chai');
const get = require('lodash.get');
const { unflatten } = require('flat');
const mocker = require('../');

const { Schema } = mongoose;
Expand Down Expand Up @@ -174,9 +175,9 @@ describe('mocker test', () => {
}
});

describe('static values', () => {
describe('generate(staticFields)', () => {
const embed = new Schema({ name: String });
const stringShema = new Schema({
const thingShema = new Schema({
str: { type: String },
nested: { name: String },
doubleNested: {
Expand Down Expand Up @@ -206,10 +207,8 @@ describe('mocker test', () => {
ofEmbedded: [{ name: 'ofEmbedded' }, { name: 'ofEmbedded' }],
};

const StringThing = mongoose.model('SomeThing', stringShema);

it('should use static value', () => {
const thingMocker = mocker(StringThing);
const thingMocker = mocker(thingShema);
const mock = thingMocker.generate(staticFields);
// expect(mock).to.deep.include(staticFields);
const paths = [
Expand All @@ -226,69 +225,147 @@ describe('mocker test', () => {
expect(get(mock, path)).to.eql(get(staticFields, path));
});
});

});

describe('value overrides via options', () => {
const stringShema = new Schema({ firstName: String, username: String, lastName: String });
const StringThing = mongoose.model('StrThing', stringShema);
describe('options.<propertyName>.value', () => {
describe('value', () => {
it('should use static value - at root', () => {
const userSchema = new Schema({ firstName: String, username: String, lastName: String });
const thingMocker = mocker(userSchema, { firstName: { value: 'blabla' } });
const mock = thingMocker.generate();
expect(mock.firstName).to.eql('blabla');
});

it('should use static value - at root', () => {
const thingMocker = mocker(StringThing, { firstName: { value: 'blabla' } });
const mock = thingMocker.generate();
expect(mock.firstName).to.eql('blabla');
it('should use static value - at leaf', () => {
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', () => {
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('value()', () => {
it('should use value() function for property', () => {
const userSchema = new Schema({ firstName: String, username: String, lastName: String });
const thingMocker = mocker(userSchema, {
firstName: { value: () => 'John' },
username: {
value: (object) => `${object.firstName}.${object.lastName}`
}
});
const mock = thingMocker.generate({ lastName: 'Doe' });
expect(mock.firstName).to.eql('John');
expect(mock.username).to.eql('John.Doe');
});

it('should use function value - root', () => {
const thingMocker = mocker(stringShema, {
firstName: { value: () => 'John' },
username: {
value: (object) => `${object.firstName}.${object.lastName}`
}
it('should use value() function for property - nested property', () => {
const theShema = new Schema({ user: { info: { firstName: String, username: String, lastName: String } } });
const thingMocker = mocker(theShema, {
'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');
});
const mock = thingMocker.generate({ lastName: 'Doe' });
expect(mock.firstName).to.eql('John');
expect(mock.username).to.eql('John.Doe');
});
});

it('should use function value - nested', () => {
const theShema = new Schema({ user: { info: { firstName: String, username: String, lastName: String } } });
const thingMocker = mocker(theShema, {
'user.info.firstName': { value: () => 'John' },
'user.info.username': {
value: (object) => `${object.user.info.firstName}.${object.user.info.lastName}`
}
describe('options.<propertyName>.skip', () => {
describe('direct skip', () => {
it('should skip property at root', () => {
const stringShema = new Schema({ firstName: String, username: String, lastName: String });
const thingMocker = mocker(stringShema, { username: { skip: true } });
const mock = thingMocker.generate();
expect(mock).not.to.have.property('username');
expect(mock).to.have.property('firstName');
expect(mock).to.have.property('lastName');
});

it('should skip nested property - key as path', () => {
const theSchema = new Schema({ user: { firstName: String, lastName: String, username: String } });
const options = { "user.username": { skip: true } }
const thingMocker = mocker(theSchema, options);
const mock = thingMocker.generate();
expect(mock).to.have.property('user');
expect(mock.user).to.have.property('firstName');
expect(mock.user).to.have.property('lastName');
expect(mock.user).not.to.have.property('username');
});
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');
});

it('skip value - root', () => {
const thingMocker = mocker(StringThing, { username: { skip: true } });
const mock = thingMocker.generate();
expect(mock).not.to.have.property('username');
it('should skip nested property - key is nested', () => {
const sschema = new Schema({ root: { name: String, nickname: String }, other: { name: String } });
const options = { root: { name: { skip: true } } };
const thingMocker = mocker(sschema, options);
const mock = thingMocker.generate();
expect(mock).to.have.property('root');
expect(mock.root).to.have.property('nickname');
expect(mock.root).not.to.have.property('name');
});
});

it('skip value - nested', () => {
const theSchema = new Schema({ user: { firstName: String, lastName: String, username: String } });
const options = { "user.username": { skip: true } }
const thingMocker = mocker(theSchema, options);
const mock = thingMocker.generate();
expect(mock).to.have.property('user');
expect(mock.user).to.have.property('firstName');
expect(mock.user).to.have.property('lastName');
expect(mock.user).not.to.have.property('username');
describe('indirect skip (from parent)', () => {
it('should skip a subtree - from root', () => {
const sschema = new Schema({
root: { name: String, nickname: String },
other: { name: String }
});
const thingMocker = mocker(sschema, { root: { skip: true } });
const mock = thingMocker.generate();
expect(mock).not.to.have.property('root');
expect(mock).to.have.property('other');
expect(mock.other).to.have.property('name');
});

describe('nested property under root', () => {
const sschema = new Schema({
root: {
levelOne: {
name: String,
nickname: String
},
level1: {
other: {
name: String
}
}
}
});

const directOptions = {
"root.level1": { skip: true },
"root.levelOne.name": { skip: true },
}

it('direct options\'s keys', () => {
const options = directOptions;
const thingMocker = mocker(sschema, options);
const mock = thingMocker.generate();
expect(mock, 'Must have root').to.have.property('root');
expect(mock.root, 'Root must have levelOne').to.have.property('levelOne');
expect(mock.root, 'Root must not have level1').not.to.have.property('level1');
expect(mock.root.levelOne).not.to.have.property('name');
expect(mock.root.levelOne).to.have.property('nickname');
});

it('nested options\'s keys', () => {
const options = unflatten(directOptions);
const thingMocker = mocker(sschema, options);
const mock = thingMocker.generate();
expect(mock, 'Must have root').to.have.property('root');
expect(mock.root, 'Root must have levelOne').to.have.property('levelOne');
expect(mock.root, 'Root must not have level1').not.to.have.property('level1');
expect(mock.root.levelOne).not.to.have.property('name');
expect(mock.root.levelOne).to.have.property('nickname');
});
});
});
});

describe('options', () => {
describe('options.<propertyName>.type', () => {
describe('string', () => {
const stringShema = new Schema({ str: String });
const StringThing = mongoose.model('StringThing', stringShema);
Expand Down

0 comments on commit 2b7ffb2

Please sign in to comment.