Convert event badges to use token processor
[civicrm-core.git] / tests / phpunit / CRM / Utils / TokenConsistencyTest.php
CommitLineData
d0ce76fd
EM
1<?php
2/*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
0f4031da
EM
12use Civi\Token\TokenProcessor;
13
d0ce76fd
EM
14/**
15 * CRM_Utils_TokenConsistencyTest
16 *
17 * Class for ensuring tokens have internal consistency.
18 *
19 * @group Tokens
20 *
21 * @group headless
22 */
23class CRM_Utils_TokenConsistencyTest extends CiviUnitTestCase {
24
25 use CRMTraits_Custom_CustomDataTrait;
26
27 /**
28 * Created case.
29 *
30 * @var array
31 */
32 protected $case;
33
0f4031da
EM
34 /**
35 * Recurring contribution.
36 *
37 * @var array
38 */
39 protected $contributionRecur;
40
d0ce76fd
EM
41 /**
42 * Post test cleanup.
43 *
44 * @throws \API_Exception
45 * @throws \CRM_Core_Exception
46 */
47 public function tearDown(): void {
d568dbe0 48 $this->quickCleanup(['civicrm_case', 'civicrm_case_type'], TRUE);
d0ce76fd
EM
49 parent::tearDown();
50 }
51
52 /**
53 * Test that case tokens are consistently rendered.
54 *
d0ce76fd
EM
55 * @throws \CiviCRM_API3_Exception
56 */
57 public function testCaseTokenConsistency(): void {
58 $this->createLoggedInUser();
59 CRM_Core_BAO_ConfigSetting::enableComponent('CiviCase');
60 $this->createCustomGroupWithFieldOfType(['extends' => 'Case']);
61 $tokens = CRM_Core_SelectValues::caseTokens();
62 $this->assertEquals($this->getCaseTokens(), $tokens);
63 $caseID = $this->getCaseID();
78ffc4d7
EM
64 $tokenString = implode("\n", array_keys($this->getCaseTokens()));
65 $tokenHtml = CRM_Utils_Token::replaceCaseTokens($caseID, $tokenString, ['case' => $this->getCaseTokenKeys()]);
d0ce76fd
EM
66 $this->assertEquals($this->getExpectedCaseTokenOutput(), $tokenHtml);
67 // Now do the same without passing in 'knownTokens'
78ffc4d7 68 $tokenHtml = CRM_Utils_Token::replaceCaseTokens($caseID, $tokenString);
d0ce76fd 69 $this->assertEquals($this->getExpectedCaseTokenOutput(), $tokenHtml);
2f5c0408
EM
70
71 // And check our deprecated tokens still work.
72 $tokenHtml = CRM_Utils_Token::replaceCaseTokens($caseID, '{case.case_type_id} {case.status_id}');
73 $this->assertEquals('Housing Support Ongoing', $tokenHtml);
78ffc4d7
EM
74
75 $additionalTokensFromProcessor = [
76 '{case.case_type_id}' => 'Case Type ID',
77 '{case.status_id}' => 'Case Status',
78 '{case.case_type_id:name}' => 'Machine name: Case Type',
79 '{case.status_id:name}' => 'Machine name: Case Status',
80 ];
81 $expectedTokens = array_merge($this->getCaseTokens(), $additionalTokensFromProcessor);
82
83 $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
84 'controller' => __CLASS__,
85 'smarty' => FALSE,
86 'schema' => ['caseId'],
87 ]);
3c78698e 88 $this->assertEquals(array_merge($expectedTokens, $this->getDomainTokens()), $tokenProcessor->listTokens());
78ffc4d7
EM
89 $tokenProcessor->addRow([
90 'caseId' => $this->getCaseID(),
91 ]);
92 $tokenProcessor->addMessage('html', $tokenString, 'text/plain');
93
94 $tokenProcessor->evaluate();
95 foreach ($tokenProcessor->getRows() as $row) {
96 $text = $row->render('html');
97 }
98 $this->assertEquals($this->getExpectedCaseTokenOutput(), $text);
d0ce76fd
EM
99 }
100
101 /**
102 * Get expected output from token parsing.
103 *
104 * @return string
105 */
106 protected function getExpectedCaseTokenOutput(): string {
107 return '1
108Housing Support
109Case Subject
110July 23rd, 2021
111July 26th, 2021
112case details
113Ongoing
114No
78ffc4d7
EM
115' . CRM_Utils_Date::customFormat($this->case['created_date']) . '
116' . CRM_Utils_Date::customFormat($this->case['modified_date']) . '
d0ce76fd
EM
117';
118 }
119
120 /**
121 * @return int
122 */
123 protected function getContactID(): int {
124 if (!isset($this->ids['Contact'][0])) {
125 $this->ids['Contact'][0] = $this->individualCreate();
126 }
127 return $this->ids['Contact'][0];
128 }
129
130 /**
131 * Get the keys for the case tokens.
132 *
133 * @return array
134 */
135 public function getCaseTokenKeys(): array {
136 $return = [];
137 foreach (array_keys($this->getCaseTokens()) as $key) {
138 $return[] = substr($key, 6, -1);
139 }
140 return $return;
141 }
142
143 /**
144 * Get declared tokens.
145 *
146 * @return string[]
147 */
148 public function getCaseTokens(): array {
149 return [
150 '{case.id}' => 'Case ID',
2f5c0408 151 '{case.case_type_id:label}' => 'Case Type',
d0ce76fd
EM
152 '{case.subject}' => 'Case Subject',
153 '{case.start_date}' => 'Case Start Date',
154 '{case.end_date}' => 'Case End Date',
155 '{case.details}' => 'Details',
2f5c0408 156 '{case.status_id:label}' => 'Case Status',
78ffc4d7 157 '{case.is_deleted:label}' => 'Case is in the Trash',
d0ce76fd
EM
158 '{case.created_date}' => 'Created Date',
159 '{case.modified_date}' => 'Modified Date',
160 '{case.custom_1}' => 'Enter text here :: Group with field text',
161 ];
162 }
163
164 /**
165 * Get case ID.
166 *
167 * @return int
168 */
169 protected function getCaseID(): int {
170 if (!isset($this->case)) {
a0fb3068 171 $case_id = $this->callAPISuccess('Case', 'create', [
d0ce76fd
EM
172 'case_type_id' => 'housing_support',
173 'activity_subject' => 'Case Subject',
174 'client_id' => $this->getContactID(),
175 'status_id' => 1,
176 'subject' => 'Case Subject',
177 'start_date' => '2021-07-23 15:39:20',
a0fb3068 178 // Note end_date is inconsistent with status Ongoing but for the
179 // purposes of testing tokens is ok. Creating it with status Resolved
180 // then ignores our known fixed end date.
d0ce76fd
EM
181 'end_date' => '2021-07-26 18:07:20',
182 'medium_id' => 2,
183 'details' => 'case details',
184 'activity_details' => 'blah blah',
185 'sequential' => 1,
a0fb3068 186 ])['id'];
187 // Need to retrieve the case again because modified date might be updated a
188 // split-second later than the original return value because of activity
189 // triggers when the timeline is populated. The returned array from create
190 // is determined before that happens.
191 $this->case = $this->callAPISuccess('Case', 'getsingle', ['id' => $case_id]);
d0ce76fd
EM
192 }
193 return $this->case['id'];
194 }
195
0f4031da
EM
196 /**
197 * Test that contribution recur tokens are consistently rendered.
198 */
199 public function testContributionRecurTokenConsistency(): void {
200 $this->createLoggedInUser();
201 $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
202 'controller' => __CLASS__,
203 'smarty' => FALSE,
204 'schema' => ['contribution_recurId'],
205 ]);
3c78698e 206 $this->assertEquals(array_merge($this->getContributionRecurTokens(), $this->getDomainTokens()), $tokenProcessor->listTokens());
0f4031da
EM
207 $tokenString = implode("\n", array_keys($this->getContributionRecurTokens()));
208
209 $tokenProcessor->addMessage('html', $tokenString, 'text/plain');
210 $tokenProcessor->addRow(['contribution_recurId' => $this->getContributionRecurID()]);
211 $tokenProcessor->evaluate();
212 $this->assertEquals($this->getExpectedContributionRecurTokenOutPut(), $tokenProcessor->getRow(0)->render('html'));
213 }
214
215 /**
216 * Get the contribution recur tokens keyed by the token.
217 *
218 * e.g {contribution_recur.id}
219 *
220 * @return array
221 */
222 protected function getContributionRecurTokens(): array {
223 $return = [];
224 foreach ($this->getContributionRecurTokensByField() as $key => $value) {
225 $return['{contribution_recur.' . $key . '}'] = $value;
226 }
227 return $return;
228 }
229
230 protected function getContributionRecurTokensByField(): array {
231 return [
232 'id' => 'Recurring Contribution ID',
233 'amount' => 'Amount',
234 'currency' => 'Currency',
235 'frequency_unit' => 'Frequency Unit',
236 'frequency_interval' => 'Interval (number of units)',
237 'installments' => 'Number of Installments',
238 'start_date' => 'Start Date',
239 'create_date' => 'Created Date',
240 'modified_date' => 'Modified Date',
241 'cancel_date' => 'Cancel Date',
242 'cancel_reason' => 'Cancellation Reason',
243 'end_date' => 'Recurring Contribution End Date',
244 'processor_id' => 'Processor ID',
245 'payment_token_id' => 'Payment Token ID',
246 'trxn_id' => 'Transaction ID',
247 'invoice_id' => 'Invoice ID',
248 'contribution_status_id' => 'Status',
97ca72e4 249 'is_test:label' => 'Test',
0f4031da
EM
250 'cycle_day' => 'Cycle Day',
251 'next_sched_contribution_date' => 'Next Scheduled Contribution Date',
252 'failure_count' => 'Number of Failures',
253 'failure_retry_date' => 'Retry Failed Attempt Date',
97ca72e4 254 'auto_renew:label' => 'Auto Renew',
0f4031da
EM
255 'payment_processor_id' => 'Payment Processor ID',
256 'financial_type_id' => 'Financial Type ID',
257 'payment_instrument_id' => 'Payment Method',
97ca72e4 258 'is_email_receipt:label' => 'Send email Receipt?',
0f4031da
EM
259 'frequency_unit:label' => 'Frequency Unit',
260 'frequency_unit:name' => 'Machine name: Frequency Unit',
261 'contribution_status_id:label' => 'Status',
262 'contribution_status_id:name' => 'Machine name: Status',
263 'payment_processor_id:label' => 'Payment Processor',
264 'payment_processor_id:name' => 'Machine name: Payment Processor',
265 'financial_type_id:label' => 'Financial Type',
266 'financial_type_id:name' => 'Machine name: Financial Type',
267 'payment_instrument_id:label' => 'Payment Method',
268 'payment_instrument_id:name' => 'Machine name: Payment Method',
269 ];
270 }
271
272 /**
273 * Get contributionRecur ID.
274 *
275 * @return int
276 */
277 protected function getContributionRecurID(): int {
278 if (!isset($this->contributionRecur)) {
279 $paymentProcessorID = $this->processorCreate();
280 $this->contributionRecur = $this->callAPISuccess('ContributionRecur', 'create', [
281 'contact_id' => $this->getContactID(),
282 'status_id' => 1,
283 'is_email_receipt' => 1,
284 'start_date' => '2021-07-23 15:39:20',
285 'end_date' => '2021-07-26 18:07:20',
286 'cancel_date' => '2021-08-19 09:12:45',
287 'cancel_reason' => 'Because',
288 'amount' => 5990.99,
289 'currency' => 'EUR',
290 'frequency_unit' => 'year',
291 'frequency_interval' => 2,
292 'installments' => 24,
293 'payment_instrument_id' => 'Check',
294 'financial_type_id' => 'Member dues',
295 'processor_id' => 'abc',
296 'payment_processor_id' => $paymentProcessorID,
297 'trxn_id' => 123,
298 'invoice_id' => 'inv123',
299 'sequential' => 1,
300 'failure_retry_date' => '2020-01-03',
301 'auto_renew' => 1,
302 'cycle_day' => '15',
303 'is_test' => TRUE,
304 'payment_token_id' => $this->callAPISuccess('PaymentToken', 'create', [
305 'contact_id' => $this->getContactID(),
306 'token' => 456,
307 'payment_processor_id' => $paymentProcessorID,
308 ])['id'],
309 ])['values'][0];
310 }
311 return $this->contributionRecur['id'];
312 }
313
314 /**
315 * Get rendered output for contribution tokens.
316 *
317 * @return string
318 */
319 protected function getExpectedContributionRecurTokenOutPut(): string {
320 return $this->getContributionRecurID() . '
321€ 5,990.99
322EUR
323year
3242
32524
326July 23rd, 2021 3:39 PM
327' . CRM_Utils_Date::customFormat($this->contributionRecur['create_date']) . '
328' . CRM_Utils_Date::customFormat($this->contributionRecur['modified_date']) . '
329August 19th, 2021 9:12 AM
330Because
331July 26th, 2021 6:07 PM
332abc
3331
334123
335inv123
3362
97ca72e4 337Yes
0f4031da
EM
33815
339
3400
341January 3rd, 2020 12:00 AM
97ca72e4 342Yes
0f4031da
EM
3431
3442
3454
97ca72e4 346Yes
0f4031da
EM
347year
348year
349Pending Label**
350Pending
97ca72e4
EM
351Dummy (test)
352Dummy (test)
0f4031da
EM
353Member Dues
354Member Dues
355Check
356Check';
357 }
358
d41a5d53
EM
359 /**
360 * Test that membership tokens are consistently rendered.
361 *
362 * @throws \API_Exception
363 */
364 public function testMembershipTokenConsistency(): void {
365 $this->createLoggedInUser();
366 $this->restoreMembershipTypes();
367 $this->createCustomGroupWithFieldOfType(['extends' => 'Membership']);
368 $tokens = CRM_Core_SelectValues::membershipTokens();
d568dbe0
EM
369 $expectedTokens = $this->getMembershipTokens();
370 $this->assertEquals($expectedTokens, $tokens);
d41a5d53
EM
371 $newStyleTokens = "\n{membership.status_id:label}\n{membership.membership_type_id:label}\n";
372 $tokenString = $newStyleTokens . implode("\n", array_keys($this->getMembershipTokens()));
b024d6a1 373
d41a5d53
EM
374 $memberships = CRM_Utils_Token::getMembershipTokenDetails([$this->getMembershipID()]);
375 $messageToken = CRM_Utils_Token::getTokens($tokenString);
376 $tokenHtml = CRM_Utils_Token::replaceEntityTokens('membership', $memberships[$this->getMembershipID()], $tokenString, $messageToken);
377 $this->assertEquals($this->getExpectedMembershipTokenOutput(), $tokenHtml);
dd2f879a 378
b024d6a1
EM
379 // Custom fields work in the processor so test it....
380 $tokenString .= "\n{membership." . $this->getCustomFieldName('text') . '}';
dd2f879a
EM
381 // Now compare with scheduled reminder
382 $mut = new CiviMailUtils($this);
383 CRM_Utils_Time::setTime('2007-01-22 15:00:00');
384 $this->callAPISuccess('action_schedule', 'create', [
385 'title' => 'job',
386 'subject' => 'job',
387 'entity_value' => 1,
388 'mapping_id' => 4,
389 'start_action_date' => 'membership_join_date',
390 'start_action_offset' => 1,
391 'start_action_condition' => 'after',
392 'start_action_unit' => 'day',
393 'body_html' => $tokenString,
394 ]);
395 $this->callAPISuccess('job', 'send_reminder', []);
d568dbe0
EM
396 $expected = $this->getExpectedMembershipTokenOutput();
397 // Unlike the legacy method custom fields are resolved by the processor.
398 $expected .= "\nmy field";
399 $mut->checkMailLog([$expected]);
400
401 $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
402 'controller' => __CLASS__,
403 'smarty' => FALSE,
404 'schema' => ['membershipId'],
405 ]);
406 $tokens = $tokenProcessor->listTokens();
407 // Add in custom tokens as token processor supports these.
408 $expectedTokens['{membership.custom_1}'] = 'Enter text here :: Group with field text';
3c78698e 409 $this->assertEquals(array_merge($expectedTokens, $this->getDomainTokens()), $tokens);
d568dbe0
EM
410 $tokenProcessor->addMessage('html', $tokenString, 'text/plain');
411 $tokenProcessor->addRow(['membershipId' => $this->getMembershipID()]);
412 $tokenProcessor->evaluate();
413 $this->assertEquals($expected, $tokenProcessor->getRow(0)->render('html'));
414
d41a5d53
EM
415 }
416
417 /**
418 * Get declared membership tokens.
419 *
420 * @return string[]
421 */
422 public function getMembershipTokens(): array {
423 return [
424 '{membership.id}' => 'Membership ID',
eac0a5bf
EM
425 '{membership.status_id:label}' => 'Membership Status',
426 '{membership.membership_type_id:label}' => 'Membership Type',
d41a5d53
EM
427 '{membership.start_date}' => 'Membership Start Date',
428 '{membership.join_date}' => 'Membership Join Date',
429 '{membership.end_date}' => 'Membership End Date',
430 '{membership.fee}' => 'Membership Fee',
431 ];
432 }
433
434 /**
435 * Get case ID.
436 *
437 * @return int
438 */
439 protected function getMembershipID(): int {
440 if (!isset($this->ids['Membership'][0])) {
b024d6a1
EM
441 $this->ids['Membership'][0] = $this->contactMembershipCreate([
442 'contact_id' => $this->getContactID(),
443 $this->getCustomFieldName('text') => 'my field',
444 ]);
d41a5d53
EM
445 }
446 return $this->ids['Membership'][0];
447 }
448
449 /**
450 * Get expected output from token parsing.
451 *
452 * @return string
453 */
454 protected function getExpectedMembershipTokenOutput(): string {
455 return '
456Expired
457General
4581
459Expired
460General
461January 21st, 2007
462January 21st, 2007
463December 21st, 2007
464100.00';
465 }
466
1ed50dc6
EM
467 /**
468 * Test that membership tokens are consistently rendered.
469 *
470 * @throws \API_Exception
471 */
472 public function testParticipantTokenConsistency(): void {
473 $this->createLoggedInUser();
474 $this->createCustomGroupWithFieldOfType(['extends' => 'Participant']);
475 $tokens = CRM_Core_SelectValues::participantTokens();
476 $this->assertEquals($this->getParticipantTokens(), $tokens);
477 }
478
479 /**
3c78698e 480 * Get declared participant tokens.
1ed50dc6
EM
481 *
482 * @return string[]
483 */
484 public function getParticipantTokens(): array {
485 return [
b7472bd6
EM
486 '{participant.status_id}' => 'Status ID',
487 '{participant.role_id}' => 'Participant Role (ID)',
488 '{participant.register_date}' => 'Register date',
489 '{participant.source}' => 'Participant Source',
490 '{participant.fee_level}' => 'Fee level',
491 '{participant.fee_amount}' => 'Fee Amount',
492 '{participant.registered_by_id}' => 'Registered By Participant ID',
1ed50dc6 493 '{participant.transferred_to_contact_id}' => 'Transferred to Contact ID',
b7472bd6 494 '{participant.role_id:label}' => 'Participant Role (label)',
1ed50dc6 495 '{participant.fee_label}' => 'Fee Label',
1ed50dc6
EM
496 '{participant.' . $this->getCustomFieldName('text') . '}' => 'Enter text here :: Group with field text',
497 ];
498 }
499
3c78698e
EM
500 /**
501 * Test that domain tokens are consistently rendered.
502 */
503 public function testDomainTokenConsistency(): void {
504 $tokens = CRM_Core_SelectValues::domainTokens();
505 $this->assertEquals($this->getDomainTokens(), $tokens);
506 $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
507 'controller' => __CLASS__,
508 'smarty' => FALSE,
509 ]);
510 $tokens['{domain.id}'] = 'Domain ID';
511 $tokens['{domain.description}'] = 'Domain Description';
512 $this->assertEquals($tokens, $tokenProcessor->listTokens());
513 }
514
515 /**
516 * Get declared participant tokens.
517 *
518 * @return string[]
519 */
520 public function getDomainTokens(): array {
521 return [
522 '{domain.name}' => ts('Domain name'),
523 '{domain.address}' => ts('Domain (organization) address'),
524 '{domain.phone}' => ts('Domain (organization) phone'),
525 '{domain.email}' => 'Domain (organization) email',
526 '{domain.id}' => ts('Domain ID'),
527 '{domain.description}' => ts('Domain Description'),
528 ];
529 }
530
ce971869
EM
531 /**
532 * Test that domain tokens are consistently rendered.
533 */
534 public function testEventTokenConsistency(): void {
535 $tokens = CRM_Core_SelectValues::eventTokens();
536 $this->assertEquals($this->getEventTokens(), $tokens);
537 $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
538 'controller' => __CLASS__,
539 'smarty' => FALSE,
540 'schema' => ['eventId'],
541 ]);
542 $this->assertEquals(array_merge($tokens, $this->getDomainTokens()), $tokenProcessor->listTokens());
543 }
544
545 /**
546 * Get expected event tokens.
547 *
548 * @return string[]
549 */
550 protected function getEventTokens(): array {
551 return [
552 '{event.event_id}' => 'Event ID',
553 '{event.title}' => 'Event Title',
554 '{event.start_date}' => 'Event Start Date',
555 '{event.end_date}' => 'Event End Date',
556 '{event.event_type}' => 'Event Type',
557 '{event.summary}' => 'Event Summary',
558 '{event.contact_email}' => 'Event Contact Email',
559 '{event.contact_phone}' => 'Event Contact Phone',
560 '{event.description}' => 'Event Description',
561 '{event.location}' => 'Event Location',
562 '{event.fee_amount}' => 'Event Fee',
563 '{event.info_url}' => 'Event Info URL',
564 '{event.registration_url}' => 'Event Registration URL',
565 '{event.balance}' => 'Event Balance',
566 ];
567 }
568
d0ce76fd 569}