diff --git a/README.md b/README.md index a5d10b5..7400975 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,24 @@ Provides step definitions to check GTM implementation on web pages -``` -DennisDigital\Behat\Gtm\Context\GtmContext -``` - ## Installation ``` composer require dennisdigital/behat-gtm ``` +### Configuration +Setup extension by specifying your behat.yml: + +Example on how to add extension and configure items to be ignored. +```yaml +default: + extensions: + DennisDigital\Behat\Gtm\Context\GtmContext: + ignoreGtmItems: + - gtm.element +``` + ## Step Definitions ```gherkin @@ -21,3 +29,37 @@ Given google tag manager data layer setting :key should match :value ``` > Note: use `@javascript` to check using JS + +## Using dot notation to get nested GTM elements +For the nested GTM elements like +``` +{ + "event": "productImpression", + "timestamp": 1694581407435, + "eventLabel2": "97 items", + "ecommerce": { + "currencyCode": "KWD", + "impressions": [ + { + "name": "Knockout Front-Close Sports Bra", + "id": "1119313554A2", + "price": 23, + "category": "Victorias Secret/Bras/Styles/Lightly Lined Bras", + "variant": "", + "product_style_code": "11193135", + "dimension2": "configurable", + "dimension3": "Regular Product", + "dimension4": 4, + "brand": "Victorias Secret", + "list": "PLP|Victorias Secret|Apparel|All Apparel", + "position": "1" + }, + ] + }, + "gtm.uniqueEventId": 127 +} +``` +To extract the GTM value nested in arrays i.e name under impressions. We can use dot notation +``` +Given google tag manager data layer setting "ecommerce.impressions[0].name" should match "~Knockout~" +``` \ No newline at end of file diff --git a/composer.json b/composer.json index 912b699..4b5ce6f 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,8 @@ "minimum-stability": "dev", "prefer-stable": true, "require": { - "behat/mink": "~1.5" + "behat/mink": "~1.5", + "adbario/php-dot-notation": "^3.3" }, "autoload": { "psr-4": {"DennisDigital\\Behat\\Gtm\\": "src"} diff --git a/src/Context/GtmContext.php b/src/Context/GtmContext.php index 7ccff97..39933c9 100644 --- a/src/Context/GtmContext.php +++ b/src/Context/GtmContext.php @@ -9,6 +9,14 @@ * @package DennisDigital\Behat\Gtm\Context */ class GtmContext extends RawMinkContext { + + private $parameters; + + public function __construct(array $parameters = []) + { + $this->parameters = $parameters; + } + /** * Check the google tag manager present in the page * @@ -23,6 +31,27 @@ public function tagManagerIdIs($id) { } } + /** + * Waits until the Datalayer object is updated and then checks if property is available. + * + * @Given I wait for the data layer setting :arg1 + */ + public function waitDataLayerSetting($key, $loops = 10) { + $loop = 0; + do { + try { + $loop++; + $this->getDataLayerValue($key); + return true; + } catch (\Exception $e) { + // Ommit the exception until we finish the loop. + } + sleep(1); + } while ($loop < $loops); + + throw new \Exception("$key not found after waiting for $loops seconds."); + } + /** * Check google tag manager data layer contain key value pair * @@ -59,19 +88,86 @@ protected function getDataLayerValue($key) { // Loop through the array and return the data layer value foreach ($json_arr as $json_item) { - if (isset($json_item[$key])) { - return $json_item[$key]; + // Check if the key contains dot. + if (strpos($key, '.', 0) !== false) { + // Get value using dot notation. + $value = $this->getDotValue($json_item, $key); + if (!is_null($value)) { + return $value; + } + } elseif (isset($json_item[$key])) { + return $json_item[$key]; } } throw new \Exception($key . ' not found.'); } + /** + * Convert arrays to dot notation. + * + * @param $value + * @param $key + * @return mixed|null + */ + private function getDotValue($value, $key) { + $dot = dot($value); + // Check if the key contains [. + // When the key contains [0] it means that we are trying to get the value from specific index in an array. + // i.e. foo.bar[0].foo. + $pos = strpos($key, '[',0); + if ($pos) { + // Trim before [. + $keyBeforeArray = substr($key, 0, $pos); + $values = $dot->get($keyBeforeArray); + if (!empty($values)) { + // Filter the number from string. + $index = $this->getNumberFromString($key); + // Filter last variable from key. + $arrayIndexPosition = strrpos($key, "]."); + if ($arrayIndexPosition) { + $key = substr($key, $arrayIndexPosition + 2); + } + $dot = dot($values[$index]); + } + } + return $dot->get($key); + } + + /** + * Get number inside array index. + * + * @param $key + * @return string + * @throws \Exception + */ + protected function getNumberFromString($key) { + $pattern = '/\[(\d+)]/'; + if (preg_match_all($pattern, $key, $matches)) { + return $matches[1][0]; + } + throw new \Exception('Number not found in key ' . $key ); + } + /** * Get dataLayer variable JSON. */ protected function getDataLayerJson() { if ($this->getSession()->getDriver() instanceof Selenium2Driver) { - $json_arr = $this->getSession()->getDriver()->evaluateScript('return dataLayer;'); + $ignore_items = isset($this->parameters['ignoreGtmItems']) ? $this->parameters['ignoreGtmItems'] : []; + if (empty($ignore_items)) { + // Return dataLayer array as is. + $script = 'return dataLayer'; + } + else { + // Remove items to be ignored before returning the array. + $script = 'return dataLayer.map(i => { '; + foreach ($ignore_items as $i => $key) { + $script .= "delete(i['$key']);"; + } + $script .= ' return i; });'; + } + var_dump($script); + $json_arr = $this->getSession()->getDriver()->evaluateScript($script); } else { $json_arr = json_decode($this->getDataLayerJsonFromSource(), TRUE);