Import from SVN (r45945, r596)
[civicrm-core.git] / tests / phpunit / WebTest / Import / ImportCiviSeleniumTestCase.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
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
28 require_once 'CiviTest/CiviSeleniumTestCase.php';
29 require_once 'CRM/Utils/Array.php';
30 class ImportCiviSeleniumTestCase extends CiviSeleniumTestCase {
31
32 /*
33 * Function to test csv import for each component.
34 *
35 * @params string $component component name ( Event, Contribution, Membership, Activity etc)
36 * @params array $headers csv data headers
37 * @params array $rows csv data rows
38 * @params string $contactType contact type
39 * @params string $mode import mode
40 * @params array $fieldMapper select mapper fields while import
41 * @params array $other other parameters
42 * useMappingName : to reuse mapping
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.
111 $this->click('_qf_UploadFile_upload');
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.
145 $this->click('_qf_Preview_next-bottom');
146
147 sleep(10);
148
149 // Visit summary page.
150 $this->waitForElementPresent("_qf_Summary_next");
151
152 // Check success message.
153 $this->assertTrue($this->isTextPresent("Import has completed successfully. The information below summarizes the results."));
154
155 // Check summary Details.
156 $importedRecords = count($rows);
157 $checkSummary = array(
158 'Total Rows' => $importedRecords,
159 'Records Imported' => $importedRecords,
160 );
161
162 foreach ($checkSummary as $label => $value) {
163 $this->verifyText("xpath=//table[@id='summary-counts']/tbody/tr/td[text()='{$label}']/following-sibling::td", preg_quote($value));
164 }
165 }
166
167 /*
168 * Function to test contact import.
169 *
170 * @params array $headers csv data headers
171 * @params array $rows csv data rows
172 * @params string $contactType contact type
173 * @params string $mode import mode
174 * @params array $fieldMapper select mapper fields while import
175 * @params array $other other parameters
176 * contactSubtype : import for selected Contact Subtype
177 * useMappingName : to reuse mapping
178 * dateFormat : date format of data
179 * checkMapperHeaders : to override default check mapper headers
180 * saveMapping : save current mapping?
181 * saveMappingName : to override mapping name
182 * createGroup : create new group?
183 * createGroupName : to override new Group name
184 * createTag : create new tag?
185 * createTagName : to override new Tag name
186 * selectGroup : select existing group for contacts
187 * selectTag : select existing tag for contacts
188 * callbackImportSummary : function to override default import summary assertions
189 *
190 * @params string $type import type (csv/sql)
191 * @todo:currently only supports csv, need to work on sql import
192 */
193 function importContacts($headers, $rows, $contactType = 'Individual', $mode = 'Skip', $fieldMapper = array(
194 ), $other = array(), $type = 'csv') {
195
196 // Go to contact import page.
197 $this->open($this->sboxPath . "civicrm/import/contact?reset=1");
198
199 // check for upload field.
200 $this->waitForElementPresent("uploadFile");
201
202 $originalHeaders = $headers;
203 $originalRows = $rows;
204
205 // format headers and row to import contacts with relationship data.
206 $this->_formatContactCSVdata($headers, $rows);
207
208 // Create csv file of sample data.
209 $csvFile = $this->webtestCreateCSV($headers, $rows);
210
211 // Attach csv file.
212 $this->webtestAttachFile('uploadFile', $csvFile);
213
214 // First row is header.
215 $this->click('skipColumnHeader');
216
217 // select mode, default is 'Skip'.
218 if ($mode == 'Update') {
219 $this->click("CIVICRM_QFID_4_4");
220 }
221 elseif ($mode == 'Fill') {
222 $this->click("CIVICRM_QFID_8_6");
223 }
224 elseif ($mode == 'No Duplicate Checking') {
225 $this->click("CIVICRM_QFID_16_8");
226 }
227
228 // select contact type, default is 'Individual'.
229 if ($contactType == 'Organization') {
230 $this->click("CIVICRM_QFID_4_14");
231 }
232 elseif ($contactType == 'Household') {
233 $this->click("CIVICRM_QFID_2_12");
234 }
235
236 // Select contact subtype
237 if (isset($other['contactSubtype'])) {
238 if ($contactType != 'Individual') {
239 // wait for contact subtypes to repopulate.
240 sleep(5);
241 }
242 $this->waitForElementPresent("subType");
243 $this->select('subType', 'label=' . $other['contactSubtype']);
244 }
245
246 if (isset($other['dedupe'])) {
247 $this->waitForElementPresent("dedupe");
248 $this->select('dedupe', 'value=' . $other['dedupe']);
249 }
250
251 // Use already created mapping
252 $existingMapping = NULL;
253 if (isset($other['useMappingName'])) {
254 $this->select('savedMapping', "label=" . $other['useMappingName']);
255 $existingMapping = $other['useMappingName'];
256 }
257
258 // Date format, default: yyyy-mm-dd OR yyyymmdd
259 if (isset($other['dateFormat'])) {
260 // default
261 $dateFormatMapper = array(
262 'yyyy-mm-dd OR yyyymmdd' => "CIVICRM_QFID_1_16",
263 'mm/dd/yy OR mm-dd-yy' => "CIVICRM_QFID_2_18",
264 'mm/dd/yyyy OR mm-dd-yyyy' => "CIVICRM_QFID_4_20",
265 'Month dd, yyyy' => "CIVICRM_QFID_8_22",
266 'dd-mon-yy OR dd/mm/yy' => "CIVICRM_QFID_16_24",
267 'dd/mm/yyyy' => "CIVICRM_QFID_32_26",
268 );
269 $this->click($dateFormatMapper[$other['dateFormat']]);
270 }
271
272 // Submit form.
273 $this->click('_qf_DataSource_upload');
274 $this->waitForPageToLoad($this->getTimeoutMsec());
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');
364 sleep(2);
365
366 // Check confirmation alert.
367 $this->assertTrue((bool)preg_match("/^Are you sure you want to Import now[\s\S]$/", $this->getConfirmation()));
368 $this->chooseOkOnNextConfirmation();
369
370 sleep(10);
371
372 // Visit summary page.
373 $this->waitForElementPresent("_qf_Summary_next");
374
375 // Check success message.
376 $this->assertTrue($this->isTextPresent("Import has completed successfully. The information below summarizes the results."));
377
378 // Check summary Details.
379 $importedContacts = $totalRows = count($originalRows);
380
381 // Include relationships contacts ( if exists )
382 if (isset($originalHeaders['contact_relationships']) && is_array($originalHeaders['contact_relationships'])) {
383 foreach ($originalRows as $row) {
384 $importedContacts += count($row['contact_relationships']);
385 }
386 }
387
388 $importedContactsCount = ($importedContacts == 1) ? 'One contact' : "$importedContacts contacts";
389 $taggedContactsCount = ($importedContacts == 1) ? 'One contact is' : "$importedContacts contacts are";
390 $checkSummary = array(
391 'Total Rows' => $totalRows,
392 'Total Contacts' => $importedContacts,
393 );
394
395 if ($groupName) {
396 $checkSummary['Import to Groups'] = "{$groupName}: {$importedContactsCount} added to this new group.";
397 }
398
399 if ($tagName) {
400 $checkSummary['Tagged Imported Contacts'] = "{$tagName}: {$taggedContactsCount} tagged with this tag.";
401 }
402
403 if ($existingGroups) {
404 if (!isset($checkSummary['Import to Groups'])) {
405 $checkSummary['Import to Groups'] = '';
406 }
407 foreach ($existingGroups as $existingGroup) {
408 $checkSummary['Import to Groups'] .= "{$existingGroup}: {$importedContactsCount} added to this existing group.";
409 }
410 }
411
412 if ($existingTags) {
413 if (!isset($checkSummary['Tagged Imported Contacts'])) {
414 $checkSummary['Tagged Imported Contacts'] = '';
415 }
416 foreach ($existingTags as $existingTag) {
417 $checkSummary['Tagged Imported Contacts'] .= "{$existingTag}: {$taggedContactsCount} tagged with this tag.";
418 }
419 }
420
421 if (!empty($other['callbackImportSummary']) && is_callable(array(
422 $this, $other['callbackImportSummary']))) {
423 $callbackImportSummary = $other['callbackImportSummary'];
424 $this->$callbackImportSummary($originalHeaders, $originalRows, $checkSummary);
425 }
426 else {
427 foreach ($checkSummary as $label => $value) {
428 $this->verifyText("xpath=//table[@id='summary-counts']/tbody/tr/td[text()='{$label}']/following-sibling::td", preg_quote($value));
429 }
430 }
431 }
432
433 /*
434 * Helper function to get the import url of the component.
435 *
436 * @params string $component component name
437 *
438 * @return string import url
439 */
440 function _getImportComponentUrl($component) {
441
442 $importComponentUrl = array(
443 'Event' => 'civicrm/event/import?reset=1',
444 'Contribution' => 'civicrm/contribute/import?reset=1',
445 'Membership' => 'civicrm/member/import?reset=1',
446 'Activity' => 'civicrm/import/activity?reset=1',
447 );
448
449 return $importComponentUrl[$component];
450 }
451
452 /*
453 * Helper function to get the import url of the component.
454 *
455 * @params string $component component name
456 *
457 * @return string import url
458 */
459 function _getImportComponentContactType($component, $contactType) {
460 $importComponentMode = array(
461 'Event' => array('Individual' => 'CIVICRM_QFID_1_8',
462 'Household' => 'CIVICRM_QFID_2_10',
463 'Organization' => 'CIVICRM_QFID_4_12',
464 ),
465 'Contribution' => array(
466 'Individual' => 'CIVICRM_QFID_1_6',
467 'Household' => 'CIVICRM_QFID_2_8',
468 'Organization' => 'CIVICRM_QFID_4_10',
469 ),
470 'Membership' => array(
471 'Individual' => 'CIVICRM_QFID_1_6',
472 'Household' => 'CIVICRM_QFID_2_8',
473 'Organization' => 'CIVICRM_QFID_4_10',
474 ),
475 );
476
477 return $importComponentMode[$component][$contactType];
478 }
479
480 /*
481 * Helper function to check import mapping fields.
482 *
483 * @params array $headers field headers
484 * @params array $rows field rows
485 * @params array $checkMapperHeaders override default mapper headers
486 */
487 function _checkImportMapperData($headers, $rows, $existingMapping = NULL, $checkMapperHeaders = array(
488 ), $headerSelector = 'th') {
489
490 if (empty($checkMapperHeaders)) {
491 $checkMapperHeaders = array(
492 1 => 'Column Headers',
493 2 => 'Import Data (row 2)',
494 3 => 'Import Data (row 3)',
495 4 => 'Matching CiviCRM Field',
496 );
497 }
498
499 $rowNumber = 1;
500 if ($existingMapping) {
501 $this->verifyText("xpath=//div[@id='map-field']//table[1]/tbody/tr[{$rowNumber}]/th[1]", preg_quote("Saved Field Mapping: {$existingMapping}"));
502 $rowNumber++;
503 }
504
505 foreach ($checkMapperHeaders as $rownum => $value) {
506 $this->verifyText("xpath=//div[@id='map-field']//table[1]/tbody/tr[{$rowNumber}]/{$headerSelector}[{$rownum}]", preg_quote($value));
507 }
508 $rowNumber++;
509
510 foreach ($headers as $field => $header) {
511 $this->verifyText("xpath=//div[@id='map-field']//table[1]/tbody/tr[{$rowNumber}]/td[1]", preg_quote($header));
512 $colnum = 2;
513 foreach ($rows as $row) {
514 $this->verifyText("xpath=//div[@id='map-field']//table[1]/tbody/tr[{$rowNumber}]/td[{$colnum}]", preg_quote($row[$field]));
515 $colnum++;
516 }
517 $rowNumber++;
518 }
519 }
520
521 /*
522 * Helper function to get imported contact ids.
523 *
524 * @params array $rows fields rows
525 * @params string $contactType contact type
526 *
527 * @return array $contactIds imported contact ids
528 */
529 function _getImportedContactIds($rows, $contactType = 'Individual') {
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
546 $this->open($this->sboxPath . "civicrm/dashboard?reset=1");
547 $this->waitForPageToLoad($this->getTimeoutMsec());
548
549
550 // Type search name in autocomplete.
551 $this->click("css=input#sort_name_navigation");
552 $this->type("css=input#sort_name_navigation", $searchName);
553 $this->typeKeys("css=input#sort_name_navigation", $searchName);
554
555 // Wait for result list.
556 $this->waitForElementPresent("css=div.ac_results-inner li");
557
558 // Visit contact summary page.
559 $this->click("css=div.ac_results-inner li");
560 $this->waitForPageToLoad($this->getTimeoutMsec());
561
562 // Get contact id from url.
563 $matches = array();
564 preg_match('/cid=([0-9]+)/', $this->getLocation(), $matches);
565 $contactIds[] = $matches[1];
566 }
567
568 return $contactIds;
569 }
570
571 /*
572 * Helper function to format headers and rows for contact relationship data.
573 *
574 * @params array $headers data headers
575 * @params string $rows data rows
576 */
577 function _formatContactCSVdata(&$headers, &$rows) {
578 if (!isset($headers['contact_relationships'])) {
579 return;
580 }
581
582 $relationshipHeaders = $headers['contact_relationships'];
583 unset($headers['contact_relationships']);
584
585 if (empty($relationshipHeaders) || !is_array($relationshipHeaders)) {
586 return;
587 }
588
589 foreach ($relationshipHeaders as $relationshipHeader) {
590 $headers = array_merge($headers, $relationshipHeader);
591 }
592
593 foreach ($rows as & $row) {
594 $relationshipRows = $row['contact_relationships'];
595 unset($row['contact_relationships']);
596 foreach ($relationshipRows as $relationshipRow) {
597 $row = array_merge($row, $relationshipRow);
598 }
599 }
600 }
601 }
602