65d27ad8 |
1 | <?php |
2 | |
3 | /** |
65d27ad8 |
4 | * This file is part of CiviCRM |
5 | * |
6 | * CiviCRM is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Affero General Public License |
8 | * as published by the Free Software Foundation; either version 3 of |
9 | * the License, or (at your option) any later version. |
10 | * |
11 | * CiviCRM is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU Affero General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Affero General Public |
17 | * License along with this program. If not, see |
18 | * <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | /** |
a5280e5d |
22 | * Test Activity Import Parser functions |
65d27ad8 |
23 | * |
24 | * @package CiviCRM |
25 | * @group headless |
26 | */ |
27 | class CRM_Activity_Import_Parser_ActivityTest extends CiviUnitTestCase { |
28 | use CRMTraits_Custom_CustomDataTrait; |
29 | |
30 | /** |
31 | * Prepare for tests. |
32 | */ |
33 | public function setUp():void { |
34 | parent::setUp(); |
35 | $this->createLoggedInUser(); |
36 | } |
37 | |
38 | /** |
39 | * Clean up after test. |
40 | * |
41 | * @throws \CRM_Core_Exception |
42 | */ |
43 | public function tearDown():void { |
a5280e5d |
44 | $this->quickCleanup(['civicrm_contact', 'civicrm_activity', 'civicrm_activity_contact'], TRUE); |
65d27ad8 |
45 | parent::tearDown(); |
46 | } |
47 | |
48 | /** |
49 | * Test Import. |
50 | * |
51 | * So far this is just testing the class constructor & preparing for more |
52 | * tests. |
53 | * |
54 | * @throws \API_Exception |
55 | * @throws \CRM_Core_Exception |
56 | * @throws \CiviCRM_API3_Exception |
57 | */ |
58 | public function testImport(): void { |
59 | $this->createCustomGroupWithFieldOfType(['extends' => 'Activity'], 'checkbox'); |
60 | $values = [ |
a5280e5d |
61 | 'activity_details' => 'fascinating', |
65d27ad8 |
62 | 'activity_type_id' => 1, |
63 | 'activity_date_time' => '2010-01-06', |
64 | 'target_contact_id' => $this->individualCreate(), |
a5280e5d |
65 | 'activity_subject' => 'riveting stuff', |
65d27ad8 |
66 | $this->getCustomFieldName('checkbox') => 'L', |
67 | ]; |
68 | $this->importValues($values); |
69 | $this->callAPISuccessGetSingle('Activity', [$this->getCustomFieldName('checkbox') => 'L']); |
70 | } |
71 | |
72 | /** |
73 | * Create an import object. |
74 | * |
75 | * @param array $fields |
76 | * |
77 | * @return \CRM_Activity_Import_Parser_Activity |
78 | */ |
79 | protected function createImportObject(array $fields): \CRM_Activity_Import_Parser_Activity { |
a5280e5d |
80 | // @todo Eyes are weary so sanity-check this later: |
81 | // This loop seems the same as array_values($fields)? And this appears |
82 | // to only be called from one place that already has them sequentially |
83 | // indexed so is it even needed? |
65d27ad8 |
84 | $fieldMapper = []; |
85 | foreach ($fields as $index => $field) { |
86 | $fieldMapper[] = $field; |
87 | } |
88 | $importer = new CRM_Activity_Import_Parser_Activity($fieldMapper); |
89 | $importer->init(); |
90 | return $importer; |
91 | } |
92 | |
93 | /** |
94 | * Run the importer. |
95 | * |
96 | * @param array $values |
97 | * @param int $expectedOutcome |
72920c91 |
98 | * @return string The error message |
65d27ad8 |
99 | */ |
72920c91 |
100 | protected function importValues(array $values, $expectedOutcome = 1): string { |
65d27ad8 |
101 | $importer = $this->createImportObject(array_keys($values)); |
102 | $params = array_values($values); |
103 | CRM_Core_Session::singleton()->set('dateTypes', 1); |
72920c91 |
104 | $outcome = $importer->import(NULL, $params); |
105 | $this->assertEquals($expectedOutcome, $outcome); |
106 | // If there was an error it's in element 0 |
107 | return $outcome === CRM_Import_Parser::VALID ? '' : $params[0]; |
108 | } |
109 | |
110 | /** |
111 | * Test validation of various fields. |
112 | * |
113 | * @dataProvider activityImportValidationProvider |
114 | * @param array $input |
115 | * @param string $expectedError |
116 | */ |
117 | public function testActivityImportValidation(array $input, string $expectedError): void { |
118 | // Supplement some values that can't be done in a data provider because of timing. |
119 | if (!isset($input['target_contact_id'])) { |
120 | $input['target_contact_id'] = $this->individualCreate(); |
121 | } |
122 | if (isset($input['replace_me_custom_field'])) { |
123 | $this->createCustomGroupWithFieldOfType(['extends' => 'Activity'], 'radio'); |
124 | $input[$this->getCustomFieldName('radio')] = $input['replace_me_custom_field']; |
125 | unset($input['replace_me_custom_field']); |
126 | } |
127 | |
128 | // There's both an outcome int, like VALID or ERROR, which importValues |
129 | // checks, and then an error string which we check. If we're not expecting |
130 | // an error string, then tell importValues the expected outcome is VALID. |
131 | $actualError = $this->importValues($input, |
132 | empty($expectedError) ? CRM_Import_Parser::VALID : CRM_Import_Parser::ERROR); |
133 | $this->assertStringContainsString($expectedError, $actualError); |
134 | } |
135 | |
136 | /** |
137 | * Dataprovider for some import tests. |
138 | * @return array |
139 | */ |
140 | public function activityImportValidationProvider(): array { |
141 | /** |
142 | * Because this is a dataprovider that runs before setup, we |
143 | * can't specify values that don't exist yet, but we're mostly |
144 | * testing validation of just the specified ones anyway. The test itself |
145 | * will need to fill in the rest. |
146 | */ |
147 | |
148 | // This needs to be a constant for the reasons above, but this |
149 | // might make it easier to update in future if needed. |
150 | $some_date = '2021-02-06'; |
151 | |
152 | return [ |
153 | // explicit index number so easier to find when it fails |
154 | 0 => [ |
155 | 'input' => [ |
156 | 'activity_type_id' => 1, |
157 | 'activity_date_time' => $some_date, |
158 | 'activity_subject' => 'asubj', |
159 | ], |
160 | 'expected_error' => '', |
161 | ], |
162 | |
163 | 1 => [ |
164 | 'input' => [ |
165 | 'activity_type_id' => 1, |
166 | 'activity_date_time' => $some_date, |
167 | 'activity_subject' => 'asubj', |
168 | 'replace_me_custom_field' => '3', |
169 | ], |
170 | 'expected_error' => '', |
171 | ], |
172 | |
173 | 2 => [ |
174 | 'input' => [ |
175 | 'activity_label' => 'Meeting', |
176 | 'activity_date_time' => $some_date, |
177 | 'activity_subject' => 'asubj', |
178 | ], |
179 | 'expected_error' => '', |
180 | ], |
181 | |
182 | 3 => [ |
183 | 'input' => [ |
184 | 'activity_type_id' => 1, |
185 | 'activity_label' => 'Meeting', |
186 | 'activity_date_time' => $some_date, |
187 | 'activity_subject' => 'asubj', |
188 | ], |
189 | 'expected_error' => '', |
190 | ], |
191 | |
192 | 4 => [ |
193 | 'input' => [ |
194 | 'activity_type_id' => 2, |
195 | 'activity_label' => 'Meeting', |
196 | 'activity_date_time' => $some_date, |
197 | 'activity_subject' => 'asubj', |
198 | ], |
199 | 'expected_error' => 'Activity type label and Activity type ID are in conflict', |
200 | ], |
201 | |
202 | 5 => [ |
203 | 'input' => [ |
204 | 'activity_type_id' => 1, |
205 | 'activity_label' => '', |
206 | 'activity_date_time' => $some_date, |
207 | 'activity_subject' => 'asubj', |
208 | ], |
209 | 'expected_error' => '', |
210 | ], |
211 | |
212 | 6 => [ |
213 | 'input' => [ |
214 | 'activity_type_id' => '', |
215 | 'activity_label' => 'Meeting', |
216 | 'activity_date_time' => $some_date, |
217 | 'activity_subject' => 'asubj', |
218 | ], |
219 | 'expected_error' => '', |
220 | ], |
221 | |
222 | 7 => [ |
223 | 'input' => [ |
224 | 'activity_date_time' => $some_date, |
225 | 'activity_subject' => 'asubj', |
226 | ], |
227 | 'expected_error' => 'Missing required fields', |
228 | ], |
229 | |
230 | 8 => [ |
231 | 'input' => [ |
232 | 'activity_type_id' => '', |
233 | 'activity_label' => '', |
234 | 'activity_date_time' => $some_date, |
235 | 'activity_subject' => 'asubj', |
236 | ], |
237 | 'expected_error' => 'Missing required fields', |
238 | ], |
239 | |
240 | 9 => [ |
241 | 'input' => [ |
242 | 'activity_label' => 'Meeting', |
243 | 'activity_subject' => 'asubj', |
244 | ], |
245 | 'expected_error' => 'Missing required fields', |
246 | ], |
247 | |
248 | 10 => [ |
249 | 'input' => [ |
250 | 'activity_label' => 'Meeting', |
251 | 'activity_date_time' => '', |
252 | 'activity_subject' => 'asubj', |
253 | ], |
254 | 'expected_error' => 'Missing required fields', |
255 | ], |
256 | |
257 | // @todo: This is inconsistent. Subject is required in the map UI but not |
258 | // on import. Should subject be required? Personally I think the import |
259 | // is correct and it shouldn't be required in UI. |
260 | 11 => [ |
261 | 'input' => [ |
262 | 'activity_label' => 'Meeting', |
263 | 'activity_date_time' => $some_date, |
264 | ], |
265 | 'expected_error' => '', |
266 | ], |
267 | |
268 | // @todo: This is inconsistent. Subject is required in the map UI but not |
269 | // on import. Should subject be required? Personally I think the import |
270 | // is correct and it shouldn't be required in UI. |
271 | 12 => [ |
272 | 'input' => [ |
273 | 'activity_label' => 'Meeting', |
274 | 'activity_date_time' => $some_date, |
275 | 'activity_subject' => '', |
276 | ], |
277 | 'expected_error' => '', |
278 | ], |
279 | |
280 | 13 => [ |
281 | 'input' => [ |
282 | 'activity_label' => 'Meeting', |
283 | 'activity_date_time' => $some_date, |
284 | 'activity_subject' => 'asubj', |
285 | 'replace_me_custom_field' => 'InvalidValue', |
286 | ], |
287 | 'expected_error' => 'Invalid value for field(s) : Integer radio', |
288 | ], |
289 | |
290 | 14 => [ |
291 | 'input' => [ |
292 | 'activity_label' => 'Meeting', |
293 | 'activity_date_time' => $some_date, |
294 | 'activity_subject' => 'asubj', |
295 | 'replace_me_custom_field' => '', |
296 | ], |
297 | 'expected_error' => '', |
298 | ], |
299 | |
300 | // @todo This is also inconsistent. The map UI requires target contact |
301 | // but import is fine leaving it blank. In general civi is fine with |
302 | // a blank target so possibly map UI should not require it. |
303 | 15 => [ |
304 | 'input' => [ |
305 | 'target_contact_id' => '', |
306 | 'activity_label' => 'Meeting', |
307 | 'activity_date_time' => $some_date, |
308 | 'activity_subject' => 'asubj', |
309 | ], |
310 | 'expected_error' => '', |
311 | ], |
312 | |
313 | ]; |
65d27ad8 |
314 | } |
315 | |
316 | } |