ソースを参照

test(app): windows e2e

Adam 1 ヶ月 前
コミット
1ac0980c80
2 ファイル変更93 行追加7 行削除
  1. 26 7
      .github/workflows/test.yml
  2. 67 0
      packages/app/e2e/prompt.spec.ts

+ 26 - 7
.github/workflows/test.yml

@@ -8,7 +8,29 @@ on:
   workflow_dispatch:
 jobs:
   test:
-    runs-on: blacksmith-4vcpu-ubuntu-2404
+    name: test (${{ matrix.settings.name }})
+    strategy:
+      fail-fast: false
+      matrix:
+        settings:
+          - name: linux
+            host: blacksmith-4vcpu-ubuntu-2404
+            playwright: bunx playwright install --with-deps
+            workdir: .
+            command: |
+              git config --global user.email "[email protected]"
+              git config --global user.name "opencode"
+              bun turbo typecheck
+              bun turbo test
+          - name: windows
+            host: windows-latest
+            playwright: bunx playwright install
+            workdir: packages/app
+            command: bun test:e2e
+    runs-on: ${{ matrix.settings.host }}
+    defaults:
+      run:
+        shell: bash
     steps:
       - name: Checkout repository
         uses: actions/checkout@v4
@@ -20,7 +42,7 @@ jobs:
 
       - name: Install Playwright browsers
         working-directory: packages/app
-        run: bunx playwright install --with-deps
+        run: ${{ matrix.settings.playwright }}
 
       - name: Seed opencode data
         working-directory: packages/opencode
@@ -67,11 +89,8 @@ jobs:
           exit 1
 
       - name: run
-        run: |
-          git config --global user.email "[email protected]"
-          git config --global user.name "opencode"
-          bun turbo typecheck
-          bun turbo test
+        working-directory: ${{ matrix.settings.workdir }}
+        run: ${{ matrix.settings.command }}
         env:
           CI: true
           MODELS_DEV_API_JSON: ${{ github.workspace }}/packages/opencode/test/tool/fixtures/models-api.json

+ 67 - 0
packages/app/e2e/prompt.spec.ts

@@ -0,0 +1,67 @@
+import { test, expect } from "./fixtures"
+import { promptSelector } from "./utils"
+
+function sessionIDFromUrl(url: string) {
+  const match = /\/session\/([^/?#]+)/.exec(url)
+  return match?.[1]
+}
+
+test("can send a prompt and receive a reply", async ({ page, sdk, gotoSession }) => {
+  test.setTimeout(120_000)
+
+  const pageErrors: string[] = []
+  const onPageError = (err: Error) => {
+    pageErrors.push(err.message)
+  }
+  page.on("pageerror", onPageError)
+
+  await gotoSession()
+
+  const token = `E2E_OK_${Date.now()}`
+
+  const prompt = page.locator(promptSelector)
+  await prompt.click()
+  await page.keyboard.type(`Reply with exactly: ${token}`)
+  await page.keyboard.press("Enter")
+
+  await expect(page).toHaveURL(/\/session\/[^/?#]+/, { timeout: 30_000 })
+
+  const sessionID = (() => {
+    const id = sessionIDFromUrl(page.url())
+    if (!id) throw new Error(`Failed to parse session id from url: ${page.url()}`)
+    return id
+  })()
+
+  try {
+    await expect
+      .poll(
+        async () => {
+          const messages = await sdk.session.messages({ sessionID, limit: 50 }).then((r) => r.data ?? [])
+          const assistant = messages
+            .slice()
+            .reverse()
+            .find((m) => m.info.role === "assistant")
+
+          return (
+            assistant?.parts
+              .filter((p) => p.type === "text")
+              .map((p) => p.text)
+              .join("\n") ?? ""
+          )
+        },
+        { timeout: 90_000 },
+      )
+
+      .toContain(token)
+
+    const reply = page.locator('[data-component="text-part"]').filter({ hasText: token }).first()
+    await expect(reply).toBeVisible({ timeout: 90_000 })
+  } finally {
+    page.off("pageerror", onPageError)
+    await sdk.session.delete({ sessionID }).catch(() => undefined)
+  }
+
+  if (pageErrors.length > 0) {
+    throw new Error(`Page error(s):\n${pageErrors.join("\n")}`)
+  }
+})