@@ -2442,4 +2442,242 @@ export class BrowserPage extends BasePage {
2442
2442
this . page . locator ( `[data-testid="hash-field-${ fieldName } "]` ) ,
2443
2443
) . not . toBeVisible ( )
2444
2444
}
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
+ }
2445
2683
}
0 commit comments