Test & align empty token handling
[civicrm-core.git] / tests / phpunit / CRM / Utils / TokenConsistencyTest.php
index 0f79c174c1ab0f3d53c79110159e58dbde93e24b..7e7f00c1f14beb690744372264e0320914d4d179 100644 (file)
@@ -72,21 +72,12 @@ class CRM_Utils_TokenConsistencyTest extends CiviUnitTestCase {
     // And check our deprecated tokens still work.
     $tokenHtml = CRM_Utils_Token::replaceCaseTokens($caseID, '{case.case_type_id} {case.status_id}');
     $this->assertEquals('Housing Support Ongoing', $tokenHtml);
-
-    $additionalTokensFromProcessor = [
-      '{case.case_type_id}' => 'Case Type ID',
-      '{case.status_id}' => 'Case Status',
-      '{case.case_type_id:name}' => 'Machine name: Case Type',
-      '{case.status_id:name}' => 'Machine name: Case Status',
-    ];
-    $expectedTokens = array_merge($this->getCaseTokens(), $additionalTokensFromProcessor);
-
     $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
       'controller' => __CLASS__,
       'smarty' => FALSE,
       'schema' => ['caseId'],
     ]);
-    $this->assertEquals(array_merge($expectedTokens, $this->getDomainTokens()), $tokenProcessor->listTokens());
+    $this->assertEquals(array_merge($this->getCaseTokens(), $this->getDomainTokens()), $tokenProcessor->listTokens());
     $tokenProcessor->addRow([
       'caseId' => $this->getCaseID(),
     ]);
@@ -205,7 +196,8 @@ case.custom_1 :' . '
       'smarty' => FALSE,
       'schema' => ['contribution_recurId'],
     ]);
-    $this->assertEquals(array_merge($this->getContributionRecurTokens(), $this->getDomainTokens()), $tokenProcessor->listTokens());
+    $expectedTokens = array_merge($this->getContributionRecurTokens(), $this->getDomainTokens());
+    $this->assertEquals(array_diff_key($expectedTokens, $this->getUnadvertisedTokens()), $tokenProcessor->listTokens());
     $tokenString = $this->getTokenString(array_keys($this->getContributionRecurTokens()));
 
     $tokenProcessor->addMessage('html', $tokenString, 'text/plain');
@@ -214,6 +206,73 @@ case.custom_1 :' . '
     $this->assertEquals($this->getExpectedContributionRecurTokenOutPut(), $tokenProcessor->getRow(0)->render('html'));
   }
 
+  /**
+   * Get tokens that are not advertised via listTokens.
+   *
+   * @return string[]
+   */
+  public function getUnadvertisedTokens(): array {
+    return [
+      '{membership.status_id}' => 'Status ID',
+      '{membership.membership_type_id}' => 'Membership Type ID',
+      '{membership.status_id:name}' => 'Machine name: Status',
+      '{membership.membership_type_id:name}' => 'Machine name: Membership Type',
+      '{contribution_recur.frequency_unit}' => 'Frequency Unit',
+      '{contribution_recur.contribution_status_id}' => 'Status',
+      '{contribution_recur.payment_processor_id}' => 'Payment Processor ID',
+      '{contribution_recur.financial_type_id}' => 'Financial Type ID',
+      '{contribution_recur.payment_instrument_id}' => 'Payment Method',
+      '{contribution_recur.frequency_unit:name}' => 'Machine name: Frequency Unit',
+      '{contribution_recur.payment_instrument_id:name}' => 'Machine name: Payment Method',
+      '{contribution_recur.contribution_status_id:name}' => 'Machine name: Status',
+      '{contribution_recur.payment_processor_id:name}' => 'Machine name: Payment Processor',
+      '{contribution_recur.financial_type_id:name}' => 'Machine name: Financial Type',
+      '{participant.status_id:name}' => 'Machine name: Status',
+      '{participant.role_id:name}' => 'Machine name: Participant Role',
+      '{participant.status_id}' => 'Status ID',
+      '{participant.role_id}' => 'Participant Role ID',
+    ];
+  }
+
+  /**
+   * Test tokens in 2 ways to ensure consistent handling.
+   *
+   * 1) as part of the greeting processing
+   * 2) via the token processor.
+   *
+   */
+  public function testOddTokens(): void {
+
+    $variants = [
+      [
+        'string' => '{contact.individual_prefix}{ }{contact.first_name}{ }{contact.middle_name}{ }{contact.last_name}{ }{contact.individual_suffix}',
+        'expected' => 'Mr. Anthony  Anderson II',
+      ],
+      [
+        'string' => '{contact.prefix_id:label}{ }{contact.first_name}{ }{contact.middle_name}{ }{contact.last_name}{ }{contact.suffix_id:label}',
+        'expected' => 'Mr. Anthony  Anderson II',
+      ],
+    ];
+    $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
+      'smarty' => FALSE,
+      'schema' => ['contactId'],
+    ]);
+    $contactID = $this->individualCreate(['middle_name' => '']);
+    $tokenProcessor->addRow(['contactId' => $contactID]);
+    $tokenProcessor->evaluate();
+    foreach ($variants as $index => $variant) {
+      $tokenProcessor->addMessage($index, $variant['string'], 'text/plain');
+    }
+    $tokenProcessor->evaluate();
+    $result = $tokenProcessor->getRow(0);
+    foreach ($variants as $index => $variant) {
+      $greetingString = $variant['string'];
+      CRM_Utils_Token::replaceGreetingTokens($greetingString, $this->callAPISuccessGetSingle('Contact', ['id' => $contactID]), $contactID);
+      $this->assertEquals($variant['expected'], $greetingString);
+      $this->assertEquals($variant['expected'], $result->render($index));
+    }
+  }
+
   /**
    * Get the contribution recur tokens keyed by the token.
    *
@@ -410,7 +469,7 @@ contribution_recur.payment_instrument_id:name :Check
     ]);
     $tokens = $tokenProcessor->listTokens();
     // Add in custom tokens as token processor supports these.
-    $expectedTokens['{membership.custom_1}'] = 'Enter text here :: Group with field text';
+    $expectedTokens = array_merge($expectedTokens, $this->getTokensAdvertisedByTokenProcessorButNotLegacy());
     $this->assertEquals(array_merge($expectedTokens, $this->getDomainTokens()), $tokens);
     $tokenProcessor->addMessage('html', $tokenString, 'text/plain');
     $tokenProcessor->addRow(['membershipId' => $this->getMembershipID()]);
@@ -419,6 +478,19 @@ contribution_recur.payment_instrument_id:name :Check
 
   }
 
+  /**
+   * Get the advertised tokens the legacy function doesn't know about.
+   *
+   * @return string[]
+   */
+  public function getTokensAdvertisedByTokenProcessorButNotLegacy(): array {
+    return [
+      '{membership.custom_1}' => 'Enter text here :: Group with field text',
+      '{membership.source}' => 'Source',
+      '{membership.status_override_end_date}' => 'Status Override End Date',
+    ];
+  }
+
   /**
    * Get declared membership tokens.
    *
@@ -531,7 +603,7 @@ December 21st, 2007
     $this->setupParticipantScheduledReminder();
 
     $tokens = CRM_Core_SelectValues::participantTokens();
-    $this->assertEquals($this->getParticipantTokens(), $tokens);
+    $this->assertEquals(array_diff_key($this->getParticipantTokens(), $this->getUnadvertisedTokens()), $tokens);
 
     $mut = new CiviMailUtils($this);
 
@@ -553,6 +625,24 @@ December 21st, 2007
 
   }
 
+  /**
+   * Test that membership tokens are consistently rendered.
+   *
+   * @throws \API_Exception
+   * @throws \CRM_Core_Exception
+   */
+  public function testParticipantCustomDateToken(): void {
+    $this->createEventAndParticipant();
+    $dateFieldID = $this->createDateCustomField(['custom_group_id' => $this->ids['CustomGroup']['participant_'], 'default_value' => ''])['id'];
+    $input = '{participant.custom_' . $dateFieldID . '}';
+    $input .= '{participant.' . $this->getCustomFieldName('participant_int') . '}';
+    $tokenHtml = CRM_Core_BAO_MessageTemplate::renderTemplate([
+      'messageTemplate' => ['msg_html' => $input],
+      'tokenContext' => array_merge(['participantId' => $this->ids['participant'][0]], ['schema' => ['participantId', 'eventId']]),
+    ])['html'];
+    $this->assertEquals(99999, $tokenHtml);
+  }
+
   /**
    * Get declared participant tokens.
    *
@@ -665,7 +755,7 @@ December 21st, 2007
     $this->setupParticipantScheduledReminder();
 
     $tokens = CRM_Core_SelectValues::eventTokens();
-    $this->assertEquals($this->getEventTokens(), $tokens);
+    $this->assertEquals(array_merge($this->getEventTokens()), $tokens);
     $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
       'controller' => __CLASS__,
       'smarty' => FALSE,
@@ -725,45 +815,12 @@ December 21st, 2007
    * @throws \API_Exception
    */
   public function setupParticipantScheduledReminder($includeParticipant = TRUE): void {
-    $this->createCustomGroupWithFieldOfType(['extends' => 'Event']);
-    $this->createCustomGroupWithFieldOfType(['extends' => 'Participant'], 'int', 'participant_');
-    $html = '';
+    $this->createEventAndParticipant();
     $tokens = array_keys($this->getEventTokens());
     if ($includeParticipant) {
       $tokens = array_keys(array_merge($this->getEventTokens(), $this->getParticipantTokens()));
     }
     $html = $this->getTokenString($tokens);
-    $emailID = Email::create()->setValues(['email' => 'event@example.com'])->execute()->first()['id'];
-    $addressID = Address::create()->setValues([
-      'street_address' => '15 Walton St',
-      'supplemental_address_1' => 'up the road',
-      'city' => 'Emerald City',
-      'state_province_id:label' => 'Maine',
-      'postal_code' => 90210,
-    ])->execute()->first()['id'];
-    $phoneID = Phone::create()->setValues(['phone' => '456 789'])->execute()->first()['id'];
-
-    $locationBlockID = LocBlock::save(FALSE)->setRecords([
-      [
-        'email_id' => $emailID,
-        'address_id' => $addressID,
-        'phone_id' => $phoneID,
-      ],
-    ])->execute()->first()['id'];
-    $this->ids['event'][0] = $this->eventCreate([
-      'description' => 'event description',
-      $this->getCustomFieldName('text') => 'my field',
-      'loc_block_id' => $locationBlockID,
-    ])['id'];
-    // Create an unrelated participant record so that the ids don't match.
-    // this prevents things working just because the id 'happens to be valid'
-    $this->participantCreate(['register_date' => '2020-01-01', 'event_id' => $this->ids['event'][0]]);
-    $this->ids['participant'][0] = $this->participantCreate([
-      'event_id' => $this->ids['event'][0],
-      'fee_amount' => 50,
-      'fee_level' => 'steep',
-      $this->getCustomFieldName('participant_int') => '99999',
-    ]);
     CRM_Utils_Time::setTime('2007-02-20 15:00:00');
     $this->callAPISuccess('action_schedule', 'create', [
       'title' => 'job',
@@ -814,4 +871,54 @@ December 21st, 2007
     return $html;
   }
 
+  /**
+   * Create an event with a participant.
+   *
+   * @throws \API_Exception
+   */
+  protected function createEventAndParticipant(): void {
+    $this->createCustomGroupWithFieldOfType(['extends' => 'Event']);
+    $this->createCustomGroupWithFieldOfType(['extends' => 'Participant'], 'int', 'participant_');
+    $emailID = Email::create()
+      ->setValues(['email' => 'event@example.com'])
+      ->execute()
+      ->first()['id'];
+    $addressID = Address::create()->setValues([
+      'street_address' => '15 Walton St',
+      'supplemental_address_1' => 'up the road',
+      'city' => 'Emerald City',
+      'state_province_id:label' => 'Maine',
+      'postal_code' => 90210,
+    ])->execute()->first()['id'];
+    $phoneID = Phone::create()
+      ->setValues(['phone' => '456 789'])
+      ->execute()
+      ->first()['id'];
+
+    $locationBlockID = LocBlock::save(FALSE)->setRecords([
+      [
+        'email_id' => $emailID,
+        'address_id' => $addressID,
+        'phone_id' => $phoneID,
+      ],
+    ])->execute()->first()['id'];
+    $this->ids['event'][0] = $this->eventCreate([
+      'description' => 'event description',
+      $this->getCustomFieldName('text') => 'my field',
+      'loc_block_id' => $locationBlockID,
+    ])['id'];
+    // Create an unrelated participant record so that the ids don't match.
+    // this prevents things working just because the id 'happens to be valid'
+    $this->participantCreate([
+      'register_date' => '2020-01-01',
+      'event_id' => $this->ids['event'][0],
+    ]);
+    $this->ids['participant'][0] = $this->participantCreate([
+      'event_id' => $this->ids['event'][0],
+      'fee_amount' => 50,
+      'fee_level' => 'steep',
+      $this->getCustomFieldName('participant_int') => '99999',
+    ]);
+  }
+
 }