Merge pull request #94 from AutoMaker-Org/app_spec_fixes

working on improving the app spec page
This commit is contained in:
Web Dev Cody
2025-12-14 17:49:55 -05:00
committed by GitHub
30 changed files with 2209 additions and 876 deletions

View File

@@ -0,0 +1,103 @@
import { describe, it, expect, beforeEach } from "vitest";
import {
setRunningState,
getErrorMessage,
isRunning,
currentAbortController,
} from "@/routes/app-spec/common.js";
describe("app-spec/common.ts", () => {
beforeEach(() => {
// Reset state before each test
setRunningState(false, null);
});
describe("setRunningState", () => {
it("should set isRunning to true when running is true", () => {
setRunningState(true);
expect(isRunning).toBe(true);
});
it("should set isRunning to false when running is false", () => {
setRunningState(true);
setRunningState(false);
expect(isRunning).toBe(false);
});
it("should set currentAbortController when provided", () => {
const controller = new AbortController();
setRunningState(true, controller);
expect(currentAbortController).toBe(controller);
});
it("should set currentAbortController to null when not provided", () => {
const controller = new AbortController();
setRunningState(true, controller);
setRunningState(false);
expect(currentAbortController).toBe(null);
});
it("should set currentAbortController to null when explicitly passed null", () => {
const controller = new AbortController();
setRunningState(true, controller);
setRunningState(true, null);
expect(currentAbortController).toBe(null);
});
it("should update state multiple times correctly", () => {
const controller1 = new AbortController();
const controller2 = new AbortController();
setRunningState(true, controller1);
expect(isRunning).toBe(true);
expect(currentAbortController).toBe(controller1);
setRunningState(true, controller2);
expect(isRunning).toBe(true);
expect(currentAbortController).toBe(controller2);
setRunningState(false, null);
expect(isRunning).toBe(false);
expect(currentAbortController).toBe(null);
});
});
describe("getErrorMessage", () => {
it("should return message from Error instance", () => {
const error = new Error("Test error message");
expect(getErrorMessage(error)).toBe("Test error message");
});
it("should return 'Unknown error' for non-Error objects", () => {
expect(getErrorMessage("string error")).toBe("Unknown error");
expect(getErrorMessage(123)).toBe("Unknown error");
expect(getErrorMessage(null)).toBe("Unknown error");
expect(getErrorMessage(undefined)).toBe("Unknown error");
expect(getErrorMessage({})).toBe("Unknown error");
expect(getErrorMessage([])).toBe("Unknown error");
});
it("should return message from Error with empty message", () => {
const error = new Error("");
expect(getErrorMessage(error)).toBe("");
});
it("should handle Error objects with custom properties", () => {
const error = new Error("Base message");
(error as any).customProp = "custom value";
expect(getErrorMessage(error)).toBe("Base message");
});
it("should handle Error objects created with different constructors", () => {
class CustomError extends Error {
constructor(message: string) {
super(message);
this.name = "CustomError";
}
}
const customError = new CustomError("Custom error message");
expect(getErrorMessage(customError)).toBe("Custom error message");
});
});
});

View File

@@ -0,0 +1,244 @@
import { describe, it, expect } from "vitest";
describe("app-spec/parse-and-create-features.ts - JSON extraction", () => {
// Test the JSON extraction regex pattern used in parseAndCreateFeatures
const jsonExtractionPattern = /\{[\s\S]*"features"[\s\S]*\}/;
describe("JSON extraction regex", () => {
it("should extract JSON with features array", () => {
const content = `Here is the response:
{
"features": [
{
"id": "feature-1",
"title": "Test Feature",
"description": "A test feature",
"priority": 1,
"complexity": "simple",
"dependencies": []
}
]
}`;
const match = content.match(jsonExtractionPattern);
expect(match).not.toBeNull();
expect(match![0]).toContain('"features"');
expect(match![0]).toContain('"id": "feature-1"');
});
it("should extract JSON with multiple features", () => {
const content = `Some text before
{
"features": [
{
"id": "feature-1",
"title": "Feature 1"
},
{
"id": "feature-2",
"title": "Feature 2"
}
]
}
Some text after`;
const match = content.match(jsonExtractionPattern);
expect(match).not.toBeNull();
expect(match![0]).toContain('"features"');
expect(match![0]).toContain('"feature-1"');
expect(match![0]).toContain('"feature-2"');
});
it("should extract JSON with nested objects and arrays", () => {
const content = `Response:
{
"features": [
{
"id": "feature-1",
"dependencies": ["dep-1", "dep-2"],
"metadata": {
"tags": ["tag1", "tag2"]
}
}
]
}`;
const match = content.match(jsonExtractionPattern);
expect(match).not.toBeNull();
expect(match![0]).toContain('"dependencies"');
expect(match![0]).toContain('"dep-1"');
});
it("should handle JSON with whitespace and newlines", () => {
const content = `Text before
{
"features": [
{
"id": "feature-1",
"title": "Feature",
"description": "A feature\nwith newlines"
}
]
}
Text after`;
const match = content.match(jsonExtractionPattern);
expect(match).not.toBeNull();
expect(match![0]).toContain('"features"');
});
it("should extract JSON when features array is empty", () => {
const content = `Response:
{
"features": []
}`;
const match = content.match(jsonExtractionPattern);
expect(match).not.toBeNull();
expect(match![0]).toContain('"features"');
expect(match![0]).toContain("[]");
});
it("should not match content without features key", () => {
const content = `{
"otherKey": "value"
}`;
const match = content.match(jsonExtractionPattern);
expect(match).toBeNull();
});
it("should not match content without JSON structure", () => {
const content = "Just plain text with features mentioned";
const match = content.match(jsonExtractionPattern);
expect(match).toBeNull();
});
it("should extract JSON when features key appears multiple times", () => {
const content = `Before:
{
"features": [
{
"id": "feature-1",
"title": "Feature"
}
]
}
After: The word "features" appears again`;
const match = content.match(jsonExtractionPattern);
expect(match).not.toBeNull();
// Should match from first { to last }
expect(match![0]).toContain('"features"');
});
it("should handle JSON with escaped quotes", () => {
const content = `{
"features": [
{
"id": "feature-1",
"description": "A feature with \\"quotes\\""
}
]
}`;
const match = content.match(jsonExtractionPattern);
expect(match).not.toBeNull();
expect(match![0]).toContain('"features"');
});
it("should extract JSON with complex nested structure", () => {
const content = `Response:
{
"features": [
{
"id": "feature-1",
"dependencies": [
{
"id": "dep-1",
"type": "required"
}
],
"metadata": {
"tags": ["tag1"],
"notes": "Some notes"
}
}
],
"metadata": {
"version": "1.0"
}
}`;
const match = content.match(jsonExtractionPattern);
expect(match).not.toBeNull();
expect(match![0]).toContain('"features"');
expect(match![0]).toContain('"metadata"');
});
});
describe("JSON parsing validation", () => {
it("should parse valid feature JSON structure", () => {
const validJson = `{
"features": [
{
"id": "feature-1",
"title": "Test Feature",
"description": "A test feature",
"priority": 1,
"complexity": "simple",
"dependencies": []
}
]
}`;
const parsed = JSON.parse(validJson);
expect(parsed.features).toBeDefined();
expect(Array.isArray(parsed.features)).toBe(true);
expect(parsed.features.length).toBe(1);
expect(parsed.features[0].id).toBe("feature-1");
expect(parsed.features[0].title).toBe("Test Feature");
});
it("should handle features with optional fields", () => {
const jsonWithOptionalFields = `{
"features": [
{
"id": "feature-1",
"title": "Feature",
"priority": 2,
"complexity": "moderate"
}
]
}`;
const parsed = JSON.parse(jsonWithOptionalFields);
expect(parsed.features[0].id).toBe("feature-1");
expect(parsed.features[0].priority).toBe(2);
// description and dependencies are optional
expect(parsed.features[0].description).toBeUndefined();
expect(parsed.features[0].dependencies).toBeUndefined();
});
it("should handle features with dependencies", () => {
const jsonWithDeps = `{
"features": [
{
"id": "feature-1",
"title": "Feature 1",
"dependencies": []
},
{
"id": "feature-2",
"title": "Feature 2",
"dependencies": ["feature-1"]
}
]
}`;
const parsed = JSON.parse(jsonWithDeps);
expect(parsed.features[0].dependencies).toEqual([]);
expect(parsed.features[1].dependencies).toEqual(["feature-1"]);
});
});
});