CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-testing-library--vue

Simple and complete Vue DOM testing utilities that encourage good testing practices

Pending
Overview
Eval results
Files

async.mddocs/

Async Utilities

Utilities for waiting for asynchronous operations and DOM changes in Vue components.

Capabilities

WaitFor Function

Wait for a condition to be true by repeatedly executing a callback function until it succeeds or times out.

/**
 * Wait for a condition to be true
 * @param callback - Function to execute repeatedly
 * @param options - Wait configuration options
 * @returns Promise resolving to callback return value
 */
function waitFor<T>(
  callback: () => T | Promise<T>,
  options?: {
    /** Element to observe for mutations */
    container?: Element;
    /** Maximum time to wait in milliseconds */
    timeout?: number;
    /** Interval between attempts in milliseconds */
    interval?: number;
    /** Custom timeout error handler */
    onTimeout?: (error: Error) => Error;
    /** MutationObserver configuration */
    mutationObserverOptions?: MutationObserverInit;
  }
): Promise<T>;

Usage Examples:

import { render, screen, waitFor, fireEvent } from "@testing-library/vue";
import AsyncComponent from "./AsyncComponent.vue";

render(AsyncComponent);

// Wait for element to appear
await waitFor(() => {
  expect(screen.getByText("Loading complete")).toBeInTheDocument();
});

// Wait for API call to complete
const button = screen.getByRole("button", { name: "Load Data" });
await fireEvent.click(button);

await waitFor(() => {
  expect(screen.getByText("Data loaded successfully")).toBeInTheDocument();
}, { timeout: 5000 });

// Wait for element to have specific attribute
await waitFor(() => {
  const element = screen.getByTestId("status");
  expect(element).toHaveAttribute("data-status", "complete");
});

WaitForElementToBeRemoved Function

Wait for one or more elements to be removed from the DOM.

/**
 * Wait for element(s) to be removed from DOM
 * @param element - Element, elements array, or callback returning elements to wait for removal
 * @param options - Wait configuration options
 * @returns Promise that resolves when elements are removed
 */
function waitForElementToBeRemoved<T>(
  element: Element | Element[] | (() => Element | Element[] | null),
  options?: {
    /** Element to observe for mutations */
    container?: Element;
    /** Maximum time to wait in milliseconds */
    timeout?: number;
    /** Interval between attempts in milliseconds */
    interval?: number;
    /** Custom timeout error handler */
    onTimeout?: (error: Error) => Error;
    /** MutationObserver configuration */
    mutationObserverOptions?: MutationObserverInit;
  }
): Promise<void>;

Usage Examples:

// Wait for loading spinner to disappear
const spinner = screen.getByTestId("loading-spinner");
await waitForElementToBeRemoved(spinner);

// Wait for multiple elements to be removed
const modals = screen.getAllByRole("dialog");
await waitForElementToBeRemoved(modals);

// Wait using a callback (handles cases where element might not exist initially)
await waitForElementToBeRemoved(() => 
  screen.queryByText("Temporary message")
);

// With custom timeout
await waitForElementToBeRemoved(
  screen.getByText("Processing..."),
  { timeout: 10000 }
);

Configuration Options

Default Configuration

interface WaitForOptions {
  /** Default container for mutation observation (document.body) */
  container?: Element;
  /** Default timeout in milliseconds (1000ms) */
  timeout?: number;
  /** Default interval between checks in milliseconds (50ms) */
  interval?: number;
  /** Custom error handler for timeouts */
  onTimeout?: (error: Error) => Error;
  /** MutationObserver options for DOM change detection */
  mutationObserverOptions?: MutationObserverInit;
}

MutationObserver Options

interface MutationObserverInit {
  /** Observe attribute changes */
  attributes?: boolean;
  /** List of attribute names to observe */
  attributeFilter?: string[];
  /** Include old attribute values in mutations */
  attributeOldValue?: boolean;
  /** Observe character data changes */
  characterData?: boolean;
  /** Include old character data in mutations */
  characterDataOldValue?: boolean;
  /** Observe child list changes */
  childList?: boolean;
  /** Observe all descendant mutations */
  subtree?: boolean;
}

Common Async Patterns

API Loading States

import { render, screen, waitFor, fireEvent } from "@testing-library/vue";

// Test loading → success flow
const loadButton = screen.getByRole("button", { name: "Load Data" });
await fireEvent.click(loadButton);

// Wait for loading to start
await waitFor(() => {
  expect(screen.getByText("Loading...")).toBeInTheDocument();
});

// Wait for loading to finish
await waitForElementToBeRemoved(screen.getByText("Loading..."));

// Verify success state
expect(screen.getByText("Data loaded")).toBeInTheDocument();

Form Validation

// Test async form validation
const emailInput = screen.getByLabelText("Email");
const submitButton = screen.getByRole("button", { name: "Submit" });

await fireEvent.update(emailInput, "invalid-email");
await fireEvent.click(submitButton);

// Wait for validation error
await waitFor(() => {
  expect(screen.getByText("Please enter a valid email")).toBeInTheDocument();
});

Component State Changes

// Test reactive state updates
const toggleButton = screen.getByRole("button", { name: "Toggle Theme" });
await fireEvent.click(toggleButton);

// Wait for theme to change
await waitFor(() => {
  const root = document.documentElement;
  expect(root).toHaveAttribute("data-theme", "dark");
});

Navigation and Route Changes

// Test Vue Router navigation
const navLink = screen.getByRole("link", { name: "About" });
await fireEvent.click(navLink);

// Wait for new page content
await waitFor(() => {
  expect(screen.getByText("About Page")).toBeInTheDocument();
});

Error Handling

Timeout Errors

When waitFor times out, it throws a detailed error with the last error from the callback:

try {
  await waitFor(() => {
    expect(screen.getByText("Never appears")).toBeInTheDocument();
  }, { timeout: 1000 });
} catch (error) {
  // Error includes timeout info and last expectation failure
  console.log(error.message);
}

Custom Error Handling

await waitFor(() => {
  expect(screen.getByText("Content")).toBeInTheDocument();
}, {
  timeout: 5000,
  onTimeout: (error) => {
    return new Error(`Custom timeout message: ${error.message}`);
  }
});

Global Configuration

Configure default timeouts and intervals using DOM Testing Library's configure function:

/**
 * Configure global library behavior
 * @param options - Configuration options
 */
function configure(options: {
  /** Default timeout for async utilities in milliseconds */
  asyncUtilTimeout?: number;
  /** Attribute name used for getByTestId queries */
  testIdAttribute?: string;
  /** Whether to include hidden elements by default */
  defaultHidden?: boolean;
  /** CSS selector for elements to ignore */
  defaultIgnore?: string;
  /** Show original stack traces in errors */
  showOriginalStackTrace?: boolean;
  /** Enable query suggestions in error messages */
  throwSuggestions?: boolean;
}): void;

/**
 * Get current configuration
 * @returns Current configuration object
 */
function getConfig(): {
  asyncUtilTimeout: number;
  testIdAttribute: string;
  defaultHidden: boolean;
  defaultIgnore: string;
  showOriginalStackTrace: boolean;
  throwSuggestions: boolean;
};

Usage Examples:

import { configure } from "@testing-library/vue";

// Configure timeout for all async utilities
configure({
  asyncUtilTimeout: 2000,
});

// Configure test ID attribute
configure({
  testIdAttribute: 'data-cy',
});

// Enable query suggestions for better error messages
configure({
  throwSuggestions: true,
});

Install with Tessl CLI

npx tessl i tessl/npm-testing-library--vue

docs

async.md

events.md

index.md

queries.md

rendering.md

tile.json