# PDF Template Reusable Templates Hints

## Test Structure

### Basic Setup
```typescript
import { test, expect } from '@playwright/test';
import { LoginPage } from '../../../../pages/login.page';
import { DashboardMainPage } from '../../../../pages/dashboard-main.page';
import * as dotenv from 'dotenv';

dotenv.config();
test.setTimeout(10 * 60 * 1000);

test('@pdf-templates REG-XXX: Test description', async ({ page }) => {
  const loginPage = new LoginPage(page);
  const dashboardPage = new DashboardMainPage(page);

  const testEmail = process.env.TEST_EMAIL || 'cdp@hy.ly';
  const testPassword = process.env.TEST_PASSWORD || 'BMy2L1rRXZlkKoTU7';

  // Generate unique timestamp-based identifiers
  const timestamp = Date.now();
  const uniqueTemplateName = `My Template ${timestamp}`;
});
```

## Test Categories

### 1. Reusable Template Creation (REG-38)
- Creating new custom PDF templates
- Renaming templates before save
- Adding content/pages to templates
- Saving templates for reuse
- Verifying template persistence

### 2. Template Content Formatting (REG-37)
- Text editor input and formatting
- Toolbar formatting buttons (Bold, Italic, Underline)
- Text alignment options
- Font size changes
- Color and style options
- Content persistence after save

### 3. Add Page Dropdown Templates (REG-39)
- Creating templates via Add Page dropdown
- Renaming templates in modal before initial save
- Adding template content in modal
- Saving and reusing templates from dropdown
- Verifying template appears in list

## Critical Selectors and Patterns

### PDF Templates Navigation (80-second wait)
```typescript
// MANDATORY: Very long wait time for dashboard stabilization
await page.waitForTimeout(80000); // 80 seconds (CRITICAL)
console.log('⏳ Waited 80 seconds for initial dashboard load');

// Try multiple selector approaches in order
const pdfTemplatesRadio = page.getByRole('radio', { name: 'PDF Templates' });
const pdfTemplatesButton = page.getByRole('button', { name: 'PDF Templates' });
const pdfTemplatesText = page.getByText('PDF Templates', { exact: true });

// REQUIRED: Expect assertion
expect(pdfTemplatesClicked, 'PDF Templates should be clickable').toBeTruthy();
```

### Template Section Expansion (Proven Pattern)
```typescript
// Get all template sections
const templateSections = page.locator('[data-testid$="PDF Template"]');
const sectionCount = await templateSections.count();

// Expand ALL collapsed sections
for (let i = 0; i < sectionCount; i++) {
  const section = templateSections.nth(i);
  const sectionName = await section.getAttribute('data-testid');
  const expandButton = section.locator('button');
  const buttonExists = await expandButton.isVisible().catch(() => false);

  if (buttonExists) {
    // PROVEN SELECTOR: Check for down arrow (collapsed state)
    const downArrow = expandButton.locator('svg path[d*="10 11.9062"]');
    const isCollapsed = await downArrow.isVisible().catch(() => false);

    if (isCollapsed) {
      await expandButton.click();
      console.log(`✅ Expanded section: ${sectionName}`);
      await page.waitForTimeout(2000);
    }
  }
}
```

### Three-Dots Menu for Rename (Dashboard Pattern)
```typescript
// PROVEN SELECTOR: Button with aria-haspopup="dialog" and three dots SVG
const threeDotsMenu = page.locator('button:has(svg path[d^="M10.25 12C11.3546 12 12.25 11.1046"])').first();
const menuVisible = await threeDotsMenu.isVisible({ timeout: 5000 });

if (menuVisible) {
  await threeDotsMenu.click();
  console.log('✅ Clicked three-dots menu');
  await page.waitForTimeout(2000);

  // Look for Rename button in dropdown
  const renameButton = page.locator('button:has-text("Rename")').first();
  await renameButton.click();
  await page.waitForTimeout(2000);

  // Enter new name
  const renameInput = page.locator('[contenteditable="true"], input:visible').first();
  await renameInput.click();
  await page.keyboard.press('Control+A');
  await page.keyboard.type(uniqueTemplateName);
  await page.keyboard.press('Enter');
  await page.waitForTimeout(1000);

  // Click outside to confirm (dashboard pattern)
  await page.locator('body').click({ position: { x: 100, y: 100 } });
}
```

### Add Page Dropdown Pattern
```typescript
// Step 1: Click "Add Page" to open dropdown
const addPageText = page.getByText('Add Page', { exact: true });
const addPageVisible = await addPageText.isVisible({ timeout: 5000 });

if (addPageVisible) {
  await addPageText.click();
  console.log('✅ Clicked Add Page - dropdown should open');
  await page.waitForTimeout(2000);

  // Step 2: Click on page option from dropdown
  const pageOptions = [
    'Separator Page',
    'Hyly Dashboards',
    'My Dashboards'
  ];

  for (const option of pageOptions) {
    const pageOption = page.getByText(option, { exact: true });
    const optionVisible = await pageOption.isVisible({ timeout: 3000 });

    if (optionVisible) {
      console.log(`📄 Found "${option}" option in dropdown`);
      await pageOption.click();
      await page.waitForTimeout(3000);
      break;
    }
  }
}
```

### Templates Section in Add Page Panel
```typescript
// Access Templates content section
const templatesSelectors = [
  'p:has-text("Templates")',
  'div.group.flex.cursor-pointer.items-center.justify-between:has(p:has-text("Templates"))',
  page.getByText('Templates', { exact: true })
];

for (const selector of templatesSelectors) {
  const templatesElement = typeof selector === 'string'
    ? page.locator(selector).first()
    : selector;
  const isVisible = await templatesElement.isVisible({ timeout: 5000 });

  if (isVisible) {
    console.log('✅ Found "Templates" section');
    await templatesElement.click();
    await page.waitForTimeout(2000);
    break;
  }
}
```

### Add New Template Modal
```typescript
// Click on "New Template" button to open modal
const addNewTemplateSelectors = [
  'button:has-text("New Template")',
  'p:has-text("New Template")',
  'svg + p:has-text("Add")'
];

// Verify modal is visible
const modalSelectors = [
  'div[data-radix-popper-content-wrapper]',
  '[role="dialog"]',
  '[data-state="open"]'
];

const modal = page.locator('div[data-radix-popper-content-wrapper]').first();
const modalVisible = await modal.isVisible({ timeout: 5000 });
expect(modalVisible, 'Modal should be visible').toBeTruthy();
```

### Template Text Editor (Content Input)
```typescript
// Look for contenteditable text editor
const textEditor = page.locator('[contenteditable="true"]').first();
const editorVisible = await textEditor.isVisible({ timeout: 5000 });

if (editorVisible) {
  console.log('✅ Text editor is visible');

  // Clear existing content
  await textEditor.click();
  await page.keyboard.press('Control+A');
  await page.keyboard.press('Backspace');
  await page.waitForTimeout(500);

  // Type test content
  const testText = 'Test content for PDF template';
  await textEditor.type(testText);
  await page.waitForTimeout(1000);

  // Verify text was entered
  const editorContent = await textEditor.textContent();
  expect(editorContent?.includes('Test content')).toBeTruthy();
}
```

### Toolbar Formatting Buttons (Content Formatting)
```typescript
// IMPORTANT: Select text first to enable formatting buttons
await textEditor.click();
await page.keyboard.press('Control+A');
await page.waitForTimeout(500);

// Find toolbar container
const toolbarContainer = page.locator('div.right-4').first();
const toolbarGroups = toolbarContainer.locator('> *');
const groupCount = await toolbarGroups.count();

let formattingButtonsClicked = 0;

// Process each toolbar group
for (let groupIndex = 0; groupIndex < groupCount; groupIndex++) {
  const group = toolbarGroups.nth(groupIndex);
  const buttons = group.locator('button');
  const buttonCount = await buttons.count();

  // First pass: Click all radio buttons
  for (let i = 0; i < buttonCount; i++) {
    const button = buttons.nth(i);
    const role = await button.getAttribute('role');

    if (role === 'radio') {
      await button.click({ force: true });
      await page.waitForTimeout(500);
    }
  }

  // Second pass: Click formatting buttons (non-radio)
  for (let i = 0; i < buttonCount; i++) {
    const button = buttons.nth(i);
    const role = await button.getAttribute('role');
    const ariaLabel = await button.getAttribute('aria-label') || '';

    if (role !== 'radio') {
      const isVisible = await button.isVisible().catch(() => false);

      if (isVisible && ariaLabel !== 'Insert Link') {
        await button.click({ force: true });
        await page.waitForTimeout(500);

        // Toggle off if it's a formatting button
        if (ariaLabel.match(/bold|italic|underline|strikethrough/i)) {
          formattingButtonsClicked++;
          await button.click({ force: true });
          await page.waitForTimeout(300);
        }
      }
    }
  }
}

expect(formattingButtonsClicked).toBeGreaterThan(0);
```

### Hide Overlapping UI Elements
```typescript
// CRITICAL: Hide overlapping elements that block interactions
await page.evaluate(() => {
  const overlayDiv = document.querySelector('.productfruits--container');
  if (overlayDiv) {
    overlayDiv.style.display = 'none';
    overlayDiv.style.visibility = 'hidden';
    overlayDiv.style.pointerEvents = 'none';
    overlayDiv.style.opacity = '0';
  }
});
await page.waitForTimeout(500);
```

### Template Rename in Modal (Before Save)
```typescript
// Approach 1: Look for rename button
const renameButton = page.locator('button.group-hover\\:bg-transparent.group-hover\\:text-primary:has(svg)').first();
const renameButtonVisible = await renameButton.isVisible({ timeout: 3000 });

if (renameButtonVisible) {
  await renameButton.click();
  await page.waitForTimeout(1000);
}

// Approach 2: Look for name input field
const nameInputSelectors = [
  'input[placeholder*="Template Name" i]',
  'input[aria-label*="Template Name" i]',
  'input[placeholder*="name" i]',
  'input[type="text"]'
];

for (const selector of nameInputSelectors) {
  const nameInput = page.locator(selector).first();
  const isVisible = await nameInput.isVisible({ timeout: 5000 });

  if (isVisible) {
    // Use fill() instead of type() for reliability
    await nameInput.fill(uniqueTemplateName);
    console.log(`✅ Entered template name: "${uniqueTemplateName}"`);

    // Press Enter to confirm
    await nameInput.press('Enter');
    await page.waitForTimeout(1000);
    break;
  }
}
```

### Save Template
```typescript
// Look for Save button
const saveSelectors = [
  'button:has-text("Lock Annotation")',
  'button:has-text("Save")',
  'button:has-text("Update")'
];

const saveButton = page.locator('button:has-text("Save")').first();
const saveVisible = await saveButton.isVisible({ timeout: 5000 });

if (saveVisible) {
  await saveButton.click();
  console.log('✅ Saved template');
  await page.waitForTimeout(2000);

  // Wait for save confirmation
  const successMessage = page.locator('text=/Saved|Success|Updated/i').first();
  const messageVisible = await successMessage.isVisible({ timeout: 3000 });

  if (messageVisible) {
    const message = await successMessage.textContent();
    console.log(`✅ Save confirmation: ${message}`);
  }
}
```

### Re-click PDF Templates Pattern
```typescript
// IMPORTANT: After creating/modifying template, re-click PDF Templates to load editor
await test.step('Re-click PDF Templates to ensure editor is loaded', async () => {
  const pdfTemplatesButton = page.getByRole('button', { name: 'PDF Templates' });
  const buttonVisible = await pdfTemplatesButton.isVisible({ timeout: 3000 });

  if (buttonVisible) {
    await pdfTemplatesButton.click();
    console.log('✅ Re-clicked PDF Templates button');
    await page.waitForTimeout(3000);
  }
});
```

### Navigate Back Pattern
```typescript
// Step 1: Navigate back using Back button or browser back
let navigatedBack = false;

const backButton = page.getByRole('button', { name: /back|close|done/i }).first();
const backVisible = await backButton.isVisible({ timeout: 3000 });

if (backVisible) {
  await backButton.click();
  navigatedBack = true;
  await page.waitForTimeout(3000);
}

if (!navigatedBack) {
  await page.goBack();
  await page.waitForTimeout(3000);
}

// Step 2: IMPORTANT - Re-navigate to PDF Templates
const pdfTemplatesRadio = page.getByRole('radio', { name: 'PDF Templates' });
const radioVisible = await pdfTemplatesRadio.isVisible({ timeout: 5000 });

if (radioVisible) {
  await pdfTemplatesRadio.click();
  await page.waitForTimeout(3000);
}

// Step 3: Expand all sections again to find saved template
// (Use the same section expansion pattern shown above)
```

### Search in PDF Page List
```typescript
// Search for template in page list
const searchInput = page.getByPlaceholder('Search Page');
const searchVisible = await searchInput.isVisible({ timeout: 5000 });

if (searchVisible) {
  console.log('✅ Found search input');
  await searchInput.click();
  await searchInput.fill(uniqueTemplateName);
  console.log(`✅ Searched for: "${uniqueTemplateName}"`);
  await page.waitForTimeout(2000);

  // Check if template appears in search results
  const searchResult = page.getByText(uniqueTemplateName).first();
  const resultVisible = await searchResult.isVisible({ timeout: 5000 });

  if (resultVisible) {
    console.log('✅ Template found in page search list');
  }
}
```

## Required Test Structure

### Test Configuration
- **MANDATORY**: `test.setTimeout(10 * 60 * 1000)` - 10 minutes
- **MANDATORY**: 80-second wait after login before clicking PDF Templates
- **REQUIRED**: Generate unique template names using timestamp
- **CRITICAL**: Use environment variables for credentials

### Import Requirements
```typescript
import { test, expect } from '@playwright/test';
import { LoginPage } from '../../../../pages/login.page';
import { DashboardMainPage } from '../../../../pages/dashboard-main.page';
import * as dotenv from 'dotenv';
```

## Wait Time Requirements

### Critical Wait Times
- **After login**: 80 seconds (MANDATORY - for dashboard stabilization)
- **After clicking PDF Templates**: 3-5 seconds
- **After expanding sections**: 2 seconds
- **After clicking template**: 3-5 seconds
- **After re-clicking PDF Templates**: 3 seconds
- **After opening Add Page dropdown**: 2 seconds
- **After clicking page option**: 3 seconds
- **After clicking Save**: 2-3 seconds
- **After hiding overlapping elements**: 500ms

## Validation Requirements

### MUST Verify
1. PDF Templates navigation succeeds
2. Template sections can be expanded
3. Template creation/selection succeeds
4. Text editor accepts input
5. Formatting buttons are clickable
6. Template can be renamed
7. Template saves successfully
8. Template appears in list after save
9. Template content persists

## Common Patterns

### Pattern 1: Template Creation Flow
```
1. Login → Dashboard loads
2. Wait 80 seconds (CRITICAL)
3. Click PDF Templates
4. Expand all template sections
5. Click "New Custom PDF Template"
6. Re-click PDF Templates to load editor
7. Perform actions (add pages, rename, format)
8. Save template
```

### Pattern 2: Template Reuse Flow
```
1. Navigate back from template
2. Re-click PDF Templates
3. Expand all sections
4. Find saved template by name
5. Click template to open/use it
6. Verify content appears
```

### Pattern 3: Add Page Dropdown Flow
```
1. Click "Add Page" text
2. Wait for dropdown to open
3. Click "Templates" section
4. Click "New Template" button
5. Modal opens
6. Enter content in text editor
7. Rename template in input field
8. Click Save button
9. Re-open Add Page → Templates
10. Click on saved template to use it
```

## Common Issues to Avoid

### DON'T
- Skip the 80-second wait after login
- Use `waitForLoadState('networkidle')` - causes browser closure
- Hardcode credentials - use env vars with fallbacks
- Forget to re-click PDF Templates after creating template
- Forget to expand sections when looking for templates
- Click on search textbox when trying to rename template
- Skip hiding overlapping UI elements before toolbar interactions
- Forget to select text before applying formatting

### DO
- Always use proven selectors (three-dots SVG path, down arrow SVG path)
- Generate unique names using timestamp
- Re-navigate to PDF Templates after navigating back
- Expand ALL sections before searching for templates
- Use three-dots menu pattern for renaming (like dashboards)
- Hide overlapping elements before clicking toolbar buttons
- Select text (Ctrl+A) before applying formatting
- Use `fill()` method instead of type() for input fields

## Expected Flows

### Flow 1: Create Reusable Template (REG-38)
1. Login and wait 80 seconds
2. Navigate to PDF Templates
3. Expand all sections
4. Click "New Custom PDF Template"
5. Re-click PDF Templates
6. Use three-dots menu to rename template
7. Click "Add Page" to open dropdown
8. Select page type (Separator Page, etc.)
9. Save template
10. Navigate back and re-navigate to PDF Templates
11. Expand sections and verify template appears

### Flow 2: Format Template Content (REG-37)
1. Login and wait 80 seconds
2. Navigate to PDF Templates
3. Select/create template
4. Re-click PDF Templates
5. Hide overlapping UI elements
6. Click on text editor
7. Enter content
8. Select text (Ctrl+A)
9. Click formatting buttons (Bold, Italic, etc.)
10. Test alignment options
11. Save formatted content

### Flow 3: Add Page Template (REG-39)
1. Login and wait 80 seconds
2. Navigate to PDF Templates
3. Create new template
4. Re-click PDF Templates
5. Click "Add Page" text
6. Click "Templates" section
7. Click "New Template" button
8. Enter content in modal text editor
9. Rename template in modal input field
10. Click Save
11. Re-open Add Page → Templates
12. Click saved template to add to page
13. Verify content appears

## Success Criteria

### Template Creation
- Template created successfully
- Template renamed before save
- Template saved with confirmation message
- Template appears in list after save

### Template Content
- Text editor accepts input
- Formatting buttons work (Bold, Italic, Underline)
- Alignment options apply correctly
- Font size changes work
- Content persists after save

### Template Reuse
- Template appears in Templates dropdown/list
- Template can be selected and added to page
- Template content appears correctly
- Template is searchable in page list

## Error Handling

### Required Assertions
```typescript
// Navigation
expect(pdfTemplatesClicked, 'PDF Templates should be clickable').toBeTruthy();

// Template sections
expect(sectionCount, 'Template sections should exist').toBeGreaterThan(0);

// Template creation
expect(createButtonClicked, 'Create template should succeed').toBeTruthy();

// Content input
expect(contentEntered, 'Text editor should accept input').toBeTruthy();

// Formatting
expect(formattingButtonsClicked, 'Formatting buttons should work').toBeGreaterThan(0);

// Save
expect(templateSaved, 'Template should save successfully').toBeTruthy();

// Persistence
expect(templateFound, 'Template should appear in list').toBeTruthy();
```

## Debugging Tips

### Log All Template Names
```typescript
console.log('🔍 Debugging: Listing all visible templates...');
const allText = await page.locator('body').textContent();
if (allText?.includes(uniqueTemplateName)) {
  console.log(`✅ Template name "${uniqueTemplateName}" is present on page`);
} else {
  console.log(`⚠️  Template name NOT found`);

  // List available templates
  const templateItems = page.locator('p, div[class*="template"]');
  const count = await templateItems.count();
  for (let i = 0; i < Math.min(count, 20); i++) {
    const text = await templateItems.nth(i).textContent();
    if (text?.trim()) {
      console.log(`   - "${text.trim()}"`);
    }
  }
}
```

### Scroll if Template Not Found
```typescript
if (!templateClicked) {
  console.log('⚠️  Template not found, attempting to scroll...');
  const templatesPanel = page.locator('[class*="template"]').first();
  await templatesPanel.evaluate(el => el.scrollBy(0, 200));
  await page.waitForTimeout(1000);

  // Try again after scrolling
  const templateAfterScroll = page.getByText(uniqueTemplateName).first();
  const isVisibleAfterScroll = await templateAfterScroll.isVisible({ timeout: 5000 });

  if (isVisibleAfterScroll) {
    await templateAfterScroll.click();
    templateClicked = true;
  }
}
```

## Test Execution Notes

- Run in headed mode only: `npx playwright test [spec-file] --headed --project=chromium`
- Always verify file path using Glob before running
- Tests timeout at 10 minutes
- Dashboard auto-saves (no explicit Save button needed for dashboard)
- Templates require explicit Save button click
- Use `--reporter=list` when running multiple tests in sequence
