From b3dadf81c5d8eb397d5f2be1498ce1ae33c51319 Mon Sep 17 00:00:00 2001
From: Thomas Steur <thomas.steur@googlemail.com>
Date: Wed, 20 Aug 2014 18:02:08 +0200
Subject: [PATCH] refs #5989 #6026 let users define a goal depending on an
 event and fix the examples and condition was not updated when user changes
 the match_attribute

---
 core/Tracker/GoalManager.php                  |  20 +-
 lang/en.json                                  |   1 +
 plugins/Events/Actions/ActionEvent.php        |  15 ++
 plugins/Goals/API.php                         |   9 +-
 plugins/Goals/javascripts/goalsForm.js        |  40 ++-
 plugins/Goals/templates/_addEditGoal.twig     |   8 +-
 plugins/Goals/templates/_formAddGoal.twig     |   9 +-
 plugins/Goals/templates/_listGoalEdit.twig    |   5 +-
 plugins/Goals/tests/APITest.php               | 228 ++++++++++++++++++
 .../Fixtures/SomeVisitsAllConversions.php     |  24 ++
 ...lsAllowMultipleConversionsPerVisitTest.php |   4 +-
 ...e.getVisitInformationPerServerTime_day.xml |  25 +-
 ...ersionsPerVisit__VisitsSummary.get_day.xml |  16 +-
 13 files changed, 374 insertions(+), 30 deletions(-)
 create mode 100644 plugins/Goals/tests/APITest.php

diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php
index fbc73381aa..12e3865d2c 100644
--- a/core/Tracker/GoalManager.php
+++ b/core/Tracker/GoalManager.php
@@ -138,15 +138,29 @@ class GoalManager
                 || ($attribute == 'file' && $actionType != Action::TYPE_DOWNLOAD)
                 || ($attribute == 'external_website' && $actionType != Action::TYPE_OUTLINK)
                 || ($attribute == 'manually')
+                || in_array($attribute, array('event_action', 'event_name', 'event_category')) && $actionType != Action::TYPE_EVENT
             ) {
                 continue;
             }
 
             $url = $decodedActionUrl;
-            // Matching on Page Title
-            if ($attribute == 'title') {
-                $url = $action->getActionName();
+
+            switch ($attribute) {
+                case 'title':
+                    // Matching on Page Title
+                    $url = $action->getActionName();
+                    break;
+                case 'event_action':
+                    $url = $action->getEventAction();
+                    break;
+                case 'event_name':
+                    $url = $action->getEventName();
+                    break;
+                case 'event_category':
+                    $url = $action->getEventCategory();
+                    break;
             }
+
             $pattern_type = $goal['pattern_type'];
 
             $match = $this->isUrlMatchingGoal($goal, $pattern_type, $url);
diff --git a/lang/en.json b/lang/en.json
index abd9d25e92..b7dad2a8b2 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -930,6 +930,7 @@
         "CaseSensitive": "Case sensitive match",
         "ChooseGoal": "Choose Goal",
         "ClickOutlink": "Click on a Link to an external website",
+        "SendEvent": "Send an event",
         "ColumnAverageOrderRevenueDocumentation": "Average Order Value (AOV) is the total revenue from all Ecommerce Orders divided by the number of orders.",
         "ColumnAveragePriceDocumentation": "The average revenue for this %s.",
         "ColumnAverageQuantityDocumentation": "The average quantity of this %s sold in Ecommerce orders.",
diff --git a/plugins/Events/Actions/ActionEvent.php b/plugins/Events/Actions/ActionEvent.php
index 3791de794d..9921c8d39b 100644
--- a/plugins/Events/Actions/ActionEvent.php
+++ b/plugins/Events/Actions/ActionEvent.php
@@ -38,6 +38,21 @@ class ActionEvent extends Action
         return (strlen($eventCategory) > 0 && strlen($eventAction) > 0);
     }
 
+    public function getEventAction()
+    {
+        return $this->request->getParam('e_a');
+    }
+
+    public function getEventCategory()
+    {
+        return $this->request->getParam('e_c');
+    }
+
+    public function getEventName()
+    {
+        return $this->request->getParam('e_n');
+    }
+
     public function getCustomFloatValue()
     {
         return $this->eventValue;
diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php
index 43fff0bfdb..176cafad24 100644
--- a/plugins/Goals/API.php
+++ b/plugins/Goals/API.php
@@ -79,7 +79,7 @@ class API extends \Piwik\Plugin\API
      *
      * @param int $idSite
      * @param string $name
-     * @param string $matchAttribute 'url', 'title', 'file', 'external_website' or 'manually'
+     * @param string $matchAttribute 'url', 'title', 'file', 'external_website', 'manually', 'event_action', 'event_category' or 'event_name'
      * @param string $pattern eg. purchase-confirmation.htm
      * @param string $patternType 'regex', 'contains', 'exact'
      * @param bool $caseSensitive
@@ -91,7 +91,7 @@ class API extends \Piwik\Plugin\API
     public function addGoal($idSite, $name, $matchAttribute, $pattern, $patternType, $caseSensitive = false, $revenue = false, $allowMultipleConversionsPerVisit = false)
     {
         Piwik::checkUserHasAdminAccess($idSite);
-        $this->checkPatternIsValid($patternType, $pattern);
+        $this->checkPatternIsValid($patternType, $pattern, $matchAttribute);
         $name = $this->checkName($name);
         $pattern = $this->checkPattern($pattern);
 
@@ -141,7 +141,7 @@ class API extends \Piwik\Plugin\API
         Piwik::checkUserHasAdminAccess($idSite);
         $name = $this->checkName($name);
         $pattern = $this->checkPattern($pattern);
-        $this->checkPatternIsValid($patternType, $pattern);
+        $this->checkPatternIsValid($patternType, $pattern, $matchAttribute);
         Db::get()->update(Common::prefixTable('goal'),
             array(
                  'name'            => $name,
@@ -157,10 +157,11 @@ class API extends \Piwik\Plugin\API
         Cache::regenerateCacheWebsiteAttributes($idSite);
     }
 
-    private function checkPatternIsValid($patternType, $pattern)
+    private function checkPatternIsValid($patternType, $pattern, $matchAttribute)
     {
         if ($patternType == 'exact'
             && substr($pattern, 0, 4) != 'http'
+            && substr($matchAttribute, 0, 6) != 'event_'
         ) {
             throw new Exception(Piwik::translate('Goals_ExceptionInvalidMatchingString', array("http:// or https://", "http://www.yourwebsite.com/newsletter/subscribed.html")));
         }
diff --git a/plugins/Goals/javascripts/goalsForm.js b/plugins/Goals/javascripts/goalsForm.js
index 643a9ce3af..400f3813c2 100644
--- a/plugins/Goals/javascripts/goalsForm.js
+++ b/plugins/Goals/javascripts/goalsForm.js
@@ -34,6 +34,25 @@ function showCancel() {
     });
 }
 
+function onMatchAttributeChange(matchAttribute)
+{
+    if ('event' === matchAttribute) {
+        $('.entityAddContainer .whereEvent').show();
+        $('.entityAddContainer .whereUrl').hide();
+    } else {
+        $('.entityAddContainer .whereEvent').hide();
+        $('.entityAddContainer .whereUrl').show();
+    }
+
+    $('#match_attribute_name').html(mappingMatchTypeName[matchAttribute]);
+    $('#examples_pattern').html(mappingMatchTypeExamples[matchAttribute]);
+}
+
+function updateMatchAttribute () {
+    var matchTypeId = $(this).val();
+    onMatchAttributeChange(matchTypeId);
+}
+
 // init the goal form with existing goal value, if any
 function initGoalForm(goalMethodAPI, submitText, goalName, matchAttribute, pattern, patternType, caseSensitive, revenue, allowMultiple, goalId) {
     $('#goal_name').val(goalName);
@@ -46,6 +65,14 @@ function initGoalForm(goalMethodAPI, submitText, goalName, matchAttribute, patte
     } else {
         $('select[name=trigger_type] option[value=visitors]').prop('selected', true);
     }
+
+    if (0 === matchAttribute.indexOf('event')) {
+        $('select[name=event_type] option[value=' + matchAttribute + ']').prop('selected', true);
+        matchAttribute = 'event';
+    }
+
+    onMatchAttributeChange(matchAttribute);
+
     $('input[name=match_attribute][value=' + matchAttribute + ']').prop('checked', true);
     $('input[name=allow_multiple][value=' + allowMultiple + ']').prop('checked', true);
     $('#match_attribute_name').html(mappingMatchTypeName[matchAttribute]);
@@ -66,6 +93,7 @@ function initGoalForm(goalMethodAPI, submitText, goalName, matchAttribute, patte
 }
 
 function bindGoalForm() {
+
     $('select[name=trigger_type]').click(function () {
         var triggerTypeId = $(this).val();
         if (triggerTypeId == "manually") {
@@ -79,10 +107,9 @@ function bindGoalForm() {
         }
     });
 
-    $('input[name=match_attribute]').click(function () {
-        var matchTypeId = $(this).val();
-        $('#match_attribute_name').html(mappingMatchTypeName[matchTypeId]);
-        $('#examples_pattern').html(mappingMatchTypeExamples[matchTypeId]);
+    $(document).bind('Goals.edit', function () {
+        $('input[name=match_attribute]').off('change', updateMatchAttribute);
+        $('input[name=match_attribute]').change(updateMatchAttribute);
     });
 
     $('#goal_submit').click(function () {
@@ -126,6 +153,11 @@ function ajaxAddGoal() {
         parameters.caseSensitive = 0;
     } else {
         parameters.matchAttribute = $('input[name=match_attribute]:checked').val();
+
+        if (parameters.matchAttribute === 'event') {
+            parameters.matchAttribute = $('select[name=event_type]').val();
+        }
+
         parameters.patternType = $('[name=pattern_type]').val();
         parameters.pattern = encodeURIComponent($('input[name=pattern]').val());
         parameters.caseSensitive = $('#case_sensitive').prop('checked') == true ? 1 : 0;
diff --git a/plugins/Goals/templates/_addEditGoal.twig b/plugins/Goals/templates/_addEditGoal.twig
index c8feb87bc5..f9449d8ce9 100644
--- a/plugins/Goals/templates/_addEditGoal.twig
+++ b/plugins/Goals/templates/_addEditGoal.twig
@@ -55,7 +55,8 @@
         "url": "{{ 'Goals_URL'|translate }}",
         "title": "{{ 'Goals_PageTitle'|translate }}",
         "file": "{{ 'Goals_Filename'|translate }}",
-        "external_website": "{{ 'Goals_ExternalWebsiteUrl'|translate }}"
+        "external_website": "{{ 'Goals_ExternalWebsiteUrl'|translate }}",
+        "event": "{{ 'Events_Event'|translate }}"
     };
     var mappingMatchTypeExamples = {
         "url": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'checkout/confirmation'") }} \
@@ -67,7 +68,10 @@
 		<br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'(.*)\\\.zip'") }}",
         "external_website": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'amazon.com'") }} \
 		<br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'http://mypartner.com/landing.html'") }} \
-		<br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'http://www.amazon.com\\\/(.*)\\\/yourAffiliateId'") }}"
+		<br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'http://www.amazon.com\\\/(.*)\\\/yourAffiliateId'") }}",
+        "event": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'video'") }} \
+		<br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'click'") }} \
+		<br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'(.*)_banner'") }}"
     };
     bindGoalForm();
 
diff --git a/plugins/Goals/templates/_formAddGoal.twig b/plugins/Goals/templates/_formAddGoal.twig
index 002df4954b..7bff75f8cb 100644
--- a/plugins/Goals/templates/_formAddGoal.twig
+++ b/plugins/Goals/templates/_formAddGoal.twig
@@ -25,6 +25,9 @@
                     <input type="radio" id="match_attribute_title" value="title" name="match_attribute"/>
                     <label for="match_attribute_title">{{ 'Goals_VisitPageTitle'|translate }}</label>
                     <br/>
+                    <input type="radio" id="match_attribute_event" value="event" name="match_attribute"/>
+                    <label for="match_attribute_event">{{ 'Goals_SendEvent'|translate }}</label>
+                    <br/>
                     <input type="radio" id="match_attribute_file" value="file" name="match_attribute"/>
                     <label for="match_attribute_file">{{ 'Goals_Download'|translate }}</label>
                     <br/>
@@ -35,7 +38,11 @@
             </tbody>
             <tbody id="match_attribute_section">
             <tr>
-                <td class="first">{{ 'Goals_WhereThe'|translate }} <span id="match_attribute_name"></span></td>
+                <td class="first">{{ 'Goals_WhereThe'|translate }} <span class="whereUrl" id="match_attribute_name"></span><select name="event_type" class="whereEvent inp">
+                        <option value="event_category">{{ 'Events_EventCategory'|translate }}</option>
+                        <option value="event_action">{{ 'Events_EventAction'|translate }}</option>
+                        <option value="event_name">{{ 'Events_EventName'|translate }}</option>
+                    </select></td>
                 <td>
                     <select name="pattern_type" class="inp">
                         <option value="contains">{{ 'Goals_Contains'|translate("") }}</option>
diff --git a/plugins/Goals/templates/_listGoalEdit.twig b/plugins/Goals/templates/_listGoalEdit.twig
index 11c1172f90..5118cfb72a 100644
--- a/plugins/Goals/templates/_listGoalEdit.twig
+++ b/plugins/Goals/templates/_listGoalEdit.twig
@@ -50,7 +50,10 @@
         "file": "{{ 'Goals_Download'|translate }}",
         "url": "{{ 'Goals_VisitUrl'|translate }}",
         "title": "{{ 'Goals_VisitPageTitle'|translate }}",
-        "external_website": "{{ 'Goals_ClickOutlink'|translate }}"
+        "external_website": "{{ 'Goals_ClickOutlink'|translate }}",
+        "event_action": "{{ 'Goals_SendEvent'|translate }} ({{ 'Events_EventAction'|translate }})",
+        "event_category": "{{ 'Goals_SendEvent'|translate }} ({{ 'Events_EventCategory'|translate }})",
+        "event_name": "{{ 'Goals_SendEvent'|translate }} ({{ 'Events_EventName'|translate }})"
     };
 
     $(document).ready(function () {
diff --git a/plugins/Goals/tests/APITest.php b/plugins/Goals/tests/APITest.php
new file mode 100644
index 0000000000..010449e41f
--- /dev/null
+++ b/plugins/Goals/tests/APITest.php
@@ -0,0 +1,228 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\Goals\tests;
+use Piwik\Access;
+use Piwik\Plugins\Goals\API;
+use Piwik\Tests\Fixture;
+
+/**
+ * @group Goals
+ * @group Plugins
+ * @group APITest
+ * @group Database
+ */
+class APITest extends \DatabaseTestCase
+{
+    /**
+     * @var API
+     */
+    private $api;
+
+    private $idSite = 1;
+
+    public function setUp()
+    {
+        parent::setUp();
+        $this->api = API::getInstance();
+
+        Fixture::createWebsite('2014-01-01 00:00:00');
+        Fixture::createWebsite('2014-01-01 00:00:00');
+    }
+
+    public function test_addGoal_shouldReturnGoalId_IfCreationIsSuccessful()
+    {
+        $idGoal = $this->createAnyGoal();
+
+        $this->assertSame(1, $idGoal);
+    }
+
+    public function test_addGoal_shouldSucceed_IfOnlyMinimumFieldsGiven()
+    {
+        $idGoal = $this->api->addGoal($this->idSite, 'MyName', 'url', 'http://www.test.de', 'exact');
+
+        $this->assertGoal($idGoal, 'MyName', 'url', 'http://www.test.de', 'exact', 0, 0, 0);
+    }
+
+    public function test_addGoal_ShouldSucceed_IfAllFieldsGiven()
+    {
+        $idGoal = $this->api->addGoal($this->idSite, 'MyName', 'url', 'http://www.test.de', 'exact', true, 50, true);
+
+        $this->assertGoal($idGoal, 'MyName', 'url', 'http://www.test.de', 'exact', 1, 50, 1);
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage Goals_ExceptionInvalidMatchingString
+     */
+    public function test_addGoal_shouldThrowException_IfPatternTypeIsExactAndMatchAttributeNotEvent()
+    {
+        $this->api->addGoal($this->idSite, 'MyName', 'url', 'www.test.de', 'exact');
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage Goals_ExceptionInvalidMatchingString
+     */
+    public function test_addGoal_shouldThrowException_IfPatternTypeIsExactAndMatchAttributeNotEvent2()
+    {
+        $this->api->addGoal($this->idSite, 'MyName', 'external_website', 'www.test.de', 'exact');
+    }
+
+    public function test_addGoal_shouldNotThrowException_IfPatternTypeIsExactAndMatchAttributeIsEvent()
+    {
+        $this->api->addGoal($this->idSite, 'MyName1', 'event_action', 'test', 'exact');
+        $this->api->addGoal($this->idSite, 'MyName2', 'event_name', 'test', 'exact');
+        $idGoal = $this->api->addGoal($this->idSite, 'MyName3', 'event_category', 'test', 'exact');
+
+        $this->assertSame('3', $idGoal);
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage checkUserHasAdminAccess Fake exception
+     */
+    public function test_addGoal_shouldThrowException_IfNotEnoughPermission()
+    {
+        $this->setNonAdminUser();
+        $this->createAnyGoal();
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage checkUserHasAdminAccess Fake exception
+     */
+    public function test_updateGoal_shouldThrowException_IfNotEnoughPermission()
+    {
+        $idGoal = $this->createAnyGoal();
+        $this->assertSame(1, $idGoal); // make sure goal is created and does not already fail here
+        $this->setNonAdminUser();
+        $this->api->updateGoal($this->idSite, $idGoal, 'MyName', 'url', 'www.test.de', 'exact');
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage Goals_ExceptionInvalidMatchingString
+     */
+    public function test_updateGoal_shouldThrowException_IfPatternTypeIsExactAndMatchAttributeNotEvent()
+    {
+        $idGoal = $this->createAnyGoal();
+        $this->api->updateGoal($this->idSite, $idGoal, 'MyName', 'url', 'www.test.de', 'exact');
+    }
+
+    public function test_updateGoal_shouldNotThrowException_IfPatternTypeIsExactAndMatchAttributeIsEvent()
+    {
+        $idGoal = $this->createAnyGoal();
+        $this->api->updateGoal($this->idSite, $idGoal, 'MyName', 'event_action', 'www.test.de', 'exact');
+        $this->api->updateGoal($this->idSite, $idGoal, 'MyName', 'event_category', 'www.test.de', 'exact');
+        $this->api->updateGoal($this->idSite, $idGoal, 'MyName', 'event_name', 'www.test.de', 'exact');
+
+        $this->assertSame(1, $idGoal);
+    }
+
+    public function test_updateGoal_shouldUpdateAllGivenFields()
+    {
+        $idGoal = $this->createAnyGoal();
+        $this->api->updateGoal($this->idSite, $idGoal, 'UpdatedName', 'file', 'http://www.updatetest.de', 'contains', true, 999, true);
+
+        $this->assertGoal($idGoal, 'UpdatedName', 'file', 'http://www.updatetest.de', 'contains', 1, 999, 1);
+    }
+
+    public function test_updateGoal_shouldUpdateMinimalFields_ShouldLeaveOtherFieldsUntouched()
+    {
+        $idGoal = $this->createAnyGoal();
+        $this->api->updateGoal($this->idSite, $idGoal, 'UpdatedName', 'file', 'http://www.updatetest.de', 'contains');
+
+        $this->assertGoal($idGoal, 'UpdatedName', 'file', 'http://www.updatetest.de', 'contains', 0, 0, 0);
+    }
+
+    public function test_deleteGoal_shouldNotDeleteAGoal_IfGoalIdDoesNotExist()
+    {
+        $this->assertHasNoGoals();
+
+        $this->createAnyGoal();
+        $this->assertHasGoals();
+
+        $this->api->deleteGoal($this->idSite, 999);
+        $this->assertHasGoals();
+    }
+
+    public function test_deleteGoal_shouldNotDeleteAGoal_IfSiteDoesNotMatchGoalId()
+    {
+        $this->assertHasNoGoals();
+
+        $idGoal = $this->createAnyGoal();
+        $this->assertHasGoals();
+
+        $this->api->deleteGoal($idSite = 2, $idGoal);
+        $this->assertHasGoals();
+    }
+
+    public function test_deleteGoal_shouldDeleteAGoal_IfGoalAndSiteMatches()
+    {
+        $this->assertHasNoGoals();
+
+        $idGoal = $this->createAnyGoal();
+        $this->assertHasGoals();
+
+        $this->api->deleteGoal($this->idSite, $idGoal);
+        $this->assertHasNoGoals();
+    }
+
+    private function assertHasGoals()
+    {
+        $goals = $this->getGoals();
+        $this->assertNotEmpty($goals);
+    }
+
+    private function assertHasNoGoals()
+    {
+        $goals = $this->getGoals();
+        $this->assertEmpty($goals);
+    }
+
+    private function assertGoal($idGoal, $name, $url, $pattern, $patternType, $caseSenstive = 0, $revenue = 0, $allowMultiple = 0)
+    {
+        $expected = array($idGoal => array(
+            'idsite' => $this->idSite,
+            'idgoal' => $idGoal,
+            'name' => $name,
+            'match_attribute' => $url,
+            'pattern' => $pattern,
+            'pattern_type' => $patternType,
+            'case_sensitive' => $caseSenstive,
+            'allow_multiple' => $allowMultiple,
+            'revenue' => $revenue,
+            'deleted' => 0
+        ));
+
+        $goals = $this->getGoals();
+
+        $this->assertEquals($expected, $goals);
+    }
+
+    private function getGoals()
+    {
+        return $this->api->getGoals($this->idSite);
+    }
+
+    private function createAnyGoal()
+    {
+        return $this->api->addGoal($this->idSite, 'MyName1', 'event_action', 'test', 'exact');
+    }
+
+    protected function setNonAdminUser()
+    {
+        $pseudoMockAccess = new \FakeAccess;
+        \FakeAccess::setSuperUserAccess(false);
+        \FakeAccess::$idSitesView = array(99);
+        \FakeAccess::$identity = 'aUser';
+        Access::setSingletonInstance($pseudoMockAccess);
+    }
+
+}
diff --git a/tests/PHPUnit/Fixtures/SomeVisitsAllConversions.php b/tests/PHPUnit/Fixtures/SomeVisitsAllConversions.php
index 74254a586e..185fc594a8 100644
--- a/tests/PHPUnit/Fixtures/SomeVisitsAllConversions.php
+++ b/tests/PHPUnit/Fixtures/SomeVisitsAllConversions.php
@@ -53,6 +53,18 @@ class SomeVisitsAllConversions extends Fixture
                 $revenue = 10, $allowMultipleConversions = true
             );
         }
+
+        if (!self::goalExists($idSite = 1, $idGoal = 3)) {
+            API::getInstance()->addGoal($this->idSite, 'click event', 'event_action', 'click', 'contains');
+        }
+
+        if (!self::goalExists($idSite = 1, $idGoal = 4)) {
+            API::getInstance()->addGoal($this->idSite, 'category event', 'event_category', 'The_Category', 'exact', true);
+        }
+
+        if (!self::goalExists($idSite = 1, $idGoal = 5)) {
+            API::getInstance()->addGoal($this->idSite, 'name event', 'event_name', 'the_name', 'exact');
+        }
     }
 
     private function trackVisits()
@@ -93,5 +105,17 @@ class SomeVisitsAllConversions extends Fixture
         $t->setTokenAuth($this->getTokenAuth());
         $t->setForceNewVisit();
         $t->doTrackPageView('This is tracked in a new visit.');
+
+        // should trigger two goals at once (event_category, event_action)
+        $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.3)->getDatetime());
+        self::checkResponse($t->doTrackEvent('The_Category', 'click_action', 'name'));
+
+        // should not trigger a goal (the_category is case senstive goal)
+        $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.4)->getDatetime());
+        self::checkResponse($t->doTrackEvent('the_category', 'click_action', 'name'));
+
+        // should trigger a goal for event_name
+        $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.4)->getDatetime());
+        self::checkResponse($t->doTrackEvent('other_category', 'other_action', 'the_name'));
     }
 }
\ No newline at end of file
diff --git a/tests/PHPUnit/Integration/TrackGoalsAllowMultipleConversionsPerVisitTest.php b/tests/PHPUnit/Integration/TrackGoalsAllowMultipleConversionsPerVisitTest.php
index 6ed0f41a46..cac31449c2 100755
--- a/tests/PHPUnit/Integration/TrackGoalsAllowMultipleConversionsPerVisitTest.php
+++ b/tests/PHPUnit/Integration/TrackGoalsAllowMultipleConversionsPerVisitTest.php
@@ -39,11 +39,11 @@ class TrackGoalsAllowMultipleConversionsPerVisitTest extends IntegrationTestCase
 
         // test delete is working as expected
         $goals = API::getInstance()->getGoals($idSite);
-        $this->assertTrue(2 == count($goals));
+        $this->assertTrue(5 == count($goals));
         API::getInstance()->deleteGoal($idSite, self::$fixture->idGoal_OneConversionPerVisit);
         API::getInstance()->deleteGoal($idSite, self::$fixture->idGoal_MultipleConversionPerVisit);
         $goals = API::getInstance()->getGoals($idSite);
-        $this->assertTrue(empty($goals));
+        $this->assertTrue(3 == count($goals));
     }
 
     public function getApiForTesting()
diff --git a/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitTime.getVisitInformationPerServerTime_day.xml b/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitTime.getVisitInformationPerServerTime_day.xml
index 38b1c4d247..9c6eba0c0c 100644
--- a/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitTime.getVisitInformationPerServerTime_day.xml
+++ b/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitTime.getVisitInformationPerServerTime_day.xml
@@ -4,10 +4,10 @@
 		<label>0h</label>
 		<nb_uniq_visitors>1</nb_uniq_visitors>
 		<nb_visits>2</nb_visits>
-		<nb_actions>2</nb_actions>
-		<max_actions>1</max_actions>
-		<sum_visit_length>1120</sum_visit_length>
-		<bounce_count>2</bounce_count>
+		<nb_actions>5</nb_actions>
+		<max_actions>3</max_actions>
+		<sum_visit_length>363</sum_visit_length>
+		<bounce_count>0</bounce_count>
 		<goals>
 			<row idgoal='1'>
 				<nb_conversions>2</nb_conversions>
@@ -19,8 +19,23 @@
 				<nb_visits_converted>1</nb_visits_converted>
 				<revenue>666</revenue>
 			</row>
+			<row idgoal='3'>
+				<nb_conversions>2</nb_conversions>
+				<nb_visits_converted>2</nb_visits_converted>
+				<revenue>0</revenue>
+			</row>
+			<row idgoal='4'>
+				<nb_conversions>1</nb_conversions>
+				<nb_visits_converted>1</nb_visits_converted>
+				<revenue>0</revenue>
+			</row>
+			<row idgoal='5'>
+				<nb_conversions>1</nb_conversions>
+				<nb_visits_converted>1</nb_visits_converted>
+				<revenue>0</revenue>
+			</row>
 		</goals>
-		<nb_conversions>4</nb_conversions>
+		<nb_conversions>8</nb_conversions>
 		<revenue>1332</revenue>
 	</row>
 	<row>
diff --git a/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitsSummary.get_day.xml b/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitsSummary.get_day.xml
index 6a1390c2e5..76b20063bf 100644
--- a/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitsSummary.get_day.xml
+++ b/tests/PHPUnit/Integration/expected/test_trackGoals_allowMultipleConversionsPerVisit__VisitsSummary.get_day.xml
@@ -2,12 +2,12 @@
 <result>
 	<nb_uniq_visitors>1</nb_uniq_visitors>
 	<nb_visits>2</nb_visits>
-	<nb_actions>2</nb_actions>
-	<nb_visits_converted>1</nb_visits_converted>
-	<bounce_count>2</bounce_count>
-	<sum_visit_length>1120</sum_visit_length>
-	<max_actions>1</max_actions>
-	<bounce_rate>100%</bounce_rate>
-	<nb_actions_per_visit>1</nb_actions_per_visit>
-	<avg_time_on_site>560</avg_time_on_site>
+	<nb_actions>5</nb_actions>
+	<nb_visits_converted>2</nb_visits_converted>
+	<bounce_count>0</bounce_count>
+	<sum_visit_length>363</sum_visit_length>
+	<max_actions>3</max_actions>
+	<bounce_rate>0%</bounce_rate>
+	<nb_actions_per_visit>2.5</nb_actions_per_visit>
+	<avg_time_on_site>182</avg_time_on_site>
 </result>
\ No newline at end of file
-- 
GitLab