@@ -164,26 +164,84 @@ public function test_img_with_srcsets()
164164 $ this ->wp ->createImage (1000 , '2025/01 ' , 'test-320w.webp ' );
165165
166166 $ input = '<img srcset="/wp-content/uploads/2025/01/test-320w.jpg, /wp-content/uploads/2025/01/test-480w.jpg 1.5x, /wp-content/uploads/2025/01/test-640w.jpg 2x" src="/wp-content/uploads/2025/01/test-640w.jpg" /> ' ;
167- $ expected = '<picture><source srcset="/wp-content/uploads/2025/01/test-320w.webp, /wp-content/uploads/2025/01/test-480w.webp 1.5x, /wp-content/uploads/2025/01/test-640w.webp 2x, /wp-content/uploads/2025/01/test-640w.webp " type="image/webp" /><img srcset="/wp-content/uploads/2025/01/test-320w.jpg, /wp-content/uploads/2025/01/test-480w.jpg 1.5x, /wp-content/uploads/2025/01/test-640w.jpg 2x" src="/wp-content/uploads/2025/01/test-640w.jpg" /></picture> ' ;
167+ $ expected = '<picture><source srcset="/wp-content/uploads/2025/01/test-320w.webp, /wp-content/uploads/2025/01/test-480w.webp 1.5x, /wp-content/uploads/2025/01/test-640w.webp 2x" type="image/webp" /><img srcset="/wp-content/uploads/2025/01/test-320w.jpg, /wp-content/uploads/2025/01/test-480w.jpg 1.5x, /wp-content/uploads/2025/01/test-640w.jpg 2x" src="/wp-content/uploads/2025/01/test-640w.jpg" /></picture> ' ;
168168 $ output = $ this ->tiny_picture ->replace_sources ($ input );
169169
170170 $ this ->assertEquals ($ expected , $ output );
171171 }
172172
173+ public function test_get_largest_width_descriptor_returns_largest_value ()
174+ {
175+ $ srcsets = array (
176+ array ('path ' => '/wp-content/uploads/2025/01/test_320x320.jpg ' , 'size ' => '320w ' ),
177+ array ('path ' => '/wp-content/uploads/2025/01/test_320x320.jpg ' , 'size ' => '2000w ' ),
178+ array ('path ' => 'c ' , 'size ' => '2x ' ), // this is effectively ignored because there are width descriptors
179+ );
180+
181+ $ imgSource = new Tiny_Image_Source ('<img srcset="/wp-content/uploads/2025/01/test-420w.jpg 420w, /wp-content/uploads/2025/01/test-650w.jpg 650w, /wp-content/uploads/2025/01/test.jpg 2000w" src="/wp-content/uploads/2025/01/test.jpg" /> ' , '' , array ());
182+ $ largest = Tiny_Image_Source::get_largest_width_descriptor ($ srcsets );
183+
184+ $ this ->assertEquals (2000 , $ largest );
185+ }
186+
187+ public function test_get_largest_width_descriptor_without_widths_returns_zero ()
188+ {
189+ $ srcsets = array (
190+ array ('path ' => '/wp-content/uploads/2025/01/test@1x.jpg ' , 'size ' => '1x ' ),
191+ array ('path ' => '/wp-content/uploads/2025/01/test.jpg ' , 'size ' => '2x ' ),
192+ );
193+
194+ $ largest = Tiny_Image_Source::get_largest_width_descriptor ($ srcsets );
195+
196+ $ this ->assertSame (0 , $ largest );
197+ }
198+
199+ public function test_srcset_contains_width_descriptor_returns_true_when_present ()
200+ {
201+ $ parts = array (
202+ '/wp-content/uploads/2025/01/test-320w.webp 320w ' ,
203+ '/wp-content/uploads/2025/01/test-640w.webp 640w ' ,
204+ );
205+
206+ $ this ->assertTrue (Tiny_Image_Source::srcset_contains_width_descriptor ($ parts , 640 ));
207+ }
208+
209+ public function test_srcset_contains_width_descriptor_returns_false_when_missing ()
210+ {
211+ $ parts = array (
212+ '/wp-content/uploads/2025/01/test-320w.webp 320w ' ,
213+ '/wp-content/uploads/2025/01/test-640w.webp 640w ' ,
214+ );
215+
216+ $ this ->assertFalse (Tiny_Image_Source::srcset_contains_width_descriptor ($ parts , 1280 ));
217+ }
218+
219+ public function test_get_largest_width_no_descriptors ()
220+ {
221+ $ srcsets = array (
222+ array ('path ' => '/wp-content/uploads/2025/01/test.jpg ' , 'size ' => '' ),
223+ );
224+
225+ $ largest = Tiny_Image_Source::get_largest_width_descriptor ($ srcsets );
226+
227+ $ this ->assertSame (0 , $ largest );
228+ }
229+
173230 public function test_picture_with_srcsets ()
174231 {
175232 $ this ->wp ->createImage (1000 , '2025/01 ' , 'test-640w.webp ' );
176233 $ this ->wp ->createImage (1000 , '2025/01 ' , 'test-480w.webp ' );
177234 $ this ->wp ->createImage (1000 , '2025/01 ' , 'test-320w.webp ' );
178235
179236 $ input = '<picture><img srcset="/wp-content/uploads/2025/01/test-320w.jpg, /wp-content/uploads/2025/01/test-480w.jpg 1.5x, /wp-content/uploads/2025/01/test-640w.jpg 2x" src="/wp-content/uploads/2025/01/test-640w.jpg" /></picture> ' ;
180- $ expected = '<picture><source srcset="/wp-content/uploads/2025/01/test-320w.webp, /wp-content/uploads/2025/01/test-480w.webp 1.5x, /wp-content/uploads/2025/01/test-640w.webp 2x, /wp-content/uploads/2025/01/test-640w.webp " type="image/webp" /><img srcset="/wp-content/uploads/2025/01/test-320w.jpg, /wp-content/uploads/2025/01/test-480w.jpg 1.5x, /wp-content/uploads/2025/01/test-640w.jpg 2x" src="/wp-content/uploads/2025/01/test-640w.jpg" /></picture> ' ;
237+ $ expected = '<picture><source srcset="/wp-content/uploads/2025/01/test-320w.webp, /wp-content/uploads/2025/01/test-480w.webp 1.5x, /wp-content/uploads/2025/01/test-640w.webp 2x" type="image/webp" /><img srcset="/wp-content/uploads/2025/01/test-320w.jpg, /wp-content/uploads/2025/01/test-480w.jpg 1.5x, /wp-content/uploads/2025/01/test-640w.jpg 2x" src="/wp-content/uploads/2025/01/test-640w.jpg" /></picture> ' ;
181238 $ output = $ this ->tiny_picture ->replace_sources ($ input );
182239
183240 $ this ->assertEquals ($ expected , $ output );
184241 }
185242
186- public function test_picture_with_attributes () {
243+ public function test_picture_with_attributes ()
244+ {
187245 $ this ->wp ->createImage (1000 , '2025/01 ' , 'test-landscape.webp ' );
188246
189247 $ input = '<picture><source srcset="/wp-content/uploads/2025/01/test-landscape.jpg" width="200" height="200" media="(width >= 600px)" /><img src="/wp-content/uploads/2025/01/test.jpg" /></picture> ' ;
@@ -215,4 +273,58 @@ public function test_img_with_query_and_fragment_keeps_both()
215273
216274 $ this ->assertEquals ($ expected , $ output );
217275 }
276+
277+ /**
278+ * scenario where there is only a low resolution variant for a certain image.
279+ * this can happen when credits or API decides that only low resolution image
280+ * is in a different format (this is resolved in 3.6.4)
281+ */
282+ public function test_skip_low_res_if_largest_width_is_not_present ()
283+ {
284+ $ this ->wp ->createImage (37857 , '2025/09 ' , 'test_250x250.webp ' );
285+
286+ // largest size should exist otherwise we mark it as a incomplete sourceset
287+ $ input = '<img src="/wp-content/uploads/2025/09/test.png" srcset="/wp-content/uploads/2025/09/test_250x250.png 350w, /wp-content/uploads/2025/09/test.png 2000w"> ' ;
288+ $ output = $ this ->tiny_picture ->replace_sources ($ input );
289+
290+ // no replacement should be done as there is only a 250x250 but no original
291+ $ this ->assertEquals ($ input , $ output );
292+ }
293+
294+ /**
295+ * if the largest width is in a srcset, then we will use the alternative source
296+ */
297+ public function test_largest_width_is_present_so_include_sourceset ()
298+ {
299+ $ this ->wp ->createImage (37857 , '2025/09 ' , 'test_250x250.webp ' );
300+ $ this ->wp ->createImage (37857 , '2025/09 ' , 'test.webp ' );
301+
302+ // largest size should be present otherwise we mark it as a incomplete sourceset
303+ $ input = '<img src="/wp-content/uploads/2025/09/test.png" srcset="/wp-content/uploads/2025/09/test_250x250.png 350w, /wp-content/uploads/2025/09/test.png 2000w"> ' ;
304+ $ expected = '<picture><source srcset="/wp-content/uploads/2025/09/test_250x250.webp 350w, /wp-content/uploads/2025/09/test.webp 2000w" type="image/webp" /><img src="/wp-content/uploads/2025/09/test.png" srcset="/wp-content/uploads/2025/09/test_250x250.png 350w, /wp-content/uploads/2025/09/test.png 2000w"></picture> ' ;
305+ $ output = $ this ->tiny_picture ->replace_sources ($ input );
306+
307+ // no replacement should be done as there is only a 250x250 but no original
308+ $ this ->assertSame ($ expected , $ output );
309+ }
310+
311+ /**
312+ * if width and pd descriptors are present, then only width is applicable and
313+ * pd are effectively ignored
314+ *
315+ * Note that if any resource in a srcset is described with a "w" descriptor, all resources within that srcset must also be described with "w" descriptors, and the image element's src is not considered a candidate.
316+ * https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/srcset#value
317+ */
318+ public function test_mixed_descriptors_in_soruce ()
319+ {
320+ $ this ->wp ->createImage (37857 , '2025/09 ' , 'test_250x250.webp ' );
321+ $ this ->wp ->createImage (37857 , '2025/09 ' , 'test.webp ' );
322+
323+ // this will show test_250x250.png but that would also happen on the original img
324+ $ input = '<img src="/wp-content/uploads/2025/09/test.png" srcset="/wp-content/uploads/2025/09/test_250x250.png 350w, /wp-content/uploads/2025/09/test.png 2x"> ' ;
325+ $ expected = '<picture><source srcset="/wp-content/uploads/2025/09/test_250x250.webp 350w, /wp-content/uploads/2025/09/test.png 2x" type="image/webp" /><img src="/wp-content/uploads/2025/09/test.png" srcset="/wp-content/uploads/2025/09/test_250x250.png 350w, /wp-content/uploads/2025/09/test.png 2x"></picture> ' ;
326+ $ output = $ this ->tiny_picture ->replace_sources ($ input );
327+
328+ $ this ->assertSame ($ expected , $ output );
329+ }
218330}
0 commit comments