mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-04 18:49:39 -06:00
Compare commits
2 Commits
chore/basi
...
groundwork
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b05a636914 | ||
|
|
e03df83e88 |
183
apps/web/lib/testing/README.md
Normal file
183
apps/web/lib/testing/README.md
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
# Testing Utilities
|
||||||
|
|
||||||
|
Centralized testing utilities to reduce boilerplate and ensure consistency across test files.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { FIXTURES, TEST_IDS } from "@/lib/testing/constants";
|
||||||
|
import { setupTestEnvironment } from "@/lib/testing/setup";
|
||||||
|
|
||||||
|
// Setup standard test environment with cleanup
|
||||||
|
setupTestEnvironment();
|
||||||
|
|
||||||
|
describe("MyModule", () => {
|
||||||
|
test("should use standard test IDs", () => {
|
||||||
|
// Use TEST_IDS instead of magic strings
|
||||||
|
const result = processContact(TEST_IDS.contact);
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should use fixtures for test data", () => {
|
||||||
|
// Use FIXTURES instead of defining data inline
|
||||||
|
const result = validateEmail(FIXTURES.contact.email);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Utilities
|
||||||
|
|
||||||
|
### TEST_IDS
|
||||||
|
|
||||||
|
Standard identifiers to eliminate magic strings in tests.
|
||||||
|
|
||||||
|
**Available IDs:**
|
||||||
|
|
||||||
|
- `contact`, `contactAlt`
|
||||||
|
- `user`
|
||||||
|
- `environment`
|
||||||
|
- `survey`
|
||||||
|
- `organization`
|
||||||
|
- `quota`
|
||||||
|
- `attribute`
|
||||||
|
- `response`
|
||||||
|
- `team`
|
||||||
|
- `project`
|
||||||
|
- `segment`
|
||||||
|
- `webhook`
|
||||||
|
- `apiKey`
|
||||||
|
- `membership`
|
||||||
|
|
||||||
|
**Before:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const contactId = "contact-1";
|
||||||
|
const envId = "env-123";
|
||||||
|
```
|
||||||
|
|
||||||
|
**After:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { TEST_IDS } from "@/lib/testing/constants";
|
||||||
|
|
||||||
|
// Use TEST_IDS.contact and TEST_IDS.environment
|
||||||
|
```
|
||||||
|
|
||||||
|
### FIXTURES
|
||||||
|
|
||||||
|
Common test data structures to reduce duplication.
|
||||||
|
|
||||||
|
**Available fixtures:**
|
||||||
|
|
||||||
|
- `contact` - Basic contact object
|
||||||
|
- `survey` - Survey object
|
||||||
|
- `attributeKey` - Single attribute key
|
||||||
|
- `attributeKeys` - Array of attribute keys
|
||||||
|
- `responseData` - Sample response data
|
||||||
|
- `environment` - Environment object
|
||||||
|
- `organization` - Organization object
|
||||||
|
- `project` - Project object
|
||||||
|
|
||||||
|
**Before:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const mockContact = {
|
||||||
|
id: "contact-1",
|
||||||
|
email: "test@example.com",
|
||||||
|
userId: "user-1",
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**After:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { FIXTURES } from "@/lib/testing/constants";
|
||||||
|
|
||||||
|
// Use FIXTURES.contact directly
|
||||||
|
```
|
||||||
|
|
||||||
|
### setupTestEnvironment()
|
||||||
|
|
||||||
|
Standardized test cleanup to replace manual beforeEach/afterEach blocks.
|
||||||
|
|
||||||
|
**Before:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**After:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { setupTestEnvironment } from "@/lib/testing/setup";
|
||||||
|
|
||||||
|
setupTestEnvironment();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
- **Consistency:** All tests use the same IDs and cleanup patterns
|
||||||
|
- **Maintainability:** Update IDs in one place instead of 200+ locations
|
||||||
|
- **Readability:** Less boilerplate, more test logic
|
||||||
|
- **Speed:** Write new tests faster with ready-to-use fixtures
|
||||||
|
|
||||||
|
## Migration Guide
|
||||||
|
|
||||||
|
### For New Tests
|
||||||
|
|
||||||
|
Use these utilities immediately in all new test files.
|
||||||
|
|
||||||
|
### For Existing Tests
|
||||||
|
|
||||||
|
Migrate opportunistically when editing existing tests. No forced migration required.
|
||||||
|
|
||||||
|
### Example Migration
|
||||||
|
|
||||||
|
**Before (60 lines with boilerplate):**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||||
|
|
||||||
|
const contactId = "contact-1";
|
||||||
|
const environmentId = "env-1";
|
||||||
|
|
||||||
|
describe("getContact", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("fetches contact", async () => {
|
||||||
|
const result = await getContact(contactId);
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**After (45 lines, cleaner):**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { TEST_IDS } from "@/lib/testing/constants";
|
||||||
|
import { setupTestEnvironment } from "@/lib/testing/setup";
|
||||||
|
|
||||||
|
setupTestEnvironment();
|
||||||
|
|
||||||
|
describe("getContact", () => {
|
||||||
|
test("fetches contact", async () => {
|
||||||
|
const result = await getContact(TEST_IDS.contact);
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
120
apps/web/lib/testing/constants.ts
Normal file
120
apps/web/lib/testing/constants.ts
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import { TContactAttributeKey } from "@formbricks/types/contact-attribute-key";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard test IDs to eliminate magic strings across test files.
|
||||||
|
* Use these constants instead of hardcoded IDs like "contact-1", "env-123", etc.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* import { TEST_IDS } from "@/lib/testing/constants";
|
||||||
|
*
|
||||||
|
* test("should fetch contact", async () => {
|
||||||
|
* const result = await getContact(TEST_IDS.contact);
|
||||||
|
* expect(result).toBeDefined();
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const TEST_IDS = {
|
||||||
|
contact: "contact-123",
|
||||||
|
contactAlt: "contact-456",
|
||||||
|
user: "user-123",
|
||||||
|
environment: "env-123",
|
||||||
|
survey: "survey-123",
|
||||||
|
organization: "org-123",
|
||||||
|
quota: "quota-123",
|
||||||
|
attribute: "attr-123",
|
||||||
|
response: "response-123",
|
||||||
|
team: "team-123",
|
||||||
|
project: "project-123",
|
||||||
|
segment: "segment-123",
|
||||||
|
webhook: "webhook-123",
|
||||||
|
apiKey: "api-key-123",
|
||||||
|
membership: "membership-123",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common test fixtures to reduce duplicate test data definitions.
|
||||||
|
* Extend these as needed for your specific test cases.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* import { FIXTURES } from "@/lib/testing/constants";
|
||||||
|
*
|
||||||
|
* test("should create contact", async () => {
|
||||||
|
* vi.mocked(getContactAttributeKeys).mockResolvedValue(FIXTURES.attributeKeys);
|
||||||
|
* const result = await createContact(FIXTURES.contact);
|
||||||
|
* expect(result.email).toBe(FIXTURES.contact.email);
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const FIXTURES = {
|
||||||
|
contact: {
|
||||||
|
id: TEST_IDS.contact,
|
||||||
|
email: "test@example.com",
|
||||||
|
userId: TEST_IDS.user,
|
||||||
|
},
|
||||||
|
|
||||||
|
survey: {
|
||||||
|
id: TEST_IDS.survey,
|
||||||
|
name: "Test Survey",
|
||||||
|
environmentId: TEST_IDS.environment,
|
||||||
|
},
|
||||||
|
|
||||||
|
attributeKey: {
|
||||||
|
id: TEST_IDS.attribute,
|
||||||
|
key: "email",
|
||||||
|
name: "Email",
|
||||||
|
environmentId: TEST_IDS.environment,
|
||||||
|
createdAt: new Date("2024-01-01"),
|
||||||
|
updatedAt: new Date("2024-01-02"),
|
||||||
|
isUnique: false,
|
||||||
|
description: null,
|
||||||
|
type: "default" as const,
|
||||||
|
},
|
||||||
|
|
||||||
|
attributeKeys: [
|
||||||
|
{
|
||||||
|
id: "key-1",
|
||||||
|
key: "email",
|
||||||
|
name: "Email",
|
||||||
|
environmentId: TEST_IDS.environment,
|
||||||
|
createdAt: new Date("2024-01-01"),
|
||||||
|
updatedAt: new Date("2024-01-02"),
|
||||||
|
isUnique: false,
|
||||||
|
description: null,
|
||||||
|
type: "default",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "key-2",
|
||||||
|
key: "name",
|
||||||
|
name: "Name",
|
||||||
|
environmentId: TEST_IDS.environment,
|
||||||
|
createdAt: new Date("2024-01-01"),
|
||||||
|
updatedAt: new Date("2024-01-02"),
|
||||||
|
isUnique: false,
|
||||||
|
description: null,
|
||||||
|
type: "default",
|
||||||
|
},
|
||||||
|
] as TContactAttributeKey[],
|
||||||
|
|
||||||
|
responseData: {
|
||||||
|
q1: "Open text answer",
|
||||||
|
q2: "Option 1",
|
||||||
|
},
|
||||||
|
|
||||||
|
environment: {
|
||||||
|
id: TEST_IDS.environment,
|
||||||
|
name: "Test Environment",
|
||||||
|
type: "development" as const,
|
||||||
|
},
|
||||||
|
|
||||||
|
organization: {
|
||||||
|
id: TEST_IDS.organization,
|
||||||
|
name: "Test Organization",
|
||||||
|
},
|
||||||
|
|
||||||
|
project: {
|
||||||
|
id: TEST_IDS.project,
|
||||||
|
name: "Test Project",
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
31
apps/web/lib/testing/setup.ts
Normal file
31
apps/web/lib/testing/setup.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { afterEach, beforeEach, vi } from "vitest";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard test environment setup with consistent cleanup patterns.
|
||||||
|
* Call this function once at the top of your test file to ensure
|
||||||
|
* mocks are properly cleaned up between tests.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* import { setupTestEnvironment } from "@/lib/testing/setup";
|
||||||
|
*
|
||||||
|
* setupTestEnvironment();
|
||||||
|
*
|
||||||
|
* describe("MyModule", () => {
|
||||||
|
* test("should work correctly", () => {
|
||||||
|
* // Your test code here
|
||||||
|
* });
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Note: This replaces manual beforeEach/afterEach blocks in individual test files.
|
||||||
|
*/
|
||||||
|
export function setupTestEnvironment() {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -184,6 +184,10 @@ export const testInputValidation = async (service: Function, ...args: any[]): Pr
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Export new testing utilities for easy access
|
||||||
|
export { setupTestEnvironment } from "./lib/testing/setup";
|
||||||
|
export { TEST_IDS, FIXTURES } from "./lib/testing/constants";
|
||||||
|
|
||||||
vi.mock("@/lib/constants", () => ({
|
vi.mock("@/lib/constants", () => ({
|
||||||
IS_FORMBRICKS_CLOUD: false,
|
IS_FORMBRICKS_CLOUD: false,
|
||||||
POSTHOG_API_KEY: "mock-posthog-api-key",
|
POSTHOG_API_KEY: "mock-posthog-api-key",
|
||||||
|
|||||||
@@ -112,6 +112,7 @@
|
|||||||
"pages": [
|
"pages": [
|
||||||
"xm-and-surveys/surveys/website-app-surveys/quickstart",
|
"xm-and-surveys/surveys/website-app-surveys/quickstart",
|
||||||
"xm-and-surveys/surveys/website-app-surveys/framework-guides",
|
"xm-and-surveys/surveys/website-app-surveys/framework-guides",
|
||||||
|
"xm-and-surveys/surveys/website-app-surveys/google-tag-manager",
|
||||||
{
|
{
|
||||||
"group": "Features",
|
"group": "Features",
|
||||||
"icon": "wrench",
|
"icon": "wrench",
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,223 @@
|
|||||||
|
---
|
||||||
|
title: "Google Tag Manager"
|
||||||
|
description: "Deploy Formbricks surveys through GTM without modifying your website code."
|
||||||
|
icon: "tags"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- [Google Tag Manager](https://tagmanager.google.com/) installed on your website
|
||||||
|
- Your Formbricks **Environment ID** (Settings > Configuration > Website & App Connection)
|
||||||
|
- Your **App URL**: `https://app.formbricks.com` (or your self-hosted URL)
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Use PUBLIC_URL for multi-domain setups, WEBAPP_URL for single-domain setups.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Basic Setup
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Create a Custom HTML tag in GTM">
|
||||||
|
1. Create a new tag with preferred name e.g. "Formbricks Intercept Surveys"
|
||||||
|
2. Tag Type: Custom HTML
|
||||||
|
3. Paste the code from Step 2. Make sure to replace `<your-environment-id>` and if you self-host, replace `<your-app-url>`
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Add initialization script">
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="text/javascript">
|
||||||
|
!function(){
|
||||||
|
var appUrl = "https://app.formbricks.com"; // REPLACE ONLY IF YOUR SELF-HOST
|
||||||
|
var environmentId = "<your-environment-id>"; // REPLACE
|
||||||
|
var t=document.createElement("script");
|
||||||
|
t.type="text/javascript";
|
||||||
|
t.async=!0;
|
||||||
|
t.src=appUrl+"/js/formbricks.umd.cjs";
|
||||||
|
t.onload=function(){
|
||||||
|
window.formbricks && window.formbricks.setup({
|
||||||
|
environmentId: environmentId,
|
||||||
|
appUrl: appUrl
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var e=document.getElementsByTagName("script")[0];
|
||||||
|
e.parentNode.insertBefore(t,e);
|
||||||
|
}();
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Set trigger">
|
||||||
|
1. Trigger: **All Pages** - Page View (default) or use case specific event
|
||||||
|
2. Save and publish
|
||||||
|

|
||||||
|
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Test">
|
||||||
|
1. Use GTM Preview mode
|
||||||
|
2. Verify the tag fires
|
||||||
|
3. Add `?formbricksDebug=true` to the URL to see test logs in browser console (see [Debugging Mode](/xm-and-surveys/surveys/website-app-surveys/framework-guides#debugging-formbricks-integration) for more details)
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
|
||||||
|
## User Identification
|
||||||
|
|
||||||
|
Identify users to enable targeting and attributes. Learn more about [user identification](/xm-and-surveys/surveys/website-app-surveys/user-identification).
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
User identification is part of the Formbricks [Enterprise Edition](/self-hosting/advanced/license).
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Create GTM variables">
|
||||||
|
1. Go to Variables on GTM dashboard
|
||||||
|
2. Create new User-defined variable
|
||||||
|
3. Name it (e.g., "User ID")
|
||||||
|
4. Variable Type: Data Layer Variable
|
||||||
|
5. Data Layer Variable: "userId"
|
||||||
|
6. Save and publish
|
||||||
|
7. Repeat for attributes you want to track e.g. "userEmail" and "userPlan" (optional)
|
||||||
|
|
||||||
|

|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Create identification tag">
|
||||||
|
New Custom HTML tag named "Formbricks - User":
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
var check = setInterval(function() {
|
||||||
|
if (window.formbricks && window.formbricks.setUserId) {
|
||||||
|
clearInterval(check);
|
||||||
|
var userId = {{User ID}};
|
||||||
|
if (userId) {
|
||||||
|
window.formbricks.setUserId(userId);
|
||||||
|
var attrs = {};
|
||||||
|
if ({{User Email}}) attrs.email = {{User Email}};
|
||||||
|
if ({{User Plan}}) attrs.plan = {{User Plan}};
|
||||||
|
if (Object.keys(attrs).length) {
|
||||||
|
window.formbricks.setAttributes(attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
setTimeout(function() { clearInterval(check); }, 10000);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Set trigger and push data">
|
||||||
|
1. Create a custom event trigger in GTM
|
||||||
|
2. Trigger Type: Custom Event
|
||||||
|
3. Event name: `user-login` (or your preferred event name)
|
||||||
|
4. Attach this trigger to your "Formbricks - User" tag
|
||||||
|
5. Save and publish
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
6. In your code, push data with the same event name:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
window.dataLayer.push({
|
||||||
|
'event': 'user-login',
|
||||||
|
'userId': 'user-123',
|
||||||
|
'userEmail': 'user@example.com',
|
||||||
|
'userPlan': 'premium'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Track Custom Events
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Create code action in Formbricks">
|
||||||
|
Add code action via Formbricks UI
|
||||||
|
|
||||||
|

|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Create GTM variable for Event Name">
|
||||||
|
1. Go to Variables on GTM dashboard
|
||||||
|
2. Create new User-defined variable
|
||||||
|
3. Name it "Event Name"
|
||||||
|
4. Variable Type: Data Layer Variable
|
||||||
|
5. Data Layer Variable: "eventName"
|
||||||
|
6. Save and publish
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Create event tracking tag">
|
||||||
|
New Custom HTML tag:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
if (window.formbricks && window.formbricks.track) {
|
||||||
|
window.formbricks.track({{Event Name}});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Create custom trigger">
|
||||||
|
1. Create a custom event trigger in GTM
|
||||||
|
2. Trigger Type: Custom Event
|
||||||
|
3. Event name: `eventName` or name that matches with your event in code.
|
||||||
|
4. Attach this trigger to your event tracking tag
|
||||||
|
5. Save and publish
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Fire events from your site">
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Track button click
|
||||||
|
window.dataLayer.push({
|
||||||
|
'event': 'eventName',
|
||||||
|
'eventName': 'code-action'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Surveys not showing?**
|
||||||
|
- Use GTM Preview mode to check tag firing
|
||||||
|
- Add `?formbricksDebug=true` to your URL
|
||||||
|
- Check browser console for errors
|
||||||
|
- Wait 1 minute for the Server Cache to refresh
|
||||||
|
|
||||||
|
**User ID not working?**
|
||||||
|
- Verify Data Layer push syntax
|
||||||
|
- Check GTM variables are reading correct values
|
||||||
|
- Ensure user tag fires after initialization
|
||||||
|
|
||||||
|
**Events not tracking?**
|
||||||
|
- Confirm `window.formbricks` exists before calling track
|
||||||
|
- Match event names exactly with Formbricks action names
|
||||||
|
- Check timing - Formbricks must be initialized first
|
||||||
|
|
||||||
|
## Need Help?
|
||||||
|
|
||||||
|
- [GitHub Discussions](https://github.com/formbricks/formbricks/discussions)
|
||||||
|
- [Framework Guides](/xm-and-surveys/surveys/website-app-surveys/framework-guides)
|
||||||
|
- [Actions](/xm-and-surveys/surveys/website-app-surveys/actions)
|
||||||
|
- [User Identification](/xm-and-surveys/surveys/website-app-surveys/user-identification)
|
||||||
|
|
||||||
Reference in New Issue
Block a user