Skip to content

Commit aa747d3

Browse files
ockhamMamaduka
andauthored
Cover block: Add e2e test coverage for bugfixes (#75483)
The Cover block performs a number of color-related computations, e.g. for the color of the overlay (based on the average color value of the background image). These have proven finicky to get "right" from a UX POV. As a result, the Cover block has received a number of smallish bugfixes over time. However, many of those bugfixes lack test coverage. This commit seeks to remediate that. This is o increase confidence in changes to the Cover block's internal logic. It is especially relevant with regard to future changes where we might want to restructure color computation logic, and have it respond to other events outside of the current list. Co-authored-by: ockham <bernhard-reiter@git.wordpress.org> Co-authored-by: Mamaduka <mamaduka@git.wordpress.org>
1 parent 17f3eb3 commit aa747d3

File tree

2 files changed

+258
-37
lines changed

2 files changed

+258
-37
lines changed
78 Bytes
Loading

test/e2e/specs/editor/blocks/cover.spec.js

Lines changed: 258 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -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-
/^https?:\/\//,
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( /is-dark-theme/ );
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( /is-light/ );
467+
468+
// Set it back to 100 → should be dark again.
469+
await opacitySlider.fill( '100' );
470+
await expect( coverBlock ).toHaveClass( /is-dark-theme/ );
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

349560
class 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

Comments
 (0)