npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@easytesttool/easytest

v2.1.0

Published

A simple and intuitive test automation framework - Pure EasyTest Technology! No Playwright, No Puppeteer.

Readme


✨ Features

| Feature | Description | |---------|-------------| | 🚀 Zero Dependencies | Uses native browser protocols (CDP, Marionette). No heavy downloads. | | ⚡ Blazing Fast | Direct browser communication for faster test execution. | | 📱 Mobile Ready | Native iOS/Android testing + device emulation. | | 🌐 Cross-Browser | Chrome, Firefox, Safari, Edge with same API. | | 📊 Beautiful Reports | Built-in Allure reports with pie charts. | | 📧 Notifications | Email, Slack, Teams integration included. | | 🔄 Parallel Testing | Workers, sharding, and retry out of the box. | | 🎯 TypeScript First | Full type safety and IntelliSense. |

📦 Installation

npm install @easytesttool/easytest

🚀 Quick Start

import { describe, test, expect, createBrowser, beforeAll, afterAll } from '@easytesttool/easytest';

describe('My First Test', () => {
  let browser;

  beforeAll(async () => {
    browser = createBrowser();
    await browser.launch();
  });

  afterAll(async () => {
    await browser.close();
  });

  test('should navigate to example.com', async () => {
    await browser.goto('https://example.com');
    
    const title = await browser.title();
    expect(title).toContain('Example');
    
    const heading = await browser.$('h1');
    expect(await heading.text()).toBe('Example Domain');
  });
});
# Run tests
npx easytest run

# Run with browser visible
npx easytest run --headed

# Run on specific browser
npx easytest run --browser firefox

📱 Mobile Testing

// Device emulation
await browser.launch({ device: 'iPhone 14' });

// Native iOS app testing
import { iOSDriver } from '@easytesttool/easytest';
const ios = new iOSDriver();
await ios.launch('com.example.app');

// Native Android app testing
import { AndroidDriver } from '@easytesttool/easytest';
const android = new AndroidDriver();
await android.launch('com.example.app');

📧 Built-in Notifications

import { NotificationManager } from '@easytesttool/easytest';

// Configure once
NotificationManager.configure({
  slack: { webhookUrl: process.env.SLACK_WEBHOOK_URL },
  teams: { webhookUrl: process.env.TEAMS_WEBHOOK_URL },
  email: { smtp: { host: 'smtp.gmail.com', ... }, from: '[email protected]' }
});

// Send to all channels
await NotificationManager.sendResultsToAll({
  total: 100,
  passed: 95,
  failed: 5,
  duration: '5m 30s'
});

📊 Allure Reports

import { AllureReporter } from '@easytesttool/easytest';

const reporter = new AllureReporter({ outputDir: './allure-results' });

// Automatically generates beautiful reports with:
// - Pie charts for pass/fail distribution
// - Test timelines
// - Screenshots on failure
// - Step-by-step breakdown

⚡ Parallel Execution

# Run with 4 workers
npx easytest run --workers 4

# Shard across machines (CI/CD)
npx easytest run --shard 1/4
npx easytest run --shard 2/4
npx easytest run --shard 3/4
npx easytest run --shard 4/4

🔧 Configuration

Create easytest.config.ts:

import { defineConfig } from '@easytesttool/easytest';

export default defineConfig({
  testDir: './tests',
  browser: 'chrome',
  headless: true,
  timeout: 30000,
  retries: 2,
  workers: 4,
  reporter: ['html', 'allure']
});

📚 Documentation

Visit EasyTest Docs for full documentation:

🤝 Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines.

📄 License

MIT © EasyTest Team


🌐 Multi-Browser Testing

# Chrome (default)
npm run spec

# Firefox (requires geckodriver)
npm run spec:firefox

# Safari (macOS only, requires safaridriver)
npm run spec:safari

📦 Installation

npm install easytest

🚀 Quick Start

1. Create a test file

// tests/my-first-test.test.js
const { describe, test, expect, createBrowser, beforeAll, afterAll } = require('easytest');

describe('My First Test Suite', () => {
  let browser;

  beforeAll(async () => {
    browser = createBrowser({ headless: true });
    await browser.launch();
  });

  afterAll(async () => {
    await browser.close();
  });

  test('should navigate to a website', async () => {
    await browser.goto('https://example.com');
    
    const title = await browser.title();
    expect(title).toContain('Example');
  });

  test('should find and click elements', async () => {
    const heading = await browser.find('h1');
    const text = await heading.text();
    
    expect(text).toBe('Example Domain');
  });
});

2. Run your tests

# Quick run commands (npm scripts)
npm run spec                    # Run all tests in headed mode (browser visible)
npm test                        # Run all tests in headless mode
npm run test:headed             # Same as npm run spec

# CLI commands
npx easytest run                # Run all tests
npx easytest run --headed       # Run with browser visible
npx easytest run --headed --slow-mo 500   # Slow motion for debugging

🖥️ NPM Scripts

| Command | Description | |---------|-------------| | npm run spec | Run all tests in headed mode (browser visible) | | npm test | Run all tests in headless mode | | npm run test:headed | Same as npm run spec | | npm run build | Compile TypeScript to JavaScript | | npm run watch | Watch mode for development |

📖 API Reference

Browser Methods

Navigation

// Navigate to URL
await browser.goto('https://example.com');

// Go back/forward
await browser.goBack();
await browser.goForward();

// Reload page
await browser.reload();

// Get current URL
const url = browser.url();

// Get page title
const title = await browser.title();

Finding Elements

// By CSS selector
const element = await browser.find('#submit-button');
const elements = await browser.findAll('.list-item');

// By text content
const button = await browser.findByText('Submit');

// By placeholder
const input = await browser.findByPlaceholder('Enter email');

// By label
const field = await browser.findByLabel('Email Address');

// By test ID
const component = await browser.findByTestId('login-form');

// By role
const btn = await browser.findByRole('button', { name: 'Submit' });

Actions

// Click
await browser.click('#button');

// Double click
await browser.doubleClick('#item');

// Type text
await browser.type('#input', 'Hello World');

// Clear and type (fill)
await browser.fill('#input', 'New value');

// Select dropdown option
await browser.select('#country', 'USA');

// Check/uncheck checkbox
await browser.check('#agree');
await browser.uncheck('#newsletter');

// Hover
await browser.hover('#menu');

// Press key
await browser.press('Enter');

Waiting

// Wait for element
await browser.waitFor('#loading', { hidden: true });

// Wait for navigation
await browser.waitForNavigation();

// Wait for URL
await browser.waitForUrl('/dashboard');

// Wait fixed time (not recommended)
await browser.wait(1000);

Screenshots

// Take screenshot
const path = await browser.screenshot();

// Full page screenshot
await browser.screenshot({ fullPage: true });

// Custom path
await browser.screenshot({ path: './my-screenshot.png' });

Element Methods

const element = await browser.find('#my-element');

// Actions
await element.click();
await element.doubleClick();
await element.rightClick();
await element.type('text');
await element.fill('text');
await element.clear();
await element.hover();
await element.focus();
await element.blur();
await element.scrollIntoView();

// Form elements
await element.check();
await element.uncheck();
await element.select('value');
await element.selectByText('Option Text');
await element.uploadFile('/path/to/file');

// Get information
const text = await element.text();
const html = await element.html();
const value = await element.value();
const attr = await element.getAttribute('href');
const css = await element.getCssValue('color');

// State checks
const isVisible = await element.isVisible();
const isHidden = await element.isHidden();
const isEnabled = await element.isEnabled();
const isDisabled = await element.isDisabled();
const isChecked = await element.isChecked();
const isFocused = await element.isFocused();
const hasClass = await element.hasClass('active');
const hasAttr = await element.hasAttribute('disabled');
const containsText = await element.containsText('hello');

// Child elements
const child = await element.find('.child');
const children = await element.findAll('.children');
const parent = await element.parent();

Assertions

Basic Assertions

expect(value).toBe(expected);
expect(value).toEqual(expected);
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(value).toBeNull();
expect(value).toBeUndefined();
expect(value).toBeDefined();

Number Assertions

expect(5).toBeGreaterThan(3);
expect(5).toBeGreaterThanOrEqual(5);
expect(3).toBeLessThan(5);
expect(3).toBeLessThanOrEqual(3);
expect(0.1 + 0.2).toBeCloseTo(0.3);

String Assertions

expect('hello world').toContain('world');
expect('hello').toStartWith('hel');
expect('hello').toEndWith('lo');
expect('hello').toMatch(/^hel/);

Array Assertions

expect([1, 2, 3]).toHaveLength(3);
expect([]).toBeEmpty();
expect([1, 2, 3]).toContain(2);
expect([1, 2, 3]).toContainAll([1, 3]);

Object Assertions

expect({ a: 1 }).toHaveProperty('a');
expect({ a: 1 }).toHaveProperty('a', 1);
expect({ a: 1, b: 2 }).toMatchObject({ a: 1 });

Negation

expect(5).not.toBe(3);
expect('hello').not.toContain('world');

Element Assertions

const element = await browser.find('#button');

await expect(element).toBeVisible();
await expect(element).toBeHidden();
await expect(element).toBeEnabled();
await expect(element).toBeDisabled();
await expect(element).toBeChecked();
await expect(element).toBeFocused();
await expect(element).toHaveText('Click me');
await expect(element).toContainText('Click');
await expect(element).toHaveValue('hello');
await expect(element).toHaveAttribute('type', 'submit');
await expect(element).toHaveClass('btn-primary');

Page Assertions

expectPage(browser).toHaveUrl('https://example.com');
await expectPage(browser).toHaveTitle('Example Domain');
await expectPage(browser).toContainText('Welcome');

Test Hooks

describe('My Suite', () => {
  beforeAll(async () => {
    // Runs once before all tests
  });

  afterAll(async () => {
    // Runs once after all tests
  });

  beforeEach(async () => {
    // Runs before each test
  });

  afterEach(async () => {
    // Runs after each test
  });

  test('my test', async () => {
    // Test code
  });
});

Test Modifiers

// Skip a test
test.skip('skipped test', async () => {});

// Run only this test
test.only('focused test', async () => {});

// Skip a suite
describe.skip('skipped suite', () => {});

// Run only this suite
describe.only('focused suite', () => {});

Steps

test('complex test with steps', async () => {
  await step('Navigate to login page', async () => {
    await browser.goto('/login');
  });

  await step('Fill in credentials', async () => {
    await browser.fill('#email', '[email protected]');
    await browser.fill('#password', 'password');
  });

  await step('Submit form', async () => {
    await browser.click('#submit');
    await browser.waitForNavigation();
  });
});

⚙️ Configuration

Create an easytest.config.js file in your project root:

module.exports = {
  // Browser settings
  browser: 'chromium',
  headless: true,
  slowMo: 0,
  width: 1280,
  height: 720,
  
  // Test settings
  testMatch: ['**/*.test.ts', '**/*.test.js'],
  testIgnore: ['**/node_modules/**'],
  timeout: 30000,
  retries: 0,
  
  // Reporter settings
  reporter: 'html',
  outputDir: './test-results',
  screenshotsDir: './screenshots',
  
  // Base URL
  baseUrl: 'http://localhost:3000',
};

🖥️ CLI Commands

# Run all tests
npx easytest run

# Run specific test files
npx easytest run --spec "tests/login.test.js"

# Run tests matching a pattern
npx easytest run --grep "login"

# Run in headed mode (see the browser)
npx easytest run --headed

# Slow down execution (great for debugging)
npx easytest run --headed --slow-mo 500

# Set retries for flaky tests
npx easytest run --retries 2

# Generate HTML report
npx easytest run --reporter html

# Initialize EasyTest in a project
npx easytest init

CLI Options

| Option | Description | |--------|-------------| | --spec <pattern> | Test file pattern to run | | --headed | Run browser in visible mode | | --slow-mo <ms> | Slow down operations by milliseconds | | --grep <pattern> | Filter tests by name | | --retries <n> | Retry failed tests n times | | --timeout <ms> | Test timeout in milliseconds | | --reporter <type> | Reporter: console, html, json, all | | --output <dir> | Output directory for reports |

📊 Reporters

Console Reporter (default)

Outputs results to the terminal with colors.

HTML Reporter

Generates a beautiful HTML report.

npx easytest run --reporter html

JSON Reporter

Generates a JSON file with test results.

npx easytest run --reporter json

All Reporters

Generate all report types.

npx easytest run --reporter all

🎯 Best Practices

  1. Use descriptive test names

    test('should display error message when login fails', async () => {});
  2. Use data-testid for reliable selectors

    await browser.findByTestId('login-button');
  3. Use steps for complex tests

    await step('Fill login form', async () => {});
  4. Clean up in afterEach/afterAll

    afterAll(async () => {
      await browser.close();
    });
  5. Avoid hard-coded waits

    // ❌ Bad
    await browser.wait(5000);
       
    // ✅ Good
    await browser.waitFor('#element');

📝 License

MIT

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.


📚 Documentation


Made with ❤️ for developers who want simple, effective testing.