PluginProbe
Extendify / 3.1.0
Extendify v3.1.0
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / tests / Integration / QuickEdit / Schemas / CoverTest.php

CoverTest.php in Extendify 3.1.0, at tests/Integration/QuickEdit/Schemas/CoverTest.php

195 lines 8.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Extendify\Tests\Integration\QuickEdit\Schemas;
4
5 use Extendify\QuickEdit\Schemas\Cover;
6 use WP_UnitTestCase;
7
8 class CoverTest extends WP_UnitTestCase
9 {
10 public function test_fields_returns_single_background_field()
11 {
12 $fields = (new Cover())->fields();
13
14 $this->assertCount(1, $fields);
15 $this->assertSame('background', $fields[0]['key']);
16 $this->assertSame('image', $fields[0]['control']);
17 }
18
19 public function test_apply_with_valid_id_derives_url_and_patches_innerContent_chunks()
20 {
21 // innerContent for a cover with inner blocks is `[wrapper_open, null, wrapper_close]`;
22 // the nulls map to innerBlocks at serialize time.
23 $attId = self::factory()->attachment->create_upload_object($this->writeStubPng());
24 $expectedUrl = wp_get_attachment_image_url($attId, 'full');
25
26 $open = '<div class="wp-block-cover"><img class="wp-block-cover__image-background wp-image-1" src="/old.jpg" />';
27 $close = '</div>';
28 $block = [
29 'blockName' => 'core/cover',
30 'attrs' => ['url' => '/old.jpg', 'id' => 1, 'hasParallax' => true],
31 'innerBlocks' => [['blockName' => 'core/paragraph', 'attrs' => [], 'innerBlocks' => [], 'innerHTML' => '', 'innerContent' => []]],
32 'innerHTML' => $open . $close,
33 'innerContent' => [$open, null, $close],
34 ];
35
36 // The server-derived url must win over the hostile client url.
37 $result = (new Cover())->apply($block, 'background', [
38 'url' => 'https://attacker.test/evil.jpg',
39 'id' => $attId,
40 ]);
41
42 $this->assertSame($expectedUrl, $result['attrs']['url']);
43 $this->assertSame($attId, $result['attrs']['id']);
44 // hasParallax is intentionally removed — the rebuilt block can't carry it through.
45 $this->assertArrayNotHasKey('hasParallax', $result['attrs']);
46
47 // innerContent[1] stays null (innerBlocks slot preserved).
48 $this->assertNull($result['innerContent'][1]);
49
50 // The opening chunk got patched with the server url, not the client's.
51 $this->assertStringContainsString('src="' . $expectedUrl . '"', $result['innerContent'][0]);
52 $this->assertStringNotContainsString('attacker.test', $result['innerContent'][0]);
53 // Exact class proves the stale wp-image-1 was replaced by the new id
54 // (a substring check would trip on ids that start with "1").
55 $this->assertStringContainsString(
56 'class="wp-block-cover__image-background wp-image-' . $attId . '"',
57 $result['innerContent'][0]
58 );
59 }
60
61 public function test_apply_with_positive_but_invalid_id_returns_block_unchanged()
62 {
63 $open = '<div class="wp-block-cover"><img class="wp-block-cover__image-background wp-image-1" src="/old.jpg" />';
64 $close = '</div>';
65 $block = [
66 'blockName' => 'core/cover',
67 'attrs' => ['url' => '/old.jpg', 'id' => 1],
68 'innerBlocks' => [],
69 'innerHTML' => $open . $close,
70 'innerContent' => [$open, null, $close],
71 ];
72
73 // id resolves to no real image attachment: reject rather than trust the client url.
74 $result = (new Cover())->apply($block, 'background', [
75 'url' => 'https://attacker.test/evil.jpg',
76 'id' => 999999,
77 ]);
78
79 $this->assertSame($block, $result);
80 }
81
82 public function test_apply_without_id_strips_srcset_sizes_and_wp_image_class()
83 {
84 $open = '<div class="wp-block-cover"><img class="wp-block-cover__image-background wp-image-7" src="/old.jpg" srcset="/old-2x.jpg 2x" sizes="100vw" />';
85 $close = '</div>';
86 $block = [
87 'blockName' => 'core/cover',
88 'attrs' => ['url' => '/old.jpg', 'id' => 7],
89 'innerBlocks' => [],
90 'innerHTML' => $open . $close,
91 'innerContent' => [$open, null, $close],
92 ];
93
94 $result = (new Cover())->apply($block, 'background', ['url' => 'https://example.test/new.jpg']);
95
96 $this->assertArrayNotHasKey('id', $result['attrs']);
97 $this->assertStringNotContainsString('srcset=', $result['innerContent'][0]);
98 $this->assertStringNotContainsString('sizes=', $result['innerContent'][0]);
99 $this->assertStringNotContainsString('wp-image-', $result['innerContent'][0]);
100 }
101
102 public function test_apply_strips_extendify_image_import_marker_from_wrapper_div()
103 {
104 $open = '<div class="wp-block-cover extendify-image-import"><img class="wp-block-cover__image-background" src="/old.jpg" />';
105 $close = '</div>';
106 $block = [
107 'blockName' => 'core/cover',
108 'attrs' => [],
109 'innerBlocks' => [],
110 'innerHTML' => $open . $close,
111 'innerContent' => [$open, null, $close],
112 ];
113
114 $result = (new Cover())->apply($block, 'background', ['url' => 'https://example.test/new.jpg']);
115
116 $this->assertStringNotContainsString('extendify-image-import', $result['innerContent'][0]);
117 $this->assertStringContainsString('wp-block-cover', $result['innerContent'][0]);
118 }
119
120 public function test_apply_skips_color_only_cover_without_image_background_img()
121 {
122 // Color-only / video covers have no `wp-block-cover__image-background` img;
123 // apply() updates attrs but leaves innerContent alone.
124 $open = '<div class="wp-block-cover">';
125 $close = '</div>';
126 $block = [
127 'blockName' => 'core/cover',
128 'attrs' => ['overlayColor' => 'primary'],
129 'innerBlocks' => [],
130 'innerHTML' => $open . $close,
131 'innerContent' => [$open, null, $close],
132 ];
133
134 $result = (new Cover())->apply($block, 'background', ['url' => 'https://example.test/new.jpg']);
135
136 // attrs update happens even without an image-background to patch.
137 $this->assertSame('https://example.test/new.jpg', $result['attrs']['url']);
138 // The innerContent is left as-is — no synthesized <img>.
139 $this->assertSame($block['innerContent'], $result['innerContent']);
140 // innerHTML is untouched (only rewritten when innerContent is patched).
141 $this->assertSame($block['innerHTML'], $result['innerHTML']);
142 }
143
144 public function test_apply_with_no_url_returns_block_unchanged()
145 {
146 $block = [
147 'blockName' => 'core/cover',
148 'attrs' => [],
149 'innerBlocks' => [],
150 'innerHTML' => '<div></div>',
151 'innerContent' => ['<div>', null, '</div>'],
152 ];
153
154 $this->assertSame($block, (new Cover())->apply($block, 'background', ['url' => '']));
155 $this->assertSame($block, (new Cover())->apply($block, 'background', []));
156 }
157
158 public function test_apply_with_non_array_value_returns_block_unchanged()
159 {
160 $block = [
161 'blockName' => 'core/cover',
162 'attrs' => [],
163 'innerBlocks' => [],
164 'innerHTML' => '<div></div>',
165 'innerContent' => ['<div>', null, '</div>'],
166 ];
167
168 $this->assertSame($block, (new Cover())->apply($block, 'background', 'string'));
169 }
170
171 public function test_apply_with_unknown_field_returns_block_unchanged()
172 {
173 $block = [
174 'blockName' => 'core/cover',
175 'attrs' => [],
176 'innerBlocks' => [],
177 'innerHTML' => '<div></div>',
178 'innerContent' => ['<div>', null, '</div>'],
179 ];
180
181 $this->assertSame($block, (new Cover())->apply($block, 'overlayColor', 'red'));
182 }
183
184 private function writeStubPng(): string
185 {
186 // 1×1 transparent PNG so create_upload_object yields a real image attachment.
187 $bytes = base64_decode(
188 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='
189 );
190 $path = wp_tempnam('qe-test-stub') . '.png';
191 file_put_contents($path, $bytes);
192 return $path;
193 }
194 }
195