# RemixScenario / FrameworkRouteScenario

The `RemixScenario` (aliased as `FrameworkRouteScenario`) is a specialized Maddox scenario for testing React Router v7 and Remix applications. It uses `createRoutesStub` internally to provide a realistic routing environment while still allowing you to use Maddox's powerful mock verification pipeline.

## Basic Usage

```javascript
import { RemixScenario } from 'maddox';
import * as HomeRoute from './routes/home';

new RemixScenario(this)
  .addStub({ mockName: 'Home', path: '/', module: HomeRoute })
  .triggerRender()
  .test((err, response) => {
    // response is an array of all loader/action calls
  });
```

## Execution Boundaries

Maddox enforces a clear distinction between **configuration** (above the boundary) and **execution/assertion** (below the boundary). Every `RemixScenario` must call **exactly one** of the following execution boundaries before calling `.test()`:

1.  `.triggerRender()`: Standard UI-based test.
2.  `.triggerAction()`: Targeted action test (can be headless).
3.  `.triggerLoader()`: Targeted loader test (can be headless).

If you call more than one, or none at all, Maddox will throw a build error to ensure your test intent is unambiguous.

## API Reference

### `.addStub(descriptor)`
Registers a route for the test. 

*   **`mockName`** or **`id`**: (Required) You must provide exactly one of these. Maddox treats them as the same identifier. This string is used for:
    *   Targeting mocks in `mockThisFunction`.
    *   Identifying results in the final `response` array.
    *   React Router's internal route ID (useful for `useRouteLoaderData(id)`).
*   **`path`**: (Required) The URL pattern for the route.
*   **`module`**: (Required) The route module object (usually from `import * as Route`).
*   **`children`**: (Optional) An array of nested route descriptors.

### `.triggerRender(options)`
Explicitly marks the moment the Stub mounts. Everything chained above it configures the scenario; everything below it acts on the rendered DOM.

*   **`options`**: (Optional)
    *   **`waitForIdle`**: (Boolean, default: `true`) When true, Maddox automatically waits for React Router to transition out of the `loading` state (i.e., all initial loaders have finished) before proceeding to the next step.

> **Note**: The older `.render()` method is deprecated in favor of `.triggerRender()`.

### `.triggerAction(target, options)`
Simulates a programmatic form submission (mirroring `useSubmit`) as soon as the scenario is rendered.

*   **`target`**: (Required) Either a `string` (the `mockName`/`id` of a previously added stub) or a **Route Descriptor object**.
    *   If a Route Descriptor is provided, Maddox automatically calls `.addStub()` and sets the initial entry to that route's path for you.
*   **`options`**: (Optional) Submission options:
    *   **`method`**: (e.g., "post", "put", "delete").
    *   **`body`**: The data to submit (available via `request.formData()`).
    *   **`url`**: (Optional) For dynamic routes (e.g., `/users/:id`), provide the resolved URL here (e.g., `/users/123`).
    *   **`params`**: Overrides for URL segments passed to the action.
    *   **`context`**: Overrides for `AppLoadContext` passed to the action.

### `.triggerLoader(target, options)`
Automatically triggers a route loader when the scenario is rendered.

*   **`target`**: (Required) Either a `string` (the `mockName`/`id` of a previously added stub) or a **Route Descriptor object**.
    *   If a Route Descriptor is provided, Maddox automatically calls `.addStub()` and sets the initial entry to that route's path for you.
*   **`options`**: (Optional) Loader options:
    *   **`url`**: (Optional) For dynamic routes (e.g., `/users/:id`), provide the resolved URL here (e.g., `/users/456`).
    *   **`params`**: Overrides for URL segments passed to the loader.
    *   **`context`**: Overrides for `AppLoadContext` passed to the loader.

### `.withInitialEntries(entries)`
Defines the initial navigation history for the test.

*   **Revalidation**: If you start on Page A and trigger an action on Page B, Maddox will capture the revalidation of Page A's loader after the action completes.
*   **Redirects**: If your action returns a `redirect('/success')`, Maddox will navigate the router from the initial entry to the new path.

### `.withRequestMiddleware(middlewareFn)`
Allows transforming the `{ request, params, context }` object before it reaches a loader or action.

```javascript
.withRequestMiddleware(({ request, params, context }) => {
  context.user = { id: '123' }; // Simulate auth
  return { request, params, context };
})
```

## Nuances and Best Practices

### Testing "Resource Routes" (Action-only)
You can test routes that only have an `action` or `loader` without defining a React component. Maddox automatically provides named fallback components (`MaddoxLeafFallback` or `MaddoxParentFallback`) to ensure the React lifecycle completes and hooks like `useSubmit` or `useNavigation` work correctly.

### Targeted Triggers
For resource routes or API-only tests, you can skip `.addStub()` and `.withInitialEntries()` by passing the route descriptor directly to `.triggerAction()` or `.triggerLoader()`.

```javascript
new RemixScenario(this)
  .triggerAction({ 
    mockName: 'MyAction', 
    path: '/api/resource', 
    module: require('./routes/api.resource') 
  }, { method: 'post' })
  .test(...)
```

### Dynamic Paths in Targeted Triggers
If your targeted route has dynamic segments (e.g., `/users/:id`), use the `url` option to provide the resolved path:

```javascript
new RemixScenario(this)
  .triggerLoader({ 
    mockName: 'User', 
    path: '/api/users/:id', 
    module: UserLoader 
  }, { url: '/api/users/123' })
  .test(...)
```

### Revalidation vs. Isolation
*   **Isolation**: If you only care about the action's logic, you can ignore `withInitialEntries`.
*   **Full Lifecycle**: Use `withInitialEntries` to verify that an action correctly triggers revalidation of your main pages or redirects the user to a new location.

### Unified Identifiers
Always use the same string for `mockName`/`id` across your test. This ensures that your mocks, assertions, and components (using `useRouteLoaderData`) all refer to the same logical route.
