Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:32:45 +08:00
commit c02dafae1d
41 changed files with 17283 additions and 0 deletions

View File

@@ -0,0 +1,536 @@
---
name: gtm-custom-templates
description: Expert guidance for building Google Tag Manager custom templates using sandboxed JavaScript. Use when creating custom tag templates, custom variable templates, server-side client templates, converting regular JavaScript to sandboxed JS, debugging template code, writing template tests, publishing to the Community Template Gallery, working with .tpl template files, or using sandboxed JavaScript APIs like require(), sendPixel, injectScript, and other GTM template APIs.
---
# GTM Custom Templates Builder
## Overview
This skill provides comprehensive expertise for building Google Tag Manager custom templates using sandboxed JavaScript. Master template structure, sandboxed JavaScript APIs, permissions, testing, and publishing to the Community Template Gallery.
## When to Use This Skill
Invoke this skill when:
- Building a custom tag template for a third-party service
- Creating a custom variable template for complex data manipulation
- Developing server-side client templates
- Converting regular JavaScript to sandboxed JavaScript
- Debugging sandboxed JavaScript API errors
- Understanding template permissions and security
- Writing tests for custom templates
- Publishing templates to the Community Template Gallery
- Troubleshooting "undefined is not a function" errors in templates
- Implementing HTTP requests, pixel firing, or script injection in templates
## Sandboxed JavaScript Fundamentals
### What is Sandboxed JavaScript?
Sandboxed JavaScript is a restricted subset of JavaScript used in GTM custom templates. It provides security by limiting what code can execute while offering safe APIs for common operations.
**Key Differences from Regular JavaScript:**
- No direct DOM access
- No window/document object
- No eval() or Function()
- No arbitrary HTTP requests
- Must use require() to import APIs
- All operations require explicit permissions
### Basic Template Structure
```javascript
// Import required APIs
const sendPixel = require('sendPixel');
const logToConsole = require('logToConsole');
const encodeUriComponent = require('encodeUriComponent');
// Access template configuration
const endpoint = data.endpoint;
const eventName = data.eventName;
// Execute template logic
const url = endpoint + '?event=' + encodeUriComponent(eventName);
sendPixel(url, data.gtmOnSuccess, data.gtmOnFailure);
```
## Building Tag Templates
### Tag Template Workflow
1. **Define Template Info**
- Template type (TAG)
- Display name and description
- Categories and branding
- Container contexts (WEB, SERVER, etc.)
2. **Configure Template Parameters**
- Add fields for user configuration
- Set field types (TEXT, SELECT, CHECKBOX, etc.)
- Define validation rules
- Set default values
3. **Write Sandboxed JavaScript Code**
- Require necessary APIs
- Access data from template fields
- Implement tag logic
- Call gtmOnSuccess/gtmOnFailure
4. **Set Permissions**
- Configure required permissions
- Specify allowed URLs, cookies, etc.
- Minimize permission scope
5. **Write Tests**
- Test successful execution
- Test error handling
- Test edge cases
### Common Tag Template Patterns
**Simple Pixel Tag:**
```javascript
const sendPixel = require('sendPixel');
const encodeUriComponent = require('encodeUriComponent');
const pixelUrl = data.pixelUrl +
'?id=' + encodeUriComponent(data.pixelId) +
'&event=' + encodeUriComponent(data.eventName);
sendPixel(pixelUrl, data.gtmOnSuccess, data.gtmOnFailure);
```
**HTTP Request Tag:**
```javascript
const sendHttpRequest = require('sendHttpRequest');
const JSON = require('JSON');
const postBody = JSON.stringify({
event: data.eventName,
userId: data.userId
});
const options = {
headers: {'Content-Type': 'application/json'},
method: 'POST'
};
sendHttpRequest(data.endpoint, options, postBody)
.then(data.gtmOnSuccess)
.catch(data.gtmOnFailure);
```
**Script Injection Tag:**
```javascript
const injectScript = require('injectScript');
const queryPermission = require('queryPermission');
const url = 'https://example.com/script.js';
if (queryPermission('inject_script', url)) {
injectScript(url, data.gtmOnSuccess, data.gtmOnFailure);
} else {
data.gtmOnFailure();
}
```
## Building Variable Templates
### Variable Template Basics
Variable templates return a value that can be used in other GTM configurations.
**Cookie Variable:**
```javascript
const getCookieValues = require('getCookieValues');
const cookieName = data.cookieName;
const cookies = getCookieValues(cookieName);
if (cookies && cookies.length > 0) {
return cookies[0];
}
return data.defaultValue || '';
```
**LocalStorage Variable:**
```javascript
const localStorage = require('localStorage');
const key = data.storageKey;
const value = localStorage.getItem(key);
return value || data.defaultValue;
```
**Custom JavaScript Variable:**
```javascript
const makeTableMap = require('makeTableMap');
const makeNumber = require('makeNumber');
// Convert table to lookup map
const lookupTable = makeTableMap(data.table, 'key', 'value');
// Perform lookup
const inputValue = data.inputVariable;
return lookupTable[inputValue] || data.defaultValue;
```
## Sandboxed JavaScript API Reference
### Commonly Used APIs
**Data Type Conversion:**
```javascript
const makeInteger = require('makeInteger');
const makeNumber = require('makeNumber');
const makeString = require('makeString');
const num = makeNumber('123.45'); // 123.45
const int = makeInteger('123.45'); // 123
const str = makeString(123); // '123'
```
**Network APIs:**
```javascript
const sendPixel = require('sendPixel');
const sendHttpRequest = require('sendHttpRequest');
const injectScript = require('injectScript');
// Pixel
sendPixel(url, onSuccess, onFailure);
// HTTP Request
sendHttpRequest(url, options, body)
.then(onSuccess)
.catch(onFailure);
// Script injection
injectScript(url, onSuccess, onFailure);
```
**Storage APIs:**
```javascript
const getCookieValues = require('getCookieValues');
const setCookie = require('setCookie');
const localStorage = require('localStorage');
// Cookies
const cookies = getCookieValues('cookieName');
setCookie('name', 'value', {
domain: 'example.com',
path: '/',
'max-age': 3600
});
// LocalStorage
const value = localStorage.getItem('key');
localStorage.setItem('key', 'value');
```
**Utility APIs:**
```javascript
const encodeUri = require('encodeUri');
const encodeUriComponent = require('encodeUriComponent');
const decodeUri = require('decodeUri');
const decodeUriComponent = require('decodeUriComponent');
const JSON = require('JSON');
const Math = require('Math');
const Object = require('Object');
// URL encoding
const encoded = encodeUriComponent('hello world');
// JSON
const obj = JSON.parse('{"key":"value"}');
const str = JSON.stringify({key: 'value'});
// Math
const random = Math.random();
const rounded = Math.round(3.7);
```
**DOM APIs (Limited):**
```javascript
const callInWindow = require('callInWindow');
const copyFromWindow = require('copyFromWindow');
// Call window function
callInWindow('functionName', arg1, arg2);
// Copy from window
const gaData = copyFromWindow('ga');
```
## Permissions System
### Understanding Permissions
Every API requires explicit permission configuration. Permissions define what URLs, cookies, or data the template can access.
**sendPixel Permission:**
```json
{
"instance": {
"key": {"publicId": "send_pixel", "versionId": "1"},
"param": [{
"key": "allowedUrls",
"value": {"type": 1, "string": "specific"},
"list": [
{"type": 1, "string": "https://example.com/*"}
]
}]
}
}
```
**get_cookies Permission:**
```json
{
"instance": {
"key": {"publicId": "get_cookies", "versionId": "1"},
"param": [{
"key": "cookieAccess",
"value": {"type": 1, "string": "specific"},
"list": [
{"type": 1, "string": "session_id"},
{"type": 1, "string": "user_*"}
]
}]
}
}
```
**Best Practice:** Use the most restrictive permissions possible. Avoid "any" when you can specify exact URLs or cookie names.
## Template Testing
### Writing Tests
```javascript
// Test successful execution
scenarios:
- name: Tag fires successfully
code: |-
const mockData = {
endpoint: 'https://example.com/api',
eventName: 'test_event'
};
runCode(mockData);
assertApi('gtmOnSuccess').wasCalled();
assertApi('sendHttpRequest').wasCalledWith(
'https://example.com/api',
assertThat.objectContaining({method: 'POST'})
);
```
### Test Mocking
```javascript
// Mock API returns
mock('getCookieValues', (name) => {
if (name === 'session_id') {
return ['abc123'];
}
return [];
});
// Test with mock
const mockData = {cookieName: 'session_id'};
let result = runCode(mockData);
assertThat(result).isEqualTo('abc123');
```
## Common Patterns and Solutions
### Converting Regular JS to Sandboxed JS
**Regular JavaScript (won't work):**
```javascript
// Direct DOM access
document.getElementById('element');
// Direct window access
window.dataLayer.push({});
// XMLHttpRequest
const xhr = new XMLHttpRequest();
```
**Sandboxed JavaScript (will work):**
```javascript
// Use callInWindow
const callInWindow = require('callInWindow');
callInWindow('dataLayer.push', {event: 'custom'});
// Use sendHttpRequest
const sendHttpRequest = require('sendHttpRequest');
sendHttpRequest(url, {method: 'GET'})
.then(data.gtmOnSuccess)
.catch(data.gtmOnFailure);
```
### Error Handling
```javascript
const sendHttpRequest = require('sendHttpRequest');
const logToConsole = require('logToConsole');
sendHttpRequest(data.endpoint)
.then(response => {
logToConsole('Success:', response);
data.gtmOnSuccess();
})
.catch(error => {
logToConsole('Error:', error);
data.gtmOnFailure();
});
```
### Data Validation
```javascript
const makeNumber = require('makeNumber');
const getType = require('getType');
// Validate input
if (getType(data.value) === 'undefined') {
data.gtmOnFailure();
return;
}
// Convert and validate
const numValue = makeNumber(data.value);
if (numValue === undefined) {
logToConsole('Invalid number');
data.gtmOnFailure();
return;
}
// Continue with valid data
data.gtmOnSuccess();
```
## Template Field Configuration
### Common Field Types
```javascript
// Text input
{
"type": "TEXT",
"name": "apiKey",
"displayName": "API Key",
"simpleValueType": true,
"valueValidators": [
{"type": "NON_EMPTY"},
{"type": "REGEX", "args": ["^[a-zA-Z0-9]{32}$"]}
]
}
// Select dropdown
{
"type": "SELECT",
"name": "eventType",
"displayName": "Event Type",
"selectItems": [
{"value": "pageview", "displayValue": "Pageview"},
{"value": "event", "displayValue": "Event"}
],
"simpleValueType": true
}
// Checkbox
{
"type": "CHECKBOX",
"name": "enableDebug",
"checkboxText": "Enable Debug Logging",
"simpleValueType": true,
"defaultValue": false
}
// Group
{
"type": "GROUP",
"name": "advancedSettings",
"displayName": "Advanced Settings",
"groupStyle": "ZIPPY_CLOSED",
"subParams": [
// Nested fields here
]
}
```
## Publishing to Community Template Gallery
1. **Complete Template Info**
- Descriptive name and description
- Appropriate categories
- Brand information
- Thumbnail image (optional)
2. **Add Comprehensive Tests**
- Test all major code paths
- Test error handling
- Test edge cases
3. **Document Template**
- Clear field descriptions
- Help text for complex fields
- Notes section with usage instructions
4. **Submit for Review**
- Export template from GTM
- Submit to Community Template Gallery
- Address reviewer feedback
## Template Boilerplates
This skill includes ready-to-use template boilerplates:
- **assets/tag-template-boilerplate.tpl** - Complete tag template structure
- **assets/variable-template-boilerplate.tpl** - Complete variable template structure
Copy these files and customize for your specific use case.
## References
This skill includes comprehensive reference documentation:
- **references/sandboxed-javascript-api.md** - Complete API reference
- **references/custom-templates-guide.md** - Simo Ahava's comprehensive guide
- **references/template-testing.md** - Testing documentation and patterns
- **references/template-examples.md** - Real-world template examples
Search references for specific APIs:
```bash
grep -r "sendHttpRequest" references/
grep -r "permissions" references/
grep -r "testing" references/
```
## Integration with Other Skills
- **gtm-general** - Understanding GTM concepts and architecture
- **gtm-setup** - Testing templates in GTM containers
- **gtm-tags** - Understanding tag structure for tag templates
- **gtm-variables** - Understanding variable structure for variable templates
- **gtm-datalayer** - Templates that interact with data layer
- **gtm-debugging** - Testing and debugging custom templates
- **gtm-api** - Programmatic template management
## Quick Reference
**Import API:** `const apiName = require('apiName');`
**Success/Failure:** Always call `data.gtmOnSuccess()` or `data.gtmOnFailure()`
**Permissions:** Every API requires explicit permission configuration
**Testing:** Use `runCode(mockData)` and assertions
**Debugging:** Use `logToConsole()` with debug environment permission

View File

@@ -0,0 +1,24 @@
# Example Asset File
This placeholder represents where asset files would be stored.
Replace with actual asset files (templates, images, fonts, etc.) or delete if not needed.
Asset files are NOT intended to be loaded into context, but rather used within
the output Claude produces.
Example asset files from other skills:
- Brand guidelines: logo.png, slides_template.pptx
- Frontend builder: hello-world/ directory with HTML/React boilerplate
- Typography: custom-font.ttf, font-family.woff2
- Data: sample_data.csv, test_dataset.json
## Common Asset Types
- Templates: .pptx, .docx, boilerplate directories
- Images: .png, .jpg, .svg, .gif
- Fonts: .ttf, .otf, .woff, .woff2
- Boilerplate code: Project directories, starter files
- Icons: .ico, .svg
- Data files: .csv, .json, .xml, .yaml
Note: This is a text placeholder. Actual assets can be any file type.

View File

@@ -0,0 +1,168 @@
___INFO___
{
"type": "TAG",
"id": "cvt_temp_public_id",
"version": 1,
"securityGroups": [],
"displayName": "My Custom Tag",
"categories": ["ANALYTICS", "MARKETING"],
"brand": {
"id": "brand_id",
"displayName": "My Brand",
"thumbnail": ""
},
"description": "A custom tag template for...",
"containerContexts": [
"WEB"
]
}
___TEMPLATE_PARAMETERS___
[
{
"type": "TEXT",
"name": "endpoint",
"displayName": "API Endpoint",
"simpleValueType": true,
"help": "Enter the API endpoint URL",
"valueValidators": [
{
"type": "NON_EMPTY"
},
{
"type": "REGEX",
"args": ["^https?://.*"]
}
]
},
{
"type": "TEXT",
"name": "eventName",
"displayName": "Event Name",
"simpleValueType": true,
"defaultValue": "custom_event"
},
{
"type": "CHECKBOX",
"name": "debug",
"checkboxText": "Enable Debug Mode",
"simpleValueType": true,
"defaultValue": false
}
]
___SANDBOXED_JS_FOR_WEB_TEMPLATE___
// Require necessary APIs
const sendPixel = require('sendPixel');
const logToConsole = require('logToConsole');
const encodeUriComponent = require('encodeUriComponent');
// Access template data
const endpoint = data.endpoint;
const eventName = data.eventName;
const debug = data.debug;
// Debug logging
if (debug) {
logToConsole('Custom Tag Firing', {
endpoint: endpoint,
eventName: eventName
});
}
// Build pixel URL
const pixelUrl = endpoint + '?event=' + encodeUriComponent(eventName);
// Fire pixel
sendPixel(pixelUrl, data.gtmOnSuccess, data.gtmOnFailure);
___WEB_PERMISSIONS___
[
{
"instance": {
"key": {
"publicId": "logging",
"versionId": "1"
},
"param": [
{
"key": "environments",
"value": {
"type": 1,
"string": "debug"
}
}
]
},
"clientAnnotations": {
"isEditedByUser": true
},
"isRequired": true
},
{
"instance": {
"key": {
"publicId": "send_pixel",
"versionId": "1"
},
"param": [
{
"key": "allowedUrls",
"value": {
"type": 1,
"string": "any"
}
}
]
},
"clientAnnotations": {
"isEditedByUser": true
},
"isRequired": true
}
]
___TESTS___
scenarios:
- name: Tag fires successfully
code: |-
const mockData = {
endpoint: 'https://example.com/pixel',
eventName: 'test_event',
debug: false
};
// Call runCode to run the template's code.
runCode(mockData);
// Verify that the tag finished successfully.
assertApi('gtmOnSuccess').wasCalled();
- name: Debug mode logs to console
code: |-
const mockData = {
endpoint: 'https://example.com/pixel',
eventName: 'test_event',
debug: true
};
runCode(mockData);
// Verify logging occurred
assertApi('logToConsole').wasCalledWith('Custom Tag Firing', {
endpoint: 'https://example.com/pixel',
eventName: 'test_event'
});
___NOTES___
Created using Custom Template Boilerplate

View File

@@ -0,0 +1,133 @@
___INFO___
{
"type": "MACRO",
"id": "cvt_temp_public_id",
"version": 1,
"securityGroups": [],
"displayName": "My Custom Variable",
"categories": ["UTILITY"],
"brand": {
"id": "brand_id",
"displayName": "My Brand"
},
"description": "A custom variable template for...",
"containerContexts": [
"WEB"
]
}
___TEMPLATE_PARAMETERS___
[
{
"type": "TEXT",
"name": "cookieName",
"displayName": "Cookie Name",
"simpleValueType": true,
"help": "Name of the cookie to read",
"valueValidators": [
{
"type": "NON_EMPTY"
}
]
},
{
"type": "TEXT",
"name": "defaultValue",
"displayName": "Default Value",
"simpleValueType": true,
"help": "Value to return if cookie is not found"
}
]
___SANDBOXED_JS_FOR_WEB_TEMPLATE___
// Require necessary APIs
const getCookieValues = require('getCookieValues');
const logToConsole = require('logToConsole');
// Access template data
const cookieName = data.cookieName;
const defaultValue = data.defaultValue;
// Get cookie values
const cookies = getCookieValues(cookieName);
// Return first cookie value or default
if (cookies && cookies.length > 0) {
return cookies[0];
}
return defaultValue;
___WEB_PERMISSIONS___
[
{
"instance": {
"key": {
"publicId": "get_cookies",
"versionId": "1"
},
"param": [
{
"key": "cookieAccess",
"value": {
"type": 1,
"string": "any"
}
}
]
},
"clientAnnotations": {
"isEditedByUser": true
},
"isRequired": true
}
]
___TESTS___
scenarios:
- name: Returns cookie value when exists
code: |-
mock('getCookieValues', (name) => {
if (name === 'testCookie') {
return ['cookieValue'];
}
return [];
});
const mockData = {
cookieName: 'testCookie',
defaultValue: 'default'
};
let result = runCode(mockData);
assertThat(result).isEqualTo('cookieValue');
- name: Returns default value when cookie does not exist
code: |-
mock('getCookieValues', (name) => {
return [];
});
const mockData = {
cookieName: 'nonExistentCookie',
defaultValue: 'defaultValue'
};
let result = runCode(mockData);
assertThat(result).isEqualTo('defaultValue');
___NOTES___
Created using Custom Variable Template Boilerplate

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,357 @@
# Sandboxed JavaScript API Reference
This is the complete Sandboxed JavaScript API reference for Google Tag Manager custom templates. These APIs enable you to build powerful custom templates while maintaining security and performance standards.
Source: https://developers.google.com/tag-platform/tag-manager/templates/api
## API Categories
### Core APIs
These APIs work with sandboxed JavaScript to build custom templates in Google Tag Manager.
#### Consent Management
- **addConsentListener(consentType, listener)** → void
- Registers a listener function to execute when the state of the specified consent type changes.
- **isConsentGranted(consentType)** → boolean
- Returns true if the specified consent type is granted.
- **setDefaultConsentState(consentSettings)** → void
- Pushes a default consent update to the data layer.
- **updateConsentState(consentSettings)** → void
- Pushes a consent update to the data layer.
#### Event Handling
- **addEventCallback(callback)** → void
- Allows you to register a callback function that will be invoked at the end of an event.
- **callLater(function)** → void
- Schedules a call to a function to occur asynchronously.
#### Window and Global Object Access
- **aliasInWindow(toPath, fromPath)** → boolean
- Lets you create an alias (e.g. window.foo = window.bar).
- **callInWindow(pathToFunction, args)** → *
- Allows you to call functions from a path off the window object, in a policy-controlled way.
- **copyFromWindow(key)** → *
- Copies a variable from window object.
- **setInWindow(key, value, overrideExisting)** → boolean
- Sets the given value in window at the given key.
#### Queue and Array Creation
- **createArgumentsQueue(fnKey, arrayKey)** → function
- Creates a queue that is populated with argument objects, in support of tag solutions that require it.
- **createQueue(arrayKey)** → function
- Creates an array in window and returns a function that will push values onto that array.
#### URI and Encoding
- **decodeUri(encoded_uri)** → string
- Decodes any encoded characters in the provided URI.
- **decodeUriComponent(encoded_uri_component)** → string
- Decodes any encoded characters in the provided URI component.
- **encodeUri(uri)** → string
- Returns an encoded Uniform Resource Identifier (URI) by escaping special characters.
- **encodeUriComponent(str)** → string
- Returns an encoded Uniform Resource Identifier (URI) by escaping special characters.
- **fromBase64(base64EncodedString)** → string
- Lets you decode strings from their base64 representation.
- **toBase64(input)** → string
- Lets you encode a string into a base64 representation.
#### Utility Functions
- **generateRandom(min, max)** → number
- Returns a random number (integer) within the given range.
- **getContainerVersion()** → object
- Returns an object containing data about the current container.
- **getType(value)** → string
- Returns a string describing the given value's type.
- **logToConsole(obj1, obj2, ...)** → void
- Logs arguments to the browser console.
- **makeInteger(value)** → number
- Converts the given value to a number (integer).
- **makeNumber(value)** → number
- Converts the given value to a number.
- **makeString(value)** → string
- Returns the given value as a string.
- **makeTableMap(tableObj, keyColumnName, valueColumnName)** → object
- Converts a simple table object with two columns to a Map.
- **queryPermission(permission, functionArgs)** → boolean
- Query the allowed and narrowed permissions.
- **require(name)** → function
- Imports a built-in function by name.
#### Data Layer Access
- **copyFromDataLayer(key, dataLayerVersion)** → *
- Returns the value currently assigned to the given key in the data layer.
- **gtagSet(object)** → void
- Pushes a gtag set command to the data layer.
#### Cookie Management
- **getCookieValues(name, decode)** → array
- Returns the values of all cookies with the given name.
- **setCookie(name, value, options, encode)** → void
- Sets or deletes the cookie with the specified name, value, and options.
#### URL and Query Parameters
- **getQueryParameters(queryKey, retrieveAll)** → *
- Returns the first or all of the parameters for the current URL's queryKey.
- **getUrl(component)** → string
- Returns a string that represents all or a portion of the current URL.
- **parseUrl(url)** → object
- Returns an object that contains all of a given URL's component parts.
- **getReferrerUrl(component)** → string
- Reads the document object for the referrer and returns a string that represents a portion of the referrer.
- **getReferrerQueryParameters(queryKey, retrieveAll)** → *
- Acts the same way as getQueryParameters, except it acts on the referrer instead of the current URL.
#### Time Management
- **getTimestamp()** → number
- Returns a number that represents the current time in milliseconds since Unix epoch.
- **getTimestampMillis()** → number
- Returns a number that represents the current time in milliseconds since Unix epoch.
#### Network and Injection
- **injectHiddenIframe(url, onSuccess)** → void
- Adds an invisible iframe to the page.
- **injectScript(url, onSuccess, onFailure, cacheToken)** → void
- Adds a script tag to the page to load the given URL asynchronously.
- **sendPixel(url, onSuccess, onFailure)** → void
- Makes a GET request to a specified URL endpoint.
- **sha256(input, onSuccess, onFailure, options)** → void
- Calculates the SHA-256 digest of the input.
#### Document Reading
- **readCharacterSet()** → string
- Returns the value of document.characterSet.
- **readTitle()** → string
- Returns the value of document.title.
- **readAnalyticsStorage(cookieOptions)** → object
- Retrieves the data stored for analytics and returns an object with client_id and sessions.
#### Built-in Objects
- **JSON** → object
- Returns an object that provides JSON functions.
- **Math** → object
- An object providing Math functions.
- **Object** → object
- Returns an object that provides Object methods.
- **localStorage** → object
- Returns an object with methods for accessing local storage.
- **templateStorage** → object
- Returns an object with methods for accessing template storage.
---
### Test APIs
These APIs work with sandboxed JavaScript tests to build tests for custom templates in Google Tag Manager.
#### Assertions and Validation
- **assertApi(apiName)** → object
- Returns a matcher object that can be used to fluently make assertions about the given API.
- **assertThat(actual, opt_message)** → object
- Returns an object that can be used to fluently make assertions about a subject's value.
- **fail(opt_message)** → void
- Immediately fails the current test and prints the given message, if supplied.
#### Mocking
- **mock(apiName, returnValue)** → void
- Allows you to override the behavior of Sandboxed APIs.
- **mockObject(apiName, objectMock)** → void
- Lets you override the behavior of Sandboxed APIs that return an object.
#### Test Execution
- **runCode(data)** → *
- Runs the code for the template in the current test environment.
---
## API Summary
**Total Core APIs**: 49 functions across 13 categories
**Total Test APIs**: 6 functions across 3 categories
**Total APIs**: 55 functions
### Core API Categories
1. Consent Management (4 functions)
2. Event Handling (2 functions)
3. Window and Global Object Access (4 functions)
4. Queue and Array Creation (2 functions)
5. URI and Encoding (6 functions)
6. Utility Functions (8 functions)
7. Data Layer Access (2 functions)
8. Cookie Management (2 functions)
9. URL and Query Parameters (5 functions)
10. Time Management (2 functions)
11. Network and Injection (4 functions)
12. Document Reading (3 functions)
13. Built-in Objects (5 functions)
### Test API Categories
1. Assertions and Validation (3 functions)
2. Mocking (2 functions)
3. Test Execution (1 function)
---
## Usage Examples
### Working with the Data Layer
```javascript
// Get a value from the data layer
var userId = copyFromDataLayer('userId');
// Push a gtag set command
gtagSet({
'event': 'page_view',
'page_title': readTitle()
});
```
### Managing Cookies
```javascript
// Get cookie values
var cookieValues = getCookieValues('tracking_id', true);
// Set a cookie
setCookie('my_cookie', 'value123', {
'domain': 'example.com',
'path': '/',
'max-age': 86400
});
```
### Working with URLs
```javascript
// Get URL components
var hostname = getUrl('hostname');
var pathname = getUrl('pathname');
var queryString = getUrl('query');
// Parse a URL
var urlParts = parseUrl('https://example.com/page?param=value');
// Get query parameters
var userParam = getQueryParameters('user_id', false);
```
### Async Operations
```javascript
// Inject a script
injectScript('https://example.com/tracker.js', function() {
logToConsole('Script loaded successfully');
}, function() {
logToConsole('Script failed to load');
});
// Send a pixel
sendPixel('https://example.com/track?event=purchase', function() {
logToConsole('Pixel sent');
}, function() {
logToConsole('Pixel failed');
});
// Calculate SHA-256
sha256('input_string', function(result) {
logToConsole('SHA-256: ' + result);
}, function() {
logToConsole('Hash calculation failed');
});
```
### Consent Management
```javascript
// Check if consent is granted
if (isConsentGranted('analytics_storage')) {
// Proceed with analytics
}
// Add a consent listener
addConsentListener('analytics_storage', function() {
logToConsole('Consent state changed');
});
// Update consent
updateConsentState({
'analytics_storage': 'granted',
'ad_storage': 'denied'
});
```
### Testing Custom Templates
```javascript
// Mock an API
mock('sendPixel', undefined);
// Assert API behavior
assertApi('sendPixel').wasCalled();
// Run template code with test data
var result = runCode({
'event': 'test_event',
'user_id': '12345'
});
```
---
## Reference
- **Official Documentation**: https://developers.google.com/tag-platform/tag-manager/templates/api
- **Google Tag Manager**: https://tagmanager.google.com
- **GTM Custom Templates Guide**: https://developers.google.com/tag-platform/tag-manager/templates/guide

View File

@@ -0,0 +1,149 @@
# Custom Template Examples
<!-- To be populated with practical examples -->
## Tag Template Examples
### Simple Pixel Tag Template
```javascript
// Code section
const sendPixel = require('sendPixel');
const encodeUriComponent = require('encodeUriComponent');
const getUrl = require('getUrl');
const pixelUrl = 'https://example.com/pixel?'
+ 'page=' + encodeUriComponent(getUrl())
+ '&event=' + encodeUriComponent(data.eventName);
sendPixel(pixelUrl, data.gtmOnSuccess, data.gtmOnFailure);
```
### HTTP Request Tag Template
```javascript
// Code section
const sendHttpRequest = require('sendHttpRequest');
const JSON = require('JSON');
const postBody = JSON.stringify({
event: data.eventName,
userId: data.userId,
timestamp: Date.now()
});
const options = {
headers: {'Content-Type': 'application/json'},
method: 'POST',
};
sendHttpRequest(data.endpoint, options, postBody)
.then(data.gtmOnSuccess)
.catch(data.gtmOnFailure);
```
## Variable Template Examples
### Cookie Variable Template
```javascript
// Code section
const getCookieValues = require('getCookieValues');
const cookieName = data.cookieName;
const cookies = getCookieValues(cookieName);
if (cookies.length > 0) {
return cookies[0];
}
return data.defaultValue;
```
### LocalStorage Variable Template
```javascript
// Code section
const localStorage = require('localStorage');
const key = data.localStorageKey;
return localStorage.getItem(key) || data.defaultValue;
```
### Custom JavaScript Function Variable
```javascript
// Code section
const makeTableMap = require('makeTableMap');
const lookupTable = makeTableMap(data.tableInput, 'input', 'output');
const inputValue = data.inputVariable;
return lookupTable[inputValue] || data.defaultValue;
```
## Common Patterns
### Error Handling
```javascript
const sendHttpRequest = require('sendHttpRequest');
const logToConsole = require('logToConsole');
sendHttpRequest(url, options)
.then(response => {
logToConsole('Success:', response);
data.gtmOnSuccess();
})
.catch(error => {
logToConsole('Error:', error);
data.gtmOnFailure();
});
```
### Conditional Logic
```javascript
const getType = require('getType');
if (getType(data.value) === 'undefined') {
return data.defaultValue;
}
if (data.value > 0) {
return 'positive';
} else if (data.value < 0) {
return 'negative';
} else {
return 'zero';
}
```
### Data Validation
```javascript
const makeNumber = require('makeNumber');
const makeString = require('makeString');
// Validate and convert input
const numericValue = makeNumber(data.inputValue);
if (numericValue === undefined) {
logToConsole('Invalid numeric input');
data.gtmOnFailure();
return;
}
// Continue with validated value
const result = numericValue * 2;
data.gtmOnSuccess();
```
## Placeholder for Additional Examples
This file will be enhanced with:
- Server-side client template examples
- Complex permission configurations
- Field validation patterns
- Real-world use cases
- Community template patterns
**Status**: ⚠️ To be enhanced with documentation extraction

View File

@@ -0,0 +1,96 @@
# Tests
**Source**: https://developers.google.com/tag-platform/tag-manager/templates/tests
**Extracted**: 2025-01-09
## Overview
Unit tests for Google Tag Manager custom templates help you validate the functionality of your templates. You can create a set of tests for each template that can be run without needing to deploy your tag, which allows you to continuously test your template's behavior during development. Each test can provide sample input values, mock function calls, and assert code behavior.
## Limitations
- Unit tests do not check validation rules but you can manually check validation using the **Run Code** button.
- Permission checks do not happen on mocked APIs in unit tests.
## Step-by-Step Guide: Creating a Variable Template with Tests
This guide creates a variable template that takes an input string and returns the uppercase version of that string.
### Steps 1-3: Create Template and Add Field
1. Create a new variable template. Click **Templates** in the left navigation and click **New** under the **Variable Templates** section.
2. Click **Fields**.
3. Click **Add Field** and select **Text input**. Name the field \`text1\` and set the display name to _"Text 1"_.
### Step 4: Add Template Code
In the **Code** tab, replace the default code with this sandboxed JavaScript:
\`\`\`javascript
let input = data.text1;
return input.toUpperCase();
\`\`\`
### Steps 5-7: Create First Test
5. Click **Tests** to open the testing tab.
6. Click **Add Test** and change the test's name from _"Untitled test 1"_ to _"Handles strings"_.
7. Click on the expand icon to reveal the test's sandboxed JavaScript editor. Replace the code with:
\`\`\`javascript
// Call runCode to run the template's code with a lowercase string
let variableResult = runCode({text1: 'this is a test'});
// Validate that the result of runCode is an uppercase string.
assertThat(variableResult).isEqualTo('THIS IS A TEST');
\`\`\`
This test passes the string \`'this is a test'\` to the variable and verifies that the variable returns the expected value of \`'THIS IS A TEST'\`. The \`runCode\` API is used to run the template code in the **Code** tab. The argument to \`runCode\` is an object that is used as the data global. The \`assertThat\` API returns an object that can be used to fluently make assertions about a subject's value.
### Step 8: Run Tests
Click **▶ Run Tests** to run the test. The output of the test will appear in the Console.
The **▶ Run Tests** button runs all of the enabled tests in the template, in the order shown. To change the order, use the drag icon. A test can be temporarily enabled or disabled by clicking on the circle to the left of the test name. To run a single test, click the ▶ button that appears when you move the mouse over the test.
The console should print the total number of tests run and the number of tests that failed, if any. In this case, only one test was run and it should pass.
### Steps 9-11: Create Second Test for Edge Cases
9. Click **Add Test** again to add a second test. Change the test's name from _"Untitled test 2"_ to _"Handles undefined"_.
10. Click on the test to expand it and reveal the sandboxed JavaScript editor. Enter:
\`\`\`javascript
let variableResult = runCode({});
assertThat(variableResult).isEqualTo(undefined);
\`\`\`
11. Click **▶ Run Tests** to run all of the tests at once. The output of the test will appear in the console.
The _Handles undefined_ test should fail. Congratulations, you found a bug!
### Steps 12-14: Fix Code and Re-test
12. Click **Code** to go back and edit the template's sandboxed JavaScript code. Update the code as follows:
\`\`\`javascript
const getType = require('getType');
let input = data.text1;
if (getType(input) !== 'string') {
return input;
}
return input.toUpperCase();
\`\`\`
The updated code follows the best practice of validating the \`input\` variable before using it.
13. Click **Tests** to go back to the list of test cases.
14. Click **▶ Run Tests** to run all of the test cases again. This time the _Handles undefined_ test should pass.
15. Click **Save**, and close the Template Editor.
## Core APIs
### runCode
Executes the template's code with provided sample data object. Arguments are merged into the data global variable used in the template code.
### assertThat
Returns an object that can be used to fluently make assertions about a subject's value. Used for validation in tests.