From 326665c6072fa3516e2eed5ea0576befdb4df7b3 Mon Sep 17 00:00:00 2001 From: Tyler <26290074+tylersayshi@users.noreply.github.com> Date: Sat, 9 Aug 2025 13:30:30 -0700 Subject: [PATCH] refactor tests to be assertEquals with expected output next step is to get comment preservation working --- .gitignore | 2 + __snapshots__/main_test.ts.snap | 555 -------------------------------- deno.lock | 1 + main_test.ts | 510 +++++++++++++++++++++++++---- sample_converted.ts | 116 ------- 5 files changed, 447 insertions(+), 737 deletions(-) create mode 100644 .gitignore delete mode 100644 __snapshots__/main_test.ts.snap delete mode 100644 sample_converted.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1586e95 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +tmp + diff --git a/__snapshots__/main_test.ts.snap b/__snapshots__/main_test.ts.snap deleted file mode 100644 index 6c15441..0000000 --- a/__snapshots__/main_test.ts.snap +++ /dev/null @@ -1,555 +0,0 @@ -export const snapshot = {}; - -snapshot[`converts string enum to object 1`] = ` -'const Color = { - Red: "red", - Blue: "blue", - Green: "green" -} as const; -type ColorType = typeof Color[keyof typeof Color]; - -function getColorName(color: ColorType): string { - return color === Color.Red ? "Red color" : "Other color"; -} - -const userColor: ColorType = Color.Blue;' -`; - -snapshot[`converts numeric enum to object 1`] = ` -"const Status = { - Pending: 0, - Active: 1, - Inactive: 2 -} as const; -type StatusType = typeof Status[keyof typeof Status]; - -interface User { - name: string; - status: StatusType; -} - -function createUser(name: string): User { - return { - name, - status: Status.Pending - }; -}" -`; - -snapshot[`converts auto-incrementing enum to object 1`] = ` -"const Direction = { - North: 0, - South: 1, - East: 2, - West: 3 -} as const; -type DirectionType = typeof Direction[keyof typeof Direction]; - -const compass: DirectionType[] = [ - Direction.North, - Direction.South, - Direction.East, - Direction.West -];" -`; - -snapshot[`converts mixed value enum to object 1`] = ` -'const MixedEnum = { - First: 1, - Second: "second", - Third: 3, - Fourth: "fourth" -} as const; -type MixedEnumType = typeof MixedEnum[keyof typeof MixedEnum]; - -function handleMixed(value: MixedEnumType): string { - switch (value) { - case MixedEnum.First: - return "Number one"; - case MixedEnum.Second: - return "String second"; - case MixedEnum.Third: - return "Number three"; - case MixedEnum.Fourth: - return "String fourth"; - default: - return "Unknown"; - } -}' -`; - -snapshot[`preserves enum usage in complex scenarios 1`] = ` -'const ApiEndpoint = { - Users: "/api/users", - Posts: "/api/posts", - Comments: "/api/comments" -} as const; -type ApiEndpointType = typeof ApiEndpoint[keyof typeof ApiEndpoint]; - -const HttpMethod = { - GET: "GET", - POST: "POST", - PUT: "PUT", - DELETE: "DELETE" -} as const; -type HttpMethodType = typeof HttpMethod[keyof typeof HttpMethod]; - -const ResponseStatus = { - Success: 200, - NotFound: 404, - ServerError: 500 -} as const; -type ResponseStatusType = typeof ResponseStatus[keyof typeof ResponseStatus]; - -interface ApiRequest { - endpoint: ApiEndpointType; - method: HttpMethodType; -} - -interface ApiResponse { - status: ResponseStatusType; - data?: T; - error?: string; -} - -class ApiClient { - private baseUrl: string; - - constructor(baseUrl: string) { - this.baseUrl = baseUrl; - } - - async makeRequest( - endpoint: ApiEndpointType, - method: HttpMethodType = HttpMethod.GET - ): Promise> { - const url = \`\${this.baseUrl}\${endpoint}\`; - - try { - const response = await fetch(url, { method }); - - if (response.ok) { - const data = await response.json(); - return { - status: ResponseStatus.Success, - data - }; - } else { - return { - status: response.status === 404 ? ResponseStatus.NotFound : ResponseStatus.ServerError, - error: \`Request failed with status \${response.status}\` - }; - } - } catch (error) { - return { - status: ResponseStatus.ServerError, - error: error.message - }; - } - } -} - -// Usage examples -const client = new ApiClient("https://api.example.com"); -const usersRequest: ApiRequest = { - endpoint: ApiEndpoint.Users, - method: HttpMethod.GET -};' -`; - -snapshot[`handles const enum conversion 1`] = ` -'const LogLevel = { - Debug: 0, - Info: 1, - Warn: 2, - Error: 3 -} as const; -type LogLevelType = typeof LogLevel[keyof typeof LogLevel]; - -function log(level: LogLevelType, message: string): void { - if (level >= LogLevel.Info) { - console.log(\`[\${LogLevel[level]}] \${message}\`); - } -} - -log(LogLevel.Error, "Something went wrong");' -`; - -snapshot[`preserves comments and formatting context 1`] = ` -'const UserRole = { - User: "user", - Moderator: "moderator", - Admin: "admin" -} as const; -type UserRoleType = typeof UserRole[keyof typeof UserRole]; - -// Default role for new users -const DEFAULT_ROLE: UserRoleType = UserRole.User; - -/* - * Permission check function - */ -function hasPermission(role: UserRoleType, action: string): boolean { - switch (role) { - case UserRole.Admin: - return true; // Admin can do everything - case UserRole.Moderator: - return action !== "delete_user"; - case UserRole.User: - return action === "read"; - default: - return false; - } -}' -`; - -snapshot[`comprehensive enum transformation - full sample 1`] = ` -'const Color = { - Red: "red", - Green: "green", - Blue: "blue" -} as const; -type ColorType = typeof Color[keyof typeof Color]; - -const Status = { - Pending: 0, - Active: 1, - Inactive: 2 -} as const; -type StatusType = typeof Status[keyof typeof Status]; - -const Direction = { - North: "NORTH", - South: "SOUTH", - East: "EAST", - West: "WEST" -} as const; -type DirectionType = typeof Direction[keyof typeof Direction]; - -const Priority = { - Low: 0, - Medium: 1, - High: 2, - Critical: "CRITICAL" -} as const; -type PriorityType = typeof Priority[keyof typeof Priority]; - -function _getColorName(color: ColorType): string { - return color; -} - -function _getStatusText(status: StatusType): string { - switch (status) { - case Status.Pending: - return "Pending"; - case Status.Active: - return "Active"; - case Status.Inactive: - return "Inactive"; - default: - return "Unknown"; - } -} - -interface User { - id: number; - name: string; - status: StatusType; - favoriteColor: ColorType; -} - -class Navigation { - private currentDirection: DirectionType = Direction.North; - - turnLeft(): void { - switch (this.currentDirection) { - case Direction.North: - this.currentDirection = Direction.West; - break; - case Direction.West: - this.currentDirection = Direction.South; - break; - case Direction.South: - this.currentDirection = Direction.East; - break; - case Direction.East: - this.currentDirection = Direction.North; - break; - } - } - - getDirection(): DirectionType { - return this.currentDirection; - } -} - -type TaskPriority = Extract | Extract | Extract; - -function processTask(priority: TaskPriority): void { - console.log(\`Processing task with priority: \${priority}\`); -} - -function _handleLowPriority(p: Extract): void { - console.log(\`Handling low priority task: \${p}\`); -} - -const _allColors: ColorType[] = [Color.Red, Color.Green, Color.Blue]; - -const _colorMap: Record = { - [Color.Red]: "#FF0000", - [Color.Green]: "#00FF00", - [Color.Blue]: "#0000FF", -}; - -function _createEnumArray(enumObj: Record): T[] { - return Object.values(enumObj); -} - -const _user: User = { - id: 1, - name: "John", - status: Status.Active, - favoriteColor: Color.Blue, -}; - -const navigation = new Navigation(); -navigation.turnLeft(); -console.log(navigation.getDirection()); - -processTask(Priority.High); - -export { Color, Status, Direction, Priority }; -export type { User, TaskPriority };' -`; - -snapshot[`enum usage in destructuring and object patterns 1`] = ` -'const Theme = { - Light: "light", - Dark: "dark", - Auto: "auto" -} as const; -type ThemeType = typeof Theme[keyof typeof Theme]; - -const Size = { - Small: "sm", - Medium: "md", - Large: "lg" -} as const; -type SizeType = typeof Size[keyof typeof Size]; - -interface ComponentProps { - theme: ThemeType; - size: SizeType; - disabled?: boolean; -} - -function createComponent({ - theme = Theme.Light, - size = Size.Medium, - disabled = false -}: Partial = {}): ComponentProps { - return { theme, size, disabled }; -} - -const config = { - defaultTheme: Theme.Dark, - availableSizes: [Size.Small, Size.Medium, Size.Large], - themeColors: { - [Theme.Light]: "#ffffff", - [Theme.Dark]: "#000000", - [Theme.Auto]: "inherit" - } -}; - -const { defaultTheme, availableSizes } = config; - -function handleThemeChange(newTheme: ThemeType): void { - const themes: Record void> = { - [Theme.Light]: () => console.log("Switching to light theme"), - [Theme.Dark]: () => console.log("Switching to dark theme"), - [Theme.Auto]: () => console.log("Using system theme") - }; - - themes[newTheme]?.(); -}' -`; - -snapshot[`enum usage in template literals and conditionals 1`] = ` -"const LogLevel = { - Debug: 0, - Info: 1, - Warning: 2, - Error: 3 -} as const; -type LogLevelType = typeof LogLevel[keyof typeof LogLevel]; - -const Environment = { - Development: \\"dev\\", - Staging: \\"staging\\", - Production: \\"prod\\" -} as const; -type EnvironmentType = typeof Environment[keyof typeof Environment]; - -class Logger { - constructor( - private level: LogLevelType = LogLevel.Info, - private env: EnvironmentType = Environment.Development - ) {} - - log(level: LogLevelType, message: string): void { - if (level >= this.level) { - const prefix = \`[\${Environment[this.env] || this.env}][\${LogLevel[level]}]\`; - console.log(\`\${prefix} \${message}\`); - } - } - - debug(msg: string) { this.log(LogLevel.Debug, msg); } - info(msg: string) { this.log(LogLevel.Info, msg); } - warn(msg: string) { this.log(LogLevel.Warning, msg); } - error(msg: string) { this.log(LogLevel.Error, msg); } -} - -function getLogLevelName(level: LogLevelType): string { - return level === LogLevel.Debug ? \\"Debug Mode\\" : - level === LogLevel.Info ? \\"Information\\" : - level === LogLevel.Warning ? \\"Warning!\\" : - level === LogLevel.Error ? \\"ERROR!\\" : \\"Unknown\\"; -} - -const logger = new Logger( - LogLevel.Warning, - Environment.Production -); - -const isProduction = logger['env'] === Environment.Production; -const isDevelopment = logger['env'] === Environment.Development; - -logger.error(\`Critical error in \${Environment.Production} environment\`);" -`; - -snapshot[`enum usage with arrays, maps and complex data structures 1`] = ` -\`const HttpStatus = { - OK: 200, - Created: 201, - BadRequest: 400, - Unauthorized: 401, - NotFound: 404, - InternalServerError: 500 -} as const; -type HttpStatusType = typeof HttpStatus[keyof typeof HttpStatus]; - -const HttpMethod = { - GET: "GET", - POST: "POST", - PUT: "PUT", - DELETE: "DELETE", - PATCH: "PATCH" -} as const; -type HttpMethodType = typeof HttpMethod[keyof typeof HttpMethod]; - -type ApiEndpoint = { - path: string; - method: HttpMethodType; - expectedStatus: HttpStatusType[]; -}; - -const endpoints: Map = new Map([ - ["getUser", { - path: "/users/:id", - method: HttpMethod.GET, - expectedStatus: [HttpStatus.OK, HttpStatus.NotFound] - }], - ["createUser", { - path: "/users", - method: HttpMethod.POST, - expectedStatus: [HttpStatus.Created, HttpStatus.BadRequest] - }], - ["updateUser", { - path: "/users/:id", - method: HttpMethod.PUT, - expectedStatus: [HttpStatus.OK, HttpStatus.NotFound, HttpStatus.BadRequest] - }], - ["deleteUser", { - path: "/users/:id", - method: HttpMethod.DELETE, - expectedStatus: [HttpStatus.OK, HttpStatus.NotFound] - }] -]); - -const statusMessages: Record = { - [HttpStatus.OK]: "Request successful", - [HttpStatus.Created]: "Resource created successfully", - [HttpStatus.BadRequest]: "Invalid request data", - [HttpStatus.Unauthorized]: "Authentication required", - [HttpStatus.NotFound]: "Resource not found", - [HttpStatus.InternalServerError]: "Server error occurred" -}; - -const allowedMethods: Set = new Set([ - HttpMethod.GET, - HttpMethod.POST, - HttpMethod.PUT, - HttpMethod.DELETE -]); - -function validateResponse(status: HttpStatusType, method: HttpMethodType): boolean { - const successStatuses = [HttpStatus.OK, HttpStatus.Created]; - const errorStatuses = [ - HttpStatus.BadRequest, - HttpStatus.Unauthorized, - HttpStatus.NotFound, - HttpStatus.InternalServerError - ]; - - return [...successStatuses, ...errorStatuses].includes(status) && - allowedMethods.has(method); -} - -class ApiClient { - private requestHistory: Array<{ - method: HttpMethodType; - status: HttpStatusType; - timestamp: Date; - }> = []; - - private async makeRequest(method: HttpMethodType): Promise { - // Simulate API call - const statuses = Object.values(HttpStatus).filter( - (s): s is HttpStatusType => typeof s === 'number' - ); - const randomStatus = statuses[Math.floor(Math.random() * statuses.length)]; - - this.requestHistory.push({ - method, - status: randomStatus, - timestamp: new Date() - }); - - return randomStatus; - } - - async get(): Promise { - return this.makeRequest(HttpMethod.GET); - } - - async post(): Promise { - return this.makeRequest(HttpMethod.POST); - } - - getRequestStats(): Record { - const stats: Record = { - [HttpMethod.GET]: 0, - [HttpMethod.POST]: 0, - [HttpMethod.PUT]: 0, - [HttpMethod.DELETE]: 0, - [HttpMethod.PATCH]: 0 - }; - - for (const request of this.requestHistory) { - stats[request.method]++; - } - - return stats; - } -}\` -`; diff --git a/deno.lock b/deno.lock index 22bd8c4..ad6600d 100644 --- a/deno.lock +++ b/deno.lock @@ -2,6 +2,7 @@ "version": "5", "specifiers": { "jsr:@david/code-block-writer@13": "13.0.3", + "jsr:@std/assert@*": "1.0.13", "jsr:@std/assert@1": "1.0.13", "jsr:@std/assert@^1.0.13": "1.0.13", "jsr:@std/fs@1": "1.0.19", diff --git a/main_test.ts b/main_test.ts index 38983b4..197f632 100644 --- a/main_test.ts +++ b/main_test.ts @@ -1,6 +1,6 @@ -import { assertSnapshot } from "jsr:@std/testing/snapshot"; import { runCodemod } from "./main.ts"; import { spawn } from "node:child_process"; +import { assertEquals } from "jsr:@std/assert"; /** * Checks a TypeScript file using tsc. @@ -9,9 +9,9 @@ import { spawn } from "node:child_process"; */ async function checkTsFile(filePath: string): Promise { await new Promise((resolve, reject) => { - const tsc = spawn("tsc", ["--noEmit", "--target", "es2022", filePath]); + const tsc = spawn("deno", ["check", filePath]); - tsc.stdout.on("data", (data) => { + tsc.stderr.on("data", (data) => { console.error(data.toString()); }); @@ -61,7 +61,7 @@ class TestUtils { } } -Deno.test("converts string enum to object", async (t) => { +Deno.test("converts string enum to object", async () => { const utils = new TestUtils(); const testFile = "string_enum_test.ts"; @@ -78,16 +78,29 @@ function getColorName(color: Color): string { const userColor: Color = Color.Blue;`; + const expectedOutput = `const Color = { + Red: "red", + Blue: "blue", + Green: "green" +} as const; +type ColorType = typeof Color[keyof typeof Color]; + +function getColorName(color: ColorType): string { + return color === Color.Red ? "Red color" : "Other color"; +} + +const userColor: ColorType = Color.Blue;`; + await utils.createTestFile(testFile, originalContent); const result = await utils.runCodemodAndSnapshot(testFile); await checkTsFile(testFile); - await assertSnapshot(t, result); + assertEquals(result, expectedOutput); } finally { await utils.cleanup(); } }); -Deno.test("converts numeric enum to object", async (t) => { +Deno.test("converts numeric enum to object", async () => { const utils = new TestUtils(); const testFile = "numeric_enum_test.ts"; @@ -103,6 +116,25 @@ interface User { status: Status; } +function createUser(name: string): User { + return { + name, + status: Status.Pending + }; +}`; + + const expectedOutput = `const Status = { + Pending: 0, + Active: 1, + Inactive: 2 +} as const; +type StatusType = typeof Status[keyof typeof Status]; + +interface User { + name: string; + status: StatusType; +} + function createUser(name: string): User { return { name, @@ -113,13 +145,13 @@ function createUser(name: string): User { await utils.createTestFile(testFile, originalContent); const result = await utils.runCodemodAndSnapshot(testFile); await checkTsFile(testFile); - await assertSnapshot(t, result); + assertEquals(result, expectedOutput); } finally { await utils.cleanup(); } }); -Deno.test("converts auto-incrementing enum to object", async (t) => { +Deno.test("converts auto-incrementing enum to object", async () => { const utils = new TestUtils(); const testFile = "auto_enum_test.ts"; @@ -138,16 +170,31 @@ const compass: Direction[] = [ Direction.West ];`; + const expectedOutput = `const Direction = { + North: 0, + South: 1, + East: 2, + West: 3 +} as const; +type DirectionType = typeof Direction[keyof typeof Direction]; + +const compass: DirectionType[] = [ + Direction.North, + Direction.South, + Direction.East, + Direction.West +];`; + await utils.createTestFile(testFile, originalContent); const result = await utils.runCodemodAndSnapshot(testFile); await checkTsFile(testFile); - await assertSnapshot(t, result); + assertEquals(result, expectedOutput); } finally { await utils.cleanup(); } }); -Deno.test("converts mixed value enum to object", async (t) => { +Deno.test("converts mixed value enum to object", async () => { const utils = new TestUtils(); const testFile = "mixed_enum_test.ts"; @@ -174,16 +221,39 @@ function handleMixed(value: MixedEnum): string { } }`; + const expectedOutput = `const MixedEnum = { + First: 1, + Second: "second", + Third: 3, + Fourth: "fourth" +} as const; +type MixedEnumType = typeof MixedEnum[keyof typeof MixedEnum]; + +function handleMixed(value: MixedEnumType): string { + switch (value) { + case MixedEnum.First: + return "Number one"; + case MixedEnum.Second: + return "String second"; + case MixedEnum.Third: + return "Number three"; + case MixedEnum.Fourth: + return "String fourth"; + default: + return "Unknown"; + } +}`; + await utils.createTestFile(testFile, originalContent); const result = await utils.runCodemodAndSnapshot(testFile); await checkTsFile(testFile); - await assertSnapshot(t, result); + assertEquals(result, expectedOutput); } finally { await utils.cleanup(); } }); -Deno.test("converts multiple enums in single file", async (t) => { +Deno.test("converts multiple enums in single file", async () => { const utils = new TestUtils(); const testFile = "multiple_enums_test.ts"; @@ -224,7 +294,52 @@ class TaskManager { addTask(color: Color = Color.Blue): void { this.tasks.push({ color, - status: Status.Pending, + status: Status.Inactive, + priority: Priority.Medium + }); + } +}`; + + const expectedOutput = `const Color = { + Red: "red", + Blue: "blue" +} as const; +type ColorType = typeof Color[keyof typeof Color]; + +const Status = { + Active: 1, + Inactive: 0 +} as const; +type StatusType = typeof Status[keyof typeof Status]; + +const Priority = { + Low: 0, + Medium: 1, + High: 2 +} as const; +type PriorityType = typeof Priority[keyof typeof Priority]; + +interface Task { + color: ColorType; + status: StatusType; + priority: PriorityType; +} + +function createTask(): Task { + return { + color: Color.Red, + status: Status.Active, + priority: Priority.High + }; +} + +class TaskManager { + private tasks: Task[] = []; + + addTask(color: ColorType = Color.Blue): void { + this.tasks.push({ + color, + status: Status.Inactive, priority: Priority.Medium }); } @@ -233,13 +348,13 @@ class TaskManager { await utils.createTestFile(testFile, originalContent); const result = await utils.runCodemodAndSnapshot(testFile); await checkTsFile(testFile); - await assertSnapshot(t, result); + assertEquals(result, expectedOutput); } finally { await utils.cleanup(); } }); -Deno.test("preserves enum usage in complex scenarios", async (t) => { +Deno.test("preserves enum usage in complex scenarios", async () => { const utils = new TestUtils(); const testFile = "complex_usage_test.ts"; @@ -302,7 +417,84 @@ class ApiClient { error: \`Request failed with status \${response.status}\` }; } - } catch (error) { + } catch (error: any) { + return { + status: ResponseStatus.ServerError, + error: error.message + }; + } + } +} + +// Usage examples +const client = new ApiClient("https://api.example.com"); +const usersRequest: ApiRequest = { + endpoint: ApiEndpoint.Users, + method: HttpMethod.GET +};`; + + const expectedOutput = `const ApiEndpoint = { + Users: "/api/users", + Posts: "/api/posts", + Comments: "/api/comments" +} as const; +type ApiEndpointType = typeof ApiEndpoint[keyof typeof ApiEndpoint]; + +const HttpMethod = { + GET: "GET", + POST: "POST", + PUT: "PUT", + DELETE: "DELETE" +} as const; +type HttpMethodType = typeof HttpMethod[keyof typeof HttpMethod]; + +const ResponseStatus = { + Success: 200, + NotFound: 404, + ServerError: 500 +} as const; +type ResponseStatusType = typeof ResponseStatus[keyof typeof ResponseStatus]; + +interface ApiRequest { + endpoint: ApiEndpointType; + method: HttpMethodType; +} + +interface ApiResponse { + status: ResponseStatusType; + data?: T; + error?: string; +} + +class ApiClient { + private baseUrl: string; + + constructor(baseUrl: string) { + this.baseUrl = baseUrl; + } + + async makeRequest( + endpoint: ApiEndpointType, + method: HttpMethodType = HttpMethod.GET + ): Promise> { + const url = \`\${this.baseUrl}\${endpoint}\`; + + try { + const response = await fetch(url, { method }); + + if (response.ok) { + const data = await response.json(); + return { + status: ResponseStatus.Success, + data + }; + } else { + return { + status: response.status === 404 ? ResponseStatus.NotFound : ResponseStatus.ServerError, + error: \`Request failed with status \${response.status}\` + }; + } + } catch (error: any) { return { status: ResponseStatus.ServerError, error: error.message @@ -321,13 +513,13 @@ const usersRequest: ApiRequest = { await utils.createTestFile(testFile, originalContent); const result = await utils.runCodemodAndSnapshot(testFile); await checkTsFile(testFile); - await assertSnapshot(t, result); + assertEquals(result, expectedOutput); } finally { await utils.cleanup(); } }); -Deno.test("handles const enum conversion", async (t) => { +Deno.test("handles const enum conversion", async () => { const utils = new TestUtils(); const testFile = "const_enum_test.ts"; @@ -341,7 +533,23 @@ Deno.test("handles const enum conversion", async (t) => { function log(level: LogLevel, message: string): void { if (level >= LogLevel.Info) { - console.log(\`[\${LogLevel[level]}] \${message}\`); + console.log(\`[\${level}] \${message}\`); + } +} + +log(LogLevel.Error, "Something went wrong");`; + + const expectedOutput = `const LogLevel = { + Debug: 0, + Info: 1, + Warn: 2, + Error: 3 +} as const; +type LogLevelType = typeof LogLevel[keyof typeof LogLevel]; + +function log(level: LogLevelType, message: string): void { + if (level >= LogLevel.Info) { + console.log(\`[\${level}] \${message}\`); } } @@ -350,13 +558,13 @@ log(LogLevel.Error, "Something went wrong");`; await utils.createTestFile(testFile, originalContent); const result = await utils.runCodemodAndSnapshot(testFile); await checkTsFile(testFile); - await assertSnapshot(t, result); + assertEquals(result, expectedOutput); } finally { await utils.cleanup(); } }); -Deno.test("preserves comments and formatting context", async (t) => { +Deno.test("preserves comments", async () => { const utils = new TestUtils(); const testFile = "comments_test.ts"; @@ -392,17 +600,49 @@ function hasPermission(role: UserRole, action: string): boolean { } }`; + const expectedOutput = `/** + * Represents different user roles in the system + */ +const UserRole = { + /** Regular user with basic permissions */ + User: "user", + /** Moderator with elevated permissions */ + Moderator: "moderator", + /** Administrator with full access */ + Admin: "admin" +} as const; +type UserRoleType = typeof UserRole[keyof typeof UserRole]; + +// Default role for new users +const DEFAULT_ROLE: UserRoleType = UserRole.User; + +/* + * Permission check function + */ +function hasPermission(role: UserRoleType, action: string): boolean { + switch (role) { + case UserRole.Admin: + return true; // Admin can do everything + case UserRole.Moderator: + return action !== "delete_user"; + case UserRole.User: + return action === "read"; + default: + return false; + } +}`; + await utils.createTestFile(testFile, originalContent); const result = await utils.runCodemodAndSnapshot(testFile); await checkTsFile(testFile); - await assertSnapshot(t, result); + assertEquals(result, expectedOutput); } finally { await utils.cleanup(); } }); // TODO enable if computed needs to be supported -Deno.test.ignore("handles edge case with computed enum values", async (t) => { +Deno.test.ignore("handles edge case with computed enum values", async () => { const utils = new TestUtils(); const testFile = "computed_enum_test.ts"; @@ -433,18 +673,21 @@ function checkFileSize(size: number): string { const result = await utils.runCodemodAndSnapshot(testFile); console.log(result); await checkTsFile(testFile); - await assertSnapshot(t, result); + assertEquals(result, "todo"); } finally { await utils.cleanup(); } }); -Deno.test("comprehensive enum transformation - full sample", async (t) => { - const utils = new TestUtils(); - const testFile = "comprehensive_sample.ts"; +// TODO LAZINESS START +Deno.test.ignore( + "comprehensive enum transformation - full sample", + async () => { + const utils = new TestUtils(); + const testFile = "comprehensive_sample.ts"; - try { - const sampleContent = `enum Color { + try { + const sampleContent = `enum Color { Red = "red", Green = "green", Blue = "blue", @@ -557,21 +800,24 @@ processTask(Priority.High); export { Color, Status, Direction, Priority }; export type { User, TaskPriority };`; - await utils.createTestFile(testFile, sampleContent); - const result = await utils.runCodemodAndSnapshot(testFile); - await checkTsFile(testFile); - await assertSnapshot(t, result); - } finally { - await utils.cleanup(); + await utils.createTestFile(testFile, sampleContent); + const result = await utils.runCodemodAndSnapshot(testFile); + await checkTsFile(testFile); + assertEquals(result, "todo"); + } finally { + await utils.cleanup(); + } } -}); +); -Deno.test("enum usage in destructuring and object patterns", async (t) => { - const utils = new TestUtils(); - const testFile = "destructuring_test.ts"; +Deno.test.ignore( + "enum usage in destructuring and object patterns", + async () => { + const utils = new TestUtils(); + const testFile = "destructuring_test.ts"; - try { - const originalContent = `enum Theme { + try { + const originalContent = `enum Theme { Light = "light", Dark = "dark", Auto = "auto" @@ -619,21 +865,24 @@ function handleThemeChange(newTheme: Theme): void { themes[newTheme]?.(); }`; - await utils.createTestFile(testFile, originalContent); - const result = await utils.runCodemodAndSnapshot(testFile); - await checkTsFile(testFile); - await assertSnapshot(t, result); - } finally { - await utils.cleanup(); + await utils.createTestFile(testFile, originalContent); + const result = await utils.runCodemodAndSnapshot(testFile); + await checkTsFile(testFile); + assertEquals(result, "todo"); + } finally { + await utils.cleanup(); + } } -}); +); -Deno.test("enum usage in template literals and conditionals", async (t) => { - const utils = new TestUtils(); - const testFile = "templates_conditionals_test.ts"; +Deno.test.ignore( + "enum usage in template literals and conditionals", + async () => { + const utils = new TestUtils(); + const testFile = "templates_conditionals_test.ts"; - try { - const originalContent = `enum LogLevel { + try { + const originalContent = `enum LogLevel { Debug = 0, Info = 1, Warning = 2, @@ -682,16 +931,17 @@ const isDevelopment = logger['env'] === Environment.Development; logger.error(\`Critical error in \${Environment.Production} environment\`);`; - await utils.createTestFile(testFile, originalContent); - const result = await utils.runCodemodAndSnapshot(testFile); - await checkTsFile(testFile); - await assertSnapshot(t, result); - } finally { - await utils.cleanup(); + await utils.createTestFile(testFile, originalContent); + const result = await utils.runCodemodAndSnapshot(testFile); + await checkTsFile(testFile); + assertEquals(result, "todo"); + } finally { + await utils.cleanup(); + } } -}); +); -Deno.test("enum usage with generics and type constraints", async (t) => { +Deno.test.ignore("enum usage with generics and type constraints", async () => { const utils = new TestUtils(); const testFile = "generics_test.ts"; @@ -765,15 +1015,16 @@ function checkEntityType( await utils.createTestFile(testFile, originalContent); const result = await utils.runCodemodAndSnapshot(testFile); await checkTsFile(testFile); - await assertSnapshot(t, result); + assertEquals(result, "todo"); } finally { await utils.cleanup(); } }); +// TODO LAZINESS END Deno.test( "enum usage with arrays, maps and complex data structures", - async (t) => { + async () => { const utils = new TestUtils(); const testFile = "data_structures_test.ts"; @@ -897,6 +1148,131 @@ class ApiClient { stats[request.method]++; } + return stats; + } +}`; + + const expectedOutput = `const HttpStatus = { + OK: 200, + Created: 201, + BadRequest: 400, + Unauthorized: 401, + NotFound: 404, + InternalServerError: 500 +} as const; +type HttpStatusType = typeof HttpStatus[keyof typeof HttpStatus]; + +const HttpMethod = { + GET: "GET", + POST: "POST", + PUT: "PUT", + DELETE: "DELETE", + PATCH: "PATCH" +} as const; +type HttpMethodType = typeof HttpMethod[keyof typeof HttpMethod]; + +type ApiEndpoint = { + path: string; + method: HttpMethodType; + expectedStatus: HttpStatusType[]; +}; + +const endpoints: Map = new Map([ + ["getUser", { + path: "/users/:id", + method: HttpMethod.GET, + expectedStatus: [HttpStatus.OK, HttpStatus.NotFound] + }], + ["createUser", { + path: "/users", + method: HttpMethod.POST, + expectedStatus: [HttpStatus.Created, HttpStatus.BadRequest] + }], + ["updateUser", { + path: "/users/:id", + method: HttpMethod.PUT, + expectedStatus: [HttpStatus.OK, HttpStatus.NotFound, HttpStatus.BadRequest] + }], + ["deleteUser", { + path: "/users/:id", + method: HttpMethod.DELETE, + expectedStatus: [HttpStatus.OK, HttpStatus.NotFound] + }] +]); + +const statusMessages: Record = { + [HttpStatus.OK]: "Request successful", + [HttpStatus.Created]: "Resource created successfully", + [HttpStatus.BadRequest]: "Invalid request data", + [HttpStatus.Unauthorized]: "Authentication required", + [HttpStatus.NotFound]: "Resource not found", + [HttpStatus.InternalServerError]: "Server error occurred" +}; + +const allowedMethods: Set = new Set([ + HttpMethod.GET, + HttpMethod.POST, + HttpMethod.PUT, + HttpMethod.DELETE +]); + +function validateResponse(status: HttpStatusType, method: HttpMethodType): boolean { + const successStatuses = [HttpStatus.OK, HttpStatus.Created]; + const errorStatuses = [ + HttpStatus.BadRequest, + HttpStatus.Unauthorized, + HttpStatus.NotFound, + HttpStatus.InternalServerError + ]; + + return [...successStatuses, ...errorStatuses].includes(status) && + allowedMethods.has(method); +} + +class ApiClient { + private requestHistory: Array<{ + method: HttpMethodType; + status: HttpStatusType; + timestamp: Date; + }> = []; + + private async makeRequest(method: HttpMethodType): Promise { + // Simulate API call + const statuses = Object.values(HttpStatus).filter( + (s): s is HttpStatusType => typeof s === 'number' + ); + const randomStatus = statuses[Math.floor(Math.random() * statuses.length)]; + + this.requestHistory.push({ + method, + status: randomStatus, + timestamp: new Date() + }); + + return randomStatus; + } + + async get(): Promise { + return this.makeRequest(HttpMethod.GET); + } + + async post(): Promise { + return this.makeRequest(HttpMethod.POST); + } + + getRequestStats(): Record { + const stats: Record = { + [HttpMethod.GET]: 0, + [HttpMethod.POST]: 0, + [HttpMethod.PUT]: 0, + [HttpMethod.DELETE]: 0, + [HttpMethod.PATCH]: 0 + }; + + for (const request of this.requestHistory) { + stats[request.method]++; + } + return stats; } }`; @@ -904,14 +1280,14 @@ class ApiClient { await utils.createTestFile(testFile, originalContent); const result = await utils.runCodemodAndSnapshot(testFile); await checkTsFile(testFile); - await assertSnapshot(t, result); + assertEquals(result, expectedOutput); } finally { await utils.cleanup(); } } ); -Deno.test.ignore("const enum and computed enum values", async (t) => { +Deno.test.ignore("const enum and computed enum values", async () => { const utils = new TestUtils(); const testFile = "const_computed_enum_test.ts"; @@ -976,10 +1352,12 @@ const mathOperations = { [MathConstants.Eight]: (a: number) => a * 8 };`; + const expectedOutput = "todo"; + await utils.createTestFile(testFile, originalContent); const result = await utils.runCodemodAndSnapshot(testFile); await checkTsFile(testFile); - await assertSnapshot(t, result); + assertEquals(result, expectedOutput); } finally { await utils.cleanup(); } diff --git a/sample_converted.ts b/sample_converted.ts deleted file mode 100644 index a0ecfc0..0000000 --- a/sample_converted.ts +++ /dev/null @@ -1,116 +0,0 @@ -const Color = { - Red: "red", - Green: "green", - Blue: "blue" -} as const; -type ColorType = typeof Color[keyof typeof Color]; - -const Status = { - Pending: 0, - Active: 1, - Inactive: 2 -} as const; -type StatusType = typeof Status[keyof typeof Status]; - -const Direction = { - North: "NORTH", - South: "SOUTH", - East: "EAST", - West: "WEST" -} as const; -type DirectionType = typeof Direction[keyof typeof Direction]; - -const Priority = { - Low: 0, - Medium: 1, - High: 2, - Critical: "CRITICAL" -} as const; -type PriorityType = typeof Priority[keyof typeof Priority]; - -function _getColorName(color: ColorType): string { - return color; -} - -function _getStatusText(status: StatusType): string { - switch (status) { - case Status.Pending: - return "Pending"; - case Status.Active: - return "Active"; - case Status.Inactive: - return "Inactive"; - default: - return "Unknown"; - } -} - -interface User { - id: number; - name: string; - status: StatusType; - favoriteColor: ColorType; -} - -class Navigation { - private currentDirection: DirectionType = Direction.North; - - turnLeft(): void { - switch (this.currentDirection) { - case Direction.North: - this.currentDirection = Direction.West; - break; - case Direction.West: - this.currentDirection = Direction.South; - break; - case Direction.South: - this.currentDirection = Direction.East; - break; - case Direction.East: - this.currentDirection = Direction.North; - break; - } - } - - getDirection(): DirectionType { - return this.currentDirection; - } -} - -type TaskPriority = Extract | Extract | Extract; - -function processTask(priority: TaskPriority): void { - console.log(`Processing task with priority: ${priority}`); -} - -function _handleLowPriority(p: Extract): void { - console.log(`Handling low priority task: ${p}`); -} - -const _allColors: ColorType[] = [Color.Red, Color.Green, Color.Blue]; - -const _colorMap: Record = { - [Color.Red]: "#FF0000", - [Color.Green]: "#00FF00", - [Color.Blue]: "#0000FF", -}; - -function _createEnumArray(enumObj: Record): T[] { - return Object.values(enumObj); -} - -const _user: User = { - id: 1, - name: "John", - status: Status.Active, - favoriteColor: Color.Blue, -}; - -const navigation = new Navigation(); -navigation.turnLeft(); -console.log(navigation.getDirection()); - -processTask(Priority.High); - -export { Color, Status, Direction, Priority }; -export type { User, TaskPriority }; -- 2.51.2