├── .github ├── ISSUE_TEMPLATE │ ├── question---.md │ ├── feature-request----.md │ └── bug-report----.md └── pull_request_template.md ├── .circleci └── config.yml ├── index.test-d.ts ├── index.d.ts ├── package.json ├── .gitignore ├── readme.md ├── index.js └── test.js /.github/ISSUE_TEMPLATE/question---.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Question \U0001F4AD" 3 | about: Ask us anything! 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | Fixes # 12 | 13 | ## Description 14 | 15 | 16 | 17 | ## How to test 18 | 19 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # https://circleci.com/docs/2.0/configuration-reference 2 | 3 | version: 2.1 4 | 5 | orbs: 6 | codecov: codecov/codecov@1.0.2 7 | 8 | defaults: &defaults 9 | working_directory: ~/repo 10 | docker: 11 | - image: circleci/node:latest 12 | 13 | jobs: 14 | build-and-test: 15 | <<: *defaults 16 | steps: 17 | - checkout 18 | - run: npm ci 19 | - run: npm run coverage 20 | - run: npm run coverage:upload 21 | - run: npm run coverage:check 22 | 23 | workflows: 24 | version: 2 25 | build: 26 | jobs: 27 | - build-and-test -------------------------------------------------------------------------------- /index.test-d.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import jaymock = require('.'); 3 | 4 | const jm = jaymock(); 5 | 6 | expectType(jm.populate({ 7 | name: 'fake({{name.lastName}}, {{name.firstName}} {{name.suffix}})', 8 | ssn: 'chance.ssn', 9 | knownAddresses: { 10 | street: 'address.streetAddress', 11 | city: 'address.city', 12 | zipCode: 'address.zipCode', 13 | _repeat: 2 14 | }, 15 | _repeat: 10 16 | })); 17 | expectType(jm.extend('fn', () => 'unicorn')); 18 | expectType(jm.extend({'fn': () => 'unicorn'})); 19 | expectType(jm.setFakerLocale('fr')); 20 | expectType(jm.setFakerSeed(1337)); -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request----.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Feature Request \U0001F3A8 " 3 | about: Give us ideas, then we'll see if we can make it happen. 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | ### Description 13 | 14 | 15 | 16 | ### Motivation 17 | 18 | 19 | 20 | ### Collaboration 21 | 22 | 23 | 24 | ### Additional Context 25 | 26 | 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report----.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Bug Report \U0001F41B " 3 | about: Notice something off? Tell us about it here. 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | ### Description 13 | 14 | 15 | 16 | ### Steps to Reproduce 17 | 18 | 19 | 20 | ### Expected Result 21 | 22 | 23 | 24 | ### Actual Result 25 | 26 | 27 | 28 | ### Additional Context 29 | 30 | 36 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace jaymock { 2 | interface Template { 3 | [key: string]: string | number | Template; 4 | } 5 | interface Populated { 6 | [key: string]: unknown | Array | Populated; 7 | } 8 | } 9 | 10 | declare function jaymock(): { 11 | /** 12 | Populates the template object with fake data. 13 | @param template - A [`template`](https://github.com/Meeshkan/jaymock#template) object to populate. 14 | @returns An `object` populated with fake data. 15 | */ 16 | populate(template: jaymock.Template | object): jaymock.Populated; 17 | 18 | /** 19 | Adds a custom data generation function that can be called in the `populate` `template` object using the value of its `name`. 20 | @param name - A `string` used to refer to the `body` function. 21 | @param body - A `function` that can be referred to in the `template` object. 22 | */ 23 | extend(name: string | {[key: string]: Function}, body?: Function): void; 24 | 25 | /** 26 | Sets `Faker.js`'s language locale. 27 | @param locale - `Faker.js` locale. 28 | */ 29 | setFakerLocale(locale: string): void; 30 | 31 | /** 32 | Sets `Faker.js`'s randomness seed. 33 | @param seed - `Faker.js` randomness seed. 34 | */ 35 | setFakerSeed(seed: number): void; 36 | }; 37 | 38 | export = jaymock; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@meeshkanml/jaymock", 3 | "version": "1.1.0", 4 | "description": "Minimal fake JSON test data generator", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "xo && ava && tsd", 8 | "coverage": "nyc npm test", 9 | "coverage:upload": "nyc report --reporter=lcov && codecov", 10 | "coverage:check": "nyc report && nyc check-coverage --lines 100 --functions 100 --branches 100" 11 | }, 12 | "files": [ 13 | "index.js", 14 | "index.d.ts" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/Meeshkan/jaymock.git" 19 | }, 20 | "xo": { 21 | "semicolon": false, 22 | "rules": { 23 | "max-depth": [ 24 | "error", 25 | 5 26 | ] 27 | } 28 | }, 29 | "author": "Meeshkan", 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/Meeshkan/jaymock/issues" 33 | }, 34 | "homepage": "https://github.com/Meeshkan/jaymock#readme", 35 | "dependencies": { 36 | "faker": "^4.1.0", 37 | "lodash.clonedeep": "^4.5.0" 38 | }, 39 | "devDependencies": { 40 | "ava": "^2.4.0", 41 | "codecov": "^3.6.1", 42 | "nyc": "^14.1.1", 43 | "supervillains": "^3.0.0", 44 | "tsd": "^0.11.0", 45 | "xo": "^0.25.3" 46 | }, 47 | "keywords": [ 48 | "mock", 49 | "json", 50 | "fake", 51 | "data" 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # jaymock 2 | 3 | > Minimal fake JSON test data generator. 4 | 5 | [![CircleCI](https://img.shields.io/circleci/build/github/meeshkan/jaymock?style=for-the-badge)](https://circleci.com/gh/Meeshkan/jaymock) [![XO](https://img.shields.io/badge/code_style-XO-5ed9c7.svg?style=for-the-badge)](https://github.com/xojs/xo) [![Codecov](https://img.shields.io/codecov/c/github/Meeshkan/jaymock?style=for-the-badge)](https://codecov.io/gh/Meeshkan/jaymock) 6 | 7 | ## Install 8 | 9 | ``` 10 | ~ ❯❯❯ npm install @meeshkanml/jaymock 11 | ``` 12 | 13 | ## Usage 14 | 15 | ```js 16 | const jaymock = require('@meeshkanml/jaymock') 17 | 18 | const data = { 19 | firstName: 'name.firstName', 20 | lastName: 'name.lastName', 21 | ssn: 'ssn', 22 | address: { 23 | streetAddress: 'address.streetAddress', 24 | city: 'address.city', 25 | zipCode: 'address.zipCode' 26 | }, 27 | emails: 'internet.email', 28 | ipAddress: 'internet.ip', 29 | _repeat: 2 30 | } 31 | 32 | const jm = jaymock() 33 | 34 | const randExp = require('randexp').randexp 35 | // Add custom functions using `.extend()` 36 | jm.extend({ 37 | ssn: () => randExp(/^\d{3}-\d{2}-\d{4}$/) 38 | }) 39 | 40 | const fakeData = jm.populate(data) 41 | /* 42 | [ 43 | { 44 | firstName: 'Marguerite', 45 | lastName: 'Will', 46 | ssn: '076-86-6001', 47 | address: { 48 | streetAddress: '4509 Abernathy Port', 49 | city: 'Port Charles', 50 | zipCode: '26322' 51 | }, 52 | emails: 'Missouri64@yahoo.com', 53 | ipAddress: '44.210.55.248' 54 | }, 55 | { 56 | firstName: 'Fredrick', 57 | lastName: 'McClure', 58 | ssn: '610-42-4980', 59 | address: { 60 | streetAddress: '56363 Goyette Station', 61 | city: 'West Floydmouth', 62 | zipCode: '73634-6751' 63 | }, 64 | emails: 'Aurore58@hotmail.com', 65 | ipAddress: '237.7.221.162' 66 | } 67 | ] 68 | */ 69 | ``` 70 | 71 | ### Mock API using [express](https://github.com/expressjs/express) 72 | 73 | ```js 74 | const jaymock = require('@meeshkanml/jaymock') 75 | const express = require('express') 76 | 77 | app = express() 78 | app.use(express.json()) 79 | 80 | const jm = jaymock() 81 | jm.extend('chance', new require('chance')()) 82 | 83 | app.post('/', (req, res) => res.json(jm.populate(req.body))) 84 | 85 | app.listen(3000) 86 | ``` 87 | 88 | ## API 89 | 90 | ### .populate(template) 91 | 92 | Returns an `object`, populated with fake data. 93 | 94 | #### template 95 | 96 | Type: `object` 97 | 98 | Each object's value can be one of [`Faker.js`'s API methods](https://github.com/marak/Faker.js/#api-methods), in the format `'{topic}.{subtopic}'` (e.g. `'name.firstName'`) or a custom method, defined using the `.extend` function, in the format `'{function_name}'` or `{function_name}.{nested_function_name}` (e.g. `'foo'` will call `foo()` and `'foo.bar'` will call `foo.bar()`). 99 | 100 | A fake value can be generated `n` times, into an array of `n` values, by including `|n` at the end of the individual object's method name (e.g. `'name.firstName|5'` will generate an array, populated with `5` fake first names). This also works with custom functions, accordingly. 101 | 102 | To use the [`faker.fake()`](https://github.com/marak/Faker.js/#fakerfake) method (which permits the combination of faker API methods), use the format `'fake({mustache_strings})'` (e.g. `'fake({{name.lastName}}, {{name.firstName}} {{name.suffix}})'`). 103 | 104 | ### .extend(name, body) 105 | 106 | Adds a custom data generation function that can be called in the [`.populate`](#populatetemplate) `template` using the value of `name`. 107 | 108 | #### name 109 | 110 | Type: `string` 111 | 112 | #### body 113 | 114 | Type: `function` 115 | 116 | ### .extend(functions) 117 | 118 | Adds custom data generation functions that can be called in the [`.populate`](#populatetemplate) `template` using the value of each object key. 119 | 120 | #### functions 121 | 122 | Type: `object` 123 | 124 | Each object `key` should be the relevant function's name and `value` the function's body (e.g. `{ 'chance': new require('chance')() }`). 125 | 126 | ### .setFakerLocale(locale) 127 | 128 | Sets [`Faker.js`'s language locale](https://github.com/Marak/Faker.js/#localization). 129 | 130 | #### locale 131 | 132 | Type: `string` 133 | 134 | Any of [`Faker.js`'s locale options](https://github.com/Marak/Faker.js/#localization). 135 | 136 | ### .setFakerSeed(seed) 137 | 138 | Sets [`Faker.js`'s randomness seed](https://github.com/Marak/Faker.js/#setting-a-randomness-seed). 139 | 140 | #### seed 141 | 142 | Type: `number` 143 | 144 | ## Related 145 | 146 | - [micro-jaymock](https://github.com/Meeshkan/micro-jaymock) - Tiny API mocking microservice, which uses jaymock 147 | - [jaymock-cli](https://github.com/Meeshkan/jaymock-cli) - CLI for this module 148 | 149 | ## Contributing 150 | 151 | Thanks for wanting to contribute! We will soon have a contributing page 152 | detailing how to contribute. Meanwhile, feel free to star this repository, open issues, 153 | and ask for more features and support. 154 | 155 | Please note that this project is governed by the [Meeshkan Community Code of Conduct](https://github.com/meeshkan/code-of-conduct). By participating in this project, you agree to abide by its terms. 156 | 157 | ## Credits 158 | 159 | - [`Faker.js`](https://github.com/Marak/Faker.js) is used as `jaymock`'s core fake data generator. 160 | 161 | ## License 162 | 163 | MIT © [Meeshkan](http://meeshkan.com/) 164 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | const faker = require('faker') 8 | const cloneDeep = require('lodash.clonedeep') 9 | 10 | /** 11 | * Helper functions. 12 | */ 13 | 14 | const isType = (value, type) => typeof value === type 15 | const isObject = value => isType(value, 'object') 16 | const generateArrayOfLength = length => [...new Array(length)] 17 | const isObjectKey = (key, object) => Object.keys(object).includes(key) 18 | 19 | /** 20 | * Returns Faker.js generated data. 21 | * 22 | * @param {String} topic 23 | * @param {String} subtopic 24 | * @returns {String|Number|Object} Fake Data 25 | * @api private 26 | */ 27 | 28 | const generateFakerData = (topic, subtopic) => { 29 | if (topic === 'fake') { 30 | return faker[topic](subtopic.slice(1, -1)) 31 | } 32 | 33 | return faker[topic][subtopic]() 34 | } 35 | 36 | /** 37 | * Parses function payload to the appropriate topic and subtopic. 38 | * 39 | * @param {String} payload 40 | * @returns {Array} [Topic, Subtopic] 41 | * @api private 42 | */ 43 | 44 | const parsePayload = payload => { 45 | let topic 46 | let subtopic 47 | payload = String(payload) 48 | if (payload.startsWith('fake')) { 49 | topic = 'fake'; 50 | [, subtopic] = payload.split(topic) 51 | } else { 52 | [topic, subtopic] = payload.split(/\.(.+)/) 53 | } 54 | 55 | return [topic, subtopic] 56 | } 57 | 58 | /** 59 | * Returns the appropriate fake data 60 | * (generated using either Faker.js or custom functions). 61 | * 62 | * @param {String} payload 63 | * @param {Object} customFunctions 64 | * @returns {String|Number|Object} Fake Data 65 | * @api private 66 | */ 67 | 68 | const fake = (payload, customFunctions) => { 69 | let numOfValues = null 70 | let [topic, subtopic] = parsePayload(payload) 71 | if (subtopic && subtopic.includes('|')) { 72 | [subtopic, numOfValues] = subtopic.split('|') 73 | numOfValues = parseInt(numOfValues, 10) 74 | } else if (!subtopic && topic && topic.includes('|')) { 75 | [topic, numOfValues] = topic.split('|') 76 | numOfValues = parseInt(numOfValues, 10) 77 | } 78 | 79 | if (isObjectKey(topic, customFunctions)) { 80 | const func = customFunctions[topic] 81 | if (numOfValues) { 82 | if (func[subtopic] !== undefined) { 83 | return generateArrayOfLength(numOfValues).map(_ => func[subtopic]()) 84 | } 85 | 86 | return generateArrayOfLength(numOfValues).map(_ => func()) 87 | } 88 | 89 | if (func[subtopic]) { 90 | return func[subtopic]() 91 | } 92 | 93 | return func() 94 | } 95 | 96 | if (!subtopic || ((faker[topic] === undefined || faker[topic][subtopic] === undefined) && !subtopic.includes('.') && !subtopic.includes('|'))) { 97 | subtopic = subtopic ? '.' + subtopic : '' 98 | throw new Error(`Function ${JSON.stringify(topic + subtopic)} does not exist`) 99 | } 100 | 101 | if (numOfValues) { 102 | return generateArrayOfLength(numOfValues).map(_ => generateFakerData(topic, subtopic)) 103 | } 104 | 105 | return generateFakerData(topic, subtopic) 106 | } 107 | 108 | /** 109 | * Populates the template object with fake data. 110 | * 111 | * @param {String} object 112 | * @param {String} funcObject 113 | * @param {Boolean} firstRun 114 | * @returns {Object} Populated Object 115 | * @api private 116 | */ 117 | 118 | const populateObject = (object, funcObject, firstRun = true) => { 119 | object = cloneDeep(object) 120 | const repeatParentObject = firstRun && isObjectKey('_repeat', object) && object._repeat !== undefined 121 | for (let [key, value] of Object.entries(object)) { 122 | if (repeatParentObject) { 123 | value = object 124 | } 125 | 126 | if (isObject(value)) { 127 | if (value._repeat) { 128 | const repeatCount = value._repeat 129 | delete value._repeat 130 | if (repeatParentObject) { 131 | const temp = object 132 | object = [] 133 | for (let j = 0; j < repeatCount; j++) { 134 | object.push(populateObject(temp, funcObject, false)) 135 | } 136 | 137 | return object 138 | } 139 | 140 | object[key] = [] 141 | for (let j = 0; j < repeatCount; j++) { 142 | object[key].push(populateObject(value, funcObject, false)) 143 | } 144 | } else { 145 | object[key] = populateObject(value, funcObject, false) 146 | } 147 | } else { 148 | object[key] = fake(value, funcObject) 149 | } 150 | } 151 | 152 | return object 153 | } 154 | 155 | const jaymock = () => new JayMock() 156 | 157 | /** 158 | * Expose `jaymock`. 159 | */ 160 | 161 | module.exports = jaymock 162 | 163 | /** 164 | * Initializes `JayMock`. 165 | * 166 | * @method jaymock 167 | * @api public 168 | */ 169 | 170 | function JayMock() { 171 | this.template = {} 172 | this.functions = {} 173 | } 174 | 175 | /** 176 | * Populates the template object with fake data. 177 | * 178 | * @method jaymock.populate 179 | * @param {Object} template 180 | * @returns {Object} Populated Object 181 | * @api public 182 | */ 183 | 184 | JayMock.prototype.populate = function (template) { 185 | this.template = template 186 | return populateObject(this.template, this.functions) 187 | } 188 | 189 | /** 190 | * Adds custom data generation function(s). 191 | * 192 | * @method jaymock.extend 193 | * @param {String|Object} funcName 194 | * @param {?String} funcBody 195 | * @api public 196 | */ 197 | 198 | JayMock.prototype.extend = function (funcName, funcBody) { 199 | if (isObject(funcName) && !funcBody) { 200 | this.functions = funcName 201 | } else { 202 | this.functions[funcName] = funcBody 203 | } 204 | } 205 | 206 | /** 207 | * Sets `Faker.js`'s language locale. 208 | * 209 | * @method jaymock.setFakerLocale 210 | * @param {String} locale 211 | * @api public 212 | */ 213 | 214 | JayMock.prototype.setFakerLocale = function (locale) { 215 | faker.locale = locale 216 | } 217 | 218 | /** 219 | * Sets `Faker.js`'s randomness seed. 220 | * 221 | * @method jaymock.setFakerSeed 222 | * @param {Number} seed 223 | * @api public 224 | */ 225 | 226 | JayMock.prototype.setFakerSeed = function (seed) { 227 | faker.seed(seed) 228 | } 229 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import supervillains from 'supervillains' 3 | import jaymock from '.' 4 | 5 | const fixtures = { 6 | flat: { 7 | firstName: 'name.firstName', 8 | lastName: 'name.lastName' 9 | }, 10 | nested: { 11 | addresses: { 12 | homeAddress: { 13 | streetAddress: 'address.streetAddress', 14 | city: 'address.city', 15 | zipCode: 'address.zipCode' 16 | }, 17 | workAddress: { 18 | streetAddress: 'address.streetAddress', 19 | city: 'address.city', 20 | zipCode: 'address.zipCode' 21 | } 22 | } 23 | }, 24 | repeat: { 25 | firstName: 'name.firstName', 26 | lastName: 'name.lastName', 27 | _repeat: 5 28 | }, 29 | repeatNested: { 30 | firstName: 'name.firstName', 31 | lastName: 'name.lastName', 32 | ipAddress: { 33 | ipv4: 'internet.ip', 34 | ipv6: 'internet.ipv6', 35 | _repeat: 3 36 | } 37 | }, 38 | arrayFunction: { 39 | ipAddress: 'internet.ip|5' 40 | }, 41 | customFunction: { 42 | color: 'hexColor' 43 | }, 44 | customFunctionAsObject: { 45 | unicorn: 'foo' 46 | }, 47 | customNestedFunction: { 48 | supervillain: 'supervillains.random' 49 | }, 50 | customArrayFunction: { 51 | color: 'hexColor|10' 52 | }, 53 | customNestedArrayFunction: { 54 | supervillains: 'supervillains.random|5' 55 | }, 56 | fakerFake: { 57 | fullName: 'fake({{name.lastName}}, {{name.firstName}} {{name.suffix}})' 58 | }, 59 | invalidFakerFunction: { 60 | invalid: 'name.doesnt_exist' 61 | }, 62 | invalidCustomFunction: { 63 | invalid: 'doesnt_exist' 64 | }, 65 | fakerLocale: { 66 | name: 'name.firstName' 67 | }, 68 | fakerSeed: { 69 | number: 'random.number' 70 | } 71 | } 72 | 73 | const ipAddressRegex = /^(?:\d{1,3}\.){3}\d{1,3}$/ 74 | const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/ 75 | const randomHexColor = () => '#' + ('000000' + Math.floor(Math.random() * 16777215).toString(16)).slice(-6) 76 | 77 | test('flat object', t => { 78 | const data = fixtures.flat 79 | const expectedKeys = Object.keys(data) 80 | const obj = jaymock().populate(data) 81 | const actualKeys = Object.keys(obj) 82 | t.deepEqual(expectedKeys, actualKeys) 83 | actualKeys.forEach(key => { 84 | t.true(obj[key] !== undefined && obj[key].length > 0) 85 | }) 86 | }) 87 | 88 | test('nested object', t => { 89 | const data = fixtures.nested 90 | const expectedKeys = Object.keys(data) 91 | const obj = jaymock().populate(data) 92 | const actualKeys = Object.keys(data) 93 | t.deepEqual(expectedKeys, actualKeys) 94 | actualKeys.forEach(key => { 95 | t.true(obj[key] !== undefined && (obj[key].length > 0 || typeof obj[key] === 'object')) 96 | }) 97 | }) 98 | 99 | test('repeat mother object', t => { 100 | const data = fixtures.repeat 101 | const expectedKeys = Object.keys(data).filter(x => x !== '_repeat') 102 | const objects = jaymock().populate(data) 103 | objects.forEach(obj => { 104 | const actualKeys = Object.keys(obj) 105 | t.deepEqual(expectedKeys, actualKeys) 106 | actualKeys.forEach(key => { 107 | t.true(obj[key] !== undefined && obj[key].length > 0) 108 | }) 109 | }) 110 | }) 111 | 112 | test('repeat nested object', t => { 113 | const data = fixtures.repeatNested 114 | const obj = jaymock().populate(data) 115 | Object.keys(obj).forEach(key => { 116 | const value = obj[key] 117 | t.true(value !== undefined && value.length > 0) 118 | if (key === 'ipAddress') { 119 | t.true(Array.isArray(value)) 120 | value.forEach(innerObj => { 121 | const expectedKeys = Object.keys(data.ipAddress).filter(x => x !== '_repeat') 122 | const actualKeys = Object.keys(innerObj) 123 | t.deepEqual(expectedKeys, actualKeys) 124 | actualKeys.forEach(innerKey => { 125 | t.true(innerObj[innerKey] !== undefined && innerObj[innerKey].length > 0) 126 | }) 127 | }) 128 | } 129 | }) 130 | }) 131 | 132 | test('{faker function}|{desired array length}', t => { 133 | const data = fixtures.arrayFunction 134 | const expectedKeys = Object.keys(data) 135 | const obj = jaymock().populate(data) 136 | const actualKeys = Object.keys(obj) 137 | t.deepEqual(expectedKeys, actualKeys) 138 | const actualArray = obj.ipAddress 139 | t.true(Array.isArray(actualArray) && actualArray.length === parseInt(data.ipAddress.split('|')[1], 10)) 140 | actualArray.forEach(value => t.regex(value, ipAddressRegex)) 141 | }) 142 | 143 | test('custom data generation function', t => { 144 | const data = fixtures.customFunction 145 | const expectedKeys = Object.keys(data) 146 | const jm = jaymock() 147 | jm.extend('hexColor', () => randomHexColor()) 148 | const obj = jm.populate(data) 149 | const actualKeys = Object.keys(obj) 150 | t.deepEqual(expectedKeys, actualKeys) 151 | t.regex(obj.color, hexColorRegex) 152 | }) 153 | 154 | test('custom data generation function passed as object', t => { 155 | const data = fixtures.customFunctionAsObject 156 | const jm = jaymock() 157 | jm.extend({foo: () => 'bar'}) 158 | const obj = jm.populate(data) 159 | t.is(obj.unicorn, 'bar') 160 | }) 161 | 162 | test('custom nested, data generation function', t => { 163 | const data = fixtures.customNestedFunction 164 | const jm = jaymock() 165 | jm.extend({supervillains}) 166 | const obj = jm.populate(data) 167 | t.true(supervillains.all.includes(obj.supervillain)) 168 | }) 169 | 170 | test('{custom function}|{desired array length}', t => { 171 | const data = fixtures.customArrayFunction 172 | const expectedKeys = Object.keys(data) 173 | const jm = jaymock() 174 | jm.extend('hexColor', () => randomHexColor()) 175 | const obj = jm.populate(data) 176 | const actualKeys = Object.keys(obj) 177 | t.deepEqual(expectedKeys, actualKeys) 178 | const actualArray = obj.color 179 | t.true(Array.isArray(actualArray) && actualArray.length === parseInt(data.color.split('|')[1], 10)) 180 | actualArray.forEach(value => t.regex(value, hexColorRegex)) 181 | }) 182 | 183 | test('{custom nested function}|{desired array length}', t => { 184 | const data = fixtures.customNestedArrayFunction 185 | const jm = jaymock() 186 | jm.extend({supervillains}) 187 | const obj = jm.populate(data) 188 | t.true(obj.supervillains.every(element => supervillains.all.includes(element))) 189 | }) 190 | 191 | test('faker.fake() generation function', t => { 192 | const data = fixtures.fakerFake 193 | const expectedKeys = Object.keys(data) 194 | const obj = jaymock().populate(data) 195 | const actualKeys = Object.keys(obj) 196 | t.deepEqual(expectedKeys, actualKeys) 197 | t.true(obj.fullName.split(' ').length > 1) 198 | }) 199 | 200 | test('invalid faker function', t => { 201 | const data = fixtures.invalidFakerFunction 202 | const error = t.throws(() => { 203 | jaymock().populate(data) 204 | }, Error) 205 | t.is(error.message, `Function ${JSON.stringify(data.invalid)} does not exist`) 206 | }) 207 | 208 | test('invalid custom function', t => { 209 | const data = fixtures.invalidCustomFunction 210 | const error = t.throws(() => { 211 | jaymock().populate(data) 212 | }, Error) 213 | t.is(error.message, `Function ${JSON.stringify(data.invalid)} does not exist`) 214 | }) 215 | 216 | test('faker locale', t => { 217 | const data = fixtures.fakerLocale 218 | const jm = jaymock() 219 | jm.setFakerLocale('ru') 220 | const obj = jm.populate(data) 221 | t.regex(obj[Object.keys(obj)[0]], /[\w\u0430-\u044F]+/) 222 | }) 223 | 224 | test('faker random seed', t => { 225 | const data = fixtures.fakerSeed 226 | const jm = jaymock() 227 | jm.setFakerSeed(1) 228 | const obj = jm.populate(data) 229 | t.is(obj[Object.keys(obj)[0]], 41702) 230 | }) 231 | --------------------------------------------------------------------------------