diff --git a/CRM/Utils/GuzzleMiddleware.php b/CRM/Utils/GuzzleMiddleware.php index 0c9815361265..c8b76cce88ca 100644 --- a/CRM/Utils/GuzzleMiddleware.php +++ b/CRM/Utils/GuzzleMiddleware.php @@ -227,7 +227,7 @@ public static function curlLog(\Psr\Log\LoggerInterface $logger) { $curlFmt = new class() extends \GuzzleHttp\MessageFormatter { - public function format(RequestInterface $request, ResponseInterface $response = NULL, \Exception $error = NULL) { + public function format(RequestInterface $request, ?ResponseInterface $response = NULL, ?\Throwable $error = NULL): string { $cmd = '$ curl'; if ($request->getMethod() !== 'GET') { $cmd .= ' -X ' . escapeshellarg($request->getMethod()); diff --git a/ext/afform/mock/tests/phpunit/E2E/AfformMock/MockPublicFormTest.php b/ext/afform/mock/tests/phpunit/E2E/AfformMock/MockPublicFormTest.php index d9fe9ac341ad..7a0e525d09bd 100644 --- a/ext/afform/mock/tests/phpunit/E2E/AfformMock/MockPublicFormTest.php +++ b/ext/afform/mock/tests/phpunit/E2E/AfformMock/MockPublicFormTest.php @@ -67,80 +67,50 @@ public function testPublicEditDisallowed() { } /** - * The email token `{afform.mockPublicFormUrl}` should evaluate to an authenticated URL. + * There are two tokens ({afform.mockPublicFormUrl} and {afform.mockPublicFormLink}) + * which are rendered in two contexts (text and HTML). + * + * Make sure that the resulting URLs point to the same place, regardless of which + * variant or environment is used. + * + * @return void */ - public function testAuthenticatedUrlToken_Plain() { - if (!function_exists('authx_civicrm_config')) { - $this->fail('Cannot test without authx'); - } - + public function testWellFormedTokens() { $lebowski = $this->getLebowskiCID(); - $text = $this->renderTokens($lebowski, 'Please go to {afform.mockPublicFormUrl}', 'text/plain'); - if (!preg_match(';Please go to ([^\s]+);', $text, $m)) { - $this->fail('Plain text message did not have URL in expected place: ' . $text); - } - $url = $m[1]; - $this->assertMatchesRegularExpression(';^https?:.*civicrm/mock-public-form.*;', $url, "URL should look plausible"); + $messages = \CRM_Core_TokenSmarty::render([ + 'text' => 'url=({afform.mockPublicFormUrl}) link=({afform.mockPublicFormLink})', + 'html' => '
url=({afform.mockPublicFormUrl}) link=({afform.mockPublicFormLink})
', + ], ['contactId' => $lebowski]); - // Going to this page will cause us to authenticate as the target contact - $http = $this->createGuzzle(['http_errors' => FALSE, 'cookies' => new \GuzzleHttp\Cookie\CookieJar()]); - $response = $http->get($url); - $r = (string) $response->getBody(); - $this->assertStatusCode(200, $response); - $response = $http->get('civicrm/authx/id'); - $this->assertContactJson($lebowski, $response); - } + $httpTextUrl = '(https?:[a-zA-Z0-9_/\.\?\-\+:=#&]+)'; + $httpHtmlUrl = '(https?:[a-zA-Z0-9_/\.\?\-\+:=#&\;]+)'; + $textPattern = ";url=\($httpTextUrl\) link=\(\[My public form\]\($httpTextUrl\)\); "; + $htmlPattern = ";\url=\($httpHtmlUrl\) link=\(My public form\)\
;"; - /** - * The email token `{afform.mockPublicFormUrl}` should evaluate to an authenticated URL. - */ - public function testAuthenticatedUrlToken_Html() { - if (!function_exists('authx_civicrm_config')) { - $this->fail('Cannot test without authx'); - } + $this->assertMatchesRegularExpression($textPattern, $messages['text']); + $this->assertMatchesRegularExpression($htmlPattern, $messages['html']); - $lebowski = $this->getLebowskiCID(); - $html = $this->renderTokens($lebowski, 'Please go to my form', 'text/html'); + preg_match($textPattern, $messages['text'], $textMatches); + preg_match($htmlPattern, $messages['html'], $htmlMatches); - if (!preg_match(';a href="([^"]+)";', $html, $m)) { - $this->fail('HTML message did not have URL in expected place: ' . $html); - } - $url = html_entity_decode($m[1]); - $this->assertMatchesRegularExpression(';^https?:.*civicrm/mock-public-form.*;', $url, "URL should look plausible"); + $this->assertEquals($textMatches[1], html_entity_decode($htmlMatches[1]), 'Text and HTML values of {afform.mockPublicFormUrl} should point to same place'); + $this->assertEquals($textMatches[2], html_entity_decode($htmlMatches[2]), 'Text and HTML values of {afform.mockPublicFormLink} should point to same place'); - // Going to this page will cause us to authenticate as the target contact - $http = $this->createGuzzle(['cookies' => new \GuzzleHttp\Cookie\CookieJar()]); - $response = $http->get($url); - $this->assertStatusCode(200, $response); - $response = $http->get('civicrm/authx/id'); - $this->assertContactJson($lebowski, $response); + $this->assertMatchesRegularExpression(';^https?:.*civicrm/mock-public-form.*;', $textMatches[1], "URL should look plausible"); + $this->assertMatchesRegularExpression(';^https?:.*civicrm/mock-public-form.*;', $textMatches[2], "URL should look plausible"); } /** - * The email token `{afform.mockPublicFormLink}` should evaluate to an authenticated URL. + * The email token `{afform.mockPublicFormUrl}` should evaluate to an authenticated URL. */ - public function testAuthenticatedLinkToken_Html() { - if (!function_exists('authx_civicrm_config')) { - $this->fail('Cannot test without authx'); - } + public function testAuthenticatedUrlToken() { + $this->assertTrue(function_exists('authx_civicrm_config'), 'Cannot test without authx'); $lebowski = $this->getLebowskiCID(); - $html = $this->renderTokens($lebowski, 'Please go to {afform.mockPublicFormLink}', 'text/html'); - $doc = \phpQuery::newDocument($html, 'text/html'); - $this->assertEquals(1, $doc->find('a')->count(), 'Document should have hyperlink'); - foreach ($doc->find('a') as $item) { - /** @var \DOMElement $item */ - $this->assertMatchesRegularExpression(';^https?:.*civicrm/mock-public-form.*;', $item->getAttribute('href')); - $this->assertEquals('My public form', $item->firstChild->data); - $url = $item->getAttribute('href'); - } - - // Going to this page will cause us to authenticate as the target contact - $http = $this->createGuzzle(['cookies' => new \GuzzleHttp\Cookie\CookieJar()]); - $response = $http->get($url); - $this->assertStatusCode(200, $response); - $response = $http->get('civicrm/authx/id'); - $this->assertContactJson($lebowski, $response); + $url = $this->renderTokens($lebowski, '{afform.mockPublicFormUrl}', 'text/plain'); + $this->assertMatchesRegularExpression(';^https?:.*civicrm/mock-public-form.*;', $url, "URL should look plausible"); + + $this->assertUrlStartsSession($url, $lebowski); } protected function renderTokens($cid, $body, $format) { @@ -151,7 +121,7 @@ protected function renderTokens($cid, $body, $format) { return $tp->getRow(0)->render('example'); } - protected function getLebowskiCID() { + protected function getLebowskiCID(): int { $contact = \civicrm_api3('Contact', 'create', [ 'contact_type' => 'Individual', 'first_name' => 'Jeffrey', @@ -179,4 +149,25 @@ public function assertContactJson($cid, $response) { $this->assertEquals($cid, $j['contact_id'], "Response did not give expected contact ID\n" . $formattedFailure); } + /** + * Opening $url + * + * @param string $url + * @param int $contactId + * + * @return void + * @throws \GuzzleHttp\Exception\GuzzleException + */ + protected function assertUrlStartsSession(string $url, int $contactId): void { + $http = $this->createGuzzle([ + 'http_errors' => FALSE, + 'cookies' => new \GuzzleHttp\Cookie\CookieJar(), + ]); + $response = $http->get($url); + $r = (string) $response->getBody(); + $this->assertStatusCode(200, $response); + $response = $http->get('civicrm/authx/id'); + $this->assertContactJson($contactId, $response); + } + }