CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-web-component-tester

A comprehensive testing framework specifically designed for web components with browser-based testing environment, Mocha/Chai/Sinon integration, and support for both local and remote testing via Sauce Labs.

Pending
Overview
Eval results
Files

browser-environment.mddocs/

Browser Environment

The Web Component Tester browser environment provides a comprehensive client-side testing runtime with pre-loaded testing libraries and web component-specific utilities.

Capabilities

Global WCT Object

The main browser-side API exposed through the global WCT object.

/**
 * Global WCT object providing browser-side testing functionality
 */
interface WCT {
  /** Shared object for data between test suites */
  share: any;
  /** Load and run test suites from files */
  loadSuites(files: string[]): void;
  /** Current browser configuration */
  _config: Config;
  /** Current test reporter instance */
  _reporter: any;
  /** Child runner constructor for nested tests */
  _ChildRunner: typeof ChildRunner;
}

Usage Examples:

// Load test suites dynamically
WCT.loadSuites([
  'test/unit-tests.js',
  'test/integration-tests.html'
]);

// Share data between test suites
WCT.share.testData = { userId: 123, apiKey: 'test-key' };

// Access shared data in tests
suite('API Tests', function() {
  test('uses shared data', function() {
    assert.equal(WCT.share.testData.userId, 123);
  });
});

Helper Functions

Web component-specific testing utilities available globally in the browser.

/**
 * Safely execute a test step with error handling
 * @param callback - Node-style callback function
 * @param stepFn - Function to execute safely
 */
function safeStep(callback: (error?: any) => void, stepFn: () => void): void;

/**
 * Run test at declaration time (before Mocha starts)
 * @param name - Test name
 * @param testFn - Test function (sync or async)
 */
function testImmediate(name: string, testFn: Function): void;

/**
 * Async-only variant of testImmediate
 * @param name - Test name  
 * @param testFn - Async test function
 */
function testImmediateAsync(name: string, testFn: Function): void;

/**
 * Trigger flush of pending events, observations, and DOM updates
 * @param callback - Function to call after flush completes
 */
function flush(callback: () => void): void;

/**
 * Advance a single animation frame with flush
 * @param callback - Function to call after animation frame
 */
function animationFrameFlush(callback: () => void): void;

/**
 * Wait for a condition to be met with timeout
 * @param fn - Function to test (should throw if condition not met)
 * @param next - Function to call when condition is met
 * @param intervalOrMutationEl - Polling interval or mutation element
 * @param timeout - Maximum wait time in milliseconds
 * @param timeoutTime - Internal parameter (calculated automatically)
 */
function waitFor(
  fn: () => void,
  next: () => void,
  intervalOrMutationEl?: number | MutationEl,
  timeout?: number,
  timeoutTime?: number
): void;

Usage Examples:

// Safe step execution
test('handles async operations', function(done) {
  safeStep(done, function() {
    // This code is wrapped in try/catch
    myAsyncOperation().then(function(result) {
      assert.equal(result.status, 'success');
    });
  });
});

// Immediate test execution
testImmediate('runs before Mocha setup', function() {
  // This runs at page load time
  assert.isTrue(document.readyState !== 'loading');
});

// DOM flush utility
test('waits for DOM updates', function(done) {
  element.someProperty = 'new value';
  
  flush(function() {
    // DOM is now updated
    assert.equal(element.textContent, 'new value');
    done();
  });
});

// Animation frame utility
test('waits for animations', function(done) {
  element.classList.add('animate');
  
  animationFrameFlush(function() {
    // Animation frame has completed
    assert.isTrue(element.classList.contains('animated'));
    done();
  });
});

// Wait for condition
test('waits for condition', function(done) {
  let ready = false;
  
  setTimeout(() => ready = true, 500);
  
  waitFor(
    function() {
      if (!ready) throw new Error('Not ready yet');
    },
    function() {
      assert.isTrue(ready);
      done();
    },
    50, // Check every 50ms
    1000 // Timeout after 1 second
  );
});

Browser Configuration

Client-side configuration interface for customizing the browser environment.

interface Config {
  /** Scripts to load before WCT starts */
  environmentScripts: string[];
  /** HTML imports to load */
  environmentImports: string[];
  /** Absolute root for client scripts */
  root: string | null;
  /** Wait for web component frameworks to load */
  waitForFrameworks: boolean;
  /** Custom wait function */
  waitFor: Function | null;
  /** Maximum concurrent HTML suites */
  numConcurrentSuites: number;
  /** Treat console.error as test failure */
  trackConsoleError: boolean;
  /** Mocha configuration options */
  mochaOptions: MochaSetupOptions;
  /** Enable debug logging */
  verbose: boolean;
}

/**
 * Setup browser configuration
 * @param options - Configuration options to merge
 */
function setup(options: Partial<Config>): void;

/**
 * Get configuration value
 * @param key - Configuration key
 * @returns Configuration value
 */
function get<K extends keyof Config>(key: K): Config[K];

Usage Examples:

<script>
  // Configure before loading browser.js
  WCT = {
    environmentScripts: [
      'custom-assertion-library.js',
      'test-utilities.js'
    ],
    waitForFrameworks: true,
    mochaOptions: {
      timeout: 30000,
      ui: 'bdd'
    },
    verbose: true
  };
</script>
<script src="../web-component-tester/browser.js"></script>

Test Fixture Integration

Integration with @polymer/test-fixture for DOM state management.

/**
 * Create DOM fixture from template
 * @param id - Fixture template ID
 * @returns Created DOM element(s)
 */
function fixture(id: string): Element | DocumentFragment;

Usage Examples:

<test-fixture id="basic">
  <template>
    <my-element foo="bar"></my-element>
  </template>
</test-fixture>

<test-fixture id="multiple">
  <template>
    <div>First</div>
    <div>Second</div>
  </template>
</test-fixture>

<script>
  suite('<my-element>', function() {
    let element;
    
    setup(function() {
      element = fixture('basic');
    });
    
    test('has initial properties', function() {
      assert.equal(element.foo, 'bar');
    });
    
    test('handles multiple elements', function() {
      const elements = fixture('multiple');
      assert.equal(elements.length, 2);
    });
  });
</script>

Accessibility Testing

Integration with accessibility-developer-tools for automated accessibility testing.

/**
 * Run accessibility test suite on a fixture
 * @param fixtureId - ID of test fixture to test
 * @param ignoredTests - Array of test names to ignore (optional)
 * @param beforeEach - Function to run before each test (optional)
 */
function a11ySuite(
  fixtureId: string,
  ignoredTests?: string[],
  beforeEach?: Function
): void;

Usage Examples:

<test-fixture id="basic-button">
  <template>
    <paper-button>Click me</paper-button>
  </template>
</test-fixture>

<test-fixture id="form-elements">
  <template>
    <paper-input label="Name"></paper-input>
    <paper-button>Submit</paper-button>
  </template>
</test-fixture>

<script>
  // Basic accessibility testing
  a11ySuite('basic-button');
  
  // Ignore specific tests
  a11ySuite('form-elements', [
    'AX_COLOR_01', // Skip color contrast test
    'AX_FOCUS_02'  // Skip focus management test
  ]);
  
  // Custom setup before each accessibility test
  a11ySuite('form-elements', [], function() {
    // Setup test state
    fixture('form-elements').querySelector('paper-input').value = 'Test';
  });
</script>

Suite Loading

Dynamic test suite loading functionality.

/**
 * Load and execute test suites from file URLs
 * @param files - Array of test file URLs (.js or .html)
 */
function loadSuites(files: string[]): void;

Usage Examples:

<!doctype html>
<html>
<head>
  <script src="../web-component-tester/browser.js"></script>
</head>
<body>
  <script>
    // Load multiple test suites
    WCT.loadSuites([
      'unit/element-tests.html',
      'unit/behavior-tests.js',
      'integration/full-tests.html'
    ]);
  </script>
</body>
</html>

Pre-loaded Testing Libraries

The browser environment automatically loads these testing libraries:

Mocha

  • TDD Interface: suite(), test(), setup(), teardown()
  • BDD Interface: describe(), it(), before(), after(), beforeEach(), afterEach()

Chai

  • Expect Interface: expect(value).to.equal(expected)
  • Assert Interface: assert.equal(actual, expected)

Sinon

  • Spies: sinon.spy(), sinon.stub()
  • Mocks: sinon.mock(object)
  • Fake Timers: sinon.useFakeTimers()

Sinon-Chai

  • Chai Assertions for Sinon: expect(spy).to.have.been.called

Additional Libraries

  • Lodash: Utility functions (_.forEach, _.map, etc.)
  • Async: Async utilities (async.parallel, async.series)
  • Stacky: Stack trace formatting

Types

interface MutationEl {
  onMutation(mutationEl: this, cb: () => void): void;
}

interface MochaSetupOptions {
  ui?: string;
  timeout?: number;
  slow?: number;
  bail?: boolean;
  grep?: string | RegExp;
}

Install with Tessl CLI

npx tessl i tessl/npm-web-component-tester

docs

browser-environment.md

build-tools.md

cli.md

configuration.md

index.md

plugin-system.md

test-runner.md

tile.json