diff --git a/test/e2e/assets/10x10_e2e_test_image_green.png b/test/e2e/assets/10x10_e2e_test_image_green.png new file mode 100644 index 00000000000000..78e734faa0e972 Binary files /dev/null and b/test/e2e/assets/10x10_e2e_test_image_green.png differ diff --git a/test/e2e/specs/editor/blocks/cover.spec.js b/test/e2e/specs/editor/blocks/cover.spec.js index e7ba0047b88e23..cd480144ffde0e 100644 --- a/test/e2e/specs/editor/blocks/cover.spec.js +++ b/test/e2e/specs/editor/blocks/cover.spec.js @@ -24,6 +24,10 @@ test.describe( 'Cover', () => { await admin.createNewPost(); } ); + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.deleteAllMedia(); + } ); + test( 'can set overlay color using color picker on block placeholder', async ( { editor, } ) => { @@ -48,51 +52,129 @@ test.describe( 'Cover', () => { ); } ); - test( 'can set background image using image upload on block placeholder', async ( { + test( 'computes overlay color correctly for uploaded image', async ( { editor, + page, coverBlockUtils, } ) => { - await editor.insertBlock( { name: 'core/cover' } ); - const coverBlock = editor.canvas.getByRole( 'document', { - name: 'Block: Cover', - } ); + let coverBlock; - await coverBlockUtils.upload( - coverBlock.getByTestId( 'form-file-upload-input' ) - ); + await test.step( 'can set background image using image upload on block placeholder', async () => { + await editor.insertBlock( { name: 'core/cover' } ); + coverBlock = editor.canvas.getByRole( 'document', { + name: 'Block: Cover', + } ); - // Wait for the img's src attribute to be prefixed with http. - // Otherwise, the URL for the img src attribute starts is a placeholder - // beginning with `blob`. Increased timeout for client-side media processing. - // With client-side processing, the filename may be changed by the server. - await expect( coverBlock.locator( 'img' ) ).toHaveAttribute( - 'src', - /^https?:\/\//, - { timeout: 30_000 } - ); - } ); + const fileName = await coverBlockUtils.upload( + coverBlock.getByTestId( 'form-file-upload-input' ) + ); + const fileBasename = path.basename( fileName ); + + // Wait for the img's src attribute to be prefixed with http. + // Otherwise, the URL for the img src attribute starts is a placeholder + // beginning with `blob`. + await expect( async () => { + const src = await coverBlock + .locator( 'img' ) + .getAttribute( 'src' ); + expect( src.includes( fileBasename ) ).toBe( true ); + } ).toPass(); + } ); - test( 'dims background image down by 50% with the average image color when an image is uploaded', async ( { - editor, - coverBlockUtils, - } ) => { - await editor.insertBlock( { name: 'core/cover' } ); - const coverBlock = editor.canvas.getByRole( 'document', { - name: 'Block: Cover', + await test.step( 'dims background image down by 50% with the average image color when an image is uploaded', async () => { + // The overlay is a separate aria-hidden span before the image. + const overlay = coverBlock.locator( '.wp-block-cover__background' ); + + await expect( overlay ).toHaveCSS( + 'background-color', + 'rgb(179, 179, 179)' + ); + await expect( overlay ).toHaveCSS( 'opacity', '0.5' ); } ); - await coverBlockUtils.upload( - coverBlock.getByTestId( 'form-file-upload-input' ) - ); + await test.step( 'auto-updates overlay color when replacing image after save and reload', async () => { + // Save and reload. + await editor.saveDraft(); + await page.reload(); - // The overlay is a separate aria-hidden span before the image. - const overlay = coverBlock.locator( '.wp-block-cover__background' ); + // Replace the image with a green one. + coverBlock = editor.canvas.getByRole( 'document', { + name: 'Block: Cover', + } ); + await expect( coverBlock ).toBeVisible(); + await editor.selectBlocks( coverBlock ); + await editor.showBlockToolbar(); + + await page + .getByRole( 'toolbar', { name: 'Block tools' } ) + .getByRole( 'button', { name: 'Replace' } ) + .click(); + + const replaceInput = page.getByTestId( 'form-file-upload-input' ); + await coverBlockUtils.upload( + replaceInput, + coverBlockUtils.GREEN_IMAGE_FILE_PATH + ); + await expect( coverBlock.locator( 'img' ) ).toBeVisible(); + + // The overlay should have auto-updated to the green image's average + // color — no longer the gray from the first image. + // This is the regression from PR #65105 / issue #64702. + const overlay = coverBlock.locator( '.wp-block-cover__background' ); + await expect( overlay ).toHaveCSS( + 'background-color', + 'rgb(179, 255, 179)' + ); + } ); - await expect( overlay ).toHaveCSS( - 'background-color', - 'rgb(179, 179, 179)' - ); - await expect( overlay ).toHaveCSS( 'opacity', '0.5' ); + await test.step( 'should not auto-update a manually set overlay color when replacing image after save and reload', async () => { + // Manually change the overlay color to blue. + await editor.selectBlocks( coverBlock ); + await editor.openDocumentSettingsSidebar(); + const editorSettings = page.getByRole( 'region', { + name: 'Editor settings', + } ); + await editorSettings.getByRole( 'tab', { name: 'Styles' } ).click(); + await editorSettings + .getByRole( 'button', { name: 'Overlay' } ) + .click(); + await page + .getByRole( 'button', { name: 'Custom color picker' } ) + .click(); + await page + .getByRole( 'textbox', { name: 'Hex color' } ) + .fill( '0000ff' ); + + const overlay = coverBlock.locator( '.wp-block-cover__background' ); + await expect( overlay ).toHaveCSS( + 'background-color', + 'rgb(0, 0, 255)' + ); + + // Replace the image again with the original black-and-white one. + // Because the user explicitly set the overlay color, it should NOT + // be auto-detected from the new image. + await editor.selectBlocks( coverBlock ); + await editor.showBlockToolbar(); + + await page + .getByRole( 'toolbar', { name: 'Block tools' } ) + .getByRole( 'button', { name: 'Replace' } ) + .click(); + + const secondReplaceInput = page.getByTestId( + 'form-file-upload-input' + ); + await coverBlockUtils.upload( secondReplaceInput ); + await expect( coverBlock.locator( 'img' ) ).toBeVisible(); + + // The overlay should still be blue — the user's manual choice is + // preserved even after replacing the image. + await expect( overlay ).toHaveCSS( + 'background-color', + 'rgb(0, 0, 255)' + ); + } ); } ); test( 'can have the title edited', async ( { editor } ) => { @@ -344,6 +426,135 @@ test.describe( 'Cover', () => { await expect( coverImage ).toHaveCSS( 'object-position', '20% 30%' ); } ); + + test( 'correctly computes isDark based on dimRatio and overlay color', async ( { + page, + editor, + } ) => { + // A cover with a black overlay at 100% should be dark. + await editor.insertBlock( { name: 'core/cover' } ); + const coverBlock = editor.canvas.getByRole( 'document', { + name: 'Block: Cover', + } ); + await coverBlock + .getByRole( 'button', { + name: 'Black', + } ) + .click(); + + // Black overlay at default 100% opacity → dark theme. + await expect( coverBlock ).toHaveClass( /is-dark-theme/ ); + + // Select the Cover block (not the inner Paragraph) before opening sidebar. + await editor.selectBlocks( coverBlock ); + + // Open sidebar and set overlay opacity to 0. + await editor.openDocumentSettingsSidebar(); + const editorSettings = page.getByRole( 'region', { + name: 'Editor settings', + } ); + await editorSettings.getByRole( 'tab', { name: 'Styles' } ).click(); + + const opacitySlider = editorSettings.getByRole( 'slider', { + name: 'Overlay opacity', + } ); + + // With dimRatio at 0, the overlay is fully transparent. + // The background defaults to white → should be light, not dark. + // This is the regression from PR #53253. + await opacitySlider.fill( '0' ); + await expect( coverBlock ).toHaveClass( /is-light/ ); + + // Set it back to 100 → should be dark again. + await opacitySlider.fill( '100' ); + await expect( coverBlock ).toHaveClass( /is-dark-theme/ ); + } ); + + test( 'preserves explicit dimRatio of 100 when replacing an image', async ( { + page, + editor, + coverBlockUtils, + } ) => { + await editor.insertBlock( { name: 'core/cover' } ); + const coverBlock = editor.canvas.getByRole( 'document', { + name: 'Block: Cover', + } ); + + // Upload an initial image. + await coverBlockUtils.upload( + coverBlock.getByTestId( 'form-file-upload-input' ) + ); + + // Wait for the image to load. + await expect( coverBlock.locator( 'img' ) ).toBeVisible(); + + // Select the Cover block (not the inner Paragraph) before opening sidebar. + await editor.selectBlocks( coverBlock ); + + // Open the sidebar and set overlay opacity to 100. + await editor.openDocumentSettingsSidebar(); + const editorSettings = page.getByRole( 'region', { + name: 'Editor settings', + } ); + await editorSettings.getByRole( 'tab', { name: 'Styles' } ).click(); + + const opacitySlider = editorSettings.getByRole( 'slider', { + name: 'Overlay opacity', + } ); + await opacitySlider.fill( '100' ); + + // Verify the overlay opacity is now 100%. + const overlay = coverBlock.locator( '.wp-block-cover__background' ); + await expect( overlay ).toHaveCSS( + 'background-color', + 'rgb(179, 179, 179)' + ); + await expect( overlay ).toHaveCSS( 'opacity', '1' ); + + // Replace the image via the toolbar. + await editor.selectBlocks( coverBlock ); + await editor.showBlockToolbar(); + + await page + .getByRole( 'toolbar', { name: 'Block tools' } ) + .getByRole( 'button', { name: 'Replace' } ) + .click(); + + // Upload a new image via the replace dropdown. + const replaceInput = page.getByTestId( 'form-file-upload-input' ); + await coverBlockUtils.upload( replaceInput ); + + // Wait for the new image to load. + await expect( coverBlock.locator( 'img' ) ).toBeVisible(); + + // The dimRatio should STILL be 1, not reset to 0.5. + // This is the regression from PR #55422 / issue #52835. + await expect( overlay ).toHaveCSS( + 'background-color', + 'rgb(179, 179, 179)' + ); + await expect( overlay ).toHaveCSS( 'opacity', '1' ); + } ); + + test( 'shows the overlay when using an empty featured image', async ( { + editor, + } ) => { + await editor.insertBlock( { name: 'core/cover' } ); + const coverBlock = editor.canvas.getByRole( 'document', { + name: 'Block: Cover', + } ); + + // Enable "Use featured image" without setting a featured image. + // This is the scenario from issue #57887 / PR #59855: the overlay + // was not rendered when useFeaturedImage was true but the featured + // image was empty. + await coverBlock + .getByRole( 'button', { name: 'Use featured image' } ) + .click(); + + const overlay = coverBlock.locator( '.wp-block-cover__background' ); + await expect( overlay ).toBeVisible(); + } ); } ); class CoverBlockUtils { @@ -359,15 +570,25 @@ class CoverBlockUtils { 'assets', '10x10_e2e_test_image_z9T8jK.png' ); + + this.GREEN_IMAGE_FILE_PATH = path.join( + __dirname, + '..', + '..', + '..', + 'assets', + '10x10_e2e_test_image_green.png' + ); } - async upload( locator ) { + async upload( locator, imagePath ) { + const srcPath = imagePath || this.TEST_IMAGE_FILE_PATH; const tmpDirectory = await fs.mkdtemp( path.join( os.tmpdir(), 'gutenberg-test-image-' ) ); const fileName = uuid(); const tmpFileName = path.join( tmpDirectory, fileName + '.png' ); - await fs.copyFile( this.TEST_IMAGE_FILE_PATH, tmpFileName ); + await fs.copyFile( srcPath, tmpFileName ); await locator.setInputFiles( tmpFileName );