Merge pull request #17236 from civicrm/5.25
[civicrm-core.git] / tests / phpunit / Civi / Token / TokenProcessorTest.php
1 <?php
2 namespace Civi\Token;
3
4 use Civi\Token\Event\TokenRegisterEvent;
5 use Civi\Token\Event\TokenValueEvent;
6 use Symfony\Component\EventDispatcher\EventDispatcher;
7
8 class TokenProcessorTest extends \CiviUnitTestCase {
9
10 /**
11 * @var \Symfony\Component\EventDispatcher\EventDispatcher
12 */
13 protected $dispatcher;
14
15 /**
16 * @var array
17 * Array(string $funcName => int $invocationCount).
18 */
19 protected $counts;
20
21 protected function setUp() {
22 $this->useTransaction(TRUE);
23 parent::setUp();
24 $this->dispatcher = new EventDispatcher();
25 $this->dispatcher->addListener(Events::TOKEN_REGISTER, [$this, 'onListTokens']);
26 $this->dispatcher->addListener(Events::TOKEN_EVALUATE, [$this, 'onEvalTokens']);
27 $this->counts = [
28 'onListTokens' => 0,
29 'onEvalTokens' => 0,
30 ];
31 }
32
33 /**
34 * Test that a row can be added via "addRow(array $context)".
35 */
36 public function testAddRow() {
37 $p = new TokenProcessor($this->dispatcher, [
38 'controller' => __CLASS__,
39 ]);
40 $createdRow = $p->addRow(['one' => 'Apple'])
41 ->context('two', 'Banana');
42 $gotRow = $p->getRow(0);
43 foreach ([$createdRow, $gotRow] as $row) {
44 $this->assertEquals('Apple', $row->context['one']);
45 $this->assertEquals('Banana', $row->context['two']);
46 }
47 }
48
49 /**
50 * Test that multiple rows can be added via "addRows(array $contexts)".
51 */
52 public function testAddRows() {
53 $p = new TokenProcessor($this->dispatcher, [
54 'controller' => __CLASS__,
55 ]);
56 $createdRows = $p->addRows([
57 ['one' => 'Apple', 'two' => 'Banana'],
58 ['one' => 'Pomme', 'two' => 'Banane'],
59 ]);
60 $gotRow0 = $p->getRow(0);
61 foreach ([$createdRows[0], $gotRow0] as $row) {
62 $this->assertEquals('Apple', $row->context['one']);
63 $this->assertEquals('Banana', $row->context['two']);
64 }
65 $gotRow1 = $p->getRow(1);
66 foreach ([$createdRows[1], $gotRow1] as $row) {
67 $this->assertEquals('Pomme', $row->context['one']);
68 $this->assertEquals('Banane', $row->context['two']);
69 }
70 }
71
72 /**
73 * Check that the TokenRow helper can correctly read/update context
74 * values.
75 */
76 public function testRowContext() {
77 $p = new TokenProcessor($this->dispatcher, [
78 'controller' => __CLASS__,
79 'omega' => '99',
80 ]);
81 $createdRow = $p->addRow()
82 ->context('one', 1)
83 ->context('two', [2 => 3])
84 ->context([
85 'two' => [4 => 5],
86 'three' => [6 => 7],
87 'omega' => '98',
88 ]);
89 $gotRow = $p->getRow(0);
90 foreach ([$createdRow, $gotRow] as $row) {
91 $this->assertEquals(1, $row->context['one']);
92 $this->assertEquals(3, $row->context['two'][2]);
93 $this->assertEquals(5, $row->context['two'][4]);
94 $this->assertEquals(7, $row->context['three'][6]);
95 $this->assertEquals(98, $row->context['omega']);
96 $this->assertEquals(__CLASS__, $row->context['controller']);
97 }
98 }
99
100 /**
101 * Check that getContextValues() returns the correct data
102 */
103 public function testGetContextValues() {
104 $p = new TokenProcessor($this->dispatcher, [
105 'controller' => __CLASS__,
106 'omega' => '99',
107 ]);
108 $p->addRow()->context('id', 10)->context('omega', '98');
109 $p->addRow()->context('id', 10)->context('contact', (object) ['cid' => 10]);
110 $p->addRow()->context('id', 11)->context('contact', (object) ['cid' => 11]);
111 $this->assertArrayValuesEqual([10, 11], $p->getContextValues('id'));
112 $this->assertArrayValuesEqual(['99', '98'], $p->getContextValues('omega'));
113 $this->assertArrayValuesEqual([10, 11], $p->getContextValues('contact', 'cid'));
114 }
115
116 /**
117 * Check that the TokenRow helper can correctly read/update token
118 * values.
119 */
120 public function testRowTokens() {
121 $p = new TokenProcessor($this->dispatcher, [
122 'controller' => __CLASS__,
123 ]);
124 $createdRow = $p->addRow()
125 ->tokens('one', 1)
126 ->tokens('two', [2 => 3])
127 ->tokens([
128 'two' => [4 => 5],
129 'three' => [6 => 7],
130 ])
131 ->tokens('four', 8, 9);
132 $gotRow = $p->getRow(0);
133 foreach ([$createdRow, $gotRow] as $row) {
134 $this->assertEquals(1, $row->tokens['one']);
135 $this->assertEquals(3, $row->tokens['two'][2]);
136 $this->assertEquals(5, $row->tokens['two'][4]);
137 $this->assertEquals(7, $row->tokens['three'][6]);
138 $this->assertEquals(9, $row->tokens['four'][8]);
139 }
140 }
141
142 public function testGetMessageTokens() {
143 $p = new TokenProcessor($this->dispatcher, [
144 'controller' => __CLASS__,
145 ]);
146 $p->addMessage('greeting_html', 'Good morning, <p>{contact.display_name}</p>. {custom.foobar}!', 'text/html');
147 $p->addMessage('greeting_text', 'Good morning, {contact.display_name}. {custom.whizbang}, {contact.first_name}!', 'text/plain');
148 $expected = [
149 'contact' => ['display_name', 'first_name'],
150 'custom' => ['foobar', 'whizbang'],
151 ];
152 $this->assertEquals($expected, $p->getMessageTokens());
153 }
154
155 public function testListTokens() {
156 $p = new TokenProcessor($this->dispatcher, [
157 'controller' => __CLASS__,
158 ]);
159 $p->addToken(['entity' => 'MyEntity', 'field' => 'myField', 'label' => 'My Label']);
160 $this->assertEquals(['{MyEntity.myField}' => 'My Label'], $p->listTokens());
161 }
162
163 /**
164 * Perform a full mail-merge, substituting multiple tokens for multiple
165 * contacts in multiple messages.
166 */
167 public function testFull() {
168 $p = new TokenProcessor($this->dispatcher, [
169 'controller' => __CLASS__,
170 ]);
171 $p->addMessage('greeting_html', 'Good morning, <p>{contact.display_name}</p>. {custom.foobar} Bye!', 'text/html');
172 $p->addMessage('greeting_text', 'Good morning, {contact.display_name}. {custom.foobar} Bye!', 'text/plain');
173 $p->addRow()
174 ->context(['contact_id' => 123])
175 ->format('text/plain')->tokens([
176 'contact' => ['display_name' => 'What'],
177 ]);
178 $p->addRow()
179 ->context(['contact_id' => 4])
180 ->format('text/plain')->tokens([
181 'contact' => ['display_name' => 'Who'],
182 ]);
183 $p->addRow()
184 ->context(['contact_id' => 10])
185 ->format('text/plain')->tokens([
186 'contact' => ['display_name' => 'Darth Vader'],
187 ]);
188
189 $expectHtml = [
190 0 => 'Good morning, <p>What</p>. #0123 is a good number. Trickster {contact.display_name}. Bye!',
191 1 => 'Good morning, <p>Who</p>. #0004 is a good number. Trickster {contact.display_name}. Bye!',
192 2 => 'Good morning, <p>Darth Vader</p>. #0010 is a good number. Trickster {contact.display_name}. Bye!',
193 ];
194
195 $expectText = [
196 0 => 'Good morning, What. #0123 is a good number. Trickster {contact.display_name}. Bye!',
197 1 => 'Good morning, Who. #0004 is a good number. Trickster {contact.display_name}. Bye!',
198 2 => 'Good morning, Darth Vader. #0010 is a good number. Trickster {contact.display_name}. Bye!',
199 ];
200
201 $rowCount = 0;
202 foreach ($p->evaluate()->getRows() as $key => $row) {
203 /** @var TokenRow */
204 $this->assertTrue($row instanceof TokenRow);
205 $this->assertEquals($expectHtml[$key], $row->render('greeting_html'));
206 $this->assertEquals($expectText[$key], $row->render('greeting_text'));
207 $rowCount++;
208 }
209 $this->assertEquals(3, $rowCount);
210 // This may change in the future.
211 $this->assertEquals(0, $this->counts['onListTokens']);
212 $this->assertEquals(1, $this->counts['onEvalTokens']);
213 }
214
215 public function onListTokens(TokenRegisterEvent $e) {
216 $this->counts[__FUNCTION__]++;
217 $e->register('custom', [
218 'foobar' => 'A special message about foobar',
219 ]);
220 }
221
222 public function onEvalTokens(TokenValueEvent $e) {
223 $this->counts[__FUNCTION__]++;
224 foreach ($e->getRows() as $row) {
225 /** @var TokenRow $row */
226 $row->format('text/html');
227 $row->tokens['custom']['foobar'] = sprintf("#%04d is a good number. Trickster {contact.display_name}.", $row->context['contact_id']);
228 }
229 }
230
231 }