v1.1.0: pooled runtime, 959 tests, production hardening (0 squash)
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* SceneTarget discriminated union and type guards.
|
||||
*
|
||||
* The scene target is the stage. It must be possible to mount any component,
|
||||
* any story, any page, and treat it as a deterministic scene. The adapter
|
||||
* boundary is the moat that keeps renderer concerns out of the core.
|
||||
*/
|
||||
|
||||
import type { Environment } from './types.js'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SceneTarget Union
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type SceneTarget =
|
||||
| { kind: 'page'; url: string }
|
||||
| { kind: 'playwright-page'; pageRef: string; url?: string }
|
||||
| { kind: 'storybook-story'; storyId: string; storybookUrl: string }
|
||||
| { kind: 'react-component'; rendererId: string; componentId: string }
|
||||
| { kind: 'vue-component'; rendererId: string; componentId: string }
|
||||
| { kind: 'custom-renderer'; rendererId: string; targetId: string }
|
||||
| { kind: 'fixture'; fixtureId: string }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// RenderCase Contract
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface RenderCase {
|
||||
caseId: string
|
||||
input: unknown
|
||||
env?: Partial<Environment>
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type Guards
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function isSceneTarget(value: unknown): value is SceneTarget {
|
||||
if (typeof value !== 'object' || value === null) return false
|
||||
const obj = value as Record<string, unknown>
|
||||
if (typeof obj.kind !== 'string') return false
|
||||
switch (obj.kind) {
|
||||
case 'page':
|
||||
return typeof obj.url === 'string'
|
||||
case 'playwright-page':
|
||||
return typeof obj.pageRef === 'string' && (obj.url === undefined || typeof obj.url === 'string')
|
||||
case 'storybook-story':
|
||||
return typeof obj.storyId === 'string' && typeof obj.storybookUrl === 'string'
|
||||
case 'react-component':
|
||||
return typeof obj.rendererId === 'string' && typeof obj.componentId === 'string'
|
||||
case 'vue-component':
|
||||
return typeof obj.rendererId === 'string' && typeof obj.componentId === 'string'
|
||||
case 'custom-renderer':
|
||||
return typeof obj.rendererId === 'string' && typeof obj.targetId === 'string'
|
||||
case 'fixture':
|
||||
return typeof obj.fixtureId === 'string'
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function isPageTarget(target: SceneTarget): target is Extract<SceneTarget, { kind: 'page' }> {
|
||||
return target.kind === 'page'
|
||||
}
|
||||
|
||||
export function isPlaywrightPageTarget(
|
||||
target: SceneTarget
|
||||
): target is Extract<SceneTarget, { kind: 'playwright-page' }> {
|
||||
return target.kind === 'playwright-page'
|
||||
}
|
||||
|
||||
export function isStorybookStoryTarget(
|
||||
target: SceneTarget
|
||||
): target is Extract<SceneTarget, { kind: 'storybook-story' }> {
|
||||
return target.kind === 'storybook-story'
|
||||
}
|
||||
|
||||
export function isReactComponentTarget(
|
||||
target: SceneTarget
|
||||
): target is Extract<SceneTarget, { kind: 'react-component' }> {
|
||||
return target.kind === 'react-component'
|
||||
}
|
||||
|
||||
export function isVueComponentTarget(
|
||||
target: SceneTarget
|
||||
): target is Extract<SceneTarget, { kind: 'vue-component' }> {
|
||||
return target.kind === 'vue-component'
|
||||
}
|
||||
|
||||
export function isCustomRendererTarget(
|
||||
target: SceneTarget
|
||||
): target is Extract<SceneTarget, { kind: 'custom-renderer' }> {
|
||||
return target.kind === 'custom-renderer'
|
||||
}
|
||||
|
||||
export function isFixtureTarget(target: SceneTarget): target is Extract<SceneTarget, { kind: 'fixture' }> {
|
||||
return target.kind === 'fixture'
|
||||
}
|
||||
|
||||
export function isComponentTarget(
|
||||
target: SceneTarget
|
||||
): target is
|
||||
| Extract<SceneTarget, { kind: 'react-component' }>
|
||||
| Extract<SceneTarget, { kind: 'vue-component' }>
|
||||
| Extract<SceneTarget, { kind: 'custom-renderer' }> {
|
||||
return target.kind === 'react-component' || target.kind === 'vue-component' || target.kind === 'custom-renderer'
|
||||
}
|
||||
|
||||
export function isRendererTarget(
|
||||
target: SceneTarget
|
||||
): target is
|
||||
| Extract<SceneTarget, { kind: 'react-component' }>
|
||||
| Extract<SceneTarget, { kind: 'vue-component' }>
|
||||
| Extract<SceneTarget, { kind: 'storybook-story' }>
|
||||
| Extract<SceneTarget, { kind: 'custom-renderer' }> {
|
||||
return (
|
||||
target.kind === 'react-component' ||
|
||||
target.kind === 'vue-component' ||
|
||||
target.kind === 'storybook-story' ||
|
||||
target.kind === 'custom-renderer'
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Matchers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function matchSceneTarget<R>(
|
||||
target: SceneTarget,
|
||||
cases: {
|
||||
page: (url: string) => R
|
||||
'playwright-page': (pageRef: string, url?: string) => R
|
||||
'storybook-story': (storyId: string, storybookUrl: string) => R
|
||||
'react-component': (rendererId: string, componentId: string) => R
|
||||
'vue-component': (rendererId: string, componentId: string) => R
|
||||
'custom-renderer': (rendererId: string, targetId: string) => R
|
||||
fixture: (fixtureId: string) => R
|
||||
}
|
||||
): R {
|
||||
switch (target.kind) {
|
||||
case 'page':
|
||||
return cases.page(target.url)
|
||||
case 'playwright-page':
|
||||
return cases['playwright-page'](target.pageRef, target.url)
|
||||
case 'storybook-story':
|
||||
return cases['storybook-story'](target.storyId, target.storybookUrl)
|
||||
case 'react-component':
|
||||
return cases['react-component'](target.rendererId, target.componentId)
|
||||
case 'vue-component':
|
||||
return cases['vue-component'](target.rendererId, target.componentId)
|
||||
case 'custom-renderer':
|
||||
return cases['custom-renderer'](target.rendererId, target.targetId)
|
||||
case 'fixture':
|
||||
return cases.fixture(target.fixtureId)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Factory Functions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function pageTarget(url: string): SceneTarget {
|
||||
return { kind: 'page', url }
|
||||
}
|
||||
|
||||
export function playwrightPageTarget(pageRef: string, url?: string): SceneTarget {
|
||||
return { kind: 'playwright-page', pageRef, url }
|
||||
}
|
||||
|
||||
export function storybookStoryTarget(storyId: string, storybookUrl: string): SceneTarget {
|
||||
return { kind: 'storybook-story', storyId, storybookUrl }
|
||||
}
|
||||
|
||||
export function reactComponentTarget(rendererId: string, componentId: string): SceneTarget {
|
||||
return { kind: 'react-component', rendererId, componentId }
|
||||
}
|
||||
|
||||
export function vueComponentTarget(rendererId: string, componentId: string): SceneTarget {
|
||||
return { kind: 'vue-component', rendererId, componentId }
|
||||
}
|
||||
|
||||
export function customRendererTarget(rendererId: string, targetId: string): SceneTarget {
|
||||
return { kind: 'custom-renderer', rendererId, targetId }
|
||||
}
|
||||
|
||||
export function fixtureTarget(fixtureId: string): SceneTarget {
|
||||
return { kind: 'fixture', fixtureId }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// RenderCase Factory
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function createRenderCase(
|
||||
caseId: string,
|
||||
input: unknown,
|
||||
env?: Partial<Environment>,
|
||||
metadata?: Record<string, unknown>
|
||||
): RenderCase {
|
||||
return { caseId, input, env, metadata }
|
||||
}
|
||||
Reference in New Issue
Block a user