Chris@18
|
1 <?php
|
Chris@18
|
2
|
Chris@18
|
3 namespace Drupal\Tests\jsonapi\Functional;
|
Chris@18
|
4
|
Chris@18
|
5 use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
Chris@18
|
6 use Drupal\Core\Url;
|
Chris@18
|
7 use Drupal\field\Entity\FieldConfig;
|
Chris@18
|
8 use Drupal\field\Entity\FieldStorageConfig;
|
Chris@18
|
9 use Drupal\file\Entity\File;
|
Chris@18
|
10 use Drupal\taxonomy\Entity\Term;
|
Chris@18
|
11 use Drupal\taxonomy\Entity\Vocabulary;
|
Chris@18
|
12 use Drupal\Tests\BrowserTestBase;
|
Chris@18
|
13 use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
|
Chris@18
|
14 use Drupal\Tests\image\Kernel\ImageFieldCreationTrait;
|
Chris@18
|
15 use Drupal\user\Entity\Role;
|
Chris@18
|
16 use Drupal\user\RoleInterface;
|
Chris@18
|
17 use GuzzleHttp\Exception\ClientException;
|
Chris@18
|
18 use GuzzleHttp\Exception\ServerException;
|
Chris@18
|
19
|
Chris@18
|
20 /**
|
Chris@18
|
21 * Provides helper methods for the JSON:API module's functional tests.
|
Chris@18
|
22 *
|
Chris@18
|
23 * @internal
|
Chris@18
|
24 */
|
Chris@18
|
25 abstract class JsonApiFunctionalTestBase extends BrowserTestBase {
|
Chris@18
|
26
|
Chris@18
|
27 use EntityReferenceTestTrait;
|
Chris@18
|
28 use ImageFieldCreationTrait;
|
Chris@18
|
29
|
Chris@18
|
30 const IS_MULTILINGUAL = TRUE;
|
Chris@18
|
31 const IS_NOT_MULTILINGUAL = FALSE;
|
Chris@18
|
32
|
Chris@18
|
33 /**
|
Chris@18
|
34 * {@inheritdoc}
|
Chris@18
|
35 */
|
Chris@18
|
36 public static $modules = [
|
Chris@18
|
37 'jsonapi',
|
Chris@18
|
38 'serialization',
|
Chris@18
|
39 'node',
|
Chris@18
|
40 'image',
|
Chris@18
|
41 'taxonomy',
|
Chris@18
|
42 'link',
|
Chris@18
|
43 ];
|
Chris@18
|
44
|
Chris@18
|
45 /**
|
Chris@18
|
46 * Test user.
|
Chris@18
|
47 *
|
Chris@18
|
48 * @var \Drupal\user\Entity\User
|
Chris@18
|
49 */
|
Chris@18
|
50 protected $user;
|
Chris@18
|
51
|
Chris@18
|
52 /**
|
Chris@18
|
53 * Test user with access to view profiles.
|
Chris@18
|
54 *
|
Chris@18
|
55 * @var \Drupal\user\Entity\User
|
Chris@18
|
56 */
|
Chris@18
|
57 protected $userCanViewProfiles;
|
Chris@18
|
58
|
Chris@18
|
59 /**
|
Chris@18
|
60 * Test nodes.
|
Chris@18
|
61 *
|
Chris@18
|
62 * @var \Drupal\node\Entity\Node[]
|
Chris@18
|
63 */
|
Chris@18
|
64 protected $nodes = [];
|
Chris@18
|
65
|
Chris@18
|
66 /**
|
Chris@18
|
67 * Test taxonomy terms.
|
Chris@18
|
68 *
|
Chris@18
|
69 * @var \Drupal\taxonomy\Entity\Term[]
|
Chris@18
|
70 */
|
Chris@18
|
71 protected $tags = [];
|
Chris@18
|
72
|
Chris@18
|
73 /**
|
Chris@18
|
74 * Test files.
|
Chris@18
|
75 *
|
Chris@18
|
76 * @var \Drupal\file\Entity\File[]
|
Chris@18
|
77 */
|
Chris@18
|
78 protected $files = [];
|
Chris@18
|
79
|
Chris@18
|
80 /**
|
Chris@18
|
81 * The HTTP client.
|
Chris@18
|
82 *
|
Chris@18
|
83 * @var \GuzzleHttp\ClientInterface
|
Chris@18
|
84 */
|
Chris@18
|
85 protected $httpClient;
|
Chris@18
|
86
|
Chris@18
|
87 /**
|
Chris@18
|
88 * {@inheritdoc}
|
Chris@18
|
89 */
|
Chris@18
|
90 protected function setUp() {
|
Chris@18
|
91 parent::setUp();
|
Chris@18
|
92
|
Chris@18
|
93 // Set up a HTTP client that accepts relative URLs.
|
Chris@18
|
94 $this->httpClient = $this->container->get('http_client_factory')
|
Chris@18
|
95 ->fromOptions(['base_uri' => $this->baseUrl]);
|
Chris@18
|
96
|
Chris@18
|
97 // Create Basic page and Article node types.
|
Chris@18
|
98 if ($this->profile != 'standard') {
|
Chris@18
|
99 $this->drupalCreateContentType([
|
Chris@18
|
100 'type' => 'article',
|
Chris@18
|
101 'name' => 'Article',
|
Chris@18
|
102 ]);
|
Chris@18
|
103
|
Chris@18
|
104 // Setup vocabulary.
|
Chris@18
|
105 Vocabulary::create([
|
Chris@18
|
106 'vid' => 'tags',
|
Chris@18
|
107 'name' => 'Tags',
|
Chris@18
|
108 ])->save();
|
Chris@18
|
109
|
Chris@18
|
110 // Add tags and field_image to the article.
|
Chris@18
|
111 $this->createEntityReferenceField(
|
Chris@18
|
112 'node',
|
Chris@18
|
113 'article',
|
Chris@18
|
114 'field_tags',
|
Chris@18
|
115 'Tags',
|
Chris@18
|
116 'taxonomy_term',
|
Chris@18
|
117 'default',
|
Chris@18
|
118 [
|
Chris@18
|
119 'target_bundles' => [
|
Chris@18
|
120 'tags' => 'tags',
|
Chris@18
|
121 ],
|
Chris@18
|
122 'auto_create' => TRUE,
|
Chris@18
|
123 ],
|
Chris@18
|
124 FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
Chris@18
|
125 );
|
Chris@18
|
126 $this->createImageField('field_image', 'article');
|
Chris@18
|
127 $this->createImageField('field_heroless', 'article');
|
Chris@18
|
128 }
|
Chris@18
|
129
|
Chris@18
|
130 FieldStorageConfig::create([
|
Chris@18
|
131 'field_name' => 'field_link',
|
Chris@18
|
132 'entity_type' => 'node',
|
Chris@18
|
133 'type' => 'link',
|
Chris@18
|
134 'settings' => [],
|
Chris@18
|
135 'cardinality' => 1,
|
Chris@18
|
136 ])->save();
|
Chris@18
|
137
|
Chris@18
|
138 $field_config = FieldConfig::create([
|
Chris@18
|
139 'field_name' => 'field_link',
|
Chris@18
|
140 'label' => 'Link',
|
Chris@18
|
141 'entity_type' => 'node',
|
Chris@18
|
142 'bundle' => 'article',
|
Chris@18
|
143 'required' => FALSE,
|
Chris@18
|
144 'settings' => [],
|
Chris@18
|
145 'description' => '',
|
Chris@18
|
146 ]);
|
Chris@18
|
147 $field_config->save();
|
Chris@18
|
148
|
Chris@18
|
149 // Field for testing sorting.
|
Chris@18
|
150 FieldStorageConfig::create([
|
Chris@18
|
151 'field_name' => 'field_sort1',
|
Chris@18
|
152 'entity_type' => 'node',
|
Chris@18
|
153 'type' => 'integer',
|
Chris@18
|
154 ])->save();
|
Chris@18
|
155 FieldConfig::create([
|
Chris@18
|
156 'field_name' => 'field_sort1',
|
Chris@18
|
157 'entity_type' => 'node',
|
Chris@18
|
158 'bundle' => 'article',
|
Chris@18
|
159 ])->save();
|
Chris@18
|
160
|
Chris@18
|
161 // Another field for testing sorting.
|
Chris@18
|
162 FieldStorageConfig::create([
|
Chris@18
|
163 'field_name' => 'field_sort2',
|
Chris@18
|
164 'entity_type' => 'node',
|
Chris@18
|
165 'type' => 'integer',
|
Chris@18
|
166 ])->save();
|
Chris@18
|
167 FieldConfig::create([
|
Chris@18
|
168 'field_name' => 'field_sort2',
|
Chris@18
|
169 'entity_type' => 'node',
|
Chris@18
|
170 'bundle' => 'article',
|
Chris@18
|
171 ])->save();
|
Chris@18
|
172
|
Chris@18
|
173 $this->user = $this->drupalCreateUser([
|
Chris@18
|
174 'create article content',
|
Chris@18
|
175 'edit any article content',
|
Chris@18
|
176 'delete any article content',
|
Chris@18
|
177 ]);
|
Chris@18
|
178
|
Chris@18
|
179 // Create a user that can.
|
Chris@18
|
180 $this->userCanViewProfiles = $this->drupalCreateUser([
|
Chris@18
|
181 'access user profiles',
|
Chris@18
|
182 ]);
|
Chris@18
|
183
|
Chris@18
|
184 $this->grantPermissions(Role::load(RoleInterface::ANONYMOUS_ID), [
|
Chris@18
|
185 'access user profiles',
|
Chris@18
|
186 'administer taxonomy',
|
Chris@18
|
187 ]);
|
Chris@18
|
188
|
Chris@18
|
189 drupal_flush_all_caches();
|
Chris@18
|
190 }
|
Chris@18
|
191
|
Chris@18
|
192 /**
|
Chris@18
|
193 * Performs a HTTP request. Wraps the Guzzle HTTP client.
|
Chris@18
|
194 *
|
Chris@18
|
195 * Why wrap the Guzzle HTTP client? Because any error response is returned via
|
Chris@18
|
196 * an exception, which would make the tests unnecessarily complex to read.
|
Chris@18
|
197 *
|
Chris@18
|
198 * @param string $method
|
Chris@18
|
199 * HTTP method.
|
Chris@18
|
200 * @param \Drupal\Core\Url $url
|
Chris@18
|
201 * URL to request.
|
Chris@18
|
202 * @param array $request_options
|
Chris@18
|
203 * Request options to apply.
|
Chris@18
|
204 *
|
Chris@18
|
205 * @return \Psr\Http\Message\ResponseInterface
|
Chris@18
|
206 * The request response.
|
Chris@18
|
207 *
|
Chris@18
|
208 * @throws \GuzzleHttp\Exception\GuzzleException
|
Chris@18
|
209 *
|
Chris@18
|
210 * @see \GuzzleHttp\ClientInterface::request
|
Chris@18
|
211 */
|
Chris@18
|
212 protected function request($method, Url $url, array $request_options) {
|
Chris@18
|
213 try {
|
Chris@18
|
214 $response = $this->httpClient->request($method, $url->toString(), $request_options);
|
Chris@18
|
215 }
|
Chris@18
|
216 catch (ClientException $e) {
|
Chris@18
|
217 $response = $e->getResponse();
|
Chris@18
|
218 }
|
Chris@18
|
219 catch (ServerException $e) {
|
Chris@18
|
220 $response = $e->getResponse();
|
Chris@18
|
221 }
|
Chris@18
|
222
|
Chris@18
|
223 return $response;
|
Chris@18
|
224 }
|
Chris@18
|
225
|
Chris@18
|
226 /**
|
Chris@18
|
227 * Creates default content to test the API.
|
Chris@18
|
228 *
|
Chris@18
|
229 * @param int $num_articles
|
Chris@18
|
230 * Number of articles to create.
|
Chris@18
|
231 * @param int $num_tags
|
Chris@18
|
232 * Number of tags to create.
|
Chris@18
|
233 * @param bool $article_has_image
|
Chris@18
|
234 * Set to TRUE if you want to add an image to the generated articles.
|
Chris@18
|
235 * @param bool $article_has_link
|
Chris@18
|
236 * Set to TRUE if you want to add a link to the generated articles.
|
Chris@18
|
237 * @param bool $is_multilingual
|
Chris@18
|
238 * (optional) Set to TRUE if you want to enable multilingual content.
|
Chris@18
|
239 * @param bool $referencing_twice
|
Chris@18
|
240 * (optional) Set to TRUE if you want articles to reference the same tag
|
Chris@18
|
241 * twice.
|
Chris@18
|
242 */
|
Chris@18
|
243 protected function createDefaultContent($num_articles, $num_tags, $article_has_image, $article_has_link, $is_multilingual, $referencing_twice = FALSE) {
|
Chris@18
|
244 $random = $this->getRandomGenerator();
|
Chris@18
|
245 for ($created_tags = 0; $created_tags < $num_tags; $created_tags++) {
|
Chris@18
|
246 $term = Term::create([
|
Chris@18
|
247 'vid' => 'tags',
|
Chris@18
|
248 'name' => $random->name(),
|
Chris@18
|
249 ]);
|
Chris@18
|
250
|
Chris@18
|
251 if ($is_multilingual) {
|
Chris@18
|
252 $term->addTranslation('ca', ['name' => $term->getName() . ' (ca)']);
|
Chris@18
|
253 }
|
Chris@18
|
254
|
Chris@18
|
255 $term->save();
|
Chris@18
|
256 $this->tags[] = $term;
|
Chris@18
|
257 }
|
Chris@18
|
258 for ($created_nodes = 0; $created_nodes < $num_articles; $created_nodes++) {
|
Chris@18
|
259 $values = [
|
Chris@18
|
260 'uid' => ['target_id' => $this->user->id()],
|
Chris@18
|
261 'type' => 'article',
|
Chris@18
|
262 ];
|
Chris@18
|
263
|
Chris@18
|
264 if ($referencing_twice) {
|
Chris@18
|
265 $values['field_tags'] = [
|
Chris@18
|
266 ['target_id' => 1],
|
Chris@18
|
267 ['target_id' => 1],
|
Chris@18
|
268 ];
|
Chris@18
|
269 }
|
Chris@18
|
270 else {
|
Chris@18
|
271 // Get N random tags.
|
Chris@18
|
272 $selected_tags = mt_rand(1, $num_tags);
|
Chris@18
|
273 $tags = [];
|
Chris@18
|
274 while (count($tags) < $selected_tags) {
|
Chris@18
|
275 $tags[] = mt_rand(1, $num_tags);
|
Chris@18
|
276 $tags = array_unique($tags);
|
Chris@18
|
277 }
|
Chris@18
|
278 $values['field_tags'] = array_map(function ($tag) {
|
Chris@18
|
279 return ['target_id' => $tag];
|
Chris@18
|
280 }, $tags);
|
Chris@18
|
281 }
|
Chris@18
|
282 if ($article_has_image) {
|
Chris@18
|
283 $file = File::create([
|
Chris@18
|
284 'uri' => 'vfs://' . $random->name() . '.png',
|
Chris@18
|
285 ]);
|
Chris@18
|
286 $file->setPermanent();
|
Chris@18
|
287 $file->save();
|
Chris@18
|
288 $this->files[] = $file;
|
Chris@18
|
289 $values['field_image'] = ['target_id' => $file->id(), 'alt' => 'alt text'];
|
Chris@18
|
290 }
|
Chris@18
|
291 if ($article_has_link) {
|
Chris@18
|
292 $values['field_link'] = [
|
Chris@18
|
293 'title' => $this->getRandomGenerator()->name(),
|
Chris@18
|
294 'uri' => sprintf(
|
Chris@18
|
295 '%s://%s.%s',
|
Chris@18
|
296 'http' . (mt_rand(0, 2) > 1 ? '' : 's'),
|
Chris@18
|
297 $this->getRandomGenerator()->name(),
|
Chris@18
|
298 'org'
|
Chris@18
|
299 ),
|
Chris@18
|
300 ];
|
Chris@18
|
301 }
|
Chris@18
|
302
|
Chris@18
|
303 // Create values for the sort fields, to allow for testing complex
|
Chris@18
|
304 // sorting:
|
Chris@18
|
305 // - field_sort1 increments every 5 articles, starting at zero
|
Chris@18
|
306 // - field_sort2 decreases every article, ending at zero.
|
Chris@18
|
307 $values['field_sort1'] = ['value' => floor($created_nodes / 5)];
|
Chris@18
|
308 $values['field_sort2'] = ['value' => $num_articles - $created_nodes];
|
Chris@18
|
309
|
Chris@18
|
310 $node = $this->createNode($values);
|
Chris@18
|
311
|
Chris@18
|
312 if ($is_multilingual === static::IS_MULTILINGUAL) {
|
Chris@18
|
313 $values['title'] = $node->getTitle() . ' (ca)';
|
Chris@18
|
314 $values['field_image']['alt'] = 'alt text (ca)';
|
Chris@18
|
315 $node->addTranslation('ca', $values);
|
Chris@18
|
316 }
|
Chris@18
|
317 $node->save();
|
Chris@18
|
318
|
Chris@18
|
319 $this->nodes[] = $node;
|
Chris@18
|
320 }
|
Chris@18
|
321 if ($article_has_link) {
|
Chris@18
|
322 // Make sure that there is at least 1 https link for ::testRead() #19.
|
Chris@18
|
323 $this->nodes[0]->field_link = [
|
Chris@18
|
324 'title' => 'Drupal',
|
Chris@18
|
325 'uri' => 'https://drupal.org',
|
Chris@18
|
326 ];
|
Chris@18
|
327 $this->nodes[0]->save();
|
Chris@18
|
328 }
|
Chris@18
|
329 }
|
Chris@18
|
330
|
Chris@18
|
331 }
|