Hydration Readiness & E2E Testing
This reference details the technical specifications of Coralite's client-side hydration phase and how it exposes its state for End-to-End (E2E) testing frameworks.
The Testing Mode #
When Coralite is configured with mode: 'testing', it provides a specialized environment for automated testing.
In this mode, the built-in testing plugin performs several automated tasks:
- Static Build Target: The compiler builds pages as a static target, stripping development-only properties and optimizing global runtime script output (identical to
productionmode). - Incremental Build Cache Invalidation: The build compiler computes a hash of the
testing.mocksconfiguration. If your mock definitions change, it invalidates the incremental build cache (ISR) and triggers a rebuild for all affected pages. - E2E Targeting Selectors & data-testid Injection: To support reliable, non-flaky E2E tests, Coralite auto-injects and prefixes test selectors in
testingmode:- Explicit Test IDs & Auto-Prefixing: When authoring HTML templates, you MUST write static, plain-text
data-testidattributes. Dynamic binding using curly braces is not allowed. The build compiler automatically parses staticdata-testidstrings and prepends the correctpage__orinstance-id__prefix. - Auto-Generated Test IDs: Interactive elements (like
button,a,input,form,select,textarea, or elements with atabindexor interactiveroleattribute) that do not have an explicitdata-testidare automatically assigned one following the pattern[prefix][tagName]-[zero-based-index].
- Explicit Test IDs & Auto-Prefixing: When authoring HTML templates, you MUST write static, plain-text
- Velocity Engine: Animations, transitions, and smooth scrolling are globally disabled in
testingmode to eliminate race conditions and speed up tests. - State Exposure: Populates
window.__coralite__.componentswith instance state mappings for glass-box assertions. - Event Spying: Logs all
CustomEventdispatches towindow.__coralite__.events. - Fail-Fast Hydration: Any hydration mismatch or client-side error triggers a fatal red overlay, ensuring test runners catch failures immediately.
The Component & Plugin Mocking System #
In testing mode, you can override server-side component rendering (server()) or mock the client and server runtime contexts of plugins by defining mocks in the configuration object passed to Coralite.
Config Option: testing.mocks #
Mocks are configured under testing.mocks. The configuration accepts two primary properties:
| Property | Type | Description |
|---|---|---|
components |
Record<string, CoraliteComponentMock> |
An object mapping component tag names (module IDs) to component mock definitions. |
plugins |
Record<string, CoralitePluginMock> |
An object mapping plugin names to plugin mock definitions. |
Component Mocks (testing.mocks.components) #
Each component mock is keyed by the component tag name and contains the following options:
| Property | Type | Description |
|---|---|---|
server |
(context: CoraliteServerContext) => Promise<Record<string, any>> | Record<string, any> |
An asynchronous or synchronous function replacing the component's original server() method. Receives standard component isomorphic context arguments (state, page, root, module, id, session, app, noHydration, and plugin context namespaces). Mock implementations can return dynamic data conditionally based on these attributes. |
Plugin Mocks (testing.mocks.plugins) #
Each plugin mock is keyed by the plugin's name and contains configuration to override server-side and/or client-side contexts:
| Property | Type | Description |
|---|---|---|
server.context |
Record<string, any> |
Object containing properties or methods merged into the plugin's server-side context (the return value of the plugin's Phase 2 resolver during the server build). |
client.context |
Record<string, any> |
Object containing properties or methods merged into the plugin's client-side context (the return value of the plugin's Phase 2 resolver in the browser). |
Compiler Cache & ISR Integration #
To avoid running tests against stale data during incremental builds, the compiler generates a cryptographic hash of the entire testing.mocks configuration. Any modification to the mock definitions, implementations, or configuration invalidates the Incremental Static Regeneration (ISR) cache and triggers a rebuild of only the pages consuming the mocked components or plugins.
The window.__coralite__.lifecycle.hydrated Promise #
Coralite injects a global promise named window.__coralite__.lifecycle.hydrated into the built HTML document. This promise acts as a reliable hook to signal the absolute completion of the client-side hydration phase.
Resolution Lifecycle Pipelines #
The framework strict-awaits the hydration process, ensuring the window.__coralite__.lifecycle.hydrated promise only resolves after two critical asynchronous pipelines finish:
| Pipeline | Internal Variable | Description |
|---|---|---|
| Component Definition | loadPromises |
The framework scans the DOM, dynamically imports the required module chunks for any custom elements found on the current page, and registers them via the browser's native customElements.define() API. |
| Declarative Scripts | declarativePromises |
The framework fully executes the asynchronous script blocks (defined in defineComponent) for all declarative component instances mapped on the page. |
E2E Testing Implications #
Because end-to-end testing frameworks (like Playwright or Cypress) interact with the DOM much faster than front-end hydration completes, tests MUST wait for this promise immediately after page navigation.
Failing to wait for this promise can lead to race conditions where the test runner attempts to click buttons, select inputs, or assert component state before the custom elements have actually mounted or initialized their internal variables, leading to flaky or failing tests.
For practical examples on how to implement this in popular testing frameworks, see the E2E Testing Guide.