Files
automaker/apps/ui/tests/memory/file-extension-edge-cases.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

193 lines
6.1 KiB
TypeScript

/**
* Memory View File Extension Edge Cases E2E Tests
*
* Tests for file extension handling in the memory view:
* - Files with valid markdown extensions (.md, .markdown)
* - Files without extensions (edge case for isMarkdownFile)
* - Files with multiple dots in name
*/
import { test, expect } from '@playwright/test';
import {
resetMemoryDirectory,
setupProjectWithFixture,
getFixturePath,
navigateToMemory,
waitForMemoryFile,
selectMemoryFile,
waitForMemoryContentToLoad,
clickElement,
fillInput,
waitForNetworkIdle,
authenticateForTests,
waitForElementHidden,
createMemoryFileOnDisk,
} from '../utils';
// Use desktop viewport for these tests
test.use({ viewport: { width: 1280, height: 720 } });
test.describe('Memory View File Extension Edge Cases', () => {
test.beforeEach(async () => {
resetMemoryDirectory();
});
test.afterEach(async () => {
resetMemoryDirectory();
});
test('should handle file with .md extension', async ({ page }) => {
const fileName = 'standard-file.md';
const content = '# Standard Markdown';
await setupProjectWithFixture(page, getFixturePath());
await authenticateForTests(page);
await navigateToMemory(page);
// Create file via API
createMemoryFileOnDisk(fileName, content);
await waitForNetworkIdle(page);
// Refresh to load the file
await page.reload();
await waitForMemoryFile(page, fileName);
// Select and verify it opens as markdown
await selectMemoryFile(page, fileName);
await waitForMemoryContentToLoad(page);
// Should show markdown preview
const markdownPreview = page.locator('[data-testid="markdown-preview"]');
await expect(markdownPreview).toBeVisible();
// Verify content rendered
const h1 = markdownPreview.locator('h1');
await expect(h1).toHaveText('Standard Markdown');
});
test('should handle file with .markdown extension', async ({ page }) => {
const fileName = 'extended-extension.markdown';
const content = '# Extended Extension Test';
await setupProjectWithFixture(page, getFixturePath());
await authenticateForTests(page);
await navigateToMemory(page);
// Create file via API
createMemoryFileOnDisk(fileName, content);
await waitForNetworkIdle(page);
// Refresh to load the file
await page.reload();
await waitForMemoryFile(page, fileName);
// Select and verify
await selectMemoryFile(page, fileName);
await waitForMemoryContentToLoad(page);
const markdownPreview = page.locator('[data-testid="markdown-preview"]');
await expect(markdownPreview).toBeVisible();
});
test('should handle file with multiple dots in name', async ({ page }) => {
const fileName = 'my.detailed.notes.md';
const content = '# Multiple Dots Test';
await setupProjectWithFixture(page, getFixturePath());
await authenticateForTests(page);
await navigateToMemory(page);
// Create file via API
createMemoryFileOnDisk(fileName, content);
await waitForNetworkIdle(page);
// Refresh to load the file
await page.reload();
await waitForMemoryFile(page, fileName);
// Select and verify - should still recognize as markdown
await selectMemoryFile(page, fileName);
await waitForMemoryContentToLoad(page);
const markdownPreview = page.locator('[data-testid="markdown-preview"]');
await expect(markdownPreview).toBeVisible();
});
test('should NOT show file without extension in file list', async ({ page }) => {
const fileName = 'README';
const content = '# File Without Extension';
await setupProjectWithFixture(page, getFixturePath());
await authenticateForTests(page);
await navigateToMemory(page);
// Create file via API (without extension)
createMemoryFileOnDisk(fileName, content);
await waitForNetworkIdle(page);
// Refresh to load the file
await page.reload();
// Wait a moment for files to load
await page.waitForTimeout(1000);
// File should NOT appear in list because isMarkdownFile returns false for no extension
const fileButton = page.locator(`[data-testid="memory-file-${fileName}"]`);
await expect(fileButton).not.toBeVisible();
});
test('should NOT create file without .md extension via UI', async ({ page }) => {
const fileName = 'NOTES';
const content = '# Notes without extension';
await setupProjectWithFixture(page, getFixturePath());
await authenticateForTests(page);
await navigateToMemory(page);
// Create file via UI without extension
await clickElement(page, 'create-memory-button');
await page.waitForSelector('[data-testid="create-memory-dialog"]', { timeout: 5000 });
await fillInput(page, 'new-memory-name', fileName);
await fillInput(page, 'new-memory-content', content);
await clickElement(page, 'confirm-create-memory');
await waitForElementHidden(page, 'create-memory-dialog', { timeout: 5000 });
await waitForNetworkIdle(page);
// File should NOT appear in list because UI enforces .md extension
// (The UI may add .md automatically or show validation error)
const fileButton = page.locator(`[data-testid="memory-file-${fileName}"]`);
await expect(fileButton)
.not.toBeVisible({ timeout: 3000 })
.catch(() => {
// It's OK if it doesn't appear - that's expected behavior
});
});
test('should handle uppercase extensions', async ({ page }) => {
const fileName = 'uppercase.MD';
const content = '# Uppercase Extension';
await setupProjectWithFixture(page, getFixturePath());
await authenticateForTests(page);
await navigateToMemory(page);
// Create file via API with uppercase extension
createMemoryFileOnDisk(fileName, content);
await waitForNetworkIdle(page);
// Refresh to load the file
await page.reload();
await waitForMemoryFile(page, fileName);
// Select and verify - should recognize .MD as markdown (case-insensitive)
await selectMemoryFile(page, fileName);
await waitForMemoryContentToLoad(page);
const markdownPreview = page.locator('[data-testid="markdown-preview"]');
await expect(markdownPreview).toBeVisible();
});
});