Fix duplicate export declarations and migrate ContactsView with sub-components

- Remove duplicate NOTIFY_INVITE_MISSING and NOTIFY_INVITE_PROCESSING_ERROR exports
- Update InviteOneAcceptView.vue to use correct NOTIFY_INVITE_TRUNCATED_DATA constant
- Migrate ContactsView to PlatformServiceMixin and extract into modular sub-components
- Resolves TypeScript compilation errors preventing web build
This commit is contained in:
Matthew Raymer
2025-07-16 08:03:26 +00:00
parent 6ef4e314ec
commit ad8e9f823b
45 changed files with 3216 additions and 1752 deletions

View File

@@ -1,39 +1,3 @@
/**
* @fileoverview Tests for the new activity/offers system in TimeSafari
*
* CRITICAL UNDERSTANDING: Offer Acknowledgment System
* ===================================================
*
* This file tests the offer acknowledgment mechanism, which was clarified through
* systematic debugging investigation. Key findings:
*
* 1. POINTER-BASED TRACKING:
* - TimeSafari uses `lastAckedOfferToUserJwtId` to track the last acknowledged offer
* - Offers with IDs newer than this pointer are considered "new" and counted
* - The UI shows count of offers newer than the pointer
*
* 2. TWO DISMISSAL MECHANISMS:
* a) COMPLETE DISMISSAL (used in this test):
* - Triggered by: Expanding offers section (clicking chevron)
* - Method: expandOffersToUserAndMarkRead()
* - Action: Sets lastAckedOfferToUserJwtId = newOffersToUser[0].jwtId (newest)
* - Result: ALL offers marked as read, count becomes 0 (hidden)
*
* b) SELECTIVE DISMISSAL:
* - Triggered by: Clicking "Keep all above as new offers"
* - Method: markOffersAsReadStartingWith(jwtId)
* - Action: Sets lastAckedOfferToUserJwtId = nextOffer.jwtId (partial)
* - Result: Only offers above clicked offer marked as read
*
* 3. BROWSER COMPATIBILITY:
* - Initially appeared to be Chromium-specific issue
* - Investigation revealed test logic error, not browser incompatibility
* - Both Chromium and Firefox now pass consistently
*
* @author Matthew Raymer
* @since Investigation completed 2024-12-27
*/
import { test, expect } from '@playwright/test';
import { importUser, generateNewEthrUser, switchToUser } from './testUtils';
@@ -48,21 +12,13 @@ test('New offers for another user', async ({ page }) => {
await page.getByPlaceholder('URL or DID, Name, Public Key').fill(user01Did + ', A Friend');
await expect(page.locator('button > svg.fa-plus')).toBeVisible();
await page.locator('button > svg.fa-plus').click();
// The alert shows "SuccessThey were added." not "Contact Added"
await expect(page.locator('div[role="alert"]')).toContainText('They were added');
// Check if registration prompt appears (it may not if user is not registered)
const noButtonCount = await page.locator('div[role="alert"] button:has-text("No")').count();
if (noButtonCount > 0) {
await page.locator('div[role="alert"] button:has-text("No")').click(); // don't register
}
await expect(page.locator('div[role="alert"] span:has-text("Contact Added")')).toBeVisible();
await page.locator('div[role="alert"] button:has-text("No")').click(); // don't register
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert
await expect(page.locator('div[role="alert"] button > svg.fa-xmark')).toBeHidden(); // ensure alert is gone
// show buttons to make offers directly to people
await page.getByRole('button').filter({ hasText: /See Actions/i }).click();
await page.getByRole('button').filter({ hasText: /See Hours/i }).click();
// make an offer directly to user 1
// Generate a random string of 3 characters, skipping the "0." at the beginning
@@ -88,13 +44,11 @@ test('New offers for another user', async ({ page }) => {
// as user 1, go to the home page and check that two offers are shown as new
await switchToUser(page, user01Did);
await page.goto('./');
await page.waitForLoadState('networkidle');
let offerNumElemForTest = page.getByTestId('newDirectOffersActivityNumber');
await expect(offerNumElemForTest).toHaveText('2');
let offerNumElem = page.getByTestId('newDirectOffersActivityNumber');
await expect(offerNumElem).toHaveText('2');
// click on the number of new offers to go to the list page
await offerNumElemForTest.click();
await offerNumElem.click();
await expect(page.getByText('New Offers To You', { exact: true })).toBeVisible();
await page.getByTestId('showOffersToUser').locator('div > svg.fa-chevron-right').click();
@@ -102,32 +56,28 @@ test('New offers for another user', async ({ page }) => {
await expect(page.getByText(`help of ${randomString2} from #000`)).toBeVisible();
await expect(page.getByText(`help of ${randomString1} from #000`)).toBeVisible();
/**
* OFFER ACKNOWLEDGMENT MECHANISM:
*
* TimeSafari uses a pointer-based system to track which offers are "new":
* - `lastAckedOfferToUserJwtId` stores the ID of the last acknowledged offer
* - Offers newer than this pointer are considered "new" and counted
*
* Two dismissal mechanisms exist:
* 1. COMPLETE DISMISSAL: Expanding the offers section calls expandOffersToUserAndMarkRead()
* which sets lastAckedOfferToUserJwtId = newOffersToUser[0].jwtId (newest offer)
* Result: ALL offers marked as read, count goes to 0
*
* 2. SELECTIVE DISMISSAL: "Keep all above" calls markOffersAsReadStartingWith(jwtId)
* which sets lastAckedOfferToUserJwtId = nextOffer.jwtId (partial dismissal)
* Result: Only offers above the clicked offer are marked as read
*
* This test uses mechanism #1 (expansion) for complete dismissal.
* The expansion already happened when we clicked the chevron above.
*/
// click on the latest offer to keep it as "unread"
await page.hover(`li:has-text("help of ${randomString2} from #000")`);
// await page.locator('li').filter({ hasText: `help of ${randomString2} from #000` }).click();
// await page.locator('div').filter({ hasText: /keep all above/ }).click();
// now find the "Click to keep all above as new offers" after that list item and click it
const liElem = page.locator('li').filter({ hasText: `help of ${randomString2} from #000` });
await liElem.hover();
const keepAboveAsNew = await liElem.locator('div').filter({ hasText: /keep all above/ });
// now see that all offers are dismissed since we expanded the section
await keepAboveAsNew.click();
// now see that only one offer is shown as new
await page.goto('./');
await page.waitForLoadState('networkidle');
offerNumElem = page.getByTestId('newDirectOffersActivityNumber');
await expect(offerNumElem).toHaveText('1');
await offerNumElem.click();
await expect(page.getByText('New Offer To You', { exact: true })).toBeVisible();
await page.getByTestId('showOffersToUser').locator('div > svg.fa-chevron-right').click();
// now see that no offers are shown as new
await page.goto('./');
// wait until the list with ID listLatestActivity has at least one visible item
await page.locator('#listLatestActivity li').first().waitFor({ state: 'visible' });
await expect(page.getByTestId('newDirectOffersActivityNumber')).toBeHidden();
});