4 * Include dataProvider for tests
7 class CRM_Activity_Form_ActivityTest
extends CiviUnitTestCase
{
9 public function setUp() {
11 $this->assignee1
= $this->individualCreate([
12 'first_name' => 'testassignee1',
13 'last_name' => 'testassignee1',
14 'email' => 'testassignee1@gmail.com',
16 $this->assignee2
= $this->individualCreate([
17 'first_name' => 'testassignee2',
18 'last_name' => 'testassignee2',
19 'email' => 'testassignee2@gmail.com',
21 $this->target
= $this->individualCreate();
22 $this->source
= $this->individualCreate();
25 public function testActivityCreate() {
26 Civi
::settings()->set('activity_assignee_notification', TRUE);
27 //Reset filter to none.
28 Civi
::settings()->set('do_not_notify_assignees_for', []);
29 $mut = new CiviMailUtils($this, TRUE);
30 $mut->clearMessages();
32 $form = new CRM_Activity_Form_Activity();
33 $activityTypeId = CRM_Core_PseudoConstant
::getKey('CRM_Activity_DAO_Activity', 'activity_type_id', 'Meeting');
35 'source_contact_id' => $this->source
,
36 'assignee_contact_id' => [$this->assignee1
],
37 'target_contact_id' => [$this->target
],
38 'followup_assignee_contact_id' => [],
39 'activity_type_id' => $activityTypeId,
42 $activityRef = new ReflectionClass('CRM_Activity_Form_Activity');
43 $method = $activityRef->getMethod('processActivity');
44 $method->setAccessible(TRUE);
45 $method->invokeArgs($form, [&$params]);
47 $msg = $mut->getMostRecentEmail();
48 $this->assertNotEmpty($msg);
49 $mut->clearMessages();
51 //Block Meeting notification.
52 Civi
::settings()->set('do_not_notify_assignees_for', [$activityTypeId]);
53 $params['assignee_contact_id'] = [$this->assignee2
];
54 $method->invokeArgs($form, [&$params]);
55 $msg = $mut->getMostRecentEmail();
56 $this->assertEmpty($msg);
59 public function testActivityDelete() {
60 // Set the parameters of the test.
61 $numberOfSingleActivitiesToCreate = 3;
62 $numberOfRepeatingActivitiesToCreate = 6;
63 $singleActivityToDeleteOffset = 1;
64 $mode1ActivityToDeleteOffset = 1;
65 $mode2ActivityToDeleteOffset = 3;
66 $mode3ActivityToDeleteOffset = 2;
68 // Track the target contact's activities.
69 $expectedActivityIds = array_keys(CRM_Activity_BAO_Activity
::getActivities(['contact_id' => $this->target
]));
71 // Create non-repeating activities.
72 $meetingActivityTypeId = CRM_Core_PseudoConstant
::getKey('CRM_Activity_DAO_Activity', 'activity_type_id', 'Meeting');
73 $singleActivityIds = [];
74 for ($activityCount = 0; $activityCount < $numberOfSingleActivitiesToCreate; $activityCount++
) {
76 'source_contact_id' => $this->source
,
77 'target_contact_id' => $this->target
,
78 'activity_type_id' => $meetingActivityTypeId,
79 'activity_date_time' => date_create('+' . $activityCount . ' weeks')->format('YmdHis'),
81 $singleActivityBao = CRM_Activity_BAO_Activity
::create($activityParams);
82 $singleActivityIds[] = $singleActivityBao->id
;
84 $expectedActivityIds = array_merge($expectedActivityIds, $singleActivityIds);
86 // Create an activity to be repeated.
88 'source_contact_id' => $this->source
,
89 'target_contact_id' => $this->target
,
90 'activity_type_id' => $meetingActivityTypeId,
91 'activity_date_time' => date('YmdHis'),
93 $repeatingActivityBao = CRM_Activity_BAO_Activity
::create($activityParams);
95 // Create the repeating activity's schedule.
96 $actionScheduleParams = [
97 'used_for' => 'civicrm_activity',
98 'entity_value' => $repeatingActivityBao->id
,
99 'start_action_date' => $repeatingActivityBao->activity_date_time
,
100 'repetition_frequency_unit' => 'week',
101 'repetition_frequency_interval' => 1,
102 'start_action_offset' => $numberOfRepeatingActivitiesToCreate - 1,
104 $actionScheduleBao = CRM_Core_BAO_ActionSchedule
::add($actionScheduleParams);
106 // Create the activity's repeats.
107 $recurringEntityBao = new CRM_Core_BAO_RecurringEntity();
108 $recurringEntityBao->entity_table
= 'civicrm_activity';
109 $recurringEntityBao->entity_id
= $repeatingActivityBao->id
;
110 $recurringEntityBao->dateColumns
= ['activity_date_time'];
111 $recurringEntityBao->linkedEntities
= [
113 'table' => 'civicrm_activity_contact',
114 'findCriteria' => ['activity_id' => $repeatingActivityBao->id
],
115 'linkedColumns' => ['activity_id'],
116 'isRecurringEntityRecord' => FALSE,
119 $recurringEntityBao->scheduleId
= $actionScheduleBao->id
;
120 $newEntities = $recurringEntityBao->generate();
121 $repeatingActivityIds = array_merge([$repeatingActivityBao->id
], $newEntities['civicrm_activity']);
122 $expectedActivityIds = array_merge($expectedActivityIds, $repeatingActivityIds);
124 // Assert that the expected activities exist.
125 $this->assertTargetActivityIds($expectedActivityIds);
127 // Delete a non-repeating activity.
128 $activityId = $singleActivityIds[$singleActivityToDeleteOffset];
129 $this->deleteActivity($activityId);
130 $expectedActivityIds = array_diff($expectedActivityIds, [$activityId]);
131 $this->assertTargetActivityIds($expectedActivityIds);
133 // Delete one activity from series (mode 1).
134 $activityId = $repeatingActivityIds[$mode1ActivityToDeleteOffset];
135 $this->deleteActivity($activityId, 1);
136 $expectedActivityIds = array_diff($expectedActivityIds, [$activityId]);
137 $this->assertTargetActivityIds($expectedActivityIds);
139 // Delete from one activity until end of series (mode 2).
140 $activityId = $repeatingActivityIds[$mode2ActivityToDeleteOffset];
141 $this->deleteActivity($activityId, 2);
142 $expectedActivityIds = array_diff($expectedActivityIds, array_slice($repeatingActivityIds, $mode2ActivityToDeleteOffset));
143 $this->assertTargetActivityIds($expectedActivityIds);
145 // Delete all activities in series (mode 3).
146 $activityId = $repeatingActivityIds[$mode3ActivityToDeleteOffset];
147 $this->deleteActivity($activityId, 3);
148 $expectedActivityIds = array_diff($expectedActivityIds, $repeatingActivityIds);
149 $this->assertTargetActivityIds($expectedActivityIds);
153 * Asserts that the target contact has the expected activity IDs
155 * @param array $expectedActivityIds
156 * An array of the activity IDs that are expected to exist for the target contact
158 private function assertTargetActivityIds($expectedActivityIds) {
159 $actualActivityIds = array_keys(CRM_Activity_BAO_Activity
::getActivities(['contact_id' => $this->target
]));
160 $this->assertEquals(array_fill_keys($expectedActivityIds, NULL), array_fill_keys($actualActivityIds, NULL));
164 * Tests the form's deletion of activities, with optional mode for repeating activities
166 * @param int $activityId
167 * The ID of the activity to delete
169 * 1 - delete the specified activity
170 * 2 - delete the specified activity and all following activities in the series
171 * 3 - delete all activities in the series
173 private function deleteActivity($activityId, $mode = NULL) {
174 // For repeating activities, set the recurring entity mode.
175 if (!is_null($mode)) {
176 $recurringEntityBao = new CRM_Core_BAO_RecurringEntity();
177 $recurringEntityBao->entity_table
= 'civicrm_activity';
178 $recurringEntityBao->entity_id
= $activityId;
179 $recurringEntityBao->mode($mode);
182 // Use a form to delete the activity.
183 $form = new CRM_Activity_Form_Activity();
184 $form->_action
= CRM_Core_Action
::DELETE
;
185 $form->_activityId
= $activityId;
186 $form->postProcess();
190 * This is a bit messed up having a variable called name that means label but we don't want to fix it because it's a form member variable _activityTypeName that might be used in form hooks, so just make sure it doesn't flip between name and label. dev/core#1116
192 public function testActivityTypeNameIsReallyLabel() {
193 $form = new CRM_Activity_Form_Activity();
195 // the actual value is irrelevant we just need something for the tested function to act on
196 $form->_currentlyViewedContactId
= $this->source
;
198 // Let's make a new activity type that has a different name from its label just to be sure.
200 'option_group_id' => 'activity_type',
202 'label' => 'Water Plants',
206 $result = $this->callAPISuccess('option_value', 'create', $actParams);
208 $form->_activityTypeId
= $result['values'][$result['id']]['value'];
209 $this->assertNotEmpty($form->_activityTypeId
);
211 // Do the thing we want to test
212 $form->assignActivityType();
214 $this->assertEquals('Water Plants', $form->_activityTypeName
);
217 $this->callAPISuccess('option_value', 'delete', ['id' => $result['id']]);
221 * Test that the machineName and displayLabel are assigned correctly to the
224 * See also testActivityTypeNameIsReallyLabel()
226 public function testActivityTypeAssignment() {
227 $form = new CRM_Activity_Form_Activity();
229 $form->_currentlyViewedContactId
= $this->source
;
231 // Let's make a new activity type that has a different name from its label just to be sure.
233 'option_group_id' => 'activity_type',
235 'label' => 'Hide Cookies',
239 $result = $this->callAPISuccess('option_value', 'create', $actParams);
241 $form->_activityTypeId
= $result['values'][$result['id']]['value'];
243 // Do the thing we want to test
244 $form->assignActivityType();
246 // Check the smarty template has the correct values assigned.
247 $keyValuePair = $form->getTemplate()->get_template_vars('activityTypeNameAndLabel');
248 $this->assertEquals('47395hc', $keyValuePair['machineName']);
249 $this->assertEquals('Hide Cookies', $keyValuePair['displayLabel']);
252 $this->callAPISuccess('option_value', 'delete', ['id' => $result['id']]);
256 * Test that inbound email is still treated properly if you change the label.
257 * I'm not crazy about the strategy used in this test but I can't see another
260 public function testInboundEmailDisplaysWithLinebreaks() {
262 $inbound_email = $this->callAPISuccess('OptionValue', 'getsingle', [
263 'option_group_id' => 'activity_type',
264 'name' => 'Inbound Email',
266 $this->callAPISuccess('OptionValue', 'create', [
267 'id' => $inbound_email['id'],
268 'label' => 'Probably Spam',
271 // Fake an inbound email and store it
273 $messageBody = <<<ENDBODY
279 Let's check if the output when viewing the form has legible line breaks in the output.
285 <div dir="ltr">Hi,<br></div>
286 <div dir="ltr"><br></div>
287 <div dir="ltr">Wassup!?!?<br></div>
288 <div dir="ltr"><br></div>
289 <div dir="ltr">Let's check if the output when viewing the form has legible line breaks in the output.<br></div>
290 <div dir="ltr"><br></div>
291 <div dir="ltr">Thanks!<br></div>
295 $activity = $this->activityCreate([
296 'subject' => 'Important message read immediately!',
299 'details' => $messageBody,
300 'status_id' => 'Completed',
301 'activity_type_id' => 'Inbound Email',
302 'source_contact_id' => $this->source
,
303 'assignee_contact_id' => NULL,
305 $activity_id = $activity['id'];
307 // Simulate viewing it from the form.
309 $form = new CRM_Activity_Form_Activity();
310 $form->controller
= new CRM_Core_Controller_Simple('CRM_Activity_Form_Activity', 'Activity');
311 $form->set('context', 'standalone');
312 $form->set('cid', $this->source
);
313 $form->set('action', 'view');
314 $form->set('id', $activity_id);
315 $form->set('atype', $activity['values'][$activity_id]['activity_type_id']);
319 // Wish there was another way to do this
320 $form->controller
->handle($form, 'display');
322 // This isn't a faithful representation of the output since there'll
323 // probably be a lot missing, but for now I don't see a simpler way to
325 // Also this is printing the template code to the console. It doesn't hurt
326 // the test but it's clutter and I don't know where it's coming from
327 // and can't seem to prevent it.
328 $output = $form->getTemplate()->fetch($form->getTemplateFileName());
330 // This kind of suffers from the same problem as the old webtests. It's
331 // a bit brittle and tied to the UI.
332 $this->assertContains("Hi,<br />\n<br />\nWassup!?!?<br />\n<br />\nLet's check if the output when viewing the form has legible line breaks in the output.<br />\n<br />\nThanks!", $output);
335 $this->callAPISuccess('OptionValue', 'create', [
336 'id' => $inbound_email['id'],
337 'label' => $inbound_email['label'],