568 lines
26 KiB
Markdown
568 lines
26 KiB
Markdown
# 🧪 Конкретный анализ AB-тестов в воронке Soulmate
|
||
|
||
## 📋 Два AB-теста в воронке
|
||
|
||
1. **`soulmate-onboarding-image`** - тест картинки/видео на первом экране
|
||
2. **`soulmate-trial-grid`** - тест навигации после email экрана
|
||
|
||
---
|
||
|
||
## 🎯 AB-тест #1: `soulmate-onboarding-image`
|
||
|
||
### 📍 Где находится в JSON
|
||
|
||
**Файл:** `public/funnels/soulmate.json`
|
||
|
||
**Экран:** `"onboarding"` (первый экран воронки)
|
||
|
||
**Строки:** 51-106
|
||
|
||
```json
|
||
{
|
||
"id": "onboarding",
|
||
"template": "soulmate",
|
||
"variants": [
|
||
{
|
||
"conditions": [{
|
||
"conditionType": "unleash",
|
||
"unleashFlag": "soulmate-onboarding-image",
|
||
"unleashVariants": ["v0"]
|
||
}],
|
||
"overrides": {}
|
||
},
|
||
{
|
||
"conditions": [{
|
||
"conditionType": "unleash",
|
||
"unleashFlag": "soulmate-onboarding-image",
|
||
"unleashVariants": ["v1"]
|
||
}],
|
||
"overrides": {
|
||
"soulmatePortraitsDelivered": {
|
||
"mediaUrl": "/images/90b8c77f-c0cd-475d-a4de-bcabb3708c59.png",
|
||
"mediaType": "image"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"conditions": [{
|
||
"conditionType": "unleash",
|
||
"unleashFlag": "soulmate-onboarding-image",
|
||
"unleashVariants": ["v2"]
|
||
}],
|
||
"overrides": {
|
||
"soulmatePortraitsDelivered": {
|
||
"mediaUrl": "/images/275472b0-30e0-47d7-a1ab-8090bc9fb236.mp4",
|
||
"mediaType": "video"
|
||
}
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 🎬 Что тестируется
|
||
|
||
Три варианта картинки/видео на первом экране:
|
||
- **v0** - дефолтное изображение `/soulmate-portrait-delivered-male.jpg`
|
||
- **v1** - альтернативное изображение (PNG)
|
||
- **v2** - видео (MP4)
|
||
|
||
### ⏱️ КОГДА ОТПРАВЛЯЕТСЯ impression событие
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+0ms: Пользователь открывает /soulmate/onboarding │
|
||
│ Это ПЕРВЫЙ экран воронки │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+100ms: GoogleAnalytics загружается (gtag.js) │
|
||
│ window.gtag становится доступен │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+200ms: FunnelUnleashWrapper сканирует ВСЮ воронку │
|
||
│ Находит флаги: │
|
||
│ • soulmate-onboarding-image (из onboarding) │
|
||
│ • soulmate-trial-grid (из email) │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+300ms: Unleash SDK возвращает варианты │
|
||
│ • soulmate-onboarding-image → "v1" (например) │
|
||
│ • soulmate-trial-grid → "grid" (например) │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+400ms: FunnelRuntime монтируется │
|
||
│ currentScreen = onboarding экран │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+410ms: currentScreenFlags вычисляется │
|
||
│ Анализирует onboarding экран: │
|
||
│ • onboarding.variants → soulmate-onboarding-image│
|
||
│ • onboarding.navigation → нет флагов │
|
||
│ Результат: ["soulmate-onboarding-image"] │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+420ms: useEffect срабатывает │
|
||
│ (FunnelRuntime.tsx, строки 136-150) │
|
||
│ │
|
||
│ ✅ sendUnleashImpression( │
|
||
│ "soulmate-onboarding-image", │
|
||
│ "v1" │
|
||
│ ) │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+421ms: ✅ ОТПРАВКА В GOOGLE ANALYTICS │
|
||
│ │
|
||
│ window.gtag("event", "experiment_impression", { │
|
||
│ app_name: "witlab-funnel", │
|
||
│ feature: "soulmate-onboarding-image", │
|
||
│ treatment: "v1" │
|
||
│ }); │
|
||
│ │
|
||
│ sessionStorage.setItem( │
|
||
│ "unleash_impression_soulmate-onboarding-image_v1", │
|
||
│ "true" │
|
||
│ ); │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ Console (development): │
|
||
│ [Unleash Impression] ✅ Sent successfully: │
|
||
│ { │
|
||
│ feature: "soulmate-onboarding-image", │
|
||
│ variant: "v1" │
|
||
│ } │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 📊 Когда КОНКРЕТНО
|
||
|
||
**✅ Impression отправляется:** Сразу при входе пользователя в воронку на ПЕРВЫЙ экран "onboarding"
|
||
|
||
**⏰ Timing:** ~420ms после загрузки страницы `/soulmate/onboarding`
|
||
|
||
**🔧 Код:**
|
||
- **Файл:** `src/components/funnel/FunnelRuntime.tsx` (строки 136-150)
|
||
- **Функция:** `sendUnleashImpression()` из `src/lib/funnel/unleash/sendImpression.ts`
|
||
|
||
**📍 Условие отправки:**
|
||
```typescript
|
||
// В FunnelRuntime.tsx
|
||
const currentScreenFlags = useMemo(() => {
|
||
const flags = new Set<string>();
|
||
|
||
// Сканирует onboarding.variants
|
||
currentScreen.variants?.forEach((variant) => {
|
||
variant.conditions.forEach((condition) => {
|
||
if (condition.conditionType === "unleash" && condition.unleashFlag) {
|
||
flags.add(condition.unleashFlag); // ← "soulmate-onboarding-image"
|
||
}
|
||
});
|
||
});
|
||
|
||
return Array.from(flags); // ["soulmate-onboarding-image"]
|
||
}, [currentScreen]);
|
||
|
||
useEffect(() => {
|
||
if (currentScreenFlags.length === 0) return;
|
||
|
||
currentScreenFlags.forEach((flag) => {
|
||
const variant = activeVariants[flag]; // "v1"
|
||
sendUnleashImpression(flag, variant); // ← ОТПРАВКА
|
||
});
|
||
}, [currentScreenFlags, activeVariants]);
|
||
```
|
||
|
||
### 🔍 Как проверить в браузере
|
||
|
||
```javascript
|
||
// 1. Открыть /soulmate/onboarding
|
||
// 2. Открыть DevTools → Console
|
||
// 3. Увидеть:
|
||
[Unleash Impression] ✅ Sent successfully: {
|
||
feature: "soulmate-onboarding-image",
|
||
variant: "v1" // или "v0", "v2"
|
||
}
|
||
|
||
// 4. DevTools → Network → Фильтр "collect"
|
||
POST /g/collect?...&en=experiment_impression
|
||
&ep.feature=soulmate-onboarding-image
|
||
&ep.treatment=v1
|
||
|
||
// 5. DevTools → Application → Session Storage
|
||
unleash_impression_soulmate-onboarding-image_v1: "true"
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 AB-тест #2: `soulmate-trial-grid`
|
||
|
||
### 📍 Где находится в JSON
|
||
|
||
**Файл:** `public/funnels/soulmate.json`
|
||
|
||
**Экран:** `"email"` (экран ввода email)
|
||
|
||
**Строки:** 2277-2309
|
||
|
||
```json
|
||
{
|
||
"id": "email",
|
||
"template": "email",
|
||
"navigation": {
|
||
"rules": [
|
||
{
|
||
"conditions": [{
|
||
"conditionType": "unleash",
|
||
"unleashFlag": "soulmate-trial-grid",
|
||
"unleashVariants": ["coupon"]
|
||
}],
|
||
"nextScreenId": "coupon"
|
||
},
|
||
{
|
||
"conditions": [{
|
||
"conditionType": "unleash",
|
||
"unleashFlag": "soulmate-trial-grid",
|
||
"unleashVariants": ["grid"]
|
||
}],
|
||
"nextScreenId": "trial-choice"
|
||
}
|
||
],
|
||
"defaultNextScreenId": "coupon"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 🎬 Что тестируется
|
||
|
||
Два варианта навигации после email экрана:
|
||
- **coupon** - переход на экран с купоном (`coupon`)
|
||
- **grid** - переход на экран с выбором тарифов (`trial-choice`)
|
||
|
||
### ⏱️ КОГДА ОТПРАВЛЯЕТСЯ impression событие
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ Пользователь прошел всю воронку: │
|
||
│ onboarding → gender → partner-gender → ... → email │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+0ms: Пользователь попадает на экран /soulmate/email │
|
||
│ Это 19-й экран воронки (почти в конце) │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ ВАЖНО: Unleash уже загружен! │
|
||
│ activeVariants уже содержит: │
|
||
│ { │
|
||
│ "soulmate-onboarding-image": "v1", ← уже отправлено │
|
||
│ "soulmate-trial-grid": "grid" ← еще НЕ отправлено│
|
||
│ } │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+100ms: FunnelRuntime обновляется │
|
||
│ currentScreen = email экран │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+110ms: currentScreenFlags пересчитывается │
|
||
│ Анализирует email экран: │
|
||
│ • email.variants → нет unleash флагов │
|
||
│ • email.navigation.rules → soulmate-trial-grid │
|
||
│ Результат: ["soulmate-trial-grid"] │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+120ms: useEffect срабатывает │
|
||
│ (FunnelRuntime.tsx, строки 136-150) │
|
||
│ │
|
||
│ ✅ sendUnleashImpression( │
|
||
│ "soulmate-trial-grid", │
|
||
│ "grid" │
|
||
│ ) │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+121ms: Проверка sessionStorage: │
|
||
│ "unleash_impression_soulmate-trial-grid_grid" │
|
||
│ НЕ найдено ✅ (первый раз на этом экране) │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ T+122ms: ✅ ОТПРАВКА В GOOGLE ANALYTICS │
|
||
│ │
|
||
│ window.gtag("event", "experiment_impression", { │
|
||
│ app_name: "witlab-funnel", │
|
||
│ feature: "soulmate-trial-grid", │
|
||
│ treatment: "grid" │
|
||
│ }); │
|
||
│ │
|
||
│ sessionStorage.setItem( │
|
||
│ "unleash_impression_soulmate-trial-grid_grid", │
|
||
│ "true" │
|
||
│ ); │
|
||
└─────────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ Console (development): │
|
||
│ [Unleash Impression] ✅ Sent successfully: │
|
||
│ { │
|
||
│ feature: "soulmate-trial-grid", │
|
||
│ variant: "grid" │
|
||
│ } │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 📊 Когда КОНКРЕТНО
|
||
|
||
**✅ Impression отправляется:** Когда пользователь доходит до экрана "email" (19-й экран воронки)
|
||
|
||
**⏰ Timing:** ~120ms после перехода на `/soulmate/email`
|
||
|
||
**🔧 Код:**
|
||
- **Файл:** `src/components/funnel/FunnelRuntime.tsx` (строки 136-150)
|
||
- **Функция:** `sendUnleashImpression()` из `src/lib/funnel/unleash/sendImpression.ts`
|
||
|
||
**📍 Условие отправки:**
|
||
```typescript
|
||
// В FunnelRuntime.tsx
|
||
const currentScreenFlags = useMemo(() => {
|
||
const flags = new Set<string>();
|
||
|
||
// Сканирует email.variants (пусто)
|
||
// ...
|
||
|
||
// Сканирует email.navigation.rules
|
||
currentScreen.navigation?.rules?.forEach((rule) => {
|
||
rule.conditions.forEach((condition) => {
|
||
if (condition.conditionType === "unleash" && condition.unleashFlag) {
|
||
flags.add(condition.unleashFlag); // ← "soulmate-trial-grid"
|
||
}
|
||
});
|
||
});
|
||
|
||
return Array.from(flags); // ["soulmate-trial-grid"]
|
||
}, [currentScreen]);
|
||
|
||
useEffect(() => {
|
||
if (currentScreenFlags.length === 0) return;
|
||
|
||
currentScreenFlags.forEach((flag) => {
|
||
const variant = activeVariants[flag]; // "grid"
|
||
sendUnleashImpression(flag, variant); // ← ОТПРАВКА
|
||
});
|
||
}, [currentScreenFlags, activeVariants]);
|
||
```
|
||
|
||
### 🔍 Как проверить в браузере
|
||
|
||
```javascript
|
||
// 1. Пройти всю воронку до экрана email: /soulmate/email
|
||
// 2. Открыть DevTools → Console
|
||
// 3. Увидеть:
|
||
[Unleash Impression] ✅ Sent successfully: {
|
||
feature: "soulmate-trial-grid",
|
||
variant: "grid" // или "coupon"
|
||
}
|
||
|
||
// 4. DevTools → Network → Фильтр "collect"
|
||
POST /g/collect?...&en=experiment_impression
|
||
&ep.feature=soulmate-trial-grid
|
||
&ep.treatment=grid
|
||
|
||
// 5. DevTools → Application → Session Storage
|
||
// Должно быть 2 ключа:
|
||
unleash_impression_soulmate-onboarding-image_v1: "true"
|
||
unleash_impression_soulmate-trial-grid_grid: "true"
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 Сравнительная таблица
|
||
|
||
| Аспект | soulmate-onboarding-image | soulmate-trial-grid |
|
||
|--------|---------------------------|---------------------|
|
||
| **Экран** | `onboarding` (1-й экран) | `email` (19-й экран) |
|
||
| **Позиция в JSON** | `variants` | `navigation.rules` |
|
||
| **Что тестирует** | Картинка/видео на первом экране | Навигация после email |
|
||
| **Варианты** | v0, v1, v2 | coupon, grid |
|
||
| **Когда отправляется** | Сразу при входе в воронку | При достижении email экрана |
|
||
| **Timing** | T+420ms после загрузки | T+120ms после перехода на email |
|
||
| **URL** | `/soulmate/onboarding` | `/soulmate/email` |
|
||
|
||
---
|
||
|
||
## 🎯 Финальная timeline для полной воронки
|
||
|
||
```
|
||
T+0ms Пользователь открывает /soulmate/onboarding
|
||
│
|
||
T+420ms ✅ IMPRESSION #1: soulmate-onboarding-image → v1
|
||
│
|
||
T+5min Пользователь проходит всю воронку
|
||
│ (gender, partner-gender, analysis-target, partner-age,
|
||
│ partner-ethnicity, partner-eye-color, partner-hair-length,
|
||
│ burnout-support, burnout-result, birthdate,
|
||
│ nature-archetype, love-priority, love-priority-result,
|
||
│ relationship-block, qualities, qualities-result,
|
||
│ progress, progress-result)
|
||
│
|
||
T+5min Пользователь доходит до /soulmate/email
|
||
│
|
||
T+5min ✅ IMPRESSION #2: soulmate-trial-grid → grid
|
||
+120ms │
|
||
│
|
||
T+5min Пользователь вводит email и нажимает Continue
|
||
+1min │
|
||
│
|
||
└─ Переход на trial-choice (если variant = "grid")
|
||
ИЛИ
|
||
Переход на coupon (если variant = "coupon")
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 Ключевые моменты
|
||
|
||
### 1. Оба флага загружаются ЗАРАНЕЕ
|
||
|
||
```
|
||
FunnelUnleashWrapper сканирует ВСЮ воронку при загрузке
|
||
→ Находит оба флага сразу:
|
||
• soulmate-onboarding-image
|
||
• soulmate-trial-grid
|
||
→ Unleash SDK загружает варианты для ОБОИХ флагов
|
||
→ Варианты сохраняются в activeVariants
|
||
```
|
||
|
||
**Результат:** Быстрый переход между экранами (флаги уже загружены).
|
||
|
||
### 2. Impression отправляются ПОСЛЕДОВАТЕЛЬНО
|
||
|
||
```
|
||
Экран onboarding → impression для soulmate-onboarding-image
|
||
Экран email → impression для soulmate-trial-grid
|
||
```
|
||
|
||
**Результат:** Точная аналитика - события отправляются только когда пользователь РЕАЛЬНО видит экран.
|
||
|
||
### 3. Защита от дубликатов работает
|
||
|
||
```
|
||
sessionStorage хранит:
|
||
{
|
||
"unleash_impression_soulmate-onboarding-image_v1": "true",
|
||
"unleash_impression_soulmate-trial-grid_grid": "true"
|
||
}
|
||
```
|
||
|
||
**Результат:** Даже если пользователь вернется назад или перезагрузит страницу, события НЕ отправятся повторно.
|
||
|
||
---
|
||
|
||
## 🐛 Отладка
|
||
|
||
### Проверка отправки первого теста
|
||
|
||
```bash
|
||
# 1. Открыть /soulmate/onboarding
|
||
# 2. В консоли должно появиться:
|
||
[Unleash Impression] ✅ Sent successfully: {
|
||
feature: "soulmate-onboarding-image",
|
||
variant: "v1"
|
||
}
|
||
|
||
# 3. В Network tab:
|
||
POST /g/collect?...
|
||
&ep.feature=soulmate-onboarding-image
|
||
&ep.treatment=v1
|
||
```
|
||
|
||
### Проверка отправки второго теста
|
||
|
||
```bash
|
||
# 1. Пройти до /soulmate/email
|
||
# 2. В консоли должно появиться:
|
||
[Unleash Impression] ✅ Sent successfully: {
|
||
feature: "soulmate-trial-grid",
|
||
variant: "grid"
|
||
}
|
||
|
||
# 3. В Network tab:
|
||
POST /g/collect?...
|
||
&ep.feature=soulmate-trial-grid
|
||
&ep.treatment=grid
|
||
```
|
||
|
||
### Проверка что оба события отправлены
|
||
|
||
```javascript
|
||
// В консоли браузера:
|
||
Object.keys(sessionStorage)
|
||
.filter(key => key.startsWith("unleash_impression_"))
|
||
.forEach(key => console.log(key, sessionStorage.getItem(key)));
|
||
|
||
// Вывод (если пользователь дошел до email):
|
||
// unleash_impression_soulmate-onboarding-image_v1 "true"
|
||
// unleash_impression_soulmate-trial-grid_grid "true"
|
||
```
|
||
|
||
---
|
||
|
||
## ✅ Итоговый ответ
|
||
|
||
### Когда отправляется `soulmate-onboarding-image`?
|
||
|
||
**🎯 Сразу при входе в воронку на первый экран "onboarding"**
|
||
- URL: `/soulmate/onboarding`
|
||
- Timing: ~420ms после загрузки страницы
|
||
- Экран: 1-й из ~20 экранов воронки
|
||
|
||
### Когда отправляется `soulmate-trial-grid`?
|
||
|
||
**🎯 Когда пользователь доходит до экрана "email" (почти в конце воронки)**
|
||
- URL: `/soulmate/email`
|
||
- Timing: ~120ms после перехода на экран
|
||
- Экран: 19-й из ~20 экранов воронки
|
||
|
||
### Код отправки (для обоих)
|
||
|
||
**Файл:** `src/components/funnel/FunnelRuntime.tsx`
|
||
|
||
**Строки:** 136-150
|
||
|
||
```typescript
|
||
useEffect(() => {
|
||
if (currentScreenFlags.length === 0) return;
|
||
|
||
currentScreenFlags.forEach((flag) => {
|
||
const variant = activeVariants[flag];
|
||
sendUnleashImpression(flag, variant); // ← ЗДЕСЬ
|
||
});
|
||
}, [currentScreenFlags, activeVariants]);
|
||
```
|
||
|
||
**Функция:** `src/lib/funnel/unleash/sendImpression.ts`
|
||
|
||
```typescript
|
||
export function sendUnleashImpression(flag: string, variant: string | undefined) {
|
||
// Проверки...
|
||
|
||
window.gtag("event", "experiment_impression", {
|
||
app_name: "witlab-funnel",
|
||
feature: flag,
|
||
treatment: variant,
|
||
});
|
||
|
||
sessionStorage.setItem(`unleash_impression_${flag}_${variant}`, "true");
|
||
}
|
||
```
|