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 / Agent / Controllers / WPControllerTest.php

WPControllerTest.php in Extendify 3.1.0, at tests/Integration/Agent/Controllers/WPControllerTest.php

140 lines 5.7 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\Agent\Controllers;
4
5 use Extendify\Agent\Controllers\WPController;
6 use Extendify\QuickEdit\Controllers\SaveController;
7 use Extendify\Shared\Services\Import\Post;
8 use WP_UnitTestCase;
9
10 class WPControllerTest extends WP_UnitTestCase
11 {
12 public function test_lock_post_prevents_importer_update()
13 {
14 $postId = self::factory()->post->create(['post_content' => '<p>original</p>']);
15 $post = get_post($postId);
16
17 $user = self::factory()->user->create();
18 wp_set_current_user($user);
19
20 $request = new \WP_REST_Request();
21 $request->set_param('postId', $postId);
22 WPController::lockPost($request);
23
24 // Attempt to update as the cron user (no user).
25 wp_set_current_user(0);
26 $result = Post::update($post, '<p>imported content</p>');
27
28 $this->assertWPError($result);
29 $this->assertSame(1005, $result->get_error_code());
30 $this->assertSame('<p>original</p>', get_post($postId)->post_content);
31 }
32
33 // A template-part block must load by (partSlug, blockId) using the same
34 // preorder numbering TagTemplateParts assigns on the front end. Before the
35 // template-part branch, getBlockCode read only postId, so a header CTA
36 // could be saved but never opened — the click silently no-op'd.
37 public function test_get_block_code_loads_template_part_block_by_slug()
38 {
39 $this->loginAsAdmin();
40 $slug = $this->createHeaderPart(
41 '<!-- wp:paragraph --><p>AAA</p><!-- /wp:paragraph -->'
42 . '<!-- wp:group --><div class="wp-block-group">'
43 . '<!-- wp:heading --><h2>BBB</h2><!-- /wp:heading -->'
44 . '<!-- wp:paragraph --><p>CCC</p><!-- /wp:paragraph -->'
45 . '</div><!-- /wp:group -->'
46 );
47
48 // Preorder: 1=paragraph(AAA) 2=group 3=heading(BBB) 4=paragraph(CCC).
49 $req = new \WP_REST_Request();
50 $req->set_param('partSlug', $slug);
51 $req->set_param('blockId', 3);
52 $res = WPController::getBlockCode($req);
53
54 $this->assertSame(200, $res->get_status());
55 $data = $res->get_data();
56 $this->assertSame('core/heading', $data['name']);
57 $this->assertStringContainsString('<h2>BBB</h2>', $data['block']);
58 }
59
60 // Parity: the block load resolves for (slug, N) must be the exact block save
61 // mutates for (slug, N). Both share TemplatePartBlockFinder, so an edit aimed
62 // at the loaded block lands there and nowhere else — no silent off-by-one
63 // between two walks, the central correctness risk of this feature.
64 public function test_template_part_load_and_save_resolve_the_same_block()
65 {
66 $this->loginAsAdmin();
67 $slug = $this->createHeaderPart(
68 '<!-- wp:paragraph --><p>AAA</p><!-- /wp:paragraph -->'
69 . '<!-- wp:group --><div class="wp-block-group">'
70 . '<!-- wp:heading --><h2>BBB</h2><!-- /wp:heading -->'
71 . '<!-- wp:paragraph --><p>CCC</p><!-- /wp:paragraph -->'
72 . '</div><!-- /wp:group -->'
73 );
74
75 $loadReq = new \WP_REST_Request();
76 $loadReq->set_param('partSlug', $slug);
77 $loadReq->set_param('blockId', 3);
78 $loaded = WPController::getBlockCode($loadReq)->get_data();
79 $this->assertStringContainsString('<h2>BBB</h2>', $loaded['block']);
80
81 $saveReq = new \WP_REST_Request('POST', '/extendify/v1/quick-edit/save');
82 $saveReq->set_header('Content-Type', 'application/json');
83 $saveReq->set_body(wp_json_encode([
84 'source' => ['kind' => 'template-part', 'partSlug' => $slug],
85 'blockId' => 3,
86 'blockType' => 'core/heading',
87 'rawBlock' => '<!-- wp:heading --><h2>ZZZ</h2><!-- /wp:heading -->',
88 ]));
89 $saveRes = SaveController::handleSave($saveReq);
90 $this->assertSame(200, $saveRes->get_status());
91
92 $template = get_block_template(get_stylesheet() . '//' . $slug, 'wp_template_part');
93 $stored = get_post($template->wp_id)->post_content;
94 $this->assertStringContainsString('<h2>ZZZ</h2>', $stored);
95 $this->assertStringNotContainsString('<h2>BBB</h2>', $stored);
96 $this->assertStringContainsString('<p>AAA</p>', $stored);
97 $this->assertStringContainsString('<p>CCC</p>', $stored);
98 }
99
100 // The post path (postId + TagBlocks top-level numbering) stays unchanged by
101 // the new template-part branch.
102 public function test_get_block_code_post_path_still_resolves_block()
103 {
104 $this->loginAsAdmin();
105 $postId = self::factory()->post->create([
106 'post_content' => '<!-- wp:paragraph --><p>one</p><!-- /wp:paragraph -->'
107 . '<!-- wp:heading --><h2>two</h2><!-- /wp:heading -->',
108 ]);
109
110 $req = new \WP_REST_Request();
111 $req->set_param('postId', $postId);
112 $req->set_param('blockId', 2);
113 $res = WPController::getBlockCode($req);
114
115 $this->assertSame(200, $res->get_status());
116 $data = $res->get_data();
117 $this->assertSame('core/heading', $data['name']);
118 $this->assertStringContainsString('<h2>two</h2>', $data['block']);
119 }
120
121 private function loginAsAdmin(): void
122 {
123 $admin = self::factory()->user->create(['role' => 'administrator']);
124 wp_set_current_user($admin);
125 }
126
127 private function createHeaderPart(string $content): string
128 {
129 $slug = 'qe-header-' . wp_generate_password(6, false);
130 $partId = self::factory()->post->create([
131 'post_type' => 'wp_template_part',
132 'post_name' => $slug,
133 'post_status' => 'publish',
134 'post_content' => $content,
135 ]);
136 wp_set_object_terms($partId, get_stylesheet(), 'wp_theme');
137 return $slug;
138 }
139 }
140