@@ -22,12 +22,14 @@ import { getFieldSchemaMap } from "../../../../__test__/data/fieldSchemaMap";
2222import {
2323 waitForHoverOutline ,
2424 waitForCursorIcon ,
25+ waitForBuilderSDKToBeInitialized ,
2526} from "../../../../__test__/utils" ;
2627import Config from "../../../../configManager/configManager" ;
2728import { VisualBuilder } from "../../../index" ;
2829import { FieldSchemaMap } from "../../../utils/fieldSchemaMap" ;
2930import { mockDomRect } from "./mockDomRect" ;
3031import { waitFor } from "@testing-library/preact" ;
32+ import visualBuilderPostMessage from "../../../utils/visualBuilderPostMessage" ;
3133
3234vi . mock ( "../../../utils/visualBuilderPostMessage" , async ( ) => {
3335 const { getAllContentTypes } = await vi . importActual <
@@ -105,14 +107,21 @@ const MULTIPLE_FIELD = {
105107describe ( "When an element is hovered in visual builder mode" , ( ) => {
106108 let mousemoveEvent : Event ;
107109 const fieldSchemaMap = getFieldSchemaMap ( ) . all_fields ;
110+ let visualBuilder : VisualBuilder ;
108111
109- beforeAll ( ( ) => {
112+ beforeAll ( async ( ) => {
110113 // Pre-set all field schemas in cache to avoid async fetches during hover
111114 // This significantly speeds up tests, especially for html-rte, json-rte, link fields
112115 FieldSchemaMap . setFieldSchema ( "all_fields" , fieldSchemaMap ) ;
113116
114- // Field schemas are already set above - no need for additional caching
115- // The FieldSchemaMap.setFieldSchema call above sets all fields at once
117+ // Create and initialize VisualBuilder once for all tests in this describe block
118+ // This is much faster than creating/destroying it for each test
119+ Config . reset ( ) ;
120+ Config . set ( "mode" , 2 ) ;
121+ visualBuilder = new VisualBuilder ( ) ;
122+ // Wait for VisualBuilder to fully initialize before running any tests
123+ // This is critical in CI where async operations may be slower
124+ await waitForBuilderSDKToBeInitialized ( visualBuilderPostMessage ) ;
116125 } ) ;
117126
118127 beforeEach ( ( ) => {
@@ -125,18 +134,51 @@ describe("When an element is hovered in visual builder mode", () => {
125134 } ) ;
126135
127136 afterEach ( async ( ) => {
128- document . getElementsByTagName ( "html" ) [ 0 ] . innerHTML = "" ;
137+ // Clear test-specific DOM elements but preserve VisualBuilder container
138+ // Remove all children of body except the visual builder container
139+ const body = document . body ;
140+ const visualBuilderContainer = document . querySelector (
141+ '[data-testid="visual-builder__container"]'
142+ ) ;
143+ // Remove all body children except the visual builder container
144+ Array . from ( body . children ) . forEach ( ( child ) => {
145+ if ( child !== visualBuilderContainer ) {
146+ body . removeChild ( child ) ;
147+ }
148+ } ) ;
149+ // Reset VisualBuilder global state to ensure clean state between tests
150+ VisualBuilder . VisualBuilderGlobalState . value = {
151+ previousSelectedEditableDOM : null ,
152+ previousHoveredTargetDOM : null ,
153+ previousEmptyBlockParents : [ ] ,
154+ focusFieldValue : null ,
155+ focusFieldReceivedInput : false ,
156+ audienceMode : false ,
157+ locale : "en-us" ,
158+ variant : null ,
159+ focusElementObserver : null ,
160+ referenceParentMap : { } ,
161+ isFocussed : false ,
162+ } ;
129163 } ) ;
130164
131165 afterAll ( ( ) => {
166+ // Clean up VisualBuilder once after all tests complete
167+ if ( visualBuilder ) {
168+ try {
169+ visualBuilder . destroy ( ) ;
170+ } catch ( error ) {
171+ // Ignore errors during cleanup - container may already be removed
172+ // This can happen if DOM was cleared in a way that removed the container
173+ }
174+ }
132175 Config . reset ( ) ;
133176 } ) ;
134177
135178 // Test single field pattern (no multiple support)
136179 // This represents all single-only fields: boolean, date, markdown, etc.
137180 describe ( `${ SINGLE_FIELD . name } field (represents single field pattern)` , ( ) => {
138181 let fieldElement : HTMLElement ;
139- let visualBuilder : VisualBuilder ;
140182
141183 beforeEach ( ( ) => {
142184 fieldElement = document . createElement ( "p" ) ;
@@ -146,11 +188,7 @@ describe("When an element is hovered in visual builder mode", () => {
146188 . mockReturnValue ( mockDomRect . singleLeft ( ) ) ;
147189
148190 document . body . appendChild ( fieldElement ) ;
149- visualBuilder = new VisualBuilder ( ) ;
150- } ) ;
151-
152- afterEach ( ( ) => {
153- visualBuilder . destroy ( ) ;
191+ // VisualBuilder is already initialized in outer beforeAll
154192 } ) ;
155193
156194 test ( "should have outline and custom cursor" , async ( ) => {
@@ -163,7 +201,7 @@ describe("When an element is hovered in visual builder mode", () => {
163201 expect ( hoverOutline ) . toHaveAttribute ( "style" ) ;
164202
165203 // Wait for cursor icon to be set (not "loading")
166- await waitForCursorIcon ( SINGLE_FIELD . icon , { timeout : 5000 } ) ;
204+ await waitForCursorIcon ( SINGLE_FIELD . icon , { timeout : 1000 } ) ;
167205
168206 const customCursor = document . querySelector (
169207 `[data-testid="visual-builder__cursor"]`
@@ -180,7 +218,6 @@ describe("When an element is hovered in visual builder mode", () => {
180218 // This represents all multiple field types: select, html-rte, json-rte, link, reference, etc.
181219 describe ( `${ MULTIPLE_FIELD . name } field (represents multiple field pattern)` , ( ) => {
182220 let fieldElement : HTMLElement ;
183- let visualBuilder : VisualBuilder ;
184221
185222 beforeEach ( ( ) => {
186223 fieldElement = document . createElement ( "p" ) ;
@@ -190,11 +227,7 @@ describe("When an element is hovered in visual builder mode", () => {
190227 . mockReturnValue ( mockDomRect . singleLeft ( ) ) ;
191228
192229 document . body . appendChild ( fieldElement ) ;
193- visualBuilder = new VisualBuilder ( ) ;
194- } ) ;
195-
196- afterEach ( ( ) => {
197- visualBuilder . destroy ( ) ;
230+ // VisualBuilder is already initialized in outer beforeAll
198231 } ) ;
199232
200233 test ( "should have outline and custom cursor" , async ( ) => {
@@ -207,7 +240,7 @@ describe("When an element is hovered in visual builder mode", () => {
207240 expect ( hoverOutline ) . toHaveAttribute ( "style" ) ;
208241
209242 // Wait for cursor icon to be set (not "loading")
210- await waitForCursorIcon ( MULTIPLE_FIELD . icon , { timeout : 5000 } ) ;
243+ await waitForCursorIcon ( MULTIPLE_FIELD . icon , { timeout : 1000 } ) ;
211244
212245 const customCursor = document . querySelector (
213246 `[data-testid="visual-builder__cursor"]`
@@ -225,7 +258,6 @@ describe("When an element is hovered in visual builder mode", () => {
225258 let container : HTMLDivElement ;
226259 let firstField : HTMLElement ;
227260 let secondField : HTMLElement ;
228- let visualBuilder : VisualBuilder ;
229261
230262 beforeEach ( ( ) => {
231263 container = document . createElement ( "div" ) ;
@@ -255,12 +287,7 @@ describe("When an element is hovered in visual builder mode", () => {
255287 container . appendChild ( firstField ) ;
256288 container . appendChild ( secondField ) ;
257289 document . body . appendChild ( container ) ;
258-
259- visualBuilder = new VisualBuilder ( ) ;
260- } ) ;
261-
262- afterEach ( ( ) => {
263- visualBuilder . destroy ( ) ;
290+ // VisualBuilder is already initialized in outer beforeAll
264291 } ) ;
265292
266293 test ( "should have outline and custom cursor on container" , async ( ) => {
@@ -273,7 +300,7 @@ describe("When an element is hovered in visual builder mode", () => {
273300 expect ( hoverOutline ) . toHaveAttribute ( "style" ) ;
274301
275302 // Wait for cursor icon to be set (not "loading")
276- await waitForCursorIcon ( MULTIPLE_FIELD . icon , { timeout : 5000 } ) ;
303+ await waitForCursorIcon ( MULTIPLE_FIELD . icon , { timeout : 1000 } ) ;
277304
278305 const customCursor = document . querySelector (
279306 `[data-testid="visual-builder__cursor"]`
@@ -295,7 +322,7 @@ describe("When an element is hovered in visual builder mode", () => {
295322 expect ( hoverOutline ) . toHaveAttribute ( "style" ) ;
296323
297324 // Wait for cursor icon to be set (not "loading")
298- await waitForCursorIcon ( MULTIPLE_FIELD . icon , { timeout : 5000 } ) ;
325+ await waitForCursorIcon ( MULTIPLE_FIELD . icon , { timeout : 1000 } ) ;
299326
300327 const customCursor = document . querySelector (
301328 `[data-testid="visual-builder__cursor"]`
0 commit comments