From 4821ce1e8c0040a397b960be81bb37b3539f06d7 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Fri, 2 May 2025 13:22:09 +0100 Subject: [PATCH] Tests and vite config change to support --- .../portal/settings/ai/AISettings.spec.js | 132 +++++++++++++++--- packages/builder/vite.config.mjs | 1 + 2 files changed, 117 insertions(+), 16 deletions(-) diff --git a/packages/builder/src/pages/builder/portal/settings/ai/AISettings.spec.js b/packages/builder/src/pages/builder/portal/settings/ai/AISettings.spec.js index f6e579f7fc..e5bf15d947 100644 --- a/packages/builder/src/pages/builder/portal/settings/ai/AISettings.spec.js +++ b/packages/builder/src/pages/builder/portal/settings/ai/AISettings.spec.js @@ -1,11 +1,23 @@ import { it, expect, describe, vi } from "vitest" import AISettings from "./index.svelte" -import { render, fireEvent } from "@testing-library/svelte" +import { render, waitFor } from "@testing-library/svelte" import { admin, licensing, featureFlags } from "@/stores/portal" import { notifications } from "@budibase/bbui" +import { API } from "@/api" vi.spyOn(notifications, "error").mockImplementation(vi.fn) vi.spyOn(notifications, "success").mockImplementation(vi.fn) +vi.mock("@/api", () => ({ + API: { + getConfig: vi.fn().mockResolvedValue({ + config: {}, + }), + getLicenseKey: vi.fn().mockResolvedValue({ + licenseKey: "abc-123", + }), + saveConfig: vi.fn(), + }, +})) const Hosting = { Cloud: "cloud", @@ -15,7 +27,6 @@ const Hosting = { function setupEnv(hosting, features = {}, flags = {}) { const defaultFeatures = { budibaseAIEnabled: false, - customAIConfigsEnabled: false, ...features, } const defaultFlags = { @@ -41,33 +52,122 @@ describe("AISettings", () => { let instance = null const setupDOM = () => { - instance = render(AISettings, {}) + instance = render(AISettings) const modalContainer = document.createElement("div") modalContainer.classList.add("modal-container") instance.baseElement.appendChild(modalContainer) } + beforeEach(() => { + setupEnv(Hosting.Self) + }) + afterEach(() => { vi.restoreAllMocks() }) - it("that the AISettings is rendered", () => { - setupDOM() - expect(instance).toBeDefined() + describe("Basic rendering", () => { + it("should render the AI header", async () => { + setupDOM() + await waitFor(() => { + const header = instance.getByText("AI") + expect(header).toBeInTheDocument() + }) + }) + it("should show 'No LLMs are enabled' when no providers are active", async () => { + API.getConfig.mockResolvedValueOnce({ config: {} }) + setupDOM() + + await waitFor(() => { + const noEnabledText = instance.getByText("No LLMs are enabled") + expect(noEnabledText).toBeInTheDocument() + }) + }) + + it("should display the 'Enable BB AI' button", async () => { + setupDOM() + await waitFor(() => { + const enableButton = instance.getByText("Enable BB AI") + expect(enableButton).toBeInTheDocument() + }) + }) }) - describe("DOM Render tests", () => { - it("the enable bb ai button should not do anything if the user doesn't have the correct license on self host", async () => { - let addAiButton - let configModal + describe("Provider rendering", () => { + it("should display active provider with active status tag", async () => { + API.getConfig.mockResolvedValueOnce({ + config: { + BudibaseAI: { + provider: "BudibaseAI", + active: true, + isDefault: true, + name: "Budibase AI", + }, + }, + }) - setupEnv(Hosting.Self, { customAIConfigsEnabled: false }) setupDOM() - addAiButton = instance.queryByText("Enable BB AI") - expect(addAiButton).toBeInTheDocument() - await fireEvent.click(addAiButton) - configModal = instance.queryByText("Custom AI Configuration") - expect(configModal).not.toBeInTheDocument() + + await waitFor(() => { + const providerName = instance.getByText("Budibase AI") + expect(providerName).toBeInTheDocument() + + const statusTags = instance.baseElement.querySelectorAll(".tag.active") + expect(statusTags.length).toBeGreaterThan(0) + + let foundEnabledTag = false + statusTags.forEach(tag => { + if (tag.textContent === "Enabled") { + foundEnabledTag = true + } + }) + expect(foundEnabledTag).toBe(true) + }) + }) + + it("should display disabled provider with disabled status tag", async () => { + API.getConfig.mockResolvedValueOnce({ + config: { + BudibaseAI: { + provider: "BudibaseAI", + active: true, + isDefault: true, + name: "Budibase AI", + }, + OpenAI: { + provider: "OpenAI", + active: false, + isDefault: false, + name: "OpenAI", + }, + }, + }) + + setupDOM() + + await waitFor(async () => { + const disabledProvider = instance.getByText("OpenAI") + expect(disabledProvider).toBeInTheDocument() + + const disabledTags = + instance.baseElement.querySelectorAll(".tag.disabled") + expect(disabledTags.length).toBeGreaterThan(0) + + let foundDisabledTag = false + disabledTags.forEach(tag => { + if (tag.textContent === "Disabled") { + foundDisabledTag = true + } + }) + expect(foundDisabledTag).toBe(true) + + const openAIOption = disabledProvider.closest(".option") + expect(openAIOption).not.toBeNull() + const disabledTagNearOpenAI = + openAIOption.querySelector(".tag.disabled") + expect(disabledTagNearOpenAI).not.toBeNull() + expect(disabledTagNearOpenAI.textContent).toBe("Disabled") + }) }) }) }) diff --git a/packages/builder/vite.config.mjs b/packages/builder/vite.config.mjs index fb281f60bc..e84716f631 100644 --- a/packages/builder/vite.config.mjs +++ b/packages/builder/vite.config.mjs @@ -87,6 +87,7 @@ export default defineConfig(({ mode }) => { exclude: ["@roxi/routify", "fsevents"], }, resolve: { + conditions: mode === "test" ? ["browser"] : [], dedupe: ["@roxi/routify"], alias: { "@budibase/types": path.resolve(__dirname, "../types/src"),