Move into nested docroot
This commit is contained in:
parent
83a0d3a149
commit
c8b70abde9
13405 changed files with 0 additions and 0 deletions
|
@ -0,0 +1,6 @@
|
|||
name: 'BigPipe regression test'
|
||||
type: module
|
||||
description: 'Support module for BigPipe regression testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,6 @@
|
|||
big_pipe_regression_test.2678662:
|
||||
path: '/big_pipe_regression_test/2678662'
|
||||
defaults:
|
||||
_controller: '\Drupal\big_pipe_regression_test\BigPipeRegressionTestController::regression2678662'
|
||||
requirements:
|
||||
_access: 'TRUE'
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\big_pipe_regression_test;
|
||||
|
||||
use Drupal\big_pipe\Render\BigPipeMarkup;
|
||||
|
||||
class BigPipeRegressionTestController {
|
||||
|
||||
const MARKER_2678662 = '<script>var hitsTheFloor = "</body>";</script>';
|
||||
|
||||
/**
|
||||
* @see \Drupal\Tests\big_pipe\FunctionalJavascript\BigPipeRegressionTest::testMultipleBodies_2678662()
|
||||
*/
|
||||
public function regression2678662() {
|
||||
return [
|
||||
'#markup' => BigPipeMarkup::create(self::MARKER_2678662),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
name: 'BigPipe test'
|
||||
type: module
|
||||
description: 'Support module for BigPipe testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Support module for BigPipe testing.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_page_top().
|
||||
*/
|
||||
function big_pipe_test_page_top(array &$page_top) {
|
||||
// Ensure this hook is invoked on every page load.
|
||||
$page_top['#cache']['max-age'] = 0;
|
||||
|
||||
if (\Drupal::request()->query->get('trigger_session')) {
|
||||
$_SESSION['big_pipe_test'] = TRUE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
big_pipe_test:
|
||||
path: '/big_pipe_test'
|
||||
defaults:
|
||||
_controller: '\Drupal\big_pipe_test\BigPipeTestController::test'
|
||||
_title: 'BigPipe test'
|
||||
requirements:
|
||||
_access: 'TRUE'
|
||||
|
||||
no_big_pipe:
|
||||
path: '/no_big_pipe'
|
||||
defaults:
|
||||
_controller: '\Drupal\big_pipe_test\BigPipeTestController::nope'
|
||||
_title: '_no_big_pipe route option test'
|
||||
options:
|
||||
_no_big_pipe: TRUE
|
||||
requirements:
|
||||
_access: 'TRUE'
|
||||
|
||||
big_pipe_test_multi_occurrence:
|
||||
path: '/big_pipe_test_multi_occurrence'
|
||||
defaults:
|
||||
_controller: '\Drupal\big_pipe_test\BigPipeTestController::multiOccurrence'
|
||||
_title: 'BigPipe test multiple occurrences of the same placeholder'
|
||||
requirements:
|
||||
_access: 'TRUE'
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
services:
|
||||
big_pipe_test_subscriber:
|
||||
class: Drupal\big_pipe_test\EventSubscriber\BigPipeTestSubscriber
|
||||
tags:
|
||||
- { name: event_subscriber }
|
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\big_pipe_test;
|
||||
|
||||
use Drupal\big_pipe\Render\BigPipeMarkup;
|
||||
use Drupal\big_pipe\Tests\BigPipePlaceholderTestCases;
|
||||
use Drupal\big_pipe_test\EventSubscriber\BigPipeTestSubscriber;
|
||||
|
||||
class BigPipeTestController {
|
||||
|
||||
/**
|
||||
* Returns a all BigPipe placeholder test case render arrays.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test() {
|
||||
$build = [];
|
||||
|
||||
$cases = BigPipePlaceholderTestCases::cases(\Drupal::getContainer());
|
||||
|
||||
// 1. HTML placeholder: status messages. Drupal renders those automatically,
|
||||
// so all that we need to do in this controller is set a message.
|
||||
drupal_set_message('Hello from BigPipe!');
|
||||
$build['html'] = $cases['html']->renderArray;
|
||||
|
||||
// 2. HTML attribute value placeholder: form action.
|
||||
$build['html_attribute_value'] = $cases['html_attribute_value']->renderArray;
|
||||
|
||||
// 3. HTML attribute value subset placeholder: CSRF token in link.
|
||||
$build['html_attribute_value_subset'] = $cases['html_attribute_value_subset']->renderArray;
|
||||
|
||||
// 4. Edge case: custom string to be considered as a placeholder that
|
||||
// happens to not be valid HTML.
|
||||
$build['edge_case__invalid_html'] = $cases['edge_case__invalid_html']->renderArray;
|
||||
|
||||
// 5. Edge case: non-#lazy_builder placeholder.
|
||||
$build['edge_case__html_non_lazy_builder'] = $cases['edge_case__html_non_lazy_builder']->renderArray;
|
||||
|
||||
// 6. Exception: #lazy_builder that throws an exception.
|
||||
$build['exception__lazy_builder'] = $cases['exception__lazy_builder']->renderArray;
|
||||
|
||||
// 7. Exception: placeholder that causes response filter to throw exception.
|
||||
$build['exception__embedded_response'] = $cases['exception__embedded_response']->renderArray;
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function nope() {
|
||||
return ['#markup' => '<p>Nope.</p>'];
|
||||
}
|
||||
|
||||
/**
|
||||
* A page with multiple occurrences of the same placeholder.
|
||||
*
|
||||
* @see \Drupal\big_pipe\Tests\BigPipeTest::testBigPipeMultipleOccurrencePlaceholders()
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiOccurrence() {
|
||||
return [
|
||||
'item1' => [
|
||||
'#lazy_builder' => [static::class . '::counter', []],
|
||||
'#create_placeholder' => TRUE,
|
||||
],
|
||||
'item2' => [
|
||||
'#lazy_builder' => [static::class . '::counter', []],
|
||||
'#create_placeholder' => TRUE,
|
||||
],
|
||||
'item3' => [
|
||||
'#lazy_builder' => [static::class . '::counter', []],
|
||||
'#create_placeholder' => TRUE,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; builds <time> markup with current time.
|
||||
*
|
||||
* Note: does not actually use current time, that would complicate testing.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function currentTime() {
|
||||
return [
|
||||
'#markup' => '<time datetime="' . date('Y-m-d', 668948400) . '"></time>',
|
||||
'#cache' => ['max-age' => 0]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; says "hello" or "yarhar".
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function helloOrYarhar() {
|
||||
return [
|
||||
'#markup' => BigPipeMarkup::create('<marquee>Yarhar llamas forever!</marquee>'),
|
||||
'#cache' => ['max-age' => 0],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; throws exception.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function exception() {
|
||||
throw new \Exception('You are not allowed to say llamas are not cool!');
|
||||
}
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; returns content that will trigger an exception.
|
||||
*
|
||||
* @see \Drupal\big_pipe_test\EventSubscriber\BigPipeTestSubscriber::onRespondTriggerException()
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function responseException() {
|
||||
return ['#plain_text' => BigPipeTestSubscriber::CONTENT_TRIGGER_EXCEPTION];
|
||||
}
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; returns the current count.
|
||||
*
|
||||
* @see \Drupal\big_pipe\Tests\BigPipeTest::testBigPipeMultipleOccurrencePlaceholders()
|
||||
*
|
||||
* @return array
|
||||
* The render array.
|
||||
*/
|
||||
public static function counter() {
|
||||
// Lazy builders are not allowed to build their own state like this function
|
||||
// does, but in this case we're intentionally doing that for testing
|
||||
// purposes: so we can ensure that each lazy builder is only ever called
|
||||
// once with the same parameters.
|
||||
static $count;
|
||||
|
||||
if (!isset($count)) {
|
||||
$count = 0;
|
||||
}
|
||||
|
||||
$count++;
|
||||
|
||||
return [
|
||||
'#markup' => BigPipeMarkup::create("<p>The count is $count.</p>"),
|
||||
'#cache' => ['max-age' => 0],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\big_pipe_test\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Render\AttachmentsInterface;
|
||||
use Drupal\Core\Render\HtmlResponse;
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class BigPipeTestSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* @see \Drupal\big_pipe_test\BigPipeTestController::responseException()
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CONTENT_TRIGGER_EXCEPTION = 'NOPE!NOPE!NOPE!';
|
||||
|
||||
/**
|
||||
* Triggers exception for embedded HTML/AJAX responses with certain content.
|
||||
*
|
||||
* @see \Drupal\big_pipe_test\BigPipeTestController::responseException()
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
|
||||
* The event to process.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function onRespondTriggerException(FilterResponseEvent $event) {
|
||||
$response = $event->getResponse();
|
||||
|
||||
if (!$response instanceof AttachmentsInterface) {
|
||||
return;
|
||||
}
|
||||
|
||||
$attachments = $response->getAttachments();
|
||||
if (!isset($attachments['big_pipe_placeholders']) && !isset($attachments['big_pipe_nojs_placeholders'])) {
|
||||
if (strpos($response->getContent(), static::CONTENT_TRIGGER_EXCEPTION) !== FALSE) {
|
||||
throw new \Exception('Oh noes!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes all BigPipe placeholders (JS and no-JS) via headers for testing.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
|
||||
* The event to process.
|
||||
*/
|
||||
public function onRespondSetBigPipeDebugPlaceholderHeaders(FilterResponseEvent $event) {
|
||||
$response = $event->getResponse();
|
||||
if (!$response instanceof HtmlResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
$attachments = $response->getAttachments();
|
||||
|
||||
$response->headers->set('BigPipe-Test-Placeholders', '<none>');
|
||||
$response->headers->set('BigPipe-Test-No-Js-Placeholders', '<none>');
|
||||
|
||||
if (!empty($attachments['big_pipe_placeholders'])) {
|
||||
$response->headers->set('BigPipe-Test-Placeholders', implode(' ', array_keys($attachments['big_pipe_placeholders'])));
|
||||
}
|
||||
|
||||
if (!empty($attachments['big_pipe_nojs_placeholders'])) {
|
||||
$response->headers->set('BigPipe-Test-No-Js-Placeholders', implode(' ', array_map('rawurlencode', array_keys($attachments['big_pipe_nojs_placeholders']))));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
// Run just before \Drupal\big_pipe\EventSubscriber\HtmlResponseBigPipeSubscriber::onRespond().
|
||||
$events[KernelEvents::RESPONSE][] = ['onRespondSetBigPipeDebugPlaceholderHeaders', -9999];
|
||||
|
||||
// Run just after \Drupal\big_pipe\EventSubscriber\HtmlResponseBigPipeSubscriber::onRespond().
|
||||
$events[KernelEvents::RESPONSE][] = ['onRespondTriggerException', -10001];
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\big_pipe_test\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
class BigPipeTestForm extends FormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'big_pipe_test_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['#token'] = FALSE;
|
||||
|
||||
$form['big_pipe'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('BigPipe works…'),
|
||||
'#options' => [
|
||||
'js' => $this->t('… with JavaScript'),
|
||||
'nojs' => $this->t('… without JavaScript'),
|
||||
],
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) { }
|
||||
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\big_pipe\FunctionalJavascript;
|
||||
|
||||
use Drupal\big_pipe\Render\BigPipe;
|
||||
use Drupal\big_pipe_regression_test\BigPipeRegressionTestController;
|
||||
use Drupal\comment\CommentInterface;
|
||||
use Drupal\comment\Entity\Comment;
|
||||
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
|
||||
use Drupal\comment\Tests\CommentTestTrait;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
use Drupal\simpletest\ContentTypeCreationTrait;
|
||||
use Drupal\simpletest\NodeCreationTrait;
|
||||
|
||||
/**
|
||||
* BigPipe regression tests.
|
||||
*
|
||||
* @group big_pipe
|
||||
*/
|
||||
class BigPipeRegressionTest extends JavascriptTestBase {
|
||||
|
||||
use CommentTestTrait;
|
||||
use ContentTypeCreationTrait;
|
||||
use NodeCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'big_pipe',
|
||||
'big_pipe_regression_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Use the big_pipe_test_theme theme.
|
||||
$this->container->get('theme_installer')->install(['big_pipe_test_theme']);
|
||||
$this->container->get('config.factory')->getEditable('system.theme')->set('default', 'big_pipe_test_theme')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure comment form works with history and big_pipe modules.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2698811
|
||||
*/
|
||||
public function testCommentForm_2698811() {
|
||||
$this->assertTrue($this->container->get('module_installer')->install(['comment', 'history', 'ckeditor'], TRUE), 'Installed modules.');
|
||||
|
||||
// Ensure an `article` node type exists.
|
||||
$this->createContentType(['type' => 'article']);
|
||||
$this->addDefaultCommentField('node', 'article');
|
||||
|
||||
// Enable CKEditor.
|
||||
$format = $this->randomMachineName();
|
||||
FilterFormat::create([
|
||||
'format' => $format,
|
||||
'name' => $this->randomString(),
|
||||
'weight' => 1,
|
||||
'filters' => [],
|
||||
])->save();
|
||||
$settings['toolbar']['rows'] = [
|
||||
[
|
||||
[
|
||||
'name' => 'Links',
|
||||
'items' => [
|
||||
'DrupalLink',
|
||||
'DrupalUnlink',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$editor = Editor::create([
|
||||
'format' => $format,
|
||||
'editor' => 'ckeditor',
|
||||
]);
|
||||
$editor->setSettings($settings);
|
||||
$editor->save();
|
||||
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'access comments',
|
||||
'post comments',
|
||||
'use text format ' . $format,
|
||||
]);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$node = $this->createNode([
|
||||
'type' => 'article',
|
||||
'comment' => CommentItemInterface::OPEN,
|
||||
]);
|
||||
// Create some comments.
|
||||
foreach (range(1, 5) as $i) {
|
||||
$comment = Comment::create([
|
||||
'status' => CommentInterface::PUBLISHED,
|
||||
'field_name' => 'comment',
|
||||
'entity_type' => 'node',
|
||||
'entity_id' => $node->id(),
|
||||
]);
|
||||
$comment->save();
|
||||
}
|
||||
$this->drupalGet($node->toUrl()->toString());
|
||||
// Confirm that CKEditor loaded.
|
||||
$javascript = <<<JS
|
||||
(function(){
|
||||
return Object.keys(CKEDITOR.instances).length > 0;
|
||||
}());
|
||||
JS;
|
||||
$this->assertJsCondition($javascript);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure BigPipe works despite inline JS containing the string "</body>".
|
||||
*
|
||||
* @see https://www.drupal.org/node/2678662
|
||||
*/
|
||||
public function testMultipleClosingBodies_2678662() {
|
||||
$this->assertTrue($this->container->get('module_installer')->install(['render_placeholder_message_test'], TRUE), 'Installed modules.');
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser());
|
||||
$this->drupalGet(Url::fromRoute('big_pipe_regression_test.2678662'));
|
||||
|
||||
// Confirm that AJAX behaviors were instantiated, if not, this points to a
|
||||
// JavaScript syntax error.
|
||||
$javascript = <<<JS
|
||||
(function(){
|
||||
return Object.keys(Drupal.ajax.instances).length > 0;
|
||||
}());
|
||||
JS;
|
||||
$this->assertJsCondition($javascript);
|
||||
|
||||
// Besides verifying there is no JavaScript syntax error, also verify the
|
||||
// HTML structure.
|
||||
$this->assertSession()
|
||||
->responseContains(BigPipe::STOP_SIGNAL . "\n\n\n</body></html>", 'The BigPipe stop signal is present just before the closing </body> and </html> tags.');
|
||||
$js_code_until_closing_body_tag = substr(BigPipeRegressionTestController::MARKER_2678662, 0, strpos(BigPipeRegressionTestController::MARKER_2678662, '</body>'));
|
||||
$this->assertSession()
|
||||
->responseNotContains($js_code_until_closing_body_tag . "\n" . BigPipe::START_SIGNAL, 'The BigPipe start signal does NOT start at the closing </body> tag string in an inline script.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure messages set in placeholders always appear.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2712935
|
||||
*/
|
||||
public function testMessages_2712935() {
|
||||
$this->assertTrue($this->container->get('module_installer')->install(['render_placeholder_message_test'], TRUE), 'Installed modules.');
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser());
|
||||
$messages_markup = '<div role="contentinfo" aria-label="Status message"';
|
||||
|
||||
$test_routes = [
|
||||
// Messages placeholder rendered first.
|
||||
'render_placeholder_message_test.first',
|
||||
// Messages placeholder rendered after one, before another.
|
||||
'render_placeholder_message_test.middle',
|
||||
// Messages placeholder rendered last.
|
||||
'render_placeholder_message_test.last',
|
||||
];
|
||||
|
||||
$assert = $this->assertSession();
|
||||
foreach ($test_routes as $route) {
|
||||
// Verify that we start off with zero messages queued.
|
||||
$this->drupalGet(Url::fromRoute('render_placeholder_message_test.queued'));
|
||||
$assert->responseNotContains($messages_markup);
|
||||
|
||||
// Verify the test case at this route behaves as expected.
|
||||
$this->drupalGet(Url::fromRoute($route));
|
||||
$assert->elementContains('css', 'p.logged-message:nth-of-type(1)', 'Message: P1');
|
||||
$assert->elementContains('css', 'p.logged-message:nth-of-type(2)', 'Message: P2');
|
||||
$assert->responseContains($messages_markup);
|
||||
$assert->elementExists('css', 'div[aria-label="Status message"] ul');
|
||||
$assert->elementContains('css', 'div[aria-label="Status message"] ul li:nth-of-type(1)', 'P1');
|
||||
$assert->elementContains('css', 'div[aria-label="Status message"] ul li:nth-of-type(2)', 'P2');
|
||||
|
||||
// Verify that we end with all messages printed, hence again zero queued.
|
||||
$this->drupalGet(Url::fromRoute('render_placeholder_message_test.queued'));
|
||||
$assert->responseNotContains($messages_markup);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\big_pipe\Unit\Render;
|
||||
|
||||
use Drupal\big_pipe\Render\BigPipeResponse;
|
||||
use Drupal\big_pipe\Render\BigPipeResponseAttachmentsProcessor;
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Asset\AssetCollectionRendererInterface;
|
||||
use Drupal\Core\Asset\AssetResolverInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Render\AttachmentsInterface;
|
||||
use Drupal\Core\Render\AttachmentsResponseProcessorInterface;
|
||||
use Drupal\Core\Render\HtmlResponse;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\big_pipe\Render\BigPipeResponseAttachmentsProcessor
|
||||
* @group big_pipe
|
||||
*/
|
||||
class BigPipeResponseAttachmentsProcessorTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::processAttachments
|
||||
*
|
||||
* @dataProvider nonHtmlResponseProvider
|
||||
*
|
||||
* @expectedException \AssertionError
|
||||
*/
|
||||
public function testNonHtmlResponse($response_class) {
|
||||
$big_pipe_response_attachments_processor = $this->createBigPipeResponseAttachmentsProcessor($this->prophesize(AttachmentsResponseProcessorInterface::class));
|
||||
|
||||
$non_html_response = new $response_class();
|
||||
$big_pipe_response_attachments_processor->processAttachments($non_html_response);
|
||||
}
|
||||
|
||||
function nonHtmlResponseProvider() {
|
||||
return [
|
||||
'AjaxResponse, which implements AttachmentsInterface' => [AjaxResponse::class],
|
||||
'A dummy that implements AttachmentsInterface' => [get_class($this->prophesize(AttachmentsInterface::class)->reveal())],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::processAttachments
|
||||
*
|
||||
* @dataProvider attachmentsProvider
|
||||
*/
|
||||
public function testHtmlResponse(array $attachments) {
|
||||
$big_pipe_response = new BigPipeResponse('original');
|
||||
$big_pipe_response->setAttachments($attachments);
|
||||
|
||||
// This mock is the main expectation of this test: verify that the decorated
|
||||
// service (that is this mock) never receives BigPipe placeholder
|
||||
// attachments, because it doesn't know (nor should it) how to handle them.
|
||||
$html_response_attachments_processor = $this->prophesize(AttachmentsResponseProcessorInterface::class);
|
||||
$html_response_attachments_processor->processAttachments(Argument::that(function ($response) {
|
||||
return $response instanceof HtmlResponse && empty(array_intersect(['big_pipe_placeholders', 'big_pipe_nojs_placeholders'], array_keys($response->getAttachments())));
|
||||
}))
|
||||
->will(function ($args) {
|
||||
/** @var \Symfony\Component\HttpFoundation\Response|\Drupal\Core\Render\AttachmentsInterface $response */
|
||||
$response = $args[0];
|
||||
// Simulate its actual behavior.
|
||||
$attachments = array_diff_key($response->getAttachments(), ['html_response_attachment_placeholders' => TRUE]);
|
||||
$response->setContent('processed');
|
||||
$response->setAttachments($attachments);
|
||||
return $response;
|
||||
})
|
||||
->shouldBeCalled();
|
||||
|
||||
$big_pipe_response_attachments_processor = $this->createBigPipeResponseAttachmentsProcessor($html_response_attachments_processor);
|
||||
$processed_big_pipe_response = $big_pipe_response_attachments_processor->processAttachments($big_pipe_response);
|
||||
|
||||
// The secondary expectation of this test: the original (passed in) response
|
||||
// object remains unchanged, the processed (returned) response object has
|
||||
// the expected values.
|
||||
$this->assertSame($attachments, $big_pipe_response->getAttachments(), 'Attachments of original response object MUST NOT be changed.');
|
||||
$this->assertEquals('original', $big_pipe_response->getContent(), 'Content of original response object MUST NOT be changed.');
|
||||
$this->assertEquals(array_diff_key($attachments, ['html_response_attachment_placeholders' => TRUE]), $processed_big_pipe_response->getAttachments(), 'Attachments of returned (processed) response object MUST be changed.');
|
||||
$this->assertEquals('processed', $processed_big_pipe_response->getContent(), 'Content of returned (processed) response object MUST be changed.');
|
||||
}
|
||||
|
||||
public function attachmentsProvider() {
|
||||
$typical_cases = [
|
||||
'no attachments' => [[]],
|
||||
'libraries' => [['library' => ['core/drupal']]],
|
||||
'libraries + drupalSettings' => [['library' => ['core/drupal'], 'drupalSettings' => ['foo' => 'bar']]],
|
||||
];
|
||||
|
||||
$official_attachment_types = ['html_head', 'feed', 'html_head_link', 'http_header', 'library', 'placeholders', 'drupalSettings', 'html_response_attachment_placeholders'];
|
||||
$official_attachments_with_random_values = [];
|
||||
foreach ($official_attachment_types as $type) {
|
||||
$official_attachments_with_random_values[$type] = $this->randomMachineName();
|
||||
}
|
||||
$random_attachments = ['random' . $this->randomMachineName() => $this->randomMachineName()];
|
||||
$edge_cases = [
|
||||
'all official attachment types, with random assigned values, even if technically not valid, to prove BigPipeResponseAttachmentsProcessor is a perfect decorator' => [$official_attachments_with_random_values],
|
||||
'random attachment type (unofficial), with random assigned value, to prove BigPipeResponseAttachmentsProcessor is a perfect decorator' => [$random_attachments],
|
||||
];
|
||||
|
||||
$big_pipe_placeholder_attachments = ['big_pipe_placeholders' => $this->randomMachineName()];
|
||||
$big_pipe_nojs_placeholder_attachments = ['big_pipe_nojs_placeholders' => $this->randomMachineName()];
|
||||
$big_pipe_cases = [
|
||||
'only big_pipe_placeholders' => [$big_pipe_placeholder_attachments],
|
||||
'only big_pipe_nojs_placeholders' => [$big_pipe_nojs_placeholder_attachments],
|
||||
'big_pipe_placeholders + big_pipe_nojs_placeholders' => [$big_pipe_placeholder_attachments + $big_pipe_nojs_placeholder_attachments],
|
||||
];
|
||||
|
||||
$combined_cases = [
|
||||
'all official attachment types + big_pipe_placeholders + big_pipe_nojs_placeholders' => [$official_attachments_with_random_values + $big_pipe_placeholder_attachments + $big_pipe_nojs_placeholder_attachments],
|
||||
'random attachment types + big_pipe_placeholders + big_pipe_nojs_placeholders' => [$random_attachments + $big_pipe_placeholder_attachments + $big_pipe_nojs_placeholder_attachments],
|
||||
];
|
||||
|
||||
return $typical_cases + $edge_cases + $big_pipe_cases + $combined_cases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BigPipeResponseAttachmentsProcessor with mostly dummies.
|
||||
*
|
||||
* @param \Prophecy\Prophecy\ObjectProphecy $decorated_html_response_attachments_processor
|
||||
* An object prophecy implementing AttachmentsResponseProcessorInterface.
|
||||
*
|
||||
* @return \Drupal\big_pipe\Render\BigPipeResponseAttachmentsProcessor
|
||||
* The BigPipeResponseAttachmentsProcessor to test.
|
||||
*/
|
||||
protected function createBigPipeResponseAttachmentsProcessor(ObjectProphecy $decorated_html_response_attachments_processor) {
|
||||
return new BigPipeResponseAttachmentsProcessor(
|
||||
$decorated_html_response_attachments_processor->reveal(),
|
||||
$this->prophesize(AssetResolverInterface::class)->reveal(),
|
||||
$this->prophesize(ConfigFactoryInterface::class)->reveal(),
|
||||
$this->prophesize(AssetCollectionRendererInterface::class)->reveal(),
|
||||
$this->prophesize(AssetCollectionRendererInterface::class)->reveal(),
|
||||
$this->prophesize(RequestStack::class)->reveal(),
|
||||
$this->prophesize(RendererInterface::class)->reveal(),
|
||||
$this->prophesize(ModuleHandlerInterface::class)->reveal()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\big_pipe\Unit\Render\Placeholder;
|
||||
|
||||
use Drupal\big_pipe\Render\Placeholder\BigPipeStrategy;
|
||||
use Drupal\big_pipe\Tests\BigPipePlaceholderTestCases;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Session\SessionConfigurationInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Argument;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\big_pipe\Render\Placeholder\BigPipeStrategy
|
||||
* @group big_pipe
|
||||
*/
|
||||
class BigPipeStrategyTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::processPlaceholders
|
||||
*
|
||||
* @dataProvider placeholdersProvider
|
||||
*/
|
||||
public function testProcessPlaceholders(array $placeholders, $method, $route_match_has_no_big_pipe_option, $request_has_session, $request_has_big_pipe_nojs_cookie, array $expected_big_pipe_placeholders) {
|
||||
$request = new Request();
|
||||
$request->setMethod($method);
|
||||
if ($request_has_big_pipe_nojs_cookie) {
|
||||
$request->cookies->set(BigPipeStrategy::NOJS_COOKIE, 1);
|
||||
}
|
||||
$request_stack = $this->prophesize(RequestStack::class);
|
||||
$request_stack->getCurrentRequest()
|
||||
->willReturn($request);
|
||||
|
||||
$session_configuration = $this->prophesize(SessionConfigurationInterface::class);
|
||||
$session_configuration->hasSession(Argument::type(Request::class))
|
||||
->willReturn($request_has_session);
|
||||
|
||||
$route = $this->prophesize(Route::class);
|
||||
$route->getOption('_no_big_pipe')
|
||||
->willReturn($route_match_has_no_big_pipe_option);
|
||||
$route_match = $this->prophesize(RouteMatchInterface::class);
|
||||
$route_match->getRouteObject()
|
||||
->willReturn($route);
|
||||
|
||||
$big_pipe_strategy = new BigPipeStrategy($session_configuration->reveal(), $request_stack->reveal(), $route_match->reveal());
|
||||
$processed_placeholders = $big_pipe_strategy->processPlaceholders($placeholders);
|
||||
|
||||
if ($request->isMethodSafe() && !$route_match_has_no_big_pipe_option && $request_has_session) {
|
||||
$this->assertSameSize($expected_big_pipe_placeholders, $processed_placeholders, 'BigPipe is able to deliver all placeholders.');
|
||||
foreach (array_keys($placeholders) as $placeholder) {
|
||||
$this->assertSame($expected_big_pipe_placeholders[$placeholder], $processed_placeholders[$placeholder], "Verifying how BigPipeStrategy handles the placeholder '$placeholder'");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->assertSame(0, count($processed_placeholders));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \Drupal\big_pipe\Tests\BigPipePlaceholderTestCases
|
||||
*/
|
||||
public function placeholdersProvider() {
|
||||
$cases = BigPipePlaceholderTestCases::cases();
|
||||
|
||||
// Generate $placeholders variable as expected by
|
||||
// \Drupal\Core\Render\Placeholder\PlaceholderStrategyInterface::processPlaceholders().
|
||||
$placeholders = [
|
||||
$cases['html']->placeholder => $cases['html']->placeholderRenderArray,
|
||||
$cases['html_attribute_value']->placeholder => $cases['html_attribute_value']->placeholderRenderArray,
|
||||
$cases['html_attribute_value_subset']->placeholder => $cases['html_attribute_value_subset']->placeholderRenderArray,
|
||||
$cases['edge_case__invalid_html']->placeholder => $cases['edge_case__invalid_html']->placeholderRenderArray,
|
||||
$cases['edge_case__html_non_lazy_builder']->placeholder => $cases['edge_case__html_non_lazy_builder']->placeholderRenderArray,
|
||||
$cases['exception__lazy_builder']->placeholder => $cases['exception__lazy_builder']->placeholderRenderArray,
|
||||
$cases['exception__embedded_response']->placeholder => $cases['exception__embedded_response']->placeholderRenderArray,
|
||||
];
|
||||
|
||||
return [
|
||||
'_no_big_pipe absent, no session, no-JS cookie absent' => [$placeholders, 'GET', FALSE, FALSE, FALSE, []],
|
||||
'_no_big_pipe absent, no session, no-JS cookie present' => [$placeholders, 'GET', FALSE, FALSE, TRUE, []],
|
||||
'_no_big_pipe present, no session, no-JS cookie absent' => [$placeholders, 'GET', TRUE, FALSE, FALSE, []],
|
||||
'_no_big_pipe present, no session, no-JS cookie present' => [$placeholders, 'GET', TRUE, FALSE, TRUE, []],
|
||||
'_no_big_pipe present, session, no-JS cookie absent' => [$placeholders, 'GET', TRUE, TRUE, FALSE, []],
|
||||
'_no_big_pipe present, session, no-JS cookie present' => [$placeholders, 'GET', TRUE, TRUE, TRUE, []],
|
||||
'_no_big_pipe absent, session, no-JS cookie absent: (JS-powered) BigPipe placeholder used for HTML placeholders' => [$placeholders, 'GET', FALSE, TRUE, FALSE, [
|
||||
$cases['html']->placeholder => $cases['html']->bigPipePlaceholderRenderArray,
|
||||
$cases['html_attribute_value']->placeholder => $cases['html_attribute_value']->bigPipeNoJsPlaceholderRenderArray,
|
||||
$cases['html_attribute_value_subset']->placeholder => $cases['html_attribute_value_subset']->bigPipeNoJsPlaceholderRenderArray,
|
||||
$cases['edge_case__invalid_html']->placeholder => $cases['edge_case__invalid_html']->bigPipeNoJsPlaceholderRenderArray,
|
||||
$cases['edge_case__html_non_lazy_builder']->placeholder => $cases['edge_case__html_non_lazy_builder']->bigPipePlaceholderRenderArray,
|
||||
$cases['exception__lazy_builder']->placeholder => $cases['exception__lazy_builder']->bigPipePlaceholderRenderArray,
|
||||
$cases['exception__embedded_response']->placeholder => $cases['exception__embedded_response']->bigPipePlaceholderRenderArray,
|
||||
]],
|
||||
'_no_big_pipe absent, session, no-JS cookie absent: (JS-powered) BigPipe placeholder used for HTML placeholders — but unsafe method' => [$placeholders, 'POST', FALSE, TRUE, FALSE, []],
|
||||
'_no_big_pipe absent, session, no-JS cookie present: no-JS BigPipe placeholder used for HTML placeholders' => [$placeholders, 'GET', FALSE, TRUE, TRUE, [
|
||||
$cases['html']->placeholder => $cases['html']->bigPipeNoJsPlaceholderRenderArray,
|
||||
$cases['html_attribute_value']->placeholder => $cases['html_attribute_value']->bigPipeNoJsPlaceholderRenderArray,
|
||||
$cases['html_attribute_value_subset']->placeholder => $cases['html_attribute_value_subset']->bigPipeNoJsPlaceholderRenderArray,
|
||||
$cases['edge_case__invalid_html']->placeholder => $cases['edge_case__invalid_html']->bigPipeNoJsPlaceholderRenderArray,
|
||||
$cases['edge_case__html_non_lazy_builder']->placeholder => $cases['edge_case__html_non_lazy_builder']->bigPipeNoJsPlaceholderRenderArray,
|
||||
$cases['exception__lazy_builder']->placeholder => $cases['exception__lazy_builder']->bigPipeNoJsPlaceholderRenderArray,
|
||||
$cases['exception__embedded_response']->placeholder => $cases['exception__embedded_response']->bigPipeNoJsPlaceholderRenderArray,
|
||||
]],
|
||||
'_no_big_pipe absent, session, no-JS cookie present: no-JS BigPipe placeholder used for HTML placeholders — but unsafe method' => [$placeholders, 'POST', FALSE, TRUE, TRUE, []],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
name: 'BigPipe test theme'
|
||||
type: theme
|
||||
description: 'Theme for testing BigPipe edge cases.'
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,13 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Test that comments still work with the form above instead of below.
|
||||
*
|
||||
* @see \Drupal\Tests\big_pipe\FunctionalJavascript\BigPipeRegressionTest::testCommentForm_2698811()
|
||||
*/
|
||||
#}
|
||||
<section{{ attributes }}>
|
||||
{{ comment_form }}
|
||||
|
||||
{{ comments }}
|
||||
</section>
|
Reference in a new issue