26 KiB
🧪 Конкретный анализ AB-тестов в воронке Soulmate
📋 Два AB-теста в воронке
soulmate-onboarding-image- тест картинки/видео на первом экранеsoulmate-trial-grid- тест навигации после email экрана
🎯 AB-тест #1: soulmate-onboarding-image
📍 Где находится в JSON
Файл: public/funnels/soulmate.json
Экран: "onboarding" (первый экран воронки)
Строки: 51-106
{
"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
📍 Условие отправки:
// В 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]);
🔍 Как проверить в браузере
// 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
{
"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
📍 Условие отправки:
// В 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]);
🔍 Как проверить в браузере
// 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"
}
Результат: Даже если пользователь вернется назад или перезагрузит страницу, события НЕ отправятся повторно.
🐛 Отладка
Проверка отправки первого теста
# 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
Проверка отправки второго теста
# 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
Проверка что оба события отправлены
// В консоли браузера:
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
useEffect(() => {
if (currentScreenFlags.length === 0) return;
currentScreenFlags.forEach((flag) => {
const variant = activeVariants[flag];
sendUnleashImpression(flag, variant); // ← ЗДЕСЬ
});
}, [currentScreenFlags, activeVariants]);
Функция: src/lib/funnel/unleash/sendImpression.ts
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");
}