mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 08:03:28 +00:00
test(dev): tighten confirmation dialog boundary guard
This commit is contained in:
@@ -1,10 +1,6 @@
|
||||
import { createContext, useContext } from 'react'
|
||||
|
||||
// Why: the context stays in a component-free module so React Fast Refresh never
|
||||
// rebuilds it. A module exporting both a component and this hook can never be a
|
||||
// refresh boundary, so Vite applies its updates in two waves under different
|
||||
// `?t=` stamps; importers refreshed in different waves then hold different
|
||||
// context objects and consumers throw below until a full reload.
|
||||
// Keep the context component-free so Fast Refresh preserves its identity.
|
||||
|
||||
export type ConfirmationDialogOptions = {
|
||||
title: string
|
||||
|
||||
@@ -8,53 +8,15 @@ import * as providerModule from '@/components/confirmation-dialog'
|
||||
import { ConfirmationDialogProvider } from '@/components/confirmation-dialog'
|
||||
import { useConfirmationDialog } from '@/components/confirmation-dialog-context'
|
||||
|
||||
// Why: crash reports 42d63029 / 5ee3b8b6. A module exporting both the provider and the
|
||||
// hook is not a Fast Refresh boundary, so Vite applied its updates in two passes under two
|
||||
// `?t=` stamps, `createContext` ran twice, and the provider published a different context
|
||||
// object than `ChecksPanel` read.
|
||||
//
|
||||
// Two earlier versions of this test were evaded, both by approximating the runtime instead
|
||||
// of asking it. Matching source text missed a re-export of the hook; a hand-rolled `^[A-Z]`
|
||||
// name check called `export class ConfirmationDialogQueue {}` a component when the runtime
|
||||
// rejects it. Hence the real predicate below rather than a third approximation.
|
||||
|
||||
// react-refresh ships no types; take the one binding this needs.
|
||||
const { isLikelyComponentType } = createRequire(import.meta.url)('react-refresh/runtime') as {
|
||||
isLikelyComponentType: (value: unknown) => boolean
|
||||
}
|
||||
|
||||
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
||||
return (
|
||||
Object.prototype.toString.call(value) === '[object Object]' &&
|
||||
((value as object).constructor === Object || (value as object).constructor === undefined)
|
||||
)
|
||||
}
|
||||
|
||||
// Mirrors `isCompoundComponent` in @vitejs/plugin-react's refresh-runtime, which accepts a
|
||||
// plain object whose every member is a component (`export const Dialog = { Root, Trigger }`).
|
||||
// The runtime's third accept term, `prevExports[key] === nextExports[key]`, is deliberately
|
||||
// left out: it holds only for exports whose identity survives a re-execution, which nothing
|
||||
// but a primitive does, and an unchanged primitive breaks the boundary the moment it changes.
|
||||
function isRefreshSafeExport(value: unknown): boolean {
|
||||
if (isLikelyComponentType(value)) {
|
||||
return true
|
||||
}
|
||||
if (!isPlainObject(value)) {
|
||||
return false
|
||||
}
|
||||
return Object.values(value).every((member) => isLikelyComponentType(member))
|
||||
}
|
||||
|
||||
function unsafeExports(moduleNamespace: object): string[] {
|
||||
return Object.entries(moduleNamespace)
|
||||
.filter(([, value]) => !isRefreshSafeExport(value))
|
||||
.map(([name]) => name)
|
||||
}
|
||||
|
||||
describe('confirmation dialog Fast Refresh boundary', () => {
|
||||
it('exports nothing from the provider module that invalidates the refresh boundary', () => {
|
||||
expect(Object.keys(providerModule).length).toBeGreaterThan(0)
|
||||
expect(unsafeExports(providerModule)).toEqual([])
|
||||
expect(Object.keys(providerModule)).toEqual(['ConfirmationDialogProvider'])
|
||||
expect(isLikelyComponentType(providerModule.ConfirmationDialogProvider)).toBe(true)
|
||||
})
|
||||
|
||||
it('keeps the context and hook in a component-free module', () => {
|
||||
|
||||
Reference in New Issue
Block a user