/** * Renders a piece for a participant who did not set it up, on that * participant's first start, through the worker's renderer under the shell's * display ceiling. */ import { expect } from "@std/expect"; import { describe, it } from "@std/testing/bdd"; import type { VDomOp } from "@commonfabric/html/vdom-ops"; import { WorkerReconciler } from "@commonfabric/html/worker"; import { Identity } from "@commonfabric/identity"; import { defaultRenderConfidentialityCeiling } from "@commonfabric/lib-shell/runtime"; import { Runtime } from "@commonfabric/runner"; import { rendererVDOMSchema } from "@commonfabric/runner/schemas"; import { EmulatedStorageManager } from "../../../runner/src/storage/v2-emulate.ts"; import { newSharedServer } from "../../../runner/test/memory-v2-test-utils.ts"; import { renderConfidentialityResolverFor, renderMembershipProviderFor, renderModulePolicySourceFor, renderSpaceAccessProviderFor, } from "@/backends/runtime-processor.ts"; const owner = await Identity.fromPassphrase("render after first start owner"); const visitor = await Identity.fromPassphrase( "render after first start visitor", ); const space = owner.did(); // The piece hands a cell labeled with its module policy and an unlabeled // origin cell to a part it instantiates per participant. The part binds the // origin to an element, so the renderer reads the binding through the part's // instance, which also holds the link to the labeled cell. const program = { main: "/main.tsx", files: [{ name: "/main.tsx", contents: `/// import { Confidential, pattern, UI, type VNode, Writable, } from "commonfabric"; import type { PolicyOf } from "commonfabric/cfc"; import { cfcPattern, exchangeRule, exchangeRules, THIS_POLICY, v, } from "commonfabric/cfc"; export const release = exchangeRule({ appliesTo: THIS_POLICY, pre: { integrity: [cfcPattern.hasRole(v("user"), THIS_POLICY.subject, "reader")] }, post: { addAlternatives: [cfcPattern.user(v("user"))] }, }); export const rules = exchangeRules([release]); type Brief = Confidential]>; type Origin = { name: string }; const Part = pattern<{ brief: Brief; origin: Origin }, { [UI]: VNode }>( ({ origin }) => { const seen = new Writable.perUser(null); return { [UI]:
, }; }, ); export default pattern, { [UI]: VNode }>(() => { const brief = new Writable.perSpace("the brief"); const origin = new Writable.perSpace({ name: "origin" }); const part = Part.asScope("user")({ brief, origin }); return { [UI]: part[UI] }; }); `, }], }; describe("render-after-first-start", () => { it("binds the element the part's instance carries", async () => { // This participant's first start writes its instance of the part, whose // link to the labeled cell requires the policy manifest the owner's // setup installed. The element bound to the unlabeled origin, read // through that instance, ends with its binding, which the shell's // display ceiling admits for this participant. const server = newSharedServer(); const ownerStorage = EmulatedStorageManager.connectTo(server, { as: owner, }); const ownerRuntime = new Runtime({ apiUrl: new URL(import.meta.url), storageManager: ownerStorage, }); const storage = EmulatedStorageManager.connectTo(server, { as: visitor }); const runtime = new Runtime({ apiUrl: new URL(import.meta.url), storageManager: storage, }); try { const compiled = await ownerRuntime.patternManager.compilePattern( program, { space }, ); const tx = ownerRuntime.edit(); const setUp = ownerRuntime.getCell(space, "piece", undefined, tx); await setUp.sync(); ownerRuntime.run(tx, compiled, {}, setUp); ownerRuntime.prepareTxForCommit(tx); expect((await tx.commit().settled).error).toBeUndefined(); await ownerRuntime.idle(); await ownerStorage.synced(); const failures: unknown[] = []; runtime.pieceStartCommitFailureObserver = ({ error }) => failures.push(error); const ceiling = defaultRenderConfidentialityCeiling(visitor.did()); const membershipProvider = renderMembershipProviderFor( runtime, visitor, ceiling, ); const modulePolicySource = renderModulePolicySourceFor(runtime, ceiling); const ops: VDomOp[] = []; const reconciler = new WorkerReconciler({ renderDeclassificationPolicy: "deny", renderConfidentialityCeiling: ceiling, resolveRenderConfidentiality: renderConfidentialityResolverFor( runtime, visitor, ceiling, visitor.did(), membershipProvider, modulePolicySource, ), membershipProvider, modulePolicySource, spaceAccess: renderSpaceAccessProviderFor(runtime), onOps: (batch) => { for (const op of batch) ops.push(op); return ops.length; }, }); const piece = runtime.getCell(space, "piece"); await piece.sync(); // Mounted as the start resolves, which is before its commit's verdict. await runtime.start(piece); const unmount = reconciler.mount(piece.asSchema(rendererVDOMSchema)); await runtime.idle(); await runtime.runner.idlePieceInstantiationSettlements(); await storage.synced(); await runtime.idle(); reconciler.flush(); unmount(); expect(failures).toEqual([]); const created = ops.find((op) => op.op === "create-element" && op.tagName === "cf-owner-view" ); if (created?.op !== "create-element") { throw new Error("the part's element was not rendered"); } // Whether the element's `originator` is bound once every op is applied. let bound = false; for (const op of ops) { if ( op.op === "set-binding" && op.nodeId === created.nodeId && op.propName === "originator" ) { bound = true; } else if ( op.op === "remove-prop" && op.nodeId === created.nodeId && op.key === "originator" ) { bound = false; } } expect(bound).toBe(true); } finally { await storage.synced(); await runtime.dispose(); await ownerRuntime.dispose(); await storage.close(); await ownerStorage.close(); await server.close(); } }); });