Files
app/tests/magnitude/03-delete-node.mag.ts
Albert a520814771 feat: Implement node deletion with shared modal and fix SurrealDB RecordId handling
Implements complete node deletion functionality for both galaxy view and debug panel:

**Core Changes:**
- Created shared DeleteNodeModal component used by both ThoughtGalaxy and UserMenu
- Modal provides consistent UX with proper confirmation messaging
- Deletion follows write-through cache pattern: ATproto first, then SurrealDB

**SurrealDB RecordId Fixes:**
- Fixed SELECT query to use type::thing($table, $recordId) for UUID-based RecordIds
- Fixed DELETE query to use type::thing() instead of db.delete() to handle dashes in UUIDs
- Without type::thing(), SurrealDB interprets dashes as subtraction operators

**Testing & Documentation:**
- Added comprehensive Magnitude tests for delete functionality (galaxy view and debug panel)
- Updated CLAUDE.md with complete testing workflow documentation
- Added pre-commit checklist requiring database verification and test execution
- Documented PlaywrightMCP manual testing process before Magnitude test writing

**Database Setup:**
- Configured docker-compose.yml to use environment variables for credentials
- Updated namespace/database to match .env configuration (ponderants/main)

**File Changes:**
- app/api/nodes/[id]/route.ts: Fixed RecordId query patterns (SELECT and DELETE)
- components/DeleteNodeModal.tsx: New shared modal component
- components/ThoughtGalaxy.tsx: Uses shared DeleteNodeModal
- components/UserMenu.tsx: Replaced browser confirm() with shared DeleteNodeModal
- tests/magnitude/03-delete-node.mag.ts: Added debug panel delete test
- AGENTS.md: Added testing workflow and pre-commit checklist documentation
- docker-compose.yml: Environment variable configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 13:25:01 +00:00

205 lines
9.2 KiB
TypeScript

import { test } from 'magnitude-test';
const TEST_HANDLE = process.env.TEST_BLUESKY_HANDLE;
const TEST_PASSWORD = process.env.TEST_BLUESKY_PASSWORD;
if (!TEST_HANDLE || !TEST_PASSWORD) {
throw new Error('TEST_BLUESKY_HANDLE and TEST_BLUESKY_PASSWORD must be set in .env');
}
test('User can delete their own node from galaxy view', async (agent) => {
// Act: Log in
await agent.act('Navigate to /login');
await agent.act(`Type "${TEST_HANDLE}" into the "Your Handle" input field`);
await agent.act('Click the "Log in with Bluesky" button');
await agent.check('The page URL contains "bsky.social"');
await agent.act(`Type "${TEST_HANDLE}" into the username/identifier field`);
await agent.act(`Type "${TEST_PASSWORD}" into the password field`);
await agent.act('Click the submit/authorize button');
await agent.check('The page URL contains "/chat"');
// Act: Create a test node via chat
await agent.act('Type "This is a test node for deletion" into the chat input');
await agent.act('Press Enter or click send');
await agent.check('AI responds with a message');
// Act: Trigger node creation
await agent.act('Wait for the AI to suggest creating a node or manually trigger node creation');
await agent.check('A node draft is created in the editor');
// Act: Publish the node
await agent.act('Click the "Publish" button');
await agent.check('A success notification appears');
await agent.check('The node is published to Bluesky');
// Act: Navigate to Galaxy view
await agent.act('Click the "Galaxy" navigation link');
await agent.check('The galaxy visualization loads');
await agent.check('At least one node is visible in the 3D galaxy view');
// Act: Click on the newly created node
await agent.act('Click on the test node in the galaxy view');
await agent.check('A node detail panel opens showing the node title and body');
await agent.check('The node detail panel shows "This is a test node for deletion"');
// Check: Verify delete button is visible (only for user\'s own nodes)
await agent.check('A "Delete" button is visible in the node detail panel');
// Act: Click the delete button
await agent.act('Click the "Delete" button');
// Check: Verify delete confirmation modal appears
await agent.check('A delete confirmation modal appears');
await agent.check('The modal is displayed above the node detail panel');
await agent.check('The modal shows "Are you sure you want to delete this node?"');
await agent.check('The modal explains this will remove the post from Bluesky');
await agent.check('The modal has a "Delete Permanently" button');
await agent.check('The modal has a "Cancel" button');
// Act: Confirm deletion
await agent.act('Click the "Delete Permanently" button');
// Check: Verify deletion succeeded
await agent.check('A success notification appears saying "Node deleted"');
await agent.check('The node detail panel closes');
await agent.check('The node is no longer visible in the galaxy view');
// Act: Verify node is deleted from Bluesky
await agent.act('Navigate to the user\'s Bluesky profile');
await agent.check('The test node "This is a test node for deletion" is not visible on Bluesky');
});
test('Delete button is not shown for other users\' nodes', async (agent) => {
// This test would require viewing another user's public galaxy
// Skipping for now as it requires a second test account
await agent.act('Skip this test - requires second test account');
});
test('Cancel button closes delete confirmation without deleting', async (agent) => {
// Act: Log in
await agent.act('Navigate to /login');
await agent.act(`Type "${TEST_HANDLE}" into the "Your Handle" input field`);
await agent.act('Click the "Log in with Bluesky" button');
await agent.check('The page URL contains "bsky.social"');
await agent.act(`Type "${TEST_HANDLE}" into the username/identifier field`);
await agent.act(`Type "${TEST_PASSWORD}" into the password field`);
await agent.act('Click the submit/authorize button');
await agent.check('The page URL contains "/chat"');
// Act: Navigate to Galaxy view
await agent.act('Click the "Galaxy" navigation link');
await agent.check('The galaxy visualization loads');
// Act: Click on any existing node
await agent.act('Click on any node in the galaxy view');
await agent.check('A node detail panel opens');
// Act: Click the delete button
await agent.act('Click the "Delete" button');
await agent.check('A delete confirmation modal appears');
// Act: Click cancel
await agent.act('Click the "Cancel" button');
// Check: Verify modal closes and node is still there
await agent.check('The delete confirmation modal closes');
await agent.check('The node detail panel is still open');
await agent.check('The node is still visible in the galaxy view');
});
test('Node deletion removes associated links', async (agent) => {
// Act: Log in
await agent.act('Navigate to /login');
await agent.act(`Type "${TEST_HANDLE}" into the "Your Handle" input field`);
await agent.act('Click the "Log in with Bluesky" button');
await agent.check('The page URL contains "bsky.social"');
await agent.act(`Type "${TEST_HANDLE}" into the username/identifier field`);
await agent.act(`Type "${TEST_PASSWORD}" into the password field`);
await agent.act('Click the submit/authorize button');
await agent.check('The page URL contains "/chat"');
// Act: Create two linked nodes
await agent.act('Create a first test node via chat');
await agent.act('Create a second test node that links to the first');
// Act: Navigate to Galaxy view
await agent.act('Click the "Galaxy" navigation link');
await agent.check('The galaxy visualization shows two nodes with a link between them');
// Act: Delete one of the nodes
await agent.act('Click on the first test node');
await agent.act('Click the "Delete" button');
await agent.act('Click "Delete Permanently"');
// Check: Verify the link is also removed
await agent.check('The link between the nodes is no longer visible');
await agent.check('Only one node remains in the galaxy');
});
test('User can delete node from debug panel in Profile menu', async (agent) => {
// Act: Log in
await agent.act('Navigate to /login');
await agent.act(`Type "${TEST_HANDLE}" into the "Your Handle" input field`);
await agent.act('Click the "Log in with Bluesky" button');
await agent.check('The page URL contains "bsky.social"');
await agent.act(`Type "${TEST_HANDLE}" into the username/identifier field`);
await agent.act(`Type "${TEST_PASSWORD}" into the password field`);
await agent.act('Click the submit/authorize button');
await agent.check('The page URL contains "/chat"');
// Act: Create a test node via chat
await agent.act('Type "Test node for debug panel deletion" into the chat input');
await agent.act('Press Enter or click send');
await agent.check('AI responds with a message');
// Act: Trigger node creation and publish
await agent.act('Wait for the AI to suggest creating a node or manually trigger node creation');
await agent.check('A node draft is created in the editor');
await agent.act('Click the "Publish" button');
await agent.check('A success notification appears');
// Act: Open Profile menu
await agent.act('Click the "Profile" button in the navigation sidebar');
await agent.check('The Profile menu opens');
// Check: Verify debug panel is visible (development mode only)
await agent.check('A "Debug: SurrealDB Nodes" section is visible');
// Act: Fetch nodes from debug panel
await agent.act('Click the "Fetch Nodes" button');
await agent.check('The button shows a count greater than 0');
await agent.check('At least one node is listed in the debug panel');
// Check: Verify the test node appears
await agent.check('The node "Test node for debug panel deletion" is visible');
// Act: Click delete button in debug panel
await agent.act('Click the "Delete" button next to the test node');
// Check: Verify delete confirmation modal appears
await agent.check('A delete confirmation modal appears');
await agent.check('The modal is displayed above the profile menu');
await agent.check('The modal shows the node title "Test node for debug panel deletion"');
await agent.check('The modal explains this will remove the post from Bluesky');
await agent.check('The modal shows "This action cannot be undone"');
await agent.check('The modal has a "Delete Permanently" button');
await agent.check('The modal has a "Cancel" button');
// Act: Confirm deletion
await agent.act('Click the "Delete Permanently" button');
// Check: Verify deletion succeeded
await agent.check('A success notification appears saying "Node deleted"');
await agent.check('The notification says "Node has been deleted from Bluesky and your galaxy"');
await agent.check('The modal closes');
// Check: Verify node is removed from debug panel
await agent.check('The "Fetch Nodes" button shows a count of 0 or the node is no longer in the list');
// Act: Verify node is deleted from Bluesky and database
await agent.act('Refresh the page');
await agent.act('Click the "Profile" button again');
await agent.act('Click the "Fetch Nodes" button');
await agent.check('The node "Test node for debug panel deletion" is not in the list');
});