JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 36
  • Score
    100M100P100Q52309F
  • License MIT

☕ Latte — Lightweight Flow-Based Testing Framework for real browser testing

Package Exports

  • latte-test
  • latte-test/expect

Readme

☕ Latte — Lightweight Flow-Based Testing Framework

Latte is a super simple testing framework designed to test real websites (like login, cart, or other user flows) in a readable, beginner-friendly way using real browser automation.

🌟 Features

  • Real Browser Testing: Uses Puppeteer + Chromium to test actual websites
  • Test Any Website: localhost, staging, production, public sites - all work
  • Readable: Tests look like plain English, easy to understand
  • Minimal: No complex setup, imports, or configuration files
  • Regression detection: If a deployment breaks a flow, the test fails automatically
  • Lightweight: Just Puppeteer + simple API
  • Headless by default: Fast execution, optional browser window for debugging

🚀 Quick Start

Installation

# In any Node.js project
npm install latte-test

# Optional: For TypeScript/.tsx support
npm install tsx

Latte automatically installs Puppeteer, which downloads Chromium (~170MB) for browser automation.

Quick Start (30 seconds)

  1. Create a test file anywhere (no special folder required):
// login.test.js
  import { latte } from "latte-test";

latte("my website works", async (app) => {
  await app.open("https://example.com");
  await app.see("Example Domain");
});
  1. Run tests:
npx latte
  1. Watch Latte test your real website!

Write Your First Test

Create a test file with any supported naming pattern:

  • .latte.js/.latte.ts/.latte.tsx (recommended - Latte branding)
  • .test.js/.test.ts/.test.tsx (standard convention)
  • .spec.js/.spec.ts/.spec.tsx (specification style)

Examples:

  • login.latte.js - Latte test in JavaScript
  • auth.latte.ts - Latte test in TypeScript
  • component.latte.tsx - Latte test in TypeScript + JSX
  • cart.test.js - Standard test naming
  • api.spec.ts - Specification style

Note: .ts and .tsx files require the tsx package: npm install tsx

Example (login.test.js):

import { latte } from "latte-test";

// Test your production website
latte("user can log in to my site", async (app) => {
  await app.open("https://mysite.com/login");
  await app.type("#email", "test@example.com");
  await app.type("#password", "mypassword");
  await app.click("#login-button");
  await app.see("Welcome back!");
});

// Test your localhost during development
latte("localhost login works", async (app) => {
  await app.open("http://localhost:3000/login");
  await app.type("#email", "dev@test.com");
  await app.click("#submit");
  await app.see("Dashboard");
});

Run Tests

npx latte

Output:

☕ Latte - Lightweight Flow-Based Testing Framework

🔍 Found 1 test file(s):
   login.test.js

📄 Running login.test.js:
🧪 Running Latte tests...

✅ user can log in to my site
✅ localhost login works

==================================================
📊 Results: 2 passed, 0 failed
🎉 All tests passed!

What just happened?

  • Latte launched real Chromium browser (headless)
  • Made actual HTTP requests to your websites
  • Interacted with real DOM elements
  • Validated actual page content
  • Detected real issues if any exist

📖 Core Concepts

1. Latte Test

latte("description", async (app) => {
  // test logic here
});
  • description: A short human-readable description of the test
  • app: A real browser instance to perform user actions

2. App Actions

Action Description
app.open(url) Navigate to any URL (real browser navigation)
app.type(selector, value) Type text into an input field (real typing)
app.click(selector) Click an element (real mouse click)
app.see(text) Assert that text exists on the page (real content check)

3. Assertions

import { expect } from "latte-test";

expect(value).toBe(expected);
expect(value).not.toBe(expected);
expect(value).toEqual(expected);
expect(value).toContain(substring);
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(value).toBeNull();
expect(value).toBeUndefined();
expect(fn).toThrow("Error message");

📝 Examples

Real Website Testing

import { latte, group } from "latte-test";

group("Production Tests", () => {
  latte("SauceDemo login works", async (app) => {
    await app.open("https://www.saucedemo.com/");
    await app.type("#user-name", "standard_user");
    await app.type("#password", "secret_sauce");
    await app.click("#login-button");
    await app.see("Products");
  });

  latte("my production site works", async (app) => {
    await app.open("https://mysite.com/login");
    await app.type("#email", "test@example.com");
    await app.type("#password", "mypassword");
    await app.click("#login-btn");
    await app.see("Dashboard");
  });
});

Development Testing

import { latte } from "latte-test";

// Test your localhost during development
latte("localhost signup flow", async (app) => {
  await app.open("http://localhost:3000/signup");
  await app.type("#name", "John Doe");
  await app.type("#email", "john@example.com");
  await app.click("#create-account");
  await app.see("Account created successfully");
});

Using Expect Assertions

import { latte, expect } from "latte-test";

latte("math works correctly", async (app) => {
  expect(2 + 2).toBe(4);
  expect("hello world").toContain("world");
  expect([1, 2, 3]).toContain(2);
  expect(true).toBeTruthy();
  expect(() => {
    throw new Error("Oops!");
  }).toThrow("Oops!");
});

🏗️ Smart Test Discovery

Latte finds tests with intelligent prioritization:

your-project/
├── package.json
└── tests/                  # ⚡ Searched first (fastest)
    ├── login.test.js
    ├── cart.latte.ts
    └── auth.spec.tsx

🔍 Also Supported:

your-project/
├── package.json
├── test/                   # ✅ Common folder
├── __tests__/              # ✅ React/Jest style  
├── e2e/                    # ✅ End-to-end tests
├── login.test.js           # ✅ Root level
└── src/
    └── components.test.tsx # ✅ Alongside source

Search Priority: tests/test/__tests__/e2e/ → everywhere else

Run npx latte and it finds them all! 🔍

🌐 Browser Options

Latte runs in headless mode by default (no visible browser window) for fast execution. You can customize this:

🔧 Default (Headless)

latte("runs invisibly", async (app) => {
  await app.open("https://mysite.com");
  await app.type("#email", "test@example.com");
  await app.click("#submit");
  await app.see("Success!");
}); // Runs in background, no window visible

👀 Show Browser Window

latte("watch the test run", async (app) => {
  await app.open("https://mysite.com");
  await app.type("#email", "test@example.com");
  await app.click("#submit");
  await app.see("Success!");
}, { 
  headless: false,    // Show Chromium window
  timeout: 10000      // Custom timeout (default: 5000ms)
});

🎯 When to Use Each Mode:

  • Headless (default): CI/CD, automated testing, production monitoring
  • Visible browser: Development, debugging, demos, watching tests run

📘 TypeScript Support

Latte works seamlessly with TypeScript! Just use .ts or .tsx extensions:

// auth.test.ts
import { latte, expect } from "latte-test";

interface User {
  username: string;
  email: string;
}

latte("user registration works", async (app) => {
  await app.open("/register");
  await app.type("#username", "newuser");
  await app.type("#email", "user@example.com");
  await app.type("#password", "secure123");
  await app.click("#register-button");
  await app.see("Registration successful");
  
  // TypeScript assertions
  const state = app.getState();
  expect(state.username).toBe("newuser");
});

Note: For TypeScript support, install tsx and optionally @types/node:

npm install tsx @types/node

Puppeteer is installed automatically with Latte.

🎯 Philosophy

Flow-First Approach

Tests follow the pattern: Open → Type → Click → See

latte("user completes checkout", async (app) => {
  await app.open("https://mystore.com/checkout");     // Open
  await app.type("#email", "user@example.com");       // Type
  await app.click("#submit");                          // Click
  await app.see("Order confirmed");                    // See
});

Readable Tests

Tests should read like step-by-step instructions that anyone can understand:

// ✅ Good - reads like instructions
latte("user can reset password", async (app) => {
  await app.open("https://mysite.com/forgot-password");
  await app.type("#email", "user@example.com");
  await app.click("#send-reset");
  await app.see("Reset email sent");
});

// ❌ Avoid - too technical
latte("POST /auth/reset returns 200", async (app) => {
  // complex API testing setup...
});

🔧 Advanced Usage

Optional Grouping

import { group, latte } from "latte-test";

group("Authentication", () => {
  latte("login works", async (app) => { /* ... */ });
  latte("logout works", async (app) => { /* ... */ });
});

group("Shopping", () => {
  latte("add to cart", async (app) => { /* ... */ });
  latte("checkout", async (app) => { /* ... */ });
});

Debugging Tests

latte("debug example", async (app) => {
  await app.open("https://mysite.com/login");
  await app.type("#username", "user");
  
  // Debug: Check current page state
  console.log("Current URL:", await app.getCurrentUrl());
  console.log("Page content:", await app.getContent());
  console.log("Interaction logs:", app.getLogs());
  
  // Take screenshot for debugging
  await app.screenshot("debug-login.png");
  
  await app.click("#login-button");
  await app.see("Welcome back, user!");
}, { headless: false }); // Show browser for debugging

🤝 Contributing

Latte is designed to be simple and focused. When contributing:

  1. Keep the API minimal and readable
  2. Maintain the "beginner-friendly" philosophy
  3. Ensure tests read like plain English
  4. Add examples for new features

📄 License

MIT License - feel free to use Latte in your projects!


Happy Testing! ☕

Latte makes testing flows as smooth as your morning coffee.