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