diff --git a/tests/unit/transform-foundation.integration.test.tsx b/tests/unit/transform-foundation.integration.test.tsx index 8e3514d3..a82989c1 100644 --- a/tests/unit/transform-foundation.integration.test.tsx +++ b/tests/unit/transform-foundation.integration.test.tsx @@ -225,6 +225,63 @@ async function renderQuadTransformFixtureApp() { return fixture; } +async function renderMultiSelectionFixtureApp() { + const brushA = createBoxBrush({ + id: "brush-multi-select-a", + name: "Brush Multi A", + center: { + x: -2, + y: 1, + z: 0 + } + }); + const brushB = createBoxBrush({ + id: "brush-multi-select-b", + name: "Brush Multi B", + center: { + x: 4, + y: 1, + z: 0 + } + }); + const playerStart = createPlayerStartEntity({ + id: "entity-multi-select-player", + name: "Player Multi Fixture", + position: { + x: 0, + y: 0, + z: -4 + } + }); + const store = createEditorStore({ + initialDocument: { + ...createEmptySceneDocument({ name: "Multi Selection Fixture" }), + brushes: { + [brushA.id]: brushA, + [brushB.id]: brushB + }, + entities: { + [playerStart.id]: playerStart + } + } + }); + + render(); + + await waitFor(() => { + expect(viewportHostInstances.length).toBeGreaterThan(0); + expect(getTopLeftViewportHost().setBrushSelectionChangeHandler).toHaveBeenCalled(); + }); + + return { + store, + brushA, + brushB, + playerStart, + viewportHost: getTopLeftViewportHost() + }; +} + function getLatestTransformSession( store: ReturnType ): ActiveTransformSession { @@ -576,6 +633,46 @@ describe("transform foundation integration", () => { expect(screen.getByText(/selection mode set to object/i)).toBeInTheDocument(); }); + it("keeps outliner and viewport selection state synchronized for same-kind multi-selection", async () => { + const { store, brushA, brushB, playerStart, viewportHost } = + await renderMultiSelectionFixtureApp(); + + await act(async () => { + fireEvent.click(screen.getByTestId(`outliner-brush-${brushA.id}`)); + }); + await act(async () => { + fireEvent.click(screen.getByTestId(`outliner-brush-${brushB.id}`), { + shiftKey: true + }); + }); + + expect(store.getState().selection).toEqual({ + kind: "brushes", + ids: [brushA.id, brushB.id] + }); + expect(store.getState().activeSelectionId).toBe(brushB.id); + expect(screen.getByText("Whitebox Solids")).toBeInTheDocument(); + expect(screen.getByText("2 selected")).toBeInTheDocument(); + expect(screen.getByText("Brush Multi B")).toBeInTheDocument(); + + const selectionHandler = viewportHost.setBrushSelectionChangeHandler.mock.calls.at( + -1 + )?.[0] as ((selection: { kind: "entities"; ids: string[] }) => void); + + act(() => { + selectionHandler({ + kind: "entities", + ids: [playerStart.id] + }); + }); + + expect(store.getState().selection).toEqual({ + kind: "entities", + ids: [playerStart.id] + }); + expect(store.getState().activeSelectionId).toBe(playerStart.id); + }); + it("moves an entity through the shared transform controller", async () => { const { store, playerStart, viewportHost } = await renderTransformFixtureApp();