Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cloudflare_workers/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { app as stats_priv } from '../../supabase/functions/_backend/private/sta
import { app as storeTop } from '../../supabase/functions/_backend/private/store_top.ts'
import { app as stripe_checkout } from '../../supabase/functions/_backend/private/stripe_checkout.ts'
import { app as stripe_portal } from '../../supabase/functions/_backend/private/stripe_portal.ts'
import { app as bundle_install_stats } from '../../supabase/functions/_backend/private/bundle_install_stats.ts'
import { app as update_delivery_stats } from '../../supabase/functions/_backend/private/update_delivery_stats.ts'
import { app as validate_password_compliance } from '../../supabase/functions/_backend/private/validate_password_compliance.ts'
import { app as verify_email_otp } from '../../supabase/functions/_backend/private/verify_email_otp.ts'
Expand Down Expand Up @@ -141,6 +142,7 @@ appPrivate.route('/channel_stats', channel_stats)
appPrivate.route('/native_observe_stats', native_observe_stats)
appPrivate.route('/org_notification_stats', org_notification_stats)
appPrivate.route('/update_delivery_stats', update_delivery_stats)
appPrivate.route('/bundle_install_stats', bundle_install_stats)
appPrivate.route('/stripe_checkout', stripe_checkout)
appPrivate.route('/stripe_portal', stripe_portal)
appPrivate.route('/verify_email_otp', verify_email_otp)
Expand Down
14 changes: 13 additions & 1 deletion messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1298,7 +1298,19 @@
"failed-to-fetch-release-status": "Failed to fetch release status",
"failed-to-fetch-native-observe-stats": "Failed to fetch native observe statistics",
"failed-to-fetch-update-delivery-stats": "Failed to fetch update delivery latency",
"failed-to-fetch-statistics": "Failed to fetch statistics",
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Resolved
"bundle-install-p50": "P50 install time",
"bundle-install-p70": "P70 install time",
"bundle-install-p90": "P90 install time",
"bundle-install-p95": "P95 install time",
"bundle-install-stats-fetch-error": "Could not load bundle install statistics",
"bundle-install-stats-help": "Success rate and install duration percentiles per bundle. Success rate uses install / (install + fail). Install time is measured from download start to successful install (set).",
"bundle-install-stats-no-data": "No bundle install statistics yet",
"bundle-install-stats-no-data-help": "Data appears when devices report installs and failures for bundles in the selected period.",
"bundle-install-stats-title": "Bundle install performance",
"bundle-install-success-rate": "Success rate",
"bundle-install-success-rate-help": "install / (install + fail) across all bundles in the period",
"bundle-install-timing-help": "Median time from download start to install complete",
"failed-to-fetch-bundle-install-stats": "Failed to fetch bundle install statistics",
"failed-to-get-user": "Failed to load the user",
"failed-to-regenerate-api-key": "Failed to regenerate API key",
"failed-to-update-policy": "Failed to update password policy",
Expand Down
10 changes: 10 additions & 0 deletions playwright/visual-diff.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ export const visualDiffRoutes: VisualDiffRoute[] = [
{ slug: 'dashboard', path: '/dashboard', auth: true },
{ slug: 'apps', path: '/apps', auth: true },
{ slug: 'app-overview', path: '/app/com.demo.app', auth: true },
{
slug: 'app-bundle-install-stats',
path: '/app/com.demo.app',
auth: true,
prepare: async (page) => {
const panel = page.locator('[data-testid="bundle-install-stats"]')
await panel.scrollIntoViewIfNeeded()
await panel.waitFor({ state: 'visible' })
},
},
{ slug: 'app-settings', path: '/app/com.demo.app/settings', auth: true },
{ slug: 'app-settings-access', path: '/app/com.demo.app/settings/access', auth: true },
{ slug: 'channels', path: '/app/com.demo.app/channels', auth: true },
Expand Down
97 changes: 97 additions & 0 deletions scripts/capture-bundle-install-stats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { chromium } from '@playwright/test'
import { mkdirSync } from 'node:fs'
import { join } from 'node:path'

const baseUrl = process.env.BASE_URL || 'http://127.0.0.1:5173/'
const outDir = '/opt/cursor/artifacts/screenshots'

import { createClient } from '@supabase/supabase-js'
import { mkdirSync } from 'node:fs'
import { join } from 'node:path'
import { chromium } from '@playwright/test'
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

const baseUrl = process.env.BASE_URL || 'http://127.0.0.1:5173/'
const outDir = '/opt/cursor/artifacts/screenshots'
const supabaseUrl = process.env.SUPABASE_URL || 'http://127.0.0.1:54321'
const supabaseAnon = process.env.SUPABASE_ANON_KEY || 'sb_publishable_ACJWlzQHlZjBrEguHvfOxg_3BJgxAaH'

async function login(page: import('@playwright/test').Page) {
await page.goto(`${baseUrl}login?to=/app/com.demo.app`)
await page.fill('[data-test="email"]', 'test@capgo.app')
await page.click('[data-test="continue"]')
await page.waitForSelector('[data-test="password"]')
await page.fill('[data-test="password"]', 'testtest')
const submit = page.locator('[data-test="submit"]')
if (await submit.isEnabled())
await submit.click()
else
await page.locator('form').first().evaluate((el: HTMLFormElement) => el.requestSubmit())
await page.waitForURL(/\/(apps|dashboard|app)(\/|$)/, { timeout: 45000 })
}

async function ensureLocalAuth() {
const supabase = createClient(supabaseUrl, supabaseAnon, {
auth: { autoRefreshToken: false, persistSession: false, detectSessionInUrl: false },
})
const { error } = await supabase.auth.signInWithPassword({
email: 'test@capgo.app',
password: 'testtest',
})
if (error)
throw new Error(`Local auth failed: ${error.message}`)
}
await page.goto(`${baseUrl}login?to=/app/com.demo.app`)
await page.fill('[data-test="email"]', 'test@capgo.app')
await page.click('[data-test="continue"]')
await page.waitForSelector('[data-test="password"]')
await page.fill('[data-test="password"]', 'testtest')
const submit = page.locator('[data-test="submit"]')
if (await submit.isEnabled())
await submit.click()
else
await page.locator('form').first().evaluate((el: HTMLFormElement) => el.requestSubmit())
await page.waitForURL(/\/(apps|dashboard|app)(\/|$)/, { timeout: 45000 })
}

async function main() {
mkdirSync(outDir, { recursive: true })
if (!process.env.SKIP_LOCAL_AUTH)
await ensureLocalAuth().catch(() => undefined)
const browser = await chromium.launch({ headless: true })
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } })

try {
await login(page)
await page.goto(`${baseUrl}app/com.demo.app`)
await page.waitForLoadState('networkidle')
const panel = page.locator('[data-testid="bundle-install-stats"]')
await panel.scrollIntoViewIfNeeded()
await page.waitForTimeout(1500)
await panel.screenshot({ path: join(outDir, 'bundle-install-stats-app.png') })
await page.screenshot({ path: join(outDir, 'bundle-install-stats-app-full.png'), fullPage: true })

await page.goto(`${baseUrl}app/com.demo.app/channels`)
await page.waitForLoadState('networkidle')
const statsLink = page.locator('a[href*="/statistics"]').first()
if (await statsLink.count() > 0) {
await statsLink.click()
await page.waitForLoadState('networkidle')
const channelPanel = page.locator('[data-testid="bundle-install-stats"]')
await channelPanel.scrollIntoViewIfNeeded()
await page.waitForTimeout(1500)
await channelPanel.screenshot({ path: join(outDir, 'bundle-install-stats-channel.png') })
}

console.log('Screenshots saved to', outDir)
}
catch (error) {
await page.screenshot({ path: join(outDir, 'bundle-install-stats-failure.png'), fullPage: true })
console.error(error)
process.exitCode = 1
}
finally {
await browser.close()
}
}

await main()
Loading
Loading