Giter Site home page Giter Site logo

mdasberg / ng-apimock Goto Github PK

View Code? Open in Web Editor NEW
99.0 9.0 26.0 2.27 MB

Node plugin that provides the ability to use scenario based api mocking: for local development for protractor testing

License: MIT License

TypeScript 44.85% HTML 6.06% JavaScript 37.42% Gherkin 11.58% Dockerfile 0.08%
mock protractor-tests ng-apimock scenario mocking-interface mock-data angularjs angular2 protractor protractor-cucumber

ng-apimock's Introduction

ng-apimock Build Status npm version dependency Status devDependency Status npm downloads

Node plugin that provides the ability to use scenario based api mocking:

  • for local development
  • for protractor testing

Plugins that use ng-apimock

MIGRATION to the new modular version

A new version of Ng-apimock has been released. This version has been refactored into multiple modules. You can find the migration guide here.

The functionality has been split up into the following modules:

Getting Started

npm install ng-apimock --save-dev

Once the plugin has been installed, you can require it with this line of JavaScript:

var ngApimock = require('ng-apimock')();

The "ngApimock" process mocks

Overview

In order to use the available mocks, you need to call the run function with this line of JavaScript:

ngApimock.run({
  "baseUrl": "http://<NODE_SERVER_API_URL>:<PORT>", // If not informed browser.baseUrl will be used
  "src": "test/mocks",
  "outputDir": "path/to/outputDir",
  "done": function() {
  // async
  }
});

The run function will process the mock data provided in the configuration and make it accessible for connect as middleware.

In order to watch for changes, you need to call the watch function with this line of Javascript:

ngApimock.watch("test/mocks");

The watch function will watch for changes in the mock directory and update accordingly.

Howto write mocks

There are a couple of rules to follow.

  1. For each api call create a separate file
  2. Each file needs to follow the format below.
{
  "expression": "your expression here (ie a regex without the leading and trailing '/' or a string)",
  "method": "the http method (ie GET, POST, PUT or DELETE)", // supports JSONP as well
  "body": "request body matcher (ie a regex without the leading and trailing '/' or a string)"  // optional
  "name": "identifiable name for this service call"  // if non is provided, expression$$method will be used
  "isArray": "indicates if the response data is an array or object",
  "responses": {
    "some-meaningful-scenario-name": {
      "default": true, // if false or not provided this response will not be used as default
      "status": 200, // optional - defaults to 200
      "headers": {}, // optional - defaults to {}
      "data": {}, // optional
      "file": "path/to/file.ext" // optional, when provided don't forget the matching content-type header as it will result in a file download instead of data
      "statusText": "", // optional
      "delay": 2000 // optional - defaults to no delay when provided this delay will only be used for this response
    },
    "some-other-meaningful-scenario-name": {
      "data": {}
    }
  }
}

Howto use global variables

If for instance, you have date sensitive information in you mocks, mock data is not flexible enough. You can use global variables for this. By surrounding a value in the response.data with %%theVariableName%%, you can make your data more flexible, like this:

"responses": {
    "some-meaningful-scenario-name": {
        "data": {
            "today": "%%today%%"
        }
    }
}

For local development you can use the web interface to add, change or delete variables. For protractor you can use the following commands

     ngApimock.setGlobalVariable(name, value); // to add or update
     ngApimock.deleteGlobalVariable(name); // to delete

Howto serve selected mocks

To be able to use the selected mocks you need to do two things:

  1. Add the connect middleware
  2. Add the mocking interface to your connect configuration

Add the connect middleware

When running connect you can do add the following middleware block to your configuration

var app = connect();
app.use(require('ng-apimock/lib/utils').ngApimockRequest);
app.use(function middleware2(req, res, next) {
  // middleware 2
  next();
});

Add the mocking interface to your connect configuration

When running grunt-contrib-connect you can do add the following staticServe block to your configuration

var app = connect();
app.use('/mocking', require('serve-static')('path/to/the/generated/mocking/index.html'));
app.use(function middleware2(req, res, next) {
  // middleware 2
  next();
});

Howto use for local development

As you have configured both the connect middleware and the mocking interface, everything should work out of the box. By default all the responses configured as default, will be returned if the expression matches.

If you would like to change the selected scenario, you can go to http://localhost:9000/mocking and use the interface to change the selected scenario or variables

The interface looks like this:

alt tag

Howto use for your protractor tests.

As you are building an AngularJS application you will probably use Protractor for testing your UI.

In order to use ngApimock in your protractor tests, require it in your protractor configuration like this:

exports.config = {
    onPrepare: function () {
        global.ngApimock = require('.tmp/mocking/protractor.mock.js');
    }
};

and from that point on you can use it in your tests

describe('Some test', function () {
    it('should do something', function() {
        ngApimock.selectScenario('name of some api', 'another'); // at runtime you can change a scenario
    });
 });

By default all the scenario's marked as default will be returned if the expression matches. So you only need to add ngApimock.selectScenario in case your test needs another scenario response to be returned.

NgApimock also works when running multiple tests concurrent, by using the protract session id of the test. This ensures that changing a scenario in one test, will not effect another test.

Using Angular 2 or higher with Protractor?

If you are using Angular 2 or higher in combination with Protractor you will need to add the following to you configuration.

Protractor 4

exports.config = {
    useAllAngular2AppRoots: true
};

Protractor 5 or higher

exports.config = {
    ngApimockOpts: {
        angularVersion: 2,  // {number} provide major version of Angular
        hybrid: false // optional boolean which can be used for testing Angular apps within an AngularJs app.
    }
};

Available functions

All these functions are protractor promises, so they can be chained.

selectScenario(name, scenarioName, options)

Selects the given scenario (when calling this function without a scenario or with 'passThrough' as scenario name, the call will be passed through to the actual backend)

delayResponse(name, delay)

Sets the delay time in milliseconds for the mock so the response will be delayed. The delay set here superseeds the delay defined in the response mock.

echoRequest(name, indicator)

Sets the indicator which enables / disables the request logging (only post request should be logged)

setAllScenariosToDefault()

Resets all mocks to the default scenarios

setAllScenariosToPassThrough

Resets all mocks to use passthroughs

setGlobalVariable(key, value)

Adds or updates the global key/value pair

setGlobalVariables(variables)

Adds or updates the global key/value pairs ie. {'some':'value', 'another': 'value'}

deleteGlobalVariable(key)

Remove the global variable matching the key

Howto use recording functionality

You can record API calls in NgApimock. This is usefull if you have a live API, and want to create mocks for them. You turn on Recording in the header Record (checkbox), and start calling the API. Requests are recorded for each mock. You can zoom in up to Request Response information. The response data can be used in mock files, described earlier.

Contributing

In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code committing.

ng-apimock's People

Contributors

ainsleybc avatar daanvanhulst avatar dscheerens avatar flauwekeul avatar frankmerema avatar jeroenvandepol avatar mdasberg avatar remco75 avatar stefanlemmen avatar victorsoliveira avatar wswebcreation avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ng-apimock's Issues

Release 1.4.3 breaks protractor e2e tests

The changes made as part of 1.4.3 are breaking my e2e tests which use ngApimock to select scenarios.
48c9952#diff-cbc5d6ec2fddf738fce80b86119065d2R57

Console output below:

> ng e2e --port 4210 --proxy-config proxy.conf.js

Process all the mocks
Register mocks
Generate the mocking web interface
Generate protractor.mock.js
** NG Live Development Server is listening on localhost:4210, open your browser on http://localhost:4210/ **
 10% building modules 3/3 modules 0 active[HPM] Proxy created: /ngapimock  ->  http://localhost:3000
[HPM] Subscribed to http-proxy events:  [ 'error', 'close' ]
[HPM] Proxy created: /api/v1  ->  http://localhost:3000
[HPM] Subscribed to http-proxy events:  [ 'error', 'close' ]
app running on port 3000
Date: 2017-12-13T18:35:07.328Z                                                          
Hash: 9363a89d3746d752f57e
Time: 12987ms
chunk {inline} inline.bundle.js, inline.bundle.js.map (inline) 5.83 kB [entry] [rendered]
chunk {main} main.bundle.js, main.bundle.js.map (main) 85.6 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 540 kB [initial] [rendered]
chunk {styles} styles.bundle.js, styles.bundle.js.map (styles) 647 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js, vendor.bundle.js.map (vendor) 4.95 MB [initial] [rendered]
(node:90507) [DEP0022] DeprecationWarning: os.tmpDir() is deprecated. Use os.tmpdir() instead.

webpack: Compiled successfully.
[18:35:07] I/update - chromedriver: file exists /Users/jayvincent/Sites/demo-app/node_modules/protractor/node_modules/webdriver-manager/selenium/chromedriver_2.34.zip
[18:35:07] I/update - chromedriver: unzipping chromedriver_2.34.zip
[18:35:07] I/update - chromedriver: setting permissions to 0755 for /Users/jayvincent/Sites/demo-app/node_modules/protractor/node_modules/webdriver-manager/selenium/chromedriver_2.34
[18:35:07] I/update - chromedriver: chromedriver_2.34 up to date
[18:35:07] I/launcher - Running 1 instances of WebDriver
[18:35:07] I/direct - Using ChromeDriver directly...
Feature: demo App

  Scenario: App Loads
[HPM] GET /api/v1 -> http://localhost:3000
[18:35:10] E/launcher - Trying to load mock modules on an Angular v2+ app is not yet supported.
[18:35:11] E/launcher - Error: Trying to load mock modules on an Angular v2+ app is not yet supported.
    at process.on (/Users/jayvincent/Sites/demo-app/node_modules/protractor/lib/launcher.ts:187:50)
    at emitOne (events.js:121:20)
    at process.emit (events.js:211:7)
    at process.emit (/Users/jayvincent/Sites/demo-app/node_modules/source-map-support/source-map-support.js:439:21)
    at process._fatalException (bootstrap_node.js:374:26)
[18:35:11] E/launcher - Process exited with error code 199

I'm not quite sure I understand the logic of removing the else block for the addMockModule call. Surely the addMockModule method is always present and of type function on the Protractor browser object so it will always be called - which will result in an error being thrown for all versions of Angular 2 and above.

I'm afraid the comment Angular 2+ lacks addMockModule, but hybrid apps still need this doesn't help my understanding!

I'm using Angular 5.1.0 and Protractor 5.1.2.

Is it possible to change the base URL for `/ngapimock/*` calls?

Is it possible to change the base URL for /ngapimock/* calls without using any proxy?
In most of the examples, we use proxy to redirect all these calls to our actual API server.

"/ngapimock/*": {
    "target": "http://localhost:9090",
    "secure": false,
    "logLevel": "debug"
  }

But such approach forces us to push all other API calls through the same proxy because otherwise, as far as I understand, the cookie (or session id?) will differ so selectScenario won't have any effect.

So if for example, we have our UI hosted on the port 4200 and API server on 9090 we can't change any scenario because the cookie is set to 4200 and not to 9090. Does it mean that our UI is limited to http://localhost:4200/* for API calls? And what if UI is already using http://localhost:9090/* and requires no proxy?

Should I expect additional middleware to be called after a mock is found?

I want to use ng-apimock to mock using real a settings file with a single value modified for each scenario. This is so I don't have to create copies of all the settings I don't want to change in multiple mock JSON files. I was expecting to be able to use Express middleware to do this

let featureScenarioWriter = function (req, res, next) {
    console.log("The request:" + req);
    //check request is for the settings file
    //load the real settings file and modify the key\value matching what is in the response
    //set the response to be the whole settings file with a single value changed

    next();
};
app.set('port', 3000);
// process the api calls through ng-apimock
app.use(require('ng-apimock/lib/utils').ngApimockRequest);
// serve the mocking interface for local development
app.use('/mocking', express.static('./.tmp/ngApimock'));
app.use(featureScenarioWriter);

However no request ever reaches the featureScenarioWriter function. I presume this is because the response is ended by ngApimockRequest.

Should my custom middleware be called? Is there another way I could change one value in a file as part of a scenario?

Docs: How to use the recording feature with Angular 2+?

I'm intrigued by the recording feature, but I'm not sure whether it only works with AngularJS and not with Angular 2+. If it does work with Angular 2+: How does it work and what exactly do I have to do to capture all the API requests on a given page?

Thanks!

Passthrough doesn't forward request headers?

I'm working on a project that integrates with AWS.
If I take ng-apimock out of the chain I can do requests to AWS without issues, but when I set my ng-apimocks to Passthrough I get the following error from AWS:
"message": "Missing Authentication Token"

This token is supposed to be set in the "x-api-key" header.
When I look at my request in the network tab of chrome inspector, I see the header is present on the failed request.

So it seems that ng-apimock doesn't forward the headers when proxying a request. Is that correct or am I missing something else here?

ngapimockid cookie is set too late

The problem i'm seeing is that the ngapimockid cookie is set too late, causing the calls to the (mock) backend to lack the cookie and always returning the default mocks, eventhough i'm calling selectScenario. See my analysis below.

When i add some console.log calls to my onPrepare method in protractor.conf and to the ng-apimock/lib/utils ngApimockRequest function, i see the following behavior:

PUT /ngapimock/mocks/defaults c3b27ce7-503a-4069-900c-60087b52471e

This is the setAllScenariosToDefault() call. The uuid is the ngapimockid value.

I then see the front-end doing a call: GET /teacher/groups undefined. The undefined here is the ngapimockid value.

After that, i get the console.log i put in the "hooker" code in .tmp/ngApimock/protractor.mock.js:

            require('hooker').hook(browser, 'get', {
                post: function (result) {
                    return result.then(function () {
                        // Since protractor 5.0.0 the addCookie is an object, see
                        // https://github.com/angular/protractor/blob/master/CHANGELOG.md#500
                        try {
                            console.log('setting ngapimockid cookie');
                            return browser.manage().addCookie({name: "ngapimockid", value: ngapimockid});
                        } catch (error) {
                            // Fallback protractor < 5.0.0
                            return browser.manage().addCookie('ngapimockid', ngapimockid);
                        }
                    });
                }
            });

When i add a browser.sleep(60000); in my it() and then refresh the browser, the ngapimockid cookie is present and passed to the mock server. I then get the appropriate mock back.

Error when updating or re-installing package

Error occurs when upgrading or attempting to re-install package on top of itself. The problem seems to be related to the fact that the .git directory is included in the package distribution. Can this be removed?

To reproduce:

$ npm install ng-apimock
$ npm install ng-apimock

Installing the first time seems to work fine, the second install produces the following error:

npm ERR! path /foo/node_modules/ng-apimock
npm ERR! code EISGIT
npm ERR! git /foo/node_modules/ng-apimock: Appears to be a git repo or submodule.
npm ERR! git     /foo/node_modules/ng-apimock
npm ERR! git Refusing to remove it. Update manually,
npm ERR! git or move it out of the way first.

Feature: load global variables as an object / string

Current:
We now need to add each global variable per key (for local development and also for protractor tests)

Desired situation:
It would be nice if we can add global variables as an object / string like this

[{
    name: 'key1', 
    value: '1'
}, {
    name: 'key2',
    value: '2'
}]

// or
key1::'1'::key2::'2'

ng-apimock using default mock on app launch

When using ng-apimock to mock scenarios for an angular application, it keeps returning the default scenario on app launch when I set the scenario then launch the app:

loginPage.selectScenario('terms-and-conditions-get-request', 'terms-and-conditions-not-accepted');
loginPage.navigateToBaseUrl();
loginPage.loginButton().click();

The following gives me the expected results:

loginPage.navigateToBaseUrl();
loginPage.selectScenario('terms-and-conditions-get-request','terms-and-conditions-not-accepted');
loginPage.navigateToBaseUrl();
loginPage.loginButton().click();

If I set the default to false for both possible scenarios, then it works without needing to navigate to the URL first. Is there a way to tell it to not use the default without needing to relaunch the app before setting the scenario and then again after?

Feature: response based on POST params

While in GET method we can use regexp for matching url parameters in POST requests is no option of getting different mock response based on the POST request parameters. As a developer and tester i would like to configure mock response based on the POST parameters so i can test interaction with the application and rest api calls.

Could not select scenario [short-term]

[16:06:04] E/launcher - Could not select scenario [short-term]
[16:06:04] E/launcher - Error: Could not select scenario [short-term]
at process.on (C:\workspace\ngapimock-test\node_modules\protractor\lib\launcher.ts:187:50)
at emitOne (events.js:96:13)
at process.emit (events.js:188:7)
at process.emit (C:\workspace\ngapimock-test\node_modules\source-map-support\source-map-support.js:439:21)
at process._fatalException (bootstrap_node.js:296:26)
[16:06:04] E/launcher - Process exited with error code 199

[16:06:04] E/launcher - Could not select scenario [short-term]
[16:06:04] E/launcher - Error: Could not select scenario [short-term]
at process.on (C:\workspace\ngapimock-test\node_modules\protractor\lib\launcher.ts:187:50)
at emitOne (events.js:96:13)
at process.emit (events.js:188:7)
at process.emit (C:\workspace\ngapimock-test\node_modules\source-map-support\source-map-support.js:439:21)
at process._fatalException (bootstrap_node.js:296:26)
[16:06:04] E/launcher - Process exited with error code 199

Proxy config in Angular CLI instructions is unclear

I had an issue getting ngMockapi working w/Angular CLI.

Could not select scenario [individual] thrown

I found that the proxy config (Step 3 in the Angular CLI instructions) only proxies requests that match "/api/*" to the mock data server.

It seems that requests generated from the ngMockapi API (like selectScenario()) also need to be proxied to the mock data server. The sample proxy config in the instructions doesn't include this.

In my case, I had to add an entry for ngapimock (see snippet). After that it seemed to work fine.

{
 "/activity/*": {
   "target": "http://localhost:3000",
   "secure": false,
   "logLevel": "debug"
 },
 "/ngapimock/*": {
   "target": "http://localhost:3000",
   "secure": false,
   "logLevel": "debug"
 }
}

Can you add that to the instructions? Or please advise if I'm missing something.

Add documentation on how to use ng-apimock with other testing frameworks than protractor

I'm working on a project where I wanted to do the testing using Cypress: https://www.cypress.io/
It took me some time to figure out how to best integrate Cypress and ng-apimock, but this is what I ended up doing:

Assuming ng-apimock is running on port 8101. Define the following custom cypress commands in
cypress/support/commands.js:

Cypress.Commands.add("selectScenario", function (identifier, scenario) {
  cy.request("PUT", 'http://localhost:8101/ngapimock/mocks', JSON.stringify({
    identifier: identifier,
    scenario: scenario
  }))
});

Cypress.Commands.add("resetScenariosToDefaults", function () {
  cy.request("PUT", 'http://localhost:8101/ngapimock/mocks/defaults', null);
});

You can then use these commands in your tests as follows:
cypress/integration/example_spec.js:

cy.selectScenario('yourMockName', 'yourScenarioName');
cy.resetScenariosToDefaults();

I'd love to hear if I should be doing something differently. If you agree this is the correct way of integrating ng-apimock with Cypress could you add this to the ng-apimock documentation?

It would be super awesome if there is some kind of cypress-commands.js file in the ng-apimock package which we can simply import in cypress/support/commands.js

I believe implementing that would look something like this:
ng-apimock/lib/cypress/cypress-commands.js:

module.exports = function setupCustomCommands(host, port) {
  const baseUrl = `http://${host}:${port}/ngapimock/mocks`;

  Cypress.Commands.add("selectScenario", function (identifier, scenario) {
    cy.request("PUT", baseUrl, JSON.stringify({
      identifier: identifier,
      scenario: scenario
    }))
  });

  Cypress.Commands.add("resetScenariosToDefaults", function () {
    cy.request("PUT", `${baseUrl}/defaults`, null);
  });

}

cypress/support/commands.js:

const setupCustomCommands = require('ng-apimock/lib/cypress/cypress-commands');
setupCustomCommands('localhost', '8101');

I think it would be cooler though if we don't have to pass in the host and port, because ng-apimock should already know where it is running.

global variables parsed from mocks

Feature request: When parsing mocks check for the presense of global variables

This would all the user to be informed about which global variables where found. It could be sent to the console after the registered mocks message and it could go be used to populate the UI

Query params

Hello.Can you ask me, is it support query parameters in requests?For example url: /api/projects/1/documents/is-unique?documentName=1

Add support for OPTIONS http request method.

Currently, the api mock server its running on a different port when compared to the SPA Im trying to test with Protractor. Proxying API requests is not an option in my case.

Please add the ability to support OPTIONS http request methods so that preflight requests succeed.

How to get a text/plain response?

Here is my json file:

// text.json
{
  "expression": "/api/text",
  "method": "GET",
  "name": "webUrl",
  "responses": {
    "defaultData": {
      "default": true,
      "status": 200,
      "headers": {
        "content-type": "text/plain; charset=utf-8"
      },
      "data": "https://github.com/"
    }
  }
}

It returns me data wrapped with " with my client. And how can I mock the plain text?
Any one can help?

ng-apimock use global variables in mocks without double quotes

I have added ng-apimock to our Angular project. We have successfully created first mock definitions and wrote tests with protractor.

Now we want to start using global variables in mock definitions. As it seems I always have to use double quotes for variables. But in my case I need a number and not a string.

I have created a question on stackoverflow with a sample already, you can find it here

Any ideas how to get this working?

Global variables seems to only work with data and not file attribute

Hi There,

Is is possible to use global variables in mock data when it comes from a file rather the data attribute?

In my scenario if I put the %%variable%% reference in a JSON object within the data: attribute it gets substituted correctly but if I have the same JSON object in a file that is loaded via the file: attribute it does not get substituted.

Is there a way to achieve this?

Many Thanks

Getting 404/ requests not being intercepted

Using Angular 4.3.4 & Protractor 5.4.1
Below are my configurations and .json file
It seems I must be missing a step or doing something because I am getting 404,
the console does output this however

Process all the mocks
Register mocks
Generate the mocking web interface
Generate protractor.mock.js

protractor.config:

exports.config = {
  allScriptsTimeout: 30000,
  specs: [
    './e2e/**/*.e2e-spec.ts'
  ],
  capabilities:{
		'browserName': 'chrome',	
	},
  directConnect: true,
  framework: 'jasmine',
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 60000,
    print: function() {}
  },
  onPrepare() {
    require('ts-node').register({
      project: 'e2e/tsconfig.json'
    });
    
    global.ngApimock = require('./.tmp/mocks/protractor.mock');

    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
  },
  ngApimockOpts: {
    angularVersion: 4,  // {number} provide major version of Angular
    hybrid: false // optional boolean which can be used for testing Angular apps within an AngularJs app.
  },
  specReporter: {
	maxLogLines: 5,  // limit number of lines logged per test
	suppressErrorSummary: false,  // do not print error summary
	suppressFailed: false,  // do not print information about failed tests
	suppressPassed: false,  // do not print information about passed tests
	suppressSkipped: true,  // do not print information about skipped tests
	showSpecTiming: false // print the time elapsed for each spec
  }
};

mock

{
	"expression": "api/env",
	"method": "GET",
	"name": "environmentService",
	"isArray": false,
	"responses": {
	  "defaultResponse": {
		"default": true, 
		"status": 200, 
		"headers": {}, 
		"data": {
            "success": true
            }
        },
		"file": "",
		"statusText": ""
	  }
	}
  }

in my test:

const bodyParser = require('body-parser'),
	express = require('express'),
	cors = require('cors'),
	ngApiMock = require('ng-apimock')();

describe('Test Page', ()=>{
	let page: TestPage;
	const app = express();
            app.use(bodyParser.json());
            app.use(bodyParser.urlencoded({ extended: false }));
            app.use(cors());
            app.use(require('ng-apimock/lib/utils').ngApimockRequest);
            ngApiMock.run({
	  	"baseUrl": browser.baseUrl,
  		"src":"./mocks"
    });

I am getting response

ok: false
status: 404
statusText: "Not Found"
type: 2
url: "http://localhost:49153/api/env"

decode requestUrl before matching it against the expression

When using ng-apimock in different browsers like Chrome and IE11 in my case, the requestUrl can be different between browsers. In my opinion the expression should match the decoded url for consistency. For instance when using single quotes in urls.

/search/query='val'
/search/query=%27val%27

In the current setup this leads to complex regular expression strings like this
\/search\/query=(%27|\')[a-zA-Z0-9]*(%27|\')

Could not select scenario in Protractor

Hi,

I'm trying to use ng-apimock with Protractor in an Ionic project. After a few false starts, I think I'm almost there, but I'm getting the error Could not select scenario [get-single-case] thrown when trying to use my mocks.

Here's my protractor.conf.js:

// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts

const { SpecReporter } = require('jasmine-spec-reporter');

exports.config = {
  allScriptsTimeout: 11000,
  specs: [
    './src/**/*.e2e-spec.ts'
  ],
  capabilities: {
    'browserName': 'chrome'
  },
  directConnect: true,
  baseUrl: 'http://localhost:4200/',
  framework: 'jasmine',
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000,
    print: function() {}
  },
  onPrepare() {
    require('ts-node').register({
      project: 'e2e/tsconfig.e2e.json'
    });
    
    var ngApimock = require('ng-apimock')();

    ngApimock.run({
      "src": "e2e/mocks",
    });
    
    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
    browser.ngApimock = require('../.tmp/mocks/protractor.mock.js');
  },
  ngApimockOpts: {
    angularVersion: 6,
    hybrid: false
  }
};

And here's a mock:

{
  "expression": "/cases/fdaacd04-7125-117a-ac6d-45a68b1d5de1",
  "method": "GET",
  "name": "case",
  "responses": {
    "get-single-case": {
      "default": true,
      "status": 200,
      "data": {
      ...
      }
    }
  }
}

(I've left the actual data out for brevity)

And here's my tests:

import { CasePage } from './case.po';
import { browser, ExpectedConditions, $ } from 'protractor';

describe('Case information', () => {
  let page: CasePage;

  beforeEach(() => {
    browser['ngApimock'].selectScenario('case', 'get-single-case');
    page = new CasePage();
    page.get();
  });
  
  describe('Listing cases', () => {
    it('should list cases', () => {
      var cases = page.getListofCases();
      expect(cases.count()).toEqual(20);
    });
  });
  
  describe('Show case detail', () => {
    it('should show case information', () => {
      page.clickFirstCase().then(function() {
        expect(page.getHeaderText()).toMatch(/Visit details/);
      });
    })
  });
});

Any help would be appreciated!

Cannot use the same scenario but different response

In case API is the same but different in the body in a request.

Is this possible to use ng-Apimock?

Example:
API /api/everything
body request: { "source" : "customer", "id" : "123" }
response: mocks/data/customer.json

API /api/everything
body request: { "source" : "order", "id" : "O0001" }
response: mocks/data/order_o0001.json

Runtime: Programmatically select response/selection from mock

Hi,

I have a mock file defining a set of responses. I would like to be able to select programmatically, when I implement my own middleware/interceptor, based on the request signature, a certain response from the mock definition.

It's not clear whether that is possible.

Regards,
Lucian

Is it possible to get a custom GET response after a mock POST?

So as a developer I would like to have the mock API respond with different sets of data automatically, based on what happens in the application I'm developing.

I was thinking it would be great if for each mock file I could configure certain "triggers" with the following setup:

The field triggers is a new field in the mock configuration file. Each separate trigger has its own name and contains two fields: when and then.

The when part is an array of objects (or simply 1 object if not an array).

It it's an array then it's always an AND type of selection:

  • The key "url" or "cookie" or "data" with its name as a value;
  • And a key "value" with its value being whatever it needs to match.

Examples:

  • "when": { "cookie": "userId", value: "ab123" }
  • "when": { "url": "userId", value: "ab123" }
  • "when": { "data": "data.userId", value: "ab123" }

And the then part is basically the result of when this match is found:

The then field is an array of consequences with two fields:

  1. mock (optional, defaults to self) - what mock file are we changing the scenario in?
  2. scenario - to what scenario are we changing when the when criteria are met?

So that would allow any type of API call to automatically change selected scenario's in this same mock but also all other mocks.

Configuration idea

{
  "expression": "/api/select-product/:productId",
  "method": "POST",
  "name": "selectProduct",
  "isArray": false,
  "triggers": {
    "changeBlockedUser": {
      "when": [
        {
          "url": "productId",
          "value": "123"
        }
      ],
      "then": [
        {
          "scenario": "wrongAccount"
        },
        {
          "mock": "customer-details",
          "scenario": "bannedAccount"
        },
        {
          "mock": "customer-products",
          "scenario": "noProducts"
        }
      ]
    },
    "changeEnabledUser": {
      "when": [
        {
          "url": "productId",
          "value": "888"
        }
      ],
      "then": [
        {
          "scenario": "default"
        },
        {
          "mock": "customer-details",
          "scenario": "goodAccount"
        },
        {
          "mock": "customer-products",
          "scenario": "tooManyProducts"
        }
      ]
    }
  },
  "responses": {
    "default": {
      "default": true,
      "status": 200,
      "data": {
        "customerId": "123",
        "firstName": "John",
        "lastName": "Doe"
      }
    },
    "wrongAccount": {
      "default": false,
      "status": 404,
      "data": {}
    }
  }
}

In the example above the API would see that if there's a POST to /api/select-product/123, there are a few changes in the ng-apimock scenario selections:

  • The current mock will from now on respond with the wrongAccount scenario
  • The customer-details mock will from now on respond with the bannedAccount scenario
  • The customer-products mock will from now on respond with the noProducts scenario

And if it receives the product with ID 888 if would switch to different scenarios again.


Would love to hear your thoughts, @mdasberg โ€“ we're currently thinking of taking this sort of feature into sprint if at all possible.

Cheers,

Your FrontMen colleague

Improvement: Add baseUrl at run configuration

I'm trying to use ngapimock lib, but, geneated protractor-mock.js use "browser.baseUrl" fixed info and i'm trying to use with my ionic project , but i need the mock server up at different port of my ionic project.

So, if i make a improve at run configuration adding a baseUrl info we can use that at protractor-mock.js, if was null, use browser.baseUrl.

Can i make this improve and send a PR for u ?

Thanks!

Number of imports

Feature request: Once the mocks have been registered, inform the user of how many.

This will ensure that the user that they have been imported

Setup with NG-CLI 7

Hello,
I have done an integration of that lib into an Angular 7 project but everytime I launch my e2e tests, it gets errors like : http//localhost:4200/ngapimock/runtime.js cannot be found 404...

Is there something i'm missing here ?

Protractor Error: Cannot find module './.tmp/ngApimock/protractor.mock.js'

I have installed ng-apimock and see that v1.4.2 is inserted into my package.json
"ng-apimock": "^1.4.2"

Added the required setup in protractor config:

exports.config = {
  allScriptsTimeout: 11000,
  specs: [
    './e2e/**/*.e2e-spec.ts'
  ],
  capabilities: {
    'browserName': 'chrome',
  },
  seleniumAddress: 'http://localhost:4444/wd/hub',
  baseUrl: 'http://localhost:4200/',
  framework: 'jasmine',
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000,
    print: function() {}
  },
  ngApimockOpts: {
    angularVersion: 4  
  },
  onPrepare() {
    require('ts-node').register({
      project: 'e2e/tsconfig.e2e.json'
    });
    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
    global.ngApimock = require('./.tmp/ngApimock/protractor.mock.js');
  }
};

But when I run protractor, I'm getting this error:

[15:45:25] I/launcher - Running 1 instances of WebDriver
[15:45:25] I/hosted - Using the selenium server at http://localhost:4444/wd/hub
[15:45:26] E/launcher - Error: Error: Cannot find module './.tmp/ngApimock/protractor.mock.js'
at Function.Module._resolveFilename (module.js:469:15)

My system info:

angular/cli: 1.4.9
node: 6.11.3
protractor v5.1.2

What am I missing?

Also, I have another question - the README says to create a separate file for each mock api call. But where should I save this file and do I need to name it something special?

Feature: visually show endpoints being hit

If you have many endpoints it can be hard to see which endpoints are actually used. If the server could visually show which endpoints are actually hit (used) it would make it easier to use it. A hit counter for each endpoint would also be nice.

Examples for angular2+ and protractor

Can You please provide some examples for e2e tests using angular2+ and protractor ?

I tried to do as said in : "Howto use for your protractor tests."
But this throws error: ngApimock.selectScenario(...) , Cannot find name 'ngApimock'
What am I missing here ?
I added global.ngApimock = require('.. path to: protractor.mock.js') and also did: "Using Angular 2 or higher with Protractor?" steps, but no success.

Angular 4.0.0
Protractor ~5.1.0

ngApimock.selectScenario does not change scenario. I keep getting the default scenario.

As the title says, I'm experiencing issues with the selectScenario function to set a scenario from a protractor script. Calling the function does change the selected scenario in the /mocking frontend. However, the server still gives me back the default scenario. When I run express in debug mode I see the selectScenario call from the protractor script arriving in my server.

My setup is as follows:
I testing against an angular 5 app (not hybrid)

My protractor conf contains:

ngApimockOpts: {
    angularVersion: 5,
  },
  onPrepare: () => {
    global.ngApimock = require(`../../../.tmp/ngApimock/protractor.mock`);
  }

My server.js is as follows:

const express = require('express');
const ngApimock = require('ng-apimock')();
const path = require('path');
const cors = require('cors');

const distDirectory = path.resolve(`${__dirname}/dist`);
const mocksOutputDirectory = path.resolve(`${__dirname}/.tmp/ngApimock`);
const mocksSourceDirecory = path.resolve(`${__dirname}/test/integration/api-mocks`);

const ngApiMockConfig = {
  outputDir: mocksOutputDirectory,
 src: mocksSourceDirecory
};

const app = express();

ngApimock.run(ngApiMockConfig);
ngApimock.watch(ngApiMockConfig.src);

app
  .use(cors())
  .use(require('ng-apimock/lib/utils').ngApimockRequest)
  .use('/mocking', express.static(mocksOutputDirectory))
  .use(express.static(distDirectory))
  .use((req, res) => res.sendFile('index.html', { root: distDirectory }))
  .listen(3000, 'localhost');

I have tried many things such as adding hybrid to the ngApimockOpts in the protractor conf, using a proxy for use with the angular-cli as described, adding the option useAllAngular2AppRoots: true.

All results in the same problem: The default response of my mock.

Expectations

It would be quite handy to be able to verify the mock was called with certain parameters or certain endpoints were hit, I can't currrently see a way to provide this.

selectScenario is not selecting the requested scenario

I'm calling ngApimock.selectScenario('welcome', 'welcome-new-title'); correctly in my step but it is only acting as a pass through unless default is defined true, to test this I added another scenario. Am I getting something wrong?

Mock file: welcome.json

{
  "expression": "api\/v1\/welcome",
  "method": "GET",
  "name": "welcome",
  "isArray": "false",
  "responses": {
    "welcome-title": {
      "default": true,
      "status": 200,
      "headers": {},
      "data": { "message": "Welcome to app!!" }
    },
    "welcome-new-title": {
      "default": false,
      "status": 200,
      "headers": {},
      "data": { "message": "Welcome to the new app!!" }
    }
  }
}

service http request:

getWelcome() {
    return this.http.get('api/v1/welcome').map(response => response.json())
    .catch((error: Response | any) => {
      return Observable.throw(error.json());
    }).toPromise();
  }

Edit: I'm being returned "Cannot GET /api/v1/welcome" on the mocking page

Edit: Updated expression to regex match (regex101.com), still same issue.

Feature: update scenarios in Protractor with 1 call

Current:
When ng-apimock is used within protractor tests and multiple scenario's need to be selected multiple calls to ngApimock.selectScenario('mock.name', 'mock.scenario') need to be created

Desired situation:
It would be nice that there is a new method called for example selectMultipleScenario(object) that can do something like this (this is how we implemented it in our project)

interface Mocking {
    name: string;
    scenario: string;;
}

export interface MockingData extends Array<Mocking> {
}

/**
 * Load provided Mockdata in ng-Apimock
 */
export async function loadMockdata(mockData: MockingData) {
    for (let mock of mockData) {
        await ngApimock.selectScenario(mock.name, mock.scenario);
    }
}

Protractor not able to run tests with ngApimock

I'm getting an error as soon as the spec starts when running protractor 5.1.1

[09:15:44] E/launcher - Could not select scenario [success]
[09:15:44] E/launcher - Error: Could not select scenario [success]
at process.on (C:\Program Files (x86)\Nodist\bin\node_modules\protractor\lib\launcher.ts:187:50)
at emitOne (events.js:96:13)
at process.emit (events.js:188:7)
at process.emit (C:\Users\bim4\mgo-api\src\main\client\node_modules\source-map-support\source-map-support.js:430:21)
at process.emit (C:\Program Files (x86)\Nodist\bin\node_modules\protractor\node_modules\source-map-support\source-map-support.js:430:21)
at process._fatalException (bootstrap_node.js:292:26)
[09:15:44] E/launcher - Process exited with error code 199

The test looks like:

it('should load the payment-list page', () => { browser['ngApimock'].selectScenario('api/v1/consumer', 'success'); let happyUser = page.getHappyUser(); page.navigateTo(happyUser); });

I'm using angular-cli and used the same setup as the issue in #10

The expression is named api/v1/consumer and the name is consumer. I've tried both of those as the first argument in selectScenario. The Scenario is named 'success'. If I leave the second argument blank I get a "Could not select scenario 'undefined'." What am I doing wrong?

Mocking external calls

I'm trying to setup mocking for a Angular app and I'm running into some issues.
What I did:

  1. run my angular app on localhost:4200
  2. create a server.js (mostly stolen from https://github.com/wswebcreation/rtc-demo-app). I set the rtcProxyOptions to
{
  target: 'http://localhost:4200',
  changeOrigin: true,
  ws: false
}
  1. create a few mocks
  2. node server.js

Now I see the mocks working for calls to localhost:4200/whatever. But for calls to other services (by example: localhost:1337/something ) the mocks are not called.

the mock config looks like this:

{
    "expression": "http://localhost:1337/something",
    "method": "GET",
    "name": "something",
    "isArray": false,
    "responses": {
      "success": {
        "default": true, 
        "status": 200, 
        "headers": {     }, 
        "file": "fixtures/Mocks/bla.json"
      }
    }
  }

I assume that I somehow need to configure that localhost:1337 needs to be proxied as well, but not sure how to do that. Could you please advice?

Thanks in advance!

Feature: ng-apimock watches changes in mockdata

Current:
When mockdata is changed ng-apimock needs to be reloaded and looses it's state / global variables

Desired situation:
It would be nice that there is a watcher in ng-apimock that watches the mockdata-folder and keeps the selected scenario's / global variables that already been loaded.

Please add functionality to add a delay by default

In tests it's possible to set a delay with the delayResponse function, however during development I can only add a delay to a mocked endpoint manually on the /mocking page.
I would like to add a small delay to some endpoints by default so it's easy to see during development that the loading spinners still work.

I would propose an optional "delay" or "delayResponse" field to the responses like illustrated below.

{
  "expression": "todo\/.*",
  "method": "GET",
  "name": "getTodo",
  "isArray": false,
  "responses": {
    "successful": {
      "default": true,
      "status": 200,
      "delay": 500
    }
  }
}

Low level security vulnerability in one of dependencies

Hi,

I'm using ng-apimock in one of my projects as a mock server in e2e tests.
I recently noticed that one of ng-apimock dependencies has low level security vulnerability.

When I ran npm audit command I found that lodash package in versions lower than 4.17.5 has vulnerability called "Prototype Pollution". Currently, ng-apimock uses lodash in version 4.17.4

For more informations, check link below:
https://snyk.io/vuln/npm:lodash:20180130

I would be very grateful for updating this package, which removes the message about security vulnerabilities.

ng-Apimock fails when disabling the Control Flow

In the future the Control Flow will be disabled. With protractor you can now already disable it by adding this flag in your config SELENIUM_PROMISE_MANAGER = false, see also here.

When you now disable it you will get this error:

     TypeError: deferred.fulfill is not a function
          at /Users/wswebcreation/example/dist/ng-apimock/protractor.mock.js:166:26

This can easily be fixed with this in the protractor.mock.js-file.

    /**
     * Executes the api call with the provided information.
     * @param httpMethod The http method.
     * @param urlSuffix The url suffix.
     * @param options The options object.
     * @param errorMessage The error message.
     * @return {Promise} The promise.
     * @private
     */
    function _execute(httpMethod, urlSuffix, options, errorMessage) {
        const opts = {
            headers: {
                'Content-Type': 'application/json',
                'ngapimockid': ngapimockid
            }
        };

        if (options !== undefined) {
            opts.json = options;
        }

        return new Promise((resolve, reject) => {
            request(httpMethod, baseUrl + urlSuffix, opts).done((res) => res.statusCode === 200 ? resolve() : reject(errorMessage));
        });
    }

We need to create a PR and check alle the usages of protractor.promise.defer and replace it with native promises.

Is it working with Angular 7?

So i have a question.Is it support Angular 7? I try to use this lib by your tutorial for angular cli, but it isn't working.

Use value from expression to match specific data

Is it possible to capture a value from the expression and use that to return specific data? Like a regex capture?

For example, I have this simple mock that returns an array of data that's contained in a file
"expression": "api/solutions"
"response": {
"file": "data/solutions-list.json"
}

Each item in the array has a GUID associated with it. This GUID is used in other API calls, for example this returns an svg for that specific GUID:
api/solutions/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})/svg

I'd like to be able to mock it so that the correct svg file is returned based on the GUID in the expression. For example, I could have the response for a file:
"expression": "api/solutions/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})/svg"
"response":{
"file": "data/%GUID%.svg"
}

Where %GUID% is the captured GUID from the expression.

Is this possible or is there another way I missed that I can do this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.