Merge pull request 'Adjust group creation to avoid a full reload' (#283) from group-specs into main
Reviewed-on: #283
This commit is contained in:
commit
8306cfe249
@ -18,6 +18,7 @@ import { Toast } from 'primereact/toast';
|
|||||||
import { Suspense, useEffect, useRef, useState } from 'react';
|
import { Suspense, useEffect, useRef, useState } from 'react';
|
||||||
import InvitationsBoard from '@/app/ui/invitations/board';
|
import InvitationsBoard from '@/app/ui/invitations/board';
|
||||||
import { Invitation, InvitationSerializer } from '@/app/lib/invitation';
|
import { Invitation, InvitationSerializer } from '@/app/lib/invitation';
|
||||||
|
import { Entity } from '@/app/lib/definitions';
|
||||||
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
@ -88,7 +89,22 @@ export default function Page() {
|
|||||||
|
|
||||||
const [invitations, setInvitations] = useState<Array<Invitation>>([]);
|
const [invitations, setInvitations] = useState<Array<Invitation>>([]);
|
||||||
|
|
||||||
|
function updateList<T extends Entity>(originalList: T[], element: T): T[] {
|
||||||
|
{
|
||||||
|
const index = originalList.findIndex(g => g.id === element?.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
// Replace existing element
|
||||||
|
return [
|
||||||
|
element!,
|
||||||
|
...originalList.slice(0, index),
|
||||||
|
...originalList.slice(index + 1)
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
// Add new element at the start
|
||||||
|
return [element!, ...originalList];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
@ -100,21 +116,8 @@ export default function Page() {
|
|||||||
key={guestBeingEdited?.id}
|
key={guestBeingEdited?.id}
|
||||||
groups={groups}
|
groups={groups}
|
||||||
onCreate={(newGuest) => {
|
onCreate={(newGuest) => {
|
||||||
setGuests(prevGuests => {
|
setGuests(guests => updateList(guests, newGuest));
|
||||||
const index = prevGuests.findIndex(g => g.id === newGuest?.id);
|
setGuestBeingEdited(undefined);
|
||||||
if (index !== -1) {
|
|
||||||
// Replace existing guest
|
|
||||||
return [
|
|
||||||
newGuest!,
|
|
||||||
...prevGuests.slice(0, index),
|
|
||||||
...prevGuests.slice(index + 1)
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
// Add new guest at the start
|
|
||||||
return [newGuest!, ...prevGuests];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setGuestBeingEdited(undefined) ;
|
|
||||||
}}
|
}}
|
||||||
guest={guestBeingEdited}
|
guest={guestBeingEdited}
|
||||||
visible={guestBeingEdited !== undefined}
|
visible={guestBeingEdited !== undefined}
|
||||||
@ -141,7 +144,10 @@ export default function Page() {
|
|||||||
<GroupFormDialog
|
<GroupFormDialog
|
||||||
key={groupBeingEdited?.id}
|
key={groupBeingEdited?.id}
|
||||||
groups={groups}
|
groups={groups}
|
||||||
onCreate={() => { refreshGroups(); setGroupBeingEdited(undefined) }}
|
onCreate={(newGroup) => {
|
||||||
|
setGroups(groups => updateList(groups, newGroup));
|
||||||
|
setGroupBeingEdited(undefined)
|
||||||
|
}}
|
||||||
group={groupBeingEdited}
|
group={groupBeingEdited}
|
||||||
visible={groupBeingEdited !== undefined}
|
visible={groupBeingEdited !== undefined}
|
||||||
onHide={() => { setGroupBeingEdited(undefined) }}
|
onHide={() => { setGroupBeingEdited(undefined) }}
|
||||||
@ -165,7 +171,7 @@ export default function Page() {
|
|||||||
</div>
|
</div>
|
||||||
</ TabPanel>
|
</ TabPanel>
|
||||||
<TabPanel header="Invitations" leftIcon="pi pi-envelope mx-2">
|
<TabPanel header="Invitations" leftIcon="pi pi-envelope mx-2">
|
||||||
<InvitationsBoard guests={guests} invitations={invitations}/>
|
<InvitationsBoard guests={guests} invitations={invitations} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</ TabView>
|
</ TabView>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,7 +14,7 @@ import { useState } from 'react';
|
|||||||
|
|
||||||
export default function GroupFormDialog({ groups, onCreate, onHide, group, visible }: {
|
export default function GroupFormDialog({ groups, onCreate, onHide, group, visible }: {
|
||||||
groups: Group[],
|
groups: Group[],
|
||||||
onCreate?: () => void,
|
onCreate?: (newGroup: Group) => void,
|
||||||
onHide: () => void,
|
onHide: () => void,
|
||||||
group?: Group,
|
group?: Group,
|
||||||
visible: boolean,
|
visible: boolean,
|
||||||
@ -46,15 +46,15 @@ export default function GroupFormDialog({ groups, onCreate, onHide, group, visib
|
|||||||
group.color = color;
|
group.color = color;
|
||||||
group.parentId = parentId;
|
group.parentId = parentId;
|
||||||
|
|
||||||
api.update(serializer, group, () => {
|
api.update(serializer, group, (newGroup) => {
|
||||||
resetForm();
|
resetForm();
|
||||||
onCreate && onCreate();
|
onCreate && onCreate(newGroup);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
api.create(serializer, new Group(undefined, name, undefined, icon, undefined, parentId, color), () => {
|
api.create(serializer, new Group(undefined, name, undefined, icon, undefined, parentId, color), (newGroup) => {
|
||||||
resetForm();
|
resetForm();
|
||||||
onCreate && onCreate();
|
onCreate && onCreate(newGroup);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
65
tests/groups.spec.ts
Normal file
65
tests/groups.spec.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { test, expect, Page } from '@playwright/test'
|
||||||
|
import mockGroupsAPI from './mocks/groups';
|
||||||
|
import mockGuestsAPI from './mocks/guests';
|
||||||
|
|
||||||
|
test('should allow CRUD on groups', async ({ page }) => {
|
||||||
|
await mockGuestsAPI({ page });
|
||||||
|
await mockGroupsAPI({ page });
|
||||||
|
|
||||||
|
await page.goto('/default/dashboard/guests');
|
||||||
|
await page.getByRole('tab', { name: 'Groups' }).click();
|
||||||
|
|
||||||
|
await expect(page.getByRole('button', { name: 'Add new' })).toBeVisible();
|
||||||
|
await expect(page.getByRole('button', { name: 'Reset affinities' })).toBeVisible();
|
||||||
|
|
||||||
|
|
||||||
|
// List all groups
|
||||||
|
await expect(page.getByRole('row')).toHaveCount(3); // 1 header row + 2 data rows
|
||||||
|
|
||||||
|
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(0)).toHaveText('Name');
|
||||||
|
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(1)).toHaveText('Color');
|
||||||
|
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(2)).toHaveText('Confirmed');
|
||||||
|
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(3)).toHaveText('Tentative');
|
||||||
|
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(4)).toHaveText('Pending');
|
||||||
|
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(5)).toHaveText('Declined');
|
||||||
|
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(6)).toHaveText('Considered');
|
||||||
|
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(7)).toHaveText('Total');
|
||||||
|
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(8)).toHaveText('Actions');
|
||||||
|
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toContainText('Pam\'s family');
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('0');
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(3)).toHaveText('0');
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(4)).toHaveText('1');
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(5)).toHaveText('0');
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(6)).toHaveText('2');
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(7)).toHaveText('3');
|
||||||
|
await expect(page.getByRole('row').nth(1).locator('svg:visible')).toHaveCount(3);
|
||||||
|
|
||||||
|
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(0)).toContainText('Pam\'s work');
|
||||||
|
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(2)).toHaveText('0');
|
||||||
|
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(3)).toHaveText('2');
|
||||||
|
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(4)).toHaveText('0');
|
||||||
|
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(5)).toHaveText('0');
|
||||||
|
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(6)).toHaveText('0');
|
||||||
|
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(7)).toHaveText('2');
|
||||||
|
await expect(page.getByRole('row').nth(2).locator('svg:visible')).toHaveCount(3);
|
||||||
|
|
||||||
|
// Add a new group
|
||||||
|
await page.getByRole('button', { name: 'Add new' }).click();
|
||||||
|
const dialog = page.getByRole('dialog');
|
||||||
|
await expect(dialog).toBeVisible();
|
||||||
|
|
||||||
|
await dialog.getByLabel('Name').fill("Pam's friends");
|
||||||
|
await dialog.getByRole('button', { name: 'Create' }).click();
|
||||||
|
|
||||||
|
await expect(dialog).not.toBeVisible();
|
||||||
|
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toContainText('Pam\'s friends');
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('0');
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(3)).toHaveText('0');
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(4)).toHaveText('0');
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(5)).toHaveText('0');
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(6)).toHaveText('0');
|
||||||
|
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(7)).toHaveText('0');
|
||||||
|
await expect(page.getByRole('row').nth(1).locator('svg:visible')).toHaveCount(3);
|
||||||
|
});
|
@ -73,45 +73,3 @@ test('should allow CRUD on guests', async ({ page }) => {
|
|||||||
await page.getByRole('row').nth(1).locator('svg').nth(0).click(); // Click delete icon
|
await page.getByRole('row').nth(1).locator('svg').nth(0).click(); // Click delete icon
|
||||||
await expect(page.getByText('There are 2 elements in the list')).toBeVisible();
|
await expect(page.getByText('There are 2 elements in the list')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should display the list of groups', async ({ page }) => {
|
|
||||||
await mockGroupsAPI({ page });
|
|
||||||
|
|
||||||
await page.goto('/default/dashboard/guests');
|
|
||||||
await page.getByRole('tab', { name: 'Groups' }).click();
|
|
||||||
|
|
||||||
await expect(page.getByRole('button', { name: 'Add new' })).toBeVisible();
|
|
||||||
await expect(page.getByRole('button', { name: 'Reset affinities' })).toBeVisible();
|
|
||||||
|
|
||||||
await expect(page.getByRole('row')).toHaveCount(3); // 1 header row + 2 data rows
|
|
||||||
|
|
||||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(0)).toHaveText('Name');
|
|
||||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(1)).toHaveText('Color');
|
|
||||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(2)).toHaveText('Confirmed');
|
|
||||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(3)).toHaveText('Tentative');
|
|
||||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(4)).toHaveText('Pending');
|
|
||||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(5)).toHaveText('Declined');
|
|
||||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(6)).toHaveText('Considered');
|
|
||||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(7)).toHaveText('Total');
|
|
||||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(8)).toHaveText('Actions');
|
|
||||||
|
|
||||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toContainText('Pam\'s family');
|
|
||||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('0');
|
|
||||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(3)).toHaveText('0');
|
|
||||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(4)).toHaveText('1');
|
|
||||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(5)).toHaveText('0');
|
|
||||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(6)).toHaveText('2');
|
|
||||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(7)).toHaveText('3');
|
|
||||||
await expect(page.getByRole('row').nth(1).locator('svg:visible')).toHaveCount(3);
|
|
||||||
|
|
||||||
|
|
||||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(0)).toContainText('Pam\'s work');
|
|
||||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(2)).toHaveText('0');
|
|
||||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(3)).toHaveText('2');
|
|
||||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(4)).toHaveText('0');
|
|
||||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(5)).toHaveText('0');
|
|
||||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(6)).toHaveText('0');
|
|
||||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(7)).toHaveText('2');
|
|
||||||
await expect(page.getByRole('row').nth(2).locator('svg:visible')).toHaveCount(3);
|
|
||||||
|
|
||||||
});
|
|
@ -2,6 +2,7 @@ import { Page } from "@playwright/test";
|
|||||||
|
|
||||||
export default async function mockGroupsAPI({ page }: { page: Page }): Promise<void> {
|
export default async function mockGroupsAPI({ page }: { page: Page }): Promise<void> {
|
||||||
page.route('*/**/api/default/groups', async route => {
|
page.route('*/**/api/default/groups', async route => {
|
||||||
|
if (route.request().method() === 'GET') {
|
||||||
const json = [
|
const json = [
|
||||||
{
|
{
|
||||||
"id": "ee44ffb9-1147-4842-a378-9eaeb0f0871a",
|
"id": "ee44ffb9-1147-4842-a378-9eaeb0f0871a",
|
||||||
@ -36,5 +37,24 @@ export default async function mockGroupsAPI({ page }: { page: Page }): Promise<v
|
|||||||
];
|
];
|
||||||
|
|
||||||
await route.fulfill({ json })
|
await route.fulfill({ json })
|
||||||
|
} else if (route.request().method() === 'POST') {
|
||||||
|
const json = {
|
||||||
|
"id": "4d55bc34-6f42-4e2e-82a1-71ae32da2466",
|
||||||
|
"name": "Pam's friends",
|
||||||
|
"icon": "pi pi-desktop",
|
||||||
|
"parent_id": null,
|
||||||
|
"color": "#0000ff",
|
||||||
|
"attendance": {
|
||||||
|
"total": 0,
|
||||||
|
"considered": 0,
|
||||||
|
"invited": 0,
|
||||||
|
"confirmed": 0,
|
||||||
|
"declined": 0,
|
||||||
|
"tentative": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await route.fulfill({ json })
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user