@@ -2442,4 +2442,242 @@ export class BrowserPage extends BasePage {
24422442 this . page . locator ( `[data-testid="hash-field-${ fieldName } "]` ) ,
24432443 ) . not . toBeVisible ( )
24442444 }
2445+
2446+ async editJsonProperty (
2447+ propertyKey : string ,
2448+ newValue : string | number | boolean ,
2449+ ) : Promise < void > {
2450+ // TODO: Ideally this should find by property key, but the current DOM structure
2451+ // makes it complex to navigate from key to value reliably. For now, we use the
2452+ // working approach of finding by current value.
2453+ const currentValue = await this . getJsonPropertyValue ( propertyKey )
2454+ if ( ! currentValue ) {
2455+ throw new Error ( `Property "${ propertyKey } " not found` )
2456+ }
2457+
2458+ // Find and click the value element
2459+ const valueElement = this . page
2460+ . getByTestId ( 'json-scalar-value' )
2461+ . filter ( { hasText : currentValue } )
2462+ . first ( )
2463+
2464+ await valueElement . click ( )
2465+ await expect ( this . inlineItemEditor ) . toBeVisible ( )
2466+
2467+ // Format and apply the new value
2468+ const formattedValue =
2469+ typeof newValue === 'string' ? `"${ newValue } "` : newValue . toString ( )
2470+
2471+ await this . inlineItemEditor . clear ( )
2472+ await this . inlineItemEditor . fill ( formattedValue )
2473+ await this . applyButton . click ( )
2474+ await expect ( this . inlineItemEditor ) . not . toBeVisible ( )
2475+
2476+ if ( await this . toast . isCloseButtonVisible ( ) ) {
2477+ await this . toast . closeToast ( )
2478+ }
2479+ }
2480+
2481+ // Convenience methods that use the generic editJsonProperty method
2482+ async editJsonString ( propertyKey : string , newValue : string ) : Promise < void > {
2483+ await this . editJsonProperty ( propertyKey , newValue )
2484+ }
2485+
2486+ async editJsonNumber ( propertyKey : string , newValue : number ) : Promise < void > {
2487+ await this . editJsonProperty ( propertyKey , newValue )
2488+ }
2489+
2490+ async editJsonBoolean (
2491+ propertyKey : string ,
2492+ newValue : boolean ,
2493+ ) : Promise < void > {
2494+ await this . editJsonProperty ( propertyKey , newValue )
2495+ }
2496+
2497+ async addJsonProperty (
2498+ key : string ,
2499+ value : string | number | boolean ,
2500+ ) : Promise < void > {
2501+ // For JSON objects, add a new property at the same level
2502+ await this . addJsonObjectButton . click ( )
2503+
2504+ // Wait for the form to appear
2505+ await expect ( this . jsonKeyInput ) . toBeVisible ( )
2506+ await expect ( this . jsonValueInput ) . toBeVisible ( )
2507+
2508+ // Format the key and value properly for JSON
2509+ const formattedKey = `"${ key } "`
2510+ let formattedValue : string
2511+ if ( typeof value === 'string' ) {
2512+ formattedValue = `"${ value } "`
2513+ } else {
2514+ formattedValue = value . toString ( )
2515+ }
2516+
2517+ // Fill the key and value
2518+ await this . jsonKeyInput . clear ( )
2519+ await this . jsonKeyInput . fill ( formattedKey )
2520+ await this . jsonValueInput . clear ( )
2521+ await this . jsonValueInput . fill ( formattedValue )
2522+
2523+ // Apply the changes
2524+ await this . applyButton . click ( )
2525+
2526+ // Wait for the form to disappear
2527+ await expect ( this . jsonKeyInput ) . not . toBeVisible ( )
2528+
2529+ // Close any success toast if it appears
2530+ if ( await this . toast . isCloseButtonVisible ( ) ) {
2531+ await this . toast . closeToast ( )
2532+ }
2533+ }
2534+
2535+ async editEntireJsonStructure ( newJsonStructure : string ) : Promise < void > {
2536+ // Switch to Monaco editor
2537+ await this . page
2538+ . getByRole ( 'button' , { name : 'Change editor type' } )
2539+ . click ( )
2540+
2541+ // Wait for Monaco editor
2542+ const monacoContainer = this . page . getByTestId ( 'monaco-editor-json-data' )
2543+ await expect ( monacoContainer ) . toBeVisible ( )
2544+
2545+ // Clear and set new JSON content
2546+ const textarea = monacoContainer . locator ( 'textarea' ) . first ( )
2547+ await textarea . focus ( )
2548+ await this . page . keyboard . press ( 'Control+A' )
2549+ await this . page . keyboard . press ( 'Delete' )
2550+ await textarea . type ( newJsonStructure )
2551+
2552+ // Wait for button to be enabled and click it
2553+ const updateButton = this . page . getByTestId ( 'json-data-update-btn' )
2554+ await expect ( updateButton ) . toBeEnabled ( )
2555+ await updateButton . click ( )
2556+
2557+ // Close editor and return to tree view
2558+ const cancelButton = this . page . getByTestId ( 'json-data-cancel-btn' )
2559+ if ( await cancelButton . isVisible ( ) ) {
2560+ await cancelButton . click ( )
2561+ }
2562+
2563+ if ( await this . toast . isCloseButtonVisible ( ) ) {
2564+ await this . toast . closeToast ( )
2565+ }
2566+ }
2567+
2568+ async verifyJsonPropertyExists ( key : string , value : string ) : Promise < void > {
2569+ // Expand all objects and get the actual value
2570+ const actualValue = await this . getJsonPropertyValue ( key )
2571+ expect ( actualValue ) . toBe ( value )
2572+ }
2573+
2574+ async verifyJsonPropertyNotExists ( key : string ) : Promise < void > {
2575+ const actualValue = await this . getJsonPropertyValue ( key )
2576+ expect ( actualValue ) . toBeNull ( )
2577+ }
2578+
2579+ async waitForJsonDetailsToBeVisible ( ) : Promise < void > {
2580+ await expect ( this . page . getByTestId ( 'json-details' ) ) . toBeVisible ( )
2581+ }
2582+
2583+ async waitForJsonPropertyUpdate (
2584+ key : string ,
2585+ expectedValue : string ,
2586+ ) : Promise < void > {
2587+ await expect
2588+ . poll ( async ( ) => {
2589+ try {
2590+ const actualValue = await this . getJsonPropertyValue ( key )
2591+ return actualValue === expectedValue
2592+ } catch ( error ) {
2593+ return false
2594+ }
2595+ } )
2596+ . toBe ( true )
2597+ }
2598+
2599+ async expandAllJsonObjects ( ) : Promise < void > {
2600+ // Keep expanding until no more expand buttons exist
2601+ while ( true ) {
2602+ const expandButtons = this . page . getByTestId ( 'expand-object' )
2603+ const count = await expandButtons . count ( )
2604+
2605+ if ( count === 0 ) {
2606+ break // No more expand buttons to click
2607+ }
2608+
2609+ // Click ALL visible expand buttons in this iteration
2610+ const buttons = await expandButtons . all ( )
2611+ for ( const button of buttons ) {
2612+ if ( await button . isVisible ( ) ) {
2613+ await button . click ( )
2614+ }
2615+ }
2616+
2617+ // Wait for DOM to be ready before checking for new buttons
2618+ await this . page . waitForLoadState ( 'domcontentloaded' )
2619+ }
2620+ }
2621+
2622+ async getJsonPropertyValue ( propertyName : string ) : Promise < string | null > {
2623+ // Expand all objects to make sure we can see the property
2624+ await this . expandAllJsonObjects ( )
2625+
2626+ // Get the JSON content and look for the property with a simple approach
2627+ const jsonContent = await this . jsonKeyValue . textContent ( )
2628+ if ( ! jsonContent ) return null
2629+
2630+ // Use a more precise regex pattern for different value types
2631+ // Try patterns for strings, numbers, and booleans
2632+ const patterns = [
2633+ new RegExp ( `${ propertyName } :"([^"]*)"` , 'g' ) , // String values: name:"value"
2634+ new RegExp ( `${ propertyName } :(\\d+(?:\\.\\d+)?)` , 'g' ) , // Number values: age:25
2635+ new RegExp ( `${ propertyName } :(true|false)` , 'g' ) , // Boolean values: active:true
2636+ ]
2637+
2638+ for ( const pattern of patterns ) {
2639+ pattern . lastIndex = 0 // Reset regex state
2640+ const match = pattern . exec ( jsonContent )
2641+ if ( match && match [ 1 ] ) {
2642+ return match [ 1 ]
2643+ }
2644+ }
2645+
2646+ return null
2647+ }
2648+
2649+ async verifyJsonStructureValid ( ) : Promise < void > {
2650+ // Check that no JSON error is displayed
2651+ await expect ( this . jsonError ) . not . toBeVisible ( )
2652+
2653+ // Check that the JSON data container is visible
2654+ await expect ( this . jsonKeyValue ) . toBeVisible ( )
2655+ }
2656+
2657+ async cancelJsonScalarValueEdit ( propertyKey : string ) : Promise < void > {
2658+ // Store original value, start editing, then cancel
2659+ const originalValue = await this . getJsonPropertyValue ( propertyKey )
2660+ if ( ! originalValue ) {
2661+ throw new Error ( `Property "${ propertyKey } " not found` )
2662+ }
2663+
2664+ await this . expandAllJsonObjects ( )
2665+
2666+ // Find the element containing this value
2667+ const targetElement = this . page
2668+ . getByTestId ( 'json-scalar-value' )
2669+ . filter ( { hasText : originalValue } )
2670+ . first ( )
2671+
2672+ // Start edit, make change, then cancel
2673+ await targetElement . click ( )
2674+ await expect ( this . inlineItemEditor ) . toBeVisible ( )
2675+ await this . inlineItemEditor . fill ( '"canceled_value"' )
2676+ await this . page . keyboard . press ( 'Escape' )
2677+ await expect ( this . inlineItemEditor ) . not . toBeVisible ( )
2678+
2679+ // Verify no change occurred
2680+ const finalValue = await this . getJsonPropertyValue ( propertyKey )
2681+ expect ( finalValue ) . toBe ( originalValue )
2682+ }
24452683}
0 commit comments