Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
5 changes: 5 additions & 0 deletions .github/workflows/pr-playwright-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
GEN_AI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

# for federated slack tests
SLACK_CLIENT_ID: ${{ secrets.SLACK_CLIENT_ID }}
SLACK_CLIENT_SECRET: ${{ secrets.SLACK_CLIENT_SECRET }}
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Secrets are defined at workflow-level env, exposing them to every step and action; scope them to the Playwright test step to minimize exposure.

Prompt for AI agents
Address the following comment on .github/workflows/pr-playwright-tests.yml at line 15:

<comment>Secrets are defined at workflow-level env, exposing them to every step and action; scope them to the Playwright test step to minimize exposure.</comment>

<file context>
@@ -9,6 +9,11 @@ env:
+
+  # for federated slack tests
+  SLACK_CLIENT_ID: ${{ secrets.SLACK_CLIENT_ID }}
+  SLACK_CLIENT_SECRET: ${{ secrets.SLACK_CLIENT_SECRET }}
+
   MOCK_LLM_RESPONSE: true
</file context>
Fix with Cubic


MOCK_LLM_RESPONSE: true

jobs:
Expand Down
4 changes: 2 additions & 2 deletions web/admin2_auth.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
"cookies": [
{
"name": "fastapiusersauth",
"value": "h3qhacpHbE4_09HcOLlVW4lSee48m1UbjYTUiKYwNiw",
"value": "AAdfmA4NT0BXdlg80K3xVuLGVPPPqH7JfzVk82otIw0",
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid committing a real authentication cookie/token value; rely on global-setup to generate storage state at runtime or store a placeholder to prevent leaking credentials.

Prompt for AI agents
Address the following comment on web/admin2_auth.json at line 5:

<comment>Avoid committing a real authentication cookie/token value; rely on global-setup to generate storage state at runtime or store a placeholder to prevent leaking credentials.</comment>

<file context>
@@ -2,10 +2,10 @@
     {
       &quot;name&quot;: &quot;fastapiusersauth&quot;,
-      &quot;value&quot;: &quot;h3qhacpHbE4_09HcOLlVW4lSee48m1UbjYTUiKYwNiw&quot;,
+      &quot;value&quot;: &quot;AAdfmA4NT0BXdlg80K3xVuLGVPPPqH7JfzVk82otIw0&quot;,
       &quot;domain&quot;: &quot;localhost&quot;,
       &quot;path&quot;: &quot;/&quot;,
</file context>
Suggested change
"value": "AAdfmA4NT0BXdlg80K3xVuLGVPPPqH7JfzVk82otIw0",
"value": "<redacted>",
Fix with Cubic

"domain": "localhost",
"path": "/",
"expires": 1745624493.119168,
"expires": 1758133751.1961,
"httpOnly": true,
"secure": false,
"sameSite": "Lax"
Expand Down
11 changes: 10 additions & 1 deletion web/src/app/assistants/SidebarWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { useSidebarShortcut } from "@/lib/browserUtilities";
import { UserSettingsModal } from "@/app/chat/components/modal/UserSettingsModal";
import { usePopup } from "@/components/admin/connectors/Popup";
import { useUser } from "@/components/user/UserProvider";
import { useFederatedOAuthStatus } from "@/lib/hooks/useFederatedOAuthStatus";

interface SidebarWrapperProps<T extends object> {
size?: "sm" | "lg";
Expand All @@ -41,7 +42,12 @@ export default function SidebarWrapper<T extends object>({
}, [sidebarVisible]);

const sidebarElementRef = useRef<HTMLDivElement>(null);
const { folders, openedFolders, chatSessions } = useChatContext();
const { folders, chatSessions, ccPairs } = useChatContext();
const {
connectors: federatedConnectors,
refetch: refetchFederatedConnectors,
} = useFederatedOAuthStatus();

const explicitlyUntoggle = () => {
setShowDocSidebar(false);

Expand Down Expand Up @@ -114,6 +120,9 @@ export default function SidebarWrapper<T extends object>({
<UserSettingsModal
setPopup={setPopup}
llmProviders={llmProviders}
ccPairs={ccPairs}
federatedConnectors={federatedConnectors}
refetchFederatedConnectors={refetchFederatedConnectors}
onClose={() => setUserSettingsToggled(false)}
defaultModel={user?.preferences?.default_model!}
/>
Expand Down
8 changes: 8 additions & 0 deletions web/src/app/chat/components/ChatPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { usePopup } from "@/components/admin/connectors/Popup";
import { SEARCH_PARAM_NAMES } from "../services/searchParams";
import { useFederatedConnectors, useFilters, useLlmManager } from "@/lib/hooks";
import { useFederatedOAuthStatus } from "@/lib/hooks/useFederatedOAuthStatus";
import { FeedbackType } from "@/app/chat/interfaces";
import { OnyxInitializingLoader } from "@/components/OnyxInitializingLoader";
import { FeedbackModal } from "./modal/FeedbackModal";
Expand Down Expand Up @@ -161,6 +162,10 @@ export function ChatPage({

// Also fetch federated connectors for the sources list
const { data: federatedConnectorsData } = useFederatedConnectors();
const {
connectors: federatedConnectorOAuthStatus,
refetch: refetchFederatedConnectors,
} = useFederatedOAuthStatus();
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant fetch: useFederatedOAuthStatus is invoked here and again inside FederatedOAuthModal, leading to duplicate network requests. Consider lifting the hook to ChatPage and passing data to the modal, or memoizing/centralizing the fetch.

Prompt for AI agents
Address the following comment on web/src/app/chat/components/ChatPage.tsx at line 168:

<comment>Redundant fetch: useFederatedOAuthStatus is invoked here and again inside FederatedOAuthModal, leading to duplicate network requests. Consider lifting the hook to ChatPage and passing data to the modal, or memoizing/centralizing the fetch.</comment>

<file context>
@@ -161,6 +162,10 @@ export function ChatPage({
+  const {
+    connectors: federatedConnectorOAuthStatus,
+    refetch: refetchFederatedConnectors,
+  } = useFederatedOAuthStatus();
 
   const { user, isAdmin } = useUser();
</file context>
Fix with Cubic


const { user, isAdmin } = useUser();
const existingChatIdRaw = searchParams?.get("chatId");
Expand Down Expand Up @@ -796,6 +801,9 @@ export function ChatPage({
updateCurrentLlm={llmManager.updateCurrentLlm}
defaultModel={user?.preferences.default_model!}
llmProviders={llmProviders}
ccPairs={ccPairs}
federatedConnectors={federatedConnectorOAuthStatus}
refetchFederatedConnectors={refetchFederatedConnectors}
onClose={() => {
setUserSettingsToggled(false);
setSettingsToggled(false);
Expand Down
6 changes: 3 additions & 3 deletions web/src/app/chat/components/modal/UserSettingsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ export function UserSettingsModal({
updateCurrentLlm?: (newOverride: LlmDescriptor) => void;
onClose: () => void;
defaultModel: string | null;
ccPairs?: CCPairBasicInfo[];
federatedConnectors?: FederatedConnectorOAuthStatus[];
refetchFederatedConnectors?: () => void;
ccPairs: CCPairBasicInfo[];
federatedConnectors: FederatedConnectorOAuthStatus[];
refetchFederatedConnectors: () => void;
}) {
const {
refreshUser,
Expand Down
108 changes: 108 additions & 0 deletions web/tests/e2e/connectors/federated_slack.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { test, expect } from "@chromatic-com/playwright";
import type { Page } from "@playwright/test";
import { loginAs, loginAsRandomUser } from "../utils/auth";

test.use({ storageState: "admin_auth.json" });

const SLACK_CLIENT_ID = process.env.SLACK_CLIENT_ID || "";
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fail fast or skip the test if SLACK_CLIENT_ID is not set; defaulting to "" silently misconfigures the flow.

Prompt for AI agents
Address the following comment on web/tests/e2e/connectors/federated_slack.spec.ts at line 7:

<comment>Fail fast or skip the test if SLACK_CLIENT_ID is not set; defaulting to &quot;&quot; silently misconfigures the flow.</comment>

<file context>
@@ -0,0 +1,108 @@
+
+test.use({ storageState: &quot;admin_auth.json&quot; });
+
+const SLACK_CLIENT_ID = process.env.SLACK_CLIENT_ID || &quot;&quot;;
+const SLACK_CLIENT_SECRET = process.env.SLACK_CLIENT_SECRET || &quot;&quot;;
+
</file context>

const SLACK_CLIENT_SECRET = process.env.SLACK_CLIENT_SECRET || "";

async function createFederatedSlackConnector(page: Page) {
// Navigate to add connector page
await page.goto("http://localhost:3000/admin/add-connector");
await page.waitForLoadState("networkidle");

// Click on Slack connector tile (specifically the one with "Logo Slack" text, not "Slack Bots")
await page.getByRole("link", { name: "Logo Slack" }).first().click();
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Selector depends on icon alt text; prefer a text-based matcher that remains stable and avoids coupling to alt values.

Prompt for AI agents
Address the following comment on web/tests/e2e/connectors/federated_slack.spec.ts at line 16:

<comment>Selector depends on icon alt text; prefer a text-based matcher that remains stable and avoids coupling to alt values.</comment>

<file context>
@@ -0,0 +1,108 @@
+  await page.waitForLoadState(&quot;networkidle&quot;);
+
+  // Click on Slack connector tile (specifically the one with &quot;Logo Slack&quot; text, not &quot;Slack Bots&quot;)
+  await page.getByRole(&quot;link&quot;, { name: &quot;Logo Slack&quot; }).first().click();
+  await page.waitForLoadState(&quot;networkidle&quot;);
+
</file context>

await page.waitForLoadState("networkidle");

// Fill in the client ID and client secret
await page.getByLabel(/client id/i).fill(SLACK_CLIENT_ID);
await page.getByLabel(/client secret/i).fill(SLACK_CLIENT_SECRET);

// Submit the form to create or update the federated connector
const createOrUpdateButton = await page.getByRole("button", {
name: /create|update/i,
});
await createOrUpdateButton.click();

// Wait for success message or redirect
await page.waitForTimeout(2000);
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed wait introduces flakiness; wait for a deterministic condition (e.g., success UI, navigation, or network idle) instead.

Prompt for AI agents
Address the following comment on web/tests/e2e/connectors/federated_slack.spec.ts at line 30:

<comment>Fixed wait introduces flakiness; wait for a deterministic condition (e.g., success UI, navigation, or network idle) instead.</comment>

<file context>
@@ -0,0 +1,108 @@
+  await createOrUpdateButton.click();
+
+  // Wait for success message or redirect
+  await page.waitForTimeout(2000);
+}
+
</file context>
Fix with Cubic

}

async function navigateToUserSettings(page: Page) {
// Wait for any existing modals to close
await page.waitForTimeout(1000);
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid fixed delay; wait for a specific element/state signaling the UI is ready.

Prompt for AI agents
Address the following comment on web/tests/e2e/connectors/federated_slack.spec.ts at line 35:

<comment>Avoid fixed delay; wait for a specific element/state signaling the UI is ready.</comment>

<file context>
@@ -0,0 +1,108 @@
+
+async function navigateToUserSettings(page: Page) {
+  // Wait for any existing modals to close
+  await page.waitForTimeout(1000);
+
+  // Wait for potential modal backdrop to disappear
</file context>
Fix with Cubic


// Wait for potential modal backdrop to disappear
await page
.waitForSelector(".fixed.inset-0.bg-neutral-950\\/50", {
state: "detached",
timeout: 5000,
})
.catch(() => {});

// Click on user dropdown/settings button
await page.locator("#onyx-user-dropdown").click();

// Click on settings option
await page.getByText("User Settings").click();

// Wait for settings modal to appear
await expect(page.locator("h2", { hasText: "User Settings" })).toBeVisible();
}

async function openConnectorsTab(page: Page) {
// Click on the Connectors tab in user settings
await page.getByRole("button", { name: "Connectors" }).click();

// Wait for connectors section to be visible
// Allow multiple instances of "Connected Services" to be visible
const connectedServicesLocators = page.getByText("Connected Services");
await expect(connectedServicesLocators.first()).toBeVisible();
}

test("Federated Slack Connector - Create, OAuth Modal, and User Settings Flow", async ({
page,
}) => {
// Setup: Clear cookies and log in as admin
await page.context().clearCookies();
await loginAs(page, "admin");

// Create a federated Slack connector in admin panel
await createFederatedSlackConnector(page);

// Log in as a random user
await page.context().clearCookies();
await loginAsRandomUser(page);

// Navigate back to main page and verify OAuth modal appears
await page.goto("http://localhost:3000/chat");
await page.waitForLoadState("networkidle");

// Check if the OAuth modal appears
await expect(
page.getByText(/improve answer quality by letting/i)
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assertion checks for text that does not exist in the OAuth modal; use a stable element like the "Skip for now" button to confirm the modal is shown.

Prompt for AI agents
Address the following comment on web/tests/e2e/connectors/federated_slack.spec.ts at line 85:

<comment>Assertion checks for text that does not exist in the OAuth modal; use a stable element like the &quot;Skip for now&quot; button to confirm the modal is shown.</comment>

<file context>
@@ -0,0 +1,108 @@
+
+  // Check if the OAuth modal appears
+  await expect(
+    page.getByText(/improve answer quality by letting/i)
+  ).toBeVisible({ timeout: 10000 });
+  await expect(page.getByText(/slack/i)).toBeVisible();
</file context>
Fix with Cubic

).toBeVisible({ timeout: 10000 });
await expect(page.getByText(/slack/i)).toBeVisible();

// Decline the OAuth connection
await page.getByRole("button", { name: "Skip for now" }).click();

// Wait for modal to disappear
await expect(
page.getByText(/improve answer quality by letting/i)
).not.toBeVisible();

// Go to user settings and verify the connector appears
await navigateToUserSettings(page);
await openConnectorsTab(page);

// Verify Slack connector appears in the federated connectors section
await expect(page.getByText("Federated Connectors")).toBeVisible();
await expect(page.getByText("Slack")).toBeVisible();
await expect(page.getByText("Not connected")).toBeVisible();

// Verify there's a Connect button available
await expect(page.locator("button", { hasText: /^Connect$/ })).toBeVisible();
});
Loading