Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added test/e2e/assets/10x10_e2e_test_image_green.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
295 changes: 258 additions & 37 deletions test/e2e/specs/editor/blocks/cover.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
} ) => {
Expand All @@ -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 } ) => {
Expand Down Expand Up @@ -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 {
Expand All @@ -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 );

Expand Down
Loading