Jelajahi Sumber

Run UI unit tests in CI (#25792)

Luke Parker 3 bulan lalu
induk
melakukan
ca2411d332

+ 2 - 0
packages/ui/package.json

@@ -25,6 +25,8 @@
   },
   },
   "scripts": {
   "scripts": {
     "typecheck": "tsgo --noEmit",
     "typecheck": "tsgo --noEmit",
+    "test": "bun test src",
+    "test:ci": "mkdir -p .artifacts/unit && bun test src --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml",
     "dev": "vite",
     "dev": "vite",
     "generate:tailwind": "bun run script/tailwind.ts"
     "generate:tailwind": "bun run script/tailwind.ts"
   },
   },

+ 15 - 0
packages/ui/src/components/session-diff.test.ts

@@ -19,6 +19,21 @@ describe("session diff", () => {
     expect(text(view, "additions")).toBe("one\nthree\n")
     expect(text(view, "additions")).toBe("one\nthree\n")
   })
   })
 
 
+  test("keeps missing final newlines from unified patches", () => {
+    const diff = {
+      file: "a.ts",
+      patch:
+        "Index: a.ts\n===================================================================\n--- a.ts\t\n+++ a.ts\t\n@@ -1,2 +1,2 @@\n one\n-two\n\\ No newline at end of file\n+three\n\\ No newline at end of file\n",
+      additions: 1,
+      deletions: 1,
+      status: "modified" as const,
+    }
+    const view = normalize(diff)
+
+    expect(text(view, "deletions")).toBe("one\ntwo")
+    expect(text(view, "additions")).toBe("one\nthree")
+  })
+
   test("converts legacy content into a patch", () => {
   test("converts legacy content into a patch", () => {
     const diff = {
     const diff = {
       file: "a.ts",
       file: "a.ts",

+ 27 - 7
packages/ui/src/components/session-diff.ts

@@ -29,24 +29,44 @@ function patch(diff: ReviewDiff) {
   if (typeof diff.patch === "string") {
   if (typeof diff.patch === "string") {
     try {
     try {
       const [patch] = parsePatch(diff.patch)
       const [patch] = parsePatch(diff.patch)
-      const beforeLines = []
-      const afterLines = []
+      const beforeLines: Array<{ text: string; newline: boolean }> = []
+      const afterLines: Array<{ text: string; newline: boolean }> = []
+      let previous: "-" | "+" | " " | undefined
 
 
       for (const hunk of patch.hunks) {
       for (const hunk of patch.hunks) {
         for (const line of hunk.lines) {
         for (const line of hunk.lines) {
+          if (line.startsWith("\\")) {
+            if (previous === "-" || previous === " ") {
+              const before = beforeLines.at(-1)
+              if (before) before.newline = false
+            }
+            if (previous === "+" || previous === " ") {
+              const after = afterLines.at(-1)
+              if (after) after.newline = false
+            }
+            continue
+          }
+
           if (line.startsWith("-")) {
           if (line.startsWith("-")) {
-            beforeLines.push(line.slice(1))
+            beforeLines.push({ text: line.slice(1), newline: true })
+            previous = "-"
           } else if (line.startsWith("+")) {
           } else if (line.startsWith("+")) {
-            afterLines.push(line.slice(1))
+            afterLines.push({ text: line.slice(1), newline: true })
+            previous = "+"
           } else {
           } else {
             // context line (starts with ' ')
             // context line (starts with ' ')
-            beforeLines.push(line.slice(1))
-            afterLines.push(line.slice(1))
+            beforeLines.push({ text: line.slice(1), newline: true })
+            afterLines.push({ text: line.slice(1), newline: true })
+            previous = " "
           }
           }
         }
         }
       }
       }
 
 
-      return { before: beforeLines.join("\n"), after: afterLines.join("\n"), patch: diff.patch }
+      return {
+        before: beforeLines.map((line) => line.text + (line.newline ? "\n" : "")).join(""),
+        after: afterLines.map((line) => line.text + (line.newline ? "\n" : "")).join(""),
+        patch: diff.patch,
+      }
     } catch {
     } catch {
       return { before: "", after: "", patch: diff.patch }
       return { before: "", after: "", patch: diff.patch }
     }
     }

+ 9 - 0
turbo.json

@@ -26,6 +26,15 @@
       "dependsOn": ["^build"],
       "dependsOn": ["^build"],
       "outputs": [".artifacts/unit/junit.xml"],
       "outputs": [".artifacts/unit/junit.xml"],
       "passThroughEnv": ["*"]
       "passThroughEnv": ["*"]
+    },
+    "@opencode-ai/ui#test": {
+      "dependsOn": ["^build"],
+      "outputs": []
+    },
+    "@opencode-ai/ui#test:ci": {
+      "dependsOn": ["^build"],
+      "outputs": [".artifacts/unit/junit.xml"],
+      "passThroughEnv": ["*"]
     }
     }
   }
   }
 }
 }