tests for activity import
[civicrm-core.git] / tests / phpunit / CRM / Activity / Import / Parser / ActivityTest.php
CommitLineData
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 */
27class 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}