playwright

This commit is contained in:
Jason Laster 2025-03-13 15:33:54 -04:00
parent 3cef889564
commit 3178504069
12 changed files with 75 additions and 198 deletions

View File

@ -69,11 +69,4 @@ jobs:
- name: Setup and Build
uses: ./.github/actions/setup-and-build
with:
sentry-auth-token: ${{ secrets.SENTRY_AUTH_TOKEN }}
- name: Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@v3
with:
command: pages deploy
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
sentry-auth-token: ${{ secrets.SENTRY_AUTH_TOKEN }}

View File

@ -1,11 +1,12 @@
name: Playwright Tests
on:
pull_request:
deployment_status:
jobs:
test:
name: 'Playwright Tests'
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' || (github.event_name == 'deployment_status' && github.event.deployment_status.state == 'success') }}
steps:
- uses: actions/checkout@v4
@ -26,19 +27,15 @@ jobs:
- name: Install Playwright browsers
run: npx playwright install chromium
- name: Run Playwright tests with supabase
run: pnpm test:e2e:supabase
- name: Get Vercel Preview URL
if: ${{ github.event_name == 'deployment_status' }}
run: |
echo "PLAYWRIGHT_TEST_BASE_URL=${{ github.event.deployment_status.target_url }}" >> $GITHUB_ENV
echo "Testing against Vercel Preview URL: ${{ github.event.deployment_status.target_url }}"
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 30
- name: Run Playwright tests without supabase
run: pnpm test:e2e:legacy
- name: Run Playwright tests
if: ${{ github.event_name == 'deployment_status' }}
run: npx playwright test
- name: Upload test results
if: always()

View File

@ -109,75 +109,6 @@ Run the test suite with:
pnpm test
```
---
## 🚀 Deployment
### Deploy to Cloudflare Pages
```bash
pnpm run deploy
```
Ensure you have required permissions and that Wrangler is configured.
---
## 🐳 Docker Deployment
This section outlines the methods for deploying the application using Docker. The processes for **Development** and **Production** are provided separately for clarity.
---
### 🧑‍💻 Development Environment
#### Build Options
**Option 1: Helper Scripts**
```bash
# Development build
npm run dockerbuild
```
**Option 2: Direct Docker Build Command**
```bash
docker build . --target bolt-ai-development
```
**Option 3: Docker Compose Profile**
```bash
docker-compose --profile development up
```
#### Running the Development Container
```bash
docker run -p 5173:5173 --env-file .env.local bolt-ai:development
```
---
### 🏭 Production Environment
#### Build Options
**Option 1: Helper Scripts**
```bash
# Production build
npm run dockerbuild:prod
```
**Option 2: Direct Docker Build Command**
```bash
docker build . --target bolt-ai-production
```
**Option 3: Docker Compose Profile**
```bash
docker-compose --profile production up
```
#### Running the Production Container
```bash
docker run -p 5173:5173 --env-file .env.local bolt-ai:production
```
---

View File

@ -1,31 +1,38 @@
import * as Sentry from '@sentry/nextjs';
import { createRequestHandler } from '~/lib/remix-types';
// We'll import the server build at runtime, not during compilation
// Build path will be available after the build is complete
/*
* We'll import the server build at runtime, not during compilation
* Build path will be available after the build is complete
*/
// Add Sentry's request handler to wrap the Remix request handler
const handleRequest = async (request: Request) => {
try {
// Dynamically import the server build at runtime
// In a real Vercel deployment, the server build will be available
// This is just a placeholder for type checking
const build = { /* production build will be available at runtime */ };
/*
* Dynamically import the server build at runtime
* In a real Vercel deployment, the server build will be available
* This is just a placeholder for type checking
*/
const build = {
/* production build will be available at runtime */
};
// Create the request handler
const handler = createRequestHandler({
build: build as any,
mode: process.env.NODE_ENV,
getLoadContext: () => ({
env: process.env
})
env: process.env,
}),
});
// Handle the request
return handler(request);
} catch (error) {
// Log the error with Sentry
Sentry.captureException(error);
// Return a basic error response
return new Response('Server Error', { status: 500 });
}
@ -39,4 +46,4 @@ export const DELETE = handleRequest;
export const HEAD = handleRequest;
export const OPTIONS = handleRequest;
export const runtime = 'edge';
export const runtime = 'edge';

View File

@ -4,7 +4,7 @@ import { sentryHandleError } from '~/lib/sentry';
* Using our conditional Sentry implementation instead of direct import
* This avoids loading Sentry in development environments
*/
import type { AppLoadContext, EntryContext } from '~/lib/remix-types';
import type { AppLoadContext } from '~/lib/remix-types';
import { RemixServer } from '@remix-run/react';
import { isbot } from 'isbot';
import { renderToString } from 'react-dom/server';
@ -26,9 +26,21 @@ export default async function handleRequest(
const isBot = isbot(userAgent || '');
// Create the HTML string
const markup = renderToString(
<RemixServer context={remixContext} url={request.url} />
);
const markup = renderToString(<RemixServer context={remixContext} url={request.url} />);
// If this is a bot request, we can wait for all data to be ready
if (isBot) {
/*
* In Cloudflare, we had:
* await readable.allReady;
*
* For Vercel, we could do additional processing for bots
* such as waiting for all data fetching to complete.
* Future enhancement: add mechanism to ensure all data is loaded
* before rendering for bots (important for SEO)
*/
console.log(`Bot detected: ${userAgent}`);
}
// @ts-ignore - Fix for incompatible EntryContext types between different remix versions
const head = renderHeadToString({ request, remixContext, Head });

View File

@ -200,7 +200,7 @@ async function loadAsyncHooksContextManager() {
}
}
export async function createTracer(appContext: AppLoadContext) {
export async function createTracer(_appContext: AppLoadContext) {
const honeycombApiKey = process.env.HONEYCOMB_API_KEY;
const honeycombDataset = process.env.HONEYCOMB_DATASET;

View File

@ -1,10 +1,10 @@
// This file provides compatibility types to smoothly migrate from Cloudflare to Vercel
import type {
import type {
ActionFunctionArgs as VercelActionFunctionArgs,
LoaderFunctionArgs as VercelLoaderFunctionArgs,
AppLoadContext as VercelAppLoadContext,
EntryContext as VercelEntryContext
EntryContext as VercelEntryContext,
} from '@vercel/remix';
// Re-export necessary types with compatible names
@ -14,7 +14,7 @@ export type LoaderFunction = (args: LoaderFunctionArgs) => Promise<Response> | R
export type ActionFunction = (args: ActionFunctionArgs) => Promise<Response> | Response;
export type AppLoadContext = VercelAppLoadContext;
export type EntryContext = VercelEntryContext;
export type MetaFunction = () => Array<{
export type MetaFunction = () => Array<{
title?: string;
name?: string;
content?: string;
@ -22,7 +22,7 @@ export type MetaFunction = () => Array<{
}>;
export type LinksFunction = () => Array<{ rel: string; href: string }>;
// Re-export json function
// Re-export json function
export function json<T>(data: T, init?: ResponseInit): Response {
return new Response(JSON.stringify(data), {
...init,
@ -34,14 +34,16 @@ export function json<T>(data: T, init?: ResponseInit): Response {
}
// Export a createRequestHandler function
export function createRequestHandler(options: {
export function createRequestHandler(_options: {
build: any;
mode?: string;
getLoadContext?: (req: Request) => AppLoadContext;
}) {
return async (request: Request) => {
// This is a simplified handler for type checking
// The real implementation will use Vercel's handler
return new Response("Not implemented", { status: 501 });
return async (_request: Request) => {
/*
* This is a simplified handler for type checking
* The real implementation will use Vercel's handler
*/
return new Response('Not implemented', { status: 501 });
};
}
}

View File

@ -1,7 +1,7 @@
import type { LoaderFunction } from '~/lib/remix-types';
import { providerBaseUrlEnvKeys } from '~/utils/constants';
export const loader: LoaderFunction = async ({ context, request }) => {
export const loader: LoaderFunction = async ({ context: _, request }) => {
const url = new URL(request.url);
const provider = url.searchParams.get('provider');
@ -10,6 +10,7 @@ export const loader: LoaderFunction = async ({ context, request }) => {
}
const envVarName = providerBaseUrlEnvKeys[provider].apiTokenKey;
// Use only process.env since context.env might be undefined
const isSet = !!process.env[envVarName];

View File

@ -29,7 +29,7 @@ export async function action(args: ActionFunctionArgs) {
}
async function pingTelemetryAction({ request }: ActionFunctionArgs) {
const { event, data } = await request.json() as {
const { event, data } = (await request.json()) as {
event: string;
data: any;
};

View File

@ -111,76 +111,6 @@ pnpm test
---
## 🚀 Deployment
### Deploy to Cloudflare Pages
```bash
pnpm run deploy
```
Ensure you have required permissions and that Wrangler is configured.
---
## 🐳 Docker Deployment
This section outlines the methods for deploying the application using Docker. The processes for **Development** and **Production** are provided separately for clarity.
---
### 🧑‍💻 Development Environment
#### Build Options
**Option 1: Helper Scripts**
```bash
# Development build
npm run dockerbuild
```
**Option 2: Direct Docker Build Command**
```bash
docker build . --target bolt-ai-development
```
**Option 3: Docker Compose Profile**
```bash
docker-compose --profile development up
```
#### Running the Development Container
```bash
docker run -p 5173:5173 --env-file .env.local bolt-ai:development
```
---
### 🏭 Production Environment
#### Build Options
**Option 1: Helper Scripts**
```bash
# Production build
npm run dockerbuild:prod
```
**Option 2: Direct Docker Build Command**
```bash
docker build . --target bolt-ai-production
```
**Option 3: Docker Compose Profile**
```bash
docker-compose --profile production up
```
#### Running the Production Container
```bash
docker run -p 5173:5173 --env-file .env.local bolt-ai:production
```
---
### Coolify Deployment
For an easy deployment process, use [Coolify](https://github.com/coollabsio/coolify):

View File

@ -219,7 +219,7 @@ When you add a new model to the MODEL_LIST array, it will immediately be availab
- `pnpm test`: Runs the test suite using Vitest.
- `pnpm run typecheck`: Runs TypeScript type checking.
- `pnpm run typegen`: Generates TypeScript types using Wrangler.
- `pnpm run deploy`: Builds the project and deploys it to Cloudflare Pages.
- `pnpm run deploy`: Builds the project and deploys it to Vercel.
---

View File

@ -1,6 +1,8 @@
import { defineConfig, devices } from '@playwright/test';
const port = 5175;
const usePreviewUrl = !!process.env.PLAYWRIGHT_TEST_BASE_URL;
const baseURL = usePreviewUrl ? process.env.PLAYWRIGHT_TEST_BASE_URL : `http://localhost:${port}`;
export default defineConfig({
testDir: './tests/e2e',
@ -11,7 +13,7 @@ export default defineConfig({
reporter: 'html',
timeout: 60000, // Increase global timeout to 60 seconds
use: {
baseURL: `http://localhost:${port}`,
baseURL,
trace: 'on',
},
projects: [
@ -20,11 +22,13 @@ export default defineConfig({
use: { ...devices['Desktop Chrome'] },
},
],
webServer: {
command: `pnpm dev --port ${port}`,
port,
timeout: 120000, // 2 minutes
stdout: 'pipe',
stderr: 'pipe',
},
webServer: usePreviewUrl
? undefined
: {
command: `pnpm dev --port ${port}`,
port,
timeout: 120000, // 2 minutes
stdout: 'pipe',
stderr: 'pipe',
},
});