diff --git a/apps/dashboard/src/lib/remote-development-environment/config-file.test.ts b/apps/dashboard/src/lib/remote-development-environment/config-file.test.ts index 0bf7f30f6..f94f164f4 100644 --- a/apps/dashboard/src/lib/remote-development-environment/config-file.test.ts +++ b/apps/dashboard/src/lib/remote-development-environment/config-file.test.ts @@ -1,5 +1,4 @@ -import { mkdtempSync, rmSync, writeFileSync } from "fs"; -import { tmpdir } from "os"; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs"; import { join } from "path"; import { afterEach, describe, expect, it, vi } from "vitest"; @@ -8,7 +7,7 @@ vi.mock("server-only", () => ({})); let tempDir: string | undefined; function writeTempConfig(content: string): string { - tempDir ??= mkdtempSync(join(tmpdir(), "stack-rde-config-")); + tempDir ??= mkdtempSync(join(process.cwd(), ".stack-rde-config-test-")); const configPath = join(tempDir, "stack.config.ts"); writeFileSync(configPath, content, "utf-8"); return configPath; @@ -23,6 +22,56 @@ afterEach(() => { }); describe("remote development environment config file", () => { + it("loads config exports wrapped in defineStackConfig", async () => { + const configPath = writeTempConfig(` + import { defineStackConfig } from "@stackframe/stack-shared/config"; + + export const config = defineStackConfig({ + auth: { + allowSignUp: true, + }, + }); + `); + + const { readConfigFile } = await import("./config-file"); + + await expect(readConfigFile(configPath)).resolves.toMatchInlineSnapshot(` + { + "config": { + "auth": { + "allowSignUp": true, + }, + }, + "showOnboarding": false, + } + `); + }); + + it("loads config exports wrapped in defineHexclaveConfig", async () => { + const configPath = writeTempConfig(` + import { defineHexclaveConfig } from "@stackframe/stack-shared/config"; + + export const config = defineHexclaveConfig({ + auth: { + allowSignUp: false, + }, + }); + `); + + const { readConfigFile } = await import("./config-file"); + + await expect(readConfigFile(configPath)).resolves.toMatchInlineSnapshot(` + { + "config": { + "auth": { + "allowSignUp": false, + }, + }, + "showOnboarding": false, + } + `); + }); + it("loads config exports produced by TypeScript function calls", async () => { const configPath = writeTempConfig(` function makeConfig() { @@ -90,4 +139,73 @@ describe("remote development environment config file", () => { } `); }); + + it("treats the onboarding placeholder as an empty config", async () => { + const configPath = writeTempConfig(` + export const config = "show-onboarding"; + `); + + const { readConfigFile } = await import("./config-file"); + + await expect(readConfigFile(configPath)).resolves.toMatchInlineSnapshot(` + { + "config": {}, + "showOnboarding": true, + } + `); + }); + + it("rejects modules without a valid config export", async () => { + const configPath = writeTempConfig(` + export const config = () => ({ auth: { allowSignUp: true } }); + `); + + const { readConfigFile } = await import("./config-file"); + + await expect(readConfigFile(configPath)).rejects.toThrow(`Invalid config in ${configPath}.`); + }); + + it("can rewrite a dynamic config into the rendered static format", async () => { + const configPath = writeTempConfig(` + export const config = { + auth: { + allowSignUp: false, + }, + }; + `); + const { readConfigFile, writeConfigObject } = await import("./config-file"); + const current = await readConfigFile(configPath); + + writeConfigObject(configPath, { + ...current.config, + "payments.testMode": true, + }); + + expect(readFileSync(configPath, "utf-8")).toMatchInlineSnapshot(` + "import type { StackConfig } from "@stackframe/js"; + + export const config: StackConfig = { + "auth": { + "allowSignUp": false + }, + "payments": { + "testMode": true + } + }; + " + `); + await expect(readConfigFile(configPath)).resolves.toMatchInlineSnapshot(` + { + "config": { + "auth": { + "allowSignUp": false, + }, + "payments": { + "testMode": true, + }, + }, + "showOnboarding": false, + } + `); + }); });