Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Implementation:DevExpress Testcafe AssertionExecutor

From Leeroopedia
Knowledge Sources
Domains Testing, Web_Automation
Last Updated 2026-02-12 04:00 GMT

Overview

Concrete implementation of assertion retry logic in TestCafe through the `AssertionExecutor` class, which handles automatic re-evaluation of assertions with dynamic values.

Description

The `AssertionExecutor` class implements the core retry mechanism for TestCafe assertions. It detects when assertion operands are re-executable (Selector properties, ClientFunctions), enters a retry loop with 200ms delays between attempts, and uses configurable timeouts (defaulting to the `assertionTimeout` configuration option). Internally, it uses Chai assertion functions for the actual comparison logic. This class is not exposed to users; it operates behind the scenes when assertions are executed.

Usage

Internal framework component. Users don't interact with `AssertionExecutor` directly; it's invoked automatically when assertions created via `t.expect()` are executed.

Code Reference

Source Location

  • Repository: testcafe
  • File: src/assertions/executor.ts
  • Lines: 12-103

Signature

class AssertionExecutor {
  constructor(
    command: AssertionCommand,
    timeout: number,
    callsite: object
  )

  async execute(): Promise<void>

  private async _evaluateOperands(): Promise<{ actual: any, expected: any }>
  private async _executeAssertion(actual: any, expected: any): Promise<void>
  private _shouldRetry(error: Error): boolean
  private async _retryAssertion(): Promise<void>
}

// Internal configuration
const ASSERTION_RETRY_DELAY = 200 // milliseconds

Import

// Internal class, not exported
// Used internally by TestCafe when executing assertion commands

// Users interact with assertions via t.expect():
test('Example', async t => {
  // AssertionExecutor runs behind the scenes
  await t.expect(Selector('h1').textContent).eql('Welcome');
});

I/O Contract

Inputs

Name Type Required Description
command AssertionCommand Yes Command object containing assertion details (type, operands, options)
timeout number Yes Maximum time in milliseconds to retry the assertion
callsite object Yes Source location information for error reporting

Outputs

Name Type Description
result Promise<void> Resolves if assertion passes, rejects with error if fails

Usage Examples

Internal Operation (Conceptual)

// This is how AssertionExecutor works internally (not user code)

// User writes:
await t.expect(Selector('.item').count).gte(5);

// Framework internally:
const command = {
  type: 'assertion',
  assertionType: 'gte',
  actual: SelectorPromise { /* .count property */ },
  expected: 5,
  message: null,
  options: { timeout: 3000 }
};

const executor = new AssertionExecutor(
  command,
  3000, // timeout
  callsite
);

await executor.execute();
// Internally retries every 200ms for up to 3000ms
// until Selector('.item').count >= 5

Retry Behavior Example

// User test code
fixture`Retry Logic Demo`
  .page`https://example.com`;

test('Wait for items to load', async t => {
  // Click triggers AJAX request to load items
  await t.click('#load-items-button');

  // AssertionExecutor automatically retries this assertion
  // Timeline:
  // t=0ms:    count = 0, assertion fails, schedule retry
  // t=200ms:  count = 0, assertion fails, schedule retry
  // t=400ms:  count = 3, assertion fails, schedule retry
  // t=600ms:  count = 5, assertion PASSES ✓
  await t.expect(Selector('.item').count).gte(5);
});

Static vs Re-executable Values

test('Retry behavior differences', async t => {
  const staticValue = 10;
  const dynamicValue = Selector('.counter').textContent;

  // Static comparison - fails immediately, no retry
  // (both operands are static values)
  try {
    await t.expect(staticValue).eql(20);
  } catch (e) {
    // Fails instantly
  }

  // Dynamic comparison - retries until passes or timeout
  // (actual is a Selector property - re-executable)
  await t.expect(dynamicValue).eql('10');
  // Retries every 200ms until counter text equals '10'
});

Timeout Configuration

test('Custom timeout affects retry duration', async t => {
  // Default timeout (e.g., 3000ms from config)
  await t.expect(Selector('.fast-element').visible).ok();
  // Retries for up to 3000ms with 200ms delays

  // Custom timeout (10000ms)
  await t.expect(Selector('.slow-element').visible).ok('', {
    timeout: 10000
  });
  // Retries for up to 10000ms with 200ms delays
  // Maximum ~50 retry attempts
});

Error Reporting

test('Assertion failure after timeout', async t => {
  // If element never appears, AssertionExecutor fails after timeout
  try {
    await t.expect(Selector('.non-existent').visible).ok('', {
      timeout: 2000
    });
  } catch (error) {
    // Error includes:
    // - Expected: true
    // - Actual: false (last evaluated value)
    // - Timeout: 2000ms
    // - Callsite: line number in test
    console.log(error.message);
    // "Expected .non-existent to be visible, but it was not.
    //  Assertion failed after 2000ms."
  }
});

Integration with Chai

// AssertionExecutor uses Chai internally for assertion logic

// User writes:
await t.expect(selector.count).eql(5);

// AssertionExecutor internally:
// 1. Evaluates: actualValue = await selector.count
// 2. Calls Chai: chai.expect(actualValue).to.equal(5)
// 3. If Chai throws, catches error and retries
// 4. If Chai succeeds, assertion complete

// This provides rich assertion types:
await t.expect(value).eql(expected);           // deep equality
await t.expect(value).typeOf('string');        // type check
await t.expect(value).within(1, 10);           // range check
await t.expect(value).match(/pattern/);        // regex match
// All implemented via Chai assertion library

Retry Delay Constant

// The 200ms retry delay is hardcoded in AssertionExecutor

// For 3000ms timeout:
// - Maximum retry attempts: 3000 / 200 = 15 attempts
// - Actual attempts may be fewer if assertion passes early
// - Last attempt made even if timeout expired (always try at least once)

// Example timeline with 1000ms timeout:
// t=0ms:    Attempt 1 - FAIL
// t=200ms:  Attempt 2 - FAIL
// t=400ms:  Attempt 3 - FAIL
// t=600ms:  Attempt 4 - FAIL
// t=800ms:  Attempt 5 - FAIL
// t=1000ms: Attempt 6 (final) - FAIL → throw TimeoutError

Related Pages

Implements Principle

Uses Heuristic

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment