scenetest-js

Writing Inline Assertions

Inline assertions live inside your components and verify internal state that external tests can’t easily observe. They run during normal component execution and report to the Scenetest observer.

When to Use Inline Assertions

Use inline assertions when you want to verify:

// src/components/Cart.tsx
import { should, failed } from '@scenetest/checks-react'

function Cart({ items }) {
  should('cart has items', items.length > 0)

  if (items.some(item => item.price < 0)) {
    failed('found item with negative price', { items })
  }

  return <div data-testid="cart-items">...</div>
}

Framework Imports

Import from your framework’s package:

// React
import { should, failed, serverCheck, useCheck } from '@scenetest/checks-react'

// Vue
import { should, failed, serverCheck, watchCheck } from '@scenetest/checks-vue'

// Solid
import { should, failed, serverCheck, createCheck } from '@scenetest/checks-solid'

// Svelte (use inside $effect)
import { should, failed, serverCheck, checkEffect } from '@scenetest/checks-svelte'

// Framework-agnostic (just assertions)
import { should, failed, serverCheck } from '@scenetest/checks'

Using should()

Use should() when checking that something is true:

should(description, condition, context?)
import { should } from '@scenetest/checks-react'

function UserProfile({ user }) {
  should('user has a display name', !!user.displayName)
  should('user email is verified', user.emailVerified, { email: user.email })

  return (
    <div data-testid="user-profile">
      <h1>{user.displayName}</h1>
    </div>
  )
}

Using failed()

Use failed() when something should not happen:

failed(description, context?)
import { failed } from '@scenetest/checks-react'

function ErrorBoundary({ error }) {
  if (error) {
    failed('unexpected error in render', { error: error.message })
  }

  // ...
}

failed() is for paths that should never execute. If it runs, something is wrong.

Tip: The context parameter is optional but highly valuable. Include relevant state that helps debug failures.

Multi-Context Assertions with serverCheck()

For comparing browser data with server data, use serverCheck() with your framework’s test effect hook:

import { should, serverCheck, useCheck } from '@scenetest/checks-react'

function ProfileForm({ userId }) {
  const { profile, isLoading } = useProfile(userId)

  // Run assertions when profile changes
  useCheck(() => {
    if (isLoading || !profile) return

    serverCheck(
      'Profile matches database',
      async (server, data) => {
        const dbProfile = await server.getUser(data.userId)
        should('name should match', dbProfile.name === data.localName)
      },
      () => ({ userId, localName: profile.name })
    )
  }, [isLoading, profile?.id])

  return <form>...</form>
}

The serverCheck() function:

  1. Captures data from the browser context
  2. Runs a callback in the test runner context with access to server functions
  3. Allows you to use should() inside to make assertions

Configuring Server Functions

Define server functions in your scenetest config:

// scenetest/config.ts
import { defineConfig } from '@scenetest/scenes'

export default defineConfig({
  baseUrl: 'http://localhost:5173',

  server: {
    getUser: (id) => db.users.findById(id),
    validateEmail: (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email),
  },
})

Assertions that run at the same time (within 50ms) are automatically grouped in the observer panel:

function OrderSummary({ order }) {
  // These will appear as a group in the observer
  should('order has items', order.items.length > 0)
  should('order has a total', order.total > 0)
  should('order has shipping address', !!order.shippingAddress)

  return (
    <div data-testid="order-summary">
      {/* ... */}
    </div>
  )
}

This helps you understand which assertions are related and ran together during a single render or state update.

Best Practices

Assert Invariants, Not Implementation

Good assertions verify what must be true, not how it’s computed:

// Good: asserts an invariant
should('total matches sum of items',
  order.total === order.items.reduce((sum, i) => sum + i.price, 0))

// Less useful: just checks a value exists
should('total is set', order.total !== undefined)

Include Helpful Context

Context appears in the observer panel and helps debug failures:

should('user can access feature', user.hasPermission('feature'), {
  userId: user.id,
  role: user.role,
  permissions: user.permissions,
})

Use failed() for Error Paths

Reserve failed() for code paths that indicate bugs:

function handleResponse(response) {
  switch (response.type) {
    case 'success':
      return processSuccess(response)
    case 'error':
      return processError(response)
    default:
      failed('unknown response type', { type: response.type })
  }
}

Summary