Skip to content

Commit 0d3978a

Browse files
committed
Add reporting e2e helpers and detailed tests
1 parent fe8c7e9 commit 0d3978a

File tree

6 files changed

+1810
-170
lines changed

6 files changed

+1810
-170
lines changed

e2e/members.spec.ts

Lines changed: 127 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@
33
// TODO: Remove Invitation
44
import { expect, test } from '../playwright/fixtures';
55
import { PLAYWRIGHT_BASE_URL } from '../playwright/config';
6+
import type { Page } from '@playwright/test';
7+
import path from 'path';
8+
import fs from 'fs';
9+
import os from 'os';
610

7-
async function goToMembersPage(page) {
11+
async function goToMembersPage(page: Page) {
812
await page.goto(PLAYWRIGHT_BASE_URL + '/members');
913
}
1014

11-
async function openInviteMemberModal(page) {
15+
async function openInviteMemberModal(page: Page) {
1216
await Promise.all([
1317
page.getByRole('button', { name: 'Invite Member' }).click(),
1418
expect(page.getByPlaceholder('Member Email')).toBeVisible(),
@@ -35,7 +39,7 @@ test('test that new employee can be invited', async ({ page }) => {
3539
await page.getByRole('button', { name: 'Employee' }).click();
3640
await Promise.all([
3741
page.getByRole('button', { name: 'Invite Member', exact: true }).click(),
38-
await expect(page.getByRole('main')).toContainText(`new+${editorId}@editor.test`),
42+
expect(page.getByRole('main')).toContainText(`new+${editorId}@editor.test`),
3943
]);
4044
});
4145

@@ -91,3 +95,123 @@ test('test that organization billable rate can be updated with all existing time
9195
),
9296
]);
9397
});
98+
99+
async function createPlaceholderMemberViaImport(page: Page, placeholderName: string) {
100+
const placeholderEmail = `placeholder+${Math.floor(Math.random() * 100000)}@solidtime-import.test`;
101+
const csvContent = [
102+
'User,Email,Client,Project,Task,Description,Billable,Start date,Start time,End date,End time,Tags',
103+
`${placeholderName},${placeholderEmail},,,,Imported entry,No,2024-01-01,09:00:00,2024-01-01,10:00:00,`,
104+
].join('\n');
105+
106+
// Write CSV to a temp file for upload
107+
const tmpDir = os.tmpdir();
108+
const tmpFile = path.join(tmpDir, `import-${Date.now()}.csv`);
109+
fs.writeFileSync(tmpFile, csvContent);
110+
111+
await page.goto(PLAYWRIGHT_BASE_URL + '/import');
112+
113+
// Select "Toggl Time Entries" import type
114+
await page.locator('select#importType').selectOption({ label: 'Toggl Time Entries' });
115+
116+
// Upload the CSV file
117+
await page.locator('input[type="file"]').setInputFiles(tmpFile);
118+
119+
// Click Import and wait for success
120+
await Promise.all([
121+
page.getByRole('button', { name: 'Import Data' }).click(),
122+
page.waitForResponse(
123+
(response) => response.url().includes('/import') && response.status() === 200
124+
),
125+
]);
126+
127+
// Close the result modal
128+
await page.getByRole('button', { name: 'Close' }).click();
129+
130+
// Clean up temp file
131+
fs.unlinkSync(tmpFile);
132+
}
133+
134+
test('test that changing member role updates the role in the member table', async ({ page }) => {
135+
const placeholderName = 'RoleChange ' + Math.floor(Math.random() * 10000);
136+
137+
// Create a placeholder member via import
138+
await createPlaceholderMemberViaImport(page, placeholderName);
139+
140+
// Go to members page and verify placeholder exists with role "Placeholder"
141+
await goToMembersPage(page);
142+
const memberRow = page.getByRole('row').filter({ hasText: placeholderName });
143+
await expect(memberRow).toBeVisible();
144+
await expect(memberRow.getByText('Placeholder')).toBeVisible();
145+
146+
// Open the edit modal for the placeholder member
147+
await memberRow.getByRole('button').click();
148+
await page.getByRole('menuitem').getByText('Edit').click();
149+
await expect(page.getByRole('dialog')).toBeVisible();
150+
await expect(page.getByRole('heading', { name: 'Update Member' })).toBeVisible();
151+
152+
// Change role to Employee
153+
const roleSelect = page.getByRole('dialog').getByRole('combobox').first();
154+
await roleSelect.click();
155+
await page.getByRole('option', { name: 'Employee' }).click();
156+
157+
// Submit the change and verify the API call succeeds
158+
await Promise.all([
159+
page.getByRole('button', { name: 'Update Member' }).click(),
160+
page.waitForResponse(
161+
(response) =>
162+
response.url().includes('/members/') &&
163+
response.request().method() === 'PUT' &&
164+
response.status() === 200
165+
),
166+
]);
167+
168+
// Verify dialog closed
169+
await expect(page.getByRole('dialog')).not.toBeVisible();
170+
171+
// Verify the role updated in the table
172+
await expect(memberRow.getByText('Employee')).toBeVisible();
173+
});
174+
175+
test('test that merging a placeholder member works', async ({ page }) => {
176+
const placeholderName = 'Merge Target ' + Math.floor(Math.random() * 10000);
177+
178+
// Create a placeholder member via import
179+
await createPlaceholderMemberViaImport(page, placeholderName);
180+
181+
// Go to members page
182+
await goToMembersPage(page);
183+
await expect(page.getByText(placeholderName)).toBeVisible();
184+
185+
// Find the placeholder member row and open actions menu
186+
const placeholderRow = page.getByRole('row').filter({ hasText: placeholderName });
187+
await placeholderRow.getByRole('button').click();
188+
189+
// Click Merge
190+
await page.getByTestId('member_merge').click();
191+
await expect(page.getByRole('dialog')).toBeVisible();
192+
await expect(page.getByRole('heading', { name: 'Merge Member' })).toBeVisible();
193+
194+
// Select the current user (the owner) as merge target via MemberCombobox
195+
const combobox = page.getByRole('dialog').getByRole('combobox');
196+
await combobox.click();
197+
198+
// Wait for dropdown options to load
199+
const firstOption = page.getByRole('option').first();
200+
await expect(firstOption).toBeVisible({ timeout: 10000 });
201+
await firstOption.click();
202+
203+
// Submit merge
204+
await Promise.all([
205+
page.getByRole('button', { name: 'Merge Member' }).click(),
206+
page.waitForResponse(
207+
(response) =>
208+
response.url().includes('/member/') && response.url().includes('/merge-into') && response.ok()
209+
),
210+
]);
211+
212+
// Wait for dialog to close after successful merge
213+
await expect(page.getByRole('dialog')).not.toBeVisible();
214+
215+
// Verify placeholder member is no longer in the members table
216+
await expect(page.getByRole('main').getByText(placeholderName)).not.toBeVisible();
217+
});

e2e/project-members.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ test('test that updating project member billable rate works for existing time en
3232
await page.getByRole('button', { name: 'Add Member' }).click();
3333

3434
await expect(page.getByText('Add Project Member').first()).toBeVisible();
35-
await page.getByRole('button', { name: 'Select a member' }).click();
36-
await page.keyboard.press('Enter');
35+
await page.getByRole('combobox').filter({ hasText: 'Select a member' }).click();
36+
await page.getByRole('option').first().click();
3737
await page.getByRole('button', { name: 'Add Project Member' }).click();
3838

3939
await page

0 commit comments

Comments
 (0)