Skip to content
50 changes: 46 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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~"
```
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
Expand Down
102 changes: 99 additions & 3 deletions src/Context/GtmContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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
*
Expand Down Expand Up @@ -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);
Expand Down