├── .npmignore ├── .gitignore ├── cli.js ├── package.json ├── LICENSE ├── lib ├── utils.js ├── mockdata.js └── helpers.js ├── index.js ├── index.d.ts ├── test ├── dummyjson.spec.js └── helpers.spec.js └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | example.js 3 | TODO.md 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | example.js 4 | TODO.md 5 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var os = require('os'); 4 | var fs = require('fs'); 5 | var dummyjson = require('./index'); 6 | 7 | var args = process.argv.slice(2); 8 | 9 | if (args && args[0]) { 10 | var result = dummyjson.parse( 11 | fs.readFileSync(args[0], {encoding: 'utf8'}) 12 | ); 13 | process.stdout.write(result + os.EOL); 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dummy-json", 3 | "description": "Dummy JSON mock data generator for Node.js", 4 | "version": "3.0.5", 5 | "homepage": "https://github.com/webroo/dummy-json", 6 | "author": "Matt Sweetman", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "git://github.com/webroo/dummy-json.git" 11 | }, 12 | "dependencies": { 13 | "fecha": "^3.0.3", 14 | "handlebars": "^4.7.6", 15 | "numbro": "^2.3.1", 16 | "seedrandom": "^3.0.5" 17 | }, 18 | "devDependencies": { 19 | "mocha": "^8.1.1" 20 | }, 21 | "main": "index.js", 22 | "types": "index.d.ts", 23 | "bin": { 24 | "dummyjson": "cli.js" 25 | }, 26 | "scripts": { 27 | "test": "node_modules/mocha/bin/mocha ./test" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Matt Sweetman 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | var seedrandom = require('seedrandom'); 2 | 3 | // Create an instance of the prng without a seed (so it'll be a random sequence every time) 4 | var prng = seedrandom(); 5 | 6 | var utils = { 7 | setRandomSeed: function(seed) { 8 | prng = seedrandom(seed); 9 | }, 10 | 11 | random: function() { 12 | return prng(); 13 | }, 14 | 15 | randomInt: function(min, max) { 16 | return Math.floor(utils.random() * (max - min + 1)) + min; 17 | }, 18 | 19 | randomFloat: function(min, max) { 20 | return utils.random() * (max - min) + min; 21 | }, 22 | 23 | randomBoolean: function() { 24 | return utils.random() < 0.5; 25 | }, 26 | 27 | randomDate: function(min, max) { 28 | // We add the timezone offset to avoid the date falling outside the supplied range 29 | var d = new Date(Math.floor(utils.random() * (max - min)) + min); 30 | d.setTime(d.getTime() + d.getTimezoneOffset() * 60000); 31 | return d; 32 | }, 33 | 34 | randomArrayItem: function(array) { 35 | return array[utils.randomInt(0, array.length - 1)]; 36 | }, 37 | 38 | randomChar: function(charset) { 39 | charset = charset || 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 40 | return charset.charAt(utils.randomInt(0, charset.length - 1)); 41 | } 42 | }; 43 | 44 | module.exports = utils; 45 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var Handlebars = require('handlebars'); 2 | var fecha = require('fecha'); 3 | var numbro = require('numbro'); 4 | var mockdata = require('./lib/mockdata'); 5 | var helpers = require('./lib/helpers'); 6 | var utils = require('./lib/utils'); 7 | 8 | var dummyjson = { 9 | // Global seed for the random number generator 10 | seed: null, 11 | 12 | parse: function(string, options) { 13 | options = options || {}; 14 | 15 | // Merge custom mockdata/helpers into the defaults, items with the same name will override 16 | options.mockdata = Handlebars.Utils.extend({}, mockdata, options.mockdata); 17 | options.helpers = Handlebars.Utils.extend({}, helpers, options.helpers); 18 | 19 | // If a seed is passed in the options it will override the default one 20 | utils.setRandomSeed(options.seed || dummyjson.seed); 21 | 22 | // Certain helpers, such as name and email, attempt to synchronise and use the same values when 23 | // called after one-another. This object acts as a cache so the helpers can share their values. 24 | options.mockdata.__cache = {}; 25 | 26 | return Handlebars.compile(string)(options.mockdata, { 27 | helpers: options.helpers, 28 | partials: options.partials 29 | }); 30 | }, 31 | 32 | // Expose the built-in modules so people can use them in their own helpers 33 | mockdata: mockdata, 34 | helpers: helpers, 35 | utils: utils, 36 | 37 | // Also expose the number and date formatters so people can modify their settings 38 | fecha: fecha, 39 | numbro: numbro 40 | }; 41 | 42 | module.exports = dummyjson; 43 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import fecha from 'fecha'; 2 | import numbro from 'numbro'; 3 | 4 | interface DefaultMockdata { 5 | titles: string[]; 6 | firstNames: string[]; 7 | lastNames: string[]; 8 | companies: string[]; 9 | tlds: string[]; 10 | streets: string[]; 11 | cities: string[]; 12 | countries: string[]; 13 | countryCodes: string[]; 14 | colors: string[]; 15 | lorem: string[]; 16 | } 17 | 18 | interface DefaultHelpers { 19 | repeat(min: number, max?: number, options?: any): string; 20 | int(min: number, max: number, format?: string, options?: any): string; 21 | float(min: number, max: number, format?: string, options?: any): string; 22 | boolean(options?: any): string; 23 | date(min: string, max: string, format?: string, options?: any): string; 24 | time(min: string, max: string, format?: string, options?: any): string; 25 | title(options?: any): string; 26 | firstName(options?: any): string; 27 | lastName(options?: any): string; 28 | username(options?: any): string; 29 | company(options?: any): string; 30 | tld(options?: any): string; 31 | domain(options?: any): string; 32 | email(options?: any): string; 33 | street(options?: any): string; 34 | city(options?: any): string; 35 | country(options?: any): string; 36 | countryCode(options?: any): string; 37 | zipcode(options?: any): string; 38 | postcode(options?: any): string; 39 | lat(options?: any): string; 40 | long(options?: any): string; 41 | phone(format?: string, options?: any): string; 42 | guid(options?: any): string; 43 | ipv4(options?: any): string; 44 | ipv6(options?: any): string; 45 | color(options?: any): string; 46 | hexColor(options?: any): string; 47 | char(charset: string, options?: any): string; 48 | lorem(totalWords: number, options?: any): string; 49 | random(...items: (string | number)[]): string; 50 | lowercase(value: any): string; 51 | uppercase(value: any): string; 52 | add(a: number, b: number): string; 53 | step(inc: number, options?: any): string; 54 | } 55 | 56 | interface Utils { 57 | setRandomSeed(seed: string): void; 58 | random(): number; 59 | randomInt(min: number, max: number): number; 60 | randomFloat(min: number, max: number): number; 61 | randomBoolean(): boolean; 62 | randomDate(min: number, max: number): Date; 63 | randomArrayItem(array: any[]): any; 64 | randomChar(chartset?: string): string; 65 | } 66 | 67 | export interface ParseOptions { 68 | mockdata?: Record; 69 | helpers?: Record; 70 | partials?: Record; 71 | seed?: string; 72 | } 73 | 74 | declare const dummyjson: { 75 | seed: string; 76 | parse(string: string, options?: ParseOptions): string; 77 | mockdata: DefaultMockdata; 78 | helpers: DefaultHelpers; 79 | utils: Utils; 80 | fecha: typeof fecha; 81 | numbro: typeof numbro; 82 | }; 83 | 84 | export default dummyjson; 85 | -------------------------------------------------------------------------------- /test/dummyjson.spec.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var dummyjson = require('../index'); 3 | 4 | dummyjson.seed = 'helloworld'; 5 | 6 | describe('dummyjson', function() { 7 | describe('parse', function() { 8 | it('should throw an error if not given a string to parse', function() { 9 | assert.throws( 10 | function() { 11 | dummyjson.parse(); 12 | }, 13 | Error 14 | ); 15 | }); 16 | 17 | it('should expose the default mockdata, helpers, and utils', function() { 18 | assert.notEqual(dummyjson.mockdata, undefined); 19 | assert.notEqual(dummyjson.helpers, undefined); 20 | assert.notEqual(dummyjson.utils, undefined); 21 | }); 22 | 23 | describe('custom mockdata', function() { 24 | it('should merge custom mockdata into the default mockdata and make it available in the template', function() { 25 | var customMockdata = { 26 | animal: 'fox' 27 | }; 28 | var template = '{{animal}} {{firstName}}'; 29 | var output = dummyjson.parse(template, {mockdata: customMockdata}); 30 | assert.equal(output, 'fox Ivan'); 31 | }); 32 | 33 | it('should allow the default mockdata to be overwritten if it shares the same name', function() { 34 | var customMockdata = { 35 | firstNames: ['Spongebob'] 36 | }; 37 | var template = '{{firstName}}'; 38 | var output = dummyjson.parse(template, {mockdata: customMockdata}); 39 | assert.equal(output, 'Spongebob'); 40 | }); 41 | 42 | it('should not mutate the custom mockdata object', function() { 43 | var customMockdata = { 44 | animal: 'fox' 45 | }; 46 | var template = '{{animal}}'; 47 | dummyjson.parse(template, {mockdata: customMockdata}); 48 | // Check that default mockdata has not been added to the custom mockdata 49 | assert.equal(customMockdata.firstName, undefined); 50 | }); 51 | 52 | it('should not mutate the default mockdata object', function() { 53 | var customMockdata = { 54 | animal: 'fox' 55 | }; 56 | var template = '{{animal}}'; 57 | dummyjson.parse(template, {mockdata: customMockdata}); 58 | assert.equal(dummyjson.mockdata.animal, undefined); 59 | }); 60 | }); 61 | 62 | describe('custom helpers', function() { 63 | it('should merge custom helpers into the default helpers and make them available in the template', function() { 64 | var customHelpers = { 65 | direction: function() { 66 | return 'left'; 67 | } 68 | }; 69 | var template = '{{direction}} {{firstName}}'; 70 | var output = dummyjson.parse(template, {helpers: customHelpers}); 71 | assert.equal(output, 'left Ivan'); 72 | }); 73 | 74 | it('should allow the default helpers to be overwritten if they share the same name', function() { 75 | var customHelpers = { 76 | direction: function() { 77 | return 'left'; 78 | } 79 | }; 80 | var template = '{{direction}} {{firstName}}'; 81 | var output = dummyjson.parse(template, {helpers: customHelpers}); 82 | assert.equal(output, 'left Ivan'); 83 | }); 84 | 85 | it('should not mutate the custom helpers object', function() { 86 | var customHelpers = { 87 | direction: function() { 88 | return 'left'; 89 | } 90 | }; 91 | var template = '{{direction}}'; 92 | dummyjson.parse(template, {helpers: customHelpers}); 93 | // Check that a default helper has not been added to the custom helpers 94 | assert.equal(customHelpers.firstName, undefined); 95 | }); 96 | 97 | it('should not mutate the default helpers object', function() { 98 | var customHelpers = { 99 | direction: function() { 100 | return 'left'; 101 | } 102 | }; 103 | var template = '{{direction}}'; 104 | dummyjson.parse(template, {helpers: customHelpers}); 105 | assert.equal(dummyjson.helpers.direction, undefined); 106 | }); 107 | }); 108 | 109 | describe('custom partials', function() { 110 | it('should make custom partials available in the template', function() { 111 | var customPartials = { 112 | fullname: '{{firstName}} {{lastName}}' 113 | }; 114 | var template = '{{>fullname}}'; 115 | var output = dummyjson.parse(template, {partials: customPartials}); 116 | assert.equal(output, 'Ivan Sprowl'); 117 | }); 118 | }); 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /lib/mockdata.js: -------------------------------------------------------------------------------- 1 | var titles = [ 2 | 'Mr', 'Mrs', 'Dr', 'Prof', 'Lord', 'Lady', 'Sir', 'Madam' 3 | ]; 4 | 5 | var firstNames = [ 6 | 'Leanne', 'Edward', 'Haydee', 'Lyle', 'Shea', 'Curtis', 'Roselyn', 'Marcus', 'Lyn', 'Lloyd', 7 | 'Isabelle', 'Francis', 'Olivia', 'Roman', 'Myong', 'Jamie', 'Alexis', 'Vernon', 'Chloe', 'Max', 8 | 'Kirstie', 'Tyler', 'Katelin', 'Alejandro', 'Hannah', 'Gavin', 'Lynetta', 'Russell', 'Neida', 9 | 'Kurt', 'Dannielle', 'Aiden', 'Janett', 'Vaughn', 'Michelle', 'Brian', 'Maisha', 'Theo', 'Emma', 10 | 'Cedric', 'Jocelyn', 'Darrell', 'Grace', 'Ivan', 'Rikki', 'Erik', 'Madeleine', 'Rufus', 11 | 'Florance', 'Raymond', 'Jenette', 'Danny', 'Kathy', 'Michael', 'Layla', 'Rolf', 'Selma', 'Anton', 12 | 'Rosie', 'Craig', 'Victoria', 'Andy', 'Lorelei', 'Drew', 'Yuri', 'Miles', 'Raisa', 'Rico', 13 | 'Rosanne', 'Cory', 'Dori', 'Travis', 'Joslyn', 'Austin', 'Haley', 'Ian', 'Liza', 'Rickey', 14 | 'Susana', 'Stephen', 'Richelle', 'Lance', 'Jetta', 'Heath', 'Juliana', 'Rene', 'Madelyn', 'Stan', 15 | 'Eleanore', 'Jason', 'Alexa', 'Adam', 'Jenna', 'Warren', 'Cecilia', 'Benito', 'Elaine', 'Mitch', 16 | 'Raylene', 'Cyrus' 17 | ]; 18 | 19 | var lastNames = [ 20 | 'Flinn', 'Bryd', 'Milligan', 'Keesee', 'Mercer', 'Chapman', 'Zobel', 'Carter', 'Pettey', 21 | 'Starck', 'Raymond', 'Pullman', 'Drolet', 'Higgins', 'Matzen', 'Tindel', 'Winter', 'Charley', 22 | 'Schaefer', 'Hancock', 'Dampier', 'Garling', 'Verde', 'Lenihan', 'Rhymer', 'Pleiman', 'Dunham', 23 | 'Seabury', 'Goudy', 'Latshaw', 'Whitson', 'Cumbie', 'Webster', 'Bourquin', 'Young', 'Rikard', 24 | 'Brier', 'Luck', 'Porras', 'Gilmore', 'Turner', 'Sprowl', 'Rohloff', 'Magby', 'Wallis', 'Mullens', 25 | 'Correa', 'Murphy', 'Connor', 'Gamble', 'Castleman', 'Pace', 'Durrett', 'Bourne', 'Hottle', 26 | 'Oldman', 'Paquette', 'Stine', 'Muldoon', 'Smit', 'Finn', 'Kilmer', 'Sager', 'White', 'Friedrich', 27 | 'Fennell', 'Miers', 'Carroll', 'Freeman', 'Hollis', 'Neal', 'Remus', 'Pickering', 'Woodrum', 28 | 'Bradbury', 'Caffey', 'Tuck', 'Jensen', 'Shelly', 'Hyder', 'Krumm', 'Hundt', 'Seal', 'Pendergast', 29 | 'Kelsey', 'Milling', 'Karst', 'Helland', 'Risley', 'Grieve', 'Paschall', 'Coolidge', 'Furlough', 30 | 'Brandt', 'Cadena', 'Rebelo', 'Leath', 'Backer', 'Bickers', 'Cappel' 31 | ]; 32 | 33 | var companies = [ 34 | 'Unilogic', 'Solexis', 'Dalserve', 'Terrasys', 'Pancast', 'Tomiatech', 'Kancom', 'Iridimax', 35 | 'Proline', 'Qualcore', 'Thermatek', 'VTGrafix', 'Sunopia', 'WestGate', 'Chromaton', 'Tecomix', 36 | 'Galcom', 'Zatheon', 'OmniTouch', 'Hivemind', 'MultiServ', 'Citisys', 'Polygan', 'Dynaroc', 37 | 'Storex', 'Britech', 'Thermolock', 'Cryptonica', 'LoopSys', 'ForeTrust', 'TrueXT', 'LexiconLabs', 38 | 'Bellgate', 'Dynalab', 'Logico', 'Terralabs', 'CoreMax', 'Polycore', 'Infracom', 'Coolinga', 39 | 'MultiLingua', 'Conixco', 'QuadNet', 'FortyFour', 'TurboSystems', 'Optiplex', 'Nitrocam', 40 | 'CoreXTS', 'PeerSys', 'FastMart', 'Westercom', 'Templatek', 'Cirpria', 'FastFreight', 'Baramax', 41 | 'Superwire', 'Celmax', 'Connic', 'Forecore', 'SmartSystems', 'Ulogica', 'Seelogic', 'DynaAir', 42 | 'OpenServ', 'Maxcast', 'SixtySix', 'Protheon', 'SkyCenta', 'Eluxa', 'GrafixMedia', 'VenStrategy', 43 | 'Keycast', 'Opticast', 'Cameratek', 'CorpTek', 'Sealine', 'Playtech', 'Anaplex', 'Hypervision', 44 | 'Xenosys', 'Hassifix', 'Infratouch', 'Airconix', 'StrategyLine', 'Helixicon', 'MediaDime', 45 | 'NitroSystems', 'Viewtopia', 'Cryosoft', 'DuoServe', 'Acousticom', 'Freecast', 'CoreRobotics', 46 | 'Quadtek', 'Haltheon', 'TrioSys', 'Amsquare', 'Sophis', 'Keysoft', 'Creatonix' 47 | ]; 48 | 49 | var tlds = [ 50 | 'com', 'org', 'net', 'info', 'edu', 'gov', 'co', 'biz', 'name', 'me', 'mobi', 'club', 'xyz', 'eu' 51 | ]; 52 | 53 | var streets = [ 54 | 'Warner Street', 'Ceder Avenue', 'Glendale Road', 'Chester Square', 'Beechmont Parkway', 55 | 'Carter Street', 'Hinton Road', 'Pitman Street', 'Winston Road', 'Cottontail Road', 56 | 'Buckley Street', 'Concord Avenue', 'Clemont Street', 'Sleepy Lane', 'Bushey Crescent', 57 | 'Randolph Street', 'Radcliffe Road', 'Canal Street', 'Ridgewood Drive', 'Highland Drive', 58 | 'Orchard Road', 'Foster Walk', 'Walford Way', 'Harrington Crescent', 'Emmet Road', 59 | 'Berkeley Street', 'Clarendon Street', 'Sherman Road', 'Mount Street', 'Hunter Street', 60 | 'Pearl Street', 'Barret Street', 'Taylor Street', 'Shaftsbury Avenue', 'Paxton Street', 61 | 'Park Avenue', 'Seaside Drive', 'Tavistock Place', 'Prospect Place', 'Harvard Avenue', 62 | 'Elton Way', 'Green Street', 'Appleton Street', 'Banner Street', 'Piermont Drive', 'Brook Street', 63 | 'Main Street', 'Fairmont Avenue', 'Arlington Road', 'Rutherford Street', 'Windsor Avenue', 64 | 'Maple Street', 'Wandle Street', 'Grosvenor Square', 'Hunt Street', 'Haredale Road', 65 | 'Glenn Drive', 'Mulholland Drive', 'Baker Street', 'Fuller Road', 'Coleman Avenue', 'Wall Street', 66 | 'Robinson Street', 'Blakeley Street', 'Alexander Avenue', 'Gartland Street', 'Wooster Road', 67 | 'Brentwood Drive', 'Colwood Place', 'Rivington Street', 'Bramble Lane', 'Hartswood Road', 68 | 'Albion Place', 'Waverton Street', 'Sawmill Lane', 'Templeton Parkway', 'Hill Street', 69 | 'Marsham Street', 'Stockton Lane', 'Lake Drive', 'Elm Street', 'Winchester Drive', 70 | 'Crockett Street', 'High Street', 'Longford Crescent', 'Moreland Street', 'Sterling Street', 71 | 'Golden Lane', 'Mercer Street', 'Dunstable Street', 'Chestnut Walk', 'Rutland Drive', 72 | 'Buckfield Lane', 'Pembrooke Street', 'Tower Lane', 'Willow Avenue', 'Faraday Street', 73 | 'Springfield Street', 'Crawford Street', 'Hudson Street' 74 | ]; 75 | 76 | var cities = [ 77 | 'Beaverton', 'Stanford', 'Baltimore', 'Newcastle', 'Halifax', 'Rockhampton', 'Coventry', 78 | 'Medford', 'Boulder', 'Dover', 'Waterbury', 'Christchurch', 'Manchester', 'Perth', 'Norwich', 79 | 'Redmond', 'Plymouth', 'Tacoma', 'Newport', 'Bradford', 'Aspen', 'Wellington', 'Oakland', 80 | 'Norfolk', 'Durham', 'Portsmouth', 'Detroit', 'Portland', 'Northampton', 'Dayton', 'Charleston', 81 | 'Irvine', 'Dallas', 'Albany', 'Petersburg', 'Melbourne', 'Southampton', 'Stafford', 'Bridgeport', 82 | 'Fairfield', 'Dundee', 'Spokane', 'Oakleigh', 'Bristol', 'Sacramento', 'Sheffield', 'Lewisburg', 83 | 'Miami', 'Brisbane', 'Denver', 'Kingston', 'Burwood', 'Rochester', 'Fresno', 'Cardiff', 84 | 'Auckland', 'Sudbury', 'Hastings', 'Reno', 'Hillboro', 'Palmerston', 'Oxford', 'Hobart', 85 | 'Atlanta', 'Wilmington', 'Vancouver', 'Youngstown', 'Hartford', 'London', 'Danbury', 'Birmingham', 86 | 'Columbia', 'Dublin', 'Chicago', 'Toronto', 'Orlando', 'Toledo', 'Pheonix', 'Bakersfield', 87 | 'Nottingham', 'Newark', 'Fargo', 'Walkerville', 'Exeter', 'Woodville', 'Greenville', 'Frankston', 88 | 'Bangor', 'Seattle', 'Canterbury', 'Colchester', 'Boston', 'York', 'Cambridge', 'Brighton', 89 | 'Lancaster', 'Adelaide', 'Cleveland', 'Telford', 'Richmond' 90 | ]; 91 | 92 | var countries = [ 93 | 'Andorra', 'United Arab Emirates', 'Afghanistan', 'Antigua and Barbuda', 'Anguilla', 'Albania', 94 | 'Armenia', 'Angola', 'Antarctica', 'Argentina', 'American Samoa', 'Austria', 'Australia', 'Aruba', 95 | 'Åland Islands', 'Azerbaijan', 'Bosnia and Herzegovina', 'Barbados', 'Bangladesh', 'Belgium', 96 | 'Burkina Faso', 'Bulgaria', 'Bahrain', 'Burundi', 'Benin', 'Saint Barthélemy', 'Bermuda', 97 | 'Brunei Darussalam', 'Bolivia, Plurinational State of', 'Bonaire, Sint Eustatius and Saba', 98 | 'Brazil', 'Bahamas', 'Bhutan', 'Bouvet Island', 'Botswana', 'Belarus', 'Belize', 'Canada', 99 | 'Cocos (Keeling) Islands', 'Congo, the Democratic Republic of the', 'Central African Republic', 100 | 'Congo', 'Switzerland', 'Côte d\'Ivoire', 'Cook Islands', 'Chile', 'Cameroon', 'China', 101 | 'Colombia', 'Costa Rica', 'Cuba', 'Cabo Verde', 'Curaçao', 'Christmas Island', 'Cyprus', 102 | 'Czechia', 'Germany', 'Djibouti', 'Denmark', 'Dominica', 'Dominican Republic', 'Algeria', 103 | 'Ecuador', 'Estonia', 'Egypt', 'Western Sahara', 'Eritrea', 'Spain', 'Ethiopia', 'Finland', 104 | 'Fiji', 'Falkland Islands (Malvinas)', 'Micronesia, Federated States of', 'Faroe Islands', 105 | 'France', 'Gabon', 'United Kingdom of Great Britain and Northern Ireland', 'Grenada', 'Georgia', 106 | 'French Guiana', 'Guernsey', 'Ghana', 'Gibraltar', 'Greenland', 'Gambia', 'Guinea', 'Guadeloupe', 107 | 'Equatorial Guinea', 'Greece', 'South Georgia and the South Sandwich Islands', 'Guatemala', 108 | 'Guam', 'Guinea-Bissau', 'Guyana', 'Hong Kong', 'Heard Island and McDonald Islands', 'Honduras', 109 | 'Croatia', 'Haiti', 'Hungary', 'Indonesia', 'Ireland', 'Israel', 'Isle of Man', 'India', 110 | 'British Indian Ocean Territory', 'Iraq', 'Iran, Islamic Republic of', 'Iceland', 'Italy', 111 | 'Jersey', 'Jamaica', 'Jordan', 'Japan', 'Kenya', 'Kyrgyzstan', 'Cambodia', 'Kiribati', 'Comoros', 112 | 'Saint Kitts and Nevis', 'Korea, Democratic People\'s Republic of', 'Korea, Republic of', 113 | 'Kuwait', 'Cayman Islands', 'Kazakhstan', 'Lao People\'s Democratic Republic', 'Lebanon', 114 | 'Saint Lucia', 'Liechtenstein', 'Sri Lanka', 'Liberia', 'Lesotho', 'Lithuania', 'Luxembourg', 115 | 'Latvia', 'Libya', 'Morocco', 'Monaco', 'Moldova, Republic of', 'Montenegro', 116 | 'Saint Martin (French part)', 'Madagascar', 'Marshall Islands', 'North Macedonia', 'Mali', 117 | 'Myanmar', 'Mongolia', 'Macao', 'Northern Mariana Islands', 'Martinique', 'Mauritania', 118 | 'Montserrat', 'Malta', 'Mauritius', 'Maldives', 'Malawi', 'Mexico', 'Malaysia', 'Mozambique', 119 | 'Namibia', 'New Caledonia', 'Niger', 'Norfolk Island', 'Nigeria', 'Nicaragua', 'Netherlands', 120 | 'Norway', 'Nepal', 'Nauru', 'Niue', 'New Zealand', 'Oman', 'Panama', 'Peru', 'French Polynesia', 121 | 'Papua New Guinea', 'Philippines', 'Pakistan', 'Poland', 'Saint Pierre and Miquelon', 'Pitcairn', 122 | 'Puerto Rico', 'Palestine, State of', 'Portugal', 'Palau', 'Paraguay', 'Qatar', 'Réunion', 123 | 'Romania', 'Serbia', 'Russian Federation', 'Rwanda', 'Saudi Arabia', 'Solomon Islands', 124 | 'Seychelles', 'Sudan', 'Sweden', 'Singapore', 'Saint Helena, Ascension and Tristan da Cunha', 125 | 'Slovenia', 'Svalbard and Jan Mayen', 'Slovakia', 'Sierra Leone', 'San Marino', 'Senegal', 126 | 'Somalia', 'Suriname', 'South Sudan', 'Sao Tome and Principe', 'El Salvador', 127 | 'Sint Maarten (Dutch part)', 'Syrian Arab Republic', 'Eswatini', 'Turks and Caicos Islands', 128 | 'Chad', 'French Southern Territories', 'Togo', 'Thailand', 'Tajikistan', 'Tokelau', 'Timor-Leste', 129 | 'Turkmenistan', 'Tunisia', 'Tonga', 'Turkey', 'Trinidad and Tobago', 'Tuvalu', 130 | 'Taiwan, Province of China', 'Tanzania, United Republic of', 'Ukraine', 'Uganda', 131 | 'United States Minor Outlying Islands', 'United States of America', 'Uruguay', 'Uzbekistan', 132 | 'Holy See', 'Saint Vincent and the Grenadines', 'Venezuela, Bolivarian Republic of', 133 | 'Virgin Islands, British', 'Virgin Islands, U.S.', 'Viet Nam', 'Vanuatu', 'Wallis and Futuna', 134 | 'Samoa', 'Yemen', 'Mayotte', 'South Africa', 'Zambia', 'Zimbabwe' 135 | ]; 136 | 137 | var countryCodes = [ 138 | 'AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AO', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AW', 'AX', 'AZ', 139 | 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BL', 'BM', 'BN', 'BO', 'BQ', 'BR', 'BS', 140 | 'BT', 'BV', 'BW', 'BY', 'BZ', 'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 141 | 'CO', 'CR', 'CU', 'CV', 'CW', 'CX', 'CY', 'CZ', 'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ', 'EC', 'EE', 142 | 'EG', 'EH', 'ER', 'ES', 'ET', 'FI', 'FJ', 'FK', 'FM', 'FO', 'FR', 'GA', 'GB', 'GD', 'GE', 'GF', 143 | 'GG', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR', 'GS', 'GT', 'GU', 'GW', 'GY', 'HK', 'HM', 144 | 'HN', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IM', 'IN', 'IO', 'IQ', 'IR', 'IS', 'IT', 'JE', 'JM', 145 | 'JO', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR', 'KW', 'KY', 'KZ', 'LA', 'LB', 'LC', 146 | 'LI', 'LK', 'LR', 'LS', 'LT', 'LU', 'LV', 'LY', 'MA', 'MC', 'MD', 'ME', 'MF', 'MG', 'MH', 'MK', 147 | 'ML', 'MM', 'MN', 'MO', 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', 148 | 'NC', 'NE', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', 'NZ', 'OM', 'PA', 'PE', 'PF', 'PG', 149 | 'PH', 'PK', 'PL', 'PM', 'PN', 'PR', 'PS', 'PT', 'PW', 'PY', 'QA', 'RE', 'RO', 'RS', 'RU', 'RW', 150 | 'SA', 'SB', 'SC', 'SD', 'SE', 'SG', 'SH', 'SI', 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'SS', 151 | 'ST', 'SV', 'SX', 'SY', 'SZ', 'TC', 'TD', 'TF', 'TG', 'TH', 'TJ', 'TK', 'TL', 'TM', 'TN', 'TO', 152 | 'TR', 'TT', 'TV', 'TW', 'TZ', 'UA', 'UG', 'UM', 'US', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', 153 | 'VN', 'VU', 'WF', 'WS', 'YE', 'YT', 'ZA', 'ZM', 'ZW' 154 | ]; 155 | 156 | var colors = [ 157 | 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige', 'bisque', 'black', 158 | 'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 159 | 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan', 160 | 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 161 | 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 162 | 'darkslategray', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 163 | 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro', 'ghostwhite', 164 | 'gold', 'goldenrod', 'gray', 'green', 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo', 165 | 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 166 | 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen', 'lightpink', 167 | 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray', 'lightsteelblue', 'lightyellow', 168 | 'lime', 'limegreen', 'linen', 'magenta', 'maroon', 'mediumaquamarine', 'mediumblue', 169 | 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 170 | 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 171 | 'navajowhite', 'navy', 'oldlace', 'olive', 'olivedrab', 'orange', 'orangered', 'orchid', 172 | 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 173 | 'pink', 'plum', 'powderblue', 'purple', 'rebeccapurple', 'red', 'rosybrown', 'royalblue', 174 | 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 175 | 'slateblue', 'slategray', 'snow', 'springgreen', 'steelblue', 'tan', 'teal', 'thistle', 'tomato', 176 | 'turquoise', 'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen' 177 | ]; 178 | 179 | var lorem = [ 180 | 'lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', 'elit', 'morbi', 181 | 'vulputate', 'eros', 'ut', 'mi', 'laoreet', 'viverra', 'nunc', 'lacinia', 'non', 'condimentum', 182 | 'aenean', 'lacus', 'nisl', 'auctor', 'at', 'tortor', 'ac', 'fringilla', 'sodales', 'pretium', 183 | 'quis', 'iaculis', 'in', 'aliquam', 'ultrices', 'felis', 'accumsan', 'ornare', 'etiam', 184 | 'elementum', 'aliquet', 'finibus', 'maecenas', 'dignissim', 'vel', 'blandit', 'placerat', 'sed', 185 | 'tempor', 'ex', 'faucibus', 'velit', 'nam', 'erat', 'augue', 'quisque', 'nulla', 'maximus', 186 | 'vitae', 'e', 'lobortis', 'euismod', 'tristique', 'metus', 'vehicula', 'purus', 'diam', 'mollis', 187 | 'neque', 'eu', 'porttitor', 'mauris', 'a', 'risus', 'orci', 'tincidunt', 'scelerisque', 188 | 'vestibulum', 'dui', 'ante', 'posuere', 'turpis', 'enim', 'cras', 'massa', 'cursus', 'suscipit', 189 | 'tempus', 'facilisis', 'ultricies', 'i', 'eget', 'imperdiet', 'donec', 'arcu', 'ligula', 190 | 'sagittis', 'hendrerit', 'justo', 'pellentesque', 'mattis', 'lacinia', 'leo', 'est', 'magna', 191 | 'nibh', 'sem', 'natoque', 'consequat', 'proin', 'eti', 'commodo', 'rhoncus', 'dictum', 'id', 192 | 'pharetra', 'sapien', 'gravida', 'sollicitudin', 'curabitur', 'au', 'nisi', 'bibendum', 'lectus', 193 | 'et', 'pulvinar', 'it', 'o', 'cit', 'con', 'el', 'u', 'ali', 'dia', 'lum', 'tem', 'en' 194 | ]; 195 | 196 | module.exports = { 197 | titles: titles, 198 | firstNames: firstNames, 199 | lastNames: lastNames, 200 | companies: companies, 201 | tlds: tlds, 202 | streets: streets, 203 | cities: cities, 204 | countries: countries, 205 | countryCodes: countryCodes, 206 | colors: colors, 207 | lorem: lorem 208 | }; 209 | -------------------------------------------------------------------------------- /lib/helpers.js: -------------------------------------------------------------------------------- 1 | var os = require('os'); 2 | var Handlebars = require('handlebars'); 3 | var numbro = require('numbro'); 4 | var fecha = require('fecha'); 5 | var utils = require('./utils'); 6 | 7 | // Generating int and floats is very similar so both use this function 8 | function getNumber(type, min, max, format, options) { 9 | var ret; 10 | 11 | // Juggle the arguments if the user didn't supply a format string 12 | if (!options) { 13 | options = format; 14 | format = null; 15 | } 16 | 17 | if (type === 'int') { 18 | ret = utils.randomInt(min, max); 19 | } else if (type === 'float') { 20 | ret = utils.randomFloat(min, max); 21 | } 22 | 23 | if (typeof options.hash.round === 'number') { 24 | ret = Math.round(ret / options.hash.round) * options.hash.round; 25 | } 26 | 27 | if (format) { 28 | ret = numbro(ret).format(format); 29 | } 30 | 31 | return ret; 32 | } 33 | 34 | // Generating time and dates is very similar so both use this function 35 | function getDate(type, min, max, format, options) { 36 | var ret; 37 | 38 | // Juggle the arguments if the user didn't supply a format string 39 | if (!options) { 40 | options = format; 41 | format = null; 42 | } 43 | 44 | if (type === 'date') { 45 | min = Date.parse(min); 46 | max = Date.parse(max); 47 | } else if (type === 'time') { 48 | min = Date.parse('1970-01-01T' + min); 49 | max = Date.parse('1970-01-01T' + max); 50 | } 51 | 52 | ret = utils.randomDate(min, max); 53 | 54 | if (format === 'unix') { 55 | // We need to undo the timezone offset fix from utils.randomDate() 56 | ret = Math.floor((ret.getTime() - ret.getTimezoneOffset() * 60000) / 1000); 57 | } else if (format) { 58 | ret = fecha.format(ret, format); 59 | } else if (type === 'time') { 60 | // Time has a default format if one is not specified 61 | ret = fecha.format(ret, 'HH:mm'); 62 | } 63 | 64 | return ret; 65 | } 66 | 67 | function getFirstName(options) { 68 | // The value is cached so that other helpers can use it. 69 | // Each helper is allowed to use the cached value just once. 70 | var cache = options.data.root.__cache; 71 | var ret = utils.randomArrayItem(options.data.root.firstNames); 72 | cache.firstName = ret; 73 | cache.username_firstName = ret; 74 | cache.email_firstName = ret; 75 | return ret; 76 | } 77 | 78 | function getLastName(options) { 79 | // The value is cached so that other helpers can use it. 80 | // Each helper is allowed to use the cached value just once. 81 | var cache = options.data.root.__cache; 82 | var ret = utils.randomArrayItem(options.data.root.lastNames); 83 | cache.lastName = ret; 84 | cache.username_lastName = ret; 85 | cache.email_lastName = ret; 86 | return ret; 87 | } 88 | 89 | function getCompany(options) { 90 | // The value is cached so that other helpers can use it. 91 | // Each helper is allowed to use the cached value just once. 92 | var cache = options.data.root.__cache; 93 | var ret = utils.randomArrayItem(options.data.root.companies); 94 | cache.company = ret; 95 | cache.domain_company = ret; 96 | cache.email_company = ret; 97 | return ret; 98 | } 99 | 100 | function getTld(options) { 101 | // The value is cached so that other helpers can use it. 102 | // Each helper is allowed to use the cached value just once. 103 | var cache = options.data.root.__cache; 104 | var tld = utils.randomArrayItem(options.data.root.tlds); 105 | cache.tld = tld; 106 | cache.domain_tld = tld; 107 | cache.email_tld = tld; 108 | return tld; 109 | } 110 | 111 | var helpers = { 112 | repeat: function(min, max, options) { 113 | var ret = ''; 114 | var total = 0; 115 | var data; 116 | var i; 117 | var errorMsg = 'The repeat helper requires either a repeat count or min and max values, eg: {{#repeat 5}} or {{#repeat min=5 max=10}}'; 118 | 119 | if (arguments.length === 3) { 120 | // If given two numbers then pick a random one between the two 121 | total = utils.randomInt(min, max); 122 | } else if (arguments.length === 2) { 123 | // If given one number then just use it as a fixed repeat total 124 | options = max; 125 | total = min; 126 | } else if (arguments.length === 1) { 127 | options = min; 128 | if (typeof options.hash.min === 'number' && typeof options.hash.max === 'number') { 129 | total = utils.randomInt(options.hash.min, options.hash.max); 130 | } else { 131 | throw new Error(errorMsg); 132 | } 133 | } else { 134 | throw new Error(errorMsg); 135 | } 136 | 137 | // Create a shallow copy of data so we can add variables without modifying the original 138 | data = Handlebars.Utils.extend({}, options.data); 139 | 140 | for (i = 0; i < total; i++) { 141 | // Clear the linked values on each iteration so a new set of names/companies is generated 142 | options.data.root.__cache = {}; 143 | 144 | // You can access these in your template using @index, @total, @first, @last 145 | data.index = i; 146 | data.total = total; 147 | data.first = i === 0; 148 | data.last = i === total - 1; 149 | 150 | // By using 'this' as the context the repeat block will inherit the current scope 151 | ret = ret + options.fn(this, {data: data}); 152 | 153 | if (options.hash.comma !== false) { 154 | // Trim any whitespace left by handlebars and add a comma if it doesn't already exist, 155 | // also trim any trailing commas that might be at the end of the loop 156 | ret = ret.trimRight(); 157 | if (i < total - 1 && ret.charAt(ret.length - 1) !== ',') { 158 | ret += ','; 159 | } else if (i === total - 1 && ret.charAt(ret.length - 1) === ',') { 160 | ret = ret.slice(0, -1); 161 | } 162 | ret += os.EOL; 163 | } 164 | } 165 | 166 | return ret; 167 | }, 168 | 169 | int: function(min, max, format, options) { 170 | if (arguments.length !== 3 && arguments.length !== 4) { 171 | throw new Error('The int helper requires two numeric params, eg: {{int 2 6}}'); 172 | } 173 | return getNumber('int', min, max, format, options); 174 | }, 175 | 176 | float: function(min, max, format, options) { 177 | if (arguments.length !== 3 && arguments.length !== 4) { 178 | throw new Error('The float helper requires two numeric params, eg: {{float 5 8}}'); 179 | } 180 | return getNumber('float', min, max, format, options); 181 | }, 182 | 183 | boolean: function() { 184 | return utils.randomBoolean().toString(); 185 | }, 186 | 187 | date: function(min, max, format, options) { 188 | if (arguments.length !== 3 && arguments.length !== 4) { 189 | throw new Error(`The date helper requires two string params, eg: {{date '2015-01-01' '2015-12-31'}}`); 190 | } 191 | return getDate('date', min, max, format, options); 192 | }, 193 | 194 | time: function(min, max, format, options) { 195 | if (arguments.length !== 3 && arguments.length !== 4) { 196 | throw new Error(`The time helper requires two string params, eg: {{time '09:30' '14:00'}}`); 197 | } 198 | return getDate('time', min, max, format, options); 199 | }, 200 | 201 | title: function(options) { 202 | return utils.randomArrayItem(options.data.root.titles); 203 | }, 204 | 205 | firstName: function(options) { 206 | // Try to use the cached values first, otherwise generate a new value 207 | var cache = options.data.root.__cache; 208 | var ret = cache.firstName || getFirstName(options); 209 | 210 | // The cached values are cleared so they can't be used again 211 | cache.firstName = null; 212 | return ret; 213 | }, 214 | 215 | lastName: function(options) { 216 | // Try to use the cached values first, otherwise generate a new value 217 | var cache = options.data.root.__cache; 218 | var ret = cache.lastName || getLastName(options); 219 | 220 | // The cached values are cleared so they can't be used again 221 | cache.lastName = null; 222 | return ret; 223 | }, 224 | 225 | username: function(options) { 226 | // Try to use the cached values first, otherwise generate a new value 227 | var cache = options.data.root.__cache; 228 | var first = cache.username_firstName || getFirstName(options); 229 | var last = cache.username_lastName || getLastName(options); 230 | 231 | // The cached values are cleared so they can't be used again 232 | cache.username_firstName = null; 233 | cache.username_lastName = null; 234 | 235 | function sanitize(str) { 236 | return str.toLowerCase().replace(/\s/g, ''); 237 | } 238 | 239 | return sanitize(first).substr(0, 1).toLowerCase() + 240 | sanitize(last).toLowerCase(); 241 | }, 242 | 243 | company: function(options) { 244 | // Try to use the cached values first, otherwise generate a new value 245 | var cache = options.data.root.__cache; 246 | var company = cache.company || getCompany(options); 247 | 248 | // The cached values are cleared so they can't be used again 249 | cache.company = null; 250 | return company; 251 | }, 252 | 253 | tld: function(options) { 254 | // Try to use the cached values first, otherwise generate a new value 255 | var cache = options.data.root.__cache; 256 | var tld = cache.tld || getTld(options); 257 | 258 | // The cached values are cleared so they can't be used again 259 | cache.tld = null; 260 | return tld; 261 | }, 262 | 263 | domain: function(options) { 264 | // Try to use the cached values first, otherwise generate a new value 265 | var cache = options.data.root.__cache; 266 | var company = cache.domain_company || getCompany(options); 267 | var tld = cache.domain_tld || getTld(options); 268 | 269 | // The cached values are cleared so they can't be used again 270 | cache.domain_company = null; 271 | cache.domain_tld = null; 272 | 273 | return company.toLowerCase() + '.' + tld; 274 | }, 275 | 276 | email: function(options) { 277 | // Try to use the cached values first, otherwise generate a new value 278 | var cache = options.data.root.__cache; 279 | var first = cache.email_firstName || getFirstName(options); 280 | var last = cache.email_lastName || getLastName(options); 281 | var company = cache.email_company || getCompany(options); 282 | var tld = cache.email_tld || getTld(options); 283 | 284 | // The cached values are cleared so they can't be used again 285 | cache.email_firstName = null; 286 | cache.email_lastName = null; 287 | cache.email_company = null; 288 | cache.email_tld = null; 289 | 290 | function sanitize(str) { 291 | return str.toLowerCase().replace(/\s/g, ''); 292 | } 293 | 294 | return sanitize(first) + '.' + sanitize(last) + 295 | '@' + sanitize(company) + '.' + tld; 296 | }, 297 | 298 | street: function(options) { 299 | return utils.randomArrayItem(options.data.root.streets); 300 | }, 301 | 302 | city: function(options) { 303 | return utils.randomArrayItem(options.data.root.cities); 304 | }, 305 | 306 | country: function(options) { 307 | var ret; 308 | var rootData = options.data.root; 309 | var cache = rootData.__cache; 310 | 311 | // Try to use the cached values first, otherwise generate a new value 312 | if (cache.country) { 313 | ret = cache.country; 314 | } else { 315 | var pos = utils.randomInt(0, rootData.countries.length - 1); 316 | ret = rootData.countries[pos]; 317 | cache.countryCode = rootData.countryCodes[pos]; 318 | } 319 | 320 | // The cached values are cleared so they can't be used again 321 | cache.country = null; 322 | return ret; 323 | }, 324 | 325 | countryCode: function(options) { 326 | var ret; 327 | var rootData = options.data.root; 328 | var cache = rootData.__cache; 329 | 330 | // Try to use the cached values first, otherwise generate a new value 331 | if (cache.countryCode) { 332 | ret = cache.countryCode; 333 | } else { 334 | var pos = utils.randomInt(0, rootData.countries.length - 1); 335 | ret = rootData.countryCodes[pos]; 336 | cache.country = rootData.countries[pos]; 337 | } 338 | 339 | // The cached values are cleared so they can't be used again 340 | cache.countryCode = null; 341 | return ret; 342 | }, 343 | 344 | zipcode: function() { 345 | return ('0' + utils.randomInt(1000, 99999).toString()).slice(-5); 346 | }, 347 | 348 | postcode: function() { 349 | return utils.randomChar() + utils.randomChar() + utils.randomInt(0, 9) + ' ' + 350 | utils.randomInt(0, 9) + utils.randomChar() + utils.randomChar(); 351 | }, 352 | 353 | lat: function(options) { 354 | return getNumber('float', -90, 90, '0.000000', options); 355 | }, 356 | 357 | long: function(options) { 358 | return getNumber('float', -180, 180, '0.000000', options); 359 | }, 360 | 361 | phone: function(format) { 362 | // Provide a default format if one is not given 363 | format = (typeof format === 'string') ? format : 'xxx-xxx-xxxx'; 364 | return format.replace(/x/g, function() { 365 | return utils.randomInt(0, 9); 366 | }); 367 | }, 368 | 369 | guid: function() { 370 | var ret = ''; 371 | var i = 0; 372 | var mask = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; 373 | var c, r, v; 374 | 375 | while (i++ < 36) { 376 | c = mask[i - 1]; 377 | r = utils.random() * 16 | 0; 378 | v = (c === 'x') ? r : (r & 0x3 | 0x8); 379 | ret += (c === '-' || c === '4') ? c : v.toString(16); 380 | } 381 | 382 | return ret; 383 | }, 384 | 385 | ipv4: function() { 386 | return utils.randomInt(1, 255) + '.' + utils.randomInt(0, 255) + '.' + 387 | utils.randomInt(0, 255) + '.' + utils.randomInt(0, 255); 388 | }, 389 | 390 | ipv6: function() { 391 | return utils.randomInt(1, 0xffff).toString(16) + ':' + 392 | utils.randomInt(0, 0xffff).toString(16) + ':' + 393 | utils.randomInt(0, 0xffff).toString(16) + ':' + 394 | utils.randomInt(0, 0xffff).toString(16) + ':' + 395 | utils.randomInt(0, 0xffff).toString(16) + ':' + 396 | utils.randomInt(0, 0xffff).toString(16) + ':' + 397 | utils.randomInt(0, 0xffff).toString(16) + ':' + 398 | utils.randomInt(0, 0xffff).toString(16); 399 | }, 400 | 401 | color: function(options) { 402 | return utils.randomArrayItem(options.data.root.colors); 403 | }, 404 | 405 | hexColor: function(options) { 406 | var r = utils.randomInt(0, 0xff); 407 | var g = utils.randomInt(0, 0xff); 408 | var b = utils.randomInt(0, 0xff); 409 | 410 | if (options.hash.websafe === true) { 411 | r = Math.round(r / 0x33) * 0x33; 412 | g = Math.round(g / 0x33) * 0x33; 413 | b = Math.round(b / 0x33) * 0x33; 414 | } 415 | 416 | // Ensure that single digit values are padded with leading zeros 417 | return (options.hash.withHash === false ? '' : '#') + 418 | ('0' + r.toString(16)).slice(-2) + 419 | ('0' + g.toString(16)).slice(-2) + 420 | ('0' + b.toString(16)).slice(-2); 421 | }, 422 | 423 | char: function(charset) { 424 | if (arguments.length !== 2 || typeof charset !== 'string') { 425 | throw new Error(`The char helper requires a string param, eg: {{char 'ABC'}}`); 426 | } 427 | return utils.randomChar(charset) 428 | }, 429 | 430 | lorem: function(totalWords, options) { 431 | var ret = ''; 432 | var i, word; 433 | var isNewSentence = true; 434 | var lastPunctuationIndex = 0; 435 | var errorMsg = 'The lorem helper requires either a word count or min and max values, eg: {{lorem 5}} or {{lorem min=5 max=10}}'; 436 | 437 | if (arguments.length === 1) { 438 | // Juggle the arguments as totalWords wasn't provided 439 | options = totalWords; 440 | 441 | if (options.hash.min || options.hash.max) { 442 | if (typeof options.hash.min === 'number' && typeof options.hash.max === 'number') { 443 | totalWords = utils.randomInt(options.hash.min, options.hash.max); 444 | } else { 445 | throw new Error(errorMsg); 446 | } 447 | } else { 448 | totalWords = 25; 449 | } 450 | } else if (arguments.length === 2) { 451 | if (options.hash.min || options.hash.max) { 452 | throw new Error(errorMsg); 453 | } 454 | } else { 455 | throw new Error(errorMsg); 456 | } 457 | 458 | for (i = 0; i < totalWords; i++) { 459 | word = utils.randomArrayItem(options.data.root.lorem); 460 | 461 | // If the last iteration triggered a new sentence then capitalize the first letter 462 | if (isNewSentence) { 463 | word = word.charAt(0).toUpperCase() + word.slice(1); 464 | isNewSentence = false; 465 | } 466 | 467 | // Only introduce new punctuation if we're more then 3 words away from the end, 468 | // and more than 3 words since the last punctuation, and a 1 in 3 chance. 469 | if (i < totalWords - 3 && i - lastPunctuationIndex > 3 && utils.random() < 0.3) { 470 | isNewSentence = utils.random() < 0.6; 471 | word = word + (isNewSentence ? '.' : ','); 472 | lastPunctuationIndex = i; 473 | } 474 | 475 | ret = ret + word + ' '; 476 | } 477 | 478 | // Add a period/full-stop at the very end 479 | ret = ret.trimRight() + '.'; 480 | return ret; 481 | }, 482 | 483 | random: function() { 484 | if (arguments.length < 2) { 485 | throw new Error(`The random helper requires at least one param, eg: {{random 'north' 'south'}}`); 486 | } 487 | var arr = Array.prototype.slice.call(arguments, 0, arguments.length - 1); 488 | return utils.randomArrayItem(arr); 489 | }, 490 | 491 | lowercase: function(value) { 492 | return value.toLowerCase(); 493 | }, 494 | 495 | uppercase: function(value) { 496 | return value.toUpperCase(); 497 | }, 498 | 499 | add: function(a, b) { 500 | if (arguments.length !== 3 || typeof a !== 'number' || typeof b !== 'number') { 501 | throw new Error('The add helper requires two number params, eg: {{add 2 4}}'); 502 | } 503 | return a + b; 504 | }, 505 | 506 | step: function(inc, options) { 507 | if (arguments.length !== 2 || options.data.index == null) { 508 | throw new Error('The step helper requires a numeric value and can only be used inside #repeat and #each blocks, eg: {{step 10}}'); 509 | } 510 | return options.data.index * inc; 511 | } 512 | }; 513 | 514 | module.exports = helpers; 515 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dummy JSON 2 | 3 | Dummy JSON is a Node utility that allows you to generate random JSON data using Handlebars templates. It comes with a built-in collection of Handlebars helpers that generate common data values, such as names, numbers, dates, and also allows you to write your own. 4 | 5 | * [Getting started](#getting-started) 6 | * [Built-in helpers](#built-in-helpers) 7 | * [Writing your own helpers](#writing-your-own-helpers) 8 | * [Replacing default mock data](#replacing-default-mock-data) 9 | * [Seeded random data](#seeded-random-data) 10 | * [Advanced usage](#advanced-usage) 11 | * [API](#api) 12 | 13 | ## Example 14 | 15 | Please view the following example on the [github page](https://github.com/webroo/dummy-json) if it's not formatted correctly. 16 | 17 | 18 | 19 |
Template stringOutput string
20 |
 21 | {
 22 |   "users": [
 23 |     {{#repeat 2}}
 24 |     {
 25 |       "id": {{@index}},
 26 |       "name": "{{firstName}} {{lastName}}",
 27 |       "work": "{{company}}",
 28 |       "email": "{{email}}",
 29 |       "dob": "{{date '1900' '2000' 'YYYY'}}",
 30 |       "address": "{{int 1 100}} {{street}}",
 31 |       "city": "{{city}}",
 32 |       "optedin": {{boolean}}
 33 |     }
 34 |     {{/repeat}}
 35 |   ],
 36 |   "images": [
 37 |     {{#repeat 3}}
 38 |     "img{{@index}}.png"
 39 |     {{/repeat}}
 40 |   ],
 41 |   "coordinates": {
 42 |     "x": {{float -50 50 '0.00'}},
 43 |     "y": {{float -25 25 '0.00'}}
 44 |   },
 45 |   "price": "${{int 0 99999 '0,0'}}"
 46 | }
 47 | 
48 |
49 |
 50 | {
 51 |   "users": [
 52 |     {
 53 |       "id": 0,
 54 |       "name": "Adam Carter",
 55 |       "work": "Unilogic",
 56 |       "email": "adam.carter@unilogic.com",
 57 |       "dob": "1978",
 58 |       "address": "83 Warner Street",
 59 |       "city": "Boston",
 60 |       "optedin": true
 61 |     },
 62 |     {
 63 |       "id": 1,
 64 |       "name": "Leanne Brier",
 65 |       "work": "Connic",
 66 |       "email": "leanne.brier@connic.org",
 67 |       "dob": "1987",
 68 |       "address": "9 Coleman Avenue",
 69 |       "city": "Toronto",
 70 |       "optedin": false
 71 |     }
 72 |   ],
 73 |   "images": [
 74 |     "img0.png",
 75 |     "img1.png",
 76 |     "img2.png"
 77 |   ],
 78 |   "coordinates": {
 79 |     "x": 35.12,
 80 |     "y": -21.49
 81 |   },
 82 |   "price": "$59,395"
 83 | }
 84 | 
85 |
86 | 87 | ## Getting started 88 | 89 | Install via npm: 90 | 91 | npm install dummy-json 92 | 93 | #### Generate a JSON string 94 | 95 | ```js 96 | import dummyjson from 'dummy-json'; 97 | 98 | const template = `{ 99 | "name": "{{firstName}}", 100 | "age": "{{int 18 65}}" 101 | }`; 102 | const result = dummyjson.parse(template); // Returns a string 103 | ``` 104 | 105 | #### Generate from a template file 106 | 107 | Instead of using template strings directly in your code you can create a template file and load it using Node's `fs` utility: 108 | 109 | ```js 110 | import fs from 'fs'; 111 | import dummyjson from 'dummy-json'; 112 | 113 | const template = fs.readFileSync('mytemplate.hbs', { encoding: 'utf8' }); 114 | const result = dummyjson.parse(template); 115 | ``` 116 | 117 | #### Converting the generated string to a JavaScript object 118 | 119 | If the generated output is a valid JSON string then it can be parsed into a JavaScript object: 120 | 121 | ```js 122 | const result = dummyjson.parse(template); 123 | const obj = JSON.parse(result); 124 | ``` 125 | 126 | #### Create a dummy API endpoint 127 | 128 | A common use of Dummy JSON is to create a mock API endpoint that returns random data. Here's a quick example using Express: 129 | 130 | ```js 131 | import fs from 'fs'; 132 | import express from 'express'; 133 | import dummyjson from 'dummy-json'; 134 | 135 | const template = fs.readFileSync('template.hbs', { encoding: 'utf8' }); 136 | const app = express(); 137 | 138 | app.get('/api/people', function(req, res) { 139 | res.set('Content-Type', 'application/json'); 140 | res.status(200).send(dummyjson.parse(template)); 141 | }); 142 | 143 | app.listen(3000); 144 | ``` 145 | 146 | #### Command line iterface 147 | 148 | If you install Dummy JSON globally with `npm install -g dummy-json` you can use it from the command line. Dummy JSON will write to stdout by default but you can redirect to a file like so: 149 | 150 | dummyjson template.hbs > output.json 151 | 152 | ## Built-in helpers 153 | 154 | Dummy JSON uses custom Handlebars helpers to generate the random data. Handlebars helpers are functions that are called whenever an expression is encountered in a template, such as `{{firstName}}`. You can learn how to write your own helpers in the section: [Writing your own helpers](#writing-your-own-helpers). 155 | 156 | ### Repeat 157 | 158 | `{{#repeat count [comma=true]}} ... {{/repeat}}` 159 | 160 | * `count: number` The number of times to repeat the content (required) 161 | * `comma?: boolean` Add or remove the separating comma between blocks of content (optional, default is true) 162 | 163 | `{{#repeat min=number max=number [comma=true]}} ... {{/repeat}}` 164 | 165 | * `min: number` Minimum range for the random repeat count (required) 166 | * `max: number` Maximum range for the random repeat count (required) 167 | * `comma?: boolean` Add or remove the separating comma between blocks of content (optional, default is true) 168 | 169 | There are two ways in which this helper can be used. Both repeat blocks of content, similar to Handlebars' built-in `each`, and can be used anywhere in your template, not just inside arrays. It automatically adds a comma between repeated blocks unless specified. 170 | 171 | The first way it can be used is to repeat the block a fixed a number of times: 172 | 173 | ```js 174 | // Repeat the block 3 times, automatically adding a comma between blocks 175 | "messages": [ 176 | {{#repeat 3}} 177 | "hello" 178 | {{/repeat}} 179 | ] 180 | 181 | // Output 182 | "messages": [ 183 | "hello", 184 | "hello", 185 | "hello" 186 | ] 187 | ``` 188 | 189 | The second way it can be used is to repeat the block a random number of times: 190 | 191 | ```js 192 | // Repeat the block a random number of times between 1 and 5 193 | "messages": [ 194 | {{#repeat min=1 max=5}} 195 | "hello" 196 | {{/repeat}} 197 | ]; 198 | 199 | // Output 200 | "messages": [ 201 | "hello", 202 | "hello" 203 | ]; 204 | ``` 205 | 206 | You can omit the comma by using `comma=false`, for example: 207 | 208 | ```js 209 | {{#repeat 3 comma=false}}hello{{/repeat}} // hellohellohello 210 | ``` 211 | 212 | You can get iteration position information inside the repeat block using the standard Handlebars variables `@index`, `@first`, `@last` and `@total`. Check out the helpers [Add](#add) and [Step](#step) to see how you can further modify the position values to create interesting indexes. 213 | 214 | ```js 215 | // Repeat the block 3 times using @index to modify the filename 216 | {{#repeat 3}} 217 | "img{{@index}}.png" 218 | {{/repeat}} 219 | 220 | // Output 221 | "img0.png", 222 | "img1.png", 223 | "img2.png" 224 | ``` 225 | 226 | ### Integer 227 | 228 | `{{int min max [format] [round=null]}}` 229 | 230 | * `min: number` Minimum value (required) 231 | * `max: number` Maximum value (required) 232 | * `format?: string` Formatting string (optional, default is null) 233 | * `round?: number` Rounds to the nearest multiple of the value (optional, default is no rounding) 234 | 235 | Generates a random integer from `min` (inclusive) up to and including `max` (inclusive). The optional `round` parameter will round the number to the nearest multiple of the given value. 236 | 237 | The output can be formatted using a numeric format string, provided by numbro. For a complete list of formatting options see [http://numbrojs.com/format.html](http://numbrojs.com/format.html). 238 | 239 | ```js 240 | // Generates a random integer between 0 and 100 241 | {{int 0 100}} // 43 242 | 243 | // Rounds the random integer to the nearest multiple of 5 244 | {{int 0 100 round=5}} // 65 245 | 246 | // Formats the random integer using numbro 247 | {{int 10000 50000 '0,0.00'}} // 23,462.00 248 | ``` 249 | 250 | ### Float 251 | 252 | `{{float min max [format] [round=null]}}` 253 | 254 | * `min: number` Minimum value (required) 255 | * `max: number` Maximum value (required) 256 | * `format?: string` Formatting string (optional, default is null) 257 | * `round?: number` Rounds to the nearest multiple of the value (optional, default is no rounding) 258 | 259 | Generates a random floating point number from `min` (inclusive) up to but excluding `max` (exclusive). The optional `round` parameter will round the number to the nearest multiple of the given value. 260 | 261 | The output can be formatted using a numeric format string, provided by numbro. For a complete list of formatting options see [http://numbrojs.com/format.html](http://numbrojs.com/format.html). 262 | 263 | ```js 264 | // Generates a random float between 0 and 1 265 | {{float 0 1}} // 0.4319351462490857 266 | 267 | // Rounds the random float to the nearest multiple of 0.1 268 | {{float 0 1 round=0.1}} // 0.4 269 | 270 | // Formats the random float using numbro 271 | {{float 10000 50000 '0,0.00'}} // 33,127.39 272 | ``` 273 | 274 | ### Date 275 | 276 | `{{date min max [format]}}` 277 | 278 | * `min: number` Minimum value (required) 279 | * `max: number` Maximum value (required) 280 | * `format?: string` Formatting string (optional, default is null) 281 | 282 | Generates a random date between the two values. Both `min` and `max` can be represented by any string that the [Date.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) method accepts. 283 | 284 | By default the output uses [Date.toString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toString). Alternatively the output can be formatted using a format string provided by fecha. For a complete list of formatting options see [https://github.com/taylorhakes/fecha](https://github.com/taylorhakes/fecha) 285 | 286 | ```js 287 | // Generate a random date between midnight 2010-01-01 and midnight 2015-01-01 288 | {{date '2010' '2015'}} // Thu Jan 26 2012 03:04:15 GMT+0000 (GMT) 289 | 290 | // Generate a random date between more specific values 291 | {{date '2015-06-01' '2015-06-30'}} // Mon Jun 22 2015 01:02:36 GMT+0100 (BST) 292 | 293 | // Generate a random date between even more specific values (including time) 294 | {{date '2015-06-01T09:00' '2015-06-30T17:30'}} // Sun Jun 07 2015 23:55:16 GMT+0100 (BST) 295 | 296 | // Format the date using fecha 297 | {{date '2010' '2015' 'DD/MM/YYYY'}} // 16/06/2012 298 | 299 | // Format the date using a unix timestamp 300 | {{date '2010' '2015' 'unix'}} // 1340417344 301 | ``` 302 | 303 | ### Time 304 | 305 | `{{time min max [format]}}` 306 | 307 | * `min: number` Minimum value (required) 308 | * `max: number` Maximum value (required) 309 | * `format?: string` Formatting string (optional, default is null) 310 | 311 | This is a shorthand helper for generating the time portion of a date, without needing to put the full date into the min and max values. Both `min` and `max` can be represented by any string in the 24h format `HH:mm:ss`, for example `17:48:34`, or if you want to ignore seconds: `17:48` 312 | 313 | By default the output uses `HH:mm`. Alternatively the output can be formatted using a format string provided by fecha. For a complete list of formatting options see [https://github.com/taylorhakes/fecha](https://github.com/taylorhakes/fecha) 314 | 315 | ```js 316 | // Generate a random time 317 | {{time '09:00' '17:30'}} // 14:08 318 | 319 | // Format the time using fecha 320 | {{time '09:00' '17:30' 'h:mm a'}} // 2:08 pm 321 | ``` 322 | 323 | ### Random item 324 | 325 | `{{random ...items}}` 326 | 327 | * `items: string | number` One or more parameters from which to pick a random item (required) 328 | 329 | Picks a random item from the given parameters. This is a convenient way to create small, inline random lists of your own. For more lengthy lists or ones you wish to reuse see the section on [Helpers that pick a random item from an array](#helpers-that-pick-a-random-item-from-an-array). 330 | 331 | ```js 332 | // Randomly pick one of the provided strings 333 | {{random 'North' 'South' 'East' 'West'}} // South 334 | 335 | // You can also provide numbers 336 | {{random 50 100 150 200}} // 150 337 | ``` 338 | 339 | ### Boolean 340 | 341 | `{{boolean}}` 342 | 343 | Generates a random `true` or `false` value. 344 | 345 | ### Title 346 | 347 | `{{title}}` 348 | 349 | Generates a random title prefix, from a predefined list, such as "Mr", "Mrs", "Dr", etc. 350 | 351 | ### First name 352 | 353 | `{{firstName}}` 354 | 355 | Generates a random first name, from a predefined list. This helper is kept in sync with other name-related helpers, such as username and email - see the section on [Synchronized helpers](#a-note-on-synchronized-helpers) for more information. 356 | 357 | ### Last name 358 | 359 | `{{lastName}}` 360 | 361 | Generates a random last name, from a predefined list. This helper is kept in sync with other name-related helpers, such as username and email - see the section on [Synchronized helpers](#a-note-on-synchronized-helpers) for more information. 362 | 363 | ### Company 364 | 365 | `{{company}}` 366 | 367 | Generates a random company name, from a predefined list. This helper is kept in sync with the email and domain helpers, such as username and email - see the section on [Synchronized helpers](#a-note-on-synchronized-helpers) for more information. 368 | 369 | ### Domain 370 | 371 | `{{domain}}` 372 | 373 | Generates a random domain name in the format "domain.tld", from a predefined list. This helper is kept in sync with the company and email helpers - see the section on [Synchronized helpers](#a-note-on-synchronized-helpers) for more information. 374 | 375 | ### TLD 376 | 377 | `{{tld}}` 378 | 379 | Generates a random top-level domain name, from a predefined list. This helper is kept in sync with the email helper - see the section on [Synchronized helpers](#a-note-on-synchronized-helpers) for more information. 380 | 381 | ### Email 382 | 383 | `{{email}}` 384 | 385 | Generates a random email address. This helper is kept in sync with other name-related helpers, such as username and email - see the section on [Synchronized helpers](#a-note-on-synchronized-helpers) for more information. 386 | 387 | ### Street 388 | 389 | `{{street}}` 390 | 391 | Generates a random street name, from a predefined list. 392 | 393 | ### City 394 | 395 | `{{city}}` 396 | 397 | Generates a random city name, from a predefined list. 398 | 399 | ### Country 400 | 401 | `{{country}}` 402 | 403 | Generates a random country name, from a predefined list based on [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1). This helper is kept in sync with the country code helper - see the section on [Synchronized helpers](#a-note-on-synchronized-helpers) for more information. 404 | 405 | If you want to export the entire list then you can use the following snippet in your template: 406 | 407 | ```js 408 | "countries": [ 409 | {{#each countries}} 410 | "{{this}}" 411 | {{/each}} 412 | ] 413 | ``` 414 | 415 | ### Country code 416 | 417 | `{{countryCode}}` 418 | 419 | Generates a random 2-character country code, from a predefined list based on [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1). This helper is kept in sync with the country helper - see the section on [Synchronized helpers](#a-note-on-synchronized-helpers) for more information. 420 | 421 | If you want to export the entire list then you can use the following snippet in your template: 422 | 423 | ```js 424 | "countryCodes": [ 425 | {{#each countryCodes}} 426 | "{{this}}" 427 | {{/each}} 428 | ] 429 | ``` 430 | ### Zipcode 431 | 432 | `{{zipcode}}` 433 | 434 | Generates a random US-style 5 digit zipcode. 435 | 436 | ### Postcode 437 | 438 | `{{postcode}}` 439 | 440 | Generates a random UK-style postcode in the format `AB9 9CD`. 441 | 442 | ### Latitude 443 | 444 | `{{lat}}` 445 | 446 | Generates a random latitude from -90 to +90, to 6 decimal places (roughly 10cm of precision). 447 | 448 | ### Longitude 449 | 450 | `{{long}}` 451 | 452 | Generates a random longitude from -180 to +180, to 6 decimal places (roughly 10cm of precision). 453 | 454 | ### Phone number 455 | 456 | `{{phone [format]}}` 457 | 458 | * `format?: string` Formatting string (optional, default is `xxx-xxx-xxxx`) 459 | 460 | Generates a random phone number in the format `xxx-xxx-xxxx`, for example "123-456-7890". To use a different format pass a string to the `format` parameter containing a series of lowercase "x" characters for each random integer. 461 | 462 | ```js 463 | // Generate a random phone number in the default format 464 | {{phone}} // 445-295-1044 465 | 466 | // Generate a random phone number with a custom format 467 | {{phone "+64 (x) xxx xxxx"}} // +64 (5) 883 4711 468 | ``` 469 | 470 | ### Color 471 | 472 | `{{color}}` 473 | 474 | Generates a random CSS color name from a predefined list, such as "forestgreen", "black", etc. 475 | 476 | ### Hex color 477 | 478 | `{{hexColor [websafe=false] [withHash=true]}}` 479 | 480 | * `websafe?: boolean` Generates a websafe color if true (optional, default is false) 481 | * `withHash?: boolean` Whether the color has a leading hash symbol (optional, default is true) 482 | 483 | Generates a random hexadecimal color value with an optional leading hash symbol. 484 | 485 | ```js 486 | // Generates a hex color with a leading hash symbol 487 | {{hexColor}} // #34D92C 488 | 489 | // Generates a websafe hex color 490 | {{hexColor websafe=true}} // #33CC99 491 | 492 | // Generates a hex color without a leading hash symbol 493 | {{hexColor withHash=false}} // 34D92C 494 | ``` 495 | 496 | ### GUID 497 | 498 | `{{guid}}` 499 | 500 | Generates a random 32 digit GUID. 501 | 502 | ### IPv4 address 503 | 504 | `{{ipv4}}` 505 | 506 | Generates a random IPv4 address. 507 | 508 | ### IPv6 address 509 | 510 | `{{ipv6}}` 511 | 512 | Generates a random IPv6 address. 513 | 514 | ### Character 515 | 516 | `{{char charset}}` 517 | 518 | * `charset: string` String of characters to pick from (required) 519 | 520 | Picks a single character from the given character set. 521 | 522 | ```js 523 | // Randomly pick one of the characters, in this case to generate a grade 524 | {{char "ABCDEF"}} // B 525 | 526 | // Generates a random currency symbol 527 | {{char "$€£¥"}} // € 528 | ``` 529 | 530 | ### Lorem ipsum 531 | 532 | `{{lorem [wordCount]}}` 533 | 534 | * `wordcount?: number` Number of words to generate (optional, default is 25) 535 | 536 | `{{lorem min=number max=number}}` 537 | 538 | * `min: number` Minimum range for the random word count (required) 539 | * `max: number` Maximum range for the random word count (required) 540 | 541 | There are two ways this helper can be used. Both generate random sentences of lorem ipsum text with occasional punctuation (commas and periods/full-stops). 542 | 543 | ```js 544 | // Generates 25 words of lorem ipsum 545 | {{lorem}} // Amet vel aliquam laoreet accumsan adipiscing velit... (etc) 546 | 547 | // Generates 5 words of lorem ipsum 548 | {{lorem 5}} // Orci nisi laoreet maximus dictum. 549 | 550 | // Generates a random number of words between 10 and 20 551 | {{lorem min=10 max=20}} // Felis velit aliquam aliquet sollicitudin consequat... (etc) 552 | ``` 553 | 554 | ### Lowercase 555 | 556 | `{{lowercase (helper)}}` 557 | 558 | * `helper` Any other helper that returns a string (required) 559 | 560 | Converts the output of any string-based helper to lowercase. This uses the Handlebars' [subexpression syntax](https://handlebarsjs.com/guide/expressions.html#subexpressions). 561 | 562 | ```js 563 | // Change firstName to lowercase 564 | {{lowercase (firstName)}} // ivan 565 | 566 | // Change city to lowercase 567 | {{lowercase (city)}} // boston 568 | ``` 569 | 570 | ### Uppercase 571 | 572 | `{{uppercase (helper)}}` 573 | 574 | * `helper` Any other helper that returns a string (required) 575 | 576 | Converts the output of any string-based helper to uppercase. This uses the Handlebars' [subexpression syntax](https://handlebarsjs.com/guide/expressions.html#subexpressions). 577 | 578 | ```js 579 | // Change firstName to uppercase 580 | {{uppercase (firstName)}} // IVAN 581 | 582 | // Change city to uppercase 583 | {{uppercase (city)}} // BOSTON 584 | ``` 585 | 586 | ### Add 587 | 588 | `{{add number1 number2}}` 589 | 590 | * `number1: number` First number to add (required) 591 | * `number2: number` Second number to add (required) 592 | 593 | Adds the two numbers together. This can be useful in creating 1-based indexes inside repeat blocks using the `@index` variable (which is normally zero-based). 594 | 595 | ```js 596 | // The built-in @index variable is zero-based, but we can add 1 to it 597 | "images": [ 598 | {{#repeat 3}} 599 | "image{{add @index 1}}.jpg" 600 | {{/repeat}} 601 | ] 602 | 603 | // Output 604 | "images": [ 605 | "image1.jpg", 606 | "image2.jpg", 607 | "image3.jpg" 608 | ] 609 | ``` 610 | 611 | ### Step 612 | 613 | `{{step increment}}` 614 | 615 | * `increment: number` How much to increment the generated index on each iteration (required) 616 | 617 | Creates a numeric step inside a repeat block that is a multiple of the index. (Note: this uses the `@index` variable internally and so can only be used inside `{{#repeat}}` and `{{#each}}` blocks). 618 | 619 | ```js 620 | // Increment the image index by 10 each time 621 | "images": [ 622 | {{#repeat 3}} 623 | "image{{step 10}}.jpg" 624 | {{/repeat}} 625 | ] 626 | 627 | // Output 628 | "images": [ 629 | "image0.jpg", 630 | "image10.jpg", 631 | "image20.jpg" 632 | ] 633 | ``` 634 | 635 | You can use this in conjunction with the [Add](#add) helper and [subexpression syntax](https://handlebarsjs.com/guide/expressions.html#subexpressions) to create indexes that start at higher values: 636 | 637 | ```js 638 | // Increment the image index by 10 each time, starting at 1000 639 | "images": [ 640 | {{#repeat 3}} 641 | "image{{add (step 10) 1000}}.jpg" 642 | {{/repeat}} 643 | ] 644 | 645 | // Output 646 | "images": [ 647 | "image1000.jpg", 648 | "image1010.jpg", 649 | "image1020.jpg" 650 | ] 651 | ``` 652 | 653 | ## A note on synchronized helpers 654 | 655 | Several helpers, such as name and email, are linked together in order to synchronize their values. This helps gives the random data some continuity. Synchronization happens automatically and doesn't require any additional work, for example: 656 | 657 | ```js 658 | "firstName": "{{firstName}}", // Michael 659 | "lastName": "{{lastName}}", // Turner 660 | "email": "{{email}}" // michael.turner@unilogic.com 661 | ``` 662 | 663 | The helpers can be placed in any order and will still synchronize: 664 | 665 | ```js 666 | "email": "{{email}}" // michael.turner@unilogic.com 667 | "firstName": "{{firstName}}", // Michael 668 | "lastName": "{{lastName}}", // Turner 669 | ``` 670 | 671 | The synchronization is reset whenever the same helper is used twice, or in each iteration of a repeat block: 672 | 673 | ```js 674 | "email": "{{email}}" // michael.turner@unilogic.com 675 | "firstName": "{{firstName}}", // Michael 676 | "lastName": "{{lastName}}", // Turner 677 | "email": "{{email}}" // grace.chapman@westgate.org (NOTE: sync is reset here) 678 | "firstName": "{{firstName}}", // Grace 679 | "lastName": "{{lastName}}", // Chapman 680 | ``` 681 | 682 | The following helpers synchronize their values: 683 | 684 | * `firstName`, `lastName`, `username`, `company`, `domain`, `tld`, `email` 685 | * `country`, `countryCode` 686 | 687 | ## Writing your own helpers 688 | 689 | To write your own helpers you need to create an object map of helper methods and pass it to the `options` param of `dummyjson.parse()`, for example: 690 | 691 | ```js 692 | const myHelpers = { 693 | direction() { 694 | // We use dummyjson's random() method to ensure the seeded random number generator is used 695 | return dummyjson.utils.random() > 0.5 ? 'left' : 'right'; 696 | } 697 | }; 698 | const template = '{{direction}}'; 699 | const result = dummyjson.parse(template, { helpers: myHelpers }); // Returns "left" 700 | ``` 701 | 702 | Your own helpers will be mixed with the built-in helpers, allowing you to use both in your template. 703 | 704 | The helpers use the same syntax as regular Handlebars helpers, but instead of registering them with `Handlebars.registerHelper()` you pass them to `dummyjson.parse()`. For more information on writing helpers see the [Handlebars documentation](https://handlebarsjs.com/guide/block-helpers.html). 705 | 706 | Note: when generating data using random numbers you should always use the functions from the `dummyjson.utils` module. This ensures you're using the seeded random number generator and means your results will be repeatable if you ever decide to use a seed. See the section on [Seeded random data](#seeded-random-data) for more information, and the [API](#api) for complete list of methods available in `dummyjson.utils`. 707 | 708 | ### Helpers that pick a random item from an array 709 | 710 | One of the most common types of helper is one that picks a random item from an array. If you are only dealing with a small number of items and don't need to reuse the helper consider using the inline [Random item](#random-item) helper, like so: 711 | 712 | ```js 713 | {{random 'North' 'South' 'East' 'West'}} // Will randomly pick on of the four values 714 | ``` 715 | 716 | However if you are dealing with a large array or don't want to repeat it throughout the template then it's better to write your own helper. You can use the following example as a basis for you own: 717 | 718 | ```js 719 | const myHelpers = { 720 | direction() { 721 | // Use randomArrayItem() to ensure the seeded random number generator is used 722 | return dummyjson.utils.randomArrayItem(['North', 'South', 'East', 'West']); 723 | } 724 | }; 725 | const template = '{{direction}}'; 726 | const result = dummyjson.parse(template, { helpers: myHelpers }); // Returns "East" 727 | ``` 728 | 729 | ## Replacing default mock data 730 | 731 | If you want to use a different set of names, addresses, colors and so on, then you can override the built-in data using the `mockdata` option. Here's is an example with a complete list of built-in arrays you can replace: 732 | 733 | ```js 734 | const myMockdata = { 735 | // These are all the possible arrays you can replace: 736 | firstNames: ['Bob', 'Jane', 'Carl', 'Joan'], 737 | lastNames: ['Smith', 'Jones', 'Wallis', 'Gilmore'], 738 | companies: ['Apple', 'Microsoft'], 739 | tlds: ['etc'], 740 | streets: ['etc'], 741 | cities: ['etc'], 742 | countries: ['etc'], 743 | countryCodes: ['etc'], 744 | colors: ['etc'], 745 | }; 746 | const result = dummyjson.parse(template, { mockdata: myMockdata }); 747 | ``` 748 | 749 | ## Seeded random data 750 | 751 | By default dummyjson generates different results every time it's run. If you need reproducible dummy data then you can set a global seed for the pseudo random number generator: 752 | 753 | ```js 754 | // Set the global seed, can be any string value 755 | dummyjson.seed = 'helloworld'; 756 | 757 | // Every subsequent call to parse() will now generate the same output 758 | const result = dummyjson.parse(string); 759 | ``` 760 | 761 | Alternatively you can set a one-time seed for a specific `dummyjson.parse()` call: 762 | 763 | ```js 764 | const result = dummyjson.parse(string, { seed: 'abc123' }); 765 | ``` 766 | 767 | Note: a one-time seed will not overwrite the global `dummyjson.seed`, meaning subsequent calls to `parse()` without a seed will use the original `dummyjson.seed` value. 768 | 769 | ### Ensuring your own helpers use the seed 770 | 771 | To ensure your own helpers generate reproducible data you must use the functions from the `dummyjson.utils` module whenever you want a random value. See the [API](#api) section for a complete list of functions. 772 | 773 | ```js 774 | const myHelpers = { 775 | temperature() { 776 | // Using randomInt() guarantees reproducible results when using a seed 777 | return dummyjson.utils.randomInt(0, 100) + '°C'; 778 | } 779 | }; 780 | ``` 781 | 782 | ## Advanced usage 783 | 784 | ### Replacing built-in helpers 785 | 786 | You can replace any of the built-in helpers by simply creating your own with the same name: 787 | 788 | ```js 789 | const myHelpers = { 790 | // This version of {{postcode}} will now be used instead of the built-in one 791 | postcode() { 792 | return 'helloworld'; 793 | } 794 | }; 795 | const result = dummyjson.parse(template, { helpers: myHelpers }); 796 | ``` 797 | 798 | Note: If you replace any of the synchronized helpers then you will lose the syncing functionality. If you want to use a different set of names, addresses, etc, then use the technique described in [Replacing default mock data](#replacing-default-mock-data). 799 | 800 | ### Using other static data 801 | 802 | The `mockdata` option can also be used to insert static data for use in your template: 803 | 804 | ```js 805 | const myMockdata = { 806 | copyright: 'Copyright Myself 2015' 807 | }; 808 | const template = '{{copyright}}'; 809 | const result = dummyjson.parse(template, { mockdata: myMockdata }); // Returns "Copyright Myself 2015" 810 | ``` 811 | 812 | Or arrays which you can loop over using Handlebar's [each helper](https://handlebarsjs.com/guide/builtin-helpers.html#each): 813 | 814 | ```js 815 | const myMockdata = { 816 | animals: ['fox', 'badger', 'crow'] 817 | }; 818 | const template = '{{#each animals}}{{this}},{{/each}}'; 819 | const result = dummyjson.parse(template, { mockdata: myMockdata }); // Returns "fox,badger,crow," 820 | ``` 821 | 822 | ### Using built-in helpers inside your own helpers 823 | 824 | All the built-in helpers are available for you to use from within your own helpers. They can be found on the `dummyjson.helpers` object. Here's an example of using two existing helpers to make a new one: 825 | 826 | ```js 827 | const myHelpers = { 828 | fullname(options) { 829 | // You must always forward the Handlerbars `options` argument to built-in helpers 830 | return dummyjson.helpers.firstName(options) + ' ' + dummyjson.helpers.lastName(options); 831 | } 832 | }; 833 | const template = '{{fullname}}'; 834 | const result = dummyjson.parse(template, { helpers: myHelpers }); // Returns "Ivan Young" 835 | ``` 836 | 837 | As mentioned in the comment above you must always forward the `options` argument to built-in helpers. The `options` argument is automatically given to all helpers by Handlebars, and is always passed as the last argument. See the [Handlebars documentation](https://handlebarsjs.com/guide/block-helpers.html) for more information. 838 | 839 | ### Using your own partials 840 | 841 | You can use [Handlebars partials](https://handlebarsjs.com/guide/partials.html) to encapsulate content into a reusable blocks. Partials are passed via the `options` param of `dummyjson.parse()`. 842 | 843 | ```js 844 | const myPartials = { 845 | user: `{ 846 | "id": {{@index}}, 847 | "firstName": "{{firstName}}", 848 | "lastName": "{{lastName}}", 849 | "email": "{{email}}" 850 | }` 851 | }; 852 | 853 | const template = `{ 854 | "users": [ 855 | {{#repeat 3}} 856 | {{> user}} 857 | {{/repeat}} 858 | ] 859 | }`; 860 | 861 | const result = dummyjson.parse(template, { partials: myPartials }); 862 | ``` 863 | 864 | ## API 865 | 866 | #### Parse 867 | 868 | `dummyjson.parse(template: string, options: ParseOptions): string` 869 | 870 | * `template: string` Handlebars template string 871 | * `options: ParseOptions` Options object containing any of the following properties: 872 | * `helpers: {}` Object map of custom helper functions (see [Writing your own helpers](#writing-your-own-helpers)) 873 | * `mockdata: {}` Object map of mockdata (see [Replacing default mock data](#replacing-default-mock-data)) 874 | * `partials: {}` Object map of custom partials (see [Using your own partials](#using-your-own-partials)) 875 | * `seed: string` Seed for the random number generator (see [Seeded random data](#seeded-random-data)) 876 | 877 | #### Utils 878 | 879 | `dummyjson.utils` 880 | 881 | * `.random()` Returns a random float in the range [0, 1). Use this instead of `Math.random()` 882 | * `.randomInt(min: number, max: number): number` Returns a random integer in the range [min, max] 883 | * `.randomFloat(min: number, max: number): number` Returns a random float in the range [min, max) 884 | * `.randomBoolean(): boolean` Returns a random boolean 885 | * `.randomDate(min: number, max: number): Date` Returns a random date between min/max millisecond values 886 | * `.randomArrayItem(array: any[]): any` Returns a random item from the given `array` 887 | * `.randomChar(charset: string): string` Returns a random char from the given `charset` 888 | -------------------------------------------------------------------------------- /test/helpers.spec.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var dummyjson = require('../index'); 3 | 4 | // Asserts the basic string output of dummyjson.parse() (by default dummyjson.parse() returns 5 | // a string). If the template or expected params are arrays then the contents will be joined with 6 | // line-breaks to create a multiline string. 7 | function assertStringOutput(template, expected, options) { 8 | template = template.join ? template.join('\n') : template; 9 | expected = expected.join ? expected.join('\n') : expected; 10 | var actual = dummyjson.parse(template, options); 11 | assert.deepEqual(actual, expected); 12 | } 13 | 14 | // Asserts the output of dummyjson.parse() after it's been put through JSON.parse(). 15 | // If the template param is an array then the contents will be joined with line-breaks to create 16 | // a multiline string. 17 | function assertJSONOutput(template, expected, options) { 18 | template = template.join ? template.join('\n') : template; 19 | var actual = dummyjson.parse(template, options); 20 | assert.deepEqual(JSON.parse(actual), expected); 21 | } 22 | 23 | // We set a seed so that all the tests have expected outcomes 24 | dummyjson.seed = 'helloworld'; 25 | 26 | // The helpers rely on Handlebars for their execution, so rather than test the helper functions 27 | // directly we test them via the parse() method. This makes these more like integration tests. 28 | describe('helpers', function() { 29 | describe('repeat', function() { 30 | it('should throw an error if not given a number', function() { 31 | var template = '{{#repeat}}{{/repeat}}'; 32 | assert.throws( 33 | function() { 34 | dummyjson.parse(template); 35 | }, 36 | Error 37 | ); 38 | }); 39 | 40 | it('should repeat the contents the set number of times and add commas between blocks', function() { 41 | var template = [ 42 | '[', 43 | ' {{#repeat 2}}', 44 | ' "hello"', 45 | ' {{/repeat}}', 46 | ']' 47 | ]; 48 | var expected = [ 49 | 'hello', 50 | 'hello' 51 | ]; 52 | assertJSONOutput(template, expected); 53 | }); 54 | 55 | it('should remove the last comma if the block already has a trailing comma', function() { 56 | var template = [ 57 | '[', 58 | ' {{#repeat 2}}', 59 | ' "hello",', 60 | ' {{/repeat}}', 61 | ']' 62 | ]; 63 | var expected = [ 64 | 'hello', 65 | 'hello' 66 | ]; 67 | assertJSONOutput(template, expected); 68 | }); 69 | 70 | it('should repeat the contents a random number of times between the two range values', function() { 71 | var template = [ 72 | '[', 73 | ' {{#repeat 1 5}}', 74 | ' "hello"', 75 | ' {{/repeat}}', 76 | ']' 77 | ]; 78 | var expected = [ 79 | 'hello', 80 | 'hello', 81 | 'hello' 82 | ]; 83 | assertJSONOutput(template, expected); 84 | }); 85 | 86 | it('should repeat the contents a random number of times between the min and max values', function() { 87 | var template = [ 88 | '[', 89 | ' {{#repeat min=4 max=6}}', 90 | ' "hello"', 91 | ' {{/repeat}}', 92 | ']' 93 | ]; 94 | var expected = [ 95 | 'hello', 96 | 'hello', 97 | 'hello', 98 | 'hello', 99 | 'hello' 100 | ]; 101 | assertJSONOutput(template, expected); 102 | }); 103 | 104 | it('should not repeat the contents a random number of times if the min and max values are negative', function() { 105 | var template = [ 106 | '[', 107 | ' {{#repeat min=-4 max=-6}}', 108 | ' "hello"', 109 | ' {{/repeat}}', 110 | ']' 111 | ]; 112 | var expected = []; 113 | assertJSONOutput(template, expected); 114 | }); 115 | 116 | it('should repeat the contents a random number of times using the range values instead of min & max', function() { 117 | // This is to preserve backwards compatibility for anyone using the original format: {{#repeat 2 4}} 118 | var template = [ 119 | '[', 120 | ' {{#repeat 1 3 min=4 max=6}}', 121 | ' "hello"', 122 | ' {{/repeat}}', 123 | ']' 124 | ]; 125 | var expected = [ 126 | 'hello', 127 | 'hello', 128 | ]; 129 | assertJSONOutput(template, expected); 130 | }); 131 | 132 | it('should throw an error if only given a min value', function() { 133 | var template = '{{#repeat min=5}}{{/repeat}}'; 134 | assert.throws( 135 | function() { 136 | dummyjson.parse(template); 137 | }, 138 | Error 139 | ); 140 | }); 141 | 142 | it('should throw an error if only given a max value', function() { 143 | var template = '{{#repeat max=5}}{{/repeat}}'; 144 | assert.throws( 145 | function() { 146 | dummyjson.parse(template); 147 | }, 148 | Error 149 | ); 150 | }); 151 | 152 | it('should make positional values available inside the repeat block', function() { 153 | var template = [ 154 | '[', 155 | ' {{#repeat 2}}', 156 | ' {', 157 | ' "index": {{@index}},', 158 | ' "total": {{@total}},', 159 | ' "first": {{@first}},', 160 | ' "last": {{@last}}', 161 | ' }', 162 | ' {{/repeat}}', 163 | ']' 164 | ]; 165 | var expected = [ 166 | { 167 | 'index': 0, 168 | 'total': 2, 169 | 'first': true, 170 | 'last': false 171 | }, 172 | { 173 | 'index': 1, 174 | 'total': 2, 175 | 'first': false, 176 | 'last': true 177 | } 178 | ]; 179 | assertJSONOutput(template, expected); 180 | }); 181 | 182 | it('should ensure the positional values inside the repeat block work with random repeat ranges', function() { 183 | var template = [ 184 | '[', 185 | ' {{#repeat min=2 max=5}}', 186 | ' {', 187 | ' "index": {{@index}},', 188 | ' "total": {{@total}},', 189 | ' "first": {{@first}},', 190 | ' "last": {{@last}}', 191 | ' }', 192 | ' {{/repeat}}', 193 | ']' 194 | ]; 195 | var expected = [ 196 | { 197 | 'index': 0, 198 | 'total': 3, 199 | 'first': true, 200 | 'last': false 201 | }, 202 | { 203 | 'index': 1, 204 | 'total': 3, 205 | 'first': false, 206 | 'last': false 207 | }, 208 | { 209 | 'index': 2, 210 | 'total': 3, 211 | 'first': false, 212 | 'last': true 213 | } 214 | ]; 215 | assertJSONOutput(template, expected); 216 | }); 217 | 218 | it('should allow nested repeat blocks', function() { 219 | var template = [ 220 | '[', 221 | ' {{#repeat 2}}', 222 | ' {', 223 | ' "items": [', 224 | ' {{#repeat 2}}', 225 | ' "hello"', 226 | ' {{/repeat}}', 227 | ' ]', 228 | ' }', 229 | ' {{/repeat}}', 230 | ']' 231 | ]; 232 | var expected = [ 233 | { 234 | items: [ 235 | 'hello', 236 | 'hello' 237 | ] 238 | }, 239 | { 240 | items: [ 241 | 'hello', 242 | 'hello' 243 | ] 244 | } 245 | ]; 246 | assertJSONOutput(template, expected); 247 | }); 248 | 249 | it('should not repeat the contents if given zero as a number', function() { 250 | var template = [ 251 | '[', 252 | ' {{#repeat 0}}', 253 | ' "hello"', 254 | ' {{/repeat}}', 255 | ']' 256 | ]; 257 | var expected = []; 258 | assertJSONOutput(template, expected); 259 | }); 260 | 261 | it('should not repeat the contents if given a negative number', function() { 262 | var template = [ 263 | '[', 264 | ' {{#repeat -1}}', 265 | ' "hello"', 266 | ' {{/repeat}}', 267 | ']' 268 | ]; 269 | var expected = []; 270 | assertJSONOutput(template, expected); 271 | }); 272 | }); 273 | 274 | describe('int', function() { 275 | it('should throw an error if not given range values', function() { 276 | var template = '{{int}}'; 277 | assert.throws( 278 | function() { 279 | dummyjson.parse(template); 280 | }, 281 | Error 282 | ); 283 | }); 284 | 285 | it('should throw an error if only given 1 range value', function() { 286 | var template = '{{int 100}}'; 287 | assert.throws( 288 | function() { 289 | dummyjson.parse(template); 290 | }, 291 | Error 292 | ); 293 | }); 294 | 295 | it('should return different integers when used repeatedly', function() { 296 | var template = '{{int 0 100}}, {{int 0 100}}, {{int 0 100}}'; 297 | var expected = '43, 41, 9'; 298 | assertStringOutput(template, expected); 299 | }); 300 | 301 | it('should return integers rounded to the nearest multiple of the given value', function() { 302 | var template = '{{int 0 100 round=5}}, {{int 0 100 round=10}}, {{int 0 100 round=20}}'; 303 | var expected = '45, 40, 0'; 304 | assertStringOutput(template, expected); 305 | }); 306 | 307 | it('should be inclusive of min and max values when rounding to nearest multiples', function() { 308 | var template = '{{int 5 10 round=5}}, {{int 5 10 round=5}}'; 309 | var expected = '10, 5'; 310 | assertStringOutput(template, expected, { seed: '123' }); 311 | }); 312 | 313 | it('should return an integer formatted using the format string', function() { 314 | var template = '{{int 1000 2000 "0,0.00"}}'; 315 | var expected = '1,434.00'; 316 | assertStringOutput(template, expected); 317 | }); 318 | 319 | it('should return an integer formatted using the format string in single quotes', function() { 320 | var template = '{{int 1000 2000 \'0,0.00\'}}'; 321 | var expected = '1,434.00'; 322 | assertStringOutput(template, expected); 323 | }); 324 | }); 325 | 326 | describe('float', function() { 327 | it('should throw an error if not given range values', function() { 328 | var template = '{{float}}'; 329 | assert.throws( 330 | function() { 331 | dummyjson.parse(template); 332 | }, 333 | Error 334 | ); 335 | }); 336 | 337 | it('should throw an error if only given 1 range value', function() { 338 | var template = '{{float 100}}'; 339 | assert.throws( 340 | function() { 341 | dummyjson.parse(template); 342 | }, 343 | Error 344 | ); 345 | }); 346 | 347 | it('should return different floats when used repeatedly', function() { 348 | var template = '{{float 0 1}}, {{float 0 1}}, {{float 0 1}}'; 349 | var expected = '0.4339404073209236, 0.4147789564935982, 0.0952744222319714'; 350 | assertStringOutput(template, expected); 351 | }); 352 | 353 | it('should return floats rounded to the nearest multiple of the given value', function() { 354 | var template = '{{float 0 1 round=0.1}}, {{float 0 1 round=0.1}}, {{float 0 1 round=0.1}}'; 355 | var expected = '0.4, 0.4, 0.1'; 356 | assertStringOutput(template, expected); 357 | }); 358 | 359 | it('should return a float formatted using the format string', function() { 360 | var template = '{{float 1000 2000 "0,0.00"}}'; 361 | var expected = '1,433.94'; 362 | assertStringOutput(template, expected); 363 | }); 364 | }); 365 | 366 | describe('boolean', function() { 367 | it('should return different booleans when used repeatedly', function() { 368 | var template = '{{boolean}}, {{boolean}}, {{boolean}}, {{boolean}}'; 369 | var expected = 'true, true, true, false'; 370 | assertStringOutput(template, expected); 371 | }); 372 | }); 373 | 374 | describe('date', function() { 375 | it('should throw an error if not given range values', function() { 376 | var template = '{{date}}'; 377 | assert.throws( 378 | function() { 379 | dummyjson.parse(template); 380 | }, 381 | Error 382 | ); 383 | }); 384 | 385 | it('should throw an error if only given 1 range value', function() { 386 | var template = '{{date "2005"}}'; 387 | assert.throws( 388 | function() { 389 | dummyjson.parse(template); 390 | }, 391 | Error 392 | ); 393 | }); 394 | 395 | it('should return different dates when used repeatedly', function() { 396 | // Unix time is the only real safe way of testing this, as the default Date.toString() 397 | // will localise the value to the machine's timezone 398 | var template = '{{date "2005" "2015" "unix"}}, {{date "2005" "2015" "unix"}}, {{date "2005" "2015" "unix"}}'; 399 | var expected = '1241460031, 1235413965, 1134599805'; 400 | assertStringOutput(template, expected); 401 | }); 402 | 403 | it('should return a date formatted using the format string', function() { 404 | var template = '{{date "2005" "2015" "YYYY"}}'; 405 | var expected = '2009'; 406 | assertStringOutput(template, expected); 407 | }); 408 | 409 | // This is designed to test date boundaries on machines in different timezones. 410 | // It can only be run in Pacific Standard Time (PST) so is skipped by default. 411 | it.skip('should return the exact date with PST timezone offset', () => { 412 | var template = dummyjson.parse('{{date "1980-01-01" "1980-01-01"}}'); 413 | var expected = 'Tue Jan 01 1980 00:00:00 GMT-0800 (PST)'; 414 | assertStringOutput(template, expected); 415 | 416 | // Also check fecha returns the correct date (by supplying a date formatting string) 417 | var template = dummyjson.parse('{{date "1980-01-01" "1980-01-01" "YYYY-MM-DD"}}'); 418 | var expected = '1980-01-01'; 419 | assertStringOutput(template, expected); 420 | }); 421 | }); 422 | 423 | describe('time', function() { 424 | it('should throw an error if not given range values', function() { 425 | var template = '{{time}}'; 426 | assert.throws( 427 | function() { 428 | dummyjson.parse(template); 429 | }, 430 | Error 431 | ); 432 | }); 433 | 434 | it('should throw an error if only given 1 range value', function() { 435 | var template = '{{time "09:00"}}'; 436 | assert.throws( 437 | function() { 438 | dummyjson.parse(template); 439 | }, 440 | Error 441 | ); 442 | }); 443 | 444 | it('should return different time when used repeatedly', function() { 445 | var template = '{{time "09:00Z" "17:00Z"}}, {{time "09:00Z" "17:00Z"}}, {{time "09:00Z" "17:00Z"}}'; 446 | var expected = '12:28, 12:19, 09:45'; 447 | assertStringOutput(template, expected); 448 | }); 449 | 450 | it('should return a time formatted using the format string', function() { 451 | var template = '{{time "09:00Z" "17:00Z" "HH:mm:ss"}}'; 452 | var expected = '12:28:17'; 453 | assertStringOutput(template, expected); 454 | }); 455 | 456 | it('should return a time formatted in unix time', function() { 457 | var template = '{{time "09:00Z" "17:00Z" "unix"}}'; 458 | var expected = '44897'; 459 | assertStringOutput(template, expected); 460 | }); 461 | }); 462 | 463 | describe('title', function() { 464 | it('should return different titles when used repeatedly', function() { 465 | var template = '{{title}}, {{title}}, {{title}}, {{title}}'; 466 | var expected = 'Prof, Prof, Mr, Lord'; 467 | assertStringOutput(template, expected); 468 | }); 469 | }); 470 | 471 | describe('firstName', function() { 472 | it('should return different first names when used repeatedly', function() { 473 | var template = '{{firstName}}, {{firstName}}, {{firstName}}'; 474 | var expected = 'Ivan, Darrell, Lloyd'; 475 | assertStringOutput(template, expected); 476 | }); 477 | }); 478 | 479 | describe('lastName', function() { 480 | it('should return different last names when used repeatedly', function() { 481 | var template = '{{lastName}}, {{lastName}}, {{lastName}}'; 482 | var expected = 'Magby, Sprowl, Starck'; 483 | assertStringOutput(template, expected); 484 | }); 485 | }); 486 | 487 | describe('username', function() { 488 | it('should return different usernames when used repeatedly', function() { 489 | var template = '{{username}}, {{username}}, {{username}}'; 490 | var expected = 'isprowl, lsmit, twinter'; 491 | assertStringOutput(template, expected); 492 | }); 493 | 494 | it('should remove whitespace characters from names', function() { 495 | var options = { 496 | mockdata: { 497 | firstNames: [' Philippe'], 498 | lastNames: [' Le Gerrec'], 499 | }, 500 | }; 501 | var template = '{{username}}'; 502 | var expected = 'plegerrec'; 503 | assertStringOutput(template, expected, options); 504 | }); 505 | }); 506 | 507 | describe('company', function() { 508 | it('should return different companies when used repeatedly', function() { 509 | var template = '{{company}}, {{company}}, {{company}}'; 510 | var expected = 'FortyFour, Conixco, Qualcore'; 511 | assertStringOutput(template, expected); 512 | }); 513 | }); 514 | 515 | describe('tld', function() { 516 | it('should return different tlds when used repeatedly', function() { 517 | var template = '{{tld}}, {{tld}}, {{tld}}'; 518 | var expected = 'co, gov, org'; 519 | assertStringOutput(template, expected); 520 | }); 521 | }); 522 | 523 | describe('domain', function() { 524 | it('should return different domains when used repeatedly', function() { 525 | var template = '{{domain}}, {{domain}}, {{domain}}'; 526 | var expected = 'fortyfour.gov, qualcore.name, polycore.net'; 527 | assertStringOutput(template, expected); 528 | }); 529 | }); 530 | 531 | describe('email', function() { 532 | it('should return different emails when used repeatedly', function() { 533 | var template = '{{email}}, {{email}}, {{email}}'; 534 | var expected = 'ivan.sprowl@qualcore.name, theo.winter@citisys.biz, florance.krumm@dalserve.biz'; 535 | assertStringOutput(template, expected); 536 | }); 537 | 538 | it('should remove whitespace characters from names and companies', function() { 539 | var options = { 540 | mockdata: { 541 | firstNames: ['Philippe '], 542 | lastNames: [' Le Gerrec'], 543 | companies: ['Uni logic'], 544 | }, 545 | }; 546 | var template = '{{email}}'; 547 | var expected = 'philippe.legerrec@unilogic.name'; 548 | assertStringOutput(template, expected, options); 549 | }); 550 | }); 551 | 552 | describe('street', function() { 553 | it('should return different streets when used repeatedly', function() { 554 | var template = '{{street}}, {{street}}, {{street}}'; 555 | var expected = 'Banner Street, Green Street, Cottontail Road'; 556 | assertStringOutput(template, expected); 557 | }); 558 | }); 559 | 560 | describe('city', function() { 561 | it('should return different cities when used repeatedly', function() { 562 | var template = '{{city}}, {{city}}, {{city}}'; 563 | var expected = 'Bristol, Spokane, Dover'; 564 | assertStringOutput(template, expected); 565 | }); 566 | }); 567 | 568 | describe('country', function() { 569 | it('should return different countries when used repeatedly', function() { 570 | var template = '{{country}}, {{country}}, {{country}}'; 571 | var expected = 'Iceland, Isle of Man, Burundi'; 572 | assertStringOutput(template, expected); 573 | }); 574 | }); 575 | 576 | describe('countryCode', function() { 577 | it('should return different country codes when used repeatedly', function() { 578 | var template = '{{countryCode}}, {{countryCode}}, {{countryCode}}'; 579 | var expected = 'IS, IM, BI'; 580 | assertStringOutput(template, expected); 581 | }); 582 | }); 583 | 584 | describe('zipcode', function() { 585 | it('should return different zipcodes when used repeatedly', function() { 586 | var template = '{{zipcode}}, {{zipcode}}, {{zipcode}}'; 587 | var expected = '86930, 02430, 73050'; 588 | assertStringOutput(template, expected, { seed: 'xyz1234' }); 589 | }); 590 | }); 591 | 592 | describe('postcode', function() { 593 | it('should return different postcodes when used repeatedly', function() { 594 | var template = '{{postcode}}, {{postcode}}, {{postcode}}'; 595 | var expected = 'LK0 5JE, FN4 8AO, LT5 3VA'; 596 | assertStringOutput(template, expected); 597 | }); 598 | }); 599 | 600 | describe('lat', function() { 601 | it('should return different latitude values when used repeatedly', function() { 602 | var template = '{{lat}}, {{lat}}, {{lat}}, {{lat}}'; 603 | var expected = '-11.890727, -15.339788, -72.850604, 17.676792'; 604 | assertStringOutput(template, expected); 605 | }); 606 | }); 607 | 608 | describe('long', function() { 609 | it('should return different longitude values when used repeatedly', function() { 610 | var template = '{{long}}, {{long}}, {{long}}, {{long}}'; 611 | var expected = '-23.781453, -30.679576, -145.701208, 35.353584'; 612 | assertStringOutput(template, expected); 613 | }); 614 | }); 615 | 616 | describe('phone', function() { 617 | it('should return different phone numbers when used repeatedly', function() { 618 | var template = '{{phone}}, {{phone}}, {{phone}}'; 619 | var expected = '440-531-2548, 054-753-8054, 023-013-3024'; 620 | assertStringOutput(template, expected); 621 | }); 622 | 623 | it('should allow custom phone number format strings', function() { 624 | var template = '{{phone "+1 (xxx) xxx-xxxx"}}, {{phone "(x) 1xx 9xxx"}}'; 625 | var expected = '+1 (440) 531-2548, (0) 154 9753'; 626 | assertStringOutput(template, expected); 627 | }); 628 | }); 629 | 630 | describe('guid', function() { 631 | it('should return different guids when used repeatedly', function() { 632 | var template = '{{guid}}, {{guid}}'; 633 | var expected = '66195238-c087-46d0-a145-36504604fe17, 595be376-f0e9-49ed-adb2-a756f15c11ce'; 634 | assertStringOutput(template, expected); 635 | }); 636 | }); 637 | 638 | describe('ipv4', function() { 639 | it('should return different IPv4s when used repeatedly', function() { 640 | var template = '{{ipv4}}, {{ipv4}}, {{ipv4}}'; 641 | var expected = '111.106.24.153, 95.43.54.128, 124.206.5.138'; 642 | assertStringOutput(template, expected); 643 | }); 644 | }); 645 | 646 | describe('ipv6', function() { 647 | it('should return different IPv6s when used repeatedly', function() { 648 | var template = '{{ipv6}}, {{ipv6}}'; 649 | var expected = '6f17:6a2e:1863:9923:5f10:2b49:36fd:80dc, 7c7a:ceac:569:8a67:7442:c1e0:815e:6416'; 650 | assertStringOutput(template, expected); 651 | }); 652 | }); 653 | 654 | describe('color', function() { 655 | it('should return different colors when used repeatedly', function() { 656 | var template = '{{color}}, {{color}}, {{color}}'; 657 | var expected = 'lawngreen, khaki, cadetblue'; 658 | assertStringOutput(template, expected); 659 | }); 660 | }); 661 | 662 | describe('hexColor', function() { 663 | it('should return different hexColors when used repeatedly', function() { 664 | var template = '{{hexColor}}, {{hexColor}}, {{hexColor}}, {{hexColor}}'; 665 | var expected = '#6f6a18, #995f2b, #36807c, #ce058a'; 666 | assertStringOutput(template, expected); 667 | }); 668 | 669 | it('should return different websafe hexColors when used repeatedly', function() { 670 | var template = '{{hexColor websafe=true}}, {{hexColor websafe=true}}, {{hexColor websafe=true}}'; 671 | var expected = '#666600, #996633, #339966'; 672 | assertStringOutput(template, expected); 673 | }); 674 | 675 | it('should remove the hash symbol', function() { 676 | var template = '{{hexColor withHash=false}}, {{hexColor withHash=false}}, {{hexColor withHash=false}}'; 677 | var expected = '6f6a18, 995f2b, 36807c'; 678 | assertStringOutput(template, expected); 679 | }); 680 | }); 681 | 682 | describe('char', function() { 683 | it('should return one of the characters in the charset', function() { 684 | var template = '{{char "ABC"}}, {{char "$€£"}}, {{char "123"}}'; 685 | var expected = 'B, €, 1'; 686 | assertStringOutput(template, expected); 687 | }); 688 | 689 | it('should throw an error if not given a charset', function() { 690 | var template = '{{char}}'; 691 | assert.throws( 692 | function() { 693 | dummyjson.parse(template); 694 | }, 695 | Error 696 | ); 697 | }); 698 | 699 | it('should throw an error if given more than one charset', function() { 700 | var template = '{{char "ABC" "abc"}}'; 701 | assert.throws( 702 | function() { 703 | dummyjson.parse(template); 704 | }, 705 | Error 706 | ); 707 | }); 708 | 709 | it('should throw an error if given a number', function() { 710 | var template = '{{char 50}}'; 711 | assert.throws( 712 | function() { 713 | dummyjson.parse(template); 714 | }, 715 | Error 716 | ); 717 | }); 718 | }); 719 | 720 | describe('lorem', function() { 721 | it('should return 25 lorem ipsum words by default', function() { 722 | var template = '{{lorem}}'; 723 | var expected = 'Lobortis maximus mi enim velit. Eu mollis eti dolor tristique eu id. Lobortis mi elementum vel. Nam faucibus dolor felis adipiscing, au laoreet purus velit.'; 724 | assertStringOutput(template, expected); 725 | }); 726 | 727 | it('should return the specified number of lorem ipsum words', function() { 728 | var template = '{{lorem 10}}'; 729 | var expected = 'Lobortis maximus mi enim velit. Eu mollis eti dolor orci.'; 730 | assertStringOutput(template, expected); 731 | }); 732 | 733 | it('should return a random number of words between min and max', function() { 734 | var template = '{{lorem min=10 max=20}}'; 735 | var expected = 'Maximus mi enim velit auctor. Mollis eti dolor orci est augue dolor porttitor lobortis.'; 736 | assertStringOutput(template, expected); 737 | }); 738 | 739 | it('should throw an error if min and max are provided along with a word count', function() { 740 | var template = '{{lorem 5 min=10 max=20}}'; 741 | assert.throws( 742 | function() { 743 | dummyjson.parse(template); 744 | }, 745 | Error 746 | ); 747 | }); 748 | 749 | it('should throw an error if only min is provided', function() { 750 | var template = '{{lorem max=5}}'; 751 | assert.throws( 752 | function() { 753 | dummyjson.parse(template); 754 | }, 755 | Error 756 | ); 757 | }); 758 | 759 | it('should throw an error if only max is provided', function() { 760 | var template = '{{lorem min=5}}'; 761 | assert.throws( 762 | function() { 763 | dummyjson.parse(template); 764 | }, 765 | Error 766 | ); 767 | }); 768 | 769 | it('should throw an error if more than two numbers are provided', function() { 770 | var template = '{{lorem 5 6}}'; 771 | assert.throws( 772 | function() { 773 | dummyjson.parse(template); 774 | }, 775 | Error 776 | ); 777 | }); 778 | }); 779 | 780 | describe('linked helpers', function() { 781 | it('should link all values when first name is used first', function() { 782 | var template = [ 783 | '{', 784 | ' "firstName": "{{firstName}}",', 785 | ' "lastName": "{{lastName}}",', 786 | ' "username": "{{username}}",', 787 | ' "company": "{{company}}",', 788 | ' "tld": "{{tld}}",', 789 | ' "domain": "{{domain}}",', 790 | ' "email": "{{email}}"', 791 | '}' 792 | ]; 793 | var expected = { 794 | 'firstName': 'Ivan', 795 | 'lastName': 'Sprowl', 796 | 'username': 'isprowl', 797 | 'company': 'Qualcore', 798 | 'tld': 'name', 799 | 'domain': 'qualcore.name', 800 | 'email': 'ivan.sprowl@qualcore.name' 801 | }; 802 | assertJSONOutput(template, expected); 803 | }); 804 | 805 | it('should link all values when last name is used first', function() { 806 | var template = [ 807 | '{', 808 | ' "lastName": "{{lastName}}",', 809 | ' "firstName": "{{firstName}}",', 810 | ' "username": "{{username}}",', 811 | ' "company": "{{company}}",', 812 | ' "tld": "{{tld}}",', 813 | ' "domain": "{{domain}}",', 814 | ' "email": "{{email}}"', 815 | '}' 816 | ]; 817 | var expected = { 818 | 'lastName': 'Magby', 819 | 'firstName': 'Darrell', 820 | 'username': 'dmagby', 821 | 'company': 'Qualcore', 822 | 'tld': 'name', 823 | 'domain': 'qualcore.name', 824 | 'email': 'darrell.magby@qualcore.name' 825 | }; 826 | assertJSONOutput(template, expected); 827 | }); 828 | 829 | it('should link all values when username is used first', function() { 830 | var template = [ 831 | '{', 832 | ' "username": "{{username}}",', 833 | ' "firstName": "{{firstName}}",', 834 | ' "lastName": "{{lastName}}",', 835 | ' "company": "{{company}}",', 836 | ' "tld": "{{tld}}",', 837 | ' "domain": "{{domain}}",', 838 | ' "email": "{{email}}"', 839 | '}' 840 | ]; 841 | var expected = { 842 | 'username': 'isprowl', 843 | 'firstName': 'Ivan', 844 | 'lastName': 'Sprowl', 845 | 'company': 'Qualcore', 846 | 'tld': 'name', 847 | 'domain': 'qualcore.name', 848 | 'email': 'ivan.sprowl@qualcore.name' 849 | }; 850 | assertJSONOutput(template, expected); 851 | }); 852 | 853 | it('should link all values when email is used first', function() { 854 | var template = [ 855 | '{', 856 | ' "email": "{{email}}",', 857 | ' "username": "{{username}}",', 858 | ' "firstName": "{{firstName}}",', 859 | ' "lastName": "{{lastName}}",', 860 | ' "company": "{{company}}",', 861 | ' "tld": "{{tld}}",', 862 | ' "domain": "{{domain}}"', 863 | '}' 864 | ]; 865 | var expected = { 866 | 'email': 'ivan.sprowl@qualcore.name', 867 | 'username': 'isprowl', 868 | 'firstName': 'Ivan', 869 | 'lastName': 'Sprowl', 870 | 'company': 'Qualcore', 871 | 'tld': 'name', 872 | 'domain': 'qualcore.name' 873 | }; 874 | assertJSONOutput(template, expected); 875 | }); 876 | 877 | it('should link all values when domain is used first', function() { 878 | var template = [ 879 | '{', 880 | ' "domain": "{{domain}}",', 881 | ' "firstName": "{{firstName}}",', 882 | ' "lastName": "{{lastName}}",', 883 | ' "username": "{{username}}",', 884 | ' "company": "{{company}}",', 885 | ' "tld": "{{tld}}",', 886 | ' "email": "{{email}}"', 887 | '}' 888 | ]; 889 | var expected = { 890 | 'domain': 'fortyfour.gov', 891 | 'firstName': 'Lloyd', 892 | 'lastName': 'Smit', 893 | 'username': 'lsmit', 894 | 'email': 'lloyd.smit@fortyfour.gov', 895 | 'company': 'FortyFour', 896 | 'tld': 'gov' 897 | }; 898 | assertJSONOutput(template, expected); 899 | }); 900 | 901 | it('should link all values when company is used first', function() { 902 | var template = [ 903 | '{', 904 | ' "company": "{{company}}",', 905 | ' "domain": "{{domain}}",', 906 | ' "firstName": "{{firstName}}",', 907 | ' "lastName": "{{lastName}}",', 908 | ' "username": "{{username}}",', 909 | ' "tld": "{{tld}}",', 910 | ' "email": "{{email}}"', 911 | '}' 912 | ]; 913 | var expected = { 914 | 'company': 'FortyFour', 915 | 'domain': 'fortyfour.gov', 916 | 'firstName': 'Lloyd', 917 | 'lastName': 'Smit', 918 | 'username': 'lsmit', 919 | 'email': 'lloyd.smit@fortyfour.gov', 920 | 'tld': 'gov' 921 | }; 922 | assertJSONOutput(template, expected); 923 | }); 924 | 925 | it('should link all values when tld is used first', function() { 926 | var template = [ 927 | '{', 928 | ' "tld": "{{tld}}",', 929 | ' "company": "{{company}}",', 930 | ' "domain": "{{domain}}",', 931 | ' "firstName": "{{firstName}}",', 932 | ' "lastName": "{{lastName}}",', 933 | ' "username": "{{username}}",', 934 | ' "email": "{{email}}"', 935 | '}' 936 | ]; 937 | var expected = { 938 | 'tld': 'co', 939 | 'company': 'Conixco', 940 | 'domain': 'conixco.co', 941 | 'firstName': 'Lloyd', 942 | 'lastName': 'Smit', 943 | 'username': 'lsmit', 944 | 'email': 'lloyd.smit@conixco.co' 945 | }; 946 | assertJSONOutput(template, expected); 947 | }); 948 | 949 | it('should not reuse lastName when email is repeatedly used', function() { 950 | var template = [ 951 | '{', 952 | ' "firstName1": "{{firstName}}",', 953 | ' "lastName1": "{{lastName}}",', 954 | ' "email1": "{{email}}",', 955 | ' "firstName2": "{{firstName}}",', 956 | ' "email2": "{{email}}"', 957 | '}' 958 | ]; 959 | var expected = { 960 | 'firstName1': 'Ivan', 961 | 'lastName1': 'Sprowl', 962 | 'email1': 'ivan.sprowl@qualcore.name', 963 | 'firstName2': 'Theo', 964 | 'email2': 'theo.winter@citisys.biz' 965 | }; 966 | assertJSONOutput(template, expected); 967 | }); 968 | 969 | it('should not reuse lastName when username is repeatedly used', function() { 970 | var template = [ 971 | '{', 972 | ' "firstName1": "{{firstName}}",', 973 | ' "lastName1": "{{lastName}}",', 974 | ' "username1": "{{username}}",', 975 | ' "firstName2": "{{firstName}}",', 976 | ' "username2": "{{username}}"', 977 | '}' 978 | ]; 979 | var expected = { 980 | 'firstName1': 'Ivan', 981 | 'lastName1': 'Sprowl', 982 | 'username1': 'isprowl', 983 | 'firstName2': 'Lloyd', 984 | 'username2': 'lsmit' 985 | }; 986 | assertJSONOutput(template, expected); 987 | }); 988 | 989 | it('should generate a new firstName when using a linked helper twice', function() { 990 | var template = [ 991 | '{', 992 | ' "firstName1": "{{firstName}}",', 993 | ' "username1": "{{username}}",', 994 | ' "username2": "{{username}}",', 995 | ' "firstName2": "{{firstName}}",', 996 | ' "email1": "{{email}}",', 997 | ' "email2": "{{email}}",', 998 | ' "firstName3": "{{firstName}}"', 999 | '}' 1000 | ]; 1001 | var expected = { 1002 | 'firstName1': 'Ivan', 1003 | 'username1': 'isprowl', 1004 | 'username2': 'lsmit', 1005 | 'firstName2': 'Lloyd', 1006 | 'email1': 'lloyd.smit@polycore.net', 1007 | 'email2': 'tyler.castleman@peersys.club', 1008 | 'firstName3': 'Tyler' 1009 | }; 1010 | assertJSONOutput(template, expected); 1011 | }); 1012 | 1013 | it('should generate a new lastName when using a linked helper twice', function() { 1014 | var template = [ 1015 | '{', 1016 | ' "lastName1": "{{lastName}}",', 1017 | ' "username1": "{{username}}",', 1018 | ' "username2": "{{username}}",', 1019 | ' "lastName2": "{{lastName}}",', 1020 | ' "email1": "{{email}}",', 1021 | ' "email2": "{{email}}",', 1022 | ' "lastName3": "{{lastName}}"', 1023 | '}' 1024 | ]; 1025 | var expected = { 1026 | 'lastName1': 'Magby', 1027 | 'username1': 'dmagby', 1028 | 'username2': 'lsmit', 1029 | 'lastName2': 'Smit', 1030 | 'email1': 'lloyd.smit@polycore.net', 1031 | 'email2': 'tyler.castleman@peersys.club', 1032 | 'lastName3': 'Castleman' 1033 | }; 1034 | assertJSONOutput(template, expected); 1035 | }); 1036 | 1037 | it('should generate a new company when using a linked helper twice', function() { 1038 | var template = [ 1039 | '{', 1040 | ' "company1": "{{company}}",', 1041 | ' "domain1": "{{domain}}",', 1042 | ' "domain2": "{{domain}}",', 1043 | ' "company2": "{{company}}",', 1044 | ' "email1": "{{email}}",', 1045 | ' "email2": "{{email}}",', 1046 | ' "company3": "{{company}}"', 1047 | '}' 1048 | ]; 1049 | var expected = { 1050 | 'company1': 'FortyFour', 1051 | 'domain1': 'fortyfour.gov', 1052 | 'domain2': 'qualcore.name', 1053 | 'company2': 'Qualcore', 1054 | 'email1': 'theo.winter@qualcore.name', 1055 | 'email2': 'tyler.castleman@peersys.club', 1056 | 'company3': 'PeerSys' 1057 | }; 1058 | assertJSONOutput(template, expected); 1059 | }); 1060 | 1061 | it('should generate a new tld when using a linked helper twice', function() { 1062 | var template = [ 1063 | '{', 1064 | ' "tld1": "{{tld}}",', 1065 | ' "domain1": "{{domain}}",', 1066 | ' "domain2": "{{domain}}",', 1067 | ' "tld2": "{{tld}}",', 1068 | ' "email1": "{{email}}",', 1069 | ' "email2": "{{email}}",', 1070 | ' "tld3": "{{tld}}"', 1071 | '}' 1072 | ]; 1073 | var expected = { 1074 | 'tld1': 'co', 1075 | 'domain1': 'conixco.co', 1076 | 'domain2': 'qualcore.name', 1077 | 'tld2': 'name', 1078 | 'email1': 'theo.winter@qualcore.name', 1079 | 'email2': 'tyler.castleman@peersys.club', 1080 | 'tld3': 'club' 1081 | }; 1082 | assertJSONOutput(template, expected); 1083 | }); 1084 | 1085 | it('should link countries and country codes', function() { 1086 | var template = [ 1087 | '{', 1088 | ' "country": "{{country}}",', 1089 | ' "countryCode": "{{countryCode}}",', 1090 | ' "country2": "{{country}}",', 1091 | ' "countryCode2": "{{countryCode}}",', 1092 | ' "country3": "{{country}}",', 1093 | ' "country4": "{{country}}",', 1094 | ' "countryCode4": "{{countryCode}}",', 1095 | ' "countryCode5": "{{countryCode}}",', 1096 | ' "country5": "{{country}}"', 1097 | '}' 1098 | ]; 1099 | var expected = { 1100 | 'country': 'Iceland', 1101 | 'countryCode': 'IS', 1102 | 'country2': 'Isle of Man', 1103 | 'countryCode2': 'IM', 1104 | 'country3': 'Burundi', 1105 | 'country4': 'Northern Mariana Islands', 1106 | 'countryCode4': 'MP', 1107 | 'countryCode5': 'GW', 1108 | 'country5': 'Guinea-Bissau' 1109 | }; 1110 | assertJSONOutput(template, expected); 1111 | }); 1112 | 1113 | it('should clear any linked values when starting a repeat block', function() { 1114 | var template = [ 1115 | '{', 1116 | ' "firstName": "{{firstName}}",', 1117 | ' "lastName": "{{lastName}}",', 1118 | ' "company": "{{company}}",', 1119 | ' "tld": "{{tld}}",', 1120 | ' "emails": [', 1121 | ' {{#repeat 2}}', 1122 | ' "{{email}}"', 1123 | ' {{/repeat}}', 1124 | ' ],', 1125 | ' "country": "{{country}}",', 1126 | ' "countryCodes": [', 1127 | ' {{#repeat 2}}', 1128 | ' "{{countryCode}}"', 1129 | ' {{/repeat}}', 1130 | ' ]', 1131 | '}' 1132 | ]; 1133 | var expected = { 1134 | 'firstName': 'Ivan', 1135 | 'lastName': 'Sprowl', 1136 | 'company': 'Qualcore', 1137 | 'tld': 'name', 1138 | 'emails': [ 1139 | 'theo.winter@citisys.biz', 1140 | 'florance.krumm@dalserve.biz' 1141 | ], 1142 | 'country': 'Japan', 1143 | 'countryCodes': [ 1144 | 'RO', 1145 | 'LA' 1146 | ] 1147 | }; 1148 | assertJSONOutput(template, expected); 1149 | }); 1150 | }); 1151 | 1152 | describe('random', function() { 1153 | it('should return a random item from the given string params', function() { 1154 | var template = '{{random "North" "South" "East" "West"}}'; 1155 | var expected = 'South'; 1156 | assertStringOutput(template, expected); 1157 | }); 1158 | 1159 | it('should return a random item from the given number params', function() { 1160 | var template = '{{random 10 20 30 40}}'; 1161 | var expected = '20'; 1162 | assertStringOutput(template, expected); 1163 | }); 1164 | 1165 | it('should return a single item', function() { 1166 | var template = '{{random "North"}}'; 1167 | var expected = 'North'; 1168 | assertStringOutput(template, expected); 1169 | }); 1170 | 1171 | it('should throw an error if no params are provided', function() { 1172 | var template = '{{random}}'; 1173 | assert.throws( 1174 | function() { 1175 | dummyjson.parse(template); 1176 | }, 1177 | Error 1178 | ); 1179 | }); 1180 | }); 1181 | 1182 | describe('step', function() { 1183 | it('should generate stepped numbers inside a repeat block', function() { 1184 | var template = [ 1185 | '[', 1186 | ' {{#repeat 3}}', 1187 | ' {{step 10}}', 1188 | ' {{/repeat}}', 1189 | ']' 1190 | ]; 1191 | var expected = [ 1192 | '0', 1193 | '10', 1194 | '20' 1195 | ]; 1196 | assertJSONOutput(template, expected); 1197 | }); 1198 | 1199 | it('should generate negative stepped numbers inside a repeat block', function() { 1200 | var template = [ 1201 | '[', 1202 | ' {{#repeat 3}}', 1203 | ' {{step -10}}', 1204 | ' {{/repeat}}', 1205 | ']' 1206 | ]; 1207 | var expected = [ 1208 | '0', 1209 | '-10', 1210 | '-20' 1211 | ]; 1212 | assertJSONOutput(template, expected); 1213 | }); 1214 | 1215 | it('should generate stepped numbers inside an each block', function() { 1216 | var template = '{{#each things}}{{step 10}} {{/each}}'; 1217 | var expected = '0 10 20 '; 1218 | var options = { 1219 | mockdata: { 1220 | things: ['a', 'b', 'c'], 1221 | }, 1222 | }; 1223 | assertStringOutput(template, expected, options); 1224 | }); 1225 | 1226 | it('should throw an error if no params are provided', function() { 1227 | var template = '{{step}}'; 1228 | assert.throws( 1229 | function() { 1230 | dummyjson.parse(template); 1231 | }, 1232 | Error 1233 | ); 1234 | }); 1235 | 1236 | it('should throw an error if used outside of a repeat or each block', function() { 1237 | var template = '{{step 10}}'; 1238 | assert.throws( 1239 | function() { 1240 | dummyjson.parse(template); 1241 | }, 1242 | Error 1243 | ); 1244 | }); 1245 | }); 1246 | 1247 | describe('subexpression helpers', function() { 1248 | describe('lowercase', function() { 1249 | it('should change the given value to lowercase', function() { 1250 | var template = '{{lowercase (firstName)}}'; 1251 | var expected = 'ivan'; 1252 | assertStringOutput(template, expected); 1253 | }); 1254 | }); 1255 | 1256 | describe('uppercase', function() { 1257 | it('should change the given value to uppercase', function() { 1258 | var template = '{{uppercase (firstName)}}'; 1259 | var expected = 'IVAN'; 1260 | assertStringOutput(template, expected); 1261 | }); 1262 | }); 1263 | 1264 | describe('add', function() { 1265 | it('should add two numbers together', function() { 1266 | var template = '{{add 1 2}}'; 1267 | var expected = '3'; 1268 | assertStringOutput(template, expected); 1269 | }); 1270 | 1271 | it('should be able to use the @index from a repeat block', function() { 1272 | var template = [ 1273 | '[', 1274 | ' {{#repeat 2}}', 1275 | ' {{add @index 10}}', 1276 | ' {{/repeat}}', 1277 | ']' 1278 | ]; 1279 | var expected = [ 1280 | '10', 1281 | '11' 1282 | ]; 1283 | assertJSONOutput(template, expected); 1284 | }); 1285 | 1286 | it('should be able to use in conjunction with the step helper', function() { 1287 | var template = [ 1288 | '[', 1289 | ' {{#repeat 2}}', 1290 | ' {{add 100 (step 20)}}', 1291 | ' {{/repeat}}', 1292 | ']' 1293 | ]; 1294 | var expected = [ 1295 | '100', 1296 | '120' 1297 | ]; 1298 | assertJSONOutput(template, expected); 1299 | }); 1300 | 1301 | it('should throw an error if no params are provided', function() { 1302 | var template = '{{add}}'; 1303 | assert.throws( 1304 | function() { 1305 | dummyjson.parse(template); 1306 | }, 1307 | Error 1308 | ); 1309 | }); 1310 | 1311 | it('should throw an error if only one param is provided', function() { 1312 | var template = '{{add 1}}'; 1313 | assert.throws( 1314 | function() { 1315 | dummyjson.parse(template); 1316 | }, 1317 | Error 1318 | ); 1319 | }); 1320 | }); 1321 | }); 1322 | }); 1323 | --------------------------------------------------------------------------------