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