Files
next-ai-draw-io/tests/unit/chat-helpers.test.ts
Dayuan Jiang c7a85d398f test: add Vitest and Playwright testing infrastructure (#512)
* test: add Vitest and Playwright testing infrastructure

- Add Vitest for unit tests (39 tests)
  - cached-responses.test.ts
  - ai-providers.test.ts
  - chat-helpers.test.ts
  - utils.test.ts
- Add Playwright for E2E tests (3 smoke tests)
  - Homepage load
  - Japanese locale
  - Settings dialog
- Add CI workflow (.github/workflows/test.yml)
- Add vitest.config.mts and playwright.config.ts
- Update .gitignore for test artifacts

* test: add more E2E tests for UI components

- Chat panel tests (interactive elements, iframe)
- Settings tests (dark mode, language, draw.io theme)
- Save dialog tests (buttons exist)
- History dialog tests
- Model config tests
- Keyboard interaction tests
- Upload area tests

Total: 15 E2E tests, all passing

* test: fix E2E test issues from review

Fixes based on Gemini and Codex review:
- Remove brittle nth(1) selector in keyboard tests
- Remove waitForTimeout(500) race condition
- Remove if(isVisible) silent skip patterns
- Add proper assertions instead of no-op checks
- Remove expect(count >= 0) that always passes
- Remove unused hasProviderUI variable

All 14 E2E tests and 39 unit tests pass.

* style: auto-format with Biome

* fix: resolve lint errors for CI

* test(e2e): add diagram generation tests with mocked AI responses

- Add tests for generate, edit, and append diagram operations
- Use SSE mocked responses matching AI SDK UI message stream format
- Generate mxCell XML directly in tests for deterministic assertions
- Tests verify tool card rendering and 'Complete' badge state

* test: add comprehensive E2E tests for all major features

- Error handling tests (API errors, rate limits, network timeout, truncated XML)
- Multi-turn conversation tests (sequential requests, history preservation)
- File upload tests (upload button, file preview, sending with message)
- Theme switching tests (dark mode toggle, persistence, system preference)
- Language switching tests (EN/JA/ZH, persistence, locale URLs)
- Iframe interaction tests (draw.io loading, toolbar, diagram rendering)
- Copy/paste tests (chat input, XML input, special characters)
- History restore tests (new chat, persistence, browser navigation)

* refactor: extract shared test helpers and improve error assertions

- Create tests/e2e/lib/helpers.ts with shared SSE mock functions
- Add proper error UI assertions to error-handling.spec.ts
- Remove waitForTimeout calls in favor of real assertions
- Update 6 test files to use shared helpers

* docs: add testing section to CONTRIBUTING.md

* fix: improve test infrastructure based on PR review

- Fix double build in CI: remove redundant build from playwright webServer
- Export chat helpers from shared module for proper unit testing
- Replace waitForTimeout with explicit waits in E2E tests
- Add data-testid attributes to settings and new chat buttons
- Add list reporter for CI to show failures in logs
- Add Playwright browser caching to speed up CI
- Add vitest coverage configuration
- Fix conditional test assertions to use test.skip() instead of silent pass
- Remove unused variables flagged by linter

* fix: improve E2E test assertions and remove silent skips

- Replace silent test.skip() with explicit conditional skips
- Add actual persistence assertion after page reload
- Use data-testid selector for new chat button test

* refactor: add shared fixtures and test.step() patterns

- Add tests/e2e/lib/fixtures.ts with shared test helpers
- Add tests/e2e/fixtures/diagrams.ts with XML test data
- Add expectBeforeAndAfterReload() helper for persistence tests
- Add test.step() for better test reporting in complex tests
- Consolidate mock helpers into fixtures module
- Reduce code duplication across 17 test files

* fix: make persistence tests more reliable

- Remove expectBeforeAndAfterReload from mocked API tests
- Add explicit test.step() for before/after reload checks
- Add retry config for flaky clipboard tests
- Add sleep after reload for language persistence test

* test: remove flaky XML paste test

* docs: run both unit and e2e tests before PR

* chore: add type check and unit test git hooks

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-05 01:37:32 +09:00

172 lines
5.2 KiB
TypeScript

// @vitest-environment node
import { describe, expect, it } from "vitest"
import {
isMinimalDiagram,
replaceHistoricalToolInputs,
validateFileParts,
} from "@/lib/chat-helpers"
describe("validateFileParts", () => {
it("returns valid for no files", () => {
const messages = [
{ role: "user", parts: [{ type: "text", text: "hello" }] },
]
expect(validateFileParts(messages)).toEqual({ valid: true })
})
it("returns valid for files under limit", () => {
const smallBase64 = btoa("x".repeat(100))
const messages = [
{
role: "user",
parts: [
{
type: "file",
url: `data:image/png;base64,${smallBase64}`,
},
],
},
]
expect(validateFileParts(messages)).toEqual({ valid: true })
})
it("returns error for too many files", () => {
const messages = [
{
role: "user",
parts: Array(6)
.fill(null)
.map(() => ({
type: "file",
url: "data:image/png;base64,abc",
})),
},
]
const result = validateFileParts(messages)
expect(result.valid).toBe(false)
expect(result.error).toContain("Too many files")
})
it("returns error for file exceeding size limit", () => {
// Create base64 that decodes to > 2MB
const largeBase64 = btoa("x".repeat(3 * 1024 * 1024))
const messages = [
{
role: "user",
parts: [
{
type: "file",
url: `data:image/png;base64,${largeBase64}`,
},
],
},
]
const result = validateFileParts(messages)
expect(result.valid).toBe(false)
expect(result.error).toContain("exceeds")
})
})
describe("isMinimalDiagram", () => {
it("returns true for empty diagram", () => {
const xml = '<mxCell id="0"/><mxCell id="1" parent="0"/>'
expect(isMinimalDiagram(xml)).toBe(true)
})
it("returns false for diagram with content", () => {
const xml =
'<mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="2" value="Hello"/>'
expect(isMinimalDiagram(xml)).toBe(false)
})
it("handles whitespace correctly", () => {
const xml = ' <mxCell id="0"/> <mxCell id="1" parent="0"/> '
expect(isMinimalDiagram(xml)).toBe(true)
})
})
describe("replaceHistoricalToolInputs", () => {
it("replaces display_diagram tool inputs with placeholder", () => {
const messages = [
{
role: "assistant",
content: [
{
type: "tool-call",
toolName: "display_diagram",
input: { xml: "<mxCell...>" },
},
],
},
]
const result = replaceHistoricalToolInputs(messages)
expect(result[0].content[0].input.placeholder).toContain(
"XML content replaced",
)
})
it("replaces edit_diagram tool inputs with placeholder", () => {
const messages = [
{
role: "assistant",
content: [
{
type: "tool-call",
toolName: "edit_diagram",
input: { operations: [] },
},
],
},
]
const result = replaceHistoricalToolInputs(messages)
expect(result[0].content[0].input.placeholder).toContain(
"XML content replaced",
)
})
it("removes tool calls with invalid inputs", () => {
const messages = [
{
role: "assistant",
content: [
{
type: "tool-call",
toolName: "display_diagram",
input: {},
},
{
type: "tool-call",
toolName: "display_diagram",
input: null,
},
],
},
]
const result = replaceHistoricalToolInputs(messages)
expect(result[0].content).toHaveLength(0)
})
it("preserves non-assistant messages", () => {
const messages = [{ role: "user", content: "hello" }]
const result = replaceHistoricalToolInputs(messages)
expect(result).toEqual(messages)
})
it("preserves other tool calls", () => {
const messages = [
{
role: "assistant",
content: [
{
type: "tool-call",
toolName: "other_tool",
input: { foo: "bar" },
},
],
},
]
const result = replaceHistoricalToolInputs(messages)
expect(result[0].content[0].input).toEqual({ foo: "bar" })
})
})