diff --git a/apps/main/package.json b/apps/main/package.json index 090a056dc..8e39c6f0b 100644 --- a/apps/main/package.json +++ b/apps/main/package.json @@ -18,6 +18,7 @@ "main": "./src/index.ts", "scripts": { "build": "tsup", + "test": "vitest", "typecheck": "tsup && tsc --noEmit" }, "dependencies": { diff --git a/apps/main/src/lib/proxy.test.ts b/apps/main/src/lib/proxy.test.ts new file mode 100644 index 000000000..3dba79314 --- /dev/null +++ b/apps/main/src/lib/proxy.test.ts @@ -0,0 +1,82 @@ +import { session } from "electron" +import type { Mock } from "vitest" +import { beforeEach, describe, expect, it, vi } from "vitest" + +import { logger } from "../logger" +import { getProxyConfig, setProxyConfig, updateProxy } from "./proxy" +import { store } from "./store" + +vi.mock("electron", () => ({ + session: { + defaultSession: { + setProxy: vi.fn(), + }, + }, +})) + +vi.mock("./store", () => ({ + store: { + set: vi.fn(), + get: vi.fn(), + }, +})) + +vi.mock("../logger", () => ({ + logger: { + log: vi.fn(), + }, +})) + +describe("proxy", () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + describe("setProxyConfig", () => { + it("should set proxy config and return true for valid proxy", () => { + const proxy = "http://localhost:8080" + const result = setProxyConfig(proxy) + expect(store.set).toHaveBeenCalledWith("proxy", "http://localhost:8080") + expect(result).toBe(true) + }) + + it("should return false for invalid proxy", () => { + const proxy = "invalid-proxy" + const result = setProxyConfig(proxy) + expect(store.set).toHaveBeenCalledWith("proxy", undefined) + expect(result).toBe(false) + }) + }) + + describe("getProxyConfig", () => { + it("should return normalized proxy config if set", () => { + ;(store.get as Mock).mockReturnValue("http://localhost:8080") + const result = getProxyConfig() + expect(result).toBe("http://localhost:8080") + }) + + it("should compatible dirty data", () => { + ;(store.get as Mock).mockReturnValue("http://localhost:8080,direct://") + const result = getProxyConfig() + expect(result).toBe("http://localhost:8080") + }) + }) + + describe("updateProxy", () => { + it("should set system proxy mode if no proxy config is set", () => { + ;(store.get as Mock).mockReturnValue("") + updateProxy() + expect(session.defaultSession.setProxy).toHaveBeenCalledWith({ mode: "system" }) + }) + + it("should set proxy rules if proxy config is set", () => { + ;(store.get as Mock).mockReturnValue("http://localhost:8080") + updateProxy() + expect(logger.log).toHaveBeenCalledWith("Loading proxy: http://localhost:8080,direct://") + expect(session.defaultSession.setProxy).toHaveBeenCalledWith({ + proxyRules: "http://localhost:8080,direct://", + proxyBypassRules: "", + }) + }) + }) +}) diff --git a/apps/main/src/lib/proxy.ts b/apps/main/src/lib/proxy.ts index 695a3b48c..ed84a78d1 100644 --- a/apps/main/src/lib/proxy.ts +++ b/apps/main/src/lib/proxy.ts @@ -38,18 +38,17 @@ const normalizeProxyUri = (userProxy: string) => { if (!userProxy) { return } + // Only use the first proxy if there are multiple urls + const firstInput = userProxy.split(",")[0] + try { - const proxyUrl = new URL(userProxy) + const proxyUrl = new URL(firstInput) if (!URL_SCHEME.has(proxyUrl.protocol) || !proxyUrl.hostname || !proxyUrl.port) { return } // There are multiple ways to specify a proxy in Electron, // but for security reasons, we only support simple proxy URLs for now. - return [ - `${proxyUrl.protocol}//${proxyUrl.hostname}:${proxyUrl.port}`, - // Failing over to using no proxy if the proxy is unavailable - "direct://", - ].join(",") + return `${proxyUrl.protocol}//${proxyUrl.hostname}:${proxyUrl.port}` } catch { return } @@ -67,10 +66,15 @@ export const updateProxy = () => { }) return } + const proxyRules = [ + proxyUri, + // Failing over to using no proxy if the proxy is unavailable + "direct://", + ].join(",") - logger.log(`Loading proxy: ${proxyUri}`) + logger.log(`Loading proxy: ${proxyRules}`) session.defaultSession.setProxy({ - proxyRules: proxyUri, + proxyRules, proxyBypassRules: BYPASS_RULES, }) } diff --git a/apps/main/vitest.config.ts b/apps/main/vitest.config.ts new file mode 100644 index 000000000..76797e786 --- /dev/null +++ b/apps/main/vitest.config.ts @@ -0,0 +1,22 @@ +import { resolve } from "node:path" +import { fileURLToPath } from "node:url" + +import tsconfigPath from "vite-tsconfig-paths" +import { defineProject } from "vitest/config" + +const __dirname = fileURLToPath(new URL(".", import.meta.url)) + +export default defineProject({ + root: "./", + test: { + globals: true, + environment: "node", + includeSource: [resolve(__dirname, ".")], + }, + + plugins: [ + tsconfigPath({ + projects: ["./tsconfig.json"], + }), + ], +}) diff --git a/package.json b/package.json index 6ef6beed7..ef9cb6967 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "publish": "electron-vite build && electron-forge publish", "start": "electron-vite preview", "sync:ab": "tsx scripts/pull-ab-flags.ts", - "test": "pnpm -F web run test", + "test": "pnpm --recursive run test", "typecheck": "npm run typecheck:node && npm run typecheck:web", "typecheck:node": "pnpm -F electron-main run typecheck", "typecheck:web": "pnpm -F web run typecheck"