@@ -45,4 +45,104 @@ test.describe( 'Button Group', () => {
4545 await expect ( btn ) . toHaveCSS ( 'font-style' , attributes . fontStyle ) ;
4646 await expect ( btn ) . toHaveCSS ( 'line-height' , `${ parseInt ( attributes . fontSize ) * parseFloat ( attributes . lineHeight ) } px` ) ; // Playwright use computed line-height based on font-size.
4747 } ) ;
48+
49+ test ( 'global defaults typography saves the right attributes and new blocks inherit them' , async ( { admin, editor, page, requestUtils } ) => {
50+ const getSavedDefaults = async ( ) => {
51+ const settings = await requestUtils . rest ( { path : '/wp/v2/settings' } ) ;
52+ return JSON . parse ( settings . themeisle_blocks_settings_global_defaults || '{}' ) [ 'themeisle-blocks/button-group' ] ?? { } ;
53+ } ;
54+
55+ // Build the defaults through the Otter Options global-defaults UI.
56+ await page . getByRole ( 'button' , { name : 'Otter Options' } ) . click ( ) ;
57+ await page . getByRole ( 'button' , { name : 'Block Settings' } ) . click ( ) ;
58+
59+ const row = page . locator ( '.o-options-block-item' ) . filter ( {
60+ has : page . getByText ( 'Button Group' , { exact : true } )
61+ } ) ;
62+ await row . getByRole ( 'button' , { name : 'Open Settings' } ) . click ( ) ;
63+
64+ // Appearance and Letter Case are hidden until enabled from the view-options menu.
65+ await page . getByRole ( 'button' , { name : 'View options' } ) . click ( ) ;
66+ await page . getByRole ( 'menuitemcheckbox' , { name : 'Appearance' } ) . click ( ) ;
67+ await page . getByRole ( 'menuitemcheckbox' , { name : 'Letter Case' } ) . click ( ) ;
68+ await page . keyboard . press ( 'Escape' ) ;
69+
70+ await page . getByLabel ( 'Appearance' ) . selectOption ( 'italic' ) ;
71+ await page . getByRole ( 'button' , { name : 'Uppercase' } ) . click ( ) ;
72+
73+ await page . getByRole ( 'button' , { name : 'Save' , exact : true } ) . click ( ) ;
74+
75+ // Regression: Appearance/Letter Case used to be written to the wrong keys
76+ // (fontVariant/fontStyle) instead of fontStyle/textTransform.
77+ await expect . poll ( getSavedDefaults ) . toMatchObject ( {
78+ fontStyle : 'italic' ,
79+ textTransform : 'uppercase'
80+ } ) ;
81+ expect ( ( await getSavedDefaults ( ) ) . fontVariant ) . toBeUndefined ( ) ;
82+
83+ // A Button Group inserted on a fresh editor load inherits the defaults.
84+ await admin . createNewPost ( ) ;
85+ await editor . insertBlock ( {
86+ name : 'themeisle-blocks/button-group' ,
87+ innerBlocks : [
88+ {
89+ name : 'themeisle-blocks/button' ,
90+ attributes : { text : 'Inherit me' }
91+ }
92+ ]
93+ } ) ;
94+
95+ await expect . poll ( async ( ) => {
96+ const blocks = await editor . getBlocks ( ) ;
97+ const group = blocks . find ( block => 'themeisle-blocks/button-group' === block . name ) ;
98+ return {
99+ fontStyle : group ?. attributes ?. fontStyle ,
100+ textTransform : group ?. attributes ?. textTransform
101+ } ;
102+ } ) . toEqual ( { fontStyle : 'italic' , textTransform : 'uppercase' } ) ;
103+
104+ await publishAndViewPost ( { editor, page } ) ;
105+
106+ const btn = page . locator ( 'a' ) . filter ( { hasText : 'Inherit me' } ) ;
107+ await expect ( btn ) . toHaveCSS ( 'font-style' , 'italic' ) ;
108+ await expect ( btn ) . toHaveCSS ( 'text-transform' , 'uppercase' ) ;
109+ } ) ;
110+
111+ test ( 'editor survives an empty global defaults option' , async ( { admin, editor, page, requestUtils } ) => {
112+ // Regression: an empty-string option json_decode()s to null server-side and
113+ // used to crash every newly inserted Otter block's Edit component.
114+ await requestUtils . rest ( {
115+ method : 'POST' ,
116+ path : '/wp/v2/settings' ,
117+ data : { themeisle_blocks_settings_global_defaults : '' }
118+ } ) ;
119+
120+ // A fresh editor load so the poisoned option gets localized.
121+ await admin . createNewPost ( ) ;
122+
123+ await editor . insertBlock ( {
124+ name : 'themeisle-blocks/button-group' ,
125+ innerBlocks : [
126+ {
127+ name : 'themeisle-blocks/button' ,
128+ attributes : { text : 'Still alive' }
129+ }
130+ ]
131+ } ) ;
132+
133+ // The block renders instead of falling into the block crash boundary.
134+ await expect ( editor . canvas . getByText ( 'Still alive' ) ) . toBeVisible ( ) ;
135+ await expect ( editor . canvas . locator ( '.block-editor-warning' ) ) . toHaveCount ( 0 ) ;
136+ } ) ;
137+
138+ // Global defaults persist site-wide; reset them so other specs are unaffected.
139+ // Must be '{}', not '': on unfixed builds the editor bootstrap json_decode()s
140+ // the raw option and a null result crashes every Otter block's Edit component.
141+ test . afterEach ( async ( { requestUtils } ) => {
142+ await requestUtils . rest ( {
143+ method : 'POST' ,
144+ path : '/wp/v2/settings' ,
145+ data : { themeisle_blocks_settings_global_defaults : '{}' }
146+ } ) ;
147+ } ) ;
48148} ) ;
0 commit comments