-
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
316 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import ComponentParser from "../src/ComponentParser"; | ||
|
||
describe("ComponentParser", () => { | ||
const diagnostics = { | ||
moduleName: "TestComponent", | ||
filePath: "/test/TestComponent.svelte", | ||
}; | ||
|
||
test("parses basic component exports", () => { | ||
const parser = new ComponentParser(); | ||
const source = ` | ||
<script> | ||
export let label; | ||
export let disabled = false; | ||
</script> | ||
<button {disabled}>{label}</button> | ||
`; | ||
|
||
const result = parser.parseSvelteComponent(source, diagnostics); | ||
expect(result.props).toHaveLength(2); | ||
const propNames = result.props.map((p) => p.name); | ||
expect(propNames).toContain("label"); | ||
expect(propNames).toContain("disabled"); | ||
}); | ||
|
||
test("parses component with multiple exports", () => { | ||
const parser = new ComponentParser(); | ||
const source = ` | ||
<script> | ||
export let size = 'medium'; | ||
export const buttonSizes = ['small', 'medium', 'large']; | ||
</script> | ||
<button class={size}> | ||
<slot /> | ||
</button> | ||
`; | ||
|
||
const result = parser.parseSvelteComponent(source, diagnostics); | ||
expect(result.props).toHaveLength(2); | ||
expect(result.props[0].name).toBe("size"); | ||
expect(result.props[1].name).toBe("buttonSizes"); | ||
}); | ||
|
||
test("parses component with JSDoc comments", () => { | ||
const parser = new ComponentParser(); | ||
const source = ` | ||
<script> | ||
/** The text to display in the button */ | ||
export let label; | ||
/** Controls the disabled state */ | ||
export let disabled = false; | ||
</script> | ||
<button {disabled}>{label}</button> | ||
`; | ||
|
||
const result = parser.parseSvelteComponent(source, diagnostics); | ||
expect(result.props).toHaveLength(2); | ||
|
||
const labelProp = result.props.find((p) => p.name === "label"); | ||
expect(labelProp?.description).toBe("The text to display in the button"); | ||
|
||
const disabledProp = result.props.find((p) => p.name === "disabled"); | ||
expect(disabledProp?.description).toBe("Controls the disabled state"); | ||
}); | ||
|
||
test("handles slots and slot props", () => { | ||
const parser = new ComponentParser(); | ||
const source = ` | ||
<script> | ||
export let items = []; | ||
</script> | ||
<div> | ||
<slot name="header" {items} /> | ||
<slot /> | ||
<slot name="footer" count={items.length} /> | ||
</div> | ||
`; | ||
|
||
const result = parser.parseSvelteComponent(source, diagnostics); | ||
expect(result.slots).toHaveLength(3); | ||
|
||
const defaultSlot = result.slots.find((s) => s.default); | ||
expect(defaultSlot).toBeTruthy(); | ||
|
||
const headerSlot = result.slots.find((s) => s.name === "header"); | ||
expect(headerSlot?.slot_props).toContain("items"); | ||
|
||
const footerSlot = result.slots.find((s) => s.name === "footer"); | ||
expect(footerSlot?.slot_props).toContain("count"); | ||
}); | ||
|
||
test("handles dispatched events", () => { | ||
const parser = new ComponentParser(); | ||
const source = ` | ||
<script> | ||
import { createEventDispatcher } from 'svelte'; | ||
const dispatch = createEventDispatcher(); | ||
function handleClick() { | ||
dispatch('click', { detail: 'clicked' }); | ||
} | ||
</script> | ||
<button on:click={handleClick}>Click me</button> | ||
`; | ||
|
||
const result = parser.parseSvelteComponent(source, diagnostics); | ||
expect(result.events).toHaveLength(1); | ||
expect(result.events[0].type).toBe("dispatched"); | ||
expect(result.events[0].name).toBe("click"); | ||
}); | ||
|
||
test("handles component comments", () => { | ||
const parser = new ComponentParser(); | ||
const source = ` | ||
<!-- @component | ||
A button component with customizable styles and behaviors. | ||
Use this component for consistent button styling across the app. | ||
--> | ||
<script> | ||
export let label; | ||
</script> | ||
<button>{label}</button> | ||
`; | ||
|
||
const result = parser.parseSvelteComponent(source, diagnostics); | ||
expect(result.componentComment).toContain("A button component with customizable styles"); | ||
}); | ||
|
||
test("throws an error for malformed source code", () => { | ||
const parser = new ComponentParser(); | ||
const invalidSource = ` | ||
<script> | ||
export default function { | ||
// Missing function name and parameters | ||
return <div />; | ||
} | ||
</script> | ||
`; | ||
|
||
expect(() => parser.parseSvelteComponent(invalidSource, diagnostics)).toThrow(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,75 @@ | ||
import WriterMarkdown from "../src/writer/WriterMarkdown"; | ||
import type { AppendType } from "../src/writer/WriterMarkdown"; | ||
import WriterMarkdown from "../src/writer/WriterMarkdown"; | ||
|
||
describe("WriterMarkdown", () => { | ||
test("basic functionality", () => { | ||
const types: AppendType[] = []; | ||
const document = new WriterMarkdown({ | ||
onAppend: (type) => types.push(type), | ||
}); | ||
|
||
document.append("h1", "Component Index"); | ||
document.append("h2", "Components").tableOfContents(); | ||
document.append("divider"); | ||
|
||
expect(document.end()).toEqual("# Component Index\n\n## Components\n\n\n\n---\n\n"); | ||
|
||
test("WriterMarkdown", () => { | ||
const types: AppendType[] = []; | ||
const document = new WriterMarkdown({ | ||
onAppend: (type) => types.push(type), | ||
document.append("raw", "> Quote"); | ||
document.append("p", "Text"); | ||
|
||
expect(document.end()).toEqual("# Component Index\n\n## Components\n\n\n\n---\n\n> QuoteText\n\n"); | ||
expect(types).toEqual(["h1", "h2", "divider", "raw", "p"]); | ||
}); | ||
|
||
document.append("h1", "Component Index"); | ||
document.append("h2", "Components").tableOfContents(); | ||
document.append("divider"); | ||
test("heading levels and table of contents", () => { | ||
const document = new WriterMarkdown({}); | ||
|
||
document.append("h1", "Main Title"); | ||
document.append("h2", "Section 1"); | ||
document.append("h3", "Subsection 1.1"); | ||
|
||
expect(document.end()).toEqual("# Component Index\n\n## Components\n\n\n\n---\n\n"); | ||
const output = document.end(); | ||
expect(output).toContain("# Main Title"); | ||
expect(output).toContain("## Section 1"); | ||
expect(output).toContain("### Subsection 1.1"); | ||
}); | ||
|
||
document.append("raw", "> Quote"); | ||
document.append("p", "Text"); | ||
test("quote formatting", () => { | ||
const document = new WriterMarkdown({}); | ||
|
||
expect(document.end()).toEqual("# Component Index\n\n## Components\n\n\n\n---\n\n> QuoteText\n\n"); | ||
expect(types).toEqual(["h1", "h2", "divider", "raw", "p"]); | ||
document.append("quote", "This is a quote"); | ||
document.append("p", "This is a paragraph"); | ||
document.append("quote", "Multi\nline\nquote"); | ||
|
||
const output = document.end(); | ||
expect(output).toEqual("> This is a quote\n\nThis is a paragraph\n\n> Multi\nline\nquote\n\n"); | ||
}); | ||
|
||
test("raw content and dividers", () => { | ||
const document = new WriterMarkdown({}); | ||
|
||
document.append("raw", "No line break"); | ||
document.append("raw", " after this."); | ||
document.append("divider"); | ||
document.append("p", "New paragraph"); | ||
|
||
expect(document.end()).toEqual("No line break after this.---\n\nNew paragraph\n\n"); | ||
}); | ||
|
||
test("onAppend callback receives correct arguments", () => { | ||
let lastType: AppendType | undefined; | ||
let lastDocument: WriterMarkdown | undefined; | ||
|
||
const document = new WriterMarkdown({ | ||
onAppend: (type, doc) => { | ||
lastType = type; | ||
lastDocument = doc; | ||
}, | ||
}); | ||
|
||
document.append("h1", "Title"); | ||
|
||
expect(lastType).toBe("h1"); | ||
expect(lastDocument).toBe(document); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import * as fs from "fs"; | ||
import * as path from "path"; | ||
import { getSvelteEntry } from "../src/get-svelte-entry"; | ||
|
||
describe("getSvelteEntry", () => { | ||
const mockCwd = "/mock/project"; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
jest.spyOn(process, "cwd").mockReturnValue(mockCwd); | ||
jest.spyOn(path, "join").mockImplementation((...args) => args.join("/")); | ||
jest.spyOn(fs, "existsSync"); | ||
jest.spyOn(fs, "readFileSync"); | ||
jest.spyOn(console, "log").mockImplementation(() => {}); | ||
jest.spyOn(console, "error").mockImplementation(() => {}); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.restoreAllMocks(); | ||
}); | ||
|
||
test("returns explicit entry point if valid", () => { | ||
jest.spyOn(fs, "existsSync").mockReturnValue(true); | ||
|
||
const result = getSvelteEntry("src/Component.svelte"); | ||
|
||
expect(result).toBe("src/Component.svelte"); | ||
expect(fs.existsSync).toHaveBeenCalledWith(`${mockCwd}/src/Component.svelte`); | ||
}); | ||
|
||
test("returns null if explicit entry point is invalid", () => { | ||
jest.spyOn(fs, "existsSync").mockReturnValue(false); | ||
|
||
const result = getSvelteEntry("src/NonExistent.svelte"); | ||
|
||
expect(result).toBeNull(); | ||
expect(console.log).toHaveBeenCalledWith(expect.stringContaining("Invalid entry point")); | ||
}); | ||
|
||
test("returns svelte field from package.json if no entry point provided", () => { | ||
jest.spyOn(fs, "existsSync").mockReturnValue(true); | ||
jest.spyOn(fs, "readFileSync").mockReturnValue(JSON.stringify({ svelte: "src/index.js" })); | ||
|
||
const result = getSvelteEntry(); | ||
|
||
expect(result).toBe("src/index.js"); | ||
expect(fs.readFileSync).toHaveBeenCalledWith(`${mockCwd}/package.json`, "utf-8"); | ||
}); | ||
|
||
test("returns null if package.json exists but has no svelte field", () => { | ||
jest.spyOn(fs, "existsSync").mockReturnValue(true); | ||
jest.spyOn(fs, "readFileSync").mockReturnValue(JSON.stringify({ main: "index.js" })); | ||
|
||
const result = getSvelteEntry(); | ||
|
||
expect(result).toBeNull(); | ||
expect(console.log).toHaveBeenCalledWith(expect.stringContaining("Could not determine an entry point")); | ||
}); | ||
|
||
test("returns null if package.json does not exist", () => { | ||
jest.spyOn(fs, "existsSync").mockReturnValue(false); | ||
|
||
const result = getSvelteEntry(); | ||
|
||
expect(result).toBeNull(); | ||
expect(console.log).toHaveBeenCalledWith(expect.stringContaining("Could not locate a package.json file")); | ||
}); | ||
|
||
test.skip("handles malformed package.json", () => { | ||
jest.spyOn(fs, "existsSync").mockReturnValue(true); | ||
jest.spyOn(fs, "readFileSync").mockReturnValue("invalid json"); | ||
|
||
expect(() => getSvelteEntry()).toThrow(); | ||
expect(console.error).toHaveBeenCalled(); | ||
}); | ||
|
||
test("handles empty string entry point", () => { | ||
const result = getSvelteEntry(""); | ||
|
||
expect(result).toBeNull(); | ||
expect(console.log).toHaveBeenCalledWith(expect.stringContaining("Could not locate a package.json file.")); | ||
}); | ||
|
||
test("handles undefined svelte field in package.json", () => { | ||
jest.spyOn(fs, "existsSync").mockReturnValue(true); | ||
jest.spyOn(fs, "readFileSync").mockReturnValue(JSON.stringify({ svelte: undefined })); | ||
|
||
const result = getSvelteEntry(); | ||
expect(result).toBeNull(); | ||
expect(console.log).toHaveBeenCalledWith(expect.stringContaining("Could not determine an entry point")); | ||
}); | ||
}); |