# Dashboard and Widget Test Script Review Hints

## Context
These hints are for reviewing Playwright test scripts that interact with dashboards and widgets in the Halo QA application, including dashboard operations like delete, duplicate, and favorites management.

## Critical Requirements

### 1. Test Structure (MANDATORY)
✅ **MUST Have:**
```typescript
// Set timeout at the beginning for slow builds
test.setTimeout(10 * 60 * 1000); // 10 minutes standard timeout

// Use test.step() for organization and reporting
await test.step('Step Name', async () => {
  // Step implementation
});

// Test description pattern
test('@dashboards TEST-ID: Test description', async ({ page }) => {
  // Test implementation
});
```

### 2. Page Object Model Usage
✅ **Required Imports:**
```typescript
import { test, expect } from '@playwright/test';
import { LoginPage } from '../../../../pages/login.page';
import { DashboardMainPage } from '../../../../pages/dashboard-main.page';
import { WidgetsPage } from '../../../../pages/widgets.page';
import * as dotenv from 'dotenv';  // For environment variables
```

✅ **Page Object Initialization:**
```typescript
const loginPage = new LoginPage(page);
const dashboardPage = new DashboardMainPage(page);
const widgetsPage = new WidgetsPage(page);
```

### 3. Login Pattern (CRITICAL)
✅ **Correct Login Flow:**
```typescript
await loginPage.goto();
await loginPage.login('cdp@hy.ly', 'rnJi75ESFutATsbDN');
// OR with env variables
await loginPage.login(process.env.TEST_EMAIL!, process.env.TEST_PASSWORD!);
```

❌ **NEVER:**
- Navigate directly to other apps without logging in first
- Use page.goto() for initial navigation (use loginPage.goto())
- Hardcode credentials without fallback

### 4. Dashboard Navigation Pattern
✅ **Proven Selectors:**
```typescript
// Navigate to Dashboards
await dashboardPage.navigateToDashboards();

// Create new dashboard
await dashboardPage.clickNewDashboard();

// Generate unique names
const uniqueDashboardName = await dashboardPage.generateUniqueDashboardName('Prefix');
```

### 5. Widget Selection Pattern
✅ **Widget Workflow:**
```typescript
// Open widget selection
await widgetsPage.clickSeeAllWidgets();

// Select specific widget (use appropriate method)
await widgetsPage.selectTouredConversions();
// OR
await widgetsPage.selectFirstTouchAttribution();

// Add cards
await widgetsPage.addFirstCard();
// OR for multiple cards
const cardsAdded = await widgetsPage.addAllDistinctCards(WIDGET_NAME, TOTAL_CARDS);
```

### 6. Wait Strategies (CRITICAL)
✅ **Acceptable Waits:**
```typescript
await page.waitForTimeout(2000); // 2-5 seconds for stability
await element.waitFor({ state: 'visible', timeout: 30000 });
await dashboardPage.waitForDashboardLoad();
```

❌ **NEVER Use:**
```typescript
await page.waitForLoadState('networkidle'); // Causes browser closure issues
```

### 7. Verification Patterns
✅ **Good Assertions:**
```typescript
// Verify login success
const isOnDashboard = await dashboardPage.verifyOnDashboardPage();
expect(isOnDashboard).toBeTruthy();

// Verify URL
expect(page.url()).toContain('halo-qa.hyly.ai/copilot/report');

// Verify element visibility
await expect(page.locator(`text="${uniqueDashboardName}"`).first()).toBeVisible({ timeout: 5000 });

// Verify card count
expect(cardCount).toBeGreaterThanOrEqual(TOTAL_DISTINCT_CARDS);
```

❌ **Avoid:**
```typescript
expect(true).toBeTruthy(); // Meaningless assertion
```

### 8. Selector Strategies
✅ **Priority Order (Best to Worst):**
1. Page object methods: `dashboardPage.navigateToDashboards()`
2. Semantic selectors: `page.getByText('Dashboards', { exact: true }).first()`
3. Role selectors: `page.getByRole('button', { name: 'New Dashboard' })`
4. Data attributes: `page.locator('[data-testid="New Custom Dashboards"]')`
5. Class selectors: `page.locator('.dashboard-card, .widget-container')`
6. Multiple fallbacks: `'.dashboard-card, .widget-container, [data-testid*="card"]'`

### 9. Dashboard Save Pattern
✅ **Correct Save Flow:**
```typescript
await dashboardPage.renameDashboard(uniqueDashboardName);
await expect(page.locator(`text="${uniqueDashboardName}"`).first()).toBeVisible({ timeout: 5000 });
await dashboardPage.saveDashboard();
```

### 10. Environment Variables
✅ **Acceptable Patterns:**
```typescript
// With dotenv
import * as dotenv from 'dotenv';
dotenv.config();
await loginPage.login(process.env.TEST_EMAIL!, process.env.TEST_PASSWORD!);

// With fallback
await loginPage.login(process.env.TEST_EMAIL || 'cdp@hy.ly', process.env.TEST_PASSWORD || 'rnJi75ESFutATsbDN');

// Hardcoded (only if consistent across all tests)
await loginPage.login('cdp@hy.ly', 'rnJi75ESFutATsbDN');
```

## Special Considerations

### Widget-Specific Requirements
- **Toured Conversions**: 5 distinct cards available
- **First Touch Attribution**: 5 distinct cards available
- Each widget may have different card selection patterns
- Widget panel must be closed after adding cards

### Dashboard Counter System
- Tests use a counter file to generate unique dashboard names
- Located at `utils/dashboard-counter.txt`
- Ensures no naming conflicts between test runs

### Logging and Debugging
✅ **Good Practice:**
```typescript
console.log(`✅ Dashboard created with ${cardCount} cards`);
console.log(`✅ Test completed successfully - Dashboard "${uniqueDashboardName}" created`);
```

## Review Focus Areas

1. **Page Object Usage**: Verify all interactions use page object methods where available
2. **Wait Strategy**: Ensure no `waitForLoadState('networkidle')` usage
3. **Login Flow**: Must go through Halo QA login page
4. **Test Organization**: Must use test.step() for clarity
5. **Unique Naming**: Dashboard names should be unique (use generateUniqueDashboardName)
6. **Assertions**: Must have meaningful assertions, not just test execution

## Acceptable Variations

- Using either hardcoded credentials OR environment variables (with proper fallback)
- Different widget selection methods (as long as they use page objects)
- Various assertion styles (toBeTruthy(), toBe(), toBeGreaterThanOrEqual())
- Console logging for debugging and test progress

## Dashboard Operations Patterns (NEW)

### Delete Dashboard Pattern
✅ **Correct Delete Flow:**
```typescript
// Click three dots menu
const threeDotsMenu = page.locator('.dashboard-more-menu').first();
await threeDotsMenu.click();

// Click Delete option (exact match to avoid clicking Duplicate)
const deleteOption = page.getByRole('menuitem', { name: 'Delete', exact: true });
await deleteOption.click();

// Confirm deletion if dialog appears
const confirmButton = page.locator('button:has-text("Confirm"), button:has-text("Yes")').last();
if (await confirmButton.isVisible({ timeout: 5000 })) {
  await confirmButton.click();
}
```

### Duplicate Dashboard Pattern
✅ **Correct Duplicate Flow:**
```typescript
// Click three dots menu
const threeDotsMenu = page.locator('.dashboard-more-menu').first();
await threeDotsMenu.click();

// Click Duplicate option
const duplicateOption = page.getByRole('menuitem', { name: 'Duplicate', exact: true });
await duplicateOption.click();

// Duplicated dashboard will have " (1)" suffix
const duplicateName = `${originalDashboardName} (1)`;
```

### Add to Favorites Pattern
✅ **Correct Favorites Flow:**
```typescript
// Click star icon to add to favorites
const starButton = page.locator('[data-testid="favourite-button"]');
await starButton.click();

// Confirm in dialog
const addButton = page.getByRole('button', { name: 'Add', exact: true });
await addButton.click();

// Navigate to Favorites section
const favoritesLink = page.getByText('Favorites', { exact: true }).first();
await favoritesLink.click();
```

### Dashboard List Navigation Pattern
✅ **When Dashboard List Not Visible:**
```typescript
// First check if New Dashboard button is visible
const newDashboardButton = page.getByRole('button', { name: 'New Dashboard' });
let newDashboardVisible = await newDashboardButton.isVisible({ timeout: 5000 }).catch(() => false);

// If not visible, click My Dashboards first
if (!newDashboardVisible) {
  const myDashboardsButton = page.locator('[data-testid="Custom Dashboards"]');
  await myDashboardsButton.click();
  await page.waitForTimeout(3000);
}
```

### Dashboard Search and Filter Pattern
✅ **Finding Specific Dashboards:**
```typescript
// Look for dashboard by text content
const dashboardText = page.getByText('Dashboard Name Pattern', { exact: false });

// Check if exists before interacting
const dashboardExists = await dashboardText.isVisible({ timeout: 5000 }).catch(() => false);
if (dashboardExists) {
  await dashboardText.click();
}
```

### Build Reload Handling
✅ **After Major Operations:**
```typescript
// After delete/duplicate operations, wait for build to reload
console.log('Waiting 10 seconds for build to reload...');
await page.waitForTimeout(10000);

// Navigate back to dashboards list
await dashboardPage.navigateToDashboards();
await page.waitForTimeout(3000);
```

## Red Flags to Reject

1. Missing `test.setTimeout()` configuration
2. Using `waitForLoadState('networkidle')`
3. Direct page navigation without login
4. No test.step() organization
5. Missing or meaningless assertions
6. Incorrect import paths for page objects
7. Not using page object methods when available
8. No unique dashboard naming strategy
9. Not using exact match for Delete/Duplicate to avoid confusion
10. Not handling confirmation dialogs for delete operations
11. Not waiting for build reload after major operations