/** * E2E tests for connector-authenticated features. * * These tests use a mock connector server (started in global setup) * to test features that require being logged in via the connector. * * All tests run serially because they share a single mock connector server * whose state is reset before each test via `mockConnector.reset()`. */ import type { Page } from '@playwright/test' import { test, expect } from './helpers/fixtures' test.describe.configure({ mode: 'serial' }) /** * When connected, the header shows "packages" and "orgs" links scoped to the user. * This helper waits for the packages link to appear as proof of successful connection. */ async function expectConnected(page: Page, username = 'testuser') { await expect(page.locator(`a[href="/~${username}"]`, { hasText: 'packages' })).toBeVisible({ timeout: 10_000, }) } /** * Open the connector modal by clicking the account menu button, then clicking * the npm CLI menu item inside the dropdown. */ async function openConnectorModal(page: Page) { // The AccountMenu button has aria-haspopup="true" await page.locator('button[aria-haspopup="true"]').click() // In the dropdown menu, click the npm CLI item (menuitem containing ~testuser) await page .getByRole('menuitem') .filter({ hasText: /~testuser/ }) .click() // Wait for the dialog to appear await expect(page.getByRole('dialog')).toBeVisible() } test.describe('Connector Connection', () => { test('connects via URL params and shows connected state', async ({ page, gotoConnected, mockConnector, }) => { await mockConnector.setUserOrgs(['@testorg']) await gotoConnected('/') // Header should show "packages" link for the connected user await expectConnected(page) }) test('opens connector modal and shows connected user', async ({ page, gotoConnected }) => { await gotoConnected('/') await expectConnected(page) await openConnectorModal(page) // The modal should show the connected user await expect(page.getByRole('dialog')).toContainText('testuser') }) test('can disconnect from the connector', async ({ page, gotoConnected }) => { await gotoConnected('/') await expectConnected(page) await openConnectorModal(page) const modal = page.getByRole('dialog') // Click disconnect button await modal.getByRole('button', { name: /disconnect/i }).click() // Close the modal await modal.getByRole('button', { name: /close/i }).click() // The "packages" link should disappear since we're disconnected await expect(page.locator('a[href="/~testuser"]', { hasText: 'packages' })).not.toBeVisible({ timeout: 5000, }) // The account menu button should now show "connect" text (the main button, not dropdown items) await expect(page.getByRole('button', { name: 'connect', exact: true })).toBeVisible() }) }) test.describe('Organization Management', () => { test.beforeEach(async ({ mockConnector }) => { await mockConnector.setOrgData('@testorg', { users: { testuser: 'owner', member1: 'admin', member2: 'developer', }, teams: ['core', 'docs'], teamMembers: { core: ['testuser', 'member1'], docs: ['member2'], }, }) await mockConnector.setUserOrgs(['@testorg']) }) test('shows org members when connected', async ({ page, gotoConnected }) => { await gotoConnected('/@testorg') // The org management region contains the members panel const orgManagement = page.getByRole('region', { name: /organization management/i }) await expect(orgManagement).toBeVisible({ timeout: 10_000 }) // Should show the members list const membersList = page.getByRole('list', { name: /organization members/i }) await expect(membersList).toBeVisible({ timeout: 10_000 }) // Members are shown as ~username links await expect(membersList.getByRole('link', { name: '~testuser' })).toBeVisible() await expect(membersList.getByRole('link', { name: '~member1' })).toBeVisible() await expect(membersList.getByRole('link', { name: '~member2' })).toBeVisible() }) test('can filter members by role', async ({ page, gotoConnected }) => { await gotoConnected('/@testorg') const orgManagement = page.getByRole('region', { name: /organization management/i }) await expect(orgManagement).toBeVisible({ timeout: 10_000 }) const membersList = page.getByRole('list', { name: /organization members/i }) await expect(membersList).toBeVisible({ timeout: 10_000 }) // Click the "admin" filter button (inside "Filter by role" group) await orgManagement .getByRole('group', { name: /filter by role/i }) .getByRole('button', { name: /admin/i }) .click() // Should only show admin member await expect(membersList.getByRole('link', { name: '~member1' })).toBeVisible() await expect(membersList.getByRole('link', { name: '~testuser' })).not.toBeVisible() await expect(membersList.getByRole('link', { name: '~member2' })).not.toBeVisible() }) test('can search members by name', async ({ page, gotoConnected }) => { await gotoConnected('/@testorg') const orgManagement = page.getByRole('region', { name: /organization management/i }) await expect(orgManagement).toBeVisible({ timeout: 10_000 }) const membersList = page.getByRole('list', { name: /organization members/i }) await expect(membersList).toBeVisible({ timeout: 10_000 }) const searchInput = orgManagement.getByRole('searchbox', { name: /filter members/i }) await searchInput.fill('member1') // Should only show matching member await expect(membersList.getByRole('link', { name: '~member1' })).toBeVisible() await expect(membersList.getByRole('link', { name: '~testuser' })).not.toBeVisible() await expect(membersList.getByRole('link', { name: '~member2' })).not.toBeVisible() }) test('can add a new member operation', async ({ page, gotoConnected, mockConnector }) => { await gotoConnected('/@testorg') const orgManagement = page.getByRole('region', { name: /organization management/i }) await expect(orgManagement).toBeVisible({ timeout: 10_000 }) // Click "Add member" button await orgManagement.getByRole('button', { name: /add member/i }).click() // Wait for the add-member form to appear const usernameInput = orgManagement.locator('#new-member-username') await expect(usernameInput).toBeVisible({ timeout: 5000 }) // Fill in the form await usernameInput.fill('newuser') // Select role (SelectField renders id on the