[{"data":1,"prerenderedAt":602},["ShallowReactive",2],{"navigation_docs_ru":3,"-ru-development-testing":167,"-ru-development-testing-surround":597},[4,61,101,146],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":60},"Руководство","i-lucide-book-open","\u002Fru\u002Fguide","ru\u002F1.guide",[10,15,20,25,30,35,40,45,50,55],{"title":11,"path":12,"stem":13,"icon":14},"Введение","\u002Fru\u002Fguide\u002Fintroduction","ru\u002F1.guide\u002F01.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Установка","\u002Fru\u002Fguide\u002Finstallation","ru\u002F1.guide\u002F02.installation","i-lucide-smartphone",{"title":21,"path":22,"stem":23,"icon":24},"Авторизация","\u002Fru\u002Fguide\u002Fauth","ru\u002F1.guide\u002F03.auth","i-lucide-lock",{"title":26,"path":27,"stem":28,"icon":29},"Кошельки","\u002Fru\u002Fguide\u002Fwallets","ru\u002F1.guide\u002F04.wallets","i-lucide-wallet",{"title":31,"path":32,"stem":33,"icon":34},"Категории","\u002Fru\u002Fguide\u002Fcategories","ru\u002F1.guide\u002F05.categories","i-lucide-tags",{"title":36,"path":37,"stem":38,"icon":39},"Транзакции","\u002Fru\u002Fguide\u002Ftransactions","ru\u002F1.guide\u002F06.transactions","i-lucide-receipt",{"title":41,"path":42,"stem":43,"icon":44},"Переводы","\u002Fru\u002Fguide\u002Ftransfers","ru\u002F1.guide\u002F07.transfers","i-lucide-arrow-left-right",{"title":46,"path":47,"stem":48,"icon":49},"Статистика","\u002Fru\u002Fguide\u002Fstatistics","ru\u002F1.guide\u002F08.statistics","i-lucide-bar-chart-3",{"title":51,"path":52,"stem":53,"icon":54},"Тема","\u002Fru\u002Fguide\u002Ftheme","ru\u002F1.guide\u002F09.theme","i-lucide-palette",{"title":56,"path":57,"stem":58,"icon":59},"Настройки","\u002Fru\u002Fguide\u002Fsettings","ru\u002F1.guide\u002F10.settings","i-lucide-settings",false,{"title":62,"icon":63,"path":64,"stem":65,"children":66,"page":60},"Разработка","i-lucide-code","\u002Fru\u002Fdevelopment","ru\u002F2.development",[67,71,76,81,86,91,96],{"title":16,"path":68,"stem":69,"icon":70},"\u002Fru\u002Fdevelopment\u002Finstallation","ru\u002F2.development\u002F01.installation","i-lucide-download",{"title":72,"path":73,"stem":74,"icon":75},"Граф кодовой базы","\u002Fru\u002Fdevelopment\u002Funderstand-anything","ru\u002F2.development\u002F02.understand-anything","i-lucide-network",{"title":77,"path":78,"stem":79,"icon":80},"Офлайн и PWA","\u002Fru\u002Fdevelopment\u002Foffline","ru\u002F2.development\u002F03.offline","i-lucide-wifi-off",{"title":82,"path":83,"stem":84,"icon":85},"История миграций данных","\u002Fru\u002Fdevelopment\u002Fmigration","ru\u002F2.development\u002F04.migration","i-lucide-database",{"title":87,"path":88,"stem":89,"icon":90},"Деплой","\u002Fru\u002Fdevelopment\u002Fdeployment","ru\u002F2.development\u002F05.deployment","i-lucide-rocket",{"title":92,"path":93,"stem":94,"icon":95},"Тестирование","\u002Fru\u002Fdevelopment\u002Ftesting","ru\u002F2.development\u002F06.testing","i-lucide-flask-conical",{"title":97,"path":98,"stem":99,"icon":100},"Дата-утилиты","\u002Fru\u002Fdevelopment\u002Fdate-utilities","ru\u002F2.development\u002F07.date-utilities","i-lucide-calendar",{"title":102,"icon":103,"path":104,"stem":105,"children":106,"page":60},"Справочник","i-lucide-file-code","\u002Fru\u002Freference","ru\u002F3.reference",[107,112,116,121,126,131,136,141],{"title":108,"path":109,"stem":110,"icon":111},"Архитектура","\u002Fru\u002Freference\u002Farchitecture","ru\u002F3.reference\u002F01.architecture","i-lucide-boxes",{"title":113,"path":114,"stem":115,"icon":44},"Типы транзакций","\u002Fru\u002Freference\u002Ftransaction-types","ru\u002F3.reference\u002F02.transaction-types",{"title":117,"path":118,"stem":119,"icon":120},"Синхронизация","\u002Fru\u002Freference\u002Fsync","ru\u002F3.reference\u002F03.sync","i-lucide-refresh-cw",{"title":122,"path":123,"stem":124,"icon":125},"Офлайн-first","\u002Fru\u002Freference\u002Foffline-first","ru\u002F3.reference\u002F04.offline-first","i-lucide-list-ordered",{"title":127,"path":128,"stem":129,"icon":130},"Тех. решения","\u002Fru\u002Freference\u002Ftech-decisions","ru\u002F3.reference\u002F05.tech-decisions","i-lucide-lightbulb",{"title":132,"path":133,"stem":134,"icon":135},"Валидация","\u002Fru\u002Freference\u002Fvalidation-strategy","ru\u002F3.reference\u002F06.validation-strategy","i-lucide-shield-check",{"title":137,"path":138,"stem":139,"icon":140},"Что изменилось со времён Firebase","\u002Fru\u002Freference\u002Ffirebase-migration","ru\u002F3.reference\u002F07.firebase-migration","i-lucide-hamburger",{"title":142,"path":143,"stem":144,"icon":145},"Производительность","\u002Fru\u002Freference\u002Fperformance","ru\u002F3.reference\u002F08.performance","i-lucide-gauge",{"title":147,"icon":148,"path":149,"stem":150,"children":151,"page":60},"Премиум","i-lucide-star","\u002Fru\u002Fpremium","ru\u002F4.premium",[152,157,162],{"title":153,"path":154,"stem":155,"icon":156},"Обзор","\u002Fru\u002Fpremium\u002Foverview","ru\u002F4.premium\u002F01.overview","i-lucide-layers",{"title":158,"path":159,"stem":160,"icon":161},"Telegram-бот","\u002Fru\u002Fpremium\u002Ftelegram-bot","ru\u002F4.premium\u002F02.telegram-bot","i-lucide-send",{"title":163,"path":164,"stem":165,"icon":166},"AI Chat","\u002Fru\u002Fpremium\u002Fai-chat","ru\u002F4.premium\u002F03.ai-chat","i-lucide-sparkles",{"id":168,"title":92,"body":169,"description":589,"extension":590,"links":591,"meta":592,"navigation":593,"path":93,"seo":594,"stem":94,"__hash__":596},"docs_ru\u002Fru\u002F2.development\u002F06.testing.md",{"type":170,"value":171,"toc":576},"minimark",[172,177,181,205,209,220,225,299,303,327,342,346,349,360,364,370,387,402,442,464,469,479,500,514,518,527,547,565,572],[173,174,176],"h2",{"id":175},"unit-тесты","Unit-тесты",[178,179,180],"p",{},"Запуск Vitest:",[182,183,189],"pre",{"className":184,"code":185,"filename":186,"language":187,"meta":188,"style":188},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm test\n","Terminal","bash","",[190,191,192],"code",{"__ignoreMap":188},[193,194,197,201],"span",{"class":195,"line":196},"line",1,[193,198,200],{"class":199},"sBMFI","pnpm",[193,202,204],{"class":203},"sfazB"," test\n",[173,206,208],{"id":207},"e2e-тесты","E2E-тесты",[178,210,211,212,219],{},"E2E-тесты работают на ",[213,214,218],"a",{"href":215,"rel":216},"https:\u002F\u002Fplaywright.dev",[217],"nofollow","Playwright",". Playwright поставляет браузеры отдельно от npm-пакета, поэтому после клонирования нужна разовая установка.",[221,222,224],"h3",{"id":223},"скрипты","Скрипты",[226,227,228,241],"table",{},[229,230,231],"thead",{},[232,233,234,238],"tr",{},[235,236,237],"th",{},"Скрипт",[235,239,240],{},"Назначение",[242,243,244,259,269,279,289],"tbody",{},[232,245,246,252],{},[247,248,249],"td",{},[190,250,251],{},"pnpm test:e2e:install",[247,253,254,255,258],{},"Разовая установка бинарника Chromium. Повторять после обновления ",[190,256,257],{},"@playwright\u002Ftest"," - версия браузера привязана к версии пакета.",[232,260,261,266],{},[247,262,263],{},[190,264,265],{},"pnpm test:e2e",[247,267,268],{},"Запуск всех тестов в headless-режиме. Используется в CI.",[232,270,271,276],{},[247,272,273],{},[190,274,275],{},"pnpm test:e2e:headed",[247,277,278],{},"Запуск с видимым окном браузера. Полезно, чтобы посмотреть сценарий или отладить нестабильный тест.",[232,280,281,286],{},[247,282,283],{},[190,284,285],{},"pnpm test:e2e:ui",[247,287,288],{},"Интерактивный UI Playwright с time-travel, watch-режимом и пошаговым просмотром. Лучший вариант для написания новых тестов.",[232,290,291,296],{},[247,292,293],{},[190,294,295],{},"pnpm test:e2e:report",[247,297,298],{},"Открывает HTML-отчёт последнего прогона (скриншоты, трейсы, видео падений).",[221,300,302],{"id":301},"типовой-рабочий-процесс","Типовой рабочий процесс",[304,305,306,312,317,322],"ol",{},[307,308,309,310],"li",{},"Первый раз: ",[190,311,251],{},[307,313,314,315],{},"Пишешь тесты: ",[190,316,285],{},[307,318,319,320],{},"CI или полный прогон: ",[190,321,265],{},[307,323,324,325],{},"Разбираешь падение: ",[190,326,295],{},[178,328,329,330,333,334,337,338,341],{},"Строго обязательны только ",[190,331,332],{},"test:e2e"," и ",[190,335,336],{},"test:e2e:install"," - остальные это удобные обёртки над флагами ",[190,339,340],{},"playwright"," CLI.",[173,343,345],{"id":344},"локальный-вход-без-google-seed-данные","Локальный вход без Google (seed-данные)",[178,347,348],{},"Тестам в реальном режиме (PowerSync) нужна активная сессия, но Google OAuth не автоматизируешь. Локальный стек поставляет фиксированного тест-юзера с email\u002Fпаролем и набор данных из демо, чтобы агенты \u002F Playwright \u002F ручные прогоны входили в реальный режим без Google.",[350,351,352],"blockquote",{},[178,353,354,355,359],{},"Email\u002Fpassword включён в ",[356,357,358],"strong",{},"бэкенде"," Supabase, но намеренно не выводится в UI приложения (только Google + Demo). Тест-юзер нужен для локальных тестов; его пароль локальный и не секрет.",[221,361,363],{"id":362},"seed","Seed",[178,365,366,369],{},[190,367,368],{},"app\u002Fsupabase\u002Fseed.sql"," (генерируемый, идемпотентный, в транзакции) создаёт:",[371,372,373,384],"ul",{},[307,374,375,376,379,380,383],{},"подтверждённого тест-юзера - ",[190,377,378],{},"e2e@finapp.local"," \u002F ",[190,381,382],{},"finapp-e2e"," (фиксированный UUID), и",[307,385,386],{},"его набор из демо: 8 кошельков, 32 категории, ~860 транзакций.",[178,388,389,390,393,394,397,398,401],{},"Применяется автоматически при ",[190,391,392],{},"supabase db reset"," (",[190,395,396],{},"[db.seed]"," в ",[190,399,400],{},"app\u002Fsupabase\u002Fconfig.toml","). Чтобы (пере)применить к работающей БД без полного reset:",[182,403,405],{"className":184,"code":404,"filename":186,"language":187,"meta":188,"style":188},"docker exec -i supabase_db_app psql -U postgres -d postgres \u003C app\u002Fsupabase\u002Fseed.sql\n",[190,406,407],{"__ignoreMap":188},[193,408,409,412,415,418,421,424,427,430,433,435,439],{"class":195,"line":196},[193,410,411],{"class":199},"docker",[193,413,414],{"class":203}," exec",[193,416,417],{"class":203}," -i",[193,419,420],{"class":203}," supabase_db_app",[193,422,423],{"class":203}," psql",[193,425,426],{"class":203}," -U",[193,428,429],{"class":203}," postgres",[193,431,432],{"class":203}," -d",[193,434,429],{"class":203},[193,436,438],{"class":437},"sMK4o"," \u003C",[193,440,441],{"class":203}," app\u002Fsupabase\u002Fseed.sql\n",[178,443,444,445,448,449,452,453,456,457,459,460,463],{},"Вход по email\u002Fпаролю работает через Supabase password grant даже при выключенной email-регистрации (",[190,446,447],{},"[auth.email] enable_signup = false","), потому что юзер создан заранее. Токен-колонки GoTrue в seed заданы как ",[190,450,451],{},"''"," (не ",[190,454,455],{},"NULL",") - ",[190,458,455],{}," вызывает ",[190,461,462],{},"Database error querying schema"," при входе.",[465,466,468],"h4",{"id":467},"перегенерация-seed-из-демо-данных","Перегенерация seed из демо-данных",[178,470,471,474,475,478],{},[190,472,473],{},"seed.sql"," собирается из снапшота реального генератора демо скриптом ",[190,476,477],{},"app\u002Fscripts\u002Fgen-seed.mjs",":",[304,480,481,491],{},[307,482,483,484,397,487,490],{},"Открыть приложение в demo-режиме, считать Pinia-стора́ через devtools и сохранить ",[190,485,486],{},"{ wallets, categories, trns }",[190,488,489],{},"app\u002Fscripts\u002F.demo-snapshot.json"," (в gitignore).",[307,492,493,496,497,499],{},[190,494,495],{},"node app\u002Fscripts\u002Fgen-seed.mjs"," - пишет ",[190,498,368],{},".",[178,501,502,503,379,506,509,510,513],{},"Синтетические категории ",[190,504,505],{},"adjustment",[190,507,508],{},"transfer"," пропускаются (это литералы ",[190,511,512],{},"trns.categoryId",", а не реальные строки категорий).",[221,515,517],{"id":516},"вход-под-тест-юзером","Вход под тест-юзером",[178,519,520,523,524,478],{},[190,521,522],{},"app\u002Fscripts\u002Fdev-login.mjs"," минтит сессию через password grant и печатает значение для ",[190,525,526],{},"localStorage['finapp.auth']",[182,528,530],{"className":184,"code":529,"filename":186,"language":187,"meta":188,"style":188},"node app\u002Fscripts\u002Fdev-login.mjs --raw    # печатает сырой JSON сессии\n",[190,531,532],{"__ignoreMap":188},[193,533,534,537,540,543],{"class":195,"line":196},[193,535,536],{"class":199},"node",[193,538,539],{"class":203}," app\u002Fscripts\u002Fdev-login.mjs",[193,541,542],{"class":203}," --raw",[193,544,546],{"class":545},"sHwdD","    # печатает сырой JSON сессии\n",[178,548,549,550,553,554,557,558,379,561,564],{},"В браузере выставить это значение, очистить cookie ",[190,551,552],{},"finapp.isDemo"," и перезагрузить - приложение стартует в реальном режиме PowerSync под тест-юзером, seed-данные подтягиваются. Учётные данные\u002FURL читаются из ",[190,555,556],{},"app\u002F.env"," (переопределяются ",[190,559,560],{},"E2E_EMAIL",[190,562,563],{},"E2E_PASSWORD",").",[178,566,567,568,571],{},"Это же основа для Playwright ",[190,569,570],{},"storageState"," global-setup: войти один раз через grant и переиспользовать сессию между тестами.",[573,574,575],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":188,"searchDepth":577,"depth":577,"links":578},2,[579,580,585],{"id":175,"depth":577,"text":176},{"id":207,"depth":577,"text":208,"children":581},[582,584],{"id":223,"depth":583,"text":224},3,{"id":301,"depth":583,"text":302},{"id":344,"depth":577,"text":345,"children":586},[587,588],{"id":362,"depth":583,"text":363},{"id":516,"depth":583,"text":517},"Скрипты для unit- и E2E-тестов.","md",null,{},{"icon":95},{"title":92,"description":595},"Unit-тесты на Vitest и E2E-тесты на Playwright.","fSFmtvoM5_6Tf43thzjHoJdtJhMIrV_ujXOfcPddiYA",[598,600],{"title":87,"path":88,"stem":89,"description":599,"icon":90,"children":-1},"Деплой Finapp и документации на Vercel из монорепы с Supabase и PowerSync в качестве бэкенда.",{"title":97,"path":98,"stem":99,"description":601,"icon":100,"children":-1},"Вычисление диапазонов, форматирование периодов и распределение транзакций по интервалам.",1782114340088]