Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "OpenCode Development Container",
"image": "mcr.microsoft.com/devcontainers/typescript-node:5-22-bookworm",
"features": {
"ghcr.io/devcontainers-extra/features/bun:1": {
"version": "1.3.14"
}
}
}
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ jobs:
- name: Setup Bun
uses: ./.github/actions/setup-bun

- name: Run updater action tests
timeout-minutes: 2
working-directory: packages/app
run: bun test --conditions=solid --preload ./happydom.ts ./src/components/updater-action.test.ts

- name: Configure git identity
run: |
git config --global user.email "bot@opencode.ai"
Expand Down
75 changes: 56 additions & 19 deletions packages/app/src/components/updater-action.test.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,63 @@
import { describe, expect, test } from "bun:test"
import type { UpdaterState } from "@/updater"
import { updaterAction } from "./updater-action"

describe("updaterAction", () => {
test("disables update actions when the platform has no updater", () => {
expect(updaterAction(undefined)).toEqual({ label: "settings.updates.action.checkNow" })
})
const cases: {
name: string
state: UpdaterState | undefined
expected: ReturnType<typeof updaterAction>
}[] = [
{
name: "uses check now without an action when state is unavailable",
state: undefined,
expected: { label: "settings.updates.action.checkNow" },
},
{
name: "uses check now without an action when updates are disabled",
state: { status: "disabled" },
expected: { label: "settings.updates.action.checkNow" },
},
{
name: "allows checking when the updater is idle",
state: { status: "idle" },
expected: { label: "settings.updates.action.checkNow", run: "check" },
},
{
name: "shows checking without an action while checking",
state: { status: "checking" },
expected: { label: "settings.updates.action.checking" },
},
{
name: "shows downloading without an action while downloading",
state: { status: "downloading", version: "1.2.3", percent: 50 },
expected: { label: "settings.updates.action.downloading" },
},
{
name: "allows installation when an update is ready",
state: { status: "ready", version: "1.2.3" },
expected: { label: "toast.update.action.installRestart", run: "install" },
},
{
name: "shows installing without an action while installing",
state: { status: "installing", version: "1.2.3" },
expected: { label: "settings.updates.action.installing" },
},
{
name: "allows another check when already up to date",
state: { status: "up-to-date" },
expected: { label: "settings.updates.action.checkNow", run: "check" },
},
{
name: "allows retrying a check after an error",
state: { status: "error", message: "Update failed" },
expected: { label: "settings.updates.action.checkNow", run: "check" },
},
]

test("projects updater transitions into one settings action", () => {
expect(updaterAction({ status: "idle" })).toEqual({
label: "settings.updates.action.checkNow",
run: "check",
for (const item of cases) {
test(item.name, () => {
expect(updaterAction(item.state)).toEqual(item.expected)
})
expect(updaterAction({ status: "checking" })).toEqual({ label: "settings.updates.action.checking" })
expect(updaterAction({ status: "downloading", version: "2.0.0" })).toEqual({
label: "settings.updates.action.downloading",
})
expect(updaterAction({ status: "ready", version: "2.0.0" })).toEqual({
label: "toast.update.action.installRestart",
run: "install",
})
expect(updaterAction({ status: "installing", version: "2.0.0" })).toEqual({
label: "settings.updates.action.installing",
})
})
}
})
34 changes: 18 additions & 16 deletions packages/app/src/components/updater-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@ import { usePlatform } from "@/context/platform"
import { useLanguage } from "@/context/language"
import { showToast } from "@/utils/toast"

export function updaterAction(state: UpdaterState | undefined) {
if (!state) return { label: "settings.updates.action.checkNow" as const }
switch (state.status) {
case "checking":
return { label: "settings.updates.action.checking" as const }
case "downloading":
return { label: "settings.updates.action.downloading" as const }
case "ready":
return { label: "toast.update.action.installRestart" as const, run: "install" as const }
case "installing":
return { label: "settings.updates.action.installing" as const }
case "disabled":
return { label: "settings.updates.action.checkNow" as const }
default:
return { label: "settings.updates.action.checkNow" as const, run: "check" as const }
}
const updaterActions = {
disabled: { label: "settings.updates.action.checkNow" },
idle: { label: "settings.updates.action.checkNow", run: "check" },
checking: { label: "settings.updates.action.checking" },
downloading: { label: "settings.updates.action.downloading" },
ready: { label: "toast.update.action.installRestart", run: "install" },
installing: { label: "settings.updates.action.installing" },
"up-to-date": { label: "settings.updates.action.checkNow", run: "check" },
error: { label: "settings.updates.action.checkNow", run: "check" },
} as const satisfies Record<UpdaterState["status"], { label: string; run?: "check" | "install" }>

type UpdaterAction = {
label: (typeof updaterActions)[UpdaterState["status"]]["label"]
run?: "check" | "install"
}

export function updaterAction(state: UpdaterState | undefined): UpdaterAction {
return updaterActions[state?.status ?? "disabled"]
}

export function useUpdaterAction() {
Expand Down
Loading