Update Composer, update everything

This commit is contained in:
Oliver Davies 2018-11-23 12:29:20 +00:00
parent ea3e94409f
commit dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions

View file

@ -0,0 +1,133 @@
/**
* @file
* Renders BigPipe placeholders using Drupal's Ajax system.
*/
(function($, Drupal, drupalSettings) {
/**
* Maps textContent of <script type="application/vnd.drupal-ajax"> to an AJAX response.
*
* @param {string} content
* The text content of a <script type="application/vnd.drupal-ajax"> DOM node.
* @return {Array|boolean}
* The parsed Ajax response containing an array of Ajax commands, or false in
* case the DOM node hasn't fully arrived yet.
*/
function mapTextContentToAjaxResponse(content) {
if (content === '') {
return false;
}
try {
return JSON.parse(content);
} catch (e) {
return false;
}
}
/**
* Executes Ajax commands in <script type="application/vnd.drupal-ajax"> tag.
*
* These Ajax commands replace placeholders with HTML and load missing CSS/JS.
*
* @param {number} index
* Current index.
* @param {HTMLScriptElement} placeholderReplacement
* Script tag created by BigPipe.
*/
function bigPipeProcessPlaceholderReplacement(index, placeholderReplacement) {
const placeholderId = placeholderReplacement.getAttribute(
'data-big-pipe-replacement-for-placeholder-with-id',
);
const content = this.textContent.trim();
// Ignore any placeholders that are not in the known placeholder list. Used
// to avoid someone trying to XSS the site via the placeholdering mechanism.
if (
typeof drupalSettings.bigPipePlaceholderIds[placeholderId] !== 'undefined'
) {
const response = mapTextContentToAjaxResponse(content);
// If we try to parse the content too early (when the JSON containing Ajax
// commands is still arriving), textContent will be empty or incomplete.
if (response === false) {
/**
* Mark as unprocessed so this will be retried later.
* @see bigPipeProcessDocument()
*/
$(this).removeOnce('big-pipe');
} else {
// Create a Drupal.Ajax object without associating an element, a
// progress indicator or a URL.
const ajaxObject = Drupal.ajax({
url: '',
base: false,
element: false,
progress: false,
});
// Then, simulate an AJAX response having arrived, and let the Ajax
// system handle it.
ajaxObject.success(response, 'success');
}
}
}
// The frequency with which to check for newly arrived BigPipe placeholders.
// Hence 50 ms means we check 20 times per second. Setting this to 100 ms or
// more would cause the user to see content appear noticeably slower.
const interval = drupalSettings.bigPipeInterval || 50;
// The internal ID to contain the watcher service.
let timeoutID;
/**
* Processes a streamed HTML document receiving placeholder replacements.
*
* @param {HTMLDocument} context
* The HTML document containing <script type="application/vnd.drupal-ajax">
* tags generated by BigPipe.
*
* @return {bool}
* Returns true when processing has been finished and a stop signal has been
* found.
*/
function bigPipeProcessDocument(context) {
// Make sure we have BigPipe-related scripts before processing further.
if (!context.querySelector('script[data-big-pipe-event="start"]')) {
return false;
}
$(context)
.find('script[data-big-pipe-replacement-for-placeholder-with-id]')
.once('big-pipe')
.each(bigPipeProcessPlaceholderReplacement);
// If we see the stop signal, clear the timeout: all placeholder
// replacements are guaranteed to be received and processed.
if (context.querySelector('script[data-big-pipe-event="stop"]')) {
if (timeoutID) {
clearTimeout(timeoutID);
}
return true;
}
return false;
}
function bigPipeProcess() {
timeoutID = setTimeout(() => {
if (!bigPipeProcessDocument(document)) {
bigPipeProcess();
}
}, interval);
}
bigPipeProcess();
// If something goes wrong, make sure everything is cleaned up and has had a
// chance to be processed with everything loaded.
$(window).on('load', () => {
if (timeoutID) {
clearTimeout(timeoutID);
}
bigPipeProcessDocument(document);
});
})(jQuery, Drupal, drupalSettings);

View file

@ -1,76 +1,56 @@
/**
* @file
* Renders BigPipe placeholders using Drupal's Ajax system.
*/
* DO NOT EDIT THIS FILE.
* See the following change record for more information,
* https://www.drupal.org/node/2815083
* @preserve
**/
(function ($, Drupal, drupalSettings) {
function mapTextContentToAjaxResponse(content) {
if (content === '') {
return false;
}
'use strict';
try {
return JSON.parse(content);
} catch (e) {
return false;
}
}
/**
* Executes Ajax commands in <script type="application/vnd.drupal-ajax"> tag.
*
* These Ajax commands replace placeholders with HTML and load missing CSS/JS.
*
* @param {number} index
* Current index.
* @param {HTMLScriptElement} placeholderReplacement
* Script tag created by BigPipe.
*/
function bigPipeProcessPlaceholderReplacement(index, placeholderReplacement) {
var placeholderId = placeholderReplacement.getAttribute('data-big-pipe-replacement-for-placeholder-with-id');
var content = this.textContent.trim();
// Ignore any placeholders that are not in the known placeholder list. Used
// to avoid someone trying to XSS the site via the placeholdering mechanism.
if (typeof drupalSettings.bigPipePlaceholderIds[placeholderId] !== 'undefined') {
// If we try to parse the content too early (when the JSON containing Ajax
// commands is still arriving), textContent will be empty which will cause
// JSON.parse() to fail. Remove once so that it can be processed again
// later.
// @see bigPipeProcessDocument()
if (content === '') {
var response = mapTextContentToAjaxResponse(content);
if (response === false) {
$(this).removeOnce('big-pipe');
}
else {
var response = JSON.parse(content);
// Create a Drupal.Ajax object without associating an element, a
// progress indicator or a URL.
} else {
var ajaxObject = Drupal.ajax({
url: '',
base: false,
element: false,
progress: false
});
// Then, simulate an AJAX response having arrived, and let the Ajax
// system handle it.
ajaxObject.success(response, 'success');
}
}
}
/**
* Processes a streamed HTML document receiving placeholder replacements.
*
* @param {HTMLDocument} context
* The HTML document containing <script type="application/vnd.drupal-ajax">
* tags generated by BigPipe.
*
* @return {bool}
* Returns true when processing has been finished and a stop signal has been
* found.
*/
var interval = drupalSettings.bigPipeInterval || 50;
var timeoutID = void 0;
function bigPipeProcessDocument(context) {
// Make sure we have BigPipe-related scripts before processing further.
if (!context.querySelector('script[data-big-pipe-event="start"]')) {
return false;
}
$(context).find('script[data-big-pipe-replacement-for-placeholder-with-id]')
.once('big-pipe')
.each(bigPipeProcessPlaceholderReplacement);
$(context).find('script[data-big-pipe-replacement-for-placeholder-with-id]').once('big-pipe').each(bigPipeProcessPlaceholderReplacement);
// If we see the stop signal, clear the timeout: all placeholder
// replacements are guaranteed to be received and processed.
if (context.querySelector('script[data-big-pipe-event="stop"]')) {
if (timeoutID) {
clearTimeout(timeoutID);
@ -89,22 +69,12 @@
}, interval);
}
// The frequency with which to check for newly arrived BigPipe placeholders.
// Hence 50 ms means we check 20 times per second. Setting this to 100 ms or
// more would cause the user to see content appear noticeably slower.
var interval = drupalSettings.bigPipeInterval || 50;
// The internal ID to contain the watcher service.
var timeoutID;
bigPipeProcess();
// If something goes wrong, make sure everything is cleaned up and has had a
// chance to be processed with everything loaded.
$(window).on('load', function () {
if (timeoutID) {
clearTimeout(timeoutID);
}
bigPipeProcessDocument(document);
});
})(jQuery, Drupal, drupalSettings);
})(jQuery, Drupal, drupalSettings);

View file

@ -420,7 +420,7 @@ class BigPipe {
}
$placeholder = $fragment;
assert('isset($no_js_placeholders[$placeholder])');
assert(isset($no_js_placeholders[$placeholder]));
$token = Crypt::randomBytesBase64(55);
// Render the placeholder, but include the cumulative settings assets, so
@ -446,7 +446,6 @@ class BigPipe {
}
}
// Create a new HtmlResponse. Ensure the CSS and (non-bottom) JS is sent
// before the HTML they're associated with. In other words: ensure the
// critical assets for this placeholder's markup are loaded first.
@ -483,7 +482,6 @@ class BigPipe {
}
}
// Send this embedded HTML response.
$this->sendChunk($html_response);
@ -631,7 +629,7 @@ EOF;
* AJAX page state.
*/
protected function filterEmbeddedResponse(Request $fake_request, Response $embedded_response) {
assert('$embedded_response instanceof \Drupal\Core\Render\HtmlResponse || $embedded_response instanceof \Drupal\Core\Ajax\AjaxResponse');
assert($embedded_response instanceof HtmlResponse || $embedded_response instanceof AjaxResponse);
return $this->filterResponse($fake_request, HttpKernelInterface::SUB_REQUEST, $embedded_response);
}
@ -651,7 +649,7 @@ EOF;
* The filtered response.
*/
protected function filterResponse(Request $request, $request_type, Response $response) {
assert('$request_type === \Symfony\Component\HttpKernel\HttpKernelInterface::MASTER_REQUEST || $request_type === \Symfony\Component\HttpKernel\HttpKernelInterface::SUB_REQUEST');
assert($request_type === HttpKernelInterface::MASTER_REQUEST || $request_type === HttpKernelInterface::SUB_REQUEST);
$this->requestStack->push($request);
$event = new FilterResponseEvent($this->httpKernel, $request, $request_type, $response);
$this->eventDispatcher->dispatch(KernelEvents::RESPONSE, $event);
@ -732,8 +730,8 @@ EOF;
// being rendered: any code can add messages to render.
// This violates the principle that each lazy builder must be able to render
// itself in isolation, and therefore in any order. However, we cannot
// change the way drupal_set_message() works in the Drupal 8 cycle. So we
// have to accommodate its special needs.
// change the way \Drupal\Core\Messenger\MessengerInterface::addMessage()
// works in the Drupal 8 cycle. So we have to accommodate its special needs.
// Allowing placeholders to be rendered in a particular order (in this case:
// last) would violate this isolation principle. Thus a monopoly is granted
// to this one special case, with this hard-coded solution.

View file

@ -9,6 +9,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\EnforcedResponseException;
use Drupal\Core\Render\AttachmentsInterface;
use Drupal\Core\Render\AttachmentsResponseProcessorInterface;
use Drupal\Core\Render\HtmlResponse;
use Drupal\Core\Render\HtmlResponseAttachmentsProcessor;
use Drupal\Core\Render\RendererInterface;
use Symfony\Component\HttpFoundation\RequestStack;
@ -57,7 +58,7 @@ class BigPipeResponseAttachmentsProcessor extends HtmlResponseAttachmentsProcess
* {@inheritdoc}
*/
public function processAttachments(AttachmentsInterface $response) {
assert('$response instanceof \Drupal\Core\Render\HtmlResponse');
assert($response instanceof HtmlResponse);
// First, render the actual placeholders; this will cause the BigPipe
// placeholder strategy to generate BigPipe placeholders. We need those to

View file

@ -109,7 +109,7 @@ class BigPipeStrategy implements PlaceholderStrategyInterface {
$request = $this->requestStack->getCurrentRequest();
// @todo remove this check when https://www.drupal.org/node/2367555 lands.
if (!$request->isMethodSafe()) {
if (!$request->isMethodCacheable()) {
return [];
}
@ -180,7 +180,7 @@ class BigPipeStrategy implements PlaceholderStrategyInterface {
* a placeholder for a HTML attribute value or a subset of it).
*/
protected static function placeholderIsAttributeSafe($placeholder) {
assert('is_string($placeholder)');
assert(is_string($placeholder));
return $placeholder[0] !== '<' || $placeholder !== Html::normalize($placeholder);
}

View file

@ -39,7 +39,7 @@ class BigPipeRegressionTestController {
public static function currentTime() {
return [
'#markup' => '<time datetime="' . date('Y-m-d', time()) . '"></time>',
'#cache' => ['max-age' => 0]
'#cache' => ['max-age' => 0],
];
}

View file

@ -23,4 +23,3 @@ big_pipe_test_multi_occurrence:
_title: 'BigPipe test multiple occurrences of the same placeholder'
requirements:
_access: 'TRUE'

View file

@ -2,14 +2,12 @@
/**
* @file
* Contains \Drupal\Tests\big_pipe\Unit\Render\Placeholder\BigPipePlaceholderTestCases.
*/
namespace Drupal\big_pipe\Tests;
namespace Drupal\big_pipe_test;
use Drupal\big_pipe\Render\BigPipeMarkup;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
@ -19,9 +17,9 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* - Unit test:
* \Drupal\Tests\big_pipe\Unit\Render\Placeholder\BigPipeStrategyTest
* - Integration test for BigPipe with JS on:
* \Drupal\big_pipe\Tests\BigPipeTest::testBigPipe()
* \Drupal\Tests\big_pipe\Functional\BigPipeTest::testBigPipe()
* - Integration test for BigPipe with JS off:
* \Drupal\big_pipe\Tests\BigPipeTest::testBigPipeNoJs()
* \Drupal\Tests\big_pipe\Functional\BigPipeTest::testBigPipeNoJs()
*/
class BigPipePlaceholderTestCases {
@ -33,7 +31,7 @@ class BigPipePlaceholderTestCases {
* @param \Drupal\Core\Session\AccountInterface|null $user
* Optional. Necessary to get the embedded AJAX/HTML responses.
*
* @return \Drupal\big_pipe\Tests\BigPipePlaceholderTestCase[]
* @return \Drupal\big_pipe_test\BigPipePlaceholderTestCase[]
*/
public static function cases(ContainerInterface $container = NULL, AccountInterface $user = NULL) {
// Define the two types of cacheability that we expect to see. These will be
@ -47,7 +45,6 @@ class BigPipePlaceholderTestCases {
'contexts' => ['session.exists', 'cookies:big_pipe_nojs'],
];
// 1. Real-world example of HTML placeholder.
$status_messages = new BigPipePlaceholderTestCase(
['#type' => 'status_messages'],
@ -55,7 +52,7 @@ class BigPipePlaceholderTestCases {
[
'#lazy_builder' => [
'Drupal\Core\Render\Element\StatusMessages::renderMessages',
[NULL]
[NULL],
],
]
);
@ -87,37 +84,17 @@ class BigPipePlaceholderTestCases {
];
if ($container && $user) {
$status_messages->embeddedAjaxResponseCommands = [
[
'command' => 'settings',
'settings' => [
'ajaxPageState' => [
'theme' => 'classy',
'libraries' => 'big_pipe/big_pipe,classy/base,classy/messages,core/drupal.active-link,core/html5shiv,core/normalize,system/base',
],
'pluralDelimiter' => PluralTranslatableMarkup::DELIMITER,
'user' => [
'uid' => '1',
'permissionsHash' => $container->get('user_permissions_hash_generator')->generate($user),
],
],
'merge' => TRUE,
],
[
'command' => 'add_css',
'data' => '<link rel="stylesheet" href="' . base_path() . 'core/themes/classy/css/components/messages.css?' . $container->get('state')->get('system.css_js_query_string') . '" media="all" />' . "\n"
],
[
'command' => 'insert',
'method' => 'replaceWith',
'selector' => '[data-big-pipe-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&args%5B0%5D&token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"]',
'data' => "\n" . ' <div role="contentinfo" aria-label="Status message" class="messages messages--status">' . "\n" . ' <h2 class="visually-hidden">Status message</h2>' . "\n" . ' Hello from BigPipe!' . "\n" . ' </div>' . "\n ",
'data' => ' <div role="contentinfo" aria-label="Status message" class="messages messages--status">' . "\n" . ' <h2 class="visually-hidden">Status message</h2>' . "\n" . ' Hello from BigPipe!' . "\n" . ' </div>' . "\n ",
'settings' => NULL,
],
];
$status_messages->embeddedHtmlResponse = '<link rel="stylesheet" href="' . base_path() . 'core/themes/classy/css/components/messages.css?' . $container->get('state')->get('system.css_js_query_string') . '" media="all" />' . "\n" . "\n" . ' <div role="contentinfo" aria-label="Status message" class="messages messages--status">' . "\n" . ' <h2 class="visually-hidden">Status message</h2>' . "\n" . ' Hello from BigPipe!' . "\n" . ' </div>' . "\n \n";
$status_messages->embeddedHtmlResponse = '<div role="contentinfo" aria-label="Status message" class="messages messages--status">' . "\n" . ' <h2 class="visually-hidden">Status message</h2>' . "\n" . ' Hello from BigPipe!' . "\n" . ' </div>' . "\n \n";
}
// 2. Real-world example of HTML attribute value placeholder: form action.
$form_action = new BigPipePlaceholderTestCase(
$container ? $container->get('form_builder')->getForm('Drupal\big_pipe_test\Form\BigPipeTestForm') : [],
@ -140,7 +117,6 @@ class BigPipePlaceholderTestCases {
$form_action->embeddedHtmlResponse = '<form class="big-pipe-test-form" data-drupal-selector="big-pipe-test-form" action="' . base_path() . 'big_pipe_test"';
}
// 3. Real-world example of HTML attribute value subset placeholder: CSRF
// token in link.
$csrf_token = new BigPipePlaceholderTestCase(
@ -153,7 +129,7 @@ class BigPipePlaceholderTestCases {
[
'#lazy_builder' => [
'route_processor_csrf:renderPlaceholderCsrfToken',
['admin/config/user-interface/shortcut/manage/default/add-link-inline']
['admin/config/user-interface/shortcut/manage/default/add-link-inline'],
],
]
);
@ -171,7 +147,6 @@ class BigPipePlaceholderTestCases {
$csrf_token->embeddedHtmlResponse = $container->get('csrf_token')->get('admin/appearance/default');
}
// 4. Edge case: custom string to be considered as a placeholder that
// happens to not be valid HTML.
$hello = new BigPipePlaceholderTestCase(
@ -180,14 +155,14 @@ class BigPipePlaceholderTestCases {
'#attached' => [
'placeholders' => [
'<hello' => ['#lazy_builder' => ['\Drupal\big_pipe_test\BigPipeTestController::helloOrYarhar', []]],
]
],
],
],
'<hello',
[
'#lazy_builder' => [
'hello_or_yarhar',
[]
[],
],
]
);
@ -203,7 +178,6 @@ class BigPipePlaceholderTestCases {
];
$hello->embeddedHtmlResponse = '<marquee>Yarhar llamas forever!</marquee>';
// 5. Edge case: non-#lazy_builder placeholder.
$current_time = new BigPipePlaceholderTestCase(
[
@ -214,9 +188,9 @@ class BigPipePlaceholderTestCases {
'#pre_render' => [
'\Drupal\big_pipe_test\BigPipeTestController::currentTime',
],
]
]
]
],
],
],
],
'<time>CURRENT TIME</time>',
[
@ -260,7 +234,6 @@ class BigPipePlaceholderTestCases {
];
$current_time->embeddedHtmlResponse = '<time datetime="1991-03-14"></time>';
// 6. Edge case: #lazy_builder that throws an exception.
$exception = new BigPipePlaceholderTestCase(
[

View file

@ -3,7 +3,6 @@
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 {
@ -25,7 +24,7 @@ class BigPipeTestController {
if ($has_session) {
// Only set a message if a session already exists, otherwise we always
// trigger a session, which means we can't test no-session requests.
drupal_set_message('Hello from BigPipe!');
\Drupal::messenger()->addStatus('Hello from BigPipe!');
}
$build['html'] = $cases['html']->renderArray;
@ -61,7 +60,7 @@ class BigPipeTestController {
/**
* A page with multiple occurrences of the same placeholder.
*
* @see \Drupal\big_pipe\Tests\BigPipeTest::testBigPipeMultipleOccurrencePlaceholders()
* @see \Drupal\Tests\big_pipe\Functional\BigPipeTest::testBigPipeMultiOccurrencePlaceholders()
*
* @return array
*/
@ -92,7 +91,7 @@ class BigPipeTestController {
public static function currentTime() {
return [
'#markup' => '<time datetime="' . date('Y-m-d', 668948400) . '"></time>',
'#cache' => ['max-age' => 0]
'#cache' => ['max-age' => 0],
];
}
@ -134,7 +133,7 @@ class BigPipeTestController {
/**
* #lazy_builder callback; returns the current count.
*
* @see \Drupal\big_pipe\Tests\BigPipeTest::testBigPipeMultipleOccurrencePlaceholders()
* @see \Drupal\Tests\big_pipe\Functional\BigPipeTest::testBigPipeMultiOccurrencePlaceholders()
*
* @return array
* The render array.

View file

@ -5,6 +5,11 @@ namespace Drupal\big_pipe_test\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Form to test BigPipe.
*
* @internal
*/
class BigPipeTestForm extends FormBase {
/**
@ -35,6 +40,6 @@ class BigPipeTestForm extends FormBase {
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) { }
public function submitForm(array &$form, FormStateInterface $form_state) {}
}

View file

@ -1,14 +1,16 @@
<?php
namespace Drupal\big_pipe\Tests;
namespace Drupal\Tests\big_pipe\Functional;
use Behat\Mink\Element\NodeElement;
use Drupal\big_pipe\Render\Placeholder\BigPipeStrategy;
use Drupal\big_pipe\Render\BigPipe;
use Drupal\big_pipe_test\BigPipePlaceholderTestCases;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
use Drupal\Tests\BrowserTestBase;
/**
* Tests BigPipe's no-JS detection & response delivery (with and without JS).
@ -21,7 +23,7 @@ use Drupal\simpletest\WebTestBase;
*
* @group big_pipe
*/
class BigPipeTest extends WebTestBase {
class BigPipeTest extends BrowserTestBase {
/**
* Modules to enable.
@ -95,9 +97,7 @@ class BigPipeTest extends WebTestBase {
$this->drupalLogout();
// Close the prior connection and remove the collected state.
$this->curlClose();
$this->curlCookies = [];
$this->cookies = [];
$this->getSession()->reset();
// 3. Session (anonymous).
$this->drupalGet(Url::fromRoute('user.login', [], ['query' => ['trigger_session' => 1]]));
@ -112,9 +112,7 @@ class BigPipeTest extends WebTestBase {
$this->assertRaw($no_js_to_js_markup);
// Close the prior connection and remove the collected state.
$this->curlClose();
$this->curlCookies = [];
$this->cookies = [];
$this->getSession()->reset();
// Edge case: route with '_no_big_pipe' option.
$this->drupalGet(Url::fromRoute('no_big_pipe'));
@ -138,7 +136,7 @@ class BigPipeTest extends WebTestBase {
* - \Drupal\big_pipe\Render\BigPipe
* - \Drupal\big_pipe\Render\BigPipe::sendPlaceholders()
*
* @see \Drupal\big_pipe\Tests\BigPipePlaceholderTestCases
* @see \Drupal\big_pipe_test\BigPipePlaceholderTestCases
*/
public function testBigPipe() {
// Simulate production.
@ -158,7 +156,7 @@ class BigPipeTest extends WebTestBase {
$this->drupalGet(Url::fromRoute('big_pipe_test'));
$this->assertBigPipeResponseHeadersPresent();
$this->assertNoCacheTag('cache_tag_set_in_lazy_builder');
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'cache_tag_set_in_lazy_builder');
$this->setCsrfTokenSeedInTestEnvironment();
$cases = $this->getTestCases();
@ -207,7 +205,7 @@ class BigPipeTest extends WebTestBase {
$this->assertNoRaw(BigPipe::STOP_SIGNAL, 'BigPipe stop signal absent: error occurred before then.');
$this->assertNoRaw('</body>', 'Closing body tag absent: error occurred before then.');
// The exception is expected. Do not interpret it as a test failure.
unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
unlink($this->root . '/' . $this->siteDirectory . '/error.log');
}
/**
@ -218,7 +216,7 @@ class BigPipeTest extends WebTestBase {
* - \Drupal\big_pipe\Render\BigPipe
* - \Drupal\big_pipe\Render\BigPipe::sendNoJsPlaceholders()
*
* @see \Drupal\big_pipe\Tests\BigPipePlaceholderTestCases
* @see \Drupal\big_pipe_test\BigPipePlaceholderTestCases
*/
public function testBigPipeNoJs() {
// Simulate production.
@ -238,7 +236,7 @@ class BigPipeTest extends WebTestBase {
$this->drupalGet(Url::fromRoute('big_pipe_test'));
$this->assertBigPipeResponseHeadersPresent();
$this->assertNoCacheTag('cache_tag_set_in_lazy_builder');
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'cache_tag_set_in_lazy_builder');
$this->setCsrfTokenSeedInTestEnvironment();
$cases = $this->getTestCases();
@ -275,7 +273,7 @@ class BigPipeTest extends WebTestBase {
$this->assertRaw('You are not allowed to say llamas are not cool!');
$this->assertNoRaw('</body>', 'Closing body tag absent: error occurred before then.');
// The exception is expected. Do not interpret it as a test failure.
unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
unlink($this->root . '/' . $this->siteDirectory . '/error.log');
}
/**
@ -298,7 +296,7 @@ class BigPipeTest extends WebTestBase {
$this->assertRaw('The count is 1.');
$this->assertNoRaw('The count is 2.');
$this->assertNoRaw('The count is 3.');
$raw_content = $this->getRawContent();
$raw_content = $this->getSession()->getPage()->getContent();
$this->assertTrue(substr_count($raw_content, $expected_placeholder_replacement) == 1, 'Only one placeholder replacement was found for the duplicate #lazy_builder arrays.');
// By calling performMetaRefresh() here, we simulate JavaScript being
@ -359,7 +357,7 @@ class BigPipeTest extends WebTestBase {
// Verify expected placeholder.
$expected_placeholder_html = '<span data-big-pipe-placeholder-id="' . $big_pipe_placeholder_id . '"></span>';
$this->assertRaw($expected_placeholder_html, 'BigPipe placeholder for placeholder ID "' . $big_pipe_placeholder_id . '" found.');
$pos = strpos($this->getRawContent(), $expected_placeholder_html);
$pos = strpos($this->getSession()->getPage()->getContent(), $expected_placeholder_html);
$placeholder_positions[$pos] = $big_pipe_placeholder_id;
// Verify expected placeholder replacement.
$expected_placeholder_replacement = '<script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="' . $big_pipe_placeholder_id . '">';
@ -369,14 +367,16 @@ class BigPipeTest extends WebTestBase {
$this->assertNoRaw($expected_placeholder_replacement);
continue;
}
$this->assertEqual($expected_ajax_response, trim((string) $result[0]));
$this->assertEqual($expected_ajax_response, trim($result[0]->getText()));
$this->assertRaw($expected_placeholder_replacement);
$pos = strpos($this->getRawContent(), $expected_placeholder_replacement);
$pos = strpos($this->getSession()->getPage()->getContent(), $expected_placeholder_replacement);
$placeholder_replacement_positions[$pos] = $big_pipe_placeholder_id;
}
ksort($placeholder_positions, SORT_NUMERIC);
$this->assertEqual(array_keys($expected_big_pipe_placeholders), array_values($placeholder_positions));
$placeholders = array_map(function(\SimpleXMLElement $element) { return (string) $element['data-big-pipe-placeholder-id']; }, $this->cssSelect('[data-big-pipe-placeholder-id]'));
$placeholders = array_map(function (NodeElement $element) {
return $element->getAttribute('data-big-pipe-placeholder-id');
}, $this->cssSelect('[data-big-pipe-placeholder-id]'));
$this->assertEqual(count($expected_big_pipe_placeholders), count(array_unique($placeholders)));
$expected_big_pipe_placeholders_with_replacements = [];
foreach ($expected_big_pipe_placeholder_stream_order as $big_pipe_placeholder_id) {
@ -384,13 +384,13 @@ class BigPipeTest extends WebTestBase {
}
$this->assertEqual($expected_big_pipe_placeholders_with_replacements, array_filter($expected_big_pipe_placeholders));
$this->assertSetsEqual(array_keys($expected_big_pipe_placeholders_with_replacements), array_values($placeholder_replacement_positions));
$this->assertEqual(count($expected_big_pipe_placeholders_with_replacements), preg_match_all('/' . preg_quote('<script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="', '/') . '/', $this->getRawContent()));
$this->assertEqual(count($expected_big_pipe_placeholders_with_replacements), preg_match_all('/' . preg_quote('<script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="', '/') . '/', $this->getSession()->getPage()->getContent()));
$this->pass('Verifying BigPipe start/stop signals…', 'Debug');
$this->assertRaw(BigPipe::START_SIGNAL, 'BigPipe start signal present.');
$this->assertRaw(BigPipe::STOP_SIGNAL, 'BigPipe stop signal present.');
$start_signal_position = strpos($this->getRawContent(), BigPipe::START_SIGNAL);
$stop_signal_position = strpos($this->getRawContent(), BigPipe::STOP_SIGNAL);
$start_signal_position = strpos($this->getSession()->getPage()->getContent(), BigPipe::START_SIGNAL);
$stop_signal_position = strpos($this->getSession()->getPage()->getContent(), BigPipe::STOP_SIGNAL);
$this->assertTrue($start_signal_position < $stop_signal_position, 'BigPipe start signal appears before stop signal.');
$this->pass('Verifying BigPipe placeholder replacements and start/stop signals were streamed in the correct order…', 'Debug');
@ -409,13 +409,13 @@ class BigPipeTest extends WebTestBase {
* Ensures CSRF tokens can be generated for the current user's session.
*/
protected function setCsrfTokenSeedInTestEnvironment() {
$session_data = $this->container->get('session_handler.write_safe')->read($this->cookies[$this->getSessionName()]['value']);
$session_data = $this->container->get('session_handler.write_safe')->read($this->getSession()->getCookie($this->getSessionName()));
$csrf_token_seed = unserialize(explode('_sf2_meta|', $session_data)[1])['s'];
$this->container->get('session_manager.metadata_bag')->setCsrfTokenSeed($csrf_token_seed);
}
/**
* @return \Drupal\big_pipe\Tests\BigPipePlaceholderTestCase[]
* @return \Drupal\big_pipe_test\BigPipePlaceholderTestCase[]
*/
protected function getTestCases($has_session = TRUE) {
return BigPipePlaceholderTestCases::cases($this->container, $this->rootUser);
@ -446,33 +446,39 @@ class BigPipeTest extends WebTestBase {
* Asserts whether a cookie exists on the client or not.
*/
protected function assertCookieExists($cookie_name, $expected, $cookie_label) {
$non_deleted_cookies = array_filter($this->cookies, function ($item) { return $item['value'] !== 'deleted'; });
$this->assertEqual($expected, isset($non_deleted_cookies[$cookie_name]), $expected ? "$cookie_label cookie exists." : "$cookie_label cookie does not exist.");
$this->assertEqual($expected, !empty($this->getSession()->getCookie($cookie_name)), $expected ? "$cookie_label cookie exists." : "$cookie_label cookie does not exist.");
}
/**
* Calls ::performMetaRefresh() and asserts the responses.
*/
protected function assertBigPipeNoJsMetaRefreshRedirect() {
$original_url = $this->url;
$original_url = $this->getSession()->getCurrentUrl();
// Disable automatic following of redirects by the HTTP client, so that this
// test can analyze the response headers of each redirect response.
$this->getSession()->getDriver()->getClient()->followRedirects(FALSE);
$this->performMetaRefresh();
$headers[0] = $this->getSession()->getResponseHeaders();
$statuses[0] = $this->getSession()->getStatusCode();
$this->performMetaRefresh();
$headers[1] = $this->getSession()->getResponseHeaders();
$statuses[1] = $this->getSession()->getStatusCode();
$this->getSession()->getDriver()->getClient()->followRedirects(TRUE);
$this->assertEqual($original_url, $this->url, 'Redirected back to the original location.');
$headers = $this->drupalGetHeaders(TRUE);
$this->assertEqual(2, count($headers), 'Two requests were made upon following the <meta> refresh, there are headers for two responses.');
$this->assertEqual($original_url, $this->getSession()->getCurrentUrl(), 'Redirected back to the original location.');
// First response: redirect.
$this->assertEqual('HTTP/1.1 302 Found', $headers[0][':status'], 'The first response was a 302 (redirect).');
$this->assertIdentical(0, strpos($headers[0]['set-cookie'], 'big_pipe_nojs=1'), 'The first response sets the big_pipe_nojs cookie.');
$this->assertEqual($original_url, $headers[0]['location'], 'The first response redirected back to the original page.');
$this->assertTrue(empty(array_diff(['cookies:big_pipe_nojs', 'session.exists'], explode(' ', $headers[0]['x-drupal-cache-contexts']))), 'The first response varies by the "cookies:big_pipe_nojs" and "session.exists" cache contexts.');
$this->assertFalse(isset($headers[0]['surrogate-control']), 'The first response has no "Surrogate-Control" header.');
$this->assertEqual(302, $statuses[0], 'The first response was a 302 (redirect).');
$this->assertIdentical(0, strpos($headers[0]['Set-Cookie'][0], 'big_pipe_nojs=1'), 'The first response sets the big_pipe_nojs cookie.');
$this->assertEqual($original_url, $headers[0]['Location'][0], 'The first response redirected back to the original page.');
$this->assertTrue(empty(array_diff(['cookies:big_pipe_nojs', 'session.exists'], explode(' ', $headers[0]['X-Drupal-Cache-Contexts'][0]))), 'The first response varies by the "cookies:big_pipe_nojs" and "session.exists" cache contexts.');
$this->assertFalse(isset($headers[0]['Surrogate-Control']), 'The first response has no "Surrogate-Control" header.');
// Second response: redirect followed.
$this->assertEqual('HTTP/1.1 200 OK', $headers[1][':status'], 'The second response was a 200.');
$this->assertTrue(empty(array_diff(['cookies:big_pipe_nojs', 'session.exists'], explode(' ', $headers[0]['x-drupal-cache-contexts']))), 'The first response varies by the "cookies:big_pipe_nojs" and "session.exists" cache contexts.');
$this->assertEqual('no-store, content="BigPipe/1.0"', $headers[1]['surrogate-control'], 'The second response has a "Surrogate-Control" header.');
$this->assertEqual(200, $statuses[1], 'The second response was a 200.');
$this->assertTrue(empty(array_diff(['cookies:big_pipe_nojs', 'session.exists'], explode(' ', $headers[0]['X-Drupal-Cache-Contexts'][0]))), 'The first response varies by the "cookies:big_pipe_nojs" and "session.exists" cache contexts.');
$this->assertEqual('no-store, content="BigPipe/1.0"', $headers[1]['Surrogate-Control'][0], 'The second response has a "Surrogate-Control" header.');
$this->assertNoRaw('<noscript><meta http-equiv="Refresh" content="0; URL=', 'Once the BigPipe no-JS cookie is set, the <meta> refresh is absent: only one redirect ever happens.');
}

View file

@ -11,7 +11,7 @@ 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\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\simpletest\ContentTypeCreationTrait;
use Drupal\simpletest\NodeCreationTrait;
@ -20,7 +20,7 @@ use Drupal\simpletest\NodeCreationTrait;
*
* @group big_pipe
*/
class BigPipeRegressionTest extends JavascriptTestBase {
class BigPipeRegressionTest extends WebDriverTestBase {
use CommentTestTrait;
use ContentTypeCreationTrait;

View file

@ -101,8 +101,8 @@ class BigPipeResponseAttachmentsProcessorTest extends UnitTestCase {
'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_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],

View file

@ -3,7 +3,7 @@
namespace Drupal\Tests\big_pipe\Unit\Render\Placeholder;
use Drupal\big_pipe\Render\Placeholder\BigPipeStrategy;
use Drupal\big_pipe\Tests\BigPipePlaceholderTestCases;
use Drupal\big_pipe_test\BigPipePlaceholderTestCases;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\SessionConfigurationInterface;
use Drupal\Tests\UnitTestCase;
@ -47,7 +47,7 @@ class BigPipeStrategyTest extends UnitTestCase {
$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) {
if ($request->isMethodCacheable() && !$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'");
@ -59,7 +59,7 @@ class BigPipeStrategyTest extends UnitTestCase {
}
/**
* @see \Drupal\big_pipe\Tests\BigPipePlaceholderTestCases
* @see \Drupal\big_pipe_test\BigPipePlaceholderTestCases
*/
public function placeholdersProvider() {
$cases = BigPipePlaceholderTestCases::cases();
@ -83,25 +83,29 @@ class BigPipeStrategyTest extends UnitTestCase {
'_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' => [
$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' => [
$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, []],
];
}