Skip to content
Merged
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
40 changes: 28 additions & 12 deletions .github/workflows/dojo-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
jobs:
e2e:
name: E2E Tests
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest-8

steps:
- name: Checkout code
Expand Down Expand Up @@ -52,10 +52,9 @@ jobs:
run: node ./scripts/prep-dojo-everything.js -e2e

- name: Install e2e dependencies
working-directory: typescript-sdk/apps/dojo/e2e2
working-directory: typescript-sdk/apps/dojo/e2e
run: |
pnpm install --frozen-lockfile
pnpm dlx playwright install --with-deps
pnpm install

- name: write langgraph env files
working-directory: typescript-sdk/integrations/langgraph
Expand All @@ -70,21 +69,38 @@ jobs:
echo "OPENAI_API_KEY=${OPENAI_API_KEY}" > python/ag_ui_langgraph/.env
echo "LANGSMITH_API_KEY=${LANGSMITH_API_KEY}" >> python/ag_ui_langgraph/.env

- name: Run dojo+agents and tests
working-directory: typescript-sdk/apps/dojo/e2e2
- name: Run dojo+agents
uses: JarvusInnovations/background-action@v1
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
run: |
node ../scripts/run-dojo-everything.js &
npx wait-port 9999
sleep 10
pnpm exec playwright test --reporter=dot
with:
run: |
node ../scripts/run-dojo-everything.js
working-directory: typescript-sdk/apps/dojo/e2e
wait-on: |
http://localhost:9999
tcp:localhost:8000
tcp:localhost:8001
tcp:localhost:8002
tcp:localhost:8003
tcp:localhost:8004
tcp:localhost:8005
tcp:localhost:8006
tcp:localhost:8007
tcp:localhost:8008
tcp:localhost:8009

- name: Run tests
working-directory: typescript-sdk/apps/dojo/e2e
env:
BASE_URL: http://localhost:9999
run: pnpm test

- name: Upload traces
if: always() # Uploads artifacts even if tests fail
uses: actions/upload-artifact@v4
with:
name: playwright-traces
path: typescript-sdk/apps/dojo/e2e2/test-results/
path: typescript-sdk/apps/dojo/e2e/test-results/
retention-days: 7
27 changes: 18 additions & 9 deletions typescript-sdk/apps/dojo/e2e/clean-reporter.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
function getTimestamp() {
return (process.env.CI || process.env.VERBOSE)
? new Date().toLocaleTimeString('en-US', { hour12: false })
: '';
}

function logStamp(...args) {
console.log(getTimestamp(), ...args);
}

class CleanReporter {
onBegin(config, suite) {
console.log(`\n🎭 Running ${suite.allTests().length} tests...\n`);
Expand All @@ -15,9 +25,9 @@ class CleanReporter {
.trim();

if (result.status === "passed") {
console.log(`✅ ${cleanSuite}: ${testName}`);
logStamp(`✅ ${cleanSuite}: ${testName}`);
} else if (result.status === "failed") {
console.log(`❌ ${cleanSuite}: ${testName}`);
logStamp(`❌ ${cleanSuite}: ${testName}`);

// Extract the most relevant error info
const error = result.error || result.errors?.[0];
Expand Down Expand Up @@ -60,15 +70,14 @@ class CleanReporter {

onEnd(result) {
console.log("\n" + "=".repeat(60));
console.log(`📊 TEST SUMMARY`);
logStamp(`📊 TEST SUMMARY`);
console.log("=".repeat(60));

console.log(`\n🔍 FAILURE ANALYSIS:`);
console.log(`• Most failures appear to be AI service related`);
console.log(`• Check API keys and service availability`);
console.log(
`• Run 'pnpm exec playwright show-report' for detailed HTML report`
);
if (!process.env.CI) {
console.log(
`• Run 'pnpm exec playwright show-report' for detailed HTML report`
);
}

console.log("=".repeat(60) + "\n");
}
Expand Down
1 change: 1 addition & 0 deletions typescript-sdk/apps/dojo/e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"description": "Scheduled Playwright smoke tests for CopilotKit demo apps",
"scripts": {
"postinstall": "playwright install --with-deps",
"test": "playwright test",
"test:ui": "playwright test --ui"
},
Expand Down
100 changes: 47 additions & 53 deletions typescript-sdk/apps/dojo/e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,53 @@
import { defineConfig } from "@playwright/test";
import { defineConfig, ReporterDescription } from "@playwright/test";
import { generateSimpleLayout } from "./slack-layout-simple";



function getReporters(): ReporterDescription[] {
const videoReporter: ReporterDescription = [
"./reporters/s3-video-reporter.ts",
{
outputFile: "test-results/video-urls.json",
uploadVideos: true,
},
];
const s3Reporter: ReporterDescription = [
"./node_modules/playwright-slack-report/dist/src/SlackReporter.js",
{
slackWebHookUrl: process.env.SLACK_WEBHOOK_URL,
sendResults: "always", // always send results
maxNumberOfFailuresToShow: 10,
layout: generateSimpleLayout, // Use our simple layout
},
];
const githubReporter: ReporterDescription = ["github"];
const htmlReporter: ReporterDescription = ["html", { open: "never" }];
const cleanReporter: ReporterDescription = ["./clean-reporter.js"];

const addVideoAndSlack = process.env.SLACK_WEBHOOK_URL && process.env.AWS_S3_BUCKET_NAME;

return [
process.env.CI ? githubReporter : undefined,
addVideoAndSlack ? videoReporter : undefined,
addVideoAndSlack ? s3Reporter : undefined,
htmlReporter,
cleanReporter,
].filter(Boolean) as ReporterDescription[];
}

function getBaseUrl(): string {
if (process.env.BASE_URL) {
return new URL(process.env.BASE_URL).toString();
}
console.error("BASE_URL is not set");
process.exit(1);
}

export default defineConfig({
timeout: process.env.CI ? 300_000 : 120_000, // 5min in CI, 2min locally for AI tests
workers: 1, // Serial execution to avoid race conditions and AI service conflicts
testDir: "./tests",
retries: process.env.CI ? 3 : 0, // More retries for flaky AI tests in CI, 0 for local
fullyParallel: false, // Serial execution for deterministic AI test results
retries: process.env.CI ? 1 : 0, // More retries for flaky AI tests in CI, 0 for local
fullyParallel: true,
use: {
headless: true,
viewport: { width: 1280, height: 720 },
Expand All @@ -20,6 +61,7 @@ export default defineConfig({
actionTimeout: 60_000, // 1 minute for AI-driven actions (clicking, filling)
// Test isolation - ensure clean state between tests
testIdAttribute: "data-testid",
baseURL: getBaseUrl(),
},
expect: {
timeout: 90_000, // 1.5 minutes for AI-generated content to appear
Expand All @@ -38,53 +80,5 @@ export default defineConfig({
},
},
],
reporter: process.env.CI
? [
["github"],
["html", { open: "never" }],
// S3 video uploader (runs first to upload videos)
[
"./reporters/s3-video-reporter.ts",
{
outputFile: "test-results/video-urls.json",
uploadVideos: true,
},
],
// Slack notifications (runs after videos are uploaded)
[
"./node_modules/playwright-slack-report/dist/src/SlackReporter.js",
{
slackWebHookUrl: process.env.SLACK_WEBHOOK_URL,
sendResults: "always", // always send results
maxNumberOfFailuresToShow: 10,
layout: generateSimpleLayout, // Use our simple layout
},
],
]
: process.env.SLACK_WEBHOOK_URL && process.env.AWS_S3_BUCKET_NAME
? [
// Full local testing with S3 + Slack (when both are configured)
[
"./reporters/s3-video-reporter.ts",
{
outputFile: "test-results/video-urls.json",
uploadVideos: true,
},
],
[
"./node_modules/playwright-slack-report/dist/src/SlackReporter.js",
{
slackWebHookUrl: process.env.SLACK_WEBHOOK_URL,
sendResults: "always",
maxNumberOfFailuresToShow: 10,
layout: generateSimpleLayout,
},
],
["html", { open: "never" }],
]
: [
// Standard local testing
["./clean-reporter.js"],
["html", { open: "never" }],
],
reporter: getReporters(),
});
2 changes: 2 additions & 0 deletions typescript-sdk/apps/dojo/e2e/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
packages:
- '.'
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ test("[Agno] Agentic Chat sends and receives a greeting message", async ({
}) => {
await retryOnAIFailure(async () => {
await page.goto(
"https://ag-ui-dojo-nine.vercel.app/agno/feature/agentic_chat"
"/agno/feature/agentic_chat"
);

const chat = new AgenticChatPage(page);
Expand All @@ -31,7 +31,7 @@ test("[Agno] Agentic Chat provides stock price information", async ({
}) => {
await retryOnAIFailure(async () => {
await page.goto(
"https://ag-ui-dojo-nine.vercel.app/agno/feature/agentic_chat"
"/agno/feature/agentic_chat"
);

const chat = new AgenticChatPage(page);
Expand All @@ -54,7 +54,7 @@ test("[Agno] Agentic Chat retains memory of previous questions", async ({
}) => {
await retryOnAIFailure(async () => {
await page.goto(
"https://ag-ui-dojo-nine.vercel.app/agno/feature/agentic_chat"
"/agno/feature/agentic_chat"
);

const chat = new AgenticChatPage(page);
Expand All @@ -72,7 +72,7 @@ test("[Agno] Agentic Chat retains memory of previous questions", async ({
await chat.sendMessage("What was my first question");
await chat.assertUserMessageVisible("What was my first question");
await waitForAIResponse(page);

// Check if the agent remembers the first question about AAPL stock price
await chat.assertAgentReplyVisible(/Hi/i);
});
Expand All @@ -83,7 +83,7 @@ test("[Agno] Agentic Chat retains memory of user messages during a conversation"
}) => {
await retryOnAIFailure(async () => {
await page.goto(
"https://ag-ui-dojo-nine.vercel.app/agno/feature/agentic_chat"
"/agno/feature/agentic_chat"
);

const chat = new AgenticChatPage(page);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { test, expect } from "@playwright/test";
import { ToolBaseGenUIPage } from "../../pages/agnoPages/ToolBaseGenUIPage";

const pageURL =
"https://ag-ui-dojo-nine.vercel.app/agno/feature/tool_based_generative_ui";
"/agno/feature/tool_based_generative_ui";

test('[Agno] Haiku generation and display verification', async ({
test.fixme('[Agno] Haiku generation and display verification', async ({
page,
}) => {
await page.goto(pageURL);
Expand All @@ -17,7 +17,7 @@ test('[Agno] Haiku generation and display verification', async ({
await genAIAgent.checkHaikuDisplay(page);
});

test('[Agno] Haiku generation and UI consistency for two different prompts', async ({
test.fixme('[Agno] Haiku generation and UI consistency for two different prompts', async ({
page,
}) => {
await page.goto(pageURL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ test("[CrewAI] Agentic Chat sends and receives a message", async ({
}) => {
await retryOnAIFailure(async () => {
await page.goto(
"https://ag-ui-dojo-nine.vercel.app/crewai/feature/agentic_chat"
"/crewai/feature/agentic_chat"
);

const chat = new AgenticChatPage(page);
Expand All @@ -26,12 +26,12 @@ test("[CrewAI] Agentic Chat sends and receives a message", async ({
});
});

test("[CrewAI] Agentic Chat changes background on message and reset", async ({
test.fixme("[CrewAI] Agentic Chat changes background on message and reset", async ({
page,
}) => {
await retryOnAIFailure(async () => {
await page.goto(
"https://ag-ui-dojo-nine.vercel.app/crewai/feature/agentic_chat"
"/crewai/feature/agentic_chat"
);

const chat = new AgenticChatPage(page);
Expand All @@ -42,7 +42,7 @@ test("[CrewAI] Agentic Chat changes background on message and reset", async ({
// Store initial background color
const initialBackground = await chat.getBackground();
console.log("Initial background color:", initialBackground);

// 1. Send message to change background to blue
await chat.sendMessage("Hi change the background color to blue");
await chat.assertUserMessageVisible(
Expand Down Expand Up @@ -79,7 +79,7 @@ test("[CrewAI] Agentic Chat retains memory of user messages during a conversatio
}) => {
await retryOnAIFailure(async () => {
await page.goto(
"https://ag-ui-dojo-nine.vercel.app/crewai/feature/agentic_chat"
"/crewai/feature/agentic_chat"
);

const chat = new AgenticChatPage(page);
Expand Down
Loading
Loading