Best JavaScript code snippet using apimocker
apimocker.js
Source:apimocker.js  
1/* eslint-disable no-prototype-builtins, comma-dangle */2const express = require('express');3const _ = require('underscore');4const path = require('path');5const fs = require('fs');6const bodyParser = require('body-parser');7const xmlparser = require('express-xml-bodyparser');8const jp = require('jsonpath');9const untildify = require('untildify');10const util = require('util');11const proxy = require('express-http-proxy');12const multer = require('multer');13const crypto = require('crypto');14const { createLogger, createMiddleware } = require('./logger');15const apiMocker = {};16apiMocker.defaults = {17  port: '8888',18  mockDirectory: './mocks/',19  allowedDomains: ['*'],20  allowedHeaders: ['Content-Type'],21  logRequestHeaders: false,22  allowAvoidPreFlight: false,23  useUploadFieldname: false,24  webServices: {}25};26apiMocker.createServer = (options = {}) => {27  apiMocker.options = Object.assign({}, apiMocker.defaults, options);28  const { quiet } = apiMocker.options;29  const logger = createLogger({ quiet });30  const loggerMiddleware = createMiddleware({ quiet });31  apiMocker.express = express();32  apiMocker.middlewares = [];33  apiMocker.middlewares.push(loggerMiddleware);34  if (options.uploadRoot) {35    apiMocker.middlewares.push(36      multer({37        storage: multer.diskStorage({38          destination: untildify(options.uploadRoot),39          filename: options.useUploadFieldname40            ? (req, filename, cb) => {41              cb(null, filename.fieldname);42            }43            : (req, filename, cb) => {44              cb(null, filename.originalname);45            }46        })47      }).any()48    );49  }50  let saveBody;51  if (options.proxyURL || options.allowAvoidPreFlight) {52    saveBody = (req, res, buf) => {53      req.rawBody = buf;54    };55  }56  apiMocker.middlewares.push(57    bodyParser.urlencoded({58      extended: true,59      verify: saveBody60    })61  );62  if (options.allowAvoidPreFlight) {63    apiMocker.middlewares.push(64      bodyParser.json({65        strict: false,66        verify: saveBody,67        type: '*/*'68      })69    );70  } else {71    apiMocker.middlewares.push(72      bodyParser.json({73        verify: saveBody74      })75    );76  }77  apiMocker.middlewares.push(xmlparser());78  apiMocker.middlewares.push(apiMocker.corsMiddleware);79  // new in Express 4, we use a Router now.80  apiMocker.router = express.Router();81  apiMocker.middlewares.push(apiMocker.router);82  if (options.proxyURL) {83    logger.info(`Proxying to ${options.proxyURL}`);84    const proxyOptions = {85      proxyReqPathResolver(req) {86        logger.info(`Forwarding request: ${req.originalUrl}`);87        return req.originalUrl;88      }89    };90    if (options.proxyIntercept) {91      const interceptPath = path.join(process.cwd(), options.proxyIntercept);92      logger.info(`Loading proxy intercept from ${interceptPath}`);93      // eslint-disable-next-line global-require, import/no-dynamic-require94      proxyOptions.intercept = require(interceptPath);95    }96    apiMocker.middlewares.push((req, res, next) => {97      if (req.rawBody) {98        req.body = req.rawBody;99      }100      next();101    });102    apiMocker.middlewares.push(proxy(options.proxyURL, proxyOptions));103  }104  apiMocker.logger = logger;105  return apiMocker;106};107apiMocker.setConfigFile = (file) => {108  if (!file) {109    return apiMocker;110  }111  if (!file.startsWith(path.sep)) {112    // relative path from command line113    apiMocker.configFilePath = path.resolve(process.cwd(), file);114  } else {115    apiMocker.configFilePath = file;116  }117  return apiMocker;118};119apiMocker.loadConfigFile = () => {120  if (!apiMocker.configFilePath) {121    apiMocker.logger.warn('No config file path set.');122    return;123  }124  apiMocker.logger.info(`Loading config file: ${apiMocker.configFilePath}`);125  let newOptions = _.clone(apiMocker.defaults);126  // eslint-disable-next-line global-require, import/no-dynamic-require127  const exportedValue = require(apiMocker.configFilePath);128  const config = typeof exportedValue === 'function' ? exportedValue() : exportedValue;129  if (process.env.VCAP_APP_PORT) {130    // we're running in cloudfoundry, and we need to use the VCAP port.131    config.port = process.env.VCAP_APP_PORT;132  }133  newOptions = _.extend(newOptions, apiMocker.options, config);134  newOptions.mockDirectory = untildify(newOptions.mockDirectory);135  if (newOptions.mockDirectory === '/file/system/path/to/apimocker/samplemocks/') {136    newOptions.mockDirectory = path.join(__dirname, '/../samplemocks');137    apiMocker.logger.info('Set mockDirectory to: ', newOptions.mockDirectory);138  }139  apiMocker.options = newOptions;140  _.each(apiMocker.options.webServices, (svc) => {141    _.each(svc.alternatePaths, (altPath) => {142      const altSvc = _.clone(svc);143      apiMocker.options.webServices[altPath] = altSvc;144    });145  });146  apiMocker.setRoutes(apiMocker.options.webServices);147};148apiMocker.createAdminServices = () => {149  apiMocker.router.all('/admin/reload', (req, res) => {150    apiMocker.stop();151    apiMocker.createServer(apiMocker.options).start();152    res.writeHead(200, { 'Content-Type': 'application/json' });153    res.end(`{"configFilePath": "${apiMocker.configFilePath}", "reloaded": "true"}`);154  });155  apiMocker.router.all('/admin/setMock', (req, res) => {156    let newRoute = {};157    if (req.body.serviceUrl && req.body.verb && req.body.mockFile) {158      apiMocker.logger.info(`Received JSON request: ${JSON.stringify(req.body)}`);159      newRoute = req.body;160      newRoute.verb = newRoute.verb.toLowerCase();161      newRoute.httpStatus = req.body.httpStatus;162    } else {163      newRoute.verb = req.param('verb').toLowerCase();164      newRoute.serviceUrl = req.param('serviceUrl');165      newRoute.mockFile = req.param('mockFile');166      newRoute.latency = req.param('latency');167      newRoute.contentType = req.param('contentType');168      newRoute.httpStatus = req.param('httpStatus');169    }170    // also need to save in our webServices object.171    delete apiMocker.options.webServices[newRoute.serviceUrl];172    apiMocker.options.webServices[newRoute.serviceUrl] = newRoute;173    apiMocker.setRoute(newRoute);174    res.writeHead(200, { 'Content-Type': 'application/json' });175    res.end(JSON.stringify(newRoute));176  });177};178apiMocker.setRoutes = (webServices) => {179  const topLevelKeys = _.keys(webServices);180  _.each(topLevelKeys, (key) => {181    const svc = _.clone(webServices[key]);182    // apiMocker.logger.info('about to add a new service: ' + JSON.stringify(svc));183    _.each(svc.verbs, (v) => {184      apiMocker.setRoute(apiMocker.getServiceRoute(key, v));185    });186  });187};188apiMocker.getServiceRoute = (routePath, verb) => {189  let finalSvc = _.clone(apiMocker.options.webServices[routePath]);190  finalSvc.verb = verb.toLowerCase();191  finalSvc.serviceUrl = routePath;192  if (finalSvc.responses) {193    finalSvc = _.extend(finalSvc, finalSvc.responses[verb]);194  }195  if (typeof finalSvc.latency === 'undefined') {196    finalSvc.latency = apiMocker.options.latency ? apiMocker.options.latency : 0;197  }198  delete finalSvc.responses;199  delete finalSvc.verbs;200  return finalSvc;201};202// Fills in templated Values.203apiMocker.fillTemplate = (data, req) => {204  let filled = data.toString();205  Object.keys(req.params).forEach((key) => {206    // Handle unquoted numbers first207    // Search for '"@@key"' in JSON template,208    // replace with value (no double quotes around final value)209    filled = filled.replace(new RegExp(`"@@${key}"`, 'g'), req.params[key]);210    // Handle quoted values second211    // Search for '@key' in JSON template, replace with value212    filled = filled.replace(new RegExp(`@${key}`, 'g'), req.params[key]);213  });214  return filled;215};216apiMocker.fillTemplateSwitch = (options, data) => {217  const switches = options.templateSwitch;218  let filled = data.toString();219  switches.forEach((s) => {220    let key;221    let value;222    if (!(s instanceof Object)) {223      ({ key, value } = switches[s]);224    } else {225      ({ key, value } = s);226    }227    if (value !== null) {228      // Handle unquoted numbers first229      // Search for '"@@key"' in JSON template,230      // replace with value (no double quotes around final value)231      apiMocker.logger.info(`fillTemplateSwitch -> search for "@@${key}" replace with ${value}`);232      filled = filled.replace(new RegExp(`"@@${key}"`, 'g'), value);233      // Handle quoted values second234      // Search for '@key' in JSON template, replace with value235      apiMocker.logger.info(`fillTemplateSwitch -> search for @${key} replace with ${value}`);236      filled = filled.replace(new RegExp(`@${key}`, 'g'), value);237    } else {238      apiMocker.logger.info(`fillTemplateSwitch -> skipping search for @${key} with no value.`);239    }240  });241  return filled;242};243apiMocker.processTemplateData = (data, options, req, res) => {244  let templatedData;245  if (options.templateSwitch) {246    templatedData = apiMocker.fillTemplateSwitch(options, data, req);247  }248  if (options.enableTemplate === true) {249    templatedData = apiMocker.fillTemplate(data, req);250  }251  const buff = Buffer.from(templatedData || data, 'utf8');252  res.status(options.httpStatus || 200).send(buff);253};254apiMocker.sendResponse = (req, res, serviceKeys) => {255  let originalOptions;256  let mockPath;257  // we want to look up the service info from our in-memory 'webServices' every time.258  let options = apiMocker.getServiceRoute(serviceKeys.serviceUrl, serviceKeys.verb);259  setTimeout(() => {260    if (options.httpStatus === 204 || options.httpStatus === 304) {261      // express handles these two differently - it strips out body, content-type,262      // and content-length headers.263      // There's no body or content-length, so we just send the status code.264      res.sendStatus(options.httpStatus);265      return;266    }267    // Filter whether the raw body is what we're expecting, if such filter is provided.268    if (!!options.bodies && !!options.bodies[req.method.toLowerCase()]) {269      if (270        // eslint-disable-next-line max-len271        !_.find(options.bodies[req.method.toLowerCase()], filterDef => apiMocker.compareHashed(filterDef, req.rawBody || JSON.stringify(req.body)))272      ) {273        res.status(404).send();274        return;275      }276    }277    if (options.switch && !options.jsonPathSwitchResponse) {278      options = _.clone(options);279      originalOptions = _.clone(options);280      apiMocker.setSwitchOptions(options, req);281      mockPath = path.join(apiMocker.options.mockDirectory, options.mockFile || '');282      if (!fs.existsSync(mockPath)) {283        apiMocker.logger.warn(284          `No file found: ${options.mockFile} attempting base file: ${originalOptions.mockFile}`285        );286        options.mockFile = originalOptions.mockFile;287      }288    }289    if (options.templateSwitch) {290      apiMocker.setTemplateSwitchOptions(options, req);291    }292    if (apiMocker.options.logRequestHeaders || options.logRequestHeaders) {293      apiMocker.logger.info('Request headers:');294      apiMocker.logger.info(req.headers);295    }296    if (options.headers) {297      res.set(options.headers);298    }299    if (options.mockBody) {300      if (options.contentType) {301        res.set('Content-Type', options.contentType);302      }303      apiMocker.processTemplateData(options.mockBody, options, req, res);304      return;305    }306    if (!options.mockFile) {307      const status = options.httpStatus || 404;308      res.status(status).send();309      return;310    }311    // Add mockFile name for logging312    res.locals.mockFile = options.mockFile;313    if (options.switch && options.jsonPathSwitchResponse) {314      let jpath = options.jsonPathSwitchResponse.jsonpath;315      const fpath = path.join(316        apiMocker.options.mockDirectory,317        options.jsonPathSwitchResponse.mockFile318      );319      const forceFirstObject = options.jsonPathSwitchResponse.forceFirstObject || false;320      _.each(_.keys(req.params), (key) => {321        const param = '#key#'.replace('key', key);322        jpath = jpath.replace(param, req.params[key]);323      });324      try {325        const mockFile = fs.readFileSync(fpath, { encoding: 'utf8' });326        const allElems = jp.query(JSON.parse(mockFile), jpath);327        res.status(options.httpStatus || 200).send(forceFirstObject ? allElems[0] : allElems);328      } catch (err) {329        apiMocker.logger.error(err);330        res.sendStatus(options.httpStatus || 404);331      }332      return;333    }334    mockPath = path.join(apiMocker.options.mockDirectory, options.mockFile);335    fs.exists(mockPath, (exists) => {336      if (exists) {337        if (options.contentType) {338          res.set('Content-Type', options.contentType);339          fs.readFile(mockPath, { encoding: 'utf8' }, (err, data) => {340            if (err) {341              throw err;342            }343            apiMocker.processTemplateData(data.toString(), options, req, res);344          });345        } else {346          res347            .status(options.httpStatus || 200)348            .sendFile(options.mockFile, { root: apiMocker.options.mockDirectory });349        }350      } else {351        res.sendStatus(options.httpStatus || 404);352      }353    });354  }, options.latency);355};356// Utility function to get a key's value from json body, route param, querystring, or header.357const getRequestParam = (req, key) => {358  const rawParamValue = req.body[key] || req.params[key] || req.query[key] || req.header(key);359  return rawParamValue;360};361// only used when there is a switch configured362apiMocker.setSwitchOptions = (options, req) => {363  let switchFilePrefix = '';364  let switchParamValue;365  let mockFileParts;366  let mockFilePrefix = '';367  let mockFileBaseName;368  let switches = options.switch;369  if (!(switches instanceof Array)) {370    switches = [switches];371  }372  switches.forEach((s) => {373    switchParamValue = null;374    let switchObject = s;375    let specific = true;376    if (!(s instanceof Object)) {377      // The user didn't configure a switch object. Make one.378      switchObject = {379        key: s,380        switch: s,381        type: 'default'382      };383      if (s.match(/\/(.+)\//)) {384        switchObject.type = 'regexp';385      } else if (s.indexOf('$') === 0) {386        switchObject.type = 'jsonpath';387      }388      // As we had no switch object, we have to test default-type first to389      // mimic the old behaviour.390      specific = false;391    }392    if (!switchObject.hasOwnProperty('key')) {393      // Add key if the user was too lazy394      switchObject.key = switchObject.switch;395    }396    // Sanity check the switchobject397    if (398      !switchObject.hasOwnProperty('switch')399      || !switchObject.hasOwnProperty('type')400      || !switchObject.hasOwnProperty('key')401    ) {402      return;403    }404    if (!specific || switchObject.type === 'default') {405      const rawParamValue = getRequestParam(req, switchObject.switch);406      if (rawParamValue) {407        switchParamValue = encodeURIComponent(rawParamValue);408      }409    }410    if (!switchParamValue) {411      if (switchObject.type === 'regexp') {412        const regexpTest = switchObject.switch.match(/\/(.+)\//);413        if (regexpTest) {414          // A regexp switch415          let searchBody = req.body;416          if (typeof req.body !== 'string') {417            // We don't have a body string, parse it in JSON418            searchBody = JSON.stringify(req.body);419          }420          const regexpSwitch = new RegExp(regexpTest[1]).exec(searchBody);421          if (regexpSwitch) {422            // Value is the first group423            switchParamValue = encodeURIComponent(regexpSwitch[1]);424          }425        }426      } else {427        // use JsonPath - use first value found if multiple occurances exist428        const allElems = jp.query(req.body, switchObject.switch);429        if (allElems.length > 0) {430          switchParamValue = encodeURIComponent(allElems[0]);431        }432      }433    }434    if (switchParamValue) {435      switchFilePrefix = switchFilePrefix + switchObject.key + switchParamValue;436    }437  });438  if (!switchFilePrefix) {439    return;440  }441  if (options.switchResponses && options.switchResponses[switchFilePrefix]) {442    _.extend(options, options.switchResponses[switchFilePrefix]);443    if (options.switchResponses[switchFilePrefix].mockFile) {444      return;445    }446  }447  if (options.mockFile) {448    mockFileParts = options.mockFile.split('/');449    mockFileBaseName = mockFileParts.pop();450    if (mockFileParts.length > 0) {451      mockFilePrefix = `${mockFileParts.join('/')}/`;452    }453    // eslint-disable-next-line no-param-reassign454    options.mockFile = `${mockFilePrefix + switchFilePrefix}.${mockFileBaseName}`;455  }456};457// only used when there is a templateSwitch configured458apiMocker.setTemplateSwitchOptions = (options, req) => {459  let switchParamValue;460  let switches = options.templateSwitch;461  if (!(switches instanceof Array)) {462    switches = [switches];463  }464  switches.forEach((s) => {465    switchParamValue = null;466    let switchObject = s;467    let specific = true;468    if (!(s instanceof Object)) {469      // The user didn't configure a switch object. Make one.470      switchObject = {471        key: s,472        switch: s,473        type: 'default',474        value: null475      };476      if (s.match(/\/(.+)\//)) {477        switchObject.type = 'regexp';478      } else if (s.indexOf('$') === 0) {479        switchObject.type = 'jsonpath';480      }481      // As we had no switch object, we have to test default-type first to482      // mimic the old behaviour.483      specific = false;484    }485    if (!switchObject.hasOwnProperty('key')) {486      // Add key if the user was too lazy487      switchObject.key = switchObject.switch;488    }489    // Sanity check the switchobject490    if (491      !switchObject.hasOwnProperty('switch')492      || !switchObject.hasOwnProperty('type')493      || !switchObject.hasOwnProperty('key')494    ) {495      apiMocker.logger.info(496        'templateSwitch invalid config: missing switch, type or key property. Aborting templateSwitch for this request.'497      );498      return;499    }500    if (!specific || switchObject.type === 'default') {501      const rawParamValue = getRequestParam(req, switchObject.switch);502      if (rawParamValue) {503        switchParamValue = encodeURIComponent(rawParamValue);504      }505    }506    if (!switchParamValue) {507      if (switchObject.type === 'regexp') {508        const regexpTest = switchObject.switch.match(/\/(.+)\//);509        if (regexpTest) {510          // A regexp switch511          let searchBody = req.body;512          if (typeof req.body !== 'string') {513            // We don't have a body string, parse it in JSON514            searchBody = JSON.stringify(req.body);515          }516          const regexpSwitch = new RegExp(regexpTest[1]).exec(searchBody);517          if (regexpSwitch) {518            // Value is the first group519            switchParamValue = encodeURIComponent(regexpSwitch[1]);520          }521        }522      } else {523        // use JsonPath - use first value found if multiple occurances exist524        const allElems = jp.query(req.body, switchObject.switch);525        if (allElems.length > 0) {526          switchParamValue = encodeURIComponent(allElems[0]);527        }528      }529    }530    if (switchParamValue) {531      switchObject.value = switchParamValue;532      // eslint-disable-next-line no-param-reassign533      options.templateSwitch[s] = switchObject;534    } else {535      apiMocker.logger.warn(`templateSwitch[${switchObject.switch}] value NOT FOUND`);536    }537  });538};539// Sets the route for express, in case it was not set yet.540apiMocker.setRoute = (options) => {541  const displayFile = options.mockFile || '<no mockFile>';542  const displayLatency = options.latency ? `${options.latency} ms` : '';543  apiMocker.router[options.verb](`/${options.serviceUrl}`, (req, res) => {544    apiMocker.sendResponse(req, res, options);545  });546  apiMocker.logger.info(547    `Set route: ${options.verb.toUpperCase()} ${548      options.serviceUrl549    } : ${displayFile} ${displayLatency}`550  );551  if (options.switch) {552    let switchDescription = options.switch;553    if (options.switch instanceof Array || options.switch instanceof Object) {554      switchDescription = util.inspect(options.switch);555    }556    apiMocker.logger.info(` with switch on param: ${switchDescription}`);557  }558};559// CORS middleware560apiMocker.corsMiddleware = (req, res, next) => {561  const allowedHeaders = apiMocker.options.allowedHeaders.join(',');562  const credentials = apiMocker.options.corsCredentials || '';563  res.set('Access-Control-Allow-Origin', apiMocker.options.allowedDomains);564  res.set('Access-Control-Allow-Methods', 'GET,PUT,POST,PATCH,DELETE');565  res.set('Access-Control-Allow-Headers', allowedHeaders);566  res.set('Access-Control-Allow-Credentials', credentials);567  next();568};569apiMocker.compareHashed = (filterDef, body) => {570  if (!(filterDef instanceof Object)) {571    // eslint-disable-next-line eqeqeq572    return filterDef == body;573  }574  const algo = _.keys(filterDef)[0];575  const hasher = crypto.createHash(algo);576  hasher.update(body);577  const digest = hasher.digest('hex');578  apiMocker.logger.warn(`Body hash ${algo}: ${digest}`);579  // eslint-disable-next-line eqeqeq580  return digest.toLowerCase() == filterDef[algo].toLowerCase();581};582apiMocker.start = (serverPort, callback) => {583  apiMocker.createAdminServices();584  apiMocker.loadConfigFile();585  apiMocker.middlewares.forEach((mw) => {586    if (mw === apiMocker.router && apiMocker.options.basepath) {587      apiMocker.logger.info('Using basepath: ', apiMocker.options.basepath);588      apiMocker.express.use(apiMocker.options.basepath, mw);589    } else {590      apiMocker.express.use(mw);591    }592  });593  const port = serverPort || apiMocker.options.port;594  if (apiMocker.options.staticDirectory && apiMocker.options.staticPath) {595    apiMocker.express.use(596      apiMocker.options.staticPath,597      express.static(apiMocker.options.staticDirectory)598    );599  }600  apiMocker.expressInstance = apiMocker.express.listen(port, callback);601  apiMocker.logger.info(`Mock server listening on port ${port}`);602  return apiMocker;603};604apiMocker.stop = (callback) => {605  // Invalidate cached config between uses to allow it to be reconstructed.606  delete require.cache[require.resolve(apiMocker.configFilePath)];607  if (apiMocker.expressInstance) {608    apiMocker.logger.info('Stopping mock server.');609    apiMocker.expressInstance.close(callback);610  }611  return apiMocker;612};613// expose all the 'public' methods.614exports.createServer = apiMocker.createServer;615exports.start = apiMocker.start;616exports.setConfigFile = apiMocker.setConfigFile;617exports.stop = apiMocker.stop;...test.js
Source:test.js  
1// run "grunt test", or run "mocha" in this test directory to execute.2const path = require('path');3// better assertions than node offers.4const chai = require('chai');5const sinon = require('sinon');6const mockRequire = require('mock-require');7const untildify = require('untildify');8const apiMocker = require('../lib/apimocker.js');9const { assert, expect } = chai;10describe('unit tests: ', () => {11  chai.config.includeStack = true;12  describe('createServer: ', () => {13    it('sets defaults when no options are passed in', () => {14      const mocker = apiMocker.createServer();15      expect(mocker.options.port).to.equal('8888');16      expect(mocker.options.mockDirectory).to.equal('./mocks/');17      expect(mocker.options.allowedDomains.length).to.equal(1);18      expect(mocker.options.allowedDomains[0]).to.equal('*');19      expect(mocker.options.allowedHeaders[0]).to.equal('Content-Type');20      expect(mocker.options.quiet).to.equal(undefined);21      expect(mocker.options.logRequestHeaders).to.equal(false);22    });23    it('overrides defaults with command line args', () => {24      const mocker = apiMocker.createServer({ port: 1234, quiet: true, foo: 'bar' });25      expect(mocker.options.port).to.equal(1234);26      expect(mocker.options.mockDirectory).to.equal('./mocks/');27      expect(mocker.options.allowedDomains[0]).to.equal('*');28      expect(mocker.options.quiet).to.equal(true);29      expect(mocker.options.foo).to.equal('bar');30    });31  });32  describe('setConfigFile: ', () => {33    const mocker = apiMocker.createServer();34    beforeEach(() => {35      delete mocker.configFilePath;36    });37    after(() => {38      delete mocker.configFilePath;39    });40    it('should set a relative path correctly using node path resolver', () => {41      assert.equal(42        path.resolve('../config.json'),43        mocker.setConfigFile('../config.json').configFilePath44      );45    });46    it('should set an absolute path correctly', () => {47      const absolutePath = path.normalize('/foo/bar/config.json');48      expect(mocker.setConfigFile(absolutePath).configFilePath).to.equal(absolutePath);49    });50    it('sets no path, if none is passed in', () => {51      expect(mocker.setConfigFile().configFilePath).to.equal(undefined);52    });53  });54  describe('loadConfigFile: ', () => {55    // Note:56    // Starting mock config paths with a / or ~ to avoid57    // the absoulute path resolution within setConfig which will not match58    // the path mocked by mock-require.59    const mockConfig = {60      mockDirectory: '~/foo/bar/samplemocks/',61      quiet: true,62      port: '7879',63      latency: 50,64      logRequestHeaders: true,65      allowedDomains: ['abc'],66      allowedHeaders: ['my-custom1', 'my-custom2'],67      webServices: {68        first: {69          verbs: ['get', 'post'],70          responses: {71            get: {72              mockFile: 'king.json'73            },74            post: {75              mockFile: 'ace.json'76            }77          },78          alternatePaths: ['1st']79        },80        'nested/ace': {81          mockFile: 'ace.json',82          verbs: ['get']83        },84        'var/:id': {85          mockFile: 'xml/queen.xml',86          verbs: ['get']87        },88        queen: {89          mockFile: 'xml/queen.xml',90          verbs: ['all']91        }92      }93    };94    afterEach(() => {95      mockRequire.stopAll();96    });97    it('sets options from new format mock config file', () => {98      const mocker = apiMocker.createServer({ quiet: true });99      mockRequire('/mock-config.json', mockConfig);100      mocker.setConfigFile('/mock-config.json');101      mocker.loadConfigFile();102      expect(mocker.options.port).to.equal(mockConfig.port);103      expect(mocker.options.allowedDomains[0]).to.equal(mockConfig.allowedDomains[0]);104      expect(mocker.options.allowedHeaders[0]).to.equal('my-custom1');105      expect(mocker.options.allowedHeaders[1]).to.equal('my-custom2');106      expect(mocker.options.webServices.first).to.eql(mocker.options.webServices['1st']);107      delete mocker.options.webServices['1st'];108      expect(mocker.options.webServices).to.deep.equal(mockConfig.webServices);109      expect(mocker.options.quiet).to.equal(true);110      expect(mocker.options.latency).to.equal(mockConfig.latency);111      expect(mocker.options.logRequestHeaders).to.equal(mockConfig.logRequestHeaders);112    });113    it('combines values from defaults, options, and config file', () => {114      let mocker = apiMocker.createServer({ quiet: true, test: 'fun', port: 2323 });115      mockRequire('/partial-config', { port: 8765, latency: 99, logRequestHeaders: false });116      mocker = mocker.setConfigFile('/partial-config');117      mocker.loadConfigFile();118      // value from config file119      expect(mocker.options.port).to.equal(8765);120      expect(mocker.options.latency).to.equal(99);121      expect(mocker.options.logRequestHeaders).to.equal(false);122      // value from defaults123      expect(mocker.options.allowedDomains[0]).to.equal('*');124      expect(mocker.options.webServices).to.deep.equal(mocker.defaults.webServices);125      // value from options passed in to createServer:126      expect(mocker.options.test).to.equal('fun');127    });128    it('expands ~ in mockDirectory setting', () => {129      const mocker = apiMocker.createServer({ quiet: true });130      mockRequire('/mock-config.json', mockConfig);131      mocker.setConfigFile('/mock-config.json');132      mocker.loadConfigFile();133      expect(mocker.options.mockDirectory).to.equal(untildify(mockConfig.mockDirectory));134    });135    it('supports js config files that export a function', () => {136      const mocker = apiMocker.createServer({ quiet: true });137      const port = '1111';138      mockRequire('/partial-config', () => ({ port }));139      mocker.setConfigFile('/partial-config');140      mocker.loadConfigFile();141      expect(mocker.options.port).to.equal(port);142    });143    it('supports js config files that export an object', () => {144      const mocker = apiMocker.createServer({ quiet: true });145      const port = '2222';146      mockRequire('/partial-config', { port });147      mocker.setConfigFile('/partial-config');148      mocker.loadConfigFile();149      expect(mocker.options.port).to.equal(port);150    });151    it('should not allow requests that avoid pre flight by default', () => {152      const mocker = apiMocker.createServer({ quiet: true });153      expect(mocker.options.allowAvoidPreFlight).to.equal(false);154    });155    it('should allow requests that avoid pre flight if specified in config', () => {156      const mocker = apiMocker.createServer({ quiet: true });157      mockRequire('/partial-config', {158        allowAvoidPreFlight: true159      });160      mocker.setConfigFile('/partial-config');161      mocker.loadConfigFile();162      expect(mocker.options.allowAvoidPreFlight).to.equal(true);163    });164  });165  describe('setSwitchOptions: ', () => {166    let mocker;167    let svcOptions;168    let reqStub;169    beforeEach(() => {170      mocker = apiMocker.createServer({ quiet: true });171      svcOptions = { switch: 'productId', mockFile: 'base' };172      reqStub = {173        body: {},174        params: {},175        query: {},176        header: () => {}177      };178    });179    it('does not set mock file path if switch is not found in request', () => {180      mocker.setSwitchOptions(svcOptions, reqStub);181      expect(svcOptions.mockFile).to.equal('base');182    });183    it('sets correct mock file path if switch is found in query string', () => {184      reqStub.query = { productId: '123' };185      mocker.setSwitchOptions(svcOptions, reqStub);186      expect(svcOptions.mockFile).to.equal('productId123.base');187    });188    it('sets correct mock file path if switch is found in json body', () => {189      reqStub.body.productId = '678';190      mocker.setSwitchOptions(svcOptions, reqStub);191      expect(svcOptions.mockFile).to.equal('productId678.base');192    });193    it('sets correct mock file path if switch is found in route parameter', () => {194      reqStub.params = { productId: '123' };195      mocker.setSwitchOptions(svcOptions, reqStub);196      expect(svcOptions.mockFile).to.equal('productId123.base');197    });198    it('sets correct mock file path if switch is found in request header with matching case', () => {199      reqStub.header = () => '765';200      mocker.setSwitchOptions(svcOptions, reqStub);201      expect(svcOptions.mockFile).to.equal('productId765.base');202    });203    it('sets correct mock file path if switch is found in request header with different case', () => {204      reqStub.header = () => '765';205      svcOptions = { switch: 'PRodUCTID', mockFile: 'base' };206      mocker.setSwitchOptions(svcOptions, reqStub);207      expect(svcOptions.mockFile).to.equal('PRodUCTID765.base');208    });209    it('sets correct mock file path with switch and nested path', () => {210      reqStub.body.productId = '678';211      svcOptions.mockFile = 'path/to/base';212      mocker.setSwitchOptions(svcOptions, reqStub);213      expect(svcOptions.mockFile).to.equal('path/to/productId678.base');214    });215    it('sets correct mock file path with switch value containing special character', () => {216      reqStub.body.productId = 'abc/123';217      mocker.setSwitchOptions(svcOptions, reqStub);218      expect(svcOptions.mockFile).to.equal('productIdabc%2F123.base');219    });220    it('sets correct mock file path with two switch values', () => {221      svcOptions.switch = ['productId', 'color'];222      reqStub.body.productId = '345';223      reqStub.body.color = 'red';224      mocker.setSwitchOptions(svcOptions, reqStub);225      expect(svcOptions.mockFile).to.equal('productId345colorred.base');226    });227    it('sets correct http status based on matching switch value', () => {228      svcOptions.switch = 'password';229      svcOptions.switchResponses = {230        passwordgood: { httpStatus: 200 }231      };232      reqStub.body.password = 'good';233      mocker.setSwitchOptions(svcOptions, reqStub);234      expect(svcOptions.httpStatus).to.equal(200);235    });236    it('sets correct mock file path when switch matches and switchResponse contains a mockFile', () => {237      reqStub.body.productId = '678';238      svcOptions.switchResponses = {239        productId678: { mockFile: 'specialFileName' }240      };241      mocker.setSwitchOptions(svcOptions, reqStub);242      expect(svcOptions.mockFile).to.equal('specialFileName');243    });244    it('sets correct http status when switch value does not match', () => {245      svcOptions.switch = 'password';246      svcOptions.httpStatus = 401;247      svcOptions.switchResponses = {248        passwordgood: { httpStatus: 200 }249      };250      reqStub.body.password = 'bad';251      mocker.setSwitchOptions(svcOptions, reqStub);252      expect(svcOptions.httpStatus).to.equal(401);253    });254    it('sets correct http status when two switches match', () => {255      svcOptions.switch = ['userId', 'password'];256      svcOptions.httpStatus = 401;257      svcOptions.switchResponses = {258        userId1234passwordgood: { httpStatus: 200 }259      };260      reqStub.body.password = 'good';261      reqStub.body.userId = '1234';262      mocker.setSwitchOptions(svcOptions, reqStub);263      expect(svcOptions.httpStatus).to.equal(200);264    });265    it('sets correct mock file path when switch uses JsonPath and switch matches', () => {266      svcOptions.switch = '$.car.engine.part';267      svcOptions.switchResponses = {268        '$.car.engine.partTiming%20Belt': { mockFile: 'product456' }269      };270      reqStub.body = {271        car: {272          engine: {273            part: 'Timing Belt'274          }275        }276      };277      mocker.setSwitchOptions(svcOptions, reqStub);278      expect(svcOptions.mockFile).to.equal('product456');279    });280    it('sets correct mock file path when switch uses JsonPath and switch value does not match', () => {281      svcOptions.switch = '$.car.engine.part';282      svcOptions.switchResponses = {283        '$.car.engine.partTiming%20Belt': { mockFile: 'product456' }284      };285      reqStub.body = {286        car: {287          wheel: {}288        }289      };290      mocker.setSwitchOptions(svcOptions, reqStub);291      expect(svcOptions.mockFile).to.equal('base');292    });293    it('sets correct mock file path when switch uses JsonPath as a switch object and switch matches', () => {294      svcOptions.switch = {295        type: 'jsonpath',296        switch: '$.car.engine.part'297      };298      svcOptions.switchResponses = {299        '$.car.engine.partTiming%20Belt': { mockFile: 'product456' }300      };301      reqStub.body = {302        car: {303          engine: {304            part: 'Timing Belt'305          }306        }307      };308      mocker.setSwitchOptions(svcOptions, reqStub);309      expect(svcOptions.mockFile).to.equal('product456');310    });311    it('sets correct mock file path when switch uses JsonPath and switch value does not match', () => {312      svcOptions.switch = {313        type: 'jsonpath',314        switch: '$.car.engine.part'315      };316      svcOptions.switchResponses = {317        '$.car.engine.partTiming%20Belt': { mockFile: 'product456' }318      };319      reqStub.body = {320        car: {321          wheel: {}322        }323      };324      mocker.setSwitchOptions(svcOptions, reqStub);325      expect(svcOptions.mockFile).to.equal('base');326    });327    it('sets the correct mock file path when switch uses RegExp and switch matches', () => {328      svcOptions.switch = '/"carEnginePart([^"]*)"/';329      svcOptions.switchResponses = {330        '/"carEnginePart([^"]*)"/Belt': { mockFile: 'product456' }331      };332      reqStub.body = '"carPartWheel": wheel,\n"carEnginePartBelt": belt';333      mocker.setSwitchOptions(svcOptions, reqStub);334      expect(svcOptions.mockFile).to.equal('product456');335    });336    it('sets the correct mock file path when switch uses RegExp and switch value does not match', () => {337      svcOptions.switch = '/"carEnginePart([^"]*)"/';338      svcOptions.switchResponses = {339        Belt: { mockFile: 'product456' }340      };341      reqStub.body = '"carPartWheel": wheel';342      mocker.setSwitchOptions(svcOptions, reqStub);343      expect(svcOptions.mockFile).to.equal('base');344    });345    it('sets the correct mock file path when switch uses RegExp in a switch object and switch matches', () => {346      svcOptions.switch = {347        type: 'regexp',348        switch: '/"carEnginePart([^"]*)"/',349        key: 'carenginepart'350      };351      svcOptions.switchResponses = {352        carenginepartBelt: { mockFile: 'product456' }353      };354      reqStub.body = '"carPartWheel": wheel,\n"carEnginePartBelt": belt';355      mocker.setSwitchOptions(svcOptions, reqStub);356      expect(svcOptions.mockFile).to.equal('product456');357    });358    it('sets the correct mock file path when switch uses RegExp in a switch object and switch does not match', () => {359      svcOptions.switch = {360        type: 'regexp',361        switch: '/"carEnginePart([^"]*)"/',362        key: 'carenginepart'363      };364      svcOptions.switchResponses = {365        carenginepartBelt: { mockFile: 'product456' }366      };367      reqStub.body = '"carPartWheel": wheel';368      mocker.setSwitchOptions(svcOptions, reqStub);369      expect(svcOptions.mockFile).to.equal('base');370    });371  });372  describe('setRoute:', () => {373    const am = apiMocker.createServer();374    it('sets no default http status code', () => {375      const options = {376        verb: 'get',377        latency: 0,378        serviceUrl: 'foo.com',379        mockFile: 'file.json'380      };381      am.setRoute(options);382      expect(options.httpStatus).to.equal(undefined);383    });384  });385  describe('setRoutes:', () => {386    const am = apiMocker.createServer();387    let setRouteMock;388    beforeEach(() => {389      setRouteMock = sinon.mock(am, 'setRoute');390    });391    afterEach(() => {392      setRouteMock.restore();393    });394    it('calls setRoute with a simple service definition', () => {395      const webServices = {396        first: {397          mockFile: 'king.json',398          latency: 20,399          verbs: ['get', 'post']400        }401      };402      am.options.webServices = webServices;403      setRouteMock.expects('setRoute').withExactArgs({404        latency: 20,405        mockFile: 'king.json',406        serviceUrl: 'first',407        verb: 'get'408      });409      setRouteMock.expects('setRoute').withExactArgs({410        latency: 20,411        mockFile: 'king.json',412        serviceUrl: 'first',413        verb: 'post'414      });415      am.setRoutes(webServices);416      setRouteMock.verify();417    });418    it('calls setRoute with complex service definition', () => {419      const webServices = {420        second: {421          verbs: ['delete', 'post'],422          responses: {423            delete: { httpStatus: 204 },424            post: {425              contentType: 'foobar',426              mockFile: 'king.json'427            }428          }429        }430      };431      am.options.webServices = webServices;432      setRouteMock.expects('setRoute').withExactArgs({433        httpStatus: 204,434        latency: 0,435        serviceUrl: 'second',436        verb: 'delete'437      });438      setRouteMock.expects('setRoute').withExactArgs({439        latency: 0,440        serviceUrl: 'second',441        verb: 'post',442        contentType: 'foobar',443        mockFile: 'king.json'444      });445      am.setRoutes(webServices);446      setRouteMock.verify();447    });448  });...mockers.ts
Source:mockers.ts  
1import { Dispatch } from 'react';2import { createMocker, deleteMocker, listMockers, updateMocker } from '../api/mockers';3import { IMocker } from "../interfaces/Mocker";4import { notifier } from '../utils/notify';5enum MockersActionType {6  RefreshList,7  UpdateMocker,8  AddMocker,9  RemoveMocker,10}11type Action = {12  type: MockersActionType.RefreshList;13  payload: IMocker[];14} | {15  type: MockersActionType.UpdateMocker;16  payload: IMocker;17} | {18  type: MockersActionType.AddMocker;19  payload: IMocker;20} | {21  type: MockersActionType.RemoveMocker;22  payload: { id: number }23}24export type MockerDispatcher = Dispatch<Action>25// reducer create method26export function mockersReducerInit(): IMocker[] {27  return []28}29export function mockersReducer(origin: IMocker[], action: Action): IMocker[] {30  switch (action.type) {31    case MockersActionType.RefreshList:32      if (!Array.isArray(action.payload)) {33        return [];34      }35      return [...action.payload];36    case MockersActionType.UpdateMocker:37      return origin.map((m) => m.id === action.payload.id ? action.payload : m);38    case MockersActionType.AddMocker:39      return [action.payload, ...origin];40    case MockersActionType.RemoveMocker:41      return origin.filter((m) => m.id !== action.payload.id);42    default:43      throw new Error();44  }45}46// operator methods47export function genRefreshMockersAction(dispatch: MockerDispatcher): () => Promise<void> {48  return async () => {49    const res = await listMockers();50    dispatch({51      type: MockersActionType.RefreshList,52      payload: res.mockers53    });54  };55}56export function genToggleMockersAction(dispatch: MockerDispatcher): (m: IMocker) => Promise<void> {57  return async (m: IMocker) => {58    dispatch({59      type: MockersActionType.UpdateMocker,60      payload: { ...m, status: 'updating' }61    });62    const nextStatus = m.status === 'active' ? 'inactive' : 'active';63    const res = await updateMocker({ ...m, status: nextStatus });64    65    if (res.mocker) {66      // TODO: check envoy status67      notifier.success(68        `Successfully update mocker status to ${nextStatus}.`,69        'Please wait few second for the backend service to update the Envoy proxy.'70      )71      dispatch({72        type: MockersActionType.UpdateMocker,73        payload: { ...res.mocker }74      });75    } else {76      dispatch({77        type: MockersActionType.UpdateMocker,78        payload: { ...m }79      });80    }81  }82}83export function genUpdateMockersAction(dispatch: MockerDispatcher): (m: IMocker) => Promise<boolean> {84  return async (m: IMocker) => {85    dispatch({86      type: MockersActionType.UpdateMocker,87      payload: { ...m, status: 'updating' }88    });89    const res = await updateMocker({ ...m });90    if (!res.mocker) {91      dispatch({92        type: MockersActionType.UpdateMocker,93        payload: { ...m }94      });95      return false96    }97    // TODO: check envoy status98    notifier.success(99      `Successfully update mocker.`,100      'Please wait few second for the backend service to update the Envoy proxy.'101    )102    dispatch({103      type: MockersActionType.UpdateMocker,104      payload: { ...res.mocker }105    });106    return true;107  }108}109export function genCreateMockersAction(dispatch: MockerDispatcher): (m: IMocker) => Promise<boolean> {110  return async (m: IMocker) => {111    const res = await createMocker({ ...m });112    if (!res.mocker) {113      return false114    }115    // TODO: check envoy status116    notifier.success(117      `Successfully create a new mocker.`,118      'Please wait few second for the backend service to update the Envoy proxy.'119    )120    dispatch({121      type: MockersActionType.AddMocker,122      payload: { ...res.mocker }123    });124    return true;125  }126}127export function genDeleteMockersAction(dispatch: MockerDispatcher): (mockerId: number) => Promise<boolean> {128  return async (mockerId: number) => {129    const success = await deleteMocker(mockerId);130    // TODO: check envoy status131    if (!success) {132      return false;133    }134    notifier.success(135      `Successfully create a new mocker.`,136      'Please wait few second for the backend service to update the Envoy proxy.'137    );138    dispatch({ type: MockersActionType.RemoveMocker, payload: { id: mockerId } });139    return true;140  }...Using AI Code Generation
1var mocker = require('apimocker');2mocker.setConfigFile('config.json');3mocker.run();4{5}6{7    {8    }9}10{11  "headers": {12  },13  "body": {14  }15}Using AI Code Generation
1const mocker = require('apimocker');2mocker.setConfigFile('./config.json');3mocker.run();4{5    {6      "request": {7      },8      "response": {9      }10    }11}12  {13  },14  {15  }16const mocker = require('apimocker');17mocker.setConfigFile('./config.json');18mocker.run();19{20    {21      "request": {22      },23      "response": {24      }25    }26}27  {28  },29  {30  }31const mocker = require('apimocker');32mocker.setConfigFile('./config.json');33mocker.run();34{35    {36      "request": {37      },38      "response": {39      }40    }41}42  {43  },44  {45  }Using AI Code Generation
1var mocker = require('apimocker');2mocker.setConfigFile('mocker-config.json');3mocker.listen();4{5}6{7  "request": {8  },9  "response": {10    "headers": {11    },12    "body": {13    }14  }15}16var mocker = require('apimocker');17mocker.setConfigFile('mocker-config.json');18mocker.listen();19{20}21{22  "request": {23  },24  "response": {25    "headers": {26    },27    "body": {28    }29  }30}31var mocker = require('apimocker');32mocker.setConfigFile('mocker-config.json');33mocker.listen();34{35}36{37  "request": {Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
