@@ -1600,4 +1600,242 @@ export class BrowserPage extends BasePage {
1600
1600
await this . verifyKeyTTL ( expectedTTL )
1601
1601
await this . closeKeyDetailsAndVerify ( )
1602
1602
}
1603
+
1604
+ async editJsonProperty (
1605
+ propertyKey : string ,
1606
+ newValue : string | number | boolean ,
1607
+ ) : Promise < void > {
1608
+ // TODO: Ideally this should find by property key, but the current DOM structure
1609
+ // makes it complex to navigate from key to value reliably. For now, we use the
1610
+ // working approach of finding by current value.
1611
+ const currentValue = await this . getJsonPropertyValue ( propertyKey )
1612
+ if ( ! currentValue ) {
1613
+ throw new Error ( `Property "${ propertyKey } " not found` )
1614
+ }
1615
+
1616
+ // Find and click the value element
1617
+ const valueElement = this . page
1618
+ . getByTestId ( 'json-scalar-value' )
1619
+ . filter ( { hasText : currentValue } )
1620
+ . first ( )
1621
+
1622
+ await valueElement . click ( )
1623
+ await expect ( this . inlineItemEditor ) . toBeVisible ( )
1624
+
1625
+ // Format and apply the new value
1626
+ const formattedValue =
1627
+ typeof newValue === 'string' ? `"${ newValue } "` : newValue . toString ( )
1628
+
1629
+ await this . inlineItemEditor . clear ( )
1630
+ await this . inlineItemEditor . fill ( formattedValue )
1631
+ await this . applyButton . click ( )
1632
+ await expect ( this . inlineItemEditor ) . not . toBeVisible ( )
1633
+
1634
+ if ( await this . toast . isCloseButtonVisible ( ) ) {
1635
+ await this . toast . closeToast ( )
1636
+ }
1637
+ }
1638
+
1639
+ // Convenience methods that use the generic editJsonProperty method
1640
+ async editJsonString ( propertyKey : string , newValue : string ) : Promise < void > {
1641
+ await this . editJsonProperty ( propertyKey , newValue )
1642
+ }
1643
+
1644
+ async editJsonNumber ( propertyKey : string , newValue : number ) : Promise < void > {
1645
+ await this . editJsonProperty ( propertyKey , newValue )
1646
+ }
1647
+
1648
+ async editJsonBoolean (
1649
+ propertyKey : string ,
1650
+ newValue : boolean ,
1651
+ ) : Promise < void > {
1652
+ await this . editJsonProperty ( propertyKey , newValue )
1653
+ }
1654
+
1655
+ async addJsonProperty (
1656
+ key : string ,
1657
+ value : string | number | boolean ,
1658
+ ) : Promise < void > {
1659
+ // For JSON objects, add a new property at the same level
1660
+ await this . addJsonObjectButton . click ( )
1661
+
1662
+ // Wait for the form to appear
1663
+ await expect ( this . jsonKeyInput ) . toBeVisible ( )
1664
+ await expect ( this . jsonValueInput ) . toBeVisible ( )
1665
+
1666
+ // Format the key and value properly for JSON
1667
+ const formattedKey = `"${ key } "`
1668
+ let formattedValue : string
1669
+ if ( typeof value === 'string' ) {
1670
+ formattedValue = `"${ value } "`
1671
+ } else {
1672
+ formattedValue = value . toString ( )
1673
+ }
1674
+
1675
+ // Fill the key and value
1676
+ await this . jsonKeyInput . clear ( )
1677
+ await this . jsonKeyInput . fill ( formattedKey )
1678
+ await this . jsonValueInput . clear ( )
1679
+ await this . jsonValueInput . fill ( formattedValue )
1680
+
1681
+ // Apply the changes
1682
+ await this . applyButton . click ( )
1683
+
1684
+ // Wait for the form to disappear
1685
+ await expect ( this . jsonKeyInput ) . not . toBeVisible ( )
1686
+
1687
+ // Close any success toast if it appears
1688
+ if ( await this . toast . isCloseButtonVisible ( ) ) {
1689
+ await this . toast . closeToast ( )
1690
+ }
1691
+ }
1692
+
1693
+ async editEntireJsonStructure ( newJsonStructure : string ) : Promise < void > {
1694
+ // Switch to Monaco editor
1695
+ await this . page
1696
+ . getByRole ( 'button' , { name : 'Change editor type' } )
1697
+ . click ( )
1698
+
1699
+ // Wait for Monaco editor
1700
+ const monacoContainer = this . page . getByTestId ( 'monaco-editor-json-data' )
1701
+ await expect ( monacoContainer ) . toBeVisible ( )
1702
+
1703
+ // Clear and set new JSON content
1704
+ const textarea = monacoContainer . locator ( 'textarea' ) . first ( )
1705
+ await textarea . focus ( )
1706
+ await this . page . keyboard . press ( 'Control+A' )
1707
+ await this . page . keyboard . press ( 'Delete' )
1708
+ await textarea . type ( newJsonStructure )
1709
+
1710
+ // Wait for button to be enabled and click it
1711
+ const updateButton = this . page . getByTestId ( 'json-data-update-btn' )
1712
+ await expect ( updateButton ) . toBeEnabled ( )
1713
+ await updateButton . click ( )
1714
+
1715
+ // Close editor and return to tree view
1716
+ const cancelButton = this . page . getByTestId ( 'json-data-cancel-btn' )
1717
+ if ( await cancelButton . isVisible ( ) ) {
1718
+ await cancelButton . click ( )
1719
+ }
1720
+
1721
+ if ( await this . toast . isCloseButtonVisible ( ) ) {
1722
+ await this . toast . closeToast ( )
1723
+ }
1724
+ }
1725
+
1726
+ async verifyJsonPropertyExists ( key : string , value : string ) : Promise < void > {
1727
+ // Expand all objects and get the actual value
1728
+ const actualValue = await this . getJsonPropertyValue ( key )
1729
+ expect ( actualValue ) . toBe ( value )
1730
+ }
1731
+
1732
+ async verifyJsonPropertyNotExists ( key : string ) : Promise < void > {
1733
+ const actualValue = await this . getJsonPropertyValue ( key )
1734
+ expect ( actualValue ) . toBeNull ( )
1735
+ }
1736
+
1737
+ async waitForJsonDetailsToBeVisible ( ) : Promise < void > {
1738
+ await expect ( this . page . getByTestId ( 'json-details' ) ) . toBeVisible ( )
1739
+ }
1740
+
1741
+ async waitForJsonPropertyUpdate (
1742
+ key : string ,
1743
+ expectedValue : string ,
1744
+ ) : Promise < void > {
1745
+ await expect
1746
+ . poll ( async ( ) => {
1747
+ try {
1748
+ const actualValue = await this . getJsonPropertyValue ( key )
1749
+ return actualValue === expectedValue
1750
+ } catch ( error ) {
1751
+ return false
1752
+ }
1753
+ } )
1754
+ . toBe ( true )
1755
+ }
1756
+
1757
+ async expandAllJsonObjects ( ) : Promise < void > {
1758
+ // Keep expanding until no more expand buttons exist
1759
+ while ( true ) {
1760
+ const expandButtons = this . page . getByTestId ( 'expand-object' )
1761
+ const count = await expandButtons . count ( )
1762
+
1763
+ if ( count === 0 ) {
1764
+ break // No more expand buttons to click
1765
+ }
1766
+
1767
+ // Click ALL visible expand buttons in this iteration
1768
+ const buttons = await expandButtons . all ( )
1769
+ for ( const button of buttons ) {
1770
+ if ( await button . isVisible ( ) ) {
1771
+ await button . click ( )
1772
+ }
1773
+ }
1774
+
1775
+ // Wait for DOM to be ready before checking for new buttons
1776
+ await this . page . waitForLoadState ( 'domcontentloaded' )
1777
+ }
1778
+ }
1779
+
1780
+ async getJsonPropertyValue ( propertyName : string ) : Promise < string | null > {
1781
+ // Expand all objects to make sure we can see the property
1782
+ await this . expandAllJsonObjects ( )
1783
+
1784
+ // Get the JSON content and look for the property with a simple approach
1785
+ const jsonContent = await this . jsonKeyValue . textContent ( )
1786
+ if ( ! jsonContent ) return null
1787
+
1788
+ // Use a more precise regex pattern for different value types
1789
+ // Try patterns for strings, numbers, and booleans
1790
+ const patterns = [
1791
+ new RegExp ( `${ propertyName } :"([^"]*)"` , 'g' ) , // String values: name:"value"
1792
+ new RegExp ( `${ propertyName } :(\\d+(?:\\.\\d+)?)` , 'g' ) , // Number values: age:25
1793
+ new RegExp ( `${ propertyName } :(true|false)` , 'g' ) , // Boolean values: active:true
1794
+ ]
1795
+
1796
+ for ( const pattern of patterns ) {
1797
+ pattern . lastIndex = 0 // Reset regex state
1798
+ const match = pattern . exec ( jsonContent )
1799
+ if ( match && match [ 1 ] ) {
1800
+ return match [ 1 ]
1801
+ }
1802
+ }
1803
+
1804
+ return null
1805
+ }
1806
+
1807
+ async verifyJsonStructureValid ( ) : Promise < void > {
1808
+ // Check that no JSON error is displayed
1809
+ await expect ( this . jsonError ) . not . toBeVisible ( )
1810
+
1811
+ // Check that the JSON data container is visible
1812
+ await expect ( this . jsonKeyValue ) . toBeVisible ( )
1813
+ }
1814
+
1815
+ async cancelJsonScalarValueEdit ( propertyKey : string ) : Promise < void > {
1816
+ // Store original value, start editing, then cancel
1817
+ const originalValue = await this . getJsonPropertyValue ( propertyKey )
1818
+ if ( ! originalValue ) {
1819
+ throw new Error ( `Property "${ propertyKey } " not found` )
1820
+ }
1821
+
1822
+ await this . expandAllJsonObjects ( )
1823
+
1824
+ // Find the element containing this value
1825
+ const targetElement = this . page
1826
+ . getByTestId ( 'json-scalar-value' )
1827
+ . filter ( { hasText : originalValue } )
1828
+ . first ( )
1829
+
1830
+ // Start edit, make change, then cancel
1831
+ await targetElement . click ( )
1832
+ await expect ( this . inlineItemEditor ) . toBeVisible ( )
1833
+ await this . inlineItemEditor . fill ( '"canceled_value"' )
1834
+ await this . page . keyboard . press ( 'Escape' )
1835
+ await expect ( this . inlineItemEditor ) . not . toBeVisible ( )
1836
+
1837
+ // Verify no change occurred
1838
+ const finalValue = await this . getJsonPropertyValue ( propertyKey )
1839
+ expect ( finalValue ) . toBe ( originalValue )
1840
+ }
1603
1841
}
0 commit comments