Files
automaker/apps/ui/tests/memory/mobile-memory-operations.spec.ts
gsxdsm 583c3eb4a6 Make memory and context views mobile-friendly (#813)
* Changes from fix/memory-and-context-mobile-friendly

* fix: Improve file extension detection and add path traversal protection

* refactor: Extract file extension utilities and add path traversal guards

Code review improvements:
- Extract isMarkdownFilename and isImageFilename to shared image-utils.ts
- Remove duplicated code from context-view.tsx and memory-view.tsx
- Add path traversal guard for context fixture utilities (matching memory)
- Add 7 new tests for context fixture path traversal protection
- Total 61 tests pass

Addresses code review feedback from PR #813

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: Add e2e tests for profiles crud and board background persistence

* Update apps/ui/playwright.config.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: Add robust test navigation handling and file filtering

* fix: Format NODE_OPTIONS configuration on single line

* test: Update profiles and board background persistence tests

* test: Replace iPhone 13 Pro with Pixel 5 for mobile test consistency

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-02-26 03:31:40 -08:00

175 lines
5.8 KiB
TypeScript

/**
* Mobile Memory View Operations E2E Tests
*
* Tests for file operations on mobile in the memory view:
* - Deleting files via dropdown menu on mobile
* - Creating files via mobile actions panel
*/
import { test, expect, devices } from '@playwright/test';
import {
resetMemoryDirectory,
setupProjectWithFixture,
getFixturePath,
navigateToMemory,
waitForMemoryFile,
clickElement,
fillInput,
waitForNetworkIdle,
authenticateForTests,
memoryFileExistsOnDisk,
waitForElementHidden,
} from '../utils';
// Use mobile viewport for mobile tests in Chromium CI
test.use({ ...devices['Pixel 5'] });
test.describe('Mobile Memory View Operations', () => {
test.beforeEach(async () => {
resetMemoryDirectory();
});
test.afterEach(async () => {
resetMemoryDirectory();
});
test('should create a file via mobile actions panel', async ({ page }) => {
const fileName = 'mobile-created.md';
await setupProjectWithFixture(page, getFixturePath());
await authenticateForTests(page);
await navigateToMemory(page);
// Create a test file via mobile actions panel
await clickElement(page, 'header-actions-panel-trigger');
await clickElement(page, 'create-memory-button-mobile');
await page.waitForSelector('[data-testid="create-memory-dialog"]', { timeout: 5000 });
await fillInput(page, 'new-memory-name', fileName);
await fillInput(page, 'new-memory-content', '# Created on Mobile');
await clickElement(page, 'confirm-create-memory');
await waitForElementHidden(page, 'create-memory-dialog', { timeout: 5000 });
await waitForNetworkIdle(page);
await waitForMemoryFile(page, fileName);
// Verify file appears in list
const fileButton = page.locator(`[data-testid="memory-file-${fileName}"]`);
await expect(fileButton).toBeVisible();
// Verify file exists on disk
expect(memoryFileExistsOnDisk(fileName)).toBe(true);
});
test('should delete a file via dropdown menu on mobile', async ({ page }) => {
const fileName = 'delete-via-menu-test.md';
await setupProjectWithFixture(page, getFixturePath());
await authenticateForTests(page);
await navigateToMemory(page);
// Create a test file
await clickElement(page, 'header-actions-panel-trigger');
await clickElement(page, 'create-memory-button-mobile');
await page.waitForSelector('[data-testid="create-memory-dialog"]', { timeout: 5000 });
await fillInput(page, 'new-memory-name', fileName);
await fillInput(page, 'new-memory-content', '# File to Delete');
await clickElement(page, 'confirm-create-memory');
await waitForElementHidden(page, 'create-memory-dialog', { timeout: 5000 });
await waitForNetworkIdle(page);
await waitForMemoryFile(page, fileName);
// Verify file exists
expect(memoryFileExistsOnDisk(fileName)).toBe(true);
// Close actions panel if still open
await page.keyboard.press('Escape');
await page.waitForTimeout(300);
// Click on the file menu dropdown - hover first to make it visible
const fileRow = page.locator(`[data-testid="memory-file-${fileName}"]`);
await fileRow.hover();
const fileMenuButton = page.locator(`[data-testid="memory-file-menu-${fileName}"]`);
await fileMenuButton.click({ force: true });
// Wait for dropdown
await page.waitForTimeout(300);
// Click delete in dropdown
const deleteMenuItem = page.locator(`[data-testid="delete-memory-file-${fileName}"]`);
await deleteMenuItem.click();
// Wait for file to be removed from list
await waitForElementHidden(page, `memory-file-${fileName}`, { timeout: 5000 });
// Verify file no longer exists on disk
expect(memoryFileExistsOnDisk(fileName)).toBe(false);
});
test('should refresh button be available in actions panel', async ({ page }) => {
await setupProjectWithFixture(page, getFixturePath());
await authenticateForTests(page);
await navigateToMemory(page);
// Open actions panel
await clickElement(page, 'header-actions-panel-trigger');
// Verify refresh button is visible in actions panel
const refreshButton = page.locator('[data-testid="refresh-memory-button-mobile"]');
await expect(refreshButton).toBeVisible();
});
test('should preview markdown content on mobile', async ({ page }) => {
const fileName = 'preview-test.md';
const markdownContent =
'# Preview Test\n\n**Bold text** and *italic text*\n\n- List item 1\n- List item 2';
await setupProjectWithFixture(page, getFixturePath());
await authenticateForTests(page);
await navigateToMemory(page);
// Create a test file
await clickElement(page, 'header-actions-panel-trigger');
await clickElement(page, 'create-memory-button-mobile');
await page.waitForSelector('[data-testid="create-memory-dialog"]', { timeout: 5000 });
await fillInput(page, 'new-memory-name', fileName);
await fillInput(page, 'new-memory-content', markdownContent);
await clickElement(page, 'confirm-create-memory');
await waitForElementHidden(page, 'create-memory-dialog', { timeout: 5000 });
await waitForNetworkIdle(page);
await waitForMemoryFile(page, fileName);
// Select the file by clicking on it
const fileButton = page.locator(`[data-testid="memory-file-${fileName}"]`);
await fileButton.click();
// Wait for content to load (preview or editor)
await page.waitForSelector('[data-testid="markdown-preview"], [data-testid="memory-editor"]', {
timeout: 5000,
});
// Memory files open in preview mode by default
const markdownPreview = page.locator('[data-testid="markdown-preview"]');
await expect(markdownPreview).toBeVisible();
// Verify the preview rendered the markdown (check for h1)
const h1 = markdownPreview.locator('h1');
await expect(h1).toHaveText('Preview Test');
});
});