Skip to content

Commit a8374cb

Browse files
iamskp99Pradhyuman-sharmare-Tickrajatsharma
authored
feat : exports test cases and mocks as yaml files (#31)
* test Signed-off-by: iamskp99 <iamskp99@gmail.com> * feat : add test export Signed-off-by: iamskp99 <iamskp99@gmail.com> * feat : add feature to add custom mock export and test export path Signed-off-by: iamskp99 <iamskp99@gmail.com> * docs(update) : add information about test and mock export Signed-off-by: iamskp99 <iamskp99@gmail.com> * docs(update) : add information about test and mock export Signed-off-by: iamskp99 <iamskp99@gmail.com> * docs(update) : add documentation of octokit Signed-off-by: iamskp99 <iamskp99@gmail.com> * docs(update) : add documentation of octokit Signed-off-by: iamskp99 <iamskp99@gmail.com> * docs(update) : add documentation of mock library Signed-off-by: iamskp99 <iamskp99@gmail.com> * feat : exports testcases and mocks as yaml files Signed-off-by: iamskp99 <iamskp99@gmail.com> * feat(ts-sdk): added support for test coverage (#29) * fix(express.ts): header and body issue * fix(express.ts): header and body issue * fix(express.ts): header and body issue * fix(express.ts): header and body issue * feat:changes for check method * adding * check method update * check method update * wip * fix: call create inside middleware * fix: adds calls for test and denoise methods during capturing the response a subsequent denoise call is made to identify the noise fields and test method is fixed to test the captured api responses * refactor: removes unfinished changes * style: fixes sethttpheaders * fix(simulate): adds request headers in simulate api call * fix: adds time delay before calling test function * fix(register): adds the properties of express object into wrapped object * fix: fixes borales/action-yarn version to 2.1.0 * fix(middleware): adds finish event callback to record the asynchronous responses from the handlers makes a call to capture the testcases before sending the response to the client and after res.send call by the handler. * docs: adds documentation for integration with ts/js server * feat(ts-sdk): added support for test coverage * docs(ts-sdk): adding example for testing framework * docs(ts-sdk): adding example for testing framework * docs(ts-sdk): adding example for testing framework * feat(ts-sdk): added support for test coverage * fix: merge conflicts * fix: merge conflicts * commiting * docs(ts-sdk): updating readme Signed-off-by: Pradhyuman-sharma <sharmapuru0642@gmail.com> Signed-off-by: Pradhyuman-sharma <sharmapuru0642@gmail.com> Co-authored-by: re-Tick <jain.ritik.1001@gmail.com> Co-authored-by: Rajat Sharma <lunasunkaiser@gmail.com> Signed-off-by: iamskp99 <iamskp99@gmail.com> * chore: adds spaces Signed-off-by: iamskp99 <iamskp99@gmail.com> * feat: add test export Signed-off-by: iamskp99 <iamskp99@gmail.com> * feat: add feature to add custom mock export and test export path Signed-off-by: iamskp99 <iamskp99@gmail.com> * docs(update): add information about test and mock export Signed-off-by: iamskp99 <iamskp99@gmail.com> * docs(update): add information about test and mock export Signed-off-by: iamskp99 <iamskp99@gmail.com> * docs(update): add documentation of octokit Signed-off-by: iamskp99 <iamskp99@gmail.com> * docs(update): add documentation of octokit Signed-off-by: iamskp99 <iamskp99@gmail.com> * docs(update): add documentation of mock library Signed-off-by: iamskp99 <iamskp99@gmail.com> * feat: exports testcases and mocks as yaml files Signed-off-by: iamskp99 <iamskp99@gmail.com> * fix: removes package-lock.json Signed-off-by: iamskp99 <iamskp99@gmail.com> * fix: yarn.lock Signed-off-by: iamskp99 <iamskp99@gmail.com> Signed-off-by: iamskp99 <iamskp99@gmail.com> Signed-off-by: Pradhyuman-sharma <sharmapuru0642@gmail.com> Co-authored-by: Pradhyuman Sharma <90783566+Pradhyuman-sharma@users.noreply.github.com> Co-authored-by: re-Tick <jain.ritik.1001@gmail.com> Co-authored-by: Rajat Sharma <lunasunkaiser@gmail.com>
1 parent d0ab88b commit a8374cb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+5286
-13751
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,4 @@ node_modules
184184

185185
# Build files
186186

187-
dist
187+
dist

README.md

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,30 @@ npm i https://github.com/keploy/typescript-sdk
2929
```js
3030
require("typescript-sdk/dist/integrations/express/register");
3131
```
32+
The require statement should be at the top of your main file (server.js).
33+
34+
Example :
35+
```js
36+
require("typescript-sdk/dist/integrations/express/register");
37+
var express = require('express');
38+
var app = express();
39+
app.get('/', function (req, res) {
40+
res.send('Hello World!\n');
41+
});
42+
43+
var server = app.listen(3000,() =>
44+
console.log(`Example app listening on port 3000!`));
45+
module.exports = server;
46+
```
47+
3248
## Configure
3349
```
3450
export KEPLOY_MODE="test"
3551
export KEPLOY_APP_NAME="my-app"
3652
export KEPLOY_APP_HOST="localhost"
3753
export KEPLOY_APP_PORT=5050 # port on which server is running
3854
export KEPLOY_APP_DELAY=5 # time delay before starting testruns(in seconds)
39-
export KEPLOY_APP_TIMEOUT=100 # should be number
55+
export KEPLOY_APP_TIMEOUT=100 # should be number
4056
# export KEPLOY_APP_FILTER={"urlRegex":"*"} # should be json not to capture for certain url's
4157
4258
export KEPLOY_SERVER_URL="http://localhost:8081/api" # self hosted keploy running server
@@ -50,11 +66,22 @@ There are 3 modes:
5066

5167
**Note:** `KEPLOY_MODE` value is case sensitive.
5268

53-
## Supported Frameworks
69+
## Generate E2E tests (with mocks)
70+
71+
```
72+
export KEPLOY_TEST_CASE_PATH="./example" # If KEPLOY_TEST_CASE_PATH is not provided then a folder named keploy-tests will be made containing mocks folder. If KEPLOY_MOCK_PATH is provided then the mocks will be generated there.
73+
export KEPLOY_MOCK_PATH="./exampleMockPath"
74+
```
75+
76+
**Note:** To enable `Test Export`, add `export ENABLE_TEST_EXPORT=true` in your .env file of [keploy-server](https://github.com/keploy/keploy) repository. If enabled, yaml files containing test cases will be generated in the directory provided by the user. Similarly, mocks will be generated in the yaml files.
77+
78+
## Supported Routers
5479
### 1. Express
5580
```js
5681
require("typescript-sdk/dist/integrations/express/register");
5782
```
83+
The require statement should be at the top of your main file (server.js).
84+
5885
#### Example
5986
```js
6087
require("typescript-sdk/dist/integrations/express/register");
@@ -72,6 +99,18 @@ app.listen(port, () => {
7299
console.log(`Server is running on port: ${port}`);
73100
})
74101
```
102+
Note:- Import statements can't be used. Use require instead of import.
103+
104+
## Supported Dependencies
105+
106+
### 1. Octokit
107+
108+
```js
109+
require("typescript-sdk/dist/integrations/octokit/require")
110+
```
111+
These statements should be at the top of your main file (server.js).
112+
113+
Note:- Import statements can't be used. Only CommonJs support is currently provided.
75114
## Development Setup
76115

77116
- This project uses [Yarn](https://yarnpkg.com/) for package management. To install yarn, please make sure [Node](https://nodejs.org/en/) is installed and then:
@@ -86,6 +125,40 @@ npm i -g yarn
86125
yarn install
87126
```
88127

128+
### How to use mock library
129+
130+
The external calls from unit tests will be recorded and replayed as mocks from yaml files under a directory named mocks.
131+
132+
Following is an example of unit test with octokit :
133+
134+
#### Example
135+
```js
136+
require("typescript-sdk/dist/integrations/octokit/require")
137+
var {NewContext} = require ("typescript-sdk/dist/mock/mock")
138+
var assert = require('assert');
139+
const { Octokit, App } = require("octokit");
140+
describe('routes', function () {
141+
var server, octokit;
142+
beforeEach(function () {
143+
NewContext({Mode: "record", Name: "your demo app name"}) // Set your keploy mode and name here.
144+
// Clears the cache so a new server instance is used for each test.
145+
// delete require.cache[require.resolve('../app')];
146+
147+
octokit = new Octokit({ auth: "your authentication token"});
148+
149+
});
150+
// Test to make sure URLs respond correctly.
151+
it("url/", async function () {
152+
return new Promise(function(resolve){
153+
octokit.rest.users.getAuthenticated({}).then((result) => {
154+
assert.equal(result.data.login, "your github username")
155+
resolve()
156+
});
157+
})
158+
});
159+
});
160+
```
161+
89162
### Integration with Mocha testing framework
90163
You just need to do some imports and call a built-in assert function in your code in your unit test file and that's it!!🔥🔥🔥
91164
```js
@@ -99,7 +172,7 @@ describe("test function", ()=>{
99172
done()
100173
})
101174
test("should be running", async ()=> {
102-
await keploy.assertTests();
175+
return keploy.assertTests();
103176
});
104177
after(()=>{
105178
process.exit(1); //exits the node server
@@ -108,6 +181,8 @@ describe("test function", ()=>{
108181
```
109182
Note:- To see code coverage please use nyc mocha and see how many lines are covered!!
110183

184+
Note:- Jest is not supported currently!!
185+
111186

112187
- Furthermore, to commit your changes use `yarn commit` instead of `git commit` for better commit experience.
113188

integrations/express/middleware.ts

Lines changed: 58 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,62 @@
11
import express from "express";
22
import Keploy from "../../src/keploy";
33
import { Request, Response, NextFunction } from "express";
4-
import http = require("http");
5-
6-
type Dependency = {
7-
name: string;
8-
type: string;
9-
meta: object;
10-
data: object[];
11-
};
12-
13-
type KeployContext = {
14-
mode: string | undefined;
15-
testId: string | undefined;
16-
deps: Dependency[] | undefined;
17-
};
18-
19-
class Context {
20-
static _bindings = new WeakMap<Request, Context>();
21-
static _response = new WeakMap<Request, Context>();
22-
23-
public keployContext: KeployContext;
24-
public responseBody: object[];
25-
26-
constructor(mode?: string, testId?: string, deps?: Dependency[]) {
27-
this.keployContext = {
28-
mode,
29-
testId,
30-
deps,
31-
};
32-
this.responseBody = [];
33-
}
34-
// bind sets an empty Context for req as a key in _bindings.
35-
static bind(req: Request): void {
36-
const ctx = new Context();
37-
Context._bindings.set(req, ctx);
38-
}
39-
// get returns the value of Context stored for the req key. It returns null if req key is not present
40-
static get(req: Request): Context | null {
41-
return Context._bindings.get(req) || null;
42-
}
43-
// set is used to make a key-value pair for the req and ctx
44-
static set(req: Request, ctx: Context): void {
45-
Context._bindings.set(req, ctx);
46-
Context._response.set(req, ctx);
4+
import { createExecutionContext, getExecutionContext } from "../../src/context";
5+
import { StrArr } from "../../proto/services/StrArr";
6+
7+
class ResponseBody {
8+
static responseMap = new WeakMap<Request, ResponseBody>();
9+
10+
public body: object[];
11+
12+
constructor() {
13+
this.body = [];
4714
}
4815

49-
static pushResponse(req: Request, chunks: object): void {
50-
const ctx = Context._response.get(req);
51-
const oldResponse = ctx?.responseBody;
52-
if (oldResponse === undefined || ctx == undefined) {
16+
static push(req: Request, chunks: object): void {
17+
const resp = ResponseBody.responseMap.get(req);
18+
if (resp === undefined || resp.body === undefined) {
19+
const resp = new ResponseBody();
20+
resp.body = [chunks];
21+
ResponseBody.responseMap.set(req, resp);
22+
5323
return;
5424
}
55-
oldResponse.push(chunks);
56-
Context._response.set(req, ctx);
25+
resp.body.push(chunks);
26+
ResponseBody.responseMap.set(req, resp);
5727
}
58-
static getResponse(req: Request): object[] | undefined {
59-
const ctx = Context._response.get(req);
60-
return ctx?.responseBody;
28+
static get(req: Request): object[] | undefined {
29+
const ctx = ResponseBody.responseMap.get(req);
30+
return ctx?.body;
6131
}
6232
}
6333

6434
// got package identifies header fields to identify request and response therefore, request headers
6535
// should not contain header fields (like: content-length, connection)
66-
export function getRequestHeader(headers: http.IncomingHttpHeaders) {
67-
const result: { [key: string]: string[] } = {};
36+
export function getRequestHeader(headers: any) {
37+
const result: { [key: string]: StrArr } = {};
6838
for (const key in headers) {
6939
let val = new Array<string>();
70-
if (
71-
key.toLowerCase() === "content-length" ||
72-
key.toLowerCase() === "connection"
73-
) {
74-
continue;
75-
}
7640
if (typeof headers[key] === typeof "s") {
7741
val.push(headers[key] as string);
7842
} else if (typeof headers[key] === typeof ["s"]) {
7943
val = headers[key] as string[];
8044
}
81-
result[key] = val;
45+
result[key] = { Value: val };
8246
}
8347
return result;
8448
}
8549

86-
export function getResponseHeader(header: http.OutgoingHttpHeaders) {
87-
const result: { [key: string]: string[] } = {};
50+
export function getResponseHeader(header: any) {
51+
const result: { [key: string]: StrArr } = {};
8852
for (const key in header) {
8953
let val = new Array<string>();
9054
if (typeof header[key] === typeof "s" || typeof header[key] === typeof 1) {
9155
val.push(header[key] as string);
9256
} else if (typeof header[key] === typeof ["s"]) {
9357
val = header[key] as string[];
9458
}
95-
result[key] = val;
59+
result[key] = { Value: val };
9660
}
9761
return result;
9862
}
@@ -110,22 +74,26 @@ export default function middleware(
11074
process.env.KEPLOY_MODE == "off") ||
11175
keploy == undefined
11276
) {
77+
createExecutionContext({ mode: "off" });
11378
next();
11479
return;
11580
}
11681

11782
const id = req.get("KEPLOY_TEST_ID");
11883
// test mode
11984
if (id != undefined && id != "") {
120-
const ctx = new Context("test", id, []);
121-
Context.set(req, ctx);
85+
createExecutionContext({
86+
mode: "test",
87+
testId: id,
88+
deps: keploy.getDependencies(id),
89+
mocks: keploy.getMocks(id),
90+
});
12291
captureResp(req, res, next);
12392
return;
12493
}
12594

12695
// record mode
127-
const ctx = new Context("record");
128-
Context.set(req, ctx);
96+
createExecutionContext({ mode: "record", deps: [], mocks: [] });
12997
captureResp(req, res, next);
13098
};
13199
}
@@ -138,7 +106,7 @@ function captureResp(
138106
const oldSend = res.send;
139107

140108
res.send = (chunk: object) => {
141-
Context.pushResponse(req, chunk);
109+
ResponseBody.push(req, chunk);
142110
return oldSend.apply(res, [chunk]);
143111
};
144112

@@ -157,44 +125,48 @@ export function afterMiddleware(keploy: Keploy, req: Request, res: Response) {
157125

158126
const id = req.get("KEPLOY_TEST_ID");
159127
if (id !== undefined && id !== "") {
160-
const respHeader: { [key: string]: string[] } = getResponseHeader(
128+
const respHeader: { [key: string]: StrArr } = getResponseHeader(
161129
res.getHeaders()
162130
);
163131
const resp = {
164132
status_code: res.statusCode,
165133
header: respHeader,
166134
// @ts-ignore
167-
body: String(Context.getResponse(req)),
135+
body: String(ResponseBody.get(req)),
168136
};
169137
keploy.putResp(id, resp);
170138
return;
171139
}
172140

173141
// req.headers
174-
const reqHeader: { [key: string]: string[] } = getRequestHeader(req.headers);
142+
const reqHeader: { [key: string]: StrArr } = getRequestHeader(req.headers);
175143

176144
// response headers
177-
const respHeader: { [key: string]: string[] } = getResponseHeader(
145+
const respHeader: { [key: string]: StrArr } = getResponseHeader(
178146
res.getHeaders()
179147
);
180148

181149
keploy.capture({
182-
captured: Date.now(),
183-
appId: keploy.appConfig.name,
150+
Captured: Date.now(),
151+
AppID: keploy.appConfig.name,
184152
// change url to uri ex: /url-shortner/:param
185-
uri: req.originalUrl,
186-
httpReq: {
187-
method: req.method,
188-
url: req.originalUrl,
189-
url_params: req.params,
190-
header: reqHeader,
191-
body: JSON.stringify(req.body),
153+
URI: req.originalUrl,
154+
HttpReq: {
155+
Method: req.method,
156+
URL: req.originalUrl,
157+
URLParams: req.params,
158+
Header: reqHeader,
159+
Body: JSON.stringify(req.body),
192160
},
193-
httpResp: {
194-
status_code: res.statusCode,
195-
header: respHeader,
161+
HttpResp: {
162+
StatusCode: res.statusCode,
163+
Header: respHeader,
196164
// @ts-ignore
197-
body: String(Context.getResponse(req)),
165+
Body: String(ResponseBody.get(req)),
198166
},
167+
Dependency: getExecutionContext().context.deps,
168+
TestCasePath: keploy.appConfig.testCasePath,
169+
MockPath: keploy.appConfig.mockPath,
170+
Mocks: getExecutionContext().context.mocks,
199171
});
200172
}

integrations/express/register.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Hook(["express"], function (exports) {
1717
keployApp.use(cors());
1818
keployApp.use(expressMiddleware(keploy));
1919
keployApp.appliedMiddleware = true;
20-
keploy.create();
20+
keploy.runTests();
2121
return keployApp;
2222
}
2323

0 commit comments

Comments
 (0)