diff --git a/e2e-playwright/pages/project.page.ts b/e2e-playwright/pages/project.page.ts index 3e3ce5cf9..bd9d7ea0d 100644 --- a/e2e-playwright/pages/project.page.ts +++ b/e2e-playwright/pages/project.page.ts @@ -1,4 +1,4 @@ -import { Locator, Page, expect } from '@playwright/test'; +import { expect, Locator, Page } from '@playwright/test'; import { BasePage } from './base.page'; export class ProjectPage extends BasePage { @@ -21,11 +21,13 @@ export class ProjectPage extends BasePage { 'button[aria-label="Create New Project"], button:has-text("Create Project")', ); this.projectAccordion = page.locator('[role="menuitem"]:has-text("Projects")'); - this.projectNameInput = page.locator('dialog-create-project input:first-of-type'); + this.projectNameInput = page.getByRole('textbox', { name: 'Project Name' }); this.submitBtn = page.locator('dialog-create-project button[type=submit]:enabled'); this.workCtxMenu = page.locator('work-context-menu'); this.workCtxTitle = page.locator('.current-work-context-title'); - this.projectSettingsBtn = this.workCtxMenu.locator('button:nth-of-type(4)'); + this.projectSettingsBtn = this.workCtxMenu + .locator('button[aria-label="Project Settings"]') + .or(this.workCtxMenu.locator('button').nth(3)); this.moveToArchiveBtn = page.locator('.e2e-move-done-to-archive'); this.globalErrorAlert = page.locator('.global-error-alert'); } @@ -37,14 +39,12 @@ export class ProjectPage extends BasePage { : projectName; // Hover over the Projects menu item to show the button - const projectsMenuItem = this.page.locator('[role="menuitem"]:has-text("Projects")'); + const projectsMenuItem = this.page.locator('.e2e-projects-btn'); await projectsMenuItem.hover(); await this.page.waitForTimeout(200); // Force click the button even if not visible - const createProjectBtn = this.page - .locator('[role="menuitem"]:has-text("Projects") ~ button, .additional-btn') - .first(); + const createProjectBtn = this.page.locator('.e2e-add-project-btn'); await createProjectBtn.click({ force: true }); // Wait for the dialog to appear @@ -58,13 +58,15 @@ export class ProjectPage extends BasePage { async getProject(index: number): Promise { // Projects are in a menuitem structure, not side-nav-item - return this.page.locator( - `[role="menuitem"]:has-text("Projects") + [role="menuitem"]:nth-of-type(${index})`, + // Get all project menuitems that follow the Projects header + const projectMenuItems = this.page.locator( + '[role="menuitem"]:has-text("Projects") ~ [role="menuitem"]', ); + return projectMenuItems.nth(index - 1); } async navigateToProject(projectLocator: Locator): Promise { - const projectBtn = projectLocator.locator('button:first-of-type'); + const projectBtn = projectLocator.locator('button').first(); await projectBtn.waitFor({ state: 'visible' }); await projectBtn.click(); } @@ -99,17 +101,17 @@ export class ProjectPage extends BasePage { await this.moveToArchiveBtn.click(); } - async createAndGoToDefaultProject(): Promise { + async createAndGoToTestProject(): Promise { // First click on Projects menu item to expand it await this.projectAccordion.click(); // Create a new default project - await this.createProject('Default Project'); + await this.createProject('Test Project'); // Navigate to the created project const projectName = this.testPrefix - ? `${this.testPrefix}-Default Project` - : 'Default Project'; + ? `${this.testPrefix}-Test Project` + : 'Test Project'; const newProject = this.page.locator(`[role="menuitem"]:has-text("${projectName}")`); await newProject.waitFor({ state: 'visible' }); await newProject.click(); @@ -119,18 +121,32 @@ export class ProjectPage extends BasePage { } async addNote(noteContent: string): Promise { - // Click on the add note button - const addNoteBtn = this.page.locator('.add-note-btn, button[aria-label="Add Note"]'); - await addNoteBtn.click(); + // First toggle the notes panel to make it visible + const toggleNotesBtn = this.page.locator('.e2e-toggle-notes-btn'); + await toggleNotesBtn.click(); - // Type the note content - const noteTextarea = this.page - .locator('textarea[placeholder*="note"], textarea.mat-mdc-input-element') - .last(); + // Wait for the notes section to be visible + const notesSection = this.page.locator('notes'); + await notesSection.waitFor({ state: 'visible' }); + + // Click on the add note button (force click to avoid tooltip interference) + const addNoteBtn = this.page.locator('#add-note-btn'); + await addNoteBtn.click({ force: true }); + + // Wait for the dialog to appear in the CDK overlay (full-screen dialog) + const dialogContainer = this.page.locator('mat-dialog-container'); + await dialogContainer.waitFor({ state: 'visible', timeout: 10000 }); + + // Wait for the textarea to appear and type the note content + const noteTextarea = this.page.locator('mat-dialog-container textarea'); await noteTextarea.waitFor({ state: 'visible' }); await noteTextarea.fill(noteContent); - // Press Enter to save the note - await noteTextarea.press('Enter'); + // Click the save button + const saveBtn = this.page.locator('mat-dialog-container #T-save-note'); + await saveBtn.click(); + + // Wait for the dialog to close + await dialogContainer.waitFor({ state: 'hidden', timeout: 5000 }); } } diff --git a/e2e-playwright/tests/all-basic-routes-without-error.spec.ts b/e2e-playwright/tests/all-basic-routes-without-error.spec.ts index e3e37c53e..094b8fc3d 100644 --- a/e2e-playwright/tests/all-basic-routes-without-error.spec.ts +++ b/e2e-playwright/tests/all-basic-routes-without-error.spec.ts @@ -1,6 +1,6 @@ import { test } from '../fixtures/test.fixture'; -const CANCEL_BTN = 'mat-dialog-actions button:nth-of-type(1)'; +const CANCEL_BTN = 'mat-dialog-actions button:first-child'; test.describe('All Basic Routes Without Error', () => { test('should open all basic routes from menu without error', async ({ @@ -15,16 +15,16 @@ test.describe('All Basic Routes Without Error', () => { // Click main side nav item await page.click('side-nav section.main > side-nav-item > button'); - await page.click('side-nav section.main > button:nth-of-type(1)'); + await page.locator('side-nav section.main > button').nth(0).click(); await page.waitForSelector(CANCEL_BTN, { state: 'visible' }); await page.click(CANCEL_BTN); - await page.click('side-nav section.main > button:nth-of-type(2)'); + await page.locator('side-nav section.main > button').nth(1).click(); await page.click('side-nav section.projects button'); await page.click('side-nav section.tags button'); - await page.click('side-nav section.app > button:nth-of-type(1)'); + await page.locator('side-nav section.app > button').nth(0).click(); await page.click('button.tour-settingsMenuBtn'); // Navigate to different routes diff --git a/e2e-playwright/tests/debug-test.spec.ts b/e2e-playwright/tests/debug-test.spec.ts new file mode 100644 index 000000000..6f3ae20db --- /dev/null +++ b/e2e-playwright/tests/debug-test.spec.ts @@ -0,0 +1,121 @@ +import { test, expect } from '../fixtures/test.fixture'; + +test.describe('Debug Project Note', () => { + test('debug project creation and note toggle', async ({ page, projectPage }) => { + // Create and navigate to default project + await projectPage.createAndGoToTestProject(); + + // Verify we're in a project context + const workCtxTitle = page.locator('.current-work-context-title'); + const titleText = await workCtxTitle.textContent(); + console.log('Current work context:', titleText); + + // Check if notes toggle button exists + const toggleNotesBtn = page.locator('.e2e-toggle-notes-btn'); + const toggleExists = await toggleNotesBtn.isVisible(); + console.log('Toggle notes button exists:', toggleExists); + + if (toggleExists) { + await toggleNotesBtn.click(); + + // Wait longer for notes section to appear + await page.waitForTimeout(1000); + + // Check if notes section appears + const notesSection = page.locator('notes'); + const notesVisible = await notesSection.isVisible({ timeout: 5000 }); + console.log('Notes section visible after toggle:', notesVisible); + + if (notesVisible) { + // Check if add note button exists + const addNoteBtn = page.locator('#add-note-btn'); + const addNoteBtnVisible = await addNoteBtn.isVisible({ timeout: 5000 }); + console.log('Add note button visible:', addNoteBtnVisible); + + if (addNoteBtnVisible) { + // Try to click the add note button + console.log('Attempting to click add note button...'); + + // Check for any errors or console messages + page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); + page.on('pageerror', (error) => console.log('PAGE ERROR:', error.message)); + + await addNoteBtn.click({ force: true }); + + // Wait a moment for potential async operations + await page.waitForTimeout(1000); + + // Check for any overlay or dialog elements + const allOverlays = page.locator('.cdk-overlay-container *'); + const overlayCount = await allOverlays.count(); + console.log('Total overlay elements:', overlayCount); + + // Check if dialog appears (it should be in a mat-dialog-container) + const matDialog = page.locator('mat-dialog-container'); + const matDialogVisible = await matDialog.isVisible({ timeout: 2000 }); + console.log('Mat dialog container visible:', matDialogVisible); + + // Also check for CDK overlay + const cdkOverlay = page.locator('.cdk-overlay-container mat-dialog-container'); + const cdkOverlayVisible = await cdkOverlay.isVisible({ timeout: 2000 }); + console.log('CDK overlay dialog visible:', cdkOverlayVisible); + + // Check for any dialog-like elements + const anyDialog = page.locator( + '[role="dialog"], .mat-dialog-container, mat-dialog-container', + ); + const anyDialogCount = await anyDialog.count(); + console.log('Any dialog elements found:', anyDialogCount); + + if (matDialogVisible || cdkOverlayVisible) { + // Check if textarea appears (inside the mat-dialog-container) + const textarea = page.locator('mat-dialog-container textarea'); + const textareaVisible = await textarea.isVisible({ timeout: 5000 }); + console.log('Textarea visible:', textareaVisible); + + if (textareaVisible) { + // Try to fill the textarea + await textarea.fill('Test note content'); + console.log('Filled textarea with test content'); + + // Check if save button exists (also inside the dialog) + const saveBtn = page.locator('mat-dialog-container #T-save-note'); + const saveBtnVisible = await saveBtn.isVisible({ timeout: 5000 }); + console.log('Save button visible:', saveBtnVisible); + + if (saveBtnVisible) { + // Click save button + console.log('Clicking save button...'); + await saveBtn.click(); + console.log('Save button clicked'); + + // Wait a moment for the dialog to close and note to be saved + await page.waitForTimeout(2000); + + // Check if note appears in the notes section + const noteContent = page.locator('notes note'); + const noteExists = await noteContent.count(); + console.log('Number of notes found after save:', noteExists); + } + } + } + } + } else { + // Try to find notes section in different ways + const notesAlternate = page.locator( + '[data-test="notes"], .notes-wrapper, .notes-section', + ); + const notesAlternateExists = await notesAlternate.count(); + console.log('Alternative notes selectors found:', notesAlternateExists); + + // Check if any elements with 'note' in their tag/class exist + const anyNotes = page.locator('*[class*="note"], *[id*="note"]'); + const anyNotesCount = await anyNotes.count(); + console.log('Any note-related elements found:', anyNotesCount); + } + } + + // Add a short wait to see the final state + await page.waitForTimeout(2000); + }); +}); diff --git a/e2e-playwright/tests/issue-provider-panel/issue-provider-panel.spec.ts b/e2e-playwright/tests/issue-provider-panel/issue-provider-panel.spec.ts index 97b820539..d9c234493 100644 --- a/e2e-playwright/tests/issue-provider-panel/issue-provider-panel.spec.ts +++ b/e2e-playwright/tests/issue-provider-panel/issue-provider-panel.spec.ts @@ -1,10 +1,10 @@ import { test } from '../../fixtures/test.fixture'; const PANEL_BTN = '.e2e-toggle-issue-provider-panel'; -const ITEMS1 = '.items:nth-of-type(1)'; -const ITEMS2 = '.items:nth-of-type(2)'; +const ITEMS1 = '.items:first-child'; +const ITEMS2 = '.items:nth-child(2)'; -const CANCEL_BTN = 'mat-dialog-actions button:nth-of-type(1)'; +const CANCEL_BTN = 'mat-dialog-actions button:first-child'; test.describe('Issue Provider Panel', () => { test('should open all dialogs without error', async ({ page, workViewPage }) => { @@ -18,35 +18,35 @@ test.describe('Issue Provider Panel', () => { await page.click('mat-tab-group .mat-mdc-tab:last-child'); await page.waitForSelector('issue-provider-setup-overview', { state: 'visible' }); - await page.click(`${ITEMS1} > button:nth-of-type(1)`); + await page.locator(`${ITEMS1} > button`).nth(0).click(); await page.waitForSelector(CANCEL_BTN, { state: 'visible' }); await page.click(CANCEL_BTN); - await page.click(`${ITEMS1} > button:nth-of-type(2)`); + await page.locator(`${ITEMS1} > button`).nth(1).click(); await page.waitForSelector(CANCEL_BTN, { state: 'visible' }); await page.click(CANCEL_BTN); - await page.click(`${ITEMS1} > button:nth-of-type(3)`); + await page.locator(`${ITEMS1} > button`).nth(2).click(); await page.waitForSelector(CANCEL_BTN, { state: 'visible' }); await page.click(CANCEL_BTN); - await page.click(`${ITEMS2} > button:nth-of-type(1)`); + await page.locator(`${ITEMS2} > button`).nth(0).click(); await page.waitForSelector(CANCEL_BTN, { state: 'visible' }); await page.click(CANCEL_BTN); - await page.click(`${ITEMS2} > button:nth-of-type(2)`); + await page.locator(`${ITEMS2} > button`).nth(1).click(); await page.waitForSelector(CANCEL_BTN, { state: 'visible' }); await page.click(CANCEL_BTN); - await page.click(`${ITEMS2} > button:nth-of-type(3)`); + await page.locator(`${ITEMS2} > button`).nth(2).click(); await page.waitForSelector(CANCEL_BTN, { state: 'visible' }); await page.click(CANCEL_BTN); - await page.click(`${ITEMS2} > button:nth-of-type(4)`); + await page.locator(`${ITEMS2} > button`).nth(3).click(); await page.waitForSelector(CANCEL_BTN, { state: 'visible' }); await page.click(CANCEL_BTN); - await page.click(`${ITEMS2} > button:nth-of-type(5)`); + await page.locator(`${ITEMS2} > button`).nth(4).click(); await page.waitForSelector(CANCEL_BTN, { state: 'visible' }); await page.click(CANCEL_BTN); - await page.click(`${ITEMS2} > button:nth-of-type(6)`); + await page.locator(`${ITEMS2} > button`).nth(5).click(); await page.waitForSelector(CANCEL_BTN, { state: 'visible' }); await page.click(CANCEL_BTN); - await page.click(`${ITEMS2} > button:nth-of-type(7)`); + await page.locator(`${ITEMS2} > button`).nth(6).click(); await page.waitForSelector(CANCEL_BTN, { state: 'visible' }); await page.click(CANCEL_BTN); diff --git a/e2e-playwright/tests/project-note/project-note.spec.ts b/e2e-playwright/tests/project-note/project-note.spec.ts index 7a21d11a3..117cc79e9 100644 --- a/e2e-playwright/tests/project-note/project-note.spec.ts +++ b/e2e-playwright/tests/project-note/project-note.spec.ts @@ -6,9 +6,9 @@ const FIRST_NOTE = `${NOTE}:first-of-type`; const TOGGLE_NOTES_BTN = '.e2e-toggle-notes-btn'; test.describe('Project Note', () => { - test.skip('create a note', async ({ page, projectPage }) => { + test('create a note', async ({ page, projectPage }) => { // Create and navigate to default project - await projectPage.createAndGoToDefaultProject(); + await projectPage.createAndGoToTestProject(); // Add a note await projectPage.addNote('Some new Note'); @@ -22,12 +22,12 @@ test.describe('Project Note', () => { await expect(firstNote).toContainText('Some new Note'); }); - test.skip('new note should be still available after reload', async ({ + test('new note should be still available after reload', async ({ page, projectPage, }) => { // Create and navigate to default project - await projectPage.createAndGoToDefaultProject(); + await projectPage.createAndGoToTestProject(); // Add a note await projectPage.addNote('Some new Note'); diff --git a/e2e-playwright/tests/task-basic/task-crud.spec.ts b/e2e-playwright/tests/task-basic/task-crud.spec.ts index 447a638f9..957ff49a8 100644 --- a/e2e-playwright/tests/task-basic/task-crud.spec.ts +++ b/e2e-playwright/tests/task-basic/task-crud.spec.ts @@ -2,8 +2,8 @@ import { test, expect } from '../../fixtures/test.fixture'; const TASK = 'task'; const TASK_TEXTAREA = 'task textarea'; -const FIRST_TASK = 'task:first-of-type'; -const SECOND_TASK = 'task:nth-of-type(2)'; +const FIRST_TASK = 'task:first-child'; +const SECOND_TASK = 'task:nth-child(2)'; const TASK_DONE_BTN = '.task-done-btn'; test.describe('Task CRUD Operations', () => { diff --git a/src/app/core-ui/side-nav/side-nav.component.html b/src/app/core-ui/side-nav/side-nav.component.html index 461f75a86..01e1b9354 100644 --- a/src/app/core-ui/side-nav/side-nav.component.html +++ b/src/app/core-ui/side-nav/side-nav.component.html @@ -56,7 +56,7 @@ @if (nonHiddenProjects$ | async; as projectList) {
-
+