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
114 changes: 114 additions & 0 deletions typescript-sdk/apps/dojo/e2e/pages/agnoPages/ToolBaseGenUIPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { Page, Locator, expect } from '@playwright/test';

export class ToolBaseGenUIPage {
readonly page: Page;
readonly haikuAgentIntro: Locator;
readonly messageBox: Locator;
readonly sendButton: Locator;
readonly applyButton: Locator;
readonly appliedButton: Locator;
readonly haikuBlock: Locator;
readonly japaneseLines: Locator;

constructor(page: Page) {
this.page = page;
this.haikuAgentIntro = page.getByText("I'm a haiku generator 👋. How can I help you?");
this.messageBox = page.getByRole('textbox', { name: 'Type a message...' });
this.sendButton = page.locator('[data-test-id="copilot-chat-ready"]');
this.haikuBlock = page.locator('[data-testid="haiku-card"]');
this.applyButton = page.getByRole('button', { name: 'Apply' });
this.japaneseLines = page.locator('[data-testid="haiku-line"]');
}

async generateHaiku(message: string) {
await this.messageBox.click();
await this.messageBox.fill(message);
await this.sendButton.click();
}

async checkGeneratedHaiku() {
await this.page.locator('[data-testid="haiku-card"]').last().isVisible();
const mostRecentCard = this.page.locator('[data-testid="haiku-card"]').last();
await mostRecentCard.locator('[data-testid="haiku-line"]').first().waitFor({ state: 'visible', timeout: 10000 });
}

async extractChatHaikuContent(page: Page): Promise<string> {
await page.waitForTimeout(3000);
await page.locator('[data-testid="haiku-card"]').first().waitFor({ state: 'visible' });
const allHaikuCards = page.locator('[data-testid="haiku-card"]');
const cardCount = await allHaikuCards.count();
let chatHaikuContainer;
let chatHaikuLines;

for (let cardIndex = cardCount - 1; cardIndex >= 0; cardIndex--) {
chatHaikuContainer = allHaikuCards.nth(cardIndex);
chatHaikuLines = chatHaikuContainer.locator('[data-testid="haiku-line"]');
const linesCount = await chatHaikuLines.count();

if (linesCount > 0) {
try {
await chatHaikuLines.first().waitFor({ state: 'visible', timeout: 5000 });
break;
} catch (error) {
continue;
}
}
}

if (!chatHaikuLines) {
throw new Error('No haiku cards with visible lines found');
}

const count = await chatHaikuLines.count();
const lines: string[] = [];

for (let i = 0; i < count; i++) {
const haikuLine = chatHaikuLines.nth(i);
const japaneseText = await haikuLine.locator('p').first().innerText();
lines.push(japaneseText);
}

const chatHaikuContent = lines.join('').replace(/\s/g, '');
return chatHaikuContent;
}

async extractMainDisplayHaikuContent(page: Page): Promise<string> {
const mainDisplayLines = page.locator('[data-testid="main-haiku-line"]');
const mainCount = await mainDisplayLines.count();
const lines: string[] = [];

if (mainCount > 0) {
for (let i = 0; i < mainCount; i++) {
const haikuLine = mainDisplayLines.nth(i);
const japaneseText = await haikuLine.locator('p').first().innerText();
lines.push(japaneseText);
}
}

const mainHaikuContent = lines.join('').replace(/\s/g, '');
return mainHaikuContent;
}

async checkHaikuDisplay(page: Page): Promise<void> {
const chatHaikuContent = await this.extractChatHaikuContent(page);

await page.waitForTimeout(5000);

const mainHaikuContent = await this.extractMainDisplayHaikuContent(page);

if (mainHaikuContent === '') {
expect(chatHaikuContent.length).toBeGreaterThan(0);
return;
}

if (chatHaikuContent === mainHaikuContent) {
expect(mainHaikuContent).toBe(chatHaikuContent);
} else {
await page.waitForTimeout(3000);

const updatedMainContent = await this.extractMainDisplayHaikuContent(page);

expect(updatedMainContent).toBe(chatHaikuContent);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,16 @@ export class AgenticGenUIPage {
constructor(page: Page) {
this.page = page;
this.planTaskButton = page.getByRole('button', { name: 'Agentic Generative UI' });

// Remove iframe references
this.chatInput = page.getByRole('textbox', { name: 'Type a message...' });
this.sendButton = page.locator('[data-test-id="copilot-chat-ready"]');
this.agentMessage = page.locator('.copilotKitAssistantMessage');
this.userMessage = page.locator('.copilotKitUserMessage');
this.agentGreeting = page.getByText('This agent demonstrates');
this.agentPlannerContainer = page.locator('div.bg-gray-100.rounded-lg.w-\\[500px\\].p-4.text-black.space-y-2');
this.agentPlannerContainer = page.getByTestId('task-progress');
}

async plan() {
const stepItems = this.agentPlannerContainer.locator('div.text-sm');
const stepItems = this.agentPlannerContainer.getByTestId('task-step-text');
const count = await stepItems.count();
expect(count).toBeGreaterThan(0);
for (let i = 0; i < count; i++) {
Expand All @@ -44,16 +42,14 @@ export class AgenticGenUIPage {
}

getPlannerButton(name: string | RegExp) {
// Remove iframe reference
return this.page.getByRole('button', { name });
}

async assertAgentReplyVisible(expectedText: RegExp) {
await expect(this.agentMessage.getByText(expectedText)).toBeVisible();
await expect(this.agentMessage.last().getByText(expectedText)).toBeVisible();
}

async getUserText(textOrRegex) {
// Remove iframe reference
return await this.page.getByText(textOrRegex).isVisible();
}

Expand Down
48 changes: 17 additions & 31 deletions typescript-sdk/apps/dojo/e2e/pages/crewAIPages/HumanInLoopPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,17 @@ export class HumanInLoopPage {

constructor(page: Page) {
this.page = page;
// Remove the planTaskButton since it doesn't exist in this interface
this.planTaskButton = page.getByRole('button', { name: 'Human in the loop Plan a task' });

// Update greeting text to match actual content
this.agentGreeting = page.getByText("Hi, I'm an agent specialized in helping you with your tasks. How can I help you?");
this.chatInput = page.getByRole('textbox', { name: 'Type a message...' });
this.sendButton = page.locator('[data-test-id="copilot-chat-ready"]');
this.plan = page.locator("div.bg-gray-100.rounded-lg").last();
this.plan = page.getByTestId('select-steps');
this.performStepsButton = page.getByRole('button', { name: 'Confirm' });
this.agentMessage = page.locator('.copilotKitAssistantMessage');
this.userMessage = page.locator('.copilotKitUserMessage');
}

async openChat() {
// Since there's no button to click, just wait for the chat to be ready
// The chat is already open and visible
await this.agentGreeting.isVisible();
}

Expand All @@ -44,59 +39,50 @@ export class HumanInLoopPage {
}

async getPlannerOnClick(name: string | RegExp) {
// Remove iframe reference
return this.page.getByRole('button', { name });
}

async uncheckItem(identifier: number | string): Promise<string> {
// Use the last planner (most recent one)
const plannerContainer = this.page.locator("div.bg-gray-100.rounded-lg").last();
const items = plannerContainer.locator('div.text-sm.flex.items-center');
const plannerContainer = this.page.getByTestId('select-steps');
const items = plannerContainer.getByTestId('step-item');

let item;
if (typeof identifier === 'number') {
item = items.nth(identifier);
} else {
item = items.filter({ hasText: identifier }).first();
item = items.filter({
has: this.page.getByTestId('step-text').filter({ hasText: identifier })
}).first();
}

const text = await item.innerText();

// Force click with JavaScript since the checkboxes might be custom controlled
await item.evaluate((element) => {
const checkbox = element.querySelector('input[type="checkbox"]');
if (checkbox) {
checkbox.click();
} else {
// If no checkbox found, click the element itself
element.click();
}
});
const stepTextElement = item.getByTestId('step-text');
const text = await stepTextElement.innerText();
await item.click();

return text;
}

async isStepItemUnchecked(target: number | string): Promise<boolean> {
// Remove iframe reference
const items = this.page.locator('div.text-sm.flex.items-center');
const plannerContainer = this.page.getByTestId('select-steps');
const items = plannerContainer.getByTestId('step-item');

let item;
if (typeof target === 'number') {
item = items.nth(target);
} else {
item = items.filter({ hasText: target });
item = items.filter({
has: this.page.getByTestId('step-text').filter({ hasText: target })
}).first();
}

const span = item.locator('span');
return await span.evaluate(el => el.classList.contains('line-through'));
const checkbox = item.locator('input[type="checkbox"]');
return !(await checkbox.isChecked());
}

async performSteps() {
await this.performStepsButton.click();
}

async assertAgentReplyVisible(expectedText: RegExp) {
await expect(this.agentMessage.getByText(expectedText)).toBeVisible();
await expect(this.agentMessage.last().getByText(expectedText)).toBeVisible();
}

async assertUserMessageVisible(message: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@ export class PredictiveStateUpdatesPage {
readonly approveButton: Locator;
readonly acceptedButton: Locator;
readonly confirmedChangesResponse: Locator;
readonly rejectedChangesResponse: Locator;
readonly agentMessage: Locator;
readonly userMessage: Locator;
readonly highlights: Locator;

constructor(page: Page) {
this.page = page;
// Remove iframe references and use actual greeting text
this.agentGreeting = page.getByText("Hi 👋 How can I help with your document?");
this.chatInput = page.getByRole('textbox', { name: 'Type a message...' });
this.sendButton = page.locator('[data-test-id="copilot-chat-ready"]');
this.agentResponsePrompt = page.locator('div.tiptap.ProseMirror');
this.userApprovalModal = page.locator('div.bg-white.rounded.shadow-lg >> text=Confirm Changes');
this.acceptedButton = page.getByText('✓ Accepted');
this.confirmedChangesResponse = page.locator('div.copilotKitMarkdown');
this.confirmedChangesResponse = page.locator('div.copilotKitMarkdown').first();
this.rejectedChangesResponse = page.locator('div.copilotKitMarkdown').last();
this.highlights = page.locator('.tiptap em');
this.agentMessage = page.locator('.copilotKitAssistantMessage');
this.userMessage = page.locator('.copilotKitUserMessage');
Expand All @@ -45,51 +46,42 @@ export class PredictiveStateUpdatesPage {
}

async getButton(page, buttonName) {
// Remove iframe reference
return page.getByRole('button', { name: buttonName }).click();
}

async getStatusLabelOfButton(page, statusText) {
// Remove iframe reference
return page.getByText(statusText, { exact: true });
}

async getUserApproval() {
await this.userApprovalModal.isVisible();
await this.getButton(this.page, "Confirm");
const acceptedLabel = this.userApprovalModal.locator('text=✓ Accepted');
// await expect(acceptedLabel).toBeVisible();
// const acceptedLabel = await this.getStatusLabelOfButton(this.page, "✓ Accepted");
// await acceptedLabel.isVisible();
}

async getUserRejection() {
await this.userApprovalModal.isVisible();
await this.getButton(this.page, "Reject");
await this.acceptedButton.isVisible();
const acceptedLabel = await this.getStatusLabelOfButton(this.page, "✕ Rejected");
await acceptedLabel.isVisible();
const rejectedLabel = await this.getStatusLabelOfButton(this.page, "✕ Rejected");
await rejectedLabel.isVisible();
}

async verifyAgentResponse(dragonName) {
// Remove iframe reference
const paragraphWithName = await this.page.locator(`div.tiptap >> text=${dragonName}`).first();

const fullText = await paragraphWithName.textContent();
if (!fullText) {
return null;
}

const match = fullText.match(new RegExp(dragonName, 'i')); // case-insensitive
const match = fullText.match(new RegExp(dragonName, 'i'));
return match ? match[0] : null;
}

async verifyHighlightedText(){
// Check for highlights BEFORE accepting the changes
// The highlights appear when changes are proposed, not after they're accepted
const highlightSelectors = [
'.tiptap em', // For new/added text
'.tiptap s', // For strikethrough/removed text
'.tiptap em',
'.tiptap s',
'div.tiptap em',
'div.tiptap s'
];
Expand All @@ -98,16 +90,13 @@ export class PredictiveStateUpdatesPage {
for (const selector of highlightSelectors) {
count = await this.page.locator(selector).count();
if (count > 0) {
console.log(`Found ${count} highlighted elements with selector: ${selector}`);
break;
}
}

if (count > 0) {
expect(count).toBeGreaterThan(0);
} else {
// If no highlights found, verify the changes are visible in the modal instead
console.log("No highlights in document, checking for confirmation modal");
const modal = this.page.locator('div.bg-white.rounded.shadow-lg');
await expect(modal).toBeVisible();
}
Expand Down
Loading
Loading