Compare commits
10 Commits
39aea34026
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
| 57319e6712 | |||
| a553cc6130 | |||
| 5fc02f8d9b | |||
| ef0725be58 | |||
| b457e94ccb | |||
| 4abe8183d8 | |||
| bb650a3ed9 | |||
| 9df7278d55 | |||
| a8da8753f1 | |||
| 0ea3296885 |
59
.claude/agents/playwright-test-generator.md
Normal file
59
.claude/agents/playwright-test-generator.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
name: playwright-test-generator
|
||||||
|
description: Use this agent when you need to create automated browser tests using Playwright. Examples: <example>Context: User wants to test a login flow on their web application. user: 'I need a test that logs into my app at localhost:3000 with username admin@test.com and password 123456, then verifies the dashboard page loads' assistant: 'I'll use the generator agent to create and validate this login test for you' <commentary> The user needs a specific browser automation test created, which is exactly what the generator agent is designed for. </commentary></example><example>Context: User has built a new checkout flow and wants to ensure it works correctly. user: 'Can you create a test that adds items to cart, proceeds to checkout, fills in payment details, and confirms the order?' assistant: 'I'll use the generator agent to build a comprehensive checkout flow test' <commentary> This is a complex user journey that needs to be automated and tested, perfect for the generator agent. </commentary></example>
|
||||||
|
tools: Glob, Grep, Read, mcp__playwright-test__browser_click, mcp__playwright-test__browser_drag, mcp__playwright-test__browser_evaluate, mcp__playwright-test__browser_file_upload, mcp__playwright-test__browser_handle_dialog, mcp__playwright-test__browser_hover, mcp__playwright-test__browser_navigate, mcp__playwright-test__browser_press_key, mcp__playwright-test__browser_select_option, mcp__playwright-test__browser_snapshot, mcp__playwright-test__browser_type, mcp__playwright-test__browser_verify_element_visible, mcp__playwright-test__browser_verify_list_visible, mcp__playwright-test__browser_verify_text_visible, mcp__playwright-test__browser_verify_value, mcp__playwright-test__browser_wait_for, mcp__playwright-test__generator_read_log, mcp__playwright-test__generator_setup_page, mcp__playwright-test__generator_write_test
|
||||||
|
model: sonnet
|
||||||
|
color: blue
|
||||||
|
---
|
||||||
|
|
||||||
|
You are a Playwright Test Generator, an expert in browser automation and end-to-end testing.
|
||||||
|
Your specialty is creating robust, reliable Playwright tests that accurately simulate user interactions and validate
|
||||||
|
application behavior.
|
||||||
|
|
||||||
|
# For each test you generate
|
||||||
|
- Obtain the test plan with all the steps and verification specification
|
||||||
|
- Run the `generator_setup_page` tool to set up page for the scenario
|
||||||
|
- For each step and verification in the scenario, do the following:
|
||||||
|
- Use Playwright tool to manually execute it in real-time.
|
||||||
|
- Use the step description as the intent for each Playwright tool call.
|
||||||
|
- Retrieve generator log via `generator_read_log`
|
||||||
|
- Immediately after reading the test log, invoke `generator_write_test` with the generated source code
|
||||||
|
- File should contain single test
|
||||||
|
- File name must be fs-friendly scenario name
|
||||||
|
- Test must be placed in a describe matching the top-level test plan item
|
||||||
|
- Test title must match the scenario name
|
||||||
|
- Includes a comment with the step text before each step execution. Do not duplicate comments if step requires
|
||||||
|
multiple actions.
|
||||||
|
- Always use best practices from the log when generating tests.
|
||||||
|
|
||||||
|
<example-generation>
|
||||||
|
For following plan:
|
||||||
|
|
||||||
|
```markdown file=specs/plan.md
|
||||||
|
### 1. Adding New Todos
|
||||||
|
**Seed:** `tests/seed.spec.ts`
|
||||||
|
|
||||||
|
#### 1.1 Add Valid Todo
|
||||||
|
**Steps:**
|
||||||
|
1. Click in the "What needs to be done?" input field
|
||||||
|
|
||||||
|
#### 1.2 Add Multiple Todos
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Following file is generated:
|
||||||
|
|
||||||
|
```ts file=add-valid-todo.spec.ts
|
||||||
|
// spec: specs/plan.md
|
||||||
|
// seed: tests/seed.spec.ts
|
||||||
|
|
||||||
|
test.describe('Adding New Todos', () => {
|
||||||
|
test('Add Valid Todo', async { page } => {
|
||||||
|
// 1. Click in the "What needs to be done?" input field
|
||||||
|
await page.click(...);
|
||||||
|
|
||||||
|
...
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
</example-generation>
|
||||||
45
.claude/agents/playwright-test-healer.md
Normal file
45
.claude/agents/playwright-test-healer.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
name: playwright-test-healer
|
||||||
|
description: Use this agent when you need to debug and fix failing Playwright tests. Examples: <example>Context: A developer has a failing Playwright test that needs to be debugged and fixed. user: 'The login test is failing, can you fix it?' assistant: 'I'll use the healer agent to debug and fix the failing login test.' <commentary> The user has identified a specific failing test that needs debugging and fixing, which is exactly what the healer agent is designed for. </commentary></example><example>Context: After running a test suite, several tests are reported as failing. user: 'Test user-registration.spec.ts is broken after the recent changes' assistant: 'Let me use the healer agent to investigate and fix the user-registration test.' <commentary> A specific test file is failing and needs debugging, which requires the systematic approach of the playwright-test-healer agent. </commentary></example>
|
||||||
|
tools: Glob, Grep, Read, Write, Edit, MultiEdit, mcp__playwright-test__browser_console_messages, mcp__playwright-test__browser_evaluate, mcp__playwright-test__browser_generate_locator, mcp__playwright-test__browser_network_requests, mcp__playwright-test__browser_snapshot, mcp__playwright-test__test_debug, mcp__playwright-test__test_list, mcp__playwright-test__test_run
|
||||||
|
model: sonnet
|
||||||
|
color: red
|
||||||
|
---
|
||||||
|
|
||||||
|
You are the Playwright Test Healer, an expert test automation engineer specializing in debugging and
|
||||||
|
resolving Playwright test failures. Your mission is to systematically identify, diagnose, and fix
|
||||||
|
broken Playwright tests using a methodical approach.
|
||||||
|
|
||||||
|
Your workflow:
|
||||||
|
1. **Initial Execution**: Run all tests using playwright_test_run_test tool to identify failing tests
|
||||||
|
2. **Debug failed tests**: For each failing test run playwright_test_debug_test.
|
||||||
|
3. **Error Investigation**: When the test pauses on errors, use available Playwright MCP tools to:
|
||||||
|
- Examine the error details
|
||||||
|
- Capture page snapshot to understand the context
|
||||||
|
- Analyze selectors, timing issues, or assertion failures
|
||||||
|
4. **Root Cause Analysis**: Determine the underlying cause of the failure by examining:
|
||||||
|
- Element selectors that may have changed
|
||||||
|
- Timing and synchronization issues
|
||||||
|
- Data dependencies or test environment problems
|
||||||
|
- Application changes that broke test assumptions
|
||||||
|
5. **Code Remediation**: Edit the test code to address identified issues, focusing on:
|
||||||
|
- Updating selectors to match current application state
|
||||||
|
- Fixing assertions and expected values
|
||||||
|
- Improving test reliability and maintainability
|
||||||
|
- For inherently dynamic data, utilize regular expressions to produce resilient locators
|
||||||
|
6. **Verification**: Restart the test after each fix to validate the changes
|
||||||
|
7. **Iteration**: Repeat the investigation and fixing process until the test passes cleanly
|
||||||
|
|
||||||
|
Key principles:
|
||||||
|
- Be systematic and thorough in your debugging approach
|
||||||
|
- Document your findings and reasoning for each fix
|
||||||
|
- Prefer robust, maintainable solutions over quick hacks
|
||||||
|
- Use Playwright best practices for reliable test automation
|
||||||
|
- If multiple errors exist, fix them one at a time and retest
|
||||||
|
- Provide clear explanations of what was broken and how you fixed it
|
||||||
|
- You will continue this process until the test runs successfully without any failures or errors.
|
||||||
|
- If the error persists and you have high level of confidence that the test is correct, mark this test as test.fixme()
|
||||||
|
so that it is skipped during the execution. Add a comment before the failing step explaining what is happening instead
|
||||||
|
of the expected behavior.
|
||||||
|
- Do not ask user questions, you are not interactive tool, do the most reasonable thing possible to pass the test.
|
||||||
|
- Never wait for networkidle or use other discouraged or deprecated apis
|
||||||
93
.claude/agents/playwright-test-planner.md
Normal file
93
.claude/agents/playwright-test-planner.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
---
|
||||||
|
name: playwright-test-planner
|
||||||
|
description: Use this agent when you need to create comprehensive test plan for a web application or website. Examples: <example>Context: User wants to test a new e-commerce checkout flow. user: 'I need test scenarios for our new checkout process at https://mystore.com/checkout' assistant: 'I'll use the planner agent to navigate to your checkout page and create comprehensive test scenarios.' <commentary> The user needs test planning for a specific web page, so use the planner agent to explore and create test scenarios. </commentary></example><example>Context: User has deployed a new feature and wants thorough testing coverage. user: 'Can you help me test our new user dashboard at https://app.example.com/dashboard?' assistant: 'I'll launch the planner agent to explore your dashboard and develop detailed test scenarios.' <commentary> This requires web exploration and test scenario creation, perfect for the planner agent. </commentary></example>
|
||||||
|
tools: Glob, Grep, Read, Write, mcp__playwright-test__browser_click, mcp__playwright-test__browser_close, mcp__playwright-test__browser_console_messages, mcp__playwright-test__browser_drag, mcp__playwright-test__browser_evaluate, mcp__playwright-test__browser_file_upload, mcp__playwright-test__browser_handle_dialog, mcp__playwright-test__browser_hover, mcp__playwright-test__browser_navigate, mcp__playwright-test__browser_navigate_back, mcp__playwright-test__browser_network_requests, mcp__playwright-test__browser_press_key, mcp__playwright-test__browser_select_option, mcp__playwright-test__browser_snapshot, mcp__playwright-test__browser_take_screenshot, mcp__playwright-test__browser_type, mcp__playwright-test__browser_wait_for, mcp__playwright-test__planner_setup_page
|
||||||
|
model: sonnet
|
||||||
|
color: green
|
||||||
|
---
|
||||||
|
|
||||||
|
You are an expert web test planner with extensive experience in quality assurance, user experience testing, and test
|
||||||
|
scenario design. Your expertise includes functional testing, edge case identification, and comprehensive test coverage
|
||||||
|
planning.
|
||||||
|
|
||||||
|
You will:
|
||||||
|
|
||||||
|
1. **Navigate and Explore**
|
||||||
|
- Invoke the `planner_setup_page` tool once to set up page before using any other tools
|
||||||
|
- Explore the browser snapshot
|
||||||
|
- Do not take screenshots unless absolutely necessary
|
||||||
|
- Use browser_* tools to navigate and discover interface
|
||||||
|
- Thoroughly explore the interface, identifying all interactive elements, forms, navigation paths, and functionality
|
||||||
|
|
||||||
|
2. **Analyze User Flows**
|
||||||
|
- Map out the primary user journeys and identify critical paths through the application
|
||||||
|
- Consider different user types and their typical behaviors
|
||||||
|
|
||||||
|
3. **Design Comprehensive Scenarios**
|
||||||
|
|
||||||
|
Create detailed test scenarios that cover:
|
||||||
|
- Happy path scenarios (normal user behavior)
|
||||||
|
- Edge cases and boundary conditions
|
||||||
|
- Error handling and validation
|
||||||
|
|
||||||
|
4. **Structure Test Plans**
|
||||||
|
|
||||||
|
Each scenario must include:
|
||||||
|
- Clear, descriptive title
|
||||||
|
- Detailed step-by-step instructions
|
||||||
|
- Expected outcomes where appropriate
|
||||||
|
- Assumptions about starting state (always assume blank/fresh state)
|
||||||
|
- Success criteria and failure conditions
|
||||||
|
|
||||||
|
5. **Create Documentation**
|
||||||
|
|
||||||
|
Save your test plan as requested:
|
||||||
|
- Executive summary of the tested page/application
|
||||||
|
- Individual scenarios as separate sections
|
||||||
|
- Each scenario formatted with numbered steps
|
||||||
|
- Clear expected results for verification
|
||||||
|
|
||||||
|
<example-spec>
|
||||||
|
# TodoMVC Application - Comprehensive Test Plan
|
||||||
|
|
||||||
|
## Application Overview
|
||||||
|
|
||||||
|
The TodoMVC application is a React-based todo list manager that provides core task management functionality. The
|
||||||
|
application features:
|
||||||
|
|
||||||
|
- **Task Management**: Add, edit, complete, and delete individual todos
|
||||||
|
- **Bulk Operations**: Mark all todos as complete/incomplete and clear all completed todos
|
||||||
|
- **Filtering**: View todos by All, Active, or Completed status
|
||||||
|
- **URL Routing**: Support for direct navigation to filtered views via URLs
|
||||||
|
- **Counter Display**: Real-time count of active (incomplete) todos
|
||||||
|
- **Persistence**: State maintained during session (browser refresh behavior not tested)
|
||||||
|
|
||||||
|
## Test Scenarios
|
||||||
|
|
||||||
|
### 1. Adding New Todos
|
||||||
|
|
||||||
|
**Seed:** `tests/seed.spec.ts`
|
||||||
|
|
||||||
|
#### 1.1 Add Valid Todo
|
||||||
|
**Steps:**
|
||||||
|
1. Click in the "What needs to be done?" input field
|
||||||
|
2. Type "Buy groceries"
|
||||||
|
3. Press Enter key
|
||||||
|
|
||||||
|
**Expected Results:**
|
||||||
|
- Todo appears in the list with unchecked checkbox
|
||||||
|
- Counter shows "1 item left"
|
||||||
|
- Input field is cleared and ready for next entry
|
||||||
|
- Todo list controls become visible (Mark all as complete checkbox)
|
||||||
|
|
||||||
|
#### 1.2
|
||||||
|
...
|
||||||
|
</example-spec>
|
||||||
|
|
||||||
|
**Quality Standards**:
|
||||||
|
- Write steps that are specific enough for any tester to follow
|
||||||
|
- Include negative testing scenarios
|
||||||
|
- Ensure scenarios are independent and can be run in any order
|
||||||
|
|
||||||
|
**Output Format**: Always save the complete test plan as a markdown file with clear headings, numbered steps, and
|
||||||
|
professional formatting suitable for sharing with development and QA teams.
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
# Gitea Actions workflow for running Magnitude tests
|
# Gitea Actions workflow for running Magnitude tests
|
||||||
|
# Uses docker-compose.ci.yml for fully containerized testing
|
||||||
name: Magnitude Tests
|
name: Magnitude Tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -15,56 +16,39 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Create .env file for CI
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
run: npm install -g pnpm
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Start SurrealDB
|
|
||||||
run: |
|
run: |
|
||||||
docker run -d \
|
cat > .env << EOF
|
||||||
--name surrealdb \
|
SURREALDB_URL=ws://surrealdb:8000/rpc
|
||||||
-p 8000:8000 \
|
SURREALDB_USER=root
|
||||||
-e SURREAL_USER=${{ secrets.SURREALDB_USER }} \
|
SURREALDB_PASS=root
|
||||||
-e SURREAL_PASS=${{ secrets.SURREALDB_PASS }} \
|
SURREALDB_NS=ponderants
|
||||||
surrealdb/surrealdb:latest \
|
SURREALDB_DB=main
|
||||||
start --log trace --user ${{ secrets.SURREALDB_USER }} --pass ${{ secrets.SURREALDB_PASS }} memory
|
SURREALDB_JWT_SECRET=${{ secrets.SURREALDB_JWT_SECRET }}
|
||||||
|
ATPROTO_CLIENT_ID=${{ secrets.ATPROTO_CLIENT_ID }}
|
||||||
|
ATPROTO_REDIRECT_URI=${{ secrets.ATPROTO_REDIRECT_URI }}
|
||||||
|
GOOGLE_GENERATIVE_AI_API_KEY=${{ secrets.GOOGLE_GENERATIVE_AI_API_KEY }}
|
||||||
|
DEEPGRAM_API_KEY=${{ secrets.DEEPGRAM_API_KEY }}
|
||||||
|
TEST_BLUESKY_HANDLE=${{ secrets.TEST_BLUESKY_HANDLE }}
|
||||||
|
TEST_BLUESKY_PASSWORD=${{ secrets.TEST_BLUESKY_PASSWORD }}
|
||||||
|
ANTHROPIC_API_KEY=${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
EOF
|
||||||
|
|
||||||
- name: Wait for SurrealDB
|
- name: Run tests with docker-compose
|
||||||
run: sleep 5
|
run: |
|
||||||
|
docker compose -f docker-compose.ci.yml --profile test up \
|
||||||
|
--abort-on-container-exit \
|
||||||
|
--exit-code-from magnitude
|
||||||
|
|
||||||
- name: Start Next.js dev server
|
- name: Show logs on failure
|
||||||
run: pnpm dev &
|
if: failure()
|
||||||
env:
|
run: |
|
||||||
SURREALDB_URL: ws://localhost:8000/rpc
|
echo "=== SurrealDB Logs ==="
|
||||||
SURREALDB_USER: ${{ secrets.SURREALDB_USER }}
|
docker compose -f docker-compose.ci.yml logs surrealdb
|
||||||
SURREALDB_PASS: ${{ secrets.SURREALDB_PASS }}
|
echo "=== Next.js Logs ==="
|
||||||
SURREALDB_NS: ${{ secrets.SURREALDB_NS }}
|
docker compose -f docker-compose.ci.yml logs nextjs
|
||||||
SURREALDB_DB: ${{ secrets.SURREALDB_DB }}
|
echo "=== Magnitude Logs ==="
|
||||||
ATPROTO_CLIENT_ID: ${{ secrets.ATPROTO_CLIENT_ID }}
|
docker compose -f docker-compose.ci.yml logs magnitude
|
||||||
ATPROTO_REDIRECT_URI: ${{ secrets.ATPROTO_REDIRECT_URI }}
|
|
||||||
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
|
||||||
DEEPGRAM_API_KEY: ${{ secrets.DEEPGRAM_API_KEY }}
|
|
||||||
SURREAL_JWT_SECRET: ${{ secrets.SURREAL_JWT_SECRET }}
|
|
||||||
TEST_BLUESKY_HANDLE: ${{ secrets.TEST_BLUESKY_HANDLE }}
|
|
||||||
TEST_BLUESKY_PASSWORD: ${{ secrets.TEST_BLUESKY_PASSWORD }}
|
|
||||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
||||||
|
|
||||||
- name: Wait for Next.js server
|
|
||||||
run: npx wait-on http://localhost:3000 --timeout 120000
|
|
||||||
|
|
||||||
- name: Run Magnitude tests
|
|
||||||
run: npx magnitude
|
|
||||||
env:
|
|
||||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
||||||
TEST_BLUESKY_HANDLE: ${{ secrets.TEST_BLUESKY_HANDLE }}
|
|
||||||
TEST_BLUESKY_PASSWORD: ${{ secrets.TEST_BLUESKY_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Upload test results
|
- name: Upload test results
|
||||||
if: always()
|
if: always()
|
||||||
@@ -73,3 +57,7 @@ jobs:
|
|||||||
name: magnitude-results
|
name: magnitude-results
|
||||||
path: test-results/
|
path: test-results/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: docker compose -f docker-compose.ci.yml down -v
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -4,6 +4,7 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
/.pnp
|
/.pnp
|
||||||
.pnp.js
|
.pnp.js
|
||||||
|
.pnpm-store/
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
@@ -46,3 +47,6 @@ tests/playwright/.auth/
|
|||||||
|
|
||||||
# claude settings (keep .claude/CLAUDE.md but ignore user settings)
|
# claude settings (keep .claude/CLAUDE.md but ignore user settings)
|
||||||
.claude/settings.local.json
|
.claude/settings.local.json
|
||||||
|
|
||||||
|
# surrealdb data
|
||||||
|
surreal/data/
|
||||||
|
|||||||
11
.mcp.json
Normal file
11
.mcp.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"playwright-test": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"playwright",
|
||||||
|
"run-test-mcp-server"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
AGENTS.md
43
AGENTS.md
@@ -160,28 +160,51 @@ Playwright is integrated into docker-compose for consistent testing environments
|
|||||||
|
|
||||||
**CI/CD with Gitea Actions**:
|
**CI/CD with Gitea Actions**:
|
||||||
|
|
||||||
Magnitude tests run automatically on every push and pull request via Gitea Actions:
|
Magnitude tests run automatically on every push and pull request using a fully containerized setup:
|
||||||
|
|
||||||
1. **Configuration**: `.gitea/workflows/magnitude.yml`
|
1. **Configuration**: `.gitea/workflows/magnitude.yml`
|
||||||
|
|
||||||
2. **Workflow steps**:
|
2. **Workflow steps** (simplified to just 2 steps!):
|
||||||
- Checkout code
|
- Create `.env` file with secrets
|
||||||
- Setup Node.js and pnpm
|
- Run `docker compose -f docker-compose.ci.yml --profile test up`
|
||||||
- Start SurrealDB in Docker
|
- Upload test results and show logs on failure
|
||||||
- Start Next.js dev server with environment variables
|
- Cleanup
|
||||||
- Run Magnitude tests
|
|
||||||
- Upload test results as artifacts
|
|
||||||
|
|
||||||
3. **Required Secrets** (configure in Gitea repository settings):
|
3. **Required Secrets** (configure in Gitea repository settings):
|
||||||
- `ANTHROPIC_API_KEY` - For Magnitude AI vision testing
|
- `ANTHROPIC_API_KEY` - For Magnitude AI vision testing
|
||||||
- `TEST_BLUESKY_HANDLE` - Test account handle
|
- `TEST_BLUESKY_HANDLE` - Test account handle
|
||||||
- `TEST_BLUESKY_PASSWORD` - Test account password
|
- `TEST_BLUESKY_PASSWORD` - Test account password
|
||||||
- `SURREALDB_USER`, `SURREALDB_PASS`, `SURREALDB_NS`, `SURREALDB_DB`
|
|
||||||
- `ATPROTO_CLIENT_ID`, `ATPROTO_REDIRECT_URI`
|
- `ATPROTO_CLIENT_ID`, `ATPROTO_REDIRECT_URI`
|
||||||
- `GOOGLE_API_KEY`, `DEEPGRAM_API_KEY`
|
- `GOOGLE_API_KEY`, `DEEPGRAM_API_KEY`
|
||||||
- `SURREAL_JWT_SECRET`
|
- `SURREAL_JWT_SECRET`
|
||||||
|
|
||||||
4. **Test results**: Available as workflow artifacts for 30 days
|
4. **CI-specific docker-compose**: `docker-compose.ci.yml`
|
||||||
|
- Fully containerized (SurrealDB + Next.js + Magnitude)
|
||||||
|
- Excludes surrealmcp (only needed for local MCP development)
|
||||||
|
- Health checks ensure services are ready before tests run
|
||||||
|
- Uses in-memory SurrealDB for speed
|
||||||
|
- Services dependency chain: magnitude → nextjs → surrealdb
|
||||||
|
|
||||||
|
5. **Debugging CI failures locally**:
|
||||||
|
```bash
|
||||||
|
# Runs the EXACT same docker-compose setup as CI
|
||||||
|
./scripts/test-ci-locally.sh
|
||||||
|
|
||||||
|
# Or manually:
|
||||||
|
docker compose -f docker-compose.ci.yml --profile test up \
|
||||||
|
--abort-on-container-exit \
|
||||||
|
--exit-code-from magnitude
|
||||||
|
```
|
||||||
|
Since CI just runs docker-compose, you can reproduce failures **exactly** without any differences between local and CI environments!
|
||||||
|
|
||||||
|
6. **Test results**: Available as workflow artifacts for 30 days
|
||||||
|
|
||||||
|
7. **Why this approach is better**:
|
||||||
|
- ✅ Identical local and CI environments (both use same docker-compose.ci.yml)
|
||||||
|
- ✅ Fast debugging (no push-test-fail cycles)
|
||||||
|
- ✅ Self-contained (all dependencies in containers)
|
||||||
|
- ✅ Simple (just 2 steps in CI workflow)
|
||||||
|
- ✅ Reproducible (docker-compose ensures consistency)
|
||||||
|
|
||||||
**Testing Framework Separation**:
|
**Testing Framework Separation**:
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
# Dockerfile for Playwright testing environment
|
|
||||||
# Based on official Playwright Docker image with non-root user setup
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/playwright:v1.49.1-noble
|
|
||||||
|
|
||||||
# Create a non-root user for running tests
|
|
||||||
RUN useradd -ms /bin/bash pwuser && \
|
|
||||||
mkdir -p /home/pwuser/app && \
|
|
||||||
chown -R pwuser:pwuser /home/pwuser
|
|
||||||
|
|
||||||
# Switch to non-root user
|
|
||||||
USER pwuser
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /home/pwuser/app
|
|
||||||
|
|
||||||
# Copy package files
|
|
||||||
COPY --chown=pwuser:pwuser package.json pnpm-lock.yaml ./
|
|
||||||
|
|
||||||
# Install pnpm globally for the user
|
|
||||||
RUN npm install -g pnpm
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
RUN pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
# Copy the rest of the application
|
|
||||||
COPY --chown=pwuser:pwuser . .
|
|
||||||
|
|
||||||
# Run Playwright tests
|
|
||||||
CMD ["pnpm", "exec", "playwright", "test"]
|
|
||||||
54
debug-db.mjs
Normal file
54
debug-db.mjs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import Surreal from 'surrealdb';
|
||||||
|
|
||||||
|
const USER_DID = 'did:plc:sypdx6a4u2fblmclv6wbxjl3';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const db = new Surreal();
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Connecting to SurrealDB...');
|
||||||
|
await db.connect('ws://localhost:8000/rpc');
|
||||||
|
|
||||||
|
console.log('Signing in...');
|
||||||
|
await db.signin({
|
||||||
|
username: 'root',
|
||||||
|
password: 'root',
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Using namespace/database...');
|
||||||
|
await db.use({
|
||||||
|
namespace: 'ponderants',
|
||||||
|
database: 'main',
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('\n===== ALL NODES IN DATABASE =====');
|
||||||
|
const allNodes = await db.query('SELECT * FROM node LIMIT 20');
|
||||||
|
console.log('Total nodes:', allNodes[0]?.length || 0);
|
||||||
|
console.log('Nodes:', JSON.stringify(allNodes[0], null, 2));
|
||||||
|
|
||||||
|
console.log(`\n===== NODES FOR USER ${USER_DID} (WITHOUT coords_3d filter) =====`);
|
||||||
|
const userNodesNoFilter = await db.query(
|
||||||
|
'SELECT id, title, user_did, coords_3d FROM node WHERE user_did = $userDid',
|
||||||
|
{ userDid: USER_DID }
|
||||||
|
);
|
||||||
|
console.log('Count:', userNodesNoFilter[0]?.length || 0);
|
||||||
|
console.log('Nodes:', JSON.stringify(userNodesNoFilter[0], null, 2));
|
||||||
|
|
||||||
|
console.log(`\n===== NODES FOR USER ${USER_DID} (WITH coords_3d != NONE filter) =====`);
|
||||||
|
const userNodesWithFilter = await db.query(
|
||||||
|
'SELECT id, title, user_did, coords_3d FROM node WHERE user_did = $userDid AND coords_3d != NONE',
|
||||||
|
{ userDid: USER_DID }
|
||||||
|
);
|
||||||
|
console.log('Count:', userNodesWithFilter[0]?.length || 0);
|
||||||
|
console.log('Nodes:', JSON.stringify(userNodesWithFilter[0], null, 2));
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
console.error('Stack:', error.stack);
|
||||||
|
} finally {
|
||||||
|
await db.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
89
docker-compose.ci.yml
Normal file
89
docker-compose.ci.yml
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# Simplified docker-compose for CI/CD environments
|
||||||
|
# Only includes services needed for testing (excludes surrealmcp)
|
||||||
|
|
||||||
|
services:
|
||||||
|
surrealdb:
|
||||||
|
image: surrealdb/surrealdb:latest
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
command:
|
||||||
|
- start
|
||||||
|
- --log
|
||||||
|
- trace
|
||||||
|
- --user
|
||||||
|
- ${SURREALDB_USER:-root}
|
||||||
|
- --pass
|
||||||
|
- ${SURREALDB_PASS:-root}
|
||||||
|
- memory
|
||||||
|
environment:
|
||||||
|
- SURREAL_LOG=trace
|
||||||
|
|
||||||
|
nextjs:
|
||||||
|
image: node:20-alpine
|
||||||
|
working_dir: /app
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
- /app/node_modules
|
||||||
|
- /app/.next
|
||||||
|
environment:
|
||||||
|
- SURREALDB_URL=ws://surrealdb:8000/rpc
|
||||||
|
- SURREALDB_USER=${SURREALDB_USER:-root}
|
||||||
|
- SURREALDB_PASS=${SURREALDB_PASS:-root}
|
||||||
|
- SURREALDB_NS=${SURREALDB_NS:-ponderants}
|
||||||
|
- SURREALDB_DB=${SURREALDB_DB:-main}
|
||||||
|
- SURREALDB_JWT_SECRET=${SURREALDB_JWT_SECRET}
|
||||||
|
- ATPROTO_CLIENT_ID=${ATPROTO_CLIENT_ID}
|
||||||
|
- ATPROTO_REDIRECT_URI=${ATPROTO_REDIRECT_URI}
|
||||||
|
- GOOGLE_GENERATIVE_AI_API_KEY=${GOOGLE_GENERATIVE_AI_API_KEY}
|
||||||
|
- DEEPGRAM_API_KEY=${DEEPGRAM_API_KEY}
|
||||||
|
- TEST_BLUESKY_HANDLE=${TEST_BLUESKY_HANDLE}
|
||||||
|
- TEST_BLUESKY_PASSWORD=${TEST_BLUESKY_PASSWORD}
|
||||||
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
||||||
|
- NODE_ENV=development
|
||||||
|
command: >
|
||||||
|
sh -c "
|
||||||
|
npm install -g pnpm &&
|
||||||
|
pnpm install --frozen-lockfile &&
|
||||||
|
echo 'Waiting for SurrealDB to be ready...' &&
|
||||||
|
sleep 10 &&
|
||||||
|
pnpm dev
|
||||||
|
"
|
||||||
|
depends_on:
|
||||||
|
- surrealdb
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 20
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
magnitude:
|
||||||
|
image: mcr.microsoft.com/playwright:v1.56.1-noble
|
||||||
|
working_dir: /app
|
||||||
|
user: root
|
||||||
|
network_mode: "service:nextjs"
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
- node_modules:/app/node_modules
|
||||||
|
environment:
|
||||||
|
- TEST_BLUESKY_HANDLE=${TEST_BLUESKY_HANDLE}
|
||||||
|
- TEST_BLUESKY_PASSWORD=${TEST_BLUESKY_PASSWORD}
|
||||||
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
||||||
|
- HOME=/root
|
||||||
|
command: >
|
||||||
|
sh -c "
|
||||||
|
npm install -g pnpm &&
|
||||||
|
pnpm install --frozen-lockfile &&
|
||||||
|
npx wait-on http://localhost:3000 --timeout 120000 &&
|
||||||
|
xvfb-run --auto-servernum --server-args='-screen 0 1280x960x24' npx magnitude
|
||||||
|
"
|
||||||
|
depends_on:
|
||||||
|
nextjs:
|
||||||
|
condition: service_healthy
|
||||||
|
profiles:
|
||||||
|
- test
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
node_modules:
|
||||||
@@ -34,7 +34,7 @@ services:
|
|||||||
- surrealdb
|
- surrealdb
|
||||||
|
|
||||||
playwright:
|
playwright:
|
||||||
image: mcr.microsoft.com/playwright:v1.49.1-noble
|
image: mcr.microsoft.com/playwright:v1.56.1-noble
|
||||||
working_dir: /home/pwuser/app
|
working_dir: /home/pwuser/app
|
||||||
user: pwuser
|
user: pwuser
|
||||||
network_mode: host
|
network_mode: host
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ export default {
|
|||||||
// Run tests in headless mode to avoid window focus issues
|
// Run tests in headless mode to avoid window focus issues
|
||||||
headless: true,
|
headless: true,
|
||||||
// Use Claude Sonnet 4.5 for best performance
|
// Use Claude Sonnet 4.5 for best performance
|
||||||
model: 'anthropic:claude-sonnet-4-5-20250514',
|
model: 'anthropic:claude-sonnet-4-5-20250929',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
"@types/react": "latest",
|
"@types/react": "latest",
|
||||||
"@types/react-dom": "latest",
|
"@types/react-dom": "latest",
|
||||||
"@types/three": "^0.181.0",
|
"@types/three": "^0.181.0",
|
||||||
|
"dotenv": "^17.2.3",
|
||||||
"eslint": "latest",
|
"eslint": "latest",
|
||||||
"eslint-config-next": "latest",
|
"eslint-config-next": "latest",
|
||||||
"jiti": "^2.6.1",
|
"jiti": "^2.6.1",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export default defineConfig({
|
|||||||
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:3000',
|
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:3000',
|
||||||
trace: 'on-first-retry',
|
trace: 'on-first-retry',
|
||||||
screenshot: 'only-on-failure',
|
screenshot: 'only-on-failure',
|
||||||
|
headless: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
projects: [
|
projects: [
|
||||||
|
|||||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -105,6 +105,9 @@ importers:
|
|||||||
'@types/three':
|
'@types/three':
|
||||||
specifier: ^0.181.0
|
specifier: ^0.181.0
|
||||||
version: 0.181.0
|
version: 0.181.0
|
||||||
|
dotenv:
|
||||||
|
specifier: ^17.2.3
|
||||||
|
version: 17.2.3
|
||||||
eslint:
|
eslint:
|
||||||
specifier: latest
|
specifier: latest
|
||||||
version: 9.39.1(jiti@2.6.1)
|
version: 9.39.1(jiti@2.6.1)
|
||||||
@@ -1710,6 +1713,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
|
resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
dotenv@17.2.3:
|
||||||
|
resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
draco3d@1.5.7:
|
draco3d@1.5.7:
|
||||||
resolution: {integrity: sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==}
|
resolution: {integrity: sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==}
|
||||||
|
|
||||||
@@ -5034,6 +5041,8 @@ snapshots:
|
|||||||
|
|
||||||
dotenv@16.6.1: {}
|
dotenv@16.6.1: {}
|
||||||
|
|
||||||
|
dotenv@17.2.3: {}
|
||||||
|
|
||||||
draco3d@1.5.7: {}
|
draco3d@1.5.7: {}
|
||||||
|
|
||||||
dunder-proto@1.0.1:
|
dunder-proto@1.0.1:
|
||||||
|
|||||||
85
scripts/README.md
Normal file
85
scripts/README.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# Development Scripts
|
||||||
|
|
||||||
|
## test-ci-locally.sh
|
||||||
|
|
||||||
|
Tests the CI workflow locally by running the **exact same docker-compose command** that the Gitea Actions workflow runs.
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
|
||||||
|
When CI tests fail, this script reproduces the exact CI environment locally to debug issues without repeatedly pushing to trigger CI runs. It runs `docker-compose.ci.yml` with the same parameters as the CI workflow, so you're testing in an identical environment.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/test-ci-locally.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Or run docker-compose directly (this is what the script does):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.ci.yml --profile test up \
|
||||||
|
--abort-on-container-exit \
|
||||||
|
--exit-code-from magnitude
|
||||||
|
```
|
||||||
|
|
||||||
|
### What it does
|
||||||
|
|
||||||
|
1. Checks that `.env` file exists
|
||||||
|
2. Runs `docker compose -f docker-compose.ci.yml --profile test up`
|
||||||
|
3. This starts all services:
|
||||||
|
- **surrealdb**: In-memory database with health check
|
||||||
|
- **nextjs**: Node.js container running `pnpm dev` with health check
|
||||||
|
- **magnitude**: Playwright container running the test suite
|
||||||
|
4. Waits for tests to complete
|
||||||
|
5. Exits with magnitude's exit code
|
||||||
|
6. Shows service logs on failure
|
||||||
|
7. Cleans up containers and volumes
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- Docker and docker-compose installed
|
||||||
|
- `.env` file with test credentials
|
||||||
|
|
||||||
|
### Services Architecture
|
||||||
|
|
||||||
|
The script starts a containerized test environment with proper health checks and dependencies:
|
||||||
|
|
||||||
|
```
|
||||||
|
magnitude (Playwright container - runs tests)
|
||||||
|
↓ depends on (waits for health check)
|
||||||
|
nextjs (Node.js container - runs pnpm dev)
|
||||||
|
↓ depends on (waits for health check)
|
||||||
|
surrealdb (SurrealDB container - in-memory mode)
|
||||||
|
```
|
||||||
|
|
||||||
|
All services share the same network:
|
||||||
|
- Next.js accesses SurrealDB via `ws://surrealdb:8000/rpc`
|
||||||
|
- Magnitude accesses Next.js via `http://localhost:3000`
|
||||||
|
|
||||||
|
### Why This Approach?
|
||||||
|
|
||||||
|
This is simpler and more accurate than using workflow runner tools like `act` or `act_runner` because:
|
||||||
|
|
||||||
|
1. **Identical to CI**: The CI workflow (`.gitea/workflows/magnitude.yml`) literally runs this docker-compose command, so you're testing the exact same thing
|
||||||
|
2. **No Additional Tools**: Doesn't require `act`, `act_runner`, or any workflow execution tools
|
||||||
|
3. **Direct Debugging**: Runs the actual test commands directly, making it easier to see what's happening
|
||||||
|
4. **Faster**: No overhead from workflow interpretation or runner setup
|
||||||
|
|
||||||
|
### Debugging CI Failures
|
||||||
|
|
||||||
|
If Gitea Actions fail:
|
||||||
|
|
||||||
|
1. Check the workflow logs for errors in Gitea UI
|
||||||
|
2. Run `./scripts/test-ci-locally.sh` to reproduce **exactly**
|
||||||
|
3. The script will show the same output as CI
|
||||||
|
4. Debug with docker-compose logs if needed:
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.ci.yml logs surrealdb
|
||||||
|
docker compose -f docker-compose.ci.yml logs nextjs
|
||||||
|
docker compose -f docker-compose.ci.yml logs magnitude
|
||||||
|
```
|
||||||
|
5. Fix issues locally
|
||||||
|
6. Run script again to verify fix
|
||||||
|
7. Commit and push once tests pass locally
|
||||||
|
|
||||||
|
This is **much** faster than debugging via CI push cycles and gives you identical results!
|
||||||
62
scripts/test-ci-locally.sh
Executable file
62
scripts/test-ci-locally.sh
Executable file
@@ -0,0 +1,62 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Script to test CI workflow locally by running the exact same docker-compose command as CI
|
||||||
|
# This runs docker-compose.ci.yml which is what the Gitea Actions workflow uses
|
||||||
|
|
||||||
|
set -e # Exit on error
|
||||||
|
|
||||||
|
echo "========================================="
|
||||||
|
echo "Testing CI Workflow Locally"
|
||||||
|
echo "========================================="
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Check if .env exists
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
echo -e "${RED}Error: .env file not found!${NC}"
|
||||||
|
echo "Please create .env file with required variables"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}Running the exact same docker-compose command as CI${NC}"
|
||||||
|
echo -e "${YELLOW}This executes: docker compose -f docker-compose.ci.yml --profile test up${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Cleanup function
|
||||||
|
cleanup() {
|
||||||
|
echo -e "${YELLOW}Cleaning up containers and volumes...${NC}"
|
||||||
|
docker compose -f docker-compose.ci.yml down -v
|
||||||
|
}
|
||||||
|
|
||||||
|
# Trap cleanup on exit
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# Run the exact same command that CI runs
|
||||||
|
docker compose -f docker-compose.ci.yml --profile test up \
|
||||||
|
--abort-on-container-exit \
|
||||||
|
--exit-code-from magnitude || {
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}=========================================${NC}"
|
||||||
|
echo -e "${RED}Tests failed!${NC}"
|
||||||
|
echo -e "${RED}=========================================${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Showing service logs:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "=== SurrealDB Logs ==="
|
||||||
|
docker compose -f docker-compose.ci.yml logs --tail=50 surrealdb
|
||||||
|
echo ""
|
||||||
|
echo "=== Next.js Logs ==="
|
||||||
|
docker compose -f docker-compose.ci.yml logs --tail=50 nextjs
|
||||||
|
echo ""
|
||||||
|
echo "=== Magnitude Logs ==="
|
||||||
|
docker compose -f docker-compose.ci.yml logs --tail=50 magnitude
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}=========================================${NC}"
|
||||||
|
echo -e "${GREEN}All tests passed!${NC}"
|
||||||
|
echo -e "${GREEN}=========================================${NC}"
|
||||||
@@ -2,7 +2,7 @@ import { test } from 'magnitude-test';
|
|||||||
|
|
||||||
test('[Happy Path] User can have a full voice conversation with AI', async (agent) => {
|
test('[Happy Path] User can have a full voice conversation with AI', async (agent) => {
|
||||||
// Act: Navigate to chat page (assumes user is already authenticated)
|
// Act: Navigate to chat page (assumes user is already authenticated)
|
||||||
await agent.open('http://localhost:3000/chat');
|
await agent.act('Navigate to http://localhost:3000/chat');
|
||||||
|
|
||||||
// Check: Initial state - voice button shows "Start Voice Conversation"
|
// Check: Initial state - voice button shows "Start Voice Conversation"
|
||||||
await agent.check('A button with text "Start Voice Conversation" is visible');
|
await agent.check('A button with text "Start Voice Conversation" is visible');
|
||||||
@@ -76,7 +76,7 @@ test('[Happy Path] User can have a full voice conversation with AI', async (agen
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('[Unhappy Path] Voice mode handles errors gracefully', async (agent) => {
|
test('[Unhappy Path] Voice mode handles errors gracefully', async (agent) => {
|
||||||
await agent.open('http://localhost:3000/chat');
|
await agent.act('Navigate to http://localhost:3000/chat');
|
||||||
|
|
||||||
// Act: Start voice mode
|
// Act: Start voice mode
|
||||||
await agent.act('Click the "Start Voice Conversation" button');
|
await agent.act('Click the "Start Voice Conversation" button');
|
||||||
@@ -93,7 +93,7 @@ test('[Unhappy Path] Voice mode handles errors gracefully', async (agent) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('[Happy Path] Text input is disabled during voice mode', async (agent) => {
|
test('[Happy Path] Text input is disabled during voice mode', async (agent) => {
|
||||||
await agent.open('http://localhost:3000/chat');
|
await agent.act('Navigate to http://localhost:3000/chat');
|
||||||
|
|
||||||
// Check: Text input is enabled initially
|
// Check: Text input is enabled initially
|
||||||
await agent.check('The text input field "Or type your thoughts here..." is enabled');
|
await agent.check('The text input field "Or type your thoughts here..." is enabled');
|
||||||
@@ -112,7 +112,7 @@ test('[Happy Path] Text input is disabled during voice mode', async (agent) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('[Happy Path] User can type a message while voice mode is idle', async (agent) => {
|
test('[Happy Path] User can type a message while voice mode is idle', async (agent) => {
|
||||||
await agent.open('http://localhost:3000/chat');
|
await agent.act('Navigate to http://localhost:3000/chat');
|
||||||
|
|
||||||
// Act: Type a message in the text input
|
// Act: Type a message in the text input
|
||||||
await agent.act('Type "This is a text message" into the text input field');
|
await agent.act('Type "This is a text message" into the text input field');
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import { test } from 'magnitude-test';
|
import { test } from 'magnitude-test';
|
||||||
|
|
||||||
test('Node publishes successfully with cache (no warnings)', async (agent) => {
|
test('Node publishes successfully with cache (no warnings)', async (agent) => {
|
||||||
await agent.open('http://localhost:3000');
|
await agent.act('Navigate to http://localhost:3000');
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
await agent.act('Click the "Log in with Bluesky" button');
|
await agent.act('Click the "Log in with Bluesky" button');
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { test } from 'magnitude-test';
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
test('User can publish a node from conversation', async (agent) => {
|
test('User can publish a node from conversation', async (agent) => {
|
||||||
await agent.open('http://localhost:3000');
|
await agent.act('Navigate to http://localhost:3000');
|
||||||
|
|
||||||
// Step 1: Login with Bluesky
|
// Step 1: Login with Bluesky
|
||||||
await agent.act('Click the "Log in with Bluesky" button');
|
await agent.act('Click the "Log in with Bluesky" button');
|
||||||
@@ -48,7 +48,7 @@ test('User can publish a node from conversation', async (agent) => {
|
|||||||
|
|
||||||
test('User can edit node draft before publishing', async (agent) => {
|
test('User can edit node draft before publishing', async (agent) => {
|
||||||
// Assumes user is already logged in from previous test
|
// Assumes user is already logged in from previous test
|
||||||
await agent.open('http://localhost:3000/chat');
|
await agent.act('Navigate to http://localhost:3000/chat');
|
||||||
|
|
||||||
// Start conversation
|
// Start conversation
|
||||||
await agent.act('Type "Testing the edit flow" and press Enter');
|
await agent.act('Type "Testing the edit flow" and press Enter');
|
||||||
@@ -71,7 +71,7 @@ test('User can edit node draft before publishing', async (agent) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('User can cancel node draft without publishing', async (agent) => {
|
test('User can cancel node draft without publishing', async (agent) => {
|
||||||
await agent.open('http://localhost:3000/chat');
|
await agent.act('Navigate to http://localhost:3000/chat');
|
||||||
|
|
||||||
// Start conversation
|
// Start conversation
|
||||||
await agent.act('Type "Test cancellation" and press Enter');
|
await agent.act('Type "Test cancellation" and press Enter');
|
||||||
@@ -93,7 +93,7 @@ test('User can cancel node draft without publishing', async (agent) => {
|
|||||||
|
|
||||||
test('Cannot publish node without authentication', async (agent) => {
|
test('Cannot publish node without authentication', async (agent) => {
|
||||||
// Open edit page directly without being logged in
|
// Open edit page directly without being logged in
|
||||||
await agent.open('http://localhost:3000/edit');
|
await agent.act('Navigate to http://localhost:3000/edit');
|
||||||
|
|
||||||
await agent.check('Shows empty state message');
|
await agent.check('Shows empty state message');
|
||||||
await agent.check('Message says "No Node Draft"');
|
await agent.check('Message says "No Node Draft"');
|
||||||
@@ -101,7 +101,7 @@ test('Cannot publish node without authentication', async (agent) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Cannot publish node with empty title', async (agent) => {
|
test('Cannot publish node with empty title', async (agent) => {
|
||||||
await agent.open('http://localhost:3000/chat');
|
await agent.act('Navigate to http://localhost:3000/chat');
|
||||||
|
|
||||||
// Create draft
|
// Create draft
|
||||||
await agent.act('Type "Test empty title validation" and press Enter');
|
await agent.act('Type "Test empty title validation" and press Enter');
|
||||||
@@ -116,7 +116,7 @@ test('Cannot publish node with empty title', async (agent) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Cannot publish node with empty content', async (agent) => {
|
test('Cannot publish node with empty content', async (agent) => {
|
||||||
await agent.open('http://localhost:3000/chat');
|
await agent.act('Navigate to http://localhost:3000/chat');
|
||||||
|
|
||||||
// Create draft
|
// Create draft
|
||||||
await agent.act('Type "Test empty content validation" and press Enter');
|
await agent.act('Type "Test empty content validation" and press Enter');
|
||||||
@@ -131,7 +131,7 @@ test('Cannot publish node with empty content', async (agent) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Shows error notification if publish fails', async (agent) => {
|
test('Shows error notification if publish fails', async (agent) => {
|
||||||
await agent.open('http://localhost:3000/chat');
|
await agent.act('Navigate to http://localhost:3000/chat');
|
||||||
|
|
||||||
// Create draft
|
// Create draft
|
||||||
await agent.act('Type "Test error handling" and press Enter');
|
await agent.act('Type "Test error handling" and press Enter');
|
||||||
@@ -149,7 +149,7 @@ test('Shows error notification if publish fails', async (agent) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Handles long content with truncation', async (agent) => {
|
test('Handles long content with truncation', async (agent) => {
|
||||||
await agent.open('http://localhost:3000/chat');
|
await agent.act('Navigate to http://localhost:3000/chat');
|
||||||
|
|
||||||
// Create a very long message
|
// Create a very long message
|
||||||
const longMessage = 'A'.repeat(500) + ' This is a test of long content truncation for Bluesky posts.';
|
const longMessage = 'A'.repeat(500) + ' This is a test of long content truncation for Bluesky posts.';
|
||||||
@@ -168,7 +168,7 @@ test('Handles long content with truncation', async (agent) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Shows warning when cache fails but publish succeeds', async (agent) => {
|
test('Shows warning when cache fails but publish succeeds', async (agent) => {
|
||||||
await agent.open('http://localhost:3000/chat');
|
await agent.act('Navigate to http://localhost:3000/chat');
|
||||||
|
|
||||||
await agent.act('Type "Test cache failure graceful degradation" and press Enter');
|
await agent.act('Type "Test cache failure graceful degradation" and press Enter');
|
||||||
await agent.check('AI responds');
|
await agent.check('AI responds');
|
||||||
@@ -190,7 +190,7 @@ test('Shows warning when cache fails but publish succeeds', async (agent) => {
|
|||||||
|
|
||||||
test('Complete user journey: Login → Converse → Publish → View', async (agent) => {
|
test('Complete user journey: Login → Converse → Publish → View', async (agent) => {
|
||||||
// Full end-to-end test
|
// Full end-to-end test
|
||||||
await agent.open('http://localhost:3000');
|
await agent.act('Navigate to http://localhost:3000');
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
await agent.act('Login with Bluesky')
|
await agent.act('Login with Bluesky')
|
||||||
|
|||||||
7
tests/playwright/seed.spec.ts
Normal file
7
tests/playwright/seed.spec.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test.describe('Test group', () => {
|
||||||
|
test('seed', async ({ page }) => {
|
||||||
|
// generate code here.
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user