Skip to content

Commit a1e910f

Browse files
Copilotswissspidy
andcommitted
Add e2e test for server-side HEIC conversion
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
1 parent 16fa9f0 commit a1e910f

File tree

1 file changed

+306
-0
lines changed

1 file changed

+306
-0
lines changed
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { readFileSync } from 'node:fs';
5+
import { join } from 'node:path';
6+
7+
/**
8+
* WordPress dependencies
9+
*/
10+
import type { RestAttachment } from '@mexp/media-utils';
11+
12+
/**
13+
* Internal dependencies
14+
*/
15+
import { expect, test } from '../../fixtures';
16+
17+
const heicFile = {
18+
name: 'hill-800x600.heic',
19+
mimeType: 'image/heic',
20+
buffer: readFileSync(
21+
join( __dirname, '..', '..', 'assets', 'hill-800x600.heic' )
22+
),
23+
};
24+
25+
test.describe( 'Images', () => {
26+
test.beforeAll( async ( { requestUtils } ) => {
27+
await Promise.all( [
28+
requestUtils.deleteAllMedia(),
29+
requestUtils.resetPreferences(),
30+
] );
31+
} );
32+
33+
test.describe( 'Server-side HEIC conversion', () => {
34+
test( 'uploads HEIC and lets server convert when supported', async ( {
35+
admin,
36+
page,
37+
editor,
38+
mediaUtils,
39+
requestUtils,
40+
} ) => {
41+
// Check if server supports HEIC
42+
const siteData = await requestUtils.rest( {
43+
method: 'GET',
44+
path: '/',
45+
} );
46+
47+
// Skip test if server doesn't support HEIC
48+
test.skip(
49+
! siteData.supports_heic,
50+
'Server does not support HEIC conversion'
51+
);
52+
53+
await admin.createNewPost();
54+
55+
// Enable convertUnsafe to trigger HEIC conversion flow
56+
await page.evaluate( () => {
57+
window.wp.data
58+
.dispatch( 'core/preferences' )
59+
.set(
60+
'media-experiments/preferences',
61+
'convertUnsafe',
62+
true
63+
);
64+
window.wp.data
65+
.dispatch( 'core/preferences' )
66+
.set(
67+
'media-experiments/preferences',
68+
'thumbnailGeneration',
69+
'client'
70+
);
71+
window.wp.data
72+
.dispatch( 'core/preferences' )
73+
.set(
74+
'media-experiments/preferences',
75+
'imageLibrary',
76+
'vips'
77+
);
78+
} );
79+
80+
await editor.insertBlock( { name: 'core/image' } );
81+
82+
const imageBlock = editor.canvas.locator(
83+
'role=document[name="Block: Image"i]'
84+
);
85+
await expect( imageBlock ).toBeVisible();
86+
87+
await mediaUtils.upload(
88+
imageBlock.locator( 'data-testid=form-file-upload-input' ),
89+
heicFile
90+
);
91+
92+
// Wait for upload queue to be empty
93+
await page.waitForFunction(
94+
() =>
95+
window.wp.data
96+
.select( 'media-experiments/upload' )
97+
.getItems().length === 0,
98+
undefined,
99+
{
100+
timeout: 100_000,
101+
}
102+
);
103+
104+
// Verify no upload errors
105+
await expect(
106+
page
107+
.getByRole( 'button', { name: 'Dismiss this notice' } )
108+
.filter( {
109+
hasText: 'File could not be uploaded',
110+
} )
111+
).toBeHidden();
112+
113+
await expect(
114+
page
115+
.getByRole( 'button', { name: 'Dismiss this notice' } )
116+
.filter( {
117+
hasText:
118+
/Error while uploading file .* to the media library/,
119+
} )
120+
).toBeHidden();
121+
122+
// Get the uploaded image ID
123+
const imageId = await page.evaluate(
124+
() =>
125+
window.wp.data
126+
.select( 'core/block-editor' )
127+
.getSelectedBlock()?.attributes?.id
128+
);
129+
130+
const media: RestAttachment = await requestUtils.rest( {
131+
method: 'GET',
132+
path: `/wp/v2/media/${ imageId }`,
133+
} );
134+
135+
// Verify the server converted HEIC to JPEG
136+
expect( media.mime_type ).toBe( 'image/jpeg' );
137+
138+
// Verify metadata is present
139+
/* eslint-disable camelcase */
140+
expect( media.media_details ).toEqual(
141+
expect.objectContaining( {
142+
width: 800,
143+
height: 600,
144+
filesize: expect.any( Number ),
145+
blurhash: expect.any( String ),
146+
dominant_color: expect.any( String ),
147+
} )
148+
);
149+
150+
// Verify client-side thumbnails were generated
151+
expect( media.media_details.sizes ).toEqual(
152+
expect.objectContaining( {
153+
thumbnail: expect.objectContaining( {
154+
width: 150,
155+
height: 150,
156+
filesize: expect.any( Number ),
157+
mime_type: 'image/jpeg',
158+
} ),
159+
medium: expect.objectContaining( {
160+
width: 300,
161+
height: 225,
162+
filesize: expect.any( Number ),
163+
mime_type: 'image/jpeg',
164+
} ),
165+
} )
166+
);
167+
/* eslint-enable camelcase */
168+
169+
// Verify image is displayed in the editor
170+
const settingsPanel = page
171+
.getByRole( 'region', {
172+
name: 'Editor settings',
173+
} )
174+
.getByRole( 'tabpanel', {
175+
name: 'Settings',
176+
} );
177+
178+
await expect( settingsPanel ).toHaveText(
179+
/Mime type: image\/jpeg/
180+
);
181+
182+
// Verify blurhash is visible
183+
await expect( page.locator( 'css=[data-blurhash]' ) ).toBeVisible();
184+
} );
185+
186+
test( 'falls back to client-side conversion when server does not support HEIC', async ( {
187+
admin,
188+
page,
189+
editor,
190+
mediaUtils,
191+
requestUtils,
192+
} ) => {
193+
// Check if server supports HEIC
194+
const siteData = await requestUtils.rest( {
195+
method: 'GET',
196+
path: '/',
197+
} );
198+
199+
// Skip test if server DOES support HEIC (we want to test the fallback)
200+
test.skip(
201+
siteData.supports_heic,
202+
'Server supports HEIC - testing fallback scenario'
203+
);
204+
205+
await admin.createNewPost();
206+
207+
// Enable convertUnsafe to trigger HEIC conversion flow
208+
await page.evaluate( () => {
209+
window.wp.data
210+
.dispatch( 'core/preferences' )
211+
.set(
212+
'media-experiments/preferences',
213+
'convertUnsafe',
214+
true
215+
);
216+
window.wp.data
217+
.dispatch( 'core/preferences' )
218+
.set(
219+
'media-experiments/preferences',
220+
'thumbnailGeneration',
221+
'client'
222+
);
223+
window.wp.data
224+
.dispatch( 'core/preferences' )
225+
.set(
226+
'media-experiments/preferences',
227+
'imageLibrary',
228+
'vips'
229+
);
230+
} );
231+
232+
await editor.insertBlock( { name: 'core/image' } );
233+
234+
const imageBlock = editor.canvas.locator(
235+
'role=document[name="Block: Image"i]'
236+
);
237+
await expect( imageBlock ).toBeVisible();
238+
239+
await mediaUtils.upload(
240+
imageBlock.locator( 'data-testid=form-file-upload-input' ),
241+
heicFile
242+
);
243+
244+
// Wait for upload queue to be empty
245+
await page.waitForFunction(
246+
() =>
247+
window.wp.data
248+
.select( 'media-experiments/upload' )
249+
.getItems().length === 0,
250+
undefined,
251+
{
252+
timeout: 100_000,
253+
}
254+
);
255+
256+
// Verify no upload errors
257+
await expect(
258+
page
259+
.getByRole( 'button', { name: 'Dismiss this notice' } )
260+
.filter( {
261+
hasText: 'File could not be uploaded',
262+
} )
263+
).toBeHidden();
264+
265+
// Get the uploaded image ID
266+
const imageId = await page.evaluate(
267+
() =>
268+
window.wp.data
269+
.select( 'core/block-editor' )
270+
.getSelectedBlock()?.attributes?.id
271+
);
272+
273+
const media: RestAttachment = await requestUtils.rest( {
274+
method: 'GET',
275+
path: `/wp/v2/media/${ imageId }`,
276+
} );
277+
278+
// Verify the client converted HEIC to JPEG
279+
expect( media.mime_type ).toBe( 'image/jpeg' );
280+
281+
// Verify metadata is present
282+
/* eslint-disable camelcase */
283+
expect( media.media_details ).toEqual(
284+
expect.objectContaining( {
285+
filesize: expect.any( Number ),
286+
blurhash: expect.any( String ),
287+
dominant_color: expect.any( String ),
288+
} )
289+
);
290+
/* eslint-enable camelcase */
291+
292+
// Verify image is displayed in the editor
293+
const settingsPanel = page
294+
.getByRole( 'region', {
295+
name: 'Editor settings',
296+
} )
297+
.getByRole( 'tabpanel', {
298+
name: 'Settings',
299+
} );
300+
301+
await expect( settingsPanel ).toHaveText(
302+
/Mime type: image\/jpeg/
303+
);
304+
} );
305+
} );
306+
} );

0 commit comments

Comments
 (0)