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

@majkapp/plugin-test

v1.1.0

Published

Beautiful, developer-friendly testing framework for MAJK plugins

Readme

@majkapp/plugin-test

Beautiful, developer-friendly testing framework for MAJK plugins.

Features

  • 🎯 Simple & Intuitive - Clean API inspired by modern testing frameworks
  • 🚀 Fast - Built on uvu, one of the fastest test runners
  • 🎨 Beautiful Output - Rich console output with colors and formatting
  • 📊 Multiple Reporters - Console, JSON, and HTML reports
  • 🔧 Plugin-Aware - Built-in helpers for MAJK plugin testing
  • 🧪 Mock Support - Easy mocking of plugin contexts and auth
  • 🎭 Function Testing - Direct function invocation testing
  • 🌐 UI Testing - Browser-based UI tests with Playwright and screenshot capture

Installation

npm install --save-dev @majkapp/plugin-test

Quick Start

Initialize Test Structure

npx majk-test init

This creates:

tests/
  plugin/
    functions/
      unit/      # Mock context and data
      e2e/       # Real auth and data
    ui/
      unit/      # Mock browser
      e2e/       # Real browser and auth

Write Your First Test

Create tests/plugin/functions/unit/health.test.js:

import { test, assert, invoke } from '@majkapp/plugin-test';

test('health function returns ok', async () => {
  const result = await invoke('health', {});
  assert.is(result.status, 'ok');
});

test('health includes timestamp', async () => {
  const result = await invoke('health', {});
  assert.ok(result.timestamp);
});

Run Tests

npx majk-test

API Reference

Core Functions

test(name, fn)

Define a test:

test('should do something', async () => {
  // test code
});

assert

Assertions from uvu/assert:

assert.is(actual, expected)
assert.ok(value)
assert.not(actual, expected)
assert.equal(actual, expected)  // deep equality
assert.throws(() => { ... })

Mock Helpers

mock(options?)

Create a mock plugin context:

import { mock } from '@majkapp/plugin-test';

const context = mock()
  .storage({ counter: 10 })
  .config({ apiKey: 'test-key' })
  .conversation('conv-123')
  .user('user-456')
  .build();

With options:

const context = mock({
  scenario: 'empty',
  storage: { counter: 10 },
  recordCalls: true
}).build();

Available scenarios:

  • empty() - Empty context
  • withBasicAuth() - Basic auth setup
  • withStorage() - Pre-populated storage

auth

Configure authentication:

import { auth } from '@majkapp/plugin-test';

// From environment variables
mock().auth(auth.fromEnv('PLUGIN')).build()

// From endpoint
mock().auth(auth.fromEndpoint('http://localhost:3000/auth')).build()

// Manual account
mock().auth(auth.manual('[email protected]', 'password')).build()

// With tokens
mock().auth(auth.withTokens(
  '[email protected]',
  'password',
  'access-token',
  'refresh-token'
)).build()

Invoke Helpers

invoke(functionName, params, options?)

Invoke a plugin function:

import { invoke } from '@majkapp/plugin-test';

const result = await invoke('myFunction', { arg: 'value' });

With options:

const result = await invoke('myFunction', params, {
  timeout: 5000,
  expectSuccess: true
});

invokeAll(functionNames, params, options?)

Invoke multiple functions:

const results = await invokeAll(['func1', 'func2'], params);

CLI Options

Basic Usage

npx majk-test [options]

Options

  • --plugin-dir, -d <dir> - Plugin directory (default: current directory)
  • --type, -t <type> - Test type: functions, ui, or all (default: all)
  • --env, -e <env> - Environment: unit, e2e, or all (default: all)
  • --include, -i <pattern> - Include pattern (can be repeated)
  • --exclude, -x <pattern> - Exclude pattern (can be repeated)
  • --reporter, -r <reporter> - Reporter: console, json, html (default: console)
  • --output, -o <file> - Output file for JSON/HTML reporters
  • --bail, -b - Stop on first failure
  • --timeout <ms> - Test timeout in milliseconds
  • --verbose, -v - Verbose output
  • --config, -c <file> - Config file path

Examples

Run all tests:

npx majk-test

Run only function unit tests:

npx majk-test --type functions --env unit

Run tests matching pattern:

npx majk-test "**/*health*"

Generate HTML report:

npx majk-test --reporter html --output report.html

Verbose output with bail:

npx majk-test --verbose --bail

Configuration File

Create majk-test.config.js:

module.exports = {
  type: 'all',
  env: 'all',
  reporter: 'console',
  bail: false,
  timeout: 5000,
  verbose: false,
  include: ['tests/**/*.test.js'],
  exclude: ['tests/**/*.skip.js']
};

Or majk-test.config.json:

{
  "type": "functions",
  "env": "unit",
  "reporter": "console",
  "bail": true
}

Test Organization

Recommended Structure

tests/
  plugin/
    functions/
      unit/
        health.test.js
        storage.test.js
      e2e/
        api-integration.test.js
    ui/
      unit/
        component.test.js
      e2e/
        user-flow.test.js

Naming Conventions

  • Use .test.js or .spec.js suffix
  • Group related tests in the same file
  • Use descriptive test names

Advanced Usage

Test Lifecycle

import { before, after, beforeEach, afterEach } from '@majkapp/plugin-test';

before(() => {
  // Run once before all tests
});

after(() => {
  // Run once after all tests
});

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

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

Skipping Tests

test.skip('not ready yet', async () => {
  // This test will be skipped
});

Only Run Specific Tests

test.only('focus on this', async () => {
  // Only this test will run
});

Call Recording

const mockHelper = mock()
  .recordCalls()
  .build();

const callLog = mockHelper.getCallLog();

UI Testing

Using uiTest

The uiTest function provides a simple wrapper around @majkapp/bot-plugin-server for automated UI testing:

const { uiTest } = require('@majkapp/plugin-test');

// Configure once for all tests
uiTest.configure({
  plugin: { dir: process.cwd() },
  browser: { headless: true, slowMo: 200 }
});

// Write UI tests
uiTest('navigate and click button', async (bot) => {
  await bot.navigate();
  await bot.screenshot('01-home');

  await bot.click('submitButton');
  await bot.screenshot('02-after-click');

  const page = bot.getPage();
  const text = await page.textContent('[data-majk-id="result"]');
  console.log(`Result: ${text}`);
});

uiTest('test with mock data', async (bot) => {
  // Configure mock handlers for this test
  bot.configure({
    handlers: {
      async getUsers() {
        return [
          { id: '1', name: 'Test User 1' },
          { id: '2', name: 'Test User 2' }
        ];
      }
    }
  });

  await bot.navigate();
  await bot.screenshot('01-users-loaded');

  // Verify mock data appeared in UI
  const page = bot.getPage();
  const userCount = await page.locator('[data-majk-id="userItem"]').count();
  console.log(`✅ Found ${userCount} users`);
});

Bot API for UI Testing

The bot object provides these methods:

| Method | Description | |--------|-------------| | navigate(url?) | Navigate to plugin UI | | click(id) | Click element with data-majk-id | | type(field, value) | Type into data-majk-field | | waitForState(state, opts?) | Wait for data-majk-state | | screenshot(name) | Capture screenshot (auto-numbered) | | getPage() | Get Playwright Page for advanced operations | | configure(config) | Update bot configuration (e.g., handlers) |

Screenshot Management

Screenshots are automatically organized:

screenshots/
  test-name/
    00-first-screenshot.png
    01-second-screenshot.png
    02-error-click-button.png  (automatic on error)

Features:

  • Sequential numbering (00, 01, 02...)
  • Test name as directory
  • Automatic error screenshots
  • Clear output paths in console

Mock Handlers for UI Tests

Mock handlers intercept plugin API calls:

const mockHandlers = {
  async getConversations({ limit = 100, offset = 0 }) {
    return [
      {
        id: 'conv-1',
        title: 'Plugin Architecture Discussion',
        createdAt: new Date('2025-11-10').toISOString(),
        messageCount: 15
      }
    ];
  },

  async getConversationMessages({ conversationId }) {
    return [
      {
        id: 'msg-1',
        role: 'user',
        content: 'Test message',
        timestamp: new Date().toISOString()
      }
    ];
  }
};

uiTest('verify conversations display', async (bot) => {
  bot.configure({ handlers: mockHandlers });

  await bot.navigate();
  const page = bot.getPage();

  // Navigate to conversations route
  await page.click('a:has-text("Conversations")');
  await page.waitForTimeout(1000);
  await bot.screenshot('conversations-loaded');

  // Verify mock data
  const count = await page.locator('[data-majk-id="conversationItem"]').count();
  console.log(`✅ Found ${count} conversations`);

  // Click first conversation
  await page.click('[data-majk-id="conversationItem"]:first-child');
  await page.waitForTimeout(500);
  await bot.screenshot('conversation-selected');

  // Verify messages
  const messageText = await page.textContent('[data-majk-id="messageContent"]');
  console.log(`✅ Message: ${messageText}`);
});

Advanced UI Testing

Access Playwright's full API via bot.getPage():

uiTest('advanced interactions', async (bot) => {
  await bot.navigate();
  const page = bot.getPage();

  // Custom selectors
  await page.click('.my-custom-class');

  // Wait for specific elements
  await page.waitForSelector('[data-loaded="true"]', { timeout: 5000 });

  // Extract data
  const items = await page.locator('[data-majk-id="item"]').allTextContents();
  console.log(`Items: ${items.join(', ')}`);

  // Network interception
  await page.route('**/api/**', route => {
    route.fulfill({
      status: 200,
      body: JSON.stringify({ custom: 'response' })
    });
  });

  // Fill forms
  await page.fill('input[name="email"]', '[email protected]');

  // Select dropdowns
  await page.selectOption('select[name="role"]', 'admin');

  // Standard bot API still works
  await bot.screenshot('final-state');
});

Running UI Tests

# Run all UI tests
npx majk-test --type ui

# Run UI e2e tests only
npx majk-test --type ui --env e2e

# Run specific UI test
npx majk-test tests/plugin/ui/e2e/my-test.test.js

# Show browser while testing (for debugging)
# Note: use bot-plugin-server directly for this:
npx bot-plugin-server --script tests/plugin/ui/e2e/my-test.test.js --plugin . --headed --slow-mo 500

Reporters

Console Reporter (Default)

Beautiful colored output with symbols and formatting.

JSON Reporter

Machine-readable JSON output:

npx majk-test --reporter json --output results.json

HTML Reporter

Visual HTML report:

npx majk-test --reporter html --output report.html

Tips & Best Practices

  1. Start with Unit Tests - They're faster and easier to debug
  2. Use E2E for Integration - Test real auth and data flows
  3. Keep Tests Focused - One concept per test
  4. Use Descriptive Names - Test names should explain what they verify
  5. Leverage Mock Scenarios - Reuse common setups
  6. Run Tests Often - Catch issues early

Examples

See the /examples directory for complete examples:

  • Basic function testing
  • Auth configuration
  • Mock context usage
  • E2E integration tests

License

MIT