diff --git a/packages/shadcn/src/utils/highlighter.test.ts b/packages/shadcn/src/utils/highlighter.test.ts new file mode 100644 index 0000000000..c7a03d6777 --- /dev/null +++ b/packages/shadcn/src/utils/highlighter.test.ts @@ -0,0 +1,78 @@ +import { describe, expect, test } from "vitest" + +import { highlighter } from "../../src/utils/highlighter" +import { generateRandomString } from "../../test/fuzz-utils" + +describe("fuzzing", () => { + test("should handle various input strings", () => { + const testCases = Array.from({ length: 100 }, () => ({ + text: generateRandomString(Math.floor(Math.random() * 100) + 1), + })) + + for (const { text } of testCases) { + try { + // Test each highlighter function + const errorResult = highlighter.error(text) + const warnResult = highlighter.warn(text) + const infoResult = highlighter.info(text) + const successResult = highlighter.success(text) + + // All results should be strings + expect(typeof errorResult).toBe("string") + expect(typeof warnResult).toBe("string") + expect(typeof infoResult).toBe("string") + expect(typeof successResult).toBe("string") + + // All results should have the same length as input + expect(errorResult.length).toBe(text.length) + expect(warnResult.length).toBe(text.length) + expect(infoResult.length).toBe(text.length) + expect(successResult.length).toBe(text.length) + } catch (error) { + console.error(`Failed with text: ${text}`, error) + throw error + } + } + }) + + test("should handle edge cases", () => { + const edgeCases = [ + "", // Empty string + " ", // Single space + " ", // Multiple spaces + "0", // Zero as string + "null", // "null" as string + "undefined", // "undefined" as string + "NaN", // "NaN" as string + "true", // "true" as string + "false", // "false" as string + "[]", // Empty array as string + "{}", // Empty object as string + ] + + for (const text of edgeCases) { + try { + // Test each highlighter function + const errorResult = highlighter.error(text) + const warnResult = highlighter.warn(text) + const infoResult = highlighter.info(text) + const successResult = highlighter.success(text) + + // All results should be strings + expect(typeof errorResult).toBe("string") + expect(typeof warnResult).toBe("string") + expect(typeof infoResult).toBe("string") + expect(typeof successResult).toBe("string") + + // All results should have the same length as input + expect(errorResult.length).toBe(text.length) + expect(warnResult.length).toBe(text.length) + expect(infoResult.length).toBe(text.length) + expect(successResult.length).toBe(text.length) + } catch (error) { + console.error(`Failed with edge case: ${text}`, error) + throw error + } + } + }) +}) diff --git a/packages/shadcn/test/fuzz-utils.ts b/packages/shadcn/test/fuzz-utils.ts new file mode 100644 index 0000000000..992ff41062 --- /dev/null +++ b/packages/shadcn/test/fuzz-utils.ts @@ -0,0 +1,16 @@ +export const generateRandomString = (length: number): string => { + const chars = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=[]{}|;:,.<>?" + return Array.from( + { length }, + () => chars[Math.floor(Math.random() * chars.length)] + ).join("") +} + +export const generateRandomPath = (): string => { + const segments = Array.from( + { length: Math.floor(Math.random() * 5) + 1 }, + () => generateRandomString(Math.floor(Math.random() * 10) + 1) + ) + return segments.join("/") +} diff --git a/packages/shadcn/test/utils/resolve-import.test.ts b/packages/shadcn/test/utils/resolve-import.test.ts index dce1dc9e96..58ca0517cb 100644 --- a/packages/shadcn/test/utils/resolve-import.test.ts +++ b/packages/shadcn/test/utils/resolve-import.test.ts @@ -1,8 +1,9 @@ import path from "path" import { loadConfig, type ConfigLoaderSuccessResult } from "tsconfig-paths" -import { expect, test } from "vitest" +import { describe, expect, test } from "vitest" import { resolveImport } from "../../src/utils/resolve-import" +import { generateRandomPath } from "../fuzz-utils" test("resolve import", async () => { expect( @@ -79,3 +80,88 @@ test("resolve import without base url", async () => { path.resolve(cwd, "foo/bar") ) }) + +describe("fuzzing", () => { + test("should handle various import paths", async () => { + const generateRandomConfig = (): Pick< + ConfigLoaderSuccessResult, + "absoluteBaseUrl" | "paths" + > => ({ + absoluteBaseUrl: generateRandomPath(), + paths: { + "@/*": [generateRandomPath()], + "@/components/*": [generateRandomPath()], + "@/lib/*": [generateRandomPath()], + }, + }) + + const testCases = Array.from({ length: 100 }, () => ({ + importPath: generateRandomPath(), + config: generateRandomConfig(), + })) + + for (const { importPath, config } of testCases) { + try { + const result = await resolveImport(importPath, config) + // Should either return undefined or a valid path + if (result) { + expect(typeof result).toBe("string") + expect(result.length).toBeGreaterThan(0) + } + } catch (error) { + // Expected for invalid paths + expect(error).toBeDefined() + } + } + }) + + test("should handle edge cases", async () => { + const edgeCases: Array<{ + importPath: string + config: Pick + }> = [ + { + importPath: "", + config: { + absoluteBaseUrl: "", + paths: { + "@/*": [""], + }, + }, + }, + { + importPath: "/", + config: { + absoluteBaseUrl: "/", + paths: { + "@/*": ["/"], + }, + }, + }, + { + importPath: "@/components/button", + config: { + absoluteBaseUrl: "/", + paths: { + "@/*": ["/"], + "@/components/*": ["/components"], + }, + }, + }, + ] + + for (const { importPath, config } of edgeCases) { + try { + const result = await resolveImport(importPath, config) + // Should either return undefined or a valid path + if (result) { + expect(typeof result).toBe("string") + expect(result.length).toBeGreaterThan(0) + } + } catch (error) { + console.error(`Failed with edge case:`, { importPath, config }, error) + throw error + } + } + }) +})