Add context menu actions for running entries in calendar

This commit is contained in:
Gregor Vostrak
2026-03-03 17:21:53 +01:00
parent 02f6436fd0
commit 1cdae98ed9
5 changed files with 142 additions and 46 deletions

View File

@@ -7,6 +7,7 @@ import {
createProjectViaApi,
createBareTimeEntryViaApi,
createTimeEntryViaApi,
createRunningTimeEntryViaApi,
} from './utils/api';
async function goToCalendar(page: Page) {
@@ -415,6 +416,66 @@ test('test that context menu create time entry opens the create modal', async ({
await expect(page.getByRole('dialog')).toBeVisible();
});
test('test that context menu for running entry shows stop and discard options', async ({
page,
ctx,
}) => {
const description = 'Running ctx menu test ' + Math.floor(1 + Math.random() * 10000);
await createRunningTimeEntryViaApi(ctx, description);
await goToCalendar(page);
await openContextMenu(page, description);
// Running entry should show Stop and Discard, not Edit/Duplicate/Split/Delete
await expect(page.getByRole('menuitem', { name: 'Stop' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Discard' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Edit' })).not.toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Duplicate' })).not.toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Split' })).not.toBeVisible();
});
test('test that context menu stop on running entry sets end time', async ({ page, ctx }) => {
const description = 'Running stop test ' + Math.floor(1 + Math.random() * 10000);
await createRunningTimeEntryViaApi(ctx, description);
await goToCalendar(page);
await openContextMenu(page, description);
const [updateResponse] = await Promise.all([
page.waitForResponse(
(response) =>
response.url().includes('/time-entries/') &&
response.request().method() === 'PUT' &&
response.status() === 200
),
page.getByRole('menuitem', { name: 'Stop' }).click(),
]);
const body = await updateResponse.json();
expect(body.data.end).not.toBeNull();
expect(body.data.description).toBe(description);
});
test('test that context menu discard on running entry deletes it', async ({ page, ctx }) => {
const description = 'Running discard test ' + Math.floor(1 + Math.random() * 10000);
await createRunningTimeEntryViaApi(ctx, description);
await goToCalendar(page);
await openContextMenu(page, description);
await Promise.all([
page.waitForResponse(
(response) =>
response.url().includes('/time-entries/') &&
response.request().method() === 'DELETE' &&
response.status() === 204
),
page.getByRole('menuitem', { name: 'Discard' }).click(),
]);
await expect(page.locator('.fc-event').filter({ hasText: description })).not.toBeVisible();
});
// =============================================
// Employee Permission Tests
// =============================================

View File

@@ -473,6 +473,25 @@ export async function createTimeEntryWithTagViaApi(
return { tag, entry };
}
export async function createRunningTimeEntryViaApi(ctx: TestContext, description: string) {
const start = new Date();
start.setMinutes(start.getMinutes() - 10);
const response = await ctx.request.post(
`${PLAYWRIGHT_BASE_URL}/api/v1/organizations/${ctx.orgId}/time-entries`,
{
data: {
member_id: ctx.memberId,
start: formatTimestamp(start),
description,
billable: false,
},
}
);
expect(response.status()).toBe(201);
const body = await response.json();
return body.data as { id: string; start: string; end: null; description: string };
}
export async function createBareTimeEntryViaApi(
ctx: TestContext,
description: string,