Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,27 @@ dist

# TernJS port file
.tern-port
bun-prisma/
bun-prisma/

# Keploy
.keploy/
keploy-logs.txt

# Docker
docker-compose.override.yml

# Editor settings
.vscode/
.idea/
.DS_Store
Thumbs.db

# Frontend
frontend/.next/
frontend/out/
frontend/node_modules/

# Backend
backend/node_modules/
backend/dist/
backend/keploy-logs.txt
2 changes: 2 additions & 0 deletions dashboard/backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
npm-debug.log
1 change: 1 addition & 0 deletions dashboard/backend/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
KEPLOY_TESTS_PATH=.keploy/tests
17 changes: 17 additions & 0 deletions dashboard/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM node:18

WORKDIR /app

# Install keploy binary
RUN curl -Lo keploy https://github.com/keploy/keploy/releases/latest/download/keploy_linux_amd64 && \
chmod +x keploy && \
mv keploy /usr/local/bin/keploy

COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 8000

CMD ["node", "index.js"]
31 changes: 31 additions & 0 deletions dashboard/backend/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
version: "3.8"

services:
backend:
container_name: keploy-backend
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
volumes:
- .:/app
- ../.keploy:/app/.keploy
working_dir: /app
environment:
- KEPLOY_TESTS_PATH=.keploy/tests
command: node index.js

keploy:
image: ghcr.io/keploy/keploy:latest
container_name: keploy
depends_on:
- backend
command: record -c "node index.js" --cmd-type docker
volumes:
- .:/app
- ../.keploy:/app/.keploy
working_dir: /app
ports:
- "16789:16789"
- "26789:26789"
119 changes: 119 additions & 0 deletions dashboard/backend/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var express_1 = __importDefault(require("express"));
var keploy_1 = require("./keploy");
var cors_1 = __importDefault(require("cors"));
var dotenv_1 = __importDefault(require("dotenv"));
dotenv_1.default.config();
var app = (0, express_1.default)();
var PORT = process.env.PORT || 8000;
app.use((0, cors_1.default)());
app.use(express_1.default.json());
app.get("/tests", function (req, res) { return __awaiter(void 0, void 0, void 0, function () {
var tests, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, (0, keploy_1.getAllTests)()];
case 1:
tests = _a.sent();
res.json(tests);
return [3 /*break*/, 3];
case 2:
error_1 = _a.sent();
console.error("Error fetching tests:", error_1);
res.status(500).json({ error: "Failed to fetch tests" });
return [3 /*break*/, 3];
case 3: return [2 /*return*/];
}
});
}); });
app.get("/tests/:id", function (req, res) { return __awaiter(void 0, void 0, void 0, function () {
var testId, test, error_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
testId = req.params.id;
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, (0, keploy_1.getTestById)(testId)];
case 2:
test = _a.sent();
res.json(test);
return [3 /*break*/, 4];
case 3:
error_2 = _a.sent();
console.error("Error fetching test:", error_2);
res.status(500).json({ error: "Failed to fetch test" });
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
}); });
app.post("/tests/:id/rerun", function (req, res) { return __awaiter(void 0, void 0, void 0, function () {
var testId, result, error_3;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
testId = req.params.id;
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, (0, keploy_1.rerunTest)(testId)];
case 2:
result = _a.sent();
res.json(result);
return [3 /*break*/, 4];
case 3:
error_3 = _a.sent();
console.error("Error rerunning test:", error_3);
res.status(500).json({ error: "Failed to rerun test" });
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
}); });
app.listen(PORT, function () {
console.log("Server is running on http://localhost:".concat(PORT));
});
44 changes: 44 additions & 0 deletions dashboard/backend/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import express, { Request, Response } from 'express';
import { getAllTests, getTestById, rerunTest } from './keploy';
import cors from 'cors';
import dotenv from 'dotenv';

dotenv.config();
const app = express();
const PORT = process.env.PORT || 8000;

app.use(cors());
app.use(express.json());

app.get("/tests", async (req: Request, res: Response) => {
try {
const tests = await getAllTests();
res.json(tests);
} catch (error) {
console.error("Error fetching tests:", error);
res.status(500).json({ error: "Failed to fetch tests" });
}
});
app.get("/tests/:id", async (req: Request, res: Response):Promise<void> => {
const testId = req.params.id;
try {
const test = await getTestById(testId);
res.json(test);
} catch (error) {
console.error("Error fetching test:", error);
res.status(500).json({ error: "Failed to fetch test" });
}
});
app.post("/tests/:id/rerun", async (req: Request, res: Response) => {
const testId = req.params.id;
try {
const result = await rerunTest(testId);
res.json(result);
} catch (error) {
console.error("Error rerunning test:", error);
res.status(500).json({ error: "Failed to rerun test" });
}
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
123 changes: 123 additions & 0 deletions dashboard/backend/keploy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAllTests = getAllTests;
exports.getTestById = getTestById;
exports.rerunTest = rerunTest;
var promises_1 = __importDefault(require("fs/promises"));
var path_1 = __importDefault(require("path"));
var js_yaml_1 = __importDefault(require("js-yaml"));
var child_process_1 = require("child_process");
var TESTS_DIR = path_1.default.resolve(__dirname, process.env.KEPLOY_TESTS_PATH || '');
function getAllTests() {
return __awaiter(this, void 0, void 0, function () {
var files, yamlFiles;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, promises_1.default.readdir(TESTS_DIR)];
case 1:
files = _a.sent();
console.log("Reading test files from:", TESTS_DIR);
yamlFiles = files.filter(function (file) {
return file.endsWith('.yaml') || file.endsWith('.yml');
});
return [2 /*return*/, Promise.all(yamlFiles.map(function (file) { return __awaiter(_this, void 0, void 0, function () {
var raw, data, status;
var _a, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0: return [4 /*yield*/, promises_1.default.readFile(path_1.default.join(TESTS_DIR, file), 'utf-8')];
case 1:
raw = _c.sent();
try {
data = js_yaml_1.default.load(raw);
}
catch (err) {
console.error("\u274C Failed to parse YAML file: ".concat(file), err);
return [2 /*return*/, null];
}
status = 'pending';
if ((data === null || data === void 0 ? void 0 : data.status) === 1 || (data === null || data === void 0 ? void 0 : data.status) === 'pass')
status = 'passed';
else if ((data === null || data === void 0 ? void 0 : data.status) === 0 || (data === null || data === void 0 ? void 0 : data.status) === 'fail')
status = 'failed';
return [2 /*return*/, {
id: file.replace('.yaml', ''),
route: ((_a = data === null || data === void 0 ? void 0 : data.req) === null || _a === void 0 ? void 0 : _a.url) || '',
method: ((_b = data === null || data === void 0 ? void 0 : data.req) === null || _b === void 0 ? void 0 : _b.method) || '',
status: status,
}];
}
});
}); })).then(function (results) { return results.filter(Boolean); })];
}
});
});
}
function getTestById(id) {
return __awaiter(this, void 0, void 0, function () {
var filePath, raw;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
filePath = path_1.default.join(TESTS_DIR, "".concat(id, ".yaml"));
return [4 /*yield*/, promises_1.default.readFile(filePath, 'utf-8')];
case 1:
raw = _a.sent();
return [2 /*return*/, js_yaml_1.default.load(raw)];
}
});
});
}
function rerunTest(id) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, new Promise(function (resolve, reject) {
(0, child_process_1.exec)("keploy test --id ".concat(id), function (err, stdout, stderr) {
if (err)
return reject(stderr);
resolve(stdout);
});
})];
});
});
}
Loading