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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@scom/test

v0.1.7

Published

Test runner for @scom packages

Readme

@scom/test

A lightweight, fast test runner for @scom packages built with TypeScript and Node.js. Designed to be a simple alternative to Jest with zero external dependencies.

Features

  • 🚀 Zero dependencies: Uses only Node.js built-in modules (plus TypeScript and jsdom for execution)
  • 📝 TypeScript first: Native TypeScript support with automatic transpilation
  • 🧪 Jest-compatible API: Familiar testing syntax (describe, it, expect, etc.)
  • Fast execution: Lightweight runtime with minimal overhead
  • 📊 Clear reporting: Detailed test results with timing and error information
  • 🎯 Focused testing: Run specific files, directories, or patterns
  • 🔧 CLI and programmatic: Use from command line or import as a module

Installation

npm install @scom/test --save-dev

Note: Use npx @scom/test to run the CLI - npm automatically resolves this to the correct binary.

Quick Start

1. Write a test file

// tests/math.test.ts
describe('Math utilities', () => {
  it('should add numbers correctly', () => {
    expect(1 + 1).toBe(2);
  });

  it('should handle async operations', async () => {
    const result = await Promise.resolve(42);
    expect(result).toBe(42);
  });
});

2. Run tests

# Run all tests
npx @scom/test

# Run specific test file
npx @scom/test tests/math.test.ts

# Run with verbose output
npx @scom/test --verbose

Usage

Command Line Interface

# Run all TypeScript tests in current directory
npx @scom/test

# Run tests in specific directory
npx @scom/test tests/

# Run specific test file
npx @scom/test tests/server.test.ts

# Run with options
npx @scom/test --verbose --timeout 30000 tests/

# Show help
npx @scom/test --help

Shell Compatibility

The @scom/test package uses npm's scoped package resolution, which automatically maps to the correct binary:

Recommended usage:

npx @scom/test --verbose tests/

Alternative (via npm scripts):

# In package.json
{
  "scripts": {
    "test": "@scom/test"
  }
}

# Then run:
npm test
npm run test -- --verbose  # Pass additional arguments

Package.json Scripts

{
  "scripts": {
    "test": "@scom/test",
    "test:verbose": "@scom/test --verbose"
  }
}

Programmatic API

import { runTestFile, findTestFiles, TestResult } from '@scom/test';

// Run a specific test file
const result: TestResult = await runTestFile('./tests/server.test.ts');
console.log(`Tests: ${result.passed}/${result.total}`);

// Find and run all test files
const testFiles = await findTestFiles('./tests');
for (const file of testFiles) {
  const result = await runTestFile(file);
  console.log(`${file}: ${result.passed}/${result.total} passed`);
}

Test API Reference

Test Structure

describe('Test Suite', () => {
  // Setup and teardown
  beforeAll(async () => {
    // Run once before all tests in this suite
  });

  afterAll(async () => {
    // Run once after all tests in this suite
  });

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

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

  // Test cases
  it('should do something', () => {
    expect(true).toBe(true);
  });

  test('alternative syntax for it', async () => {
    const result = await someAsyncFunction();
    expect(result).toEqual({ success: true });
  });

  // Nested test suites
  describe('Nested suite', () => {
    it('can be nested', () => {
      expect(1).toBeLessThan(2);
    });
  });
});

Test Timeouts

Control test execution timeouts using scomTest.setTimeout():

// Set timeout for individual tests
it('should complete within custom timeout', async () => {
  scomTest.setTimeout(5000); // 5 seconds for this test only
  
  const result = await longRunningOperation();
  expect(result).toBeDefined();
});

// Set timeout in beforeEach for all tests in a suite
describe('Long running operations', () => {
  beforeEach(() => {
    scomTest.setTimeout(10000); // 10 seconds for all tests in this suite
  });

  it('should handle slow database queries', async () => {
    const data = await database.complexQuery();
    expect(data).toHaveLength(100);
  });

  it('should process large files', async () => {
    const result = await fileProcessor.processLargeFile();
    expect(result.success).toBe(true);
  });
});

// Set timeout for specific async operations
test('should timeout appropriately', async () => {
  // Default timeout applies
  await expect(quickOperation()).resolves.toBeDefined();
  
  // Custom timeout for slow operation
  scomTest.setTimeout(15000);
  await expect(verySlowOperation()).resolves.toBeDefined();
  
  // Reset to default timeout
  scomTest.setTimeout(30000); // or omit to use global default
});

Timeout Behavior

  • Default timeout: 30 seconds (30000ms) for all tests
  • Global timeout: Set via CLI --timeout option
  • Test-specific timeout: Use scomTest.setTimeout() within test functions
  • Suite-wide timeout: Use scomTest.setTimeout() in beforeEach or beforeAll
  • Scope: Timeout changes only affect the current test or suite
  • Reset: Timeout automatically resets to default after each test

Best Practices

describe('API Integration Tests', () => {
  // Set longer timeout for all integration tests
  beforeAll(() => {
    scomTest.setTimeout(60000); // 1 minute for integration tests
  });

  it('should authenticate user', async () => {
    // This test inherits the 60-second timeout
    const token = await authService.login(credentials);
    expect(token).toBeDefined();
  });

  it('should handle network delays', async () => {
    // Override with even longer timeout for this specific test
    scomTest.setTimeout(120000); // 2 minutes
    
    const response = await api.callSlowEndpoint();
    expect(response.status).toBe(200);
  });

  it('should use default timeout', async () => {
    // Back to suite default (60 seconds)
    const data = await api.getFastData();
    expect(data).toBeDefined();
  });
});

Matchers

// Equality
expect(value).toBe(expected);           // Strict equality (===)
expect(value).toEqual(expected);        // Deep equality
expect(value).not.toBe(expected);       // Negation

// Truthiness
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(value).toBeNull();
expect(value).toBeUndefined();
expect(value).toBeDefined();

// Numbers
expect(number).toBeGreaterThan(3);
expect(number).toBeLessThan(10);
expect(number).toBeCloseTo(3.14, 2);    // Floating point comparison

// Strings
expect(string).toMatch(/pattern/);
expect(string).toContain('substring');

// Arrays and Objects
expect(array).toContain(item);
expect(array).toHaveLength(3);

// Exceptions
expect(() => {
  throw new Error('test');
}).toThrow();
expect(() => {
  throw new Error('specific message');
}).toThrow('specific message');

// Async
await expect(promise).resolves.toBe(value);
await expect(promise).rejects.toThrow();

CLI Options

| Option | Alias | Description | Default | |--------|-------|-------------|---------| | --verbose | -v | Show detailed output including passing tests | false | | --pattern | -p | Test file pattern | **/*.test.{ts,tsx} | | --testNamePattern | -t | Filter tests by test name pattern | - | | --timeout | - | Test timeout in milliseconds | 30000 | | --bail | -b | Stop on first test failure | false | | --help | -h | Show help message | - |

Configuration

The test runner currently uses default configuration. Configuration file support may be added in future versions.

Migration from Jest

  1. Install @scom/test:

    npm uninstall jest @types/jest
    npm install @scom/test --save-dev
  2. Update package.json:

    {
      "scripts": {
        "test": "@scom/test"
      }
    }
  3. Convert test files: Ensure all test files use .test.ts extension

  4. Remove Jest config: Delete jest.config.js and Jest configuration from package.json

  5. Update imports: Most Jest APIs are compatible, but check for any Jest-specific utilities

Examples

Basic Test

// tests/calculator.test.ts
class Calculator {
  add(a: number, b: number): number {
    return a + b;
  }
}

describe('Calculator', () => {
  let calc: Calculator;

  beforeEach(() => {
    calc = new Calculator();
  });

  it('should add two numbers', () => {
    expect(calc.add(2, 3)).toBe(5);
  });

  it('should handle negative numbers', () => {
    expect(calc.add(-1, 1)).toBe(0);
  });
});

Async Test

// tests/api.test.ts
describe('API calls', () => {
  it('should fetch data', async () => {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    
    expect(response.status).toBe(200);
    expect(data).toHaveProperty('results');
  });

  it('should handle errors', async () => {
    await expect(
      fetch('https://api.example.com/invalid')
    ).rejects.toThrow();
  });

  it('should handle slow API calls', async () => {
    // Set custom timeout for slow API
    scomTest.setTimeout(60000); // 1 minute
    
    const response = await fetch('https://slow-api.example.com/data');
    expect(response.status).toBe(200);
  });
});

Mock Functions

Basic mock functionality is available:

// tests/service.test.ts
describe('UserService', () => {
  it('should call the API', () => {
    const mockFetch = scomTest.fn().mockResolvedValue({
      ok: true,
      json: () => Promise.resolve({ id: 1, name: 'John' })
    });
    
    global.fetch = mockFetch;
    
    // Test your service
    expect(mockFetch).toHaveBeenCalledWith('/api/users/1');
  });
});

Troubleshooting

Common Issues

  1. TypeScript compilation errors: Ensure tsconfig.json is properly configured
  2. Module resolution: Use relative imports or configure path mapping
  3. Async test timeouts: Use scomTest.setTimeout() to increase timeout or CLI --timeout option
  4. File not found: Check test file patterns and paths

Debug Mode

Run with verbose output to see detailed information:

npx @scom/test --verbose tests/

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for your changes
  4. Run the test suite: npm test
  5. Submit a pull request