d3ro-voice/apps/desktop/tests/e2e/launch_readiness.spec.ts
Yun Chan eedd127ea7
Some checks failed
ci / 정본·보안·린트·타입·테스트 (push) Failing after 1m13s
ci / 워크스페이스 빌드 검증 (push) Has been skipped
ci / 모바일 린트·타입·Jest (push) Failing after 1m4s
ci / Supabase Edge Functions + Cloudflare Worker (push) Successful in 37s
ci / .NET API 서버 테스트 (push) Successful in 27s
deploy-site / deploy (push) Failing after 20s
refactor(billing): remove Stripe; payments are Payple (web) and Google Play (mobile)
Stripe is not used. Keeping its checkout, portal and webhook paths meant a
second payment provider, a second return-URL format and dead UI.

- Delete the stripe-checkout, stripe-portal and stripe-webhook functions and
  their config; billing-catalog serves Payple prices only, and the web parser
  rejects a catalog that still mixes in Stripe prices.
- Web: drop the Stripe checkout/portal buttons, provider toggle and return
  notices; billing shows Payple only. Past rows with provider='stripe' are
  still displayed ("Stripe (종료)") with a support contact instead of a portal.
- Desktop: delete the Stripe checkout modal, payment IPC channels, preload
  namespace and their types; "Remove ads with Pro" opens the web billing page
  via license.openBilling. Support/refund copy names Payple.
- billingUrl() loses the Stripe-only success/canceled result option; the
  Deno contract is regenerated.
- Migrations and the DB's accepted provider values are untouched (history).
- Docs and the backlog record the removal (MON-04, EXT-STRIPE-01, GAP-BILL-03).

Verified: typecheck (desktop/web/admin/api-client/mobile), contract:check,
deno check all functions, deno test 80/80, desktop 1478/1480 on the Electron
runtime (2 known environment failures), web and admin builds, release
metadata and mobile boundary self-tests, eslint on changed files.
2026-09-26 20:56:18 +09:00

130 lines
4.6 KiB
TypeScript

import * as fs from 'fs';
import * as path from 'path';
import { test, expect, _electron as electron } from '@playwright/test';
const SCREENSHOT_DIR = 'C:/Users/encep/.gemini/antigravity-cli/brain/3ab7ae82-5634-41d5-93a7-3c12c22503e2/screenshots';
test.describe('Launch Readiness E2E & Visual Verification', () => {
test.describe.configure({ mode: 'serial' });
let electronApp: any;
let window: any;
test.beforeAll(async () => {
if (!fs.existsSync(SCREENSHOT_DIR)) {
fs.mkdirSync(SCREENSHOT_DIR, { recursive: true });
}
electronApp = await electron.launch({
args: [
'out/main/index.js',
'--disable-gpu',
'--no-sandbox',
'--user-data-dir=C:/Users/encep/AppData/Local/Temp/playwright-d3ro-launch-test-' + Date.now(),
],
env: {
...process.env,
NODE_ENV: 'test',
},
});
// Poll for main window (excluding popup windows)
for (let i = 0; i < 40; i++) {
for (const w of electronApp.windows()) {
try {
const url = w.url();
if (url && url.includes('index.html') && !url.includes('popups/')) {
window = w;
break;
}
} catch {
// ignore
}
}
if (window) break;
await new Promise((r) => setTimeout(r, 500));
}
if (!window) {
window = electronApp.windows()[0] || await electronApp.firstWindow();
}
await window.waitForLoadState('domcontentloaded');
// Bypass onboarding via IPC config set
await window.evaluate(async () => {
if (window.electronAPI && window.electronAPI.config) {
await window.electronAPI.config.set({ key: 'onboardingCompleted', value: true });
await window.electronAPI.config.set({ key: 'appUsageMode', value: 'online' });
}
});
await window.waitForTimeout(500);
await window.reload();
await window.waitForLoadState('domcontentloaded');
await window.waitForTimeout(1500);
});
test.afterAll(async () => {
if (electronApp) {
await electronApp.close();
}
});
test('01. Should render desktop app with Free Tier AdBanner and Support button', async () => {
// Check if sidebar has Customer Support button
const supportBtn = window.locator('text=/^(Customer Support|고객지원)$/').first();
await expect(supportBtn).toBeVisible({ timeout: 10000 });
// Check if Free Tier AdBanner is visible
const adBanner = window.locator('text=/Cursor AI/').first();
await expect(adBanner).toBeVisible({ timeout: 10000 });
// Take screenshot of main window with AdBanner and sidebar
await window.screenshot({ path: path.join(SCREENSHOT_DIR, '01_desktop_main_with_adbanner.png') });
});
test('02. Should open SupportModal and interact with AI Customer Assistant', async () => {
// Click Customer Support in sidebar
await window.locator('text=/^(Customer Support|고객지원)$/').first().click();
await window.waitForTimeout(500);
// Verify modal header is visible
await expect(window.locator('text=/D3RO Voice Customer Assistance/').first()).toBeVisible();
// Click quick prompt "🎤 마이크 인식 오류"
const micPrompt = window.locator('text=/마이크 인식/').first();
if (await micPrompt.isVisible()) {
await micPrompt.click();
await window.waitForTimeout(800);
}
// Capture screenshot of SupportModal with AI chat
await window.screenshot({ path: path.join(SCREENSHOT_DIR, '02_support_modal_ai_chat.png') });
// Switch to 7-Day Refund Tab (tab 3)
const refundTab = window.locator('text=/7일 자동 환불/').first();
await refundTab.click();
await window.waitForTimeout(500);
// Click "환불 자격 자동 조회하기"
const checkRefundBtn = window.locator('button:has-text("환불 자격 자동 조회하기")').first();
if (await checkRefundBtn.isVisible()) {
await checkRefundBtn.click();
await window.waitForTimeout(1000);
}
// Capture screenshot of refund eligibility result
await window.screenshot({ path: path.join(SCREENSHOT_DIR, '03_support_modal_refund_check.png') });
// Close Support Modal
await window.keyboard.press('Escape');
await window.waitForTimeout(500);
});
test('03. Should expose the web billing upgrade link without an in-app checkout modal', async () => {
// "Remove ads with Pro" opens billingUrl({ tier }) in the external browser (LICENSE.OPEN_BILLING).
const removeAdsLink = window.locator('text=/Remove ads with Pro/').first();
if (await removeAdsLink.isVisible()) {
await expect(window.locator('text=/D3RO Voice Secure Checkout/')).toHaveCount(0);
}
});
});