Codi is a modern, lightweight JavaScript test framework that runs everywhere - Node.js, browsers, and even in online code editors like CodePen. Built for the modern web with first-class ESM support and zero configuration required. β¨
- π Universal Runtime Support: Runs in Node.js, browsers, and web environments
- π Simple and Expressive API: Intuitive
describeanditfunctions for writing tests - π Rich Assertion Library: Comprehensive set of assertion functions for all testing needs
- π Beautiful Output: Colorful, readable test results with clear success/failure indicators
- π₯οΈ Powerful CLI: Full-featured command-line interface with multiple execution modes
- β‘ Lightning Fast: Optimized for speed with minimal overhead
- π― Browser Testing: Full DOM testing capabilities with Puppeteer integration
- π§ͺ Mocking Support: Built-in HTTP mocking and Node.js test mocking capabilities
- π§ Zero Configuration: Works out of the box, configure only what you need
- π¦ ESM First: Native ECMAScript modules support with backward compatibility
- π¨ CodePen Ready: Special logging utilities for online code editor environments
- π Flexible Test Organization: Support for nested test suites and custom test IDs
- π Detailed Reporting: Comprehensive test results with timing and failure details
Requirements: Node.js 22.0.0 or higher
Install Codi as a development dependency in your project:
npm install --save-dev codi-test-frameworkOr use with other package managers:
# Using yarn
yarn add -D codi-test-framework
# Using pnpm
pnpm add -D codi-test-frameworkCreate a test file (e.g., tests/example.test.mjs):
import { describe, it, assertEqual, assertTrue } from "codi-test-framework";
describe("Math operations", () => {
it("should add two numbers correctly", () => {
const result = 2 + 3;
assertEqual(result, 5, "Addition should work correctly");
});
it("should handle edge cases", () => {
assertTrue(Number.isNaN(0 / 0), "Division by zero should return NaN");
});
});Run your tests:
# Run Node.js tests
npx codi tests
# Run browser tests
npx codi tests --browser
# Run with custom config
npx codi tests --config ./my-config.jsonCreates a test suite to group related tests.
// Simple usage
describe("User Authentication", () => {
// tests go here
});
// Advanced usage with options
describe({ name: "Database Operations", id: "db_ops" }, () => {
// tests go here
});Defines an individual test case.
// Simple usage
it("should validate user credentials", () => {
// test implementation
});
// Advanced usage with options
it({ name: "should handle async operations", parentId: "db_ops" }, async () => {
// async test implementation
});Codi provides a comprehensive set of assertion functions:
Asserts that two values are equal using strict equality (===).
assertEqual(2 + 2, 4, "Math should work");
assertEqual("hello", "hello", "Strings should match");Asserts that two values are not equal.
assertNotEqual(2 + 2, 5, "Math should be correct");Asserts that a value is truthy.
assertTrue(true, "Should be true");
assertTrue("hello", "Non-empty strings are truthy");
assertTrue(42, "Non-zero numbers are truthy");Asserts that a value is falsy.
assertFalse(false, "Should be false");
assertFalse("", "Empty strings are falsy");
assertFalse(0, "Zero is falsy");Asserts that a function throws an error, optionally with a specific message.
assertThrows(
() => {
throw new Error("Something went wrong");
},
"Something went wrong",
"Should throw with correct message",
);
// Just check that it throws
assertThrows(
() => {
JSON.parse("invalid json");
},
undefined,
"Should throw parsing error",
);Asserts that an array contains no duplicate values.
assertNoDuplicates([1, 2, 3, 4], "Array should have unique values");
assertNoDuplicates(["a", "b", "c"], "String array should be unique");Codi provides full browser testing capabilities using Puppeteer under the hood.
# Run all tests in browser environment
npx codi tests --browser
# Run specific test file in browser
npx codi tests/dom.test.mjs --browserimport { describe, it, assertEqual, assertTrue } from "codi-test-framework";
describe({ name: "DOM Manipulation", id: "dom_tests" }, () => {
it(
{ name: "should create and modify elements", parentId: "dom_tests" },
() => {
// Create element
const div = document.createElement("div");
div.textContent = "Hello World";
div.className = "test-element";
// Add to DOM
document.body.appendChild(div);
// Test DOM state
assertEqual(div.tagName, "DIV");
assertEqual(div.textContent, "Hello World");
assertTrue(document.querySelector(".test-element") !== null);
// Cleanup
document.body.removeChild(div);
},
);
it({ name: "should handle events", parentId: "dom_tests" }, () => {
const button = document.createElement("button");
button.textContent = "Click me";
let clicked = false;
button.addEventListener("click", () => {
clicked = true;
});
document.body.appendChild(button);
button.click();
assertTrue(clicked, "Button click should trigger event");
document.body.removeChild(button);
});
});Codi supports testing of modern web APIs:
// Web Components
describe("Web Components", () => {
it("should create custom elements", () => {
class MyButton extends HTMLElement {
connectedCallback() {
this.innerHTML = "<button>Custom Button</button>";
}
}
customElements.define("my-button", MyButton);
const element = document.createElement("my-button");
document.body.appendChild(element);
assertTrue(element.querySelector("button") !== null);
document.body.removeChild(element);
});
});
// Canvas Testing
describe("Canvas Operations", () => {
it("should draw on canvas", () => {
const canvas = document.createElement("canvas");
canvas.width = 100;
canvas.height = 100;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#ff0000";
ctx.fillRect(0, 0, 50, 50);
const imageData = ctx.getImageData(25, 25, 1, 1);
assertEqual(imageData.data[0], 255); // Red component
});
});
// Intersection Observer
describe("Intersection Observer", () => {
it("should observe element visibility", (done) => {
const target = document.createElement("div");
document.body.appendChild(target);
const observer = new IntersectionObserver((entries) => {
const entry = entries[0];
assertTrue(typeof entry.isIntersecting === "boolean");
observer.disconnect();
document.body.removeChild(target);
});
observer.observe(target);
});
});Codi includes powerful mocking capabilities for both Node.js and HTTP testing.
import { mock } from "codi-test-framework";
describe("Function Mocking", () => {
it("should mock functions", () => {
const mockFn = mock.fn();
mockFn.mockReturnValue("mocked result");
const result = mockFn();
assertEqual(result, "mocked result");
assertEqual(mockFn.mock.calls.length, 1);
});
});import { mockHttp } from "codi-test-framework";
describe("HTTP Testing", () => {
it("should mock HTTP requests", () => {
const { req, res } = mockHttp.createMocks({
method: "GET",
url: "/test",
headers: { "content-type": "application/json" },
});
assertEqual(req.method, "GET");
assertEqual(req.url, "/test");
res.status(200).json({ success: true });
assertEqual(res.statusCode, 200);
});
});Codi supports configuration via a codi.json file in your project root:
{
"excludeDirectories": ["node_modules", "dist", "build"],
"preload": "__preload",
"timeout": 5000
}excludeDirectories: Array of directory names to exclude from test discoverypreload: Directory containing files to preload before running teststimeout: Global timeout for tests in milliseconds
# Basic usage
npx codi <test-directory>
# Available options
npx codi tests --browser # Run in browser environment
npx codi tests --quiet # Suppress non-essential output
npx codi tests --returnResults # Return results programmatically
npx codi tests --config ./config.json # Use custom config file
npx codi --version # Show version informationCodi works seamlessly in web environments like CodePen, JSFiddle, and other online editors.
// Import from CDN
import {
describe,
it,
assertEqual,
codepenLogging,
} from "https://esm.sh/codi-test-framework";
// Enable CodePen-friendly logging
codepenLogging.enable();
describe("CodePen Tests", () => {
it("should work in CodePen", () => {
assertEqual(2 + 2, 4, "Math works in CodePen too!");
});
});
// Run tests
codi.runWebTests();<!DOCTYPE html>
<html>
<head>
<title>Codi Browser Tests</title>
</head>
<body>
<script type="module">
import {
describe,
it,
assertEqual,
runWebTests,
} from "https://esm.sh/codi-test-framework";
describe("Browser Tests", () => {
it("should work in browser", () => {
assertEqual(window.location.protocol, "http:");
});
});
runWebTests();
</script>
</body>
</html>describe("Async Operations", () => {
it("should handle promises", async () => {
const result = await Promise.resolve("async result");
assertEqual(result, "async result");
});
it("should handle fetch requests", async () => {
const response = await fetch("/api/data");
assertTrue(response.ok, "Response should be ok");
});
});describe({ name: "User Management", id: "user_mgmt" }, () => {
describe(
{ name: "Authentication", id: "auth", parentId: "user_mgmt" },
() => {
it({ name: "should login user", parentId: "auth" }, () => {
// test implementation
});
},
);
describe(
{ name: "Authorization", id: "authz", parentId: "user_mgmt" },
() => {
it({ name: "should check permissions", parentId: "authz" }, () => {
// test implementation
});
},
);
});describe("Performance Tests", () => {
it("should measure execution time", () => {
const start = performance.now();
// Operation to measure
for (let i = 0; i < 10000; i++) {
Math.random();
}
const end = performance.now();
const duration = end - start;
assertTrue(duration >= 0, "Duration should be positive");
console.log(`Operation took ${duration} milliseconds`);
});
});Codi works great in continuous integration environments:
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install dependencies
run: npm install
- name: Run Node.js tests
run: npx codi tests --returnResults
- name: Run Browser tests
run: npx codi tests --browser --returnResultsCodi's --returnResults flag makes it easy to integrate with any CI platform that checks exit codes.
Check out the examples/ directory for more advanced usage patterns:
- Advanced Browser Testing: Complete examples of DOM, Canvas, Web Components testing
- HTTP Mocking: Comprehensive HTTP testing scenarios
- Performance Testing: Measuring and asserting on performance metrics
- Accessibility Testing: Testing ARIA attributes and keyboard navigation
// Jest
describe("test suite", () => {
test("test case", () => {
expect(value).toBe(expected);
});
});
// Codi
describe("test suite", () => {
it("test case", () => {
assertEqual(value, expected);
});
});// Mocha + Chai
describe("test suite", () => {
it("test case", () => {
expect(value).to.equal(expected);
});
});
// Codi
describe("test suite", () => {
it("test case", () => {
assertEqual(value, expected);
});
});Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Requirements: Node.js 22.0.0 or higher
git clone https://github.com/RobAndrewHurst/codi.git
cd codi
npm install
npm run dev # Build and run testsnpm test # Run Node.js tests
npm run test:browser # Run browser tests
npm run test:all # Run all testsThis project is licensed under the MIT License.
- Enhanced browser testing capabilities
- Added comprehensive web API testing support
- Improved mocking system with HTTP testing
- Better error handling and reporting
- Performance optimizations
- Full CI/CD integration
See GitHub Releases for complete changelog.
Made with β€οΈ by Rob Hurst
Codi - Because testing should be simple, fast, and work everywhere. πΆ