test(server): use new e2e (#11056)

This commit is contained in:
fengmk2
2025-03-24 04:36:49 +00:00
parent cb71b292fc
commit 83a2e3bcba
6 changed files with 1049 additions and 2 deletions

View File

@@ -15,8 +15,8 @@
"test:copilot": "ava \"src/__tests__/copilot-*.spec.ts\"",
"test:coverage": "c8 ava --concurrency 1 --serial",
"test:copilot:coverage": "c8 ava --timeout=5m \"src/__tests__/copilot-*.spec.ts\"",
"e2e": "cross-env TEST_MODE=e2e ava",
"e2e:coverage": "cross-env TEST_MODE=e2e c8 ava",
"e2e": "cross-env TEST_MODE=e2e ava --serial",
"e2e:coverage": "cross-env TEST_MODE=e2e c8 ava --serial",
"data-migration": "cross-env NODE_ENV=development r ./src/data/index.ts",
"init": "yarn prisma migrate dev && yarn data-migration run",
"seed": "r ./src/seed/index.ts",

View File

@@ -33,3 +33,11 @@ e2e('should create workspace with owner', async t => {
});
t.truthy(workspace);
});
e2e('should get current user', async t => {
const user = await app.signup();
await app.switchUser(user);
const res = await app.gql({ query: getCurrentUserQuery });
t.truthy(res.currentUser);
t.is(res.currentUser!.id, user.id);
});

View File

@@ -0,0 +1,696 @@
import { randomUUID } from 'node:crypto';
import {
DocMode,
listNotificationsQuery,
MentionNotificationBodyType,
mentionUserMutation,
notificationCountQuery,
NotificationObjectType,
NotificationType,
readNotificationMutation,
} from '@affine/graphql';
import { Mockers } from '../../mocks';
import { app, e2e } from '../test';
async function init() {
const member = await app.create(Mockers.User);
const owner = await app.create(Mockers.User);
const workspace = await app.create(Mockers.Workspace, {
owner: { id: owner.id },
name: 'test-workspace-name',
avatarKey: 'test-avatar-key',
});
await app.create(Mockers.WorkspaceUser, {
workspaceId: workspace.id,
userId: member.id,
});
return {
member,
owner,
workspace,
};
}
e2e('should mention user in a doc', async t => {
const { member, owner, workspace } = await init();
await app.login(owner);
const { mentionUser: mentionId } = await app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: member.id,
workspaceId: workspace.id,
doc: {
id: 'doc-id-1',
title: 'doc-title-1',
blockId: 'block-id-1',
mode: DocMode.page,
},
},
},
});
t.truthy(mentionId);
// mention user at another doc
const { mentionUser: mentionId2 } = await app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: member.id,
workspaceId: workspace.id,
doc: {
id: 'doc-id-2',
title: 'doc-title-2',
elementId: 'element-id-2',
mode: DocMode.edgeless,
},
},
},
});
t.truthy(mentionId2);
await app.login(member);
const result = await app.gql({
query: listNotificationsQuery,
variables: {
pagination: {
first: 10,
offset: 0,
},
},
});
const notifications = result.currentUser!.notifications.edges.map(
edge => edge.node
);
t.is(notifications.length, 2);
const notification = notifications[1] as NotificationObjectType;
t.is(notification.read, false);
t.truthy(notification.createdAt);
t.truthy(notification.updatedAt);
const body = notification.body as MentionNotificationBodyType;
t.is(body.workspace!.id, workspace.id);
t.is(body.doc.id, 'doc-id-1');
t.is(body.doc.title, 'doc-title-1');
t.is(body.doc.blockId, 'block-id-1');
t.is(body.doc.mode, DocMode.page);
t.is(body.createdByUser!.id, owner.id);
t.is(body.createdByUser!.name, owner.name);
t.is(body.workspace!.id, workspace.id);
t.is(body.workspace!.name, 'test-workspace-name');
t.falsy(body.workspace!.avatarUrl);
const notification2 = notifications[0] as NotificationObjectType;
t.is(notification2.read, false);
t.truthy(notification2.createdAt);
t.truthy(notification2.updatedAt);
const body2 = notification2.body as MentionNotificationBodyType;
t.is(body2.workspace!.id, workspace.id);
t.is(body2.doc.id, 'doc-id-2');
t.is(body2.doc.title, 'doc-title-2');
t.is(body2.doc.elementId, 'element-id-2');
t.is(body2.doc.mode, DocMode.edgeless);
t.is(body2.createdByUser!.id, owner.id);
t.is(body2.workspace!.id, workspace.id);
t.is(body2.workspace!.name, 'test-workspace-name');
t.falsy(body2.workspace!.avatarUrl);
});
e2e('should mention doc mode support string value', async t => {
const { member, owner, workspace } = await init();
await app.login(owner);
const { mentionUser: mentionId } = await app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: member.id,
workspaceId: workspace.id,
doc: {
id: 'doc-id-1',
title: 'doc-title-1',
blockId: 'block-id-1',
mode: 'page' as DocMode,
},
},
},
});
t.truthy(mentionId);
});
e2e('should throw error when mention user has no Doc.Read role', async t => {
const { owner, workspace } = await init();
const otherUser = await app.create(Mockers.User);
await app.login(owner);
const docId = randomUUID();
await t.throwsAsync(
app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: otherUser.id,
workspaceId: workspace.id,
doc: {
id: docId,
title: 'doc-title-1',
blockId: 'block-id-1',
mode: DocMode.page,
},
},
},
}),
{
message: `Mentioned user can not access doc ${docId}.`,
}
);
});
e2e('should throw error when mention a not exists user', async t => {
const { owner, workspace } = await init();
await app.login(owner);
const docId = randomUUID();
await t.throwsAsync(
app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: 'user-id-not-exists',
workspaceId: workspace.id,
doc: {
id: docId,
title: 'doc-title-1',
blockId: 'block-id-1',
mode: DocMode.page,
},
},
},
}),
{
message: `Mentioned user can not access doc ${docId}.`,
}
);
});
e2e('should not mention user oneself', async t => {
const { owner, workspace } = await init();
await app.login(owner);
await t.throwsAsync(
app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: owner.id,
workspaceId: workspace.id,
doc: {
id: 'doc-id-1',
title: 'doc-title-1',
blockId: 'block-id-1',
mode: DocMode.page,
},
},
},
}),
{
message: 'You can not mention yourself.',
}
);
});
e2e('should mark notification as read', async t => {
const { member, owner, workspace } = await init();
await app.login(owner);
const { mentionUser: mentionId } = await app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: member.id,
workspaceId: workspace.id,
doc: {
id: 'doc-id-1',
title: 'doc-title-1',
blockId: 'block-id-1',
mode: DocMode.page,
},
},
},
});
t.truthy(mentionId);
await app.login(member);
const result = await app.gql({
query: listNotificationsQuery,
variables: {
pagination: {
first: 10,
offset: 0,
},
},
});
t.truthy(mentionId);
const notifications = result.currentUser!.notifications.edges.map(
edge => edge.node
);
for (const notification of notifications) {
t.is(notification.read, false);
await app.gql({
query: readNotificationMutation,
variables: {
id: notification.id,
},
});
}
const count = await app.gql({
query: notificationCountQuery,
});
t.is(count.currentUser!.notificationCount, 0);
// read again should work
for (const notification of notifications) {
t.is(notification.read, false);
await app.gql({
query: readNotificationMutation,
variables: {
id: notification.id,
},
});
}
});
e2e('should throw error when read the other user notification', async t => {
const { member, owner, workspace } = await init();
await app.login(owner);
const { mentionUser: mentionId } = await app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: member.id,
workspaceId: workspace.id,
doc: {
id: 'doc-id-1',
title: 'doc-title-1',
blockId: 'block-id-1',
mode: DocMode.page,
},
},
},
});
t.truthy(mentionId);
await app.login(member);
const result = await app.gql({
query: listNotificationsQuery,
variables: {
pagination: {
first: 10,
offset: 0,
},
},
});
const notifications = result.currentUser!.notifications.edges.map(
edge => edge.node
);
t.is(notifications[0].read, false);
await app.login(owner);
await t.throwsAsync(
app.gql({
query: readNotificationMutation,
variables: {
id: notifications[0].id,
},
}),
{
message: 'Notification not found.',
}
);
// notification not exists
await t.throwsAsync(
app.gql({
query: readNotificationMutation,
variables: {
id: 'notification-id-not-exists',
},
}),
{
message: 'Notification not found.',
}
);
});
e2e('should throw error when mention call with invalid params', async t => {
await app.signup();
await t.throwsAsync(
app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: '1',
workspaceId: '1',
doc: {
id: '1',
title: 'doc-title-1'.repeat(100),
blockId: '1',
mode: DocMode.page,
},
},
},
}),
{
message: /Validation error/,
}
);
});
e2e('should throw error when mention mode value is invalid', async t => {
await app.signup();
await t.throwsAsync(
app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: randomUUID(),
workspaceId: randomUUID(),
doc: {
id: randomUUID(),
title: 'doc-title-1',
blockId: 'block-id-1',
mode: 'invalid-mode' as DocMode,
},
},
},
}),
{
message:
'Variable "$input" got invalid value "invalid-mode" at "input.doc.mode"; Value "invalid-mode" does not exist in "DocMode" enum.',
}
);
});
e2e('should get empty notifications', async t => {
await app.signup();
const result = await app.gql({
query: listNotificationsQuery,
variables: {
pagination: {
first: 10,
offset: 0,
},
},
});
const notifications = result.currentUser!.notifications.edges.map(
edge => edge.node
);
t.is(notifications.length, 0);
t.is(result.currentUser!.notifications.totalCount, 0);
});
e2e('should list and count notifications', async t => {
const { member, owner, workspace } = await init();
await app.login(owner);
await app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: member.id,
workspaceId: workspace.id,
doc: {
id: 'doc-id-1',
title: 'doc-title-1',
blockId: 'block-id-1',
mode: DocMode.page,
},
},
},
});
await app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: member.id,
workspaceId: workspace.id,
doc: {
id: 'doc-id-2',
title: 'doc-title-2',
blockId: 'block-id-2',
mode: DocMode.page,
},
},
},
});
await app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: member.id,
workspaceId: workspace.id,
doc: {
id: 'doc-id-3',
title: 'doc-title-3',
blockId: 'block-id-3',
mode: DocMode.page,
},
},
},
});
// mention user in another workspace
const workspace2 = await app.create(Mockers.Workspace, {
owner: { id: owner.id },
name: 'test-workspace-name2',
avatarKey: 'test-avatar-key2',
});
await app.create(Mockers.WorkspaceUser, {
workspaceId: workspace2.id,
userId: member.id,
});
await app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: member.id,
workspaceId: workspace2.id,
doc: {
id: 'doc-id-4',
title: 'doc-title-4',
blockId: 'block-id-4',
mode: DocMode.page,
},
},
},
});
{
await app.login(member);
const result = await app.gql({
query: listNotificationsQuery,
variables: {
pagination: {
first: 10,
offset: 0,
},
},
});
const notifications = result.currentUser!.notifications.edges.map(
edge => edge.node
) as NotificationObjectType[];
t.is(notifications.length, 4);
t.is(result.currentUser!.notifications.totalCount, 4);
const notification = notifications[0];
t.is(notification.read, false);
const body = notification.body as MentionNotificationBodyType;
t.is(body.type, NotificationType.Mention);
t.is(body.workspace!.name, 'test-workspace-name2');
t.is(body.workspace!.id, workspace2.id);
t.falsy(body.workspace!.avatarUrl);
t.is(body.doc.id, 'doc-id-4');
t.is(body.doc.title, 'doc-title-4');
t.is(body.doc.blockId, 'block-id-4');
t.is(body.createdByUser!.id, owner.id);
const notification2 = notifications[1];
t.is(notification2.read, false);
const body2 = notification2.body as MentionNotificationBodyType;
t.is(body2.type, NotificationType.Mention);
t.is(body2.workspace!.id, workspace.id);
t.is(body2.doc.id, 'doc-id-3');
t.is(body2.doc.title, 'doc-title-3');
t.is(body2.doc.blockId, 'block-id-3');
t.is(body2.createdByUser!.id, owner.id);
t.is(body2.workspace!.id, workspace.id);
t.is(body2.workspace!.name, 'test-workspace-name');
t.falsy(body2.workspace!.avatarUrl);
}
{
await app.login(member);
const result = await app.gql({
query: listNotificationsQuery,
variables: {
pagination: {
first: 10,
offset: 2,
},
},
});
t.is(result.currentUser!.notifications.totalCount, 4);
t.is(result.currentUser!.notifications.pageInfo.hasNextPage, false);
t.is(result.currentUser!.notifications.pageInfo.hasPreviousPage, true);
const notifications = result.currentUser!.notifications.edges.map(
edge => edge.node
) as NotificationObjectType[];
t.is(notifications.length, 2);
const notification = notifications[0];
t.is(notification.read, false);
const body = notification.body as MentionNotificationBodyType;
t.is(body.workspace!.id, workspace.id);
t.is(body.doc.id, 'doc-id-2');
t.is(body.doc.title, 'doc-title-2');
t.is(body.doc.blockId, 'block-id-2');
t.is(body.createdByUser!.id, owner.id);
t.is(body.workspace!.id, workspace.id);
t.is(body.workspace!.name, 'test-workspace-name');
t.falsy(body.workspace!.avatarUrl);
const notification2 = notifications[1];
t.is(notification2.read, false);
const body2 = notification2.body as MentionNotificationBodyType;
t.is(body2.workspace!.id, workspace.id);
t.is(body2.doc.id, 'doc-id-1');
t.is(body2.doc.title, 'doc-title-1');
t.is(body2.doc.blockId, 'block-id-1');
t.is(body2.createdByUser!.id, owner.id);
t.is(body2.workspace!.id, workspace.id);
t.is(body2.workspace!.name, 'test-workspace-name');
t.falsy(body2.workspace!.avatarUrl);
}
{
await app.login(member);
const result = await app.gql({
query: listNotificationsQuery,
variables: {
pagination: {
first: 2,
offset: 0,
},
},
});
t.is(result.currentUser!.notifications.totalCount, 4);
t.is(result.currentUser!.notifications.pageInfo.hasNextPage, true);
t.is(result.currentUser!.notifications.pageInfo.hasPreviousPage, false);
const notifications = result.currentUser!.notifications.edges.map(
edge => edge.node
) as NotificationObjectType[];
t.is(notifications.length, 2);
const notification = notifications[0];
t.is(notification.read, false);
const body = notification.body as MentionNotificationBodyType;
t.is(body.workspace!.id, workspace2.id);
t.is(body.doc.id, 'doc-id-4');
t.is(body.doc.title, 'doc-title-4');
t.is(body.doc.blockId, 'block-id-4');
t.is(body.createdByUser!.id, owner.id);
t.is(body.workspace!.id, workspace2.id);
t.is(body.workspace!.name, 'test-workspace-name2');
t.falsy(body.workspace!.avatarUrl);
t.is(
notification.createdAt.toString(),
Buffer.from(
result.currentUser!.notifications.pageInfo.startCursor!,
'base64'
).toString('utf-8')
);
const notification2 = notifications[1];
t.is(notification2.read, false);
const body2 = notification2.body as MentionNotificationBodyType;
t.is(body2.workspace!.id, workspace.id);
t.is(body2.doc.id, 'doc-id-3');
t.is(body2.doc.title, 'doc-title-3');
t.is(body2.doc.blockId, 'block-id-3');
t.is(body2.createdByUser!.id, owner.id);
t.is(body2.workspace!.id, workspace.id);
t.is(body2.workspace!.name, 'test-workspace-name');
t.falsy(body2.workspace!.avatarUrl);
await app.login(owner);
await app.gql({
query: mentionUserMutation,
variables: {
input: {
userId: member.id,
workspaceId: workspace.id,
doc: {
id: 'doc-id-5',
title: 'doc-title-5',
blockId: 'block-id-5',
mode: DocMode.page,
},
},
},
});
// get new notifications
await app.login(member);
const result2 = await app.gql({
query: listNotificationsQuery,
variables: {
pagination: {
first: 2,
offset: 0,
after: result.currentUser!.notifications.pageInfo.startCursor,
},
},
});
t.is(result2.currentUser!.notifications.totalCount, 5);
t.is(result2.currentUser!.notifications.pageInfo.hasNextPage, false);
t.is(result2.currentUser!.notifications.pageInfo.hasPreviousPage, true);
const notifications2 = result2.currentUser!.notifications.edges.map(
edge => edge.node
) as NotificationObjectType[];
t.is(notifications2.length, 1);
const notification3 = notifications2[0];
t.is(notification3.read, false);
const body3 = notification3.body as MentionNotificationBodyType;
t.is(body3.workspace!.id, workspace.id);
t.is(body3.doc.id, 'doc-id-5');
t.is(body3.doc.title, 'doc-title-5');
t.is(body3.doc.blockId, 'block-id-5');
t.is(body3.createdByUser!.id, owner.id);
t.is(body3.createdByUser!.name, owner.name);
t.is(body3.workspace!.id, workspace.id);
t.is(body3.workspace!.name, 'test-workspace-name');
t.falsy(body3.workspace!.avatarUrl);
// no new notifications
const result3 = await app.gql({
query: listNotificationsQuery,
variables: {
pagination: {
first: 2,
offset: 0,
after: result2.currentUser!.notifications.pageInfo.startCursor,
},
},
});
t.is(result3.currentUser!.notifications.totalCount, 5);
t.is(result3.currentUser!.notifications.pageInfo.hasNextPage, false);
t.is(result3.currentUser!.notifications.pageInfo.hasPreviousPage, true);
t.is(result3.currentUser!.notifications.pageInfo.startCursor, null);
t.is(result3.currentUser!.notifications.pageInfo.endCursor, null);
t.is(result3.currentUser!.notifications.edges.length, 0);
}
});

View File

@@ -0,0 +1,314 @@
import {
acceptInviteByInviteIdMutation,
getMembersByWorkspaceIdQuery,
inviteByEmailMutation,
leaveWorkspaceMutation,
revokeMemberPermissionMutation,
WorkspaceMemberStatus,
} from '@affine/graphql';
import { faker } from '@faker-js/faker';
import { Models } from '../../../models';
import { Mockers } from '../../mocks';
import { app, e2e } from '../test';
e2e('should invite a user', async t => {
const u2 = await app.signup();
const owner = await app.signup();
const workspace = await app.create(Mockers.Workspace, {
owner: { id: owner.id },
});
const result = await app.gql({
query: inviteByEmailMutation,
variables: {
email: u2.email,
workspaceId: workspace.id,
},
});
t.truthy(result, 'failed to invite user');
// add invitation notification job
const invitationNotification = app.queue.last('notification.sendInvitation');
t.is(invitationNotification.payload.inviterId, owner.id);
t.is(invitationNotification.payload.inviteId, result.invite);
});
e2e('should leave a workspace', async t => {
const u2 = await app.signup();
const owner = await app.signup();
const workspace = await app.create(Mockers.Workspace, {
owner: { id: owner.id },
});
await app.create(Mockers.WorkspaceUser, {
workspaceId: workspace.id,
userId: u2.id,
});
app.switchUser(u2.id);
const { leaveWorkspace } = await app.gql({
query: leaveWorkspaceMutation,
variables: {
workspaceId: workspace.id,
},
});
t.true(leaveWorkspace, 'failed to leave workspace');
});
e2e('should revoke a user', async t => {
const u2 = await app.signup();
const owner = await app.signup();
const workspace = await app.create(Mockers.Workspace, {
owner: { id: owner.id },
});
await app.create(Mockers.WorkspaceUser, {
workspaceId: workspace.id,
userId: u2.id,
});
const { revoke } = await app.gql({
query: revokeMemberPermissionMutation,
variables: {
workspaceId: workspace.id,
userId: u2.id,
},
});
t.true(revoke, 'failed to revoke user');
});
e2e('should revoke a user on under review', async t => {
const user = await app.signup();
const owner = await app.signup();
const workspace = await app.create(Mockers.Workspace, {
owner: { id: owner.id },
});
await app.create(Mockers.WorkspaceUser, {
workspaceId: workspace.id,
userId: user.id,
status: WorkspaceMemberStatus.UnderReview,
});
const { revoke } = await app.gql({
query: revokeMemberPermissionMutation,
variables: {
workspaceId: workspace.id,
userId: user.id,
},
});
t.true(revoke, 'failed to revoke user');
const requestDeclinedNotification = app.queue.last(
'notification.sendInvitationReviewDeclined'
);
t.truthy(requestDeclinedNotification);
t.deepEqual(
requestDeclinedNotification.payload,
{
userId: user.id,
workspaceId: workspace.id,
reviewerId: owner.id,
},
'should send review declined notification'
);
});
e2e('should create user if not exist', async t => {
const owner = await app.signup();
const workspace = await app.create(Mockers.Workspace, {
owner: { id: owner.id },
});
const email = faker.internet.email();
await app.gql({
query: inviteByEmailMutation,
variables: {
email,
workspaceId: workspace.id,
},
});
const u2 = await app.get(Models).user.getUserByEmail(email);
t.truthy(u2, 'failed to create user');
});
e2e('should invite a user by link', async t => {
const u2 = await app.signup();
const owner = await app.signup();
const workspace = await app.create(Mockers.Workspace, {
owner: { id: owner.id },
});
const invite1 = await app.gql({
query: inviteByEmailMutation,
variables: {
email: u2.email,
workspaceId: workspace.id,
},
});
app.switchUser(u2);
const accept = await app.gql({
query: acceptInviteByInviteIdMutation,
variables: {
inviteId: invite1.invite,
workspaceId: workspace.id,
},
});
t.true(accept.acceptInviteById, 'failed to accept invite');
app.switchUser(owner);
const invite2 = await app.gql({
query: inviteByEmailMutation,
variables: {
email: u2.email,
workspaceId: workspace.id,
},
});
t.is(
invite2.invite,
invite1.invite,
'repeat the invitation must return same id'
);
const member = await app
.get(Models)
.workspaceUser.getActive(workspace.id, u2.id);
t.truthy(member, 'failed to invite user');
t.is(member!.id, invite1.invite, 'failed to check invite id');
});
e2e('should send invitation notification and leave email', async t => {
const u2 = await app.signup();
const owner = await app.signup();
const workspace = await app.create(Mockers.Workspace, {
owner: { id: owner.id },
});
const invite = await app.gql({
query: inviteByEmailMutation,
variables: {
email: u2.email,
workspaceId: workspace.id,
},
});
const invitationNotification = app.queue.last('notification.sendInvitation');
t.is(invitationNotification.payload.inviterId, owner.id);
t.is(invitationNotification.payload.inviteId, invite.invite);
app.switchUser(u2);
const accept = await app.gql({
query: acceptInviteByInviteIdMutation,
variables: {
inviteId: invite.invite,
workspaceId: workspace.id,
},
});
t.true(accept.acceptInviteById, 'failed to accept invite');
const acceptedNotification = app.queue.last(
'notification.sendInvitationAccepted'
);
t.is(acceptedNotification.payload.inviterId, owner.id);
t.is(acceptedNotification.payload.inviteId, invite.invite);
const leave = await app.gql({
query: leaveWorkspaceMutation,
variables: {
workspaceId: workspace.id,
sendLeaveMail: true,
},
});
t.true(leave.leaveWorkspace, 'failed to leave workspace');
const leaveMail = app.mails.last('MemberLeave');
t.is(leaveMail.to, owner.email);
t.is(leaveMail.props.user.$$userId, u2.id);
});
e2e('should support pagination for member', async t => {
const u1 = await app.signup();
const u2 = await app.signup();
const owner = await app.signup();
const workspace = await app.create(Mockers.Workspace, {
owner: { id: owner.id },
});
await app.create(Mockers.WorkspaceUser, {
workspaceId: workspace.id,
userId: u1.id,
});
await app.create(Mockers.WorkspaceUser, {
workspaceId: workspace.id,
userId: u2.id,
});
let result = await app.gql({
query: getMembersByWorkspaceIdQuery,
variables: {
workspaceId: workspace.id,
skip: 0,
take: 2,
},
});
t.is(result.workspace.memberCount, 3);
t.is(result.workspace.members.length, 2);
result = await app.gql({
query: getMembersByWorkspaceIdQuery,
variables: {
workspaceId: workspace.id,
skip: 2,
take: 2,
},
});
t.is(result.workspace.memberCount, 3);
t.is(result.workspace.members.length, 1);
result = await app.gql({
query: getMembersByWorkspaceIdQuery,
variables: {
workspaceId: workspace.id,
skip: 3,
take: 2,
},
});
t.is(result.workspace.memberCount, 3);
t.is(result.workspace.members.length, 0);
});
e2e('should limit member count correctly', async t => {
const owner = await app.signup();
const workspace = await app.create(Mockers.Workspace, {
owner: { id: owner.id },
});
await Promise.allSettled(
Array.from({ length: 10 }).map(async () => {
const user = await app.signup();
await app.create(Mockers.WorkspaceUser, {
workspaceId: workspace.id,
userId: user.id,
});
})
);
await app.switchUser(owner);
const result = await app.gql({
query: getMembersByWorkspaceIdQuery,
variables: {
workspaceId: workspace.id,
skip: 0,
take: 10,
},
});
t.is(result.workspace.memberCount, 11);
t.is(result.workspace.members.length, 10);
});

View File

@@ -2,17 +2,20 @@ export { createFactory } from './factory';
export * from './team-workspace.mock';
export * from './user.mock';
export * from './workspace.mock';
export * from './workspace-user.mock';
import { MockMailer } from './mailer.mock';
import { MockJobQueue } from './queue.mock';
import { MockTeamWorkspace } from './team-workspace.mock';
import { MockUser } from './user.mock';
import { MockWorkspace } from './workspace.mock';
import { MockWorkspaceUser } from './workspace-user.mock';
export const Mockers = {
User: MockUser,
Workspace: MockWorkspace,
TeamWorkspace: MockTeamWorkspace,
WorkspaceUser: MockWorkspaceUser,
};
export { MockJobQueue, MockMailer };

View File

@@ -0,0 +1,26 @@
import type { Prisma, WorkspaceUserRole } from '@prisma/client';
import { WorkspaceMemberStatus, WorkspaceRole } from '../../models';
import { Mocker } from './factory';
export type MockWorkspaceUserInput = Omit<
Prisma.WorkspaceUserRoleUncheckedCreateInput,
'type'
> & {
type?: WorkspaceRole;
};
export class MockWorkspaceUser extends Mocker<
MockWorkspaceUserInput,
WorkspaceUserRole
> {
override async create(input: MockWorkspaceUserInput) {
return await this.db.workspaceUserRole.create({
data: {
type: WorkspaceRole.Collaborator,
status: WorkspaceMemberStatus.Accepted,
...input,
},
});
}
}