From 4f22d37ee370018e214e9fca03fd7f7dd26d517e Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Mon, 5 Jul 2021 23:32:29 -0700 Subject: [PATCH] CaseActivity - Define class-model and unit-test for workflow message. Add base test-trait. --- CRM/Case/WorkflowMessage/CaseActivity.php | 111 ++++++++++++++++++ .../CaseActivity/adhoc_1.ex.php | 32 +++++ .../CaseActivity/class_1.ex.php | 33 ++++++ Civi/Test/WorkflowMessageTestTrait.php | 82 +++++++++++++ Civi/Token/TokenCompatSubscriber.php | 2 +- .../Case/WorkflowMessage/CaseActivityTest.php | 79 +++++++++++++ .../CRM/Core/BAO/MessageTemplateTest.php | 45 ++++--- 7 files changed, 360 insertions(+), 24 deletions(-) create mode 100644 CRM/Case/WorkflowMessage/CaseActivity.php create mode 100644 CRM/Case/WorkflowMessage/CaseActivity/adhoc_1.ex.php create mode 100644 CRM/Case/WorkflowMessage/CaseActivity/class_1.ex.php create mode 100644 Civi/Test/WorkflowMessageTestTrait.php create mode 100644 tests/phpunit/CRM/Case/WorkflowMessage/CaseActivityTest.php diff --git a/CRM/Case/WorkflowMessage/CaseActivity.php b/CRM/Case/WorkflowMessage/CaseActivity.php new file mode 100644 index 0000000000..59380ba3c1 --- /dev/null +++ b/CRM/Case/WorkflowMessage/CaseActivity.php @@ -0,0 +1,111 @@ + 123, 'display_name' => 'Bob Roberts', role => 'FIXME'] + * + * @var array|null + * @scope tokenContext, tplParams + * @required + */ + public $contact; + + /** + * @var int + * @scope tplParams as client_id + * @required + */ + public $clientId; + + /** + * @var string + * @scope tplParams + * @required + */ + public $activitySubject; + + /** + * @var string + * @scope tplParams + * @required + */ + public $activityTypeName; + + /** + * Unique ID for this activity. Unique and difficult to guess. + * + * @var string + * @scope tplParams + * @required + */ + public $idHash; + + /** + * @var bool + * @scope tplParams + * @required + */ + public $isCaseActivity; + + /** + * @var string + * @scope tplParams + */ + public $editActURL; + + /** + * @var string + * @scope tplParams + */ + public $viewActURL; + + /** + * @var string + * @scope tplParams + */ + public $manageCaseURL; + + /** + * List of conventional activity fields. + * + * Example: [['label' => ..., 'category' => ..., 'type' => ..., 'value' => ...]] + * + * @var array + * @scope tplParams as activity.fields + * @required + */ + public $activityFields; + + /** + * List of custom activity fields, grouped by CustomGroup. + * + * Example: ['My Custom Stuff' => [['label' => ..., 'category' => ..., 'type' => ..., 'value' => ...]]] + * + * @var array + * @scope tplParams as activity.customGroups + */ + public $activityCustomGroups = []; + +} diff --git a/CRM/Case/WorkflowMessage/CaseActivity/adhoc_1.ex.php b/CRM/Case/WorkflowMessage/CaseActivity/adhoc_1.ex.php new file mode 100644 index 0000000000..47c9900e4d --- /dev/null +++ b/CRM/Case/WorkflowMessage/CaseActivity/adhoc_1.ex.php @@ -0,0 +1,32 @@ + ts('Case Activity (Adhoc-style example)'), + 'tags' => [], + 'data' => function (\Civi\WorkflowMessage\Examples $examples) { + $contact = $examples->extend('generic.alex.data.modelProps.contact', [ + 'role' => 'myrole', + ]); + return [ + 'tokenContext' => [ + 'contact' => $contact, + ], + 'tplParams' => [ + 'contact' => $contact, + 'isCaseActivity' => 1, + 'client_id' => 101, + 'activityTypeName' => 'Follow up', + 'activitySubject' => 'Test 123', + 'idHash' => 'abcdefg', + 'activity' => [ + 'fields' => [ + [ + 'label' => 'Case ID', + 'type' => 'String', + 'value' => '1234', + ], + ], + ], + ], + ]; + }, +]; diff --git a/CRM/Case/WorkflowMessage/CaseActivity/class_1.ex.php b/CRM/Case/WorkflowMessage/CaseActivity/class_1.ex.php new file mode 100644 index 0000000000..65f413ecf1 --- /dev/null +++ b/CRM/Case/WorkflowMessage/CaseActivity/class_1.ex.php @@ -0,0 +1,33 @@ + ts('Case Activity (Class-style example)'), + 'tags' => ['phpunit', 'preview'], + 'data' => function (\Civi\WorkflowMessage\Examples $examples) { + return $examples->extend('generic.alex.data', [ + 'modelProps' => [ + 'contact' => [ + 'role' => 'myrole', + ], + 'isCaseActivity' => 1, + 'clientId' => 101, + 'activityTypeName' => 'Follow up', + 'activityFields' => [ + [ + 'label' => 'Case ID', + 'type' => 'String', + 'value' => '1234', + ], + ], + 'activitySubject' => 'Test 123', + 'activityCustomGroups' => [], + 'idHash' => 'abcdefg', + ], + ]); + }, + 'asserts' => [ + 'default' => [ + ['for' => 'subject', 'regex' => '/\[case #abcdefg\] Test 123/'], + ['for' => 'text', 'regex' => '/Your Case Role\(s\) : myrole/'], + ], + ], +]; diff --git a/Civi/Test/WorkflowMessageTestTrait.php b/Civi/Test/WorkflowMessageTestTrait.php new file mode 100644 index 0000000000..395721bede --- /dev/null +++ b/Civi/Test/WorkflowMessageTestTrait.php @@ -0,0 +1,82 @@ +getWorkflowClass(); + return $class::WORKFLOW; + } + + /** + * @return \Civi\Api4\Generic\AbstractGetAction + * @throws \API_Exception + */ + protected function findExamples(): \Civi\Api4\Generic\AbstractGetAction { + return \Civi\Api4\WorkflowMessageExample::get(0) + ->setSelect(['name', 'title', 'workflow', 'tags', 'data', 'asserts']) + ->addWhere('workflow', '=', $this->getWorkflowName()) + ->addWhere('tags', 'CONTAINS', 'phpunit'); + } + + /** + * @param array $exampleProps + * @param string $exampleName + * @throws \Civi\WorkflowMessage\Exception\WorkflowMessageException + */ + protected function assertConstructorEquivalence(array $exampleProps, $exampleName = ''): void { + $class = $this->getWorkflowClass(); + $instances = []; + $instances["factory_$exampleName"] = WorkflowMessage::create($this->getWorkflowName(), $exampleProps); + $instances["class_$exampleName"] = new $class($exampleProps); + + /** @var \Civi\WorkflowMessage\WorkflowMessageInterface $refInstance */ + /** @var \Civi\WorkflowMessage\WorkflowMessageInterface $cmpInstance */ + + $refName = $refInstance = NULL; + $comparisons = 0; + foreach ($instances as $cmpName => $cmpInstance) { + if ($refName === NULL) { + $refName = $cmpName; + $refInstance = $cmpInstance; + continue; + } + + $this->assertSameWorkflowMessage($refInstance, $cmpInstance, "Compare $refName vs $cmpName: "); + $comparisons++; + } + $this->assertEquals(1, $comparisons); + } + + /** + * @param \Civi\WorkflowMessage\WorkflowMessageInterface $refInstance + * @param \Civi\WorkflowMessage\WorkflowMessageInterface $cmpInstance + * @param string|null $prefix + */ + protected function assertSameWorkflowMessage(\Civi\WorkflowMessage\WorkflowMessageInterface $refInstance, \Civi\WorkflowMessage\WorkflowMessageInterface $cmpInstance, ?string $prefix = NULL): void { + if ($prefix === NULL) { + $prefix = sprintf('[%s] ', $this->getWorkflowName()); + } + $this->assertEquals($refInstance->export('tplParams'), $cmpInstance->export('tplParams'), "{$prefix}Should have same export(tplParams)"); + $this->assertEquals($refInstance->export('tokenContext'), $cmpInstance->export('tokenContext'), "{$prefix}should have same export(tokenContext)"); + $this->assertEquals($refInstance->export('envelope'), $cmpInstance->export('envelope'), "{$prefix}Should have same export(envelope)"); + $refExportAll = WorkflowMessage::exportAll($refInstance); + $cmpExportAll = WorkflowMessage::exportAll($cmpInstance); + $this->assertEquals($refExportAll, $cmpExportAll, "{$prefix}Should have same exportAll()"); + } + +} diff --git a/Civi/Token/TokenCompatSubscriber.php b/Civi/Token/TokenCompatSubscriber.php index 417450ef93..8e9abec172 100644 --- a/Civi/Token/TokenCompatSubscriber.php +++ b/Civi/Token/TokenCompatSubscriber.php @@ -303,7 +303,7 @@ class TokenCompatSubscriber implements EventSubscriberInterface { $e->string = \CRM_Utils_Token::replaceDomainTokens($e->string, $domain, $isHtml, $e->message['tokens'], $useSmarty); if (!empty($e->context['contact'])) { - \CRM_Utils_Token::replaceGreetingTokens($e->string, $e->context['contact'], $e->context['contact']['contact_id'], NULL, $useSmarty); + \CRM_Utils_Token::replaceGreetingTokens($e->string, $e->context['contact'], $e->context['contact']['contact_id'] ?? $e->context['contactId'], NULL, $useSmarty); } if ($useSmarty) { diff --git a/tests/phpunit/CRM/Case/WorkflowMessage/CaseActivityTest.php b/tests/phpunit/CRM/Case/WorkflowMessage/CaseActivityTest.php new file mode 100644 index 0000000000..5a21f5be7c --- /dev/null +++ b/tests/phpunit/CRM/Case/WorkflowMessage/CaseActivityTest.php @@ -0,0 +1,79 @@ +setSelect(['name', 'data']) + ->addWhere('name', 'IN', ['case_activity.adhoc_1', 'case_activity.class_1']) + ->execute() + ->indexBy('name') + ->column('data'); + $byAdhoc = Civi\WorkflowMessage\WorkflowMessage::create('case_activity', $examples['case_activity.adhoc_1']); + $byClass = new CRM_Case_WorkflowMessage_CaseActivity($examples['case_activity.class_1']); + $this->assertSameWorkflowMessage($byClass, $byAdhoc, 'Compare byClass and byAdhoc: '); + } + + /** + * Ensure that various methods of constructing a WorkflowMessage all produce similar results. + * + * To see this, we take all the example data and use it with diff constructors. + */ + public function testConstructorEquivalence() { + $examples = $this->findExamples()->execute()->indexBy('name')->column('data'); + $this->assertTrue(count($examples) >= 1, 'Must have at least one example data-set'); + foreach ($examples as $example) { + $this->assertConstructorEquivalence($example); + } + } + + /** + * Basic canary test fetching a specific example. + * + * @throws \API_Exception + * @throws \Civi\API\Exception\UnauthorizedException + */ + public function testExampleGet() { + $file = \Civi::paths()->getPath('[civicrm.root]/CRM/Case/WorkflowMessage/CaseActivity/class_1.ex.php'); + $workflow = 'case_activity'; + $name = 'case_activity.class_1'; + + $this->assertTrue(file_exists($file), "Expect find canary file ($file)"); + + $get = \Civi\Api4\WorkflowMessageExample::get() + ->addWhere('name', '=', $name) + ->execute() + ->single(); + $this->assertEquals($workflow, $get['workflow']); + $this->assertTrue(!isset($get['data'])); + $this->assertTrue(!isset($get['asserts'])); + + $get = \Civi\Api4\WorkflowMessageExample::get() + ->addWhere('name', '=', $name) + ->addSelect('workflow', 'data') + ->execute() + ->single(); + $this->assertEquals($workflow, $get['workflow']); + $this->assertEquals(100, $get['data']['modelProps']['contact']['contact_id']); + $this->assertEquals('myrole', $get['data']['modelProps']['contact']['role']); + } + +} diff --git a/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php b/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php index b288a7ca98..f3ae760889 100644 --- a/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php +++ b/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php @@ -195,38 +195,37 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase { $client_id = $this->individualCreate(); $contact_id = $this->individualCreate(); - $tplParams = [ - 'isCaseActivity' => 1, - 'client_id' => $client_id, - // activityTypeName means label here not name, but it's ok because label is desired here (dev/core#1116-ok-label) - 'activityTypeName' => 'Follow up', - 'activity' => [ - 'fields' => [ + $msg = \Civi\WorkflowMessage\WorkflowMessage::create('case_activity', [ + 'modelProps' => [ + 'contactId' => $contact_id, + 'contact' => ['role' => 'Sand grain counter'], + 'isCaseActivity' => 1, + 'clientId' => $client_id, + // activityTypeName means label here not name, but it's ok because label is desired here (dev/core#1116-ok-label) + 'activityTypeName' => 'Follow up', + 'activityFields' => [ [ 'label' => 'Case ID', 'type' => 'String', 'value' => '1234', ], ], + 'activitySubject' => 'Test 123', + 'idHash' => substr(sha1(CIVICRM_SITE_KEY . '1234'), 0, 7), ], - 'activitySubject' => 'Test 123', - 'idHash' => substr(sha1(CIVICRM_SITE_KEY . '1234'), 0, 7), - 'contact' => ['role' => 'Sand grain counter'], - ]; + ]); - [, $subject, $message] = CRM_Core_BAO_MessageTemplate::sendTemplate( - [ - 'valueName' => 'case_activity', - 'contactId' => $contact_id, - 'tplParams' => $tplParams, - 'from' => 'admin@example.com', - 'toName' => 'Demo', - 'toEmail' => 'admin@example.com', - 'attachments' => NULL, - ] - ); + $this->assertEquals([], \Civi\Test\Invasive::get([$msg, '_extras'])); + + [, $subject, $message] = $msg->sendTemplate([ + 'valueName' => 'case_activity', + 'from' => 'admin@example.com', + 'toName' => 'Demo', + 'toEmail' => 'admin@example.com', + 'attachments' => NULL, + ]); - $this->assertEquals('[case #' . $tplParams['idHash'] . '] Test 123', $subject); + $this->assertEquals('[case #' . $msg->getIdHash() . '] Test 123', $subject); $this->assertStringContainsString('Your Case Role(s) : Sand grain counter', $message); $this->assertStringContainsString('Case ID : 1234', $message); } -- 2.25.1