Files
effect-view/packages/vite-plugin/src/runtime.test.ts
Thilawyn 4b712de788
Publish / publish (push) Successful in 3m23s
Lint / lint (push) Successful in 48s
Finalize the Effect View rename and refresh docs, examples, and tooling (#59)
## Summary

- Rename `effect-fc-next` to `effect-view` and update package metadata, internal symbols, imports, and repository references.
- Promote the Effect 4 example to `packages/example` and move the legacy Effect 3 example to `packages/effect-fc-example`.
- Add `effect-view` to the npm publishing workflow.
- Update the Vite Fast Refresh plugin for `effect-view` and rename `effectViewPlugin()` to `effectView()`.
- Upgrade Effect, React, TypeScript, build tooling, and related dependencies.
- Refresh the documentation and homepage.

## Documentation

- Add a dedicated Async guide covering:
  - Suspense fallbacks
  - Hook ordering
  - Memoization
  - Structural prop equality
- Clarify synchronous and asynchronous component behavior.
- Document runner scope and service requirements.
- Explain stable construction of Query, Mutation, and Form instances.
- Expand focused Lens and Form examples, including curried focus helpers.
- Document the optional `@effect/platform-browser` dependency for window-focus query refresh.
- Improve the Forms guide structure and examples.
- Fix responsive homepage headline wrapping and update homepage copy.

## Notable API changes

- Package rename:

  ```diff
  - effect-fc-next
  + effect-view

---------

Co-authored-by: Julien Valverdé <julien.valverde@mailo.com>
Reviewed-on: #59
2026-07-27 04:11:42 +02:00

79 lines
2.7 KiB
TypeScript

import type * as Component from "effect-view/Component"
import * as Refreshable from "effect-view/Refreshable"
import { describe, expect, it, vi } from "vitest"
import { accept, register } from "./runtime.js"
describe("refresh runtime", () => {
const component = <A extends object>(value: A): A & Component.Component.Any =>
value as A & Component.Component.Any
const refreshCell = (value: Component.Component.Any): Refreshable.Cell => {
if (!Refreshable.isRefreshable(value))
throw new Error("Expected a refreshable component")
return value[Refreshable.RefreshableTypeId]
}
it("retains a cell and notifies subscribers for compatible updates", async () => {
const hot = {
data: {},
accept: vi.fn(),
invalidate: vi.fn(),
}
const first = register(component({ body: "first" }), hot, "module:View", "hooks")
const cell = refreshCell(first)
const listener = vi.fn()
cell.subscribe(listener)
const second = register(component({ body: "second" }), hot, "module:View", "hooks")
const secondCell = refreshCell(second)
expect(secondCell).toBe(cell)
expect(cell.current).toBe(second)
expect(cell.snapshot).toEqual({
revision: 1,
resetRevision: 0,
})
await Promise.resolve()
expect(listener).toHaveBeenCalledTimes(1)
})
it("increments resetRevision for incompatible and forced updates", () => {
const hot = {
data: {},
accept: vi.fn(),
invalidate: vi.fn(),
}
const first = register(component({}), hot, "module:View", "one")
const cell = refreshCell(first)
register(component({}), hot, "module:View", "two")
expect(cell.snapshot.resetRevision).toBe(1)
register(component({}), hot, "module:View", "two", true)
expect(cell.snapshot.resetRevision).toBe(2)
})
it("is inert outside a Vite hot context", () => {
const descriptor = component({})
expect(register(descriptor, undefined, "module:View", "hooks")).toBe(descriptor)
expect(Refreshable.isRefreshable(descriptor)).toBe(false)
})
it("invalidates when the module's View IDs change", () => {
const hot = {
data: {},
accept: vi.fn(),
invalidate: vi.fn(),
}
accept(hot, ["module:First"])
expect(hot.accept).toHaveBeenCalledTimes(1)
expect(hot.invalidate).not.toHaveBeenCalled()
accept(hot, ["module:Second"])
expect(hot.invalidate).toHaveBeenCalledWith("[effect-view] Effect View exports changed")
})
})