# Test info

- Name: Feature case for request-listing >> REQ_023 - Verify comment in Post and Edit comment
- Location: /root/code/portal-automation-test/tests/request/function.spec.ts:683:9

# Error details

```
Error: locator.waitFor: Test timeout of 180000ms exceeded.
Call log:
  - waiting for locator('//p[text()=\'Added New Successfully!\']') to be visible

    at RequestPage.addCommentToNewestPost (/root/code/portal-automation-test/pom/dashboard.page.ts:634:61)
    at /root/code/portal-automation-test/tests/request/function.spec.ts:692:13
    at /root/code/portal-automation-test/tests/request/function.spec.ts:690:9
```

# Page snapshot

```yaml
- complementary:
  - img
  - link "(Portal-Auto-Lawyer) Portal QA":
    - /url: /
    - paragraph: (Portal-Auto-Lawyer)
    - paragraph: Portal QA
  - list:
    - listitem:
      - link "Home":
        - /url: https://qa.loprx.com
        - img
        - text: Home
    - listitem:
      - link "Case":
        - /url: https://qa.loprx.com/cases
        - img
        - text: Case
    - listitem:
      - link "Request":
        - /url: https://qa.loprx.com/requests
        - img
        - text: Request
    - listitem:
      - link "Client":
        - /url: https://qa.loprx.com/clients
        - img
        - text: Client
    - listitem:
      - button "Business/Contact":
        - img
        - text: Business/Contact
    - listitem:
      - button "Inbox/Sent":
        - img
        - text: Inbox/Sent
    - listitem:
      - button "Settings":
        - img
        - text: Settings
  - list:
    - listitem:
      - link "Shared with me":
        - /url: https://qa.loprx.com/shared/requests
        - img
        - text: Shared with me
- banner:
  - button:
    - img
  - button:
    - img
  - img
  - text: Link Case First shared some information about New Case 123 Letter of Protection. Find out what it says. 1 week ago
  - img
  - text: What did Tri01 Duc say? Their response to Test Request Letter of Protection is waiting. 6 days ago
  - img
  - text: Tri01 Duc shared some information about Test Request Letter of Protection. Find out what it says. 6 days ago
  - img
  - text: What did Tri01 Duc say? Their response to Test Request Letter of Protection is waiting. 6 days ago
  - img
  - img
  - textbox "Search..."
  - img
  - img
  - paragraph: No notifications yet
  - paragraph: When you get notifications, they'll show up here
  - button "Refresh"
  - list:
    - listitem:
      - img
      - paragraph: Pause notifications...
      - list:
        - listitem: For 30 minutes
        - listitem: For 1 hour
        - listitem: For 2 hours
        - listitem: Until tomorrow
    - listitem:
      - img
    - listitem:
      - img
    - listitem:
      - img
    - listitem:
      - img
    - listitem:
      - img
    - listitem:
      - img
  - img
  - button "4 Cart":
    - img
    - text: 4 Cart
  - text: Feedback
  - img
  - link "Open user menu":
    - /url: "#"
    - text: Minh
- img
- text: "#744875 Letter of Protection"
- img
- img
- img
- link "Test Request":
  - /url: /clients/4dfe6c2b-b978-4bbd-9b29-04f2c444c8f8
- text: "-"
- 'link "DOB: 03/01/1999"':
  - /url: /clients/4dfe6c2b-b978-4bbd-9b29-04f2c444c8f8
- 'link "Case: #990951"':
  - /url: /cases/990951
- text: "-"
- 'link "DOI: __/__/____"':
  - /url: /cases/990951
- 'link "Category: Unspecified Accident"':
  - /url: /cases/990951
- text: Tag
- img
- 'button "Status: Open"'
- button "Status Details:":
  - text: "Status Details:"
  - listbox:
    - listitem:
      - text: Awaiting
      - img
  - img
- 'button "Assignee: --"':
  - text: "Assignee: --"
  - img
- button:
  - img
- button:
  - img
- button "Activity":
  - img
  - text: Activity
- button "Share":
  - img
  - text: Share
- paragraph: Minh Phong
- text: Posted
- paragraph: 09/23/2025
- img
- list:
  - listitem
- text: Read more comment Minh Phong 1 day ago Reply from A, 5708993637
- img
- text: Tri01 Duc 1 day ago Reply from B, 1811821289
- textbox "Message"
- button [disabled]:
  - img
- text: Comments here are public to anyone with access to this request, unlike the members-only private discussion room.
- paragraph: Minh Phong
- text: Posted
- paragraph: 07/08/2025
- img
- img
- list:
  - listitem
- text: Read more comment Minh Phong 09/23/2025 Reply from A, 1548630723
- img
- text: Tri01 Duc 09/23/2025 Reply from B, 2115165788
- textbox "Comment..."
- button [disabled]:
  - img
- heading "Snapshot" [level=2]
- list:
  - listitem: Minh Phong 09/23/2025 Responded Respond
  - listitem: Minh Phong 07/08/2025 Submitted a request
- heading "Discussion" [level=2]
- text: Start a discussion
- img
- heading "Check Discussion" [level=4]
- text: Inbox from A, 7211467606 1 day ago
- img
- heading "Minh Phong (You)" [level=4]
- img
- heading "Team Discussion" [level=4]
- img
- heading "Discussion" [level=2]
- text: Start a discussion
- img
- heading "Check Discussion" [level=4]
- text: Inbox from A, 7211467606 1 day ago
- img
- heading "Minh Phong (You)" [level=4]
- img
- heading "Team Discussion" [level=4]
- img
- img "image-share-this-request"
- paragraph: Share this request using the Share button.
- text: Share
```

# Test source

```ts
  534 |             await closeButton.click();
  535 |         } catch (error) {
  536 |             console.log('No notification popup appeared');
  537 |         }
  538 |
  539 |         await this.dashboardLoc.notification.btnNotification.waitFor({ state: 'visible' });
  540 |         await this.dashboardLoc.notification.btnNotification.click({ force: true });
  541 |         await this.dashboardLoc.notification.notificationType(type).click();
  542 |         if (gotoFirstNoti) {
  543 |             await this.dashboardLoc.notification.listNotiReminder.first().click();
  544 |         }
  545 |     }
  546 |
  547 |     async addMultipleTags(tagCount: number, tagPrefix: string = "test"): Promise<void> {
  548 |         await this.dashboardLoc.tag.divTag.click();
  549 |         await this.dashboardLoc.tag.inputTag.first().waitFor({ state: 'visible' });
  550 |         for (let i = 0; i < tagCount; i++) {
  551 |             await this.dashboardLoc.tag.inputTag.first().fill(`${tagPrefix}${i}`);
  552 |             await this.dashboardLoc.tag.inputTag.first().press("Enter");
  553 |         }
  554 |     }
  555 |
  556 |     async saveTags(): Promise<void> {
  557 |         await this.dashboardLoc.tag.btnSaveTag.click();
  558 |         await this.waitForSecond(1);
  559 |     }
  560 |
  561 |     async getTagCount(): Promise<number> {
  562 |         return (await this.dashboardLoc.tag.listTagDetail.all()).length;
  563 |     }
  564 |
  565 |     async removeOneTag(): Promise<void> {
  566 |         await this.dashboardLoc.tag.listRemoveTag.first().click();
  567 |     }
  568 |
  569 |     async clearAllTags(): Promise<void> {
  570 |         await this.dashboardLoc.tag.listTagDetail.first().click();
  571 |         await this.dashboardLoc.tag.btnClearAllTag.waitFor({ state: 'visible' });
  572 |         await this.waitForSecond(1);
  573 |         await this.dashboardLoc.tag.btnClearAllTag.click({ force: true });
  574 |     }
  575 |
  576 |     async showEntries(entries: Number) {
  577 |         const inputShowSelector = `//input[@id='input_per_page']`;
  578 |         await this.page.locator(inputShowSelector).click();
  579 |         await this.page.locator(inputShowSelector).fill(entries.toString());
  580 |         await this.page.waitForLoadState("domcontentloaded");
  581 |     }
  582 |
  583 |     /**
  584 |      * Universal bulk edit verification function with dynamic parameters
  585 |      * Optimized for reuse across multiple test cases and page types
  586 |      * @param tabName - The tab name to click and verify ("Contacts", "Cases", "Request", etc.)
  587 |      * @param entryCount - Number of entries to show in the table
  588 |      * @param detailPageName - Name of the detail page for back navigation verification
  589 |      * @param shouldNavigateBack - Whether to navigate back after verification (default: true)
  590 |      */
  591 |     async verifyBulkEditForTab(
  592 |         tabName: string,
  593 |         entryCount: number,
  594 |         detailPageName: string,
  595 |         shouldNavigateBack: boolean = true
  596 |     ): Promise<void> {
  597 |         // Navigate to tab and show entries
  598 |         await this.dashboardLoc.detail.tabName(tabName).waitFor({ state: 'visible' });
  599 |         await this.waitForSecond(1.5);
  600 |         await this.dashboardLoc.detail.tabName(tabName).click({ force: true });
  601 |         await this.showEntries(entryCount);
  602 |         await this.waitForSecond(2);
  603 |
  604 |         // Get original table data
  605 |         const originalData = await this.getTableData(0);
  606 |
  607 |         // Click Bulk Edit and verify
  608 |         await this.dashboardLoc.buttonByText("Bulk Edit").click();
  609 |         await this.dashboardLoc.common.spanText("Bulk Edit").first().waitFor({ state: 'visible' });
  610 |         await this.waitForSecond(2);
  611 |
  612 |         // Get bulk edit table data and compare
  613 |         const bulkEditData = await this.getTableData(1, false, true);
  614 |
  615 |         // Data validation (without expect as requested)
  616 |         if (JSON.stringify(originalData) !== JSON.stringify(bulkEditData)) {
  617 |             throw new Error(`Bulk edit data mismatch in ${tabName} tab: Original ${JSON.stringify(originalData)} vs Bulk Edit ${JSON.stringify(bulkEditData)}`);
  618 |         }
  619 |
  620 |         // Navigate back if required
  621 |         if (shouldNavigateBack) {
  622 |             await this.dashboardLoc.buttonByText("Back").first().click();
  623 |             await this.dashboardLoc.common.spanText(detailPageName).waitFor({ state: 'visible' });
  624 |         }
  625 |     }
  626 |
  627 |     // Add a comment to the newest post
  628 |     async addCommentToNewestPost(content: string): Promise<void> {
  629 |         await this.dashboardLoc.comment.cmtInPost.first().waitFor({ state: 'visible' });
  630 |         await this.dashboardLoc.comment.cmtInPost.first().click();
  631 |         await this.dashboardLoc.comment.inputComment.first().fill(content);
  632 |         await this.waitForSecond(1);
  633 |         await this.dashboardLoc.comment.btnSubmitComment.first().click({ force: true });
> 634 |         await this.dashboardLoc.comment.msgCreateCmtSuccess.waitFor({ state: 'visible' })
      |                                                             ^ Error: locator.waitFor: Test timeout of 180000ms exceeded.
  635 |     }
  636 |
  637 |     async createDiscussion(title: string, memberName: string, message: string): Promise<void> {
  638 |         await this.dashboardLoc.discussion.btnCreateDiscussion.click();
  639 |         await this.dashboardLoc.modal.headerModal("Start a discussion").waitFor({ state: 'visible' });
  640 |         await this.dashboardLoc.discussion.inputTitle.fill(title);
  641 |         await this.dashboardLoc.modal.inputByPlaceHolder("Type or select an email address").nth(1).fill(memberName);
  642 |         await this.dashboardLoc.discussion.dropdownMemberShare(memberName).waitFor({ state: "visible", timeout: 15_000 });
  643 |         await this.waitForSecond(2);
  644 |         await this.dashboardLoc.discussion.dropdownMemberShare(memberName).click();
  645 |         await this.dashboardLoc.modal.headerModal("Start a discussion").click();
  646 |         await this.dashboardLoc.discussion.msgFirstDis.fill(message);
  647 |         await this.dashboardLoc.modal.headerModal("Start a discussion").click();
  648 |         await this.dashboardLoc.buttonByText("Start").click({ force: true });
  649 |         await this.dashboardLoc.modal.headerModal("Start a discussion").waitFor({ state: 'hidden' });
  650 |         await this.dashboardLoc.msgCreateSuccess.waitFor({ state: 'visible' });
  651 |     }
  652 |
  653 |     async addMessageToDiscussion(message: string): Promise<void> {
  654 |         await this.dashboardLoc.discussion.inputMessage.fill(message);
  655 |         await this.waitForSecond(1);
  656 |         await this.dashboardLoc.discussion.btnSendMsg.click();
  657 |         await this.waitForSecond(2);
  658 |     }
  659 |
  660 |     async getNewestRequestHistory(): Promise<string> {
  661 |         await this.dashboardLoc.buttonByText("Activity").click();
  662 |         const textActivity = await this.dashboardLoc.modalActivity.listActivity.first().innerText();
  663 |         await this.dashboardLoc.modalActivity.btnClose.click();
  664 |         return textActivity.replace(/\s+/g, ' ').trim();
  665 |     }
  666 |
  667 |     async getRowCount(): Promise<number> {
  668 |         return this.dashboardLoc.table.rowsInTable.count();
  669 |     }
  670 |
  671 |     async openGlobalSearch() {
  672 |         await expect(this.dashboardLoc.iconSearchGlobal).toBeVisible();
  673 |         await this.dashboardLoc.iconSearchGlobal.click();
  674 |     }
  675 |
  676 |     async switchTab(targetTab: 'My' | 'All'): Promise<void> {
  677 |         if (targetTab === 'My') {
  678 |             await this.dashboardLoc.tabMyRequest.click();
  679 |         } else {
  680 |             await this.dashboardLoc.tabAllRequest.click();
  681 |         }
  682 |     }
  683 |
  684 |     async getExpectedResultText(count: number): Promise<string> {
  685 |         return count > 1
  686 |             ? `There are ${count} results found.`
  687 |             : `There are ${count} result found.`;
  688 |     }
  689 | }
```