Skip to content
Skills Library
Programming · Testing

testing

/testing

Writing tests for the B2C CLI project using Mocha, Chai, and MSW. Use when writing unit or integration tests, mocking HTTP requests with MSW, testing CLI commands with oclif, isolating config in tests, setting up test coverage, using sinon stubs, c8 coverage, capturing or silencing stdout, or creating MSW handlers.

Licence
Apache-2.0
Last repository change
23.10.2026

Assessment and security

100/ 100Class: good
Format25 / 25
Frontmatter with name and description, valid name, description up to 1,024 characters, name matching the folder.
Discoverability25 / 25
Description explains when to use the skill, has a reasonable length and uses third person.
Content35 / 35
At least 150 words and at most 500 lines, headings, step lists, examples or code.
Hygiene15 / 15
No placeholders such as TODO, no Windows paths, with a licence.
Riskno signals detected in the scan
Files in this package
4
Scripts
0
HTML comments
0
URLs in SKILL.md
1
Deprecation

No signals detected. Repository changed on 23.10.2026, without references to retired models or APIs.

Licence

Apache-2.0, otwarta: można używać i zmieniać, z zachowaniem licencji i informacji o zmianach.

What it does and when to use it

Writing tests for the B2C CLI project using Mocha, Chai, and MSW. Use when writing unit or integration tests, mocking HTTP requests with MSW, testing CLI commands with oclif, isolating config in tests, setting up test coverage, using sinon stubs, c8 coverage, capturing or silencing stdout, or creating MSW handlers.

Original author description

Writing tests for the B2C CLI project using Mocha, Chai, and MSW. Use when writing unit or integration tests, mocking HTTP requests with MSW, testing CLI commands with oclif, isolating config in tests, setting up test coverage, using sinon stubs, c8 coverage, capturing or silencing stdout, or creating MSW handlers.

How it works

  1. Test Framework Stack
  2. Running Tests
  3. Test Organization
  4. Import Patterns
  5. Config Isolation
  6. Polling Tests (Avoid Fake Timers)
  7. HTTP Mocking with MSW
  8. MockAuthStrategy
  9. Silencing Test Output & Command Test Guidelines
  10. Testing CLI Commands with oclif
Original SKILL.md headings · 380 lines, 1,465 words
SKILL.md preview
---
name: testing
description: Writing tests for the B2C CLI project using Mocha, Chai, and MSW. Use when writing unit or integration tests, mocking HTTP requests with MSW, testing CLI commands with oclif, isolating config in tests, setting up test coverage, using sinon stubs, c8 coverage, capturing or silencing stdout, or creating MSW handlers.
metadata:
  internal: true
---

# Testing

This skill covers project-specific testing patterns for the B2C CLI project.

## Test Framework Stack

- **Test Runner**: Mocha
- **Assertions**: Chai (property-based)
- **HTTP Mocking**: MSW (Mock Service Worker)
- **Stubbing/Mocking**: Sinon
- **Code Coverage**: c8
- **TypeScript**: tsx (native execution without compilation)

## Running Tests

For coding agents (minimal output - only failures shown):

```bash
# Run tests - only failures + summary
pnpm run test:agent

# Run tests for specific package
pnpm --filter @salesforce/b2c-tooling-sdk run test:agent
pnpm --filter @salesforce/b2c-cli run test:agent
```

For debugging (full output with coverage):

```bash
# Run all tests with coverage
pnpm run test

# Run tests for specific package
pnpm --filter @salesforce/b2c-tooling-sdk run test
pnpm --filter @salesforce/b2c-cli run test

# Run single test file (no coverage, faster)
cd packages/b2c-tooling-sdk
pnpm mocha "test/clients/webdav.test.ts"

# Run tests matching pattern
pnpm mocha --grep "mkcol" "test/**/*.test.ts"

# Watch mode for TDD
pnpm --filter @salesforce/b2c-tooling-sdk run test:watch
```

## Test Organization

Tests mirror the source directory structure with `.test.ts` suffix:

```
packages/b2c-tooling-sdk/
├── src/
│   └── clients/
│       └── webdav.ts
└── test/
    └── clients/
        └── webdav.test.ts
```

## Import Patterns

Always use package exports, not relative paths:

```typescript
// Good - uses package exports
import { WebDavClient } from '@salesforce/b2c-tooling-sdk/clients';
import { OAuthStrategy } from '@salesforce/b2c-tooling-sdk/auth';

// Avoid - relative paths
import { WebDavClient } from '../../src/clients/webdav.js';
```

This ensures tests use the same export paths as consumers.

## Config Isolation

Tests that check for "missing credentials" or "no config" scenarios need isolation from the developer's real configuration files (`~/.mobify`, `dw.json`) and environment variables.

### Using Config Isolation Helpers

```typescript
import { isolateConfig, restoreConfig } from '../helpers/config-isolation.js';

describe('config-dependent tests', () => {
  beforeEach(() => {
    isolateConfig();
  });

  afterEach(() => {
    restoreConfig();
  });

  it('handles missing credentials', async () => {
    // Test now runs without reading real ~/.mobify or SFCC_* env vars
  });
});
```

The helpers:
- Clear all `SFCC_*` and `MRT_*` environment variables
- Clear other config-affecting vars (`LANGUAGE`, `NO_COLOR`)
- Must call `restoreConfig()` in afterEach to restore original state

### For SDK Unit Tests (bypass config sources)

When testing `resolveConfig` directly without file system:

```typescript
import { resolveConfig } from '@salesforce/b2c-tooling-sdk/config';

const config = resolveConfig({}, {
  replaceDefaultSources: true,
  sources: []  // No file-based sources
});
```

### For MRT Credential Isolation

Use the `credentialsFile` option to override the default `~/.mobify` path:

```typescript
import { resolveConfig } from '@salesforce/b2c-tooling-sdk/config';

// Point to non-existent file for isolation
const config = resolveConfig({}, {
  credentialsFile: '/dev/null'
});
```

In CLI command tests, use the `stubParse` helper with the `credentials-file` flag:

```typescript
import { stubParse } from '../helpers/stub-parse.js';

stubParse(command, {'credentials-file': '/dev/null'});  // Isolates from real ~/.mobify
```

## Polling Tests (Avoid Fake Timers)

**Do not use fake timers with MSW.** MSW v2 uses microtasks internally, and fake timers prevent MSW's promises from resolving.

Instead, use the `pollInterval` option for fast tests:

```typescript
// Good - use short poll interval
const result = await siteArchiveImport(mockInstance, siteDir, {
  archiveName: 'test-import',
  waitOptions: { pollInterval: 10 }  // 10ms instead of default 3000ms
});

// Bad - fake timers break MSW
import FakeTimers from '@sinonjs/fake-timers';
const clock = FakeTimers.install();  // DON'T DO THIS with MSW
```

## HTTP Mocking with MSW

### Basic Setup

```typescript
import { expect } from 'chai';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import { WebDavClient } from '@salesforce/b2c-tooling-sdk/clients';
import { MockAuthStrategy } from '../helpers/mock-auth.js';

const TEST_HOST = 'test.salesforce.com';
const BASE_URL = `https://${TEST_HOST}`;

const server = setupServer();

describe('WebDavClient', () => {
  let client: WebDavClient;
  let mockAuth: MockAuthStrategy;

  before(() => {
    server.listen({ onUnhandledRequest: 'error' });
  });

  afterEach(() => {
    server.resetHandlers();
  });

  after(() => {
    server.close();
  });

  beforeEach(() => {
    mockAuth = new MockAuthStrategy();
    client = new WebDavClient(TEST_HOST, mockAuth);
  });

  it('creates a directory successfully', async () => {
    server.use(
      http.all(`${BASE_URL}/*`, ({ request }) => {
        if (request.method === 'MKCOL') {
          return new HttpResponse(null, { status: 201 });
        }
        return new HttpResponse(null, { status: 405 });
      }),
    );

    await client.mkcol('Cartridges/v1');
  });
});
```

### Request Capture Pattern

To verify request details, capture requests in an array:

```typescript
interface CapturedRequest {
  method: string;
  url: string;
  headers: Headers;
  body?: unknown;
}

const requests: CapturedRequest[] = [];

beforeEach(() => {
  requests.length = 0;
});

it('sends correct headers', async () => {
  server.use(
    http.put(`${BASE_URL}/*`, async ({ request }) => {
      requests.push({
        method: request.method,
        url: request.url,
        headers: request.headers,
        body: await request.text(),
      });
      return new HttpResponse(null, { status: 201 });
    }),
  );

  await client.put('path/to/file', Buffer.from('content'));

  expect(requests).to.have.length(1);
  expect(requests[0].method).to.equal('PUT');
  expect(requests[0].headers.get('Authorization')).to.equal('Bearer test-token');
});
```

### Error Responses

```typescript
it('handles 404 errors', async () => {
  server.use(
    http.get(`${BASE_URL}/api/items/:id`, () => {
      return HttpResponse.json({ error: 'Not found' }, { status: 404 });
    }),
  );

  try {
    await client.getItem('nonexistent');
    expect.fail('Should have thrown');
  } catch (error) {
    expect(error.message).to.include('404');
  }
});

it('handles network errors', async () => {
  server.use(
    http.get(`${BASE_URL}/api/items`, () => {
      return HttpResponse.error();
    }),
  );

  try {
    await client.listItems();
    expect.fail('Should have thrown');
  } catch (error) {
    expect(error.message).to.include('network');
  }
});
```

## MockAuthStrategy

Use the test helper for authentication:

```typescript
// test/helpers/mock-auth.ts
import type { AuthStrategy } from '@salesforce/b2c-tooling-sdk/auth';

export class MockAuthStrategy implements AuthStrategy {
  constructor(private token: string = 'test-token') {}

  async fetch(url: string, init?: RequestInit): Promise<Response> {
    const headers = new Headers(init?.headers);
    headers.set('Authorization', `Bearer ${this.token}`);
    return fetch(url, { ...init, headers });
  }

  async getAuthorizationHeader(): Promise<string> {
    return `Bearer ${this.token}`;
  }
}
```

Usage:

```typescript
import { MockAuthStrategy } from '../helpers/mock-auth.js';

const mockAuth = new MockAuthStrategy();
const client = new WebDavClient(TEST_HOST, mockAuth);

// Custom token for specific tests
const customAuth = new MockAuthStrategy('custom-token');
```

## Silencing Test Output & Command Test Guidelines

See [Command Testing Patterns](./references/COMMAND-TESTING-PATTERNS.md) for detailed patterns on:
- Silencing output with `stubCommandConfigAndLogger` and `runSilent`
- Verifying console output content
- `stubParse` silent logging defaults
- What to test (and what not to test) in commands
- Low-value tests to avoid

**Quick reference:** Use `stubCommandConfigAndLogger(command)` for AM/sandbox-style commands, `runSilent(() => command.run())` for everything else. `stubParse` sets `'log-level': 'silent'` automatically.

## Testing CLI Commands with oclif

See [CLI Command Testing Patterns](./references/CLI-COMMAND-TESTING.md) for integration tests with `runCommand`, SDK base command fixture tests, E2E test patterns, and when to use each approach.

## Coverage

Coverage is configured in `.c8rc.json`. View the HTML report after running tests:

```bash
pnpm run test
open coverage/index.html
```

## Test Helpers Reference

See [Test Helpers Reference](./references/HELPERS.md) for a full list of helpers available in both the CLI and SDK packages.

## Troubleshooting

**MSW handler not matching requests**: Verify the URL pattern in `http.get()`/`http.post()` matches the full URL including base path. Use `onUnhandledRequest: 'error'` in `server.listen()` to surface unmatched requests. Check that the HTTP method matches (e.g., `http.all()` for WebDAV methods like MKCOL/PROPFIND).

**Config or env vars leaking between tests**: Always pair `isolateConfig()` with `restoreConfig()` in `beforeEach`/`afterEach`. Missing `restoreConfig()` causes subsequent tests to run with cleared env vars. Use `sinon.restore()` in `afterEach` to clean up all stubs.

**Import path errors ("module not found")**: Use package exports (`@salesforce/b2c-tooling-sdk/clients`) not relative paths. If a new export was added, ensure it's in `package.json` `exports` with the `development` condition pointing to the `.ts` source file.

**Fake timers break MSW**: MSW v2 uses microtasks internally. Never use `@sinonjs/fake-timers` or `sinon.useFakeTimers()` in tests that use MSW. Use `pollInterval: 10` for fast polling tests instead.

**Test output is noisy**: Use `runSilent()` to suppress stdout/stderr from commands. The `stubParse` helper automatically sets `'log-level': 'silent'` to quiet pino logger output.

## Test Expectations

- **Behavior changes need tests.** A new feature or behavior change needs a test that would fail without the change. A bug fix needs a regression test that reproduces the bug.
- **Assert on observable outcomes**: return values, `--json` output, logged or printed text, requests sent (see the request capture pattern), and error messages. A test that only runs code without asserting a result proves little. The same applies to tests that check a mocked value comes back unchanged (see [Low-Value Tests to Avoid](./references/COMMAND-TESTING-PATTERNS.md#low-value-tests-to-avoid)).
- **Cover the error path** for code that handles API failures, missing configuration, or refused access, in addition to the success path.
- **Never commit `.only`.** To skip tests that need real credentials or a particular platform, call `this.skip()` conditionally with the reason, as the E2E tests do. Do not skip a failing test unconditionally to get a change through.
- **Report what you ran.** Name the test files or packages you ran, and do not describe a targeted run as a full-suite pass.

## Writing Tests Checklist

1. Create test file in `test/` mirroring source structure
2. Use `.test.ts` suffix
3. Import from package names, not relative paths
4. Set up MSW server for HTTP tests (avoid fake timers)
5. Use `isolateConfig()`/`restoreConfig()` for config-dependent tests
6. Use `runSilent()` for commands that produce console output
7. Use `pollInterval` option for polling operations
8. Use MockAuthStrategy for authenticated clients
9. Test both success and error paths
10. Focus on command-specific logic, not trivial delegation
11. Run tests: `pnpm --filter <package> run test`
Files in this package (4)
  • SKILL.md 12 KB
  • references/CLI-COMMAND-TESTING.md 2 KB
  • references/COMMAND-TESTING-PATTERNS.md 4 KB
  • references/HELPERS.md 1 KB

Versions

RepositoryScoreLicence
This page · SalesforceCommerceCloud/b2c-developer-tooling100Apache-2.0
iuliandita/skills100MIT
wrongstack/wrongstack100MIT
langwatch/langwatch100Apache-2.0
mcouthon/agents100MIT
nteract/nteract100BSD-3-Clause
kettleofketchup/DraftForge100GPL-3.0
skillberry-ai/cap-evolve100Apache-2.0
TheBeardedBearSAS/claude-craft100MIT

Database snapshot: 13.11.2026 · package version 411b15b033c9 (SHA-256 digest of the entire package)

Downloads in the last 30 days: 0 · ranking

Report a problem with this skill: harmful content, incorrect descriptions or an author’s removal request.

Similar skills

  • 95mocha-skill
    Generates Mocha tests in JavaScript with Chai assertions and Sinon mocking. Use when user mentions "Mocha", "Chai", "sinon", "describe/it (not Jest)". Triggers on: "Mocha", "Chai", "sinon", "mocha test".
  • 100analyzing-test-coverage
    Creates and analyzes tests using Vitest and MSW patterns. Use when writing unit tests, integration tests, analyzing coverage gaps, setting up MSW handlers, vi.fn mocks, test builders, or debugging test failures. Do NOT use for non-test TypeScript code.
  • 85Vitest Migration
    This skill should be used when the user asks to "migrate test to vitest", "convert test to vitest", "migrate WTR test", "move test from mocha to vitest", or discusses migrating test files from Web Test Runner + Mocha + Chai + Sinon to Vitest browser mode.
  • 100msw
    MSW (Mock Service Worker) best practices for API mocking in tests (formerly test-msw). This skill should be used when setting up MSW, writing request handlers, or mocking HTTP APIs. This skill does NOT cover general testing patterns (use test-vitest or test-tdd skills) or test methodology.
  • 100nextjs-testing
    Write Jest or Vitest unit tests with React Testing Library and Playwright E2E tests for Next.js projects. Use when testing components with RTL, mocking APIs with MSW, or creating Playwright user flow tests.
  • 90unit-testing
    Unit testing patterns: Vitest config with v8 coverage, Testing Library behavior testing, MSW for HTTP mocking (vs jest.mock), it.each parametrized tests, spies vs mocks vs stubs, testing async code, snapshot testing guidelines. Use when writing unit and component tests.

Feedback and reports

Your message goes to Bartosz Sokoliński. An email address is needed only for a reply. See the privacy policy.