INFRA-132 - FunctionDeclarationArgumentSpacing.SpaceBeforeEquals
[civicrm-core.git] / tests / phpunit / WebTest / Import / ImportCiviSeleniumTestCase.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
06a1bc01 6 | Copyright CiviCRM LLC (c) 2004-2014 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License along with this program; if not, contact CiviCRM LLC |
21 | at info[AT]civicrm[DOT]org. If you have questions about the |
22 | GNU Affero General Public License or the licensing of CiviCRM, |
23 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
24 +--------------------------------------------------------------------+
25*/
26
6a488035
TO
27require_once 'CiviTest/CiviSeleniumTestCase.php';
28require_once 'CRM/Utils/Array.php';
e9479dcf
EM
29
30/**
31 * Class ImportCiviSeleniumTestCase
32 */
6a488035
TO
33class ImportCiviSeleniumTestCase extends CiviSeleniumTestCase {
34
4cbe18b8 35 /**
100fef9d 36 * Test csv import for each component.
c490a46a 37 *
e16033b4
TO
38 * @param string $component
39 * Component name ( Event, Contribution, Membership, Activity etc).
40 * @param array $headers
41 * Csv data headers.
42 * @param array $rows
43 * Csv data rows.
44 * @param string $contactType
45 * Contact type.
46 * @param string $mode
47 * Import mode.
48 * @param array $fieldMapper
49 * Select mapper fields while import.
50 * @param array $other
51 * Other parameters.
c490a46a
CW
52 * useMappingName : to reuse mapping
53
54 * dateFormat : date format of data
55 * checkMapperHeaders : to override default check mapper headers
56 * saveMapping : save current mapping?
57 * saveMappingName : to override mapping name
4cbe18b8 58 */
5896d037
TO
59 function importCSVComponent(
60 $component,
6a488035
TO
61 $headers,
62 $rows,
63 $contactType = 'Individual',
6c6e6187 64 $mode = 'Skip',
6a488035 65 $fieldMapper = array(),
6c6e6187 66 $other = array()
6a488035
TO
67 ) {
68
69 // Go to contact import page.
beea38fc 70 $this->openCiviPage($this->_getImportComponentUrl($component), 'reset=1', "uploadFile");
6a488035
TO
71
72 // Create csv file of sample data.
73 $csvFile = $this->webtestCreateCSV($headers, $rows);
74
75 // Attach csv file.
76 $this->webtestAttachFile('uploadFile', $csvFile);
77
78 // First row is header.
79 $this->click('skipColumnHeader');
80
81 // select mode, default is 'Skip'.
82 if ($mode == 'Update') {
83 $this->click("CIVICRM_QFID_4_4");
84 }
85 elseif ($mode == 'No Duplicate Checking') {
86 $this->click("CIVICRM_QFID_16_6");
87 }
88
89 // select contact type, default is 'Individual'.
90 if ($component != 'Activity') {
91 $contactTypeOption = $this->_getImportComponentContactType($component, $contactType);
92 $this->click($contactTypeOption);
93 }
94
95 // Date format, default: yyyy-mm-dd OR yyyymmdd
96 if (isset($other['dateFormat'])) {
97 // default
98 $dateFormatMapper = array(
99 'yyyy-mm-dd OR yyyymmdd' => "CIVICRM_QFID_1_14",
100 'mm/dd/yy OR mm-dd-yy' => "CIVICRM_QFID_2_16",
101 'mm/dd/yyyy OR mm-dd-yyyy' => "CIVICRM_QFID_4_18",
102 'Month dd, yyyy' => "CIVICRM_QFID_8_20",
103 'dd-mon-yy OR dd/mm/yy' => "CIVICRM_QFID_16_22",
104 'dd/mm/yyyy' => "CIVICRM_QFID_32_24",
105 );
106 $this->click($dateFormatMapper[$other['dateFormat']]);
107 }
108
109 // Use already created mapping
110 $existingMapping = NULL;
111 if (isset($other['useMappingName'])) {
112 $this->select('savedMapping', "label=" . $other['useMappingName']);
113 $existingMapping = $other['useMappingName'];
114 }
115
116 // Submit form.
ab179423 117 $this->click('_qf_DataSource_upload');
6a488035
TO
118 $this->waitForPageToLoad($this->getTimeoutMsec());
119
120 // Select matching field for cvs data.
121 if (!empty($fieldMapper)) {
122 foreach ($fieldMapper as $field => $value) {
123 $this->select($field, "value={$value}");
124 }
125 }
126
127 // Check mapping data.
128 $this->_checkImportMapperData($headers,
129 $rows,
130 $existingMapping,
131 isset($other['checkMapperHeaders']) ? $other['checkMapperHeaders'] : array()
132 );
133
134 // Save mapping
135 if (isset($other['saveMapping'])) {
136 $mappingName = isset($other['saveMappingName']) ? $other['saveMappingName'] : "{$component}Import_" . substr(sha1(rand()), 0, 7);
137
138 $this->click('saveMapping');
139 $this->type('saveMappingName', $mappingName);
140 $this->type('saveMappingDesc', "Mapping for {$contactType}");
141 }
142
143 // Submit form.
144 $this->click('_qf_MapField_next');
145 $this->waitForElementPresent('_qf_Preview_next-bottom');
146
147 // Check mapping data.
148 $this->_checkImportMapperData($headers, $rows, $existingMapping, isset($other['checkMapperHeaders']) ? $other['checkMapperHeaders'] : array());
149
150 // Submit form.
225a8648 151 $this->clickLink('_qf_Preview_next-bottom', "_qf_Summary_next");
6a488035
TO
152
153 // Check success message.
154 $this->assertTrue($this->isTextPresent("Import has completed successfully. The information below summarizes the results."));
155
156 // Check summary Details.
157 $importedRecords = count($rows);
158 $checkSummary = array(
159 'Total Rows' => $importedRecords,
160 'Records Imported' => $importedRecords,
161 );
162
163 foreach ($checkSummary as $label => $value) {
164 $this->verifyText("xpath=//table[@id='summary-counts']/tbody/tr/td[text()='{$label}']/following-sibling::td", preg_quote($value));
165 }
166 }
167
4cbe18b8 168 /**
100fef9d 169 * Test contact import.
c490a46a 170 *
e16033b4
TO
171 * @param array $headers
172 * Csv data headers.
173 * @param array $rows
174 * Csv data rows.
175 * @param string $contactType
176 * Contact type.
177 * @param string $mode
178 * Import mode.
179 * @param array $fieldMapper
180 * Select mapper fields while import.
181 * @param array $other
182 * Other parameters.
c490a46a
CW
183 * contactSubtype : import for selected Contact Subtype
184
185 * useMappingName : to reuse mapping
186 * dateFormat : date format of data
187 * checkMapperHeaders : to override default check mapper headers
188 * saveMapping : save current mapping?
189 * saveMappingName : to override mapping name
190 * createGroup : create new group?
191 * createGroupName : to override new Group name
192 * createTag : create new tag?
193 * createTagName : to override new Tag name
194 * selectGroup : select existing group for contacts
195 * selectTag : select existing tag for contacts
196 * callbackImportSummary : function to override default import summary assertions
197 *
e16033b4
TO
198 * @param string $type
199 * Import type (csv/sql).
6c6e6187 200 * @todo:currently only supports csv, need to work on sql import
4cbe18b8 201 */
00be9182 202 public function importContacts($headers, $rows, $contactType = 'Individual', $mode = 'Skip', $fieldMapper = array(), $other = array(), $type = 'csv') {
6a488035
TO
203
204 // Go to contact import page.
071a6d2e 205 $this->openCiviPage("import/contact", "reset=1", "uploadFile");
6a488035
TO
206
207 $originalHeaders = $headers;
208 $originalRows = $rows;
209
210 // format headers and row to import contacts with relationship data.
211 $this->_formatContactCSVdata($headers, $rows);
212
213 // Create csv file of sample data.
214 $csvFile = $this->webtestCreateCSV($headers, $rows);
215
216 // Attach csv file.
217 $this->webtestAttachFile('uploadFile', $csvFile);
218
219 // First row is header.
220 $this->click('skipColumnHeader');
221
222 // select mode, default is 'Skip'.
223 if ($mode == 'Update') {
224 $this->click("CIVICRM_QFID_4_4");
225 }
226 elseif ($mode == 'Fill') {
227 $this->click("CIVICRM_QFID_8_6");
228 }
229 elseif ($mode == 'No Duplicate Checking') {
230 $this->click("CIVICRM_QFID_16_8");
231 }
232
233 // select contact type, default is 'Individual'.
234 if ($contactType == 'Organization') {
235 $this->click("CIVICRM_QFID_4_14");
236 }
237 elseif ($contactType == 'Household') {
238 $this->click("CIVICRM_QFID_2_12");
239 }
240
241 // Select contact subtype
242 if (isset($other['contactSubtype'])) {
70217150 243 $this->waitForElementPresent("xpath=//div[@id='common-form-controls']/table/tbody/tr[1]/td[2]/span");
beea38fc 244 $this->select('subType', $other['contactSubtype']);
6a488035
TO
245 }
246
247 if (isset($other['dedupe'])) {
248 $this->waitForElementPresent("dedupe");
249 $this->select('dedupe', 'value=' . $other['dedupe']);
250 }
251
252 // Use already created mapping
253 $existingMapping = NULL;
254 if (isset($other['useMappingName'])) {
255 $this->select('savedMapping', "label=" . $other['useMappingName']);
256 $existingMapping = $other['useMappingName'];
257 }
258
259 // Date format, default: yyyy-mm-dd OR yyyymmdd
260 if (isset($other['dateFormat'])) {
261 // default
262 $dateFormatMapper = array(
263 'yyyy-mm-dd OR yyyymmdd' => "CIVICRM_QFID_1_16",
264 'mm/dd/yy OR mm-dd-yy' => "CIVICRM_QFID_2_18",
265 'mm/dd/yyyy OR mm-dd-yyyy' => "CIVICRM_QFID_4_20",
266 'Month dd, yyyy' => "CIVICRM_QFID_8_22",
267 'dd-mon-yy OR dd/mm/yy' => "CIVICRM_QFID_16_24",
268 'dd/mm/yyyy' => "CIVICRM_QFID_32_26",
269 );
270 $this->click($dateFormatMapper[$other['dateFormat']]);
271 }
272
273 // Submit form.
beea38fc 274 $this->clickLink('_qf_DataSource_upload');
6a488035
TO
275
276 if (isset($other['checkMapperHeaders'])) {
277 $checkMapperHeaders = $other['checkMapperHeaders'];
278 }
279 else {
280 $checkMapperHeaders = array(
281 1 => 'Column Names',
282 2 => 'Import Data (row 1)',
283 3 => 'Import Data (row 2)',
284 4 => 'Matching CiviCRM Field',
285 );
286 }
287
288 // Check mapping data.
289 $this->_checkImportMapperData($headers, $rows, $existingMapping, $checkMapperHeaders, 'td');
290
291 // Select matching field for cvs data.
292 if (!empty($fieldMapper)) {
293 foreach ($fieldMapper as $field => $value) {
294 $this->select($field, "value={$value}");
295 }
296 }
297
298 // Save mapping
299 if (isset($other['saveMapping'])) {
300 $mappingName = isset($other['saveMappingName']) ? $other['saveMappingName'] : 'ContactImport_' . substr(sha1(rand()), 0, 7);
301 $this->click('saveMapping');
302 $this->type('saveMappingName', $mappingName);
303 $this->type('saveMappingDesc', "Mapping for {$contactType}");
304 }
305
306 // Submit form.
307 $this->click('_qf_MapField_next');
308 $this->waitForPageToLoad($this->getTimeoutMsec());
309
310 // Check mapping data.
311 $this->_checkImportMapperData($headers, $rows, $existingMapping, $checkMapperHeaders, 'td');
312
313 // Add imported contacts in new group.
314 $groupName = NULL;
315 $existingGroups = array();
316 if (isset($other['createGroup'])) {
317 $groupName = isset($other['createGroupName']) ? $other['createGroupName'] : 'ContactImport_' . substr(sha1(rand()), 0, 7);
318
319 $this->click("css=#new-group div.crm-accordion-header");
320 $this->type('newGroupName', $groupName);
321 $this->type('newGroupDesc', "Group For {$contactType}");
322 }
323 if (isset($other['selectGroup'])) {
324 // reuse existing groups.
325 if (is_array($other['selectGroup'])) {
326 foreach ($other['selectGroup'] as $existingGroup) {
327 $this->select('groups[]', 'label=' . $existingGroup);
328 $existingGroups[] = $existingGroup;
329 }
330 }
331 else {
332 $this->select('groups[]', 'label=' . $other['selectGroup']);
333 $existingGroups[] = $other['selectGroup'];
334 }
335 }
336
337 // Assign new tag to the imported contacts.
338 $tagName = NULL;
339 $existingTags = array();
340 if (isset($other['createTag'])) {
341 $tagName = isset($other['createTagName']) ? $other['createTagName'] : "{$contactType}_" . substr(sha1(rand()), 0, 7);
342
343 $this->click("css=#new-tag div.crm-accordion-header");
344 $this->type('newTagName', $tagName);
345 $this->type('newTagDesc', "Tag for {$contactType}");
346 }
347 if (isset($other['selectTag'])) {
348 $this->click("css=#existing-tags div.crm-accordion-header");
349 // reuse existing tags.
350 if (is_array($other['selectTag'])) {
351 foreach ($other['selectTag'] as $existingTag) {
352 $this->click("xpath=//div[@id='existing-tags']//div[@class='crm-accordion-body']//label[text()='{$existingTag}']");
353 $existingTags[] = $existingTag;
354 }
355 }
356 else {
357 $this->click("xpath=//div[@id='existing-tags']//div[@class='crm-accordion-body']//label[text()='" . $other['selectTag'] . "']");
358 $existingTags[] = $other['selectTag'];
359 }
360 }
361
362 // Submit form.
363 $this->click('_qf_Preview_next');
efb29358 364 $this->waitForPageToLoad($this->getTimeoutMsec());
6a488035
TO
365
366 // Check confirmation alert.
6c6e6187 367 $this->assertTrue((bool) preg_match("/^Are you sure you want to Import now[\s\S]$/", $this->getConfirmation()));
6a488035 368 $this->chooseOkOnNextConfirmation();
efb29358 369 $this->waitForPageToLoad($this->getTimeoutMsec());
6a488035
TO
370
371 // Visit summary page.
372 $this->waitForElementPresent("_qf_Summary_next");
373
374 // Check success message.
375 $this->assertTrue($this->isTextPresent("Import has completed successfully. The information below summarizes the results."));
376
377 // Check summary Details.
378 $importedContacts = $totalRows = count($originalRows);
379
380 // Include relationships contacts ( if exists )
381 if (isset($originalHeaders['contact_relationships']) && is_array($originalHeaders['contact_relationships'])) {
382 foreach ($originalRows as $row) {
383 $importedContacts += count($row['contact_relationships']);
384 }
385 }
386
387 $importedContactsCount = ($importedContacts == 1) ? 'One contact' : "$importedContacts contacts";
388 $taggedContactsCount = ($importedContacts == 1) ? 'One contact is' : "$importedContacts contacts are";
389 $checkSummary = array(
390 'Total Rows' => $totalRows,
391 'Total Contacts' => $importedContacts,
392 );
393
394 if ($groupName) {
395 $checkSummary['Import to Groups'] = "{$groupName}: {$importedContactsCount} added to this new group.";
396 }
397
398 if ($tagName) {
399 $checkSummary['Tagged Imported Contacts'] = "{$tagName}: {$taggedContactsCount} tagged with this tag.";
400 }
401
402 if ($existingGroups) {
403 if (!isset($checkSummary['Import to Groups'])) {
404 $checkSummary['Import to Groups'] = '';
405 }
406 foreach ($existingGroups as $existingGroup) {
407 $checkSummary['Import to Groups'] .= "{$existingGroup}: {$importedContactsCount} added to this existing group.";
408 }
409 }
410
411 if ($existingTags) {
412 if (!isset($checkSummary['Tagged Imported Contacts'])) {
413 $checkSummary['Tagged Imported Contacts'] = '';
414 }
415 foreach ($existingTags as $existingTag) {
416 $checkSummary['Tagged Imported Contacts'] .= "{$existingTag}: {$taggedContactsCount} tagged with this tag.";
417 }
418 }
419
420 if (!empty($other['callbackImportSummary']) && is_callable(array(
421 $this, $other['callbackImportSummary']))) {
422 $callbackImportSummary = $other['callbackImportSummary'];
423 $this->$callbackImportSummary($originalHeaders, $originalRows, $checkSummary);
424 }
425 else {
426 foreach ($checkSummary as $label => $value) {
427 $this->verifyText("xpath=//table[@id='summary-counts']/tbody/tr/td[text()='{$label}']/following-sibling::td", preg_quote($value));
428 }
429 }
430 }
431
4cbe18b8 432 /**
c490a46a 433 * Helper function to get the import url of the component.
e16033b4
TO
434 * @param string $component
435 * Component name.
4cbe18b8 436 *
a6c01b45
CW
437 * @return string
438 * import url
4cbe18b8 439 */
beea38fc 440 private function _getImportComponentUrl($component) {
6a488035 441 $importComponentUrl = array(
beea38fc
CW
442 'Event' => 'event/import',
443 'Contribution' => 'contribute/import',
444 'Membership' => 'member/import',
445 'Activity' => 'import/activity',
6a488035
TO
446 );
447
448 return $importComponentUrl[$component];
449 }
450
4cbe18b8
EM
451 /**
452 * @param $component
453 * @param $contactType
454 *
c490a46a 455 * @return string
4cbe18b8 456 */
00be9182 457 public function _getImportComponentContactType($component, $contactType) {
6a488035 458 $importComponentMode = array(
c490a46a
CW
459 'Event' => array(
460 'Individual' => 'CIVICRM_QFID_1_8',
6a488035
TO
461 'Household' => 'CIVICRM_QFID_2_10',
462 'Organization' => 'CIVICRM_QFID_4_12',
463 ),
464 'Contribution' => array(
465 'Individual' => 'CIVICRM_QFID_1_6',
466 'Household' => 'CIVICRM_QFID_2_8',
467 'Organization' => 'CIVICRM_QFID_4_10',
468 ),
469 'Membership' => array(
470 'Individual' => 'CIVICRM_QFID_1_6',
471 'Household' => 'CIVICRM_QFID_2_8',
472 'Organization' => 'CIVICRM_QFID_4_10',
473 ),
474 );
475
476 return $importComponentMode[$component][$contactType];
477 }
478
4cbe18b8 479 /**
c490a46a
CW
480 * Helper function to check import mapping fields.
481 * @param array $headers
482 * @param array $rows
4cbe18b8
EM
483 * @param null $existingMapping
484 * @param array $checkMapperHeaders
485 * @param string $headerSelector
486 */
00be9182 487 public function _checkImportMapperData($headers, $rows, $existingMapping = NULL, $checkMapperHeaders = array(), $headerSelector = 'th') {
6a488035
TO
488
489 if (empty($checkMapperHeaders)) {
490 $checkMapperHeaders = array(
491 1 => 'Column Headers',
492 2 => 'Import Data (row 2)',
493 3 => 'Import Data (row 3)',
494 4 => 'Matching CiviCRM Field',
495 );
496 }
497
498 $rowNumber = 1;
499 if ($existingMapping) {
500 $this->verifyText("xpath=//div[@id='map-field']//table[1]/tbody/tr[{$rowNumber}]/th[1]", preg_quote("Saved Field Mapping: {$existingMapping}"));
501 $rowNumber++;
502 }
503
504 foreach ($checkMapperHeaders as $rownum => $value) {
505 $this->verifyText("xpath=//div[@id='map-field']//table[1]/tbody/tr[{$rowNumber}]/{$headerSelector}[{$rownum}]", preg_quote($value));
506 }
507 $rowNumber++;
508
509 foreach ($headers as $field => $header) {
510 $this->verifyText("xpath=//div[@id='map-field']//table[1]/tbody/tr[{$rowNumber}]/td[1]", preg_quote($header));
511 $colnum = 2;
512 foreach ($rows as $row) {
513 $this->verifyText("xpath=//div[@id='map-field']//table[1]/tbody/tr[{$rowNumber}]/td[{$colnum}]", preg_quote($row[$field]));
514 $colnum++;
515 }
516 $rowNumber++;
517 }
518 }
519
4cbe18b8 520 /**
c490a46a
CW
521 * Helper function to get imported contact ids.
522 *
523 * @param array $rows
4cbe18b8
EM
524 * @param string $contactType
525 *
a6c01b45
CW
526 * @return array
527 * imported contact ids
4cbe18b8 528 */
00be9182 529 public function _getImportedContactIds($rows, $contactType = 'Individual') {
6a488035
TO
530 $contactIds = array();
531
532 foreach ($rows as $row) {
533 $searchName = '';
534
535 // Build search name.
536 if ($contactType == 'Individual') {
537 $searchName = "{$row['last_name']}, {$row['first_name']}";
538 }
539 elseif ($contactType == 'Organization') {
540 $searchName = $row['organization_name'];
541 }
542 elseif ($contactType == 'Household') {
543 $searchName = $row['household_name'];
544 }
545
071a6d2e 546 $this->openCiviPage("dashboard", "reset=1");
6a488035
TO
547
548 // Type search name in autocomplete.
549 $this->click("css=input#sort_name_navigation");
550 $this->type("css=input#sort_name_navigation", $searchName);
551 $this->typeKeys("css=input#sort_name_navigation", $searchName);
552
553 // Wait for result list.
8ada7e8e 554 $this->waitForElementPresent("css=ul.ui-autocomplete li");
6a488035
TO
555
556 // Visit contact summary page.
8ada7e8e 557 $this->click("css=ul.ui-autocomplete li");
558 $this->waitForPageToLoad($this->getTimeoutMsec());
6a488035
TO
559
560 // Get contact id from url.
a471a3b6 561 $contactIds[] = $this->urlArg('cid');
6a488035
TO
562 }
563
564 return $contactIds;
565 }
566
4cbe18b8 567 /**
c490a46a
CW
568 * Helper function to format headers and rows for contact relationship data.
569 *
570 * @param array $headers
571 * @param array $rows
4cbe18b8 572 */
00be9182 573 public function _formatContactCSVdata(&$headers, &$rows) {
6a488035
TO
574 if (!isset($headers['contact_relationships'])) {
575 return;
576 }
577
578 $relationshipHeaders = $headers['contact_relationships'];
579 unset($headers['contact_relationships']);
580
581 if (empty($relationshipHeaders) || !is_array($relationshipHeaders)) {
582 return;
583 }
584
585 foreach ($relationshipHeaders as $relationshipHeader) {
586 $headers = array_merge($headers, $relationshipHeader);
587 }
588
589 foreach ($rows as & $row) {
590 $relationshipRows = $row['contact_relationships'];
591 unset($row['contact_relationships']);
592 foreach ($relationshipRows as $relationshipRow) {
593 $row = array_merge($row, $relationshipRow);
594 }
595 }
596 }
597}