@@ -24,6 +24,10 @@ test.describe( 'Cover', () => {
2424 await admin . createNewPost ( ) ;
2525 } ) ;
2626
27+ test . afterAll ( async ( { requestUtils } ) => {
28+ await requestUtils . deleteAllMedia ( ) ;
29+ } ) ;
30+
2731 test ( 'can set overlay color using color picker on block placeholder' , async ( {
2832 editor,
2933 } ) => {
@@ -48,51 +52,129 @@ test.describe( 'Cover', () => {
4852 ) ;
4953 } ) ;
5054
51- test ( 'can set background image using image upload on block placeholder ' , async ( {
55+ test ( 'computes overlay color correctly for uploaded image ' , async ( {
5256 editor,
57+ page,
5358 coverBlockUtils,
5459 } ) => {
55- await editor . insertBlock ( { name : 'core/cover' } ) ;
56- const coverBlock = editor . canvas . getByRole ( 'document' , {
57- name : 'Block: Cover' ,
58- } ) ;
60+ let coverBlock ;
5961
60- await coverBlockUtils . upload (
61- coverBlock . getByTestId ( 'form-file-upload-input' )
62- ) ;
62+ await test . step ( 'can set background image using image upload on block placeholder' , async ( ) => {
63+ await editor . insertBlock ( { name : 'core/cover' } ) ;
64+ coverBlock = editor . canvas . getByRole ( 'document' , {
65+ name : 'Block: Cover' ,
66+ } ) ;
6367
64- // Wait for the img's src attribute to be prefixed with http.
65- // Otherwise, the URL for the img src attribute starts is a placeholder
66- // beginning with `blob`. Increased timeout for client-side media processing.
67- // With client-side processing, the filename may be changed by the server.
68- await expect ( coverBlock . locator ( 'img' ) ) . toHaveAttribute (
69- 'src' ,
70- / ^ h t t p s ? : \/ \/ / ,
71- { timeout : 30_000 }
72- ) ;
73- } ) ;
68+ const fileName = await coverBlockUtils . upload (
69+ coverBlock . getByTestId ( 'form-file-upload-input' )
70+ ) ;
71+ const fileBasename = path . basename ( fileName ) ;
72+
73+ // Wait for the img's src attribute to be prefixed with http.
74+ // Otherwise, the URL for the img src attribute starts is a placeholder
75+ // beginning with `blob`.
76+ await expect ( async ( ) => {
77+ const src = await coverBlock
78+ . locator ( 'img' )
79+ . getAttribute ( 'src' ) ;
80+ expect ( src . includes ( fileBasename ) ) . toBe ( true ) ;
81+ } ) . toPass ( ) ;
82+ } ) ;
7483
75- test ( 'dims background image down by 50% with the average image color when an image is uploaded' , async ( {
76- editor,
77- coverBlockUtils,
78- } ) => {
79- await editor . insertBlock ( { name : 'core/cover' } ) ;
80- const coverBlock = editor . canvas . getByRole ( 'document' , {
81- name : 'Block: Cover' ,
84+ await test . step ( 'dims background image down by 50% with the average image color when an image is uploaded' , async ( ) => {
85+ // The overlay is a separate aria-hidden span before the image.
86+ const overlay = coverBlock . locator ( '.wp-block-cover__background' ) ;
87+
88+ await expect ( overlay ) . toHaveCSS (
89+ 'background-color' ,
90+ 'rgb(179, 179, 179)'
91+ ) ;
92+ await expect ( overlay ) . toHaveCSS ( 'opacity' , '0.5' ) ;
8293 } ) ;
8394
84- await coverBlockUtils . upload (
85- coverBlock . getByTestId ( 'form-file-upload-input' )
86- ) ;
95+ await test . step ( 'auto-updates overlay color when replacing image after save and reload' , async ( ) => {
96+ // Save and reload.
97+ await editor . saveDraft ( ) ;
98+ await page . reload ( ) ;
8799
88- // The overlay is a separate aria-hidden span before the image.
89- const overlay = coverBlock . locator ( '.wp-block-cover__background' ) ;
100+ // Replace the image with a green one.
101+ coverBlock = editor . canvas . getByRole ( 'document' , {
102+ name : 'Block: Cover' ,
103+ } ) ;
104+ await expect ( coverBlock ) . toBeVisible ( ) ;
105+ await editor . selectBlocks ( coverBlock ) ;
106+ await editor . showBlockToolbar ( ) ;
107+
108+ await page
109+ . getByRole ( 'toolbar' , { name : 'Block tools' } )
110+ . getByRole ( 'button' , { name : 'Replace' } )
111+ . click ( ) ;
112+
113+ const replaceInput = page . getByTestId ( 'form-file-upload-input' ) ;
114+ await coverBlockUtils . upload (
115+ replaceInput ,
116+ coverBlockUtils . GREEN_IMAGE_FILE_PATH
117+ ) ;
118+ await expect ( coverBlock . locator ( 'img' ) ) . toBeVisible ( ) ;
119+
120+ // The overlay should have auto-updated to the green image's average
121+ // color — no longer the gray from the first image.
122+ // This is the regression from PR #65105 / issue #64702.
123+ const overlay = coverBlock . locator ( '.wp-block-cover__background' ) ;
124+ await expect ( overlay ) . toHaveCSS (
125+ 'background-color' ,
126+ 'rgb(179, 255, 179)'
127+ ) ;
128+ } ) ;
90129
91- await expect ( overlay ) . toHaveCSS (
92- 'background-color' ,
93- 'rgb(179, 179, 179)'
94- ) ;
95- await expect ( overlay ) . toHaveCSS ( 'opacity' , '0.5' ) ;
130+ await test . step ( 'should not auto-update a manually set overlay color when replacing image after save and reload' , async ( ) => {
131+ // Manually change the overlay color to blue.
132+ await editor . selectBlocks ( coverBlock ) ;
133+ await editor . openDocumentSettingsSidebar ( ) ;
134+ const editorSettings = page . getByRole ( 'region' , {
135+ name : 'Editor settings' ,
136+ } ) ;
137+ await editorSettings . getByRole ( 'tab' , { name : 'Styles' } ) . click ( ) ;
138+ await editorSettings
139+ . getByRole ( 'button' , { name : 'Overlay' } )
140+ . click ( ) ;
141+ await page
142+ . getByRole ( 'button' , { name : 'Custom color picker' } )
143+ . click ( ) ;
144+ await page
145+ . getByRole ( 'textbox' , { name : 'Hex color' } )
146+ . fill ( '0000ff' ) ;
147+
148+ const overlay = coverBlock . locator ( '.wp-block-cover__background' ) ;
149+ await expect ( overlay ) . toHaveCSS (
150+ 'background-color' ,
151+ 'rgb(0, 0, 255)'
152+ ) ;
153+
154+ // Replace the image again with the original black-and-white one.
155+ // Because the user explicitly set the overlay color, it should NOT
156+ // be auto-detected from the new image.
157+ await editor . selectBlocks ( coverBlock ) ;
158+ await editor . showBlockToolbar ( ) ;
159+
160+ await page
161+ . getByRole ( 'toolbar' , { name : 'Block tools' } )
162+ . getByRole ( 'button' , { name : 'Replace' } )
163+ . click ( ) ;
164+
165+ const secondReplaceInput = page . getByTestId (
166+ 'form-file-upload-input'
167+ ) ;
168+ await coverBlockUtils . upload ( secondReplaceInput ) ;
169+ await expect ( coverBlock . locator ( 'img' ) ) . toBeVisible ( ) ;
170+
171+ // The overlay should still be blue — the user's manual choice is
172+ // preserved even after replacing the image.
173+ await expect ( overlay ) . toHaveCSS (
174+ 'background-color' ,
175+ 'rgb(0, 0, 255)'
176+ ) ;
177+ } ) ;
96178 } ) ;
97179
98180 test ( 'can have the title edited' , async ( { editor } ) => {
@@ -344,6 +426,135 @@ test.describe( 'Cover', () => {
344426
345427 await expect ( coverImage ) . toHaveCSS ( 'object-position' , '20% 30%' ) ;
346428 } ) ;
429+
430+ test ( 'correctly computes isDark based on dimRatio and overlay color' , async ( {
431+ page,
432+ editor,
433+ } ) => {
434+ // A cover with a black overlay at 100% should be dark.
435+ await editor . insertBlock ( { name : 'core/cover' } ) ;
436+ const coverBlock = editor . canvas . getByRole ( 'document' , {
437+ name : 'Block: Cover' ,
438+ } ) ;
439+ await coverBlock
440+ . getByRole ( 'button' , {
441+ name : 'Black' ,
442+ } )
443+ . click ( ) ;
444+
445+ // Black overlay at default 100% opacity → dark theme.
446+ await expect ( coverBlock ) . toHaveClass ( / i s - d a r k - t h e m e / ) ;
447+
448+ // Select the Cover block (not the inner Paragraph) before opening sidebar.
449+ await editor . selectBlocks ( coverBlock ) ;
450+
451+ // Open sidebar and set overlay opacity to 0.
452+ await editor . openDocumentSettingsSidebar ( ) ;
453+ const editorSettings = page . getByRole ( 'region' , {
454+ name : 'Editor settings' ,
455+ } ) ;
456+ await editorSettings . getByRole ( 'tab' , { name : 'Styles' } ) . click ( ) ;
457+
458+ const opacitySlider = editorSettings . getByRole ( 'slider' , {
459+ name : 'Overlay opacity' ,
460+ } ) ;
461+
462+ // With dimRatio at 0, the overlay is fully transparent.
463+ // The background defaults to white → should be light, not dark.
464+ // This is the regression from PR #53253.
465+ await opacitySlider . fill ( '0' ) ;
466+ await expect ( coverBlock ) . toHaveClass ( / i s - l i g h t / ) ;
467+
468+ // Set it back to 100 → should be dark again.
469+ await opacitySlider . fill ( '100' ) ;
470+ await expect ( coverBlock ) . toHaveClass ( / i s - d a r k - t h e m e / ) ;
471+ } ) ;
472+
473+ test ( 'preserves explicit dimRatio of 100 when replacing an image' , async ( {
474+ page,
475+ editor,
476+ coverBlockUtils,
477+ } ) => {
478+ await editor . insertBlock ( { name : 'core/cover' } ) ;
479+ const coverBlock = editor . canvas . getByRole ( 'document' , {
480+ name : 'Block: Cover' ,
481+ } ) ;
482+
483+ // Upload an initial image.
484+ await coverBlockUtils . upload (
485+ coverBlock . getByTestId ( 'form-file-upload-input' )
486+ ) ;
487+
488+ // Wait for the image to load.
489+ await expect ( coverBlock . locator ( 'img' ) ) . toBeVisible ( ) ;
490+
491+ // Select the Cover block (not the inner Paragraph) before opening sidebar.
492+ await editor . selectBlocks ( coverBlock ) ;
493+
494+ // Open the sidebar and set overlay opacity to 100.
495+ await editor . openDocumentSettingsSidebar ( ) ;
496+ const editorSettings = page . getByRole ( 'region' , {
497+ name : 'Editor settings' ,
498+ } ) ;
499+ await editorSettings . getByRole ( 'tab' , { name : 'Styles' } ) . click ( ) ;
500+
501+ const opacitySlider = editorSettings . getByRole ( 'slider' , {
502+ name : 'Overlay opacity' ,
503+ } ) ;
504+ await opacitySlider . fill ( '100' ) ;
505+
506+ // Verify the overlay opacity is now 100%.
507+ const overlay = coverBlock . locator ( '.wp-block-cover__background' ) ;
508+ await expect ( overlay ) . toHaveCSS (
509+ 'background-color' ,
510+ 'rgb(179, 179, 179)'
511+ ) ;
512+ await expect ( overlay ) . toHaveCSS ( 'opacity' , '1' ) ;
513+
514+ // Replace the image via the toolbar.
515+ await editor . selectBlocks ( coverBlock ) ;
516+ await editor . showBlockToolbar ( ) ;
517+
518+ await page
519+ . getByRole ( 'toolbar' , { name : 'Block tools' } )
520+ . getByRole ( 'button' , { name : 'Replace' } )
521+ . click ( ) ;
522+
523+ // Upload a new image via the replace dropdown.
524+ const replaceInput = page . getByTestId ( 'form-file-upload-input' ) ;
525+ await coverBlockUtils . upload ( replaceInput ) ;
526+
527+ // Wait for the new image to load.
528+ await expect ( coverBlock . locator ( 'img' ) ) . toBeVisible ( ) ;
529+
530+ // The dimRatio should STILL be 1, not reset to 0.5.
531+ // This is the regression from PR #55422 / issue #52835.
532+ await expect ( overlay ) . toHaveCSS (
533+ 'background-color' ,
534+ 'rgb(179, 179, 179)'
535+ ) ;
536+ await expect ( overlay ) . toHaveCSS ( 'opacity' , '1' ) ;
537+ } ) ;
538+
539+ test ( 'shows the overlay when using an empty featured image' , async ( {
540+ editor,
541+ } ) => {
542+ await editor . insertBlock ( { name : 'core/cover' } ) ;
543+ const coverBlock = editor . canvas . getByRole ( 'document' , {
544+ name : 'Block: Cover' ,
545+ } ) ;
546+
547+ // Enable "Use featured image" without setting a featured image.
548+ // This is the scenario from issue #57887 / PR #59855: the overlay
549+ // was not rendered when useFeaturedImage was true but the featured
550+ // image was empty.
551+ await coverBlock
552+ . getByRole ( 'button' , { name : 'Use featured image' } )
553+ . click ( ) ;
554+
555+ const overlay = coverBlock . locator ( '.wp-block-cover__background' ) ;
556+ await expect ( overlay ) . toBeVisible ( ) ;
557+ } ) ;
347558} ) ;
348559
349560class CoverBlockUtils {
@@ -359,15 +570,25 @@ class CoverBlockUtils {
359570 'assets' ,
360571 '10x10_e2e_test_image_z9T8jK.png'
361572 ) ;
573+
574+ this . GREEN_IMAGE_FILE_PATH = path . join (
575+ __dirname ,
576+ '..' ,
577+ '..' ,
578+ '..' ,
579+ 'assets' ,
580+ '10x10_e2e_test_image_green.png'
581+ ) ;
362582 }
363583
364- async upload ( locator ) {
584+ async upload ( locator , imagePath ) {
585+ const srcPath = imagePath || this . TEST_IMAGE_FILE_PATH ;
365586 const tmpDirectory = await fs . mkdtemp (
366587 path . join ( os . tmpdir ( ) , 'gutenberg-test-image-' )
367588 ) ;
368589 const fileName = uuid ( ) ;
369590 const tmpFileName = path . join ( tmpDirectory , fileName + '.png' ) ;
370- await fs . copyFile ( this . TEST_IMAGE_FILE_PATH , tmpFileName ) ;
591+ await fs . copyFile ( srcPath , tmpFileName ) ;
371592
372593 await locator . setInputFiles ( tmpFileName ) ;
373594
0 commit comments