Skip to content

Commit 49c6ae2

Browse files
Basic conversion test
1 parent 957fc48 commit 49c6ae2

File tree

4 files changed

+137
-81
lines changed

4 files changed

+137
-81
lines changed

src/class-tiny-picture.php

Lines changed: 85 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -18,43 +18,48 @@
1818
* with this program; if not, write to the Free Software Foundation, Inc., 51
1919
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2020
*/
21-
class Tiny_Picture {
21+
class Tiny_Picture
22+
{
2223

2324

2425

25-
public function __construct() {
26+
public function __construct()
27+
{
2628
$this->init();
2729
}
2830

29-
private function init() {
30-
if ( is_admin() || is_customize_preview() ) {
31+
private function init()
32+
{
33+
if (is_admin() || is_customize_preview()) {
3134
return;
3235
}
3336

34-
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
37+
if (defined('DOING_AJAX') && DOING_AJAX) {
3538
return;
3639
}
3740

38-
if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
41+
if (defined('DOING_CRON') && DOING_CRON) {
3942
return;
4043
}
4144

4245
add_action('template_redirect', function () {
43-
ob_start( [ $this, 'replace_img_with_picture_tag' ] );
46+
ob_start([$this, 'replace_img_with_picture_tag']);
4447
}, 1000);
4548
}
4649

47-
public function replace_img_with_picture_tag( $content ) {
48-
$images = $this->filter_images( $content );
50+
public function replace_img_with_picture_tag($content)
51+
{
52+
$images = $this->filter_images($content);
4953

50-
foreach ( $images as $image ) {
51-
$content = $this->replace_image( $content, $image );
54+
foreach ($images as $image) {
55+
$content = $this->replace_image($content, $image);
5256
}
5357
return $content;
5458
}
5559

56-
private function replace_image( $content, $image ) {
57-
$content = str_replace( $image->img_element, $image->get_picture_element(), $content );
60+
private function replace_image($content, $image)
61+
{
62+
$content = str_replace($image->img_element, $image->get_picture_element(), $content);
5863
return $content;
5964
}
6065

@@ -63,33 +68,35 @@ private function replace_image( $content, $image ) {
6368
*
6469
* @return Tiny_Image[]
6570
*/
66-
private function filter_images( $content ) {
67-
if ( preg_match( '/(?=<body).*<\/body>/is', $content, $body ) ) {
71+
private function filter_images($content)
72+
{
73+
if (preg_match('/(?=<body).*<\/body>/is', $content, $body)) {
6874
$content = $body[0];
6975
}
7076

71-
$content = preg_replace( '/<!--(.*)-->/Uis', '', $content );
77+
$content = preg_replace('/<!--(.*)-->/Uis', '', $content);
7278

73-
$content = preg_replace( '#<noscript(.*?)>(.*?)</noscript>#is', '', $content );
79+
$content = preg_replace('#<noscript(.*?)>(.*?)</noscript>#is', '', $content);
7480

75-
if ( ! preg_match_all( '/<img\s.*>/isU', $content, $matches ) ) {
81+
if (! preg_match_all('/<img\s.*>/isU', $content, $matches)) {
7682
return array();
7783
}
7884

79-
$images = array_map(function ( $img ) {
80-
return new Tiny_Picture_Element( $img );
85+
$images = array_map(function ($img) {
86+
return new Tiny_Picture_Element($img);
8187
}, $matches[0]);
82-
$images = array_filter( $images );
88+
$images = array_filter($images);
8389

84-
if ( ! $images || ! is_array( $images ) ) {
90+
if (! $images || ! is_array($images)) {
8591
return array();
8692
}
8793

8894
return $images;
8995
}
9096
}
9197

92-
class Tiny_Picture_Element {
98+
class Tiny_Picture_Element
99+
{
93100

94101

95102
/**
@@ -104,97 +111,118 @@ class Tiny_Picture_Element {
104111
*/
105112
private $img_element_node;
106113

107-
public function __construct( $img_element ) {
114+
private $base_url;
115+
private $base_dir;
116+
117+
public function __construct($img_element)
118+
{
108119
$this->img_element = $img_element;
109120
$dom = new \DOMDocument();
110-
$dom->loadHTML( $img_element );
111-
$this->img_element_node = $dom->getElementsByTagName( 'img' )->item( 0 );
121+
$dom->loadHTML($img_element);
122+
$this->img_element_node = $dom->getElementsByTagName('img')->item(0);
123+
124+
$wp_upload_dir = wp_upload_dir();
125+
$this->base_url = $wp_upload_dir['baseurl'];
126+
$this->base_dir = $wp_upload_dir['basedir'];
112127
}
113128

114129
/**
115130
* Retrieves the image sources from the img element
116131
*
117132
* @return array{path: string, size: string}[] The image sources
118133
*/
119-
private function get_image_srcsets() {
134+
private function get_image_srcsets()
135+
{
120136
$result = array();
121-
$srcset = $this->img_element_node->getAttribute( 'srcset' );
137+
$srcset = $this->img_element_node->getAttribute('srcset');
122138

123-
if ( $srcset ) {
139+
if ($srcset) {
124140
// Split the srcset by commas to get individual entries
125-
$srcset_entries = explode( ',', $srcset );
141+
$srcset_entries = explode(',', $srcset);
126142

127-
foreach ( $srcset_entries as $entry ) {
143+
foreach ($srcset_entries as $entry) {
128144
// Trim whitespace
129-
$entry = trim( $entry );
145+
$entry = trim($entry);
130146

131147
// Split by whitespace to separate path and size descriptor
132-
$parts = preg_split( '/\s+/', $entry, 2 );
148+
$parts = preg_split('/\s+/', $entry, 2);
133149

134-
if ( count( $parts ) === 2 ) {
150+
if (count($parts) === 2) {
135151
// We have both path and size
136152
$result[] = array(
137153
'path' => $parts[0],
138154
'size' => $parts[1],
139155
);
140-
} elseif ( count( $parts ) === 1 ) {
156+
} elseif (count($parts) === 1) {
141157
// We only have a path (unusual in srcset)
142158
$result[] = array(
143159
'path' => $parts[0],
144160
'size' => '',
145161
);
146162
}
147163
}
148-
} elseif ( $this->img_element_node->hasAttribute( 'src' ) ) {
164+
} elseif ($this->img_element_node->hasAttribute('src')) {
149165
// No srcset, but we have a src attribute
150166
$result[] = array(
151-
'path' => $this->img_element_node->getAttribute( 'src' ),
167+
'path' => $this->img_element_node->getAttribute('src'),
152168
'size' => '',
153169
);
154170
}
155171
return $result;
156172
}
157173

158-
private function get_local_path( $url ) {
159-
$wp_upload_dir = wp_upload_dir();
160-
$local_path = parse_url( $url, PHP_URL_PATH );
161-
$format_path = str_replace( $wp_upload_dir['baseurl'], $wp_upload_dir['basedir'], $local_path );
162-
return $format_path;
174+
private function get_local_path($url)
175+
{
176+
if ( strpos( $url, 'http' ) === 0 ) {
177+
if ( strpos( $url, $this->base_url ) !== 0 ) {
178+
return false;
179+
}
180+
} else {
181+
$url = $this->base_url . ltrim( $url, '/' );
182+
}
183+
184+
// Convert to local path
185+
return str_replace( $this->base_url, $this->base_dir, $url );
163186
}
164187

165-
private function get_formatted_source( $imgsrcs, $mimetype ) {
188+
private function get_formatted_source($imgsrcs, $mimetype)
189+
{
166190
$formatted_src_set = array();
167-
foreach ( $imgsrcs as $imgsrc ) {
168-
$format_url = Tiny_Helpers::replace_file_extension( $mimetype, $imgsrc['path'] );
169-
$local_path = $this->get_local_path( $format_url );
170-
$exists_local = file_exists( $local_path );
171-
if ( $exists_local ) {
191+
foreach ($imgsrcs as $imgsrc) {
192+
$format_url = Tiny_Helpers::replace_file_extension($mimetype, $imgsrc['path']);
193+
$local_path = $this->get_local_path($format_url);
194+
if (empty($local_path)) {
195+
continue;
196+
}
197+
$exists_local = file_exists($local_path);
198+
if ($exists_local) {
172199
$formatted_src_set[] = $format_url . ' ' . $imgsrc['size'];
173200
}
174201
}
175202

176-
if ( empty( $formatted_src_set ) ) {
203+
if (empty($formatted_src_set)) {
177204
// no alternative sources found
178205
return '';
179206
}
180207

181-
$source_set = implode( ', ', $formatted_src_set );
182-
$trimmed_source_set = trim( $source_set );
208+
$source_set = implode(', ', $formatted_src_set);
209+
$trimmed_source_set = trim($source_set);
183210
return '<source type="' . $mimetype . '" srcset="' . $trimmed_source_set . '">';
184211
}
185212

186-
public function get_picture_element() {
213+
public function get_picture_element()
214+
{
187215
$srcsets = $this->get_image_srcsets();
188216

189-
$avif = $this->get_formatted_source( $srcsets, 'image/avif' );
190-
$webp = $this->get_formatted_source( $srcsets, 'image/webp' );
191-
if ( empty( $avif ) && empty( $webp ) ) {
217+
$avif = $this->get_formatted_source($srcsets, 'image/avif');
218+
$webp = $this->get_formatted_source($srcsets, 'image/webp');
219+
if (empty($avif) && empty($webp)) {
192220
return $this->img_element;
193221
}
194222

195223
$picture = '<picture>';
196-
$picture .= $this->get_formatted_source( $srcsets, 'image/avif' );
197-
$picture .= $this->get_formatted_source( $srcsets, 'image/webp' );
224+
$picture .= $this->get_formatted_source($srcsets, 'image/avif');
225+
$picture .= $this->get_formatted_source($srcsets, 'image/webp');
198226
$picture .= $this->img_element;
199227

200228
$picture .= '</picture>';

test/integration/conversion.spec.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import { Page, expect, test } from '@playwright/test';
2-
import { activatePlugin, clearMediaLibrary, deactivatePlugin, enableCompressionSizes, getWPVersion, setAPIKey, setCompressionTiming, setConversionSettings, uploadMedia } from './utils';
1+
import { APIRequestContext, Page, expect, request, test } from '@playwright/test';
2+
import { clearMediaLibrary, enableCompressionSizes, getWPVersion, newPost, setAPIKey, setCompressionTiming, setConversionSettings, uploadMedia } from './utils';
33

44
test.describe.configure({ mode: 'serial' });
55

66
let WPVersion = 0;
77

88
test.describe('conversion', () => {
99
let page: Page;
10+
let api: APIRequestContext;
1011

1112
test.beforeAll(async ({ browser }) => {
1213
page = await browser.newPage();
@@ -47,19 +48,13 @@ test.describe('conversion', () => {
4748
});
4849

4950
test('will display the optimized image on a page', async () => {
50-
await uploadMedia(page, 'input-example.jpg');
51-
52-
await page.goto('/wp-admin/post-new.php');
53-
await page.locator('iframe[name="editor-canvas"]').contentFrame().getByLabel('Add block').click();
54-
await page.getByRole('option', { name: 'Image' }).click();
55-
await page.locator('iframe[name="editor-canvas"]').contentFrame().getByRole('button', { name: 'Media Library' }).click();
56-
await page.getByLabel('input-example').click();
57-
await page.getByRole('button', { name: 'Select', exact: true }).click();
58-
await page.getByRole('button', { name: 'Publish', exact: true }).click();
59-
await page.getByLabel('Editor publish').getByRole('button', { name: 'Publish', exact: true }).click();
60-
await page.getByTestId('snackbar').getByRole('link', { name: 'View Post' }).click();
61-
await page.locator('img').click();
51+
const media = await uploadMedia(page, 'input-example.jpg');
52+
const postURL = await newPost(page, {
53+
title: 'test',
54+
content: `<figure class="wp-block-image size-large" id="tinytest"><img src="${media}" alt="" class="wp-image-209"/></figure>`,
55+
}, WPVersion);
56+
57+
page.goto(postURL);
58+
await page.waitForRequest(request => request.url().includes('input-example.avif'), { timeout: 1000 });
6259
});
63-
64-
test('will delete the optimized image when the original is deleted', async () => {});
6560
});

test/integration/settings.spec.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ let page: Page;
88
test.describe('settings', () => {
99
test.beforeAll(async ({ browser }) => {
1010
page = await browser.newPage();
11-
11+
1212
await setAPIKey(page, '');
1313

1414
// Resize on background
@@ -17,6 +17,10 @@ test.describe('settings', () => {
1717
// Enable all sizes
1818
await enableCompressionSizes(page, [], true);
1919

20+
await setConversionSettings(page, {
21+
convert: false,
22+
});
23+
2024
await page.locator('#submit').click();
2125
});
2226

@@ -215,20 +219,14 @@ test.describe('settings', () => {
215219
});
216220

217221
test('will not convert by default', async () => {
218-
222+
await expect(page.locator('#tinypng_conversion_convert')).not.toBeChecked();
219223
});
220224

221-
test('cannot have replace true while convert is false', async () => {
222-
223-
})
224-
225225
test('will store conversion settings', async () => {
226226
await setConversionSettings(page, {
227-
replace: true,
228227
convert: true,
229228
});
230229

231-
await expect(page.locator('#tinypng_conversion_replace')).toBeChecked();
232230
await expect(page.locator('#tinypng_conversion_convert')).toBeChecked();
233231
});
234232
});

test/integration/utils.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export async function uploadMedia(page: Page, file: string) {
1010
const fileChooser = await fileChooserPromise;
1111
await fileChooser.setFiles(path.join(__dirname, `../fixtures/${file}`));
1212
await page.locator('#html-upload').click();
13+
const imageURL = await page.getByLabel('Download').getAttribute('href');
14+
return imageURL;
1315
}
1416

1517
export async function clearMediaLibrary(page: Page) {
@@ -199,3 +201,36 @@ export async function setConversionSettings(page: Page, settings: { convert: boo
199201

200202
await page.locator('#submit').click();
201203
}
204+
205+
interface NewPostOptions {
206+
title?: string;
207+
content: string;
208+
excerpt?: string;
209+
}
210+
export async function newPost(page: Page, options: NewPostOptions, WPVersion: number): Promise<string> {
211+
const query = new URLSearchParams();
212+
const { title, content, excerpt } = options;
213+
214+
if (title) {
215+
query.set('post_title', title);
216+
}
217+
if (excerpt) {
218+
query.set('excerpt', excerpt);
219+
}
220+
221+
await page.goto('/wp-admin/post-new.php?' + query.toString());
222+
223+
if (WPVersion > 5) {
224+
await page.evaluate((contentHtml) => {
225+
wp.data.dispatch('core/editor').resetBlocks([]);
226+
wp.data.dispatch('core/editor').insertBlocks(wp.blocks.parse(contentHtml));
227+
}, content);
228+
await page.getByRole('button', { name: 'Publish', exact: true }).click();
229+
await page.getByLabel('Editor publish').getByRole('button', { name: 'Publish', exact: true }).click();
230+
await page.getByLabel('Editor publish').getByRole('link', { name: 'View Post' }).click();
231+
} else {
232+
// TODO:...
233+
}
234+
235+
return page.url();
236+
}

0 commit comments

Comments
 (0)