mirror of
https://github.com/documenso/documenso.git
synced 2025-11-12 15:53:02 +10:00
feat: add organisations (#1820)
This commit is contained in:
@ -8,15 +8,17 @@ import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
test.describe('Document API', () => {
|
||||
test('sendDocument: should respect sendCompletionEmails setting', async ({ request }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const { document } = await seedPendingDocumentWithFullFields({
|
||||
owner: user,
|
||||
recipients: ['signer@example.com'],
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
const { token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test',
|
||||
expiresIn: null,
|
||||
});
|
||||
@ -81,11 +83,12 @@ test.describe('Document API', () => {
|
||||
test('sendDocument: should not modify email settings when sendCompletionEmails is not provided', async ({
|
||||
request,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const { document } = await seedPendingDocumentWithFullFields({
|
||||
owner: user,
|
||||
recipients: ['signer@example.com'],
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
// Set initial email settings
|
||||
@ -109,6 +112,7 @@ test.describe('Document API', () => {
|
||||
|
||||
const { token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
@ -1,281 +0,0 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { TeamMemberRole } from '@prisma/client';
|
||||
|
||||
import {
|
||||
ZFindTeamMembersResponseSchema,
|
||||
ZSuccessfulInviteTeamMemberResponseSchema,
|
||||
ZSuccessfulRemoveTeamMemberResponseSchema,
|
||||
ZSuccessfulUpdateTeamMemberResponseSchema,
|
||||
ZUnsuccessfulResponseSchema,
|
||||
} from '@documenso/api/v1/schema';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { seedTeam } from '@documenso/prisma/seed/teams';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
test.describe('Team API', () => {
|
||||
test('findTeamMembers: should list team members', async ({ request }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 3,
|
||||
});
|
||||
|
||||
const ownerMember = team.members.find((member) => member.userId === team.owner.id)!;
|
||||
|
||||
// Should not be undefined
|
||||
expect(ownerMember).toBeTruthy();
|
||||
|
||||
const { token } = await createApiToken({
|
||||
userId: team.owner.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const response = await request.get(
|
||||
`${NEXT_PUBLIC_WEBAPP_URL()}/api/v1/team/${team.id}/members`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(response.ok()).toBeTruthy();
|
||||
expect(response.status()).toBe(200);
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const parsed = ZFindTeamMembersResponseSchema.safeParse(data);
|
||||
|
||||
const safeData = parsed.success ? parsed.data : null;
|
||||
|
||||
expect(parsed.success).toBeTruthy();
|
||||
|
||||
expect(safeData!.members).toHaveLength(4); // Owner + 3 members
|
||||
expect(safeData!.members[0]).toHaveProperty('id');
|
||||
expect(safeData!.members[0]).toHaveProperty('email');
|
||||
expect(safeData!.members[0]).toHaveProperty('role');
|
||||
|
||||
expect(safeData!.members).toContainEqual({
|
||||
id: ownerMember.id,
|
||||
email: ownerMember.user.email,
|
||||
role: ownerMember.role,
|
||||
});
|
||||
});
|
||||
|
||||
test('inviteTeamMember: should invite a new team member', async ({ request }) => {
|
||||
const team = await seedTeam();
|
||||
|
||||
const { token } = await createApiToken({
|
||||
userId: team.owner.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const newUser = await seedUser();
|
||||
|
||||
const response = await request.post(
|
||||
`${NEXT_PUBLIC_WEBAPP_URL()}/api/v1/team/${team.id}/members/invite`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: {
|
||||
email: newUser.email,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(response.ok()).toBeTruthy();
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const parsed = ZSuccessfulInviteTeamMemberResponseSchema.safeParse(data);
|
||||
|
||||
const safeData = parsed.success ? parsed.data : null;
|
||||
|
||||
expect(parsed.success).toBeTruthy();
|
||||
expect(safeData!.message).toBe('An invite has been sent to the member');
|
||||
|
||||
const invite = await prisma.teamMemberInvite.findFirst({
|
||||
where: {
|
||||
email: newUser.email,
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
|
||||
expect(invite).toBeTruthy();
|
||||
});
|
||||
|
||||
test('updateTeamMember: should update a team member role', async ({ request }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 3,
|
||||
});
|
||||
|
||||
const { token } = await createApiToken({
|
||||
userId: team.owner.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const member = team.members.find((member) => member.role === TeamMemberRole.MEMBER)!;
|
||||
|
||||
// Should not be undefined
|
||||
expect(member).toBeTruthy();
|
||||
|
||||
const response = await request.put(
|
||||
`${NEXT_PUBLIC_WEBAPP_URL()}/api/v1/team/${team.id}/members/${member.id}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: {
|
||||
role: TeamMemberRole.ADMIN,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(response.ok()).toBeTruthy();
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const parsed = ZSuccessfulUpdateTeamMemberResponseSchema.safeParse(data);
|
||||
|
||||
const safeData = parsed.success ? parsed.data : null;
|
||||
|
||||
expect(parsed.success).toBeTruthy();
|
||||
|
||||
expect(safeData!.id).toBe(member.id);
|
||||
expect(safeData!.email).toBe(member.user.email);
|
||||
expect(safeData!.role).toBe(TeamMemberRole.ADMIN);
|
||||
});
|
||||
|
||||
test('removeTeamMember: should remove a team member', async ({ request }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 3,
|
||||
});
|
||||
|
||||
const { token } = await createApiToken({
|
||||
userId: team.owner.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const member = team.members.find((member) => member.role === TeamMemberRole.MEMBER)!;
|
||||
|
||||
// Should not be undefined
|
||||
expect(member).toBeTruthy();
|
||||
|
||||
const response = await request.delete(
|
||||
`${NEXT_PUBLIC_WEBAPP_URL()}/api/v1/team/${team.id}/members/${member.id}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(response.status()).toBe(200);
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const parsed = ZSuccessfulRemoveTeamMemberResponseSchema.safeParse(data);
|
||||
|
||||
const safeData = parsed.success ? parsed.data : null;
|
||||
|
||||
expect(parsed.success).toBeTruthy();
|
||||
|
||||
expect(safeData!.id).toBe(member.id);
|
||||
expect(safeData!.email).toBe(member.user.email);
|
||||
expect(safeData!.role).toBe(member.role);
|
||||
|
||||
const removedMemberCount = await prisma.teamMember.count({
|
||||
where: {
|
||||
id: member.id,
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
|
||||
expect(removedMemberCount).toBe(0);
|
||||
});
|
||||
|
||||
test('removeTeamMember: should not remove team owner', async ({ request }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 3,
|
||||
});
|
||||
|
||||
const { token } = await createApiToken({
|
||||
userId: team.owner.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const ownerMember = team.members.find((member) => member.userId === team.owner.id)!;
|
||||
|
||||
// Should not be undefined
|
||||
expect(ownerMember).toBeTruthy();
|
||||
|
||||
const response = await request.delete(
|
||||
`${NEXT_PUBLIC_WEBAPP_URL()}/api/v1/team/${team.id}/members/${ownerMember.id}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(response.status()).toBe(403);
|
||||
|
||||
const parsed = ZUnsuccessfulResponseSchema.safeParse(await response.json());
|
||||
|
||||
expect(parsed.success).toBeTruthy();
|
||||
});
|
||||
|
||||
test('removeTeamMember: should not remove self', async ({ request }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 3,
|
||||
});
|
||||
|
||||
const member = team.members.find((member) => member.role === TeamMemberRole.MEMBER)!;
|
||||
|
||||
// Make our non-owner member an admin
|
||||
await prisma.teamMember.update({
|
||||
where: {
|
||||
id: member.id,
|
||||
},
|
||||
data: {
|
||||
role: TeamMemberRole.ADMIN,
|
||||
},
|
||||
});
|
||||
|
||||
const { token } = await createApiToken({
|
||||
userId: member.userId,
|
||||
teamId: team.id,
|
||||
tokenName: 'test',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const response = await request.delete(
|
||||
`${NEXT_PUBLIC_WEBAPP_URL()}/api/v1/team/${team.id}/members/${member.id}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(response.status()).toBe(403);
|
||||
|
||||
const parsed = ZUnsuccessfulResponseSchema.safeParse(await response.json());
|
||||
|
||||
expect(parsed.success).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -18,17 +18,18 @@ test.describe('Template Field Prefill API v1', () => {
|
||||
request,
|
||||
}) => {
|
||||
// 1. Create a user
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
// 2. Create an API token for the user
|
||||
const { token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test-token',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
// 3. Create a template with seedBlankTemplate
|
||||
const template = await seedBlankTemplate(user, {
|
||||
const template = await seedBlankTemplate(user, team.id, {
|
||||
createTemplateOptions: {
|
||||
title: 'Template with Advanced Fields',
|
||||
},
|
||||
@ -349,17 +350,18 @@ test.describe('Template Field Prefill API v1', () => {
|
||||
request,
|
||||
}) => {
|
||||
// 1. Create a user
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
// 2. Create an API token for the user
|
||||
const { token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test-token',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
// 3. Create a template with seedBlankTemplate
|
||||
const template = await seedBlankTemplate(user, {
|
||||
const template = await seedBlankTemplate(user, team.id, {
|
||||
createTemplateOptions: {
|
||||
title: 'Template with Default Fields',
|
||||
},
|
||||
@ -519,17 +521,18 @@ test.describe('Template Field Prefill API v1', () => {
|
||||
|
||||
test('should handle invalid field prefill values', async ({ request }) => {
|
||||
// 1. Create a user
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
// 2. Create an API token for the user
|
||||
const { token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test-token',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
// 3. Create a template using seedBlankTemplate
|
||||
const template = await seedBlankTemplate(user, {
|
||||
const template = await seedBlankTemplate(user, team.id, {
|
||||
createTemplateOptions: {
|
||||
title: 'Template for Invalid Test',
|
||||
visibility: 'EVERYONE',
|
||||
|
||||
@ -8,10 +8,11 @@ test.describe('Embedding Presign API', () => {
|
||||
test('createEmbeddingPresignToken: should create a token with default expiration', async ({
|
||||
request,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const { token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test',
|
||||
expiresIn: null,
|
||||
});
|
||||
@ -44,10 +45,11 @@ test.describe('Embedding Presign API', () => {
|
||||
test('createEmbeddingPresignToken: should create a token with custom expiration', async ({
|
||||
request,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const { token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test',
|
||||
expiresIn: null,
|
||||
});
|
||||
@ -81,10 +83,11 @@ test.describe('Embedding Presign API', () => {
|
||||
test.skip('createEmbeddingPresignToken: should create a token with immediate expiration in dev mode', async ({
|
||||
request,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const { token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test',
|
||||
expiresIn: null,
|
||||
});
|
||||
@ -116,10 +119,11 @@ test.describe('Embedding Presign API', () => {
|
||||
});
|
||||
|
||||
test('verifyEmbeddingPresignToken: should verify a valid token', async ({ request }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const { token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test',
|
||||
expiresIn: null,
|
||||
});
|
||||
@ -170,10 +174,11 @@ test.describe('Embedding Presign API', () => {
|
||||
});
|
||||
|
||||
test('verifyEmbeddingPresignToken: should reject an invalid token', async ({ request }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const { token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
@ -18,17 +18,18 @@ test.describe('Template Field Prefill API v2', () => {
|
||||
request,
|
||||
}) => {
|
||||
// 1. Create a user
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
// 2. Create an API token for the user
|
||||
const { token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test-token',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
// 3. Create a template with seedBlankTemplate
|
||||
const template = await seedBlankTemplate(user, {
|
||||
const template = await seedBlankTemplate(user, team.id, {
|
||||
createTemplateOptions: {
|
||||
title: 'Template with Advanced Fields V2',
|
||||
},
|
||||
@ -346,17 +347,18 @@ test.describe('Template Field Prefill API v2', () => {
|
||||
request,
|
||||
}) => {
|
||||
// 1. Create a user
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
// 2. Create an API token for the user
|
||||
const { token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test-token',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
// 3. Create a template with seedBlankTemplate
|
||||
const template = await seedBlankTemplate(user, {
|
||||
const template = await seedBlankTemplate(user, team.id, {
|
||||
createTemplateOptions: {
|
||||
title: 'Template with Default Fields V2',
|
||||
},
|
||||
@ -511,17 +513,18 @@ test.describe('Template Field Prefill API v2', () => {
|
||||
|
||||
test('should handle invalid field prefill values', async ({ request }) => {
|
||||
// 1. Create a user
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
// 2. Create an API token for the user
|
||||
const { token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test-token',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
// 3. Create a template using seedBlankTemplate
|
||||
const template = await seedBlankTemplate(user, {
|
||||
const template = await seedBlankTemplate(user, team.id, {
|
||||
createTemplateOptions: {
|
||||
title: 'Template for Invalid Test V2',
|
||||
visibility: 'EVERYONE',
|
||||
|
||||
@ -6,9 +6,9 @@ import { seedUser } from '@documenso/prisma/seed/users';
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test('[COMMAND_MENU]: should see sent documents', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const recipient = await seedUser();
|
||||
const document = await seedPendingDocument(user, [recipient]);
|
||||
const { user, team } = await seedUser();
|
||||
const { user: recipient } = await seedUser();
|
||||
const document = await seedPendingDocument(user, team.id, [recipient]);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
@ -22,9 +22,9 @@ test('[COMMAND_MENU]: should see sent documents', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[COMMAND_MENU]: should see received documents', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const recipient = await seedUser();
|
||||
const document = await seedPendingDocument(user, [recipient]);
|
||||
const { user, team } = await seedUser();
|
||||
const { user: recipient } = await seedUser();
|
||||
const document = await seedPendingDocument(user, team.id, [recipient]);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
@ -38,9 +38,9 @@ test('[COMMAND_MENU]: should see received documents', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[COMMAND_MENU]: should be able to search by recipient', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const recipient = await seedUser();
|
||||
const document = await seedPendingDocument(user, [recipient]);
|
||||
const { user, team } = await seedUser();
|
||||
const { user: recipient } = await seedUser();
|
||||
const document = await seedPendingDocument(user, team.id, [recipient]);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
|
||||
@ -8,11 +8,11 @@ import { seedUser } from '@documenso/prisma/seed/users';
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test('[DOCUMENT_AUTH]: should grant access when not required', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const recipientWithAccount = await seedUser();
|
||||
const { user: recipientWithAccount } = await seedUser();
|
||||
|
||||
const document = await seedPendingDocument(user, [
|
||||
const document = await seedPendingDocument(user, team.id, [
|
||||
recipientWithAccount,
|
||||
'recipientwithoutaccount@documenso.com',
|
||||
]);
|
||||
@ -32,12 +32,13 @@ test('[DOCUMENT_AUTH]: should grant access when not required', async ({ page })
|
||||
});
|
||||
|
||||
test('[DOCUMENT_AUTH]: should allow or deny access when required', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const recipientWithAccount = await seedUser();
|
||||
const { user: recipientWithAccount } = await seedUser();
|
||||
|
||||
const document = await seedPendingDocument(
|
||||
user,
|
||||
team.id,
|
||||
[recipientWithAccount, 'recipientwithoutaccount@documenso.com'],
|
||||
{
|
||||
createDocumentOptions: {
|
||||
|
||||
@ -18,12 +18,13 @@ import { signSignaturePad } from '../fixtures/signature';
|
||||
test.describe.configure({ mode: 'parallel', timeout: 60000 });
|
||||
|
||||
test('[DOCUMENT_AUTH]: should allow signing when no auth setup', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const recipientWithAccount = await seedUser();
|
||||
const { user: recipientWithAccount } = await seedUser();
|
||||
|
||||
const { recipients } = await seedPendingDocumentWithFullFields({
|
||||
owner: user,
|
||||
teamId: team.id,
|
||||
recipients: [recipientWithAccount, seedTestEmail()],
|
||||
});
|
||||
|
||||
@ -56,12 +57,13 @@ test('[DOCUMENT_AUTH]: should allow signing when no auth setup', async ({ page }
|
||||
});
|
||||
|
||||
test('[DOCUMENT_AUTH]: should allow signing with valid global auth', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const recipientWithAccount = await seedUser();
|
||||
const { user: recipientWithAccount } = await seedUser();
|
||||
|
||||
const { recipients } = await seedPendingDocumentWithFullFields({
|
||||
owner: user,
|
||||
teamId: team.id,
|
||||
recipients: [recipientWithAccount],
|
||||
updateDocumentOptions: {
|
||||
authOptions: createDocumentAuthOptions({
|
||||
@ -107,12 +109,13 @@ test('[DOCUMENT_AUTH]: should allow signing with valid global auth', async ({ pa
|
||||
test.skip('[DOCUMENT_AUTH]: should deny signing document when required for global auth', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const recipientWithAccount = await seedUser();
|
||||
const { user: recipientWithAccount } = await seedUser();
|
||||
|
||||
const { recipients } = await seedPendingDocumentNoFields({
|
||||
owner: user,
|
||||
teamId: team.id,
|
||||
recipients: [recipientWithAccount],
|
||||
updateDocumentOptions: {
|
||||
authOptions: createDocumentAuthOptions({
|
||||
@ -138,12 +141,13 @@ test.skip('[DOCUMENT_AUTH]: should deny signing document when required for globa
|
||||
test('[DOCUMENT_AUTH]: should deny signing fields when required for global auth', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const recipientWithAccount = await seedUser();
|
||||
const { user: recipientWithAccount } = await seedUser();
|
||||
|
||||
const { recipients } = await seedPendingDocumentWithFullFields({
|
||||
owner: user,
|
||||
teamId: team.id,
|
||||
recipients: [recipientWithAccount, seedTestEmail()],
|
||||
updateDocumentOptions: {
|
||||
authOptions: createDocumentAuthOptions({
|
||||
@ -177,14 +181,15 @@ test('[DOCUMENT_AUTH]: should deny signing fields when required for global auth'
|
||||
test('[DOCUMENT_AUTH]: should allow field signing when required for recipient auth', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const recipientWithInheritAuth = await seedUser();
|
||||
const recipientWithExplicitNoneAuth = await seedUser();
|
||||
const recipientWithExplicitAccountAuth = await seedUser();
|
||||
const { user: recipientWithInheritAuth } = await seedUser();
|
||||
const { user: recipientWithExplicitNoneAuth } = await seedUser();
|
||||
const { user: recipientWithExplicitAccountAuth } = await seedUser();
|
||||
|
||||
const { recipients } = await seedPendingDocumentWithFullFields({
|
||||
owner: user,
|
||||
teamId: team.id,
|
||||
recipients: [
|
||||
recipientWithInheritAuth,
|
||||
recipientWithExplicitNoneAuth,
|
||||
@ -276,14 +281,15 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient au
|
||||
test('[DOCUMENT_AUTH]: should allow field signing when required for recipient and global auth', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const recipientWithInheritAuth = await seedUser();
|
||||
const recipientWithExplicitNoneAuth = await seedUser();
|
||||
const recipientWithExplicitAccountAuth = await seedUser();
|
||||
const { user: recipientWithInheritAuth } = await seedUser();
|
||||
const { user: recipientWithExplicitNoneAuth } = await seedUser();
|
||||
const { user: recipientWithExplicitAccountAuth } = await seedUser();
|
||||
|
||||
const { recipients } = await seedPendingDocumentWithFullFields({
|
||||
owner: user,
|
||||
teamId: team.id,
|
||||
recipients: [
|
||||
recipientWithInheritAuth,
|
||||
recipientWithExplicitNoneAuth,
|
||||
|
||||
@ -16,13 +16,14 @@ import { signDirectSignaturePad, signSignaturePad } from '../fixtures/signature'
|
||||
test('[NEXT_RECIPIENT_DICTATION]: should allow updating next recipient when dictation is enabled', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const firstSigner = await seedUser();
|
||||
const secondSigner = await seedUser();
|
||||
const thirdSigner = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
const { user: firstSigner } = await seedUser();
|
||||
const { user: secondSigner } = await seedUser();
|
||||
const { user: thirdSigner } = await seedUser();
|
||||
|
||||
const { recipients, document } = await seedPendingDocumentWithFullFields({
|
||||
owner: user,
|
||||
teamId: team.id,
|
||||
recipients: [firstSigner, secondSigner, thirdSigner],
|
||||
recipientsCreateOptions: [{ signingOrder: 1 }, { signingOrder: 2 }, { signingOrder: 3 }],
|
||||
updateDocumentOptions: {
|
||||
@ -109,12 +110,13 @@ test('[NEXT_RECIPIENT_DICTATION]: should allow updating next recipient when dict
|
||||
});
|
||||
|
||||
test('[NEXT_RECIPIENT_DICTATION]: should not show dictation UI when disabled', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const firstSigner = await seedUser();
|
||||
const secondSigner = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
const { user: firstSigner } = await seedUser();
|
||||
const { user: secondSigner } = await seedUser();
|
||||
|
||||
const { recipients, document } = await seedPendingDocumentWithFullFields({
|
||||
owner: user,
|
||||
teamId: team.id,
|
||||
recipients: [firstSigner, secondSigner],
|
||||
recipientsCreateOptions: [{ signingOrder: 1 }, { signingOrder: 2 }],
|
||||
updateDocumentOptions: {
|
||||
@ -194,12 +196,13 @@ test('[NEXT_RECIPIENT_DICTATION]: should not show dictation UI when disabled', a
|
||||
});
|
||||
|
||||
test('[NEXT_RECIPIENT_DICTATION]: should work with parallel signing flow', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const firstSigner = await seedUser();
|
||||
const secondSigner = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
const { user: firstSigner } = await seedUser();
|
||||
const { user: secondSigner } = await seedUser();
|
||||
|
||||
const { recipients, document } = await seedPendingDocumentWithFullFields({
|
||||
owner: user,
|
||||
teamId: team.id,
|
||||
recipients: [firstSigner, secondSigner],
|
||||
recipientsCreateOptions: [{ signingOrder: 1 }, { signingOrder: 2 }],
|
||||
updateDocumentOptions: {
|
||||
@ -278,13 +281,14 @@ test('[NEXT_RECIPIENT_DICTATION]: should work with parallel signing flow', async
|
||||
test('[NEXT_RECIPIENT_DICTATION]: should allow assistant to dictate next signer', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const assistant = await seedUser();
|
||||
const signer = await seedUser();
|
||||
const thirdSigner = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
const { user: assistant } = await seedUser();
|
||||
const { user: signer } = await seedUser();
|
||||
const { user: thirdSigner } = await seedUser();
|
||||
|
||||
const { recipients, document } = await seedPendingDocumentWithFullFields({
|
||||
owner: user,
|
||||
teamId: team.id,
|
||||
recipients: [assistant, signer, thirdSigner],
|
||||
recipientsCreateOptions: [
|
||||
{ signingOrder: 1, role: RecipientRole.ASSISTANT },
|
||||
|
||||
@ -5,137 +5,18 @@ import {
|
||||
seedDraftDocument,
|
||||
seedPendingDocument,
|
||||
} from '@documenso/prisma/seed/documents';
|
||||
import { seedUserSubscription } from '@documenso/prisma/seed/subscriptions';
|
||||
import { seedTeam } from '@documenso/prisma/seed/teams';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test.describe('[EE_ONLY]', () => {
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
const enterprisePriceId = process.env.NEXT_PUBLIC_STRIPE_ENTERPRISE_PLAN_MONTHLY_PRICE_ID || '';
|
||||
|
||||
test.beforeEach(() => {
|
||||
test.skip(
|
||||
process.env.NEXT_PUBLIC_FEATURE_BILLING_ENABLED !== 'true' || !enterprisePriceId,
|
||||
'Billing required for this test',
|
||||
);
|
||||
});
|
||||
|
||||
test('[DOCUMENT_FLOW] add action auth settings', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await seedUserSubscription({
|
||||
userId: user.id,
|
||||
priceId: enterprisePriceId,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(user);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
// Set EE action auth.
|
||||
await page.getByTestId('documentActionSelectValue').click();
|
||||
await page.getByRole('option').filter({ hasText: 'Require passkey' }).click();
|
||||
await expect(page.getByTestId('documentActionSelectValue')).toContainText('Require passkey');
|
||||
|
||||
// Save the settings by going to the next step.
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
|
||||
|
||||
// Return to the settings step to check that the results are saved correctly.
|
||||
await page.getByRole('button', { name: 'Go Back' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
|
||||
|
||||
await expect(page.getByTestId('documentActionSelectValue')).toContainText('Require passkey');
|
||||
});
|
||||
|
||||
test('[DOCUMENT_FLOW] enterprise team member can add action auth settings', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const owner = team.owner;
|
||||
const teamMemberUser = team.members[1].user;
|
||||
|
||||
// Make the team enterprise by giving the owner the enterprise subscription.
|
||||
await seedUserSubscription({
|
||||
userId: team.ownerUserId,
|
||||
priceId: enterprisePriceId,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(owner, {
|
||||
createDocumentOptions: {
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamMemberUser.email,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
// Set EE action auth.
|
||||
await page.getByTestId('documentActionSelectValue').click();
|
||||
await page.getByRole('option').filter({ hasText: 'Require passkey' }).click();
|
||||
await expect(page.getByTestId('documentActionSelectValue')).toContainText('Require passkey');
|
||||
|
||||
// Save the settings by going to the next step.
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
|
||||
|
||||
// Advanced settings should be visible.
|
||||
await expect(page.getByLabel('Show advanced settings')).toBeVisible();
|
||||
});
|
||||
|
||||
test('[DOCUMENT_FLOW] enterprise team member should not have access to enterprise on personal account', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const teamMemberUser = team.members[1].user;
|
||||
|
||||
// Make the team enterprise by giving the owner the enterprise subscription.
|
||||
await seedUserSubscription({
|
||||
userId: team.ownerUserId,
|
||||
priceId: enterprisePriceId,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(teamMemberUser);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamMemberUser.email,
|
||||
redirectPath: `/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
// Global action auth should not be visible.
|
||||
await expect(page.getByTestId('documentActionSelectValue')).not.toBeVisible();
|
||||
|
||||
// Next step.
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
|
||||
|
||||
// Advanced settings should not be visible.
|
||||
await expect(page.getByLabel('Show advanced settings')).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('[DOCUMENT_FLOW]: add settings', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const document = await seedBlankDocument(user);
|
||||
const { user, team } = await seedUser();
|
||||
const document = await seedBlankDocument(user, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/documents/${document.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
// Set title.
|
||||
@ -163,21 +44,21 @@ test('[DOCUMENT_FLOW]: add settings', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[DOCUMENT_FLOW]: title should be disabled depending on document status', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const pendingDocument = await seedPendingDocument(user, []);
|
||||
const draftDocument = await seedDraftDocument(user, []);
|
||||
const pendingDocument = await seedPendingDocument(user, team.id, []);
|
||||
const draftDocument = await seedDraftDocument(user, team.id, []);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/documents/${pendingDocument.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/documents/${pendingDocument.id}/edit`,
|
||||
});
|
||||
|
||||
// Should be disabled for pending documents.
|
||||
await expect(page.getByLabel('Title')).toBeDisabled();
|
||||
|
||||
// Should be enabled for draft documents.
|
||||
await page.goto(`/documents/${draftDocument.id}/edit`);
|
||||
await page.goto(`/t/${team.url}/documents/${draftDocument.id}/edit`);
|
||||
await expect(page.getByLabel('Title')).toBeEnabled();
|
||||
});
|
||||
|
||||
@ -1,71 +1,18 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
|
||||
import { seedUserSubscription } from '@documenso/prisma/seed/subscriptions';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test.describe('[EE_ONLY]', () => {
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
const enterprisePriceId = process.env.NEXT_PUBLIC_STRIPE_ENTERPRISE_PLAN_MONTHLY_PRICE_ID || '';
|
||||
|
||||
test.beforeEach(() => {
|
||||
test.skip(
|
||||
process.env.NEXT_PUBLIC_FEATURE_BILLING_ENABLED !== 'true' || !enterprisePriceId,
|
||||
'Billing required for this test',
|
||||
);
|
||||
});
|
||||
|
||||
test('[DOCUMENT_FLOW] add EE settings', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await seedUserSubscription({
|
||||
userId: user.id,
|
||||
priceId: enterprisePriceId,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(user);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
// Save the settings by going to the next step.
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
|
||||
|
||||
// Add 2 signers.
|
||||
await page.getByPlaceholder('Email').fill('recipient1@documenso.com');
|
||||
await page.getByPlaceholder('Name').fill('Recipient 1');
|
||||
|
||||
await page.getByRole('button', { name: 'Add Signer' }).click();
|
||||
await page.getByLabel('Email').nth(1).fill('recipient2@documenso.com');
|
||||
await page.getByLabel('Name').nth(1).fill('Recipient 2');
|
||||
|
||||
// Display advanced settings.
|
||||
await page.getByLabel('Show advanced settings').check();
|
||||
|
||||
// Navigate to the next step and back.
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Go Back' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
|
||||
|
||||
// Todo: Fix stepper component back issue before finishing test.
|
||||
});
|
||||
});
|
||||
|
||||
test('[DOCUMENT_FLOW]: add signers', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const document = await seedBlankDocument(user);
|
||||
const { user, team } = await seedUser();
|
||||
const document = await seedBlankDocument(user, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/documents/${document.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
// Save the settings by going to the next step.
|
||||
|
||||
@ -35,11 +35,12 @@ const getDocumentByToken = async (token: string) => {
|
||||
};
|
||||
|
||||
test('[DOCUMENT_FLOW]: should be able to upload a PDF document', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/t/${team.url}/documents`,
|
||||
});
|
||||
|
||||
// Upload document.
|
||||
@ -58,17 +59,17 @@ test('[DOCUMENT_FLOW]: should be able to upload a PDF document', async ({ page }
|
||||
await fileChooser.setFiles(path.join(__dirname, '../../../../assets/example.pdf'));
|
||||
|
||||
// Wait to be redirected to the edit page.
|
||||
await page.waitForURL(/\/documents\/\d+/);
|
||||
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
||||
});
|
||||
|
||||
test('[DOCUMENT_FLOW]: should be able to create a document', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const document = await seedBlankDocument(user);
|
||||
const { user, team } = await seedUser();
|
||||
const document = await seedBlankDocument(user, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/documents/${document.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
const documentTitle = `example-${Date.now()}.pdf`;
|
||||
@ -114,7 +115,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document', async ({ page }) =>
|
||||
await page.waitForTimeout(2500);
|
||||
await page.getByRole('button', { name: 'Send' }).click();
|
||||
|
||||
await page.waitForURL('/documents');
|
||||
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
||||
|
||||
// Assert document was created
|
||||
await expect(page.getByRole('link', { name: documentTitle })).toBeVisible();
|
||||
@ -123,13 +124,13 @@ test('[DOCUMENT_FLOW]: should be able to create a document', async ({ page }) =>
|
||||
test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipients', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const document = await seedBlankDocument(user);
|
||||
const { user, team } = await seedUser();
|
||||
const document = await seedBlankDocument(user, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/documents/${document.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
const documentTitle = `example-${Date.now()}.pdf`;
|
||||
@ -199,7 +200,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipie
|
||||
await page.waitForTimeout(2500);
|
||||
await page.getByRole('button', { name: 'Send' }).click();
|
||||
|
||||
await page.waitForURL('/documents');
|
||||
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
||||
|
||||
// Assert document was created
|
||||
await expect(page.getByRole('link', { name: documentTitle })).toBeVisible();
|
||||
@ -208,13 +209,13 @@ test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipie
|
||||
test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipients with different roles', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const document = await seedBlankDocument(user);
|
||||
const { user, team } = await seedUser();
|
||||
const document = await seedBlankDocument(user, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/documents/${document.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
// Set title
|
||||
@ -297,7 +298,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipie
|
||||
await page.waitForTimeout(2500);
|
||||
await page.getByRole('button', { name: 'Send' }).click();
|
||||
|
||||
await page.waitForURL('/documents');
|
||||
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
||||
|
||||
// Assert document was created
|
||||
await expect(page.getByRole('link', { name: 'Test Title' })).toBeVisible();
|
||||
@ -306,13 +307,13 @@ test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipie
|
||||
test('[DOCUMENT_FLOW]: should not be able to create a document without signatures', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const document = await seedBlankDocument(user);
|
||||
const { user, team } = await seedUser();
|
||||
const document = await seedBlankDocument(user, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/documents/${document.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
const documentTitle = `example-${Date.now()}.pdf`;
|
||||
@ -342,10 +343,11 @@ test('[DOCUMENT_FLOW]: should not be able to create a document without signature
|
||||
});
|
||||
|
||||
test('[DOCUMENT_FLOW]: should be able to approve a document', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const { recipients } = await seedPendingDocumentWithFullFields({
|
||||
owner: user,
|
||||
teamId: team.id,
|
||||
recipients: ['user@documenso.com', 'approver@documenso.com'],
|
||||
recipientsCreateOptions: [
|
||||
{
|
||||
@ -393,13 +395,13 @@ test('[DOCUMENT_FLOW]: should be able to approve a document', async ({ page }) =
|
||||
test('[DOCUMENT_FLOW]: should be able to create, send with redirect url, sign a document and redirect to redirect url', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const document = await seedBlankDocument(user);
|
||||
const { user, team } = await seedUser();
|
||||
const document = await seedBlankDocument(user, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/documents/${document.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
const documentTitle = `example-${Date.now()}.pdf`;
|
||||
@ -430,12 +432,12 @@ test('[DOCUMENT_FLOW]: should be able to create, send with redirect url, sign a
|
||||
await page.waitForTimeout(2500);
|
||||
await page.getByRole('button', { name: 'Send' }).click();
|
||||
|
||||
await page.waitForURL('/documents');
|
||||
await page.waitForURL(`/t/${team.url}/documents`);
|
||||
|
||||
// Assert document was created
|
||||
await expect(page.getByRole('link', { name: documentTitle })).toBeVisible();
|
||||
await page.getByRole('link', { name: documentTitle }).click();
|
||||
await page.waitForURL(/\/documents\/\d+/);
|
||||
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
||||
|
||||
const url = page.url().split('/');
|
||||
const documentId = url[url.length - 1];
|
||||
@ -467,11 +469,12 @@ test('[DOCUMENT_FLOW]: should be able to create, send with redirect url, sign a
|
||||
});
|
||||
|
||||
test('[DOCUMENT_FLOW]: should be able to sign a document with custom date', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const now = DateTime.utc();
|
||||
|
||||
const { document, recipients } = await seedPendingDocumentWithFullFields({
|
||||
teamId: team.id,
|
||||
owner: user,
|
||||
recipients: ['user1@example.com'],
|
||||
fields: [FieldType.DATE],
|
||||
@ -516,13 +519,13 @@ test('[DOCUMENT_FLOW]: should be able to sign a document with custom date', asyn
|
||||
test('[DOCUMENT_FLOW]: should be able to create and sign a document with 3 recipients in sequential order', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const document = await seedBlankDocument(user);
|
||||
const { user, team } = await seedUser();
|
||||
const document = await seedBlankDocument(user, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/documents/${document.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
const documentTitle = `Sequential-Signing-${Date.now()}.pdf`;
|
||||
@ -579,7 +582,7 @@ test('[DOCUMENT_FLOW]: should be able to create and sign a document with 3 recip
|
||||
await page.waitForTimeout(2500);
|
||||
await page.getByRole('button', { name: 'Send' }).click();
|
||||
|
||||
await page.waitForURL('/documents');
|
||||
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
||||
|
||||
await expect(page.getByRole('link', { name: documentTitle })).toBeVisible();
|
||||
|
||||
@ -642,9 +645,10 @@ test('[DOCUMENT_FLOW]: should be able to create and sign a document with 3 recip
|
||||
test('[DOCUMENT_FLOW]: should prevent out-of-order signing in sequential mode', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const { recipients } = await seedPendingDocumentWithFullFields({
|
||||
teamId: team.id,
|
||||
owner: user,
|
||||
recipients: ['user1@example.com', 'user2@example.com', 'user3@example.com'],
|
||||
fields: [FieldType.SIGNATURE],
|
||||
|
||||
@ -13,16 +13,20 @@ import { checkDocumentTabCount } from '../fixtures/documents';
|
||||
test.describe.configure({ mode: 'serial' });
|
||||
|
||||
const seedDeleteDocumentsTestRequirements = async () => {
|
||||
const [sender, recipientA, recipientB] = await Promise.all([seedUser(), seedUser(), seedUser()]);
|
||||
const [sender, recipientA, recipientB] = await Promise.all([
|
||||
seedUser({ setTeamEmailAsOwner: true }),
|
||||
seedUser({ setTeamEmailAsOwner: true }),
|
||||
seedUser({ setTeamEmailAsOwner: true }),
|
||||
]);
|
||||
|
||||
const [draftDocument, pendingDocument, completedDocument] = await Promise.all([
|
||||
seedDraftDocument(sender, [recipientA, recipientB], {
|
||||
seedDraftDocument(sender.user, sender.team.id, [recipientA.user, recipientB.user], {
|
||||
createDocumentOptions: { title: 'Document 1 - Draft' },
|
||||
}),
|
||||
seedPendingDocument(sender, [recipientA, recipientB], {
|
||||
seedPendingDocument(sender.user, sender.team.id, [recipientA.user, recipientB.user], {
|
||||
createDocumentOptions: { title: 'Document 1 - Pending' },
|
||||
}),
|
||||
seedCompletedDocument(sender, [recipientA, recipientB], {
|
||||
seedCompletedDocument(sender.user, sender.team.id, [recipientA.user, recipientB.user], {
|
||||
createDocumentOptions: { title: 'Document 1 - Completed' },
|
||||
}),
|
||||
]);
|
||||
@ -41,7 +45,8 @@ test('[DOCUMENTS]: seeded documents should be visible', async ({ page }) => {
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: sender.email,
|
||||
email: sender.user.email,
|
||||
redirectPath: `/t/${sender.team.url}/documents`,
|
||||
});
|
||||
|
||||
await expect(page.getByRole('link', { name: 'Document 1 - Completed' })).toBeVisible();
|
||||
@ -53,7 +58,8 @@ test('[DOCUMENTS]: seeded documents should be visible', async ({ page }) => {
|
||||
for (const recipient of recipients) {
|
||||
await apiSignin({
|
||||
page,
|
||||
email: recipient.email,
|
||||
email: recipient.user.email,
|
||||
redirectPath: `/t/${recipient.team.url}/documents`,
|
||||
});
|
||||
|
||||
await expect(page.getByRole('link', { name: 'Document 1 - Completed' })).toBeVisible();
|
||||
@ -72,7 +78,8 @@ test('[DOCUMENTS]: deleting a completed document should not remove it from recip
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: sender.email,
|
||||
email: sender.user.email,
|
||||
redirectPath: `/t/${sender.team.url}/documents`,
|
||||
});
|
||||
|
||||
// Open document action menu.
|
||||
@ -95,7 +102,8 @@ test('[DOCUMENTS]: deleting a completed document should not remove it from recip
|
||||
for (const recipient of recipients) {
|
||||
await apiSignin({
|
||||
page,
|
||||
email: recipient.email,
|
||||
email: recipient.user.email,
|
||||
redirectPath: `/t/${recipient.team.url}/documents`,
|
||||
});
|
||||
|
||||
await expect(page.getByRole('link', { name: 'Document 1 - Completed' })).toBeVisible();
|
||||
@ -113,7 +121,8 @@ test('[DOCUMENTS]: deleting a pending document should remove it from recipients'
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: sender.email,
|
||||
email: sender.user.email,
|
||||
redirectPath: `/t/${sender.team.url}/documents`,
|
||||
});
|
||||
|
||||
// Open document action menu.
|
||||
@ -135,6 +144,7 @@ test('[DOCUMENTS]: deleting a pending document should remove it from recipients'
|
||||
email: recipient.email,
|
||||
});
|
||||
|
||||
// Check dashboard inbox.
|
||||
await expect(page.getByRole('link', { name: 'Document 1 - Pending' })).not.toBeVisible();
|
||||
await apiSignout({ page });
|
||||
}
|
||||
@ -145,7 +155,8 @@ test('[DOCUMENTS]: deleting draft documents should permanently remove it', async
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: sender.email,
|
||||
email: sender.user.email,
|
||||
redirectPath: `/t/${sender.team.url}/documents`,
|
||||
});
|
||||
|
||||
// Open document action menu.
|
||||
@ -174,7 +185,8 @@ test('[DOCUMENTS]: deleting pending documents should permanently remove it', asy
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: sender.email,
|
||||
email: sender.user.email,
|
||||
redirectPath: `/t/${sender.team.url}/documents`,
|
||||
});
|
||||
|
||||
// Open document action menu.
|
||||
@ -205,7 +217,8 @@ test('[DOCUMENTS]: deleting completed documents as an owner should hide it from
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: sender.email,
|
||||
email: sender.user.email,
|
||||
redirectPath: `/t/${sender.team.url}/documents`,
|
||||
});
|
||||
|
||||
// Open document action menu.
|
||||
@ -231,7 +244,8 @@ test('[DOCUMENTS]: deleting completed documents as an owner should hide it from
|
||||
await apiSignout({ page });
|
||||
await apiSignin({
|
||||
page,
|
||||
email: recipients[0].email,
|
||||
email: recipients[0].user.email,
|
||||
redirectPath: `/t/${recipients[0].team.url}/documents`,
|
||||
});
|
||||
|
||||
// Check document counts.
|
||||
@ -252,7 +266,8 @@ test('[DOCUMENTS]: deleting documents as a recipient should only hide it for the
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: recipientA.email,
|
||||
email: recipientA.user.email,
|
||||
redirectPath: `/t/${recipientA.team.url}/documents`,
|
||||
});
|
||||
|
||||
// Open document action menu.
|
||||
@ -301,7 +316,8 @@ test('[DOCUMENTS]: deleting documents as a recipient should only hide it for the
|
||||
await apiSignout({ page });
|
||||
await apiSignin({
|
||||
page,
|
||||
email: sender.email,
|
||||
email: sender.user.email,
|
||||
redirectPath: `/t/${sender.team.url}/documents`,
|
||||
});
|
||||
|
||||
// Check document counts for sender.
|
||||
@ -315,7 +331,8 @@ test('[DOCUMENTS]: deleting documents as a recipient should only hide it for the
|
||||
await apiSignout({ page });
|
||||
await apiSignin({
|
||||
page,
|
||||
email: recipientB.email,
|
||||
email: recipientB.user.email,
|
||||
redirectPath: `/t/${recipientB.team.url}/documents`,
|
||||
});
|
||||
|
||||
// Check document counts for other recipient.
|
||||
|
||||
@ -14,12 +14,15 @@ import { signSignaturePad } from '../fixtures/signature';
|
||||
|
||||
test.describe('Signing Certificate Tests', () => {
|
||||
test('individual document should always include signing certificate', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser({
|
||||
isPersonalOrganisation: true,
|
||||
});
|
||||
|
||||
const { document, recipients } = await seedPendingDocumentWithFullFields({
|
||||
owner: user,
|
||||
recipients: ['signer@example.com'],
|
||||
fields: [FieldType.SIGNATURE],
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
const documentData = await prisma.documentData
|
||||
@ -76,20 +79,28 @@ test.describe('Signing Certificate Tests', () => {
|
||||
test('team document with signing certificate enabled should include certificate', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam();
|
||||
const { owner, team } = await seedTeam();
|
||||
|
||||
const { document, recipients } = await seedPendingDocumentWithFullFields({
|
||||
owner: team.owner,
|
||||
owner: owner,
|
||||
recipients: ['signer@example.com'],
|
||||
fields: [FieldType.SIGNATURE],
|
||||
updateDocumentOptions: {
|
||||
teamId: team.id,
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
const teamSettingsId = await prisma.teamGlobalSettings.findFirstOrThrow({
|
||||
where: {
|
||||
team: {
|
||||
id: team.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.teamGlobalSettings.create({
|
||||
await prisma.teamGlobalSettings.update({
|
||||
where: {
|
||||
id: teamSettingsId.id,
|
||||
},
|
||||
data: {
|
||||
teamId: team.id,
|
||||
includeSigningCertificate: true,
|
||||
},
|
||||
});
|
||||
@ -148,20 +159,28 @@ test.describe('Signing Certificate Tests', () => {
|
||||
test('team document with signing certificate disabled should not include certificate', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam();
|
||||
const { owner, team } = await seedTeam();
|
||||
|
||||
const { document, recipients } = await seedPendingDocumentWithFullFields({
|
||||
owner: team.owner,
|
||||
owner: owner,
|
||||
recipients: ['signer@example.com'],
|
||||
fields: [FieldType.SIGNATURE],
|
||||
updateDocumentOptions: {
|
||||
teamId: team.id,
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
const teamSettingsId = await prisma.teamGlobalSettings.findFirstOrThrow({
|
||||
where: {
|
||||
team: {
|
||||
id: team.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.teamGlobalSettings.create({
|
||||
await prisma.teamGlobalSettings.update({
|
||||
where: {
|
||||
id: teamSettingsId.id,
|
||||
},
|
||||
data: {
|
||||
teamId: team.id,
|
||||
includeSigningCertificate: false,
|
||||
},
|
||||
});
|
||||
@ -218,16 +237,22 @@ test.describe('Signing Certificate Tests', () => {
|
||||
});
|
||||
|
||||
test('team can toggle signing certificate setting', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const { owner, team } = await seedTeam();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: team.owner.email,
|
||||
email: owner.email,
|
||||
redirectPath: `/t/${team.url}/settings/preferences`,
|
||||
});
|
||||
|
||||
// Toggle signing certificate setting
|
||||
await page.getByLabel('Include the Signing Certificate in the Document').click();
|
||||
await page
|
||||
.getByRole('group')
|
||||
.locator('div')
|
||||
.filter({ hasText: 'Include the Signing' })
|
||||
.getByRole('combobox')
|
||||
.click();
|
||||
await page.getByRole('option', { name: 'No' }).click();
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: /Update/ })
|
||||
.first()
|
||||
@ -244,7 +269,13 @@ test.describe('Signing Certificate Tests', () => {
|
||||
expect(updatedTeam.teamGlobalSettings?.includeSigningCertificate).toBe(false);
|
||||
|
||||
// Toggle the setting back to true
|
||||
await page.getByLabel('Include the Signing Certificate in the Document').click();
|
||||
await page
|
||||
.getByRole('group')
|
||||
.locator('div')
|
||||
.filter({ hasText: 'Include the Signing' })
|
||||
.getByRole('combobox')
|
||||
.click();
|
||||
await page.getByRole('option', { name: 'Yes' }).click();
|
||||
await page
|
||||
.getByRole('button', { name: /Update/ })
|
||||
.first()
|
||||
|
||||
@ -17,7 +17,7 @@ export const apiSignin = async ({
|
||||
page,
|
||||
email = 'example@documenso.com',
|
||||
password = 'password',
|
||||
redirectPath = '/documents',
|
||||
redirectPath = '/',
|
||||
}: LoginOptions) => {
|
||||
const { request } = page.context();
|
||||
|
||||
|
||||
9
packages/app-tests/e2e/fixtures/generic.ts
Normal file
9
packages/app-tests/e2e/fixtures/generic.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { type Page, expect } from '@playwright/test';
|
||||
|
||||
export const expectTextToBeVisible = async (page: Page, text: string) => {
|
||||
await expect(page.getByText(text).first()).toBeVisible();
|
||||
};
|
||||
|
||||
export const expectTextToNotBeVisible = async (page: Page, text: string) => {
|
||||
await expect(page.getByText(text).first()).not.toBeVisible();
|
||||
};
|
||||
@ -1,866 +0,0 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import path from 'node:path';
|
||||
|
||||
import { FolderType } from '@documenso/prisma/client';
|
||||
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
|
||||
import { seedBlankFolder } from '@documenso/prisma/seed/folders';
|
||||
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test.describe.configure({ mode: 'parallel' });
|
||||
|
||||
test('create folder button is visible on documents page', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/',
|
||||
});
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Create Folder' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can create a document folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: 'Create Folder' }).click();
|
||||
await expect(page.getByRole('dialog', { name: 'Create New folder' })).toBeVisible();
|
||||
|
||||
await page.getByLabel('Folder name').fill('My folder');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(page.getByText('My folder')).toBeVisible();
|
||||
|
||||
await page.goto('/documents');
|
||||
await expect(page.locator('div').filter({ hasText: 'My folder' }).nth(3)).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can create a document subfolder inside a document folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const folder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Client Contracts',
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/documents/f/${folder.id}`,
|
||||
});
|
||||
|
||||
await expect(page.getByText('Client Contracts')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Create Folder' }).click();
|
||||
await expect(page.getByRole('dialog', { name: 'Create New folder' })).toBeVisible();
|
||||
|
||||
await page.getByLabel('Folder name').fill('Invoices');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(page.getByText('Invoices')).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can create a document inside a document folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const folder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Client Contracts',
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/documents/f/${folder.id}`,
|
||||
});
|
||||
|
||||
const fileInput = page.locator('input[type="file"]').nth(1);
|
||||
await fileInput.waitFor({ state: 'attached' });
|
||||
|
||||
await fileInput.setInputFiles(
|
||||
path.join(__dirname, '../../../assets/documenso-supporter-pledge.pdf'),
|
||||
);
|
||||
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
await expect(page.getByText('documenso-supporter-pledge.pdf')).toBeVisible();
|
||||
|
||||
await page.goto(`/documents/f/${folder.id}`);
|
||||
|
||||
await expect(page.getByText('documenso-supporter-pledge.pdf')).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can pin a document folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Contracts',
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/documents',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: '•••' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Pin' }).click();
|
||||
|
||||
await page.reload();
|
||||
|
||||
await expect(page.locator('svg.text-documenso.h-3.w-3')).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can unpin a document folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Contracts',
|
||||
pinned: true,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/documents',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: '•••' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Unpin' }).click();
|
||||
|
||||
await page.reload();
|
||||
|
||||
await expect(page.locator('svg.text-documenso.h-3.w-3')).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('user can rename a document folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Contracts',
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/documents',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: '•••' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Settings' }).click();
|
||||
|
||||
await page.getByLabel('Name').fill('Archive');
|
||||
await page.getByRole('button', { name: 'Save Changes' }).click();
|
||||
|
||||
await expect(page.getByText('Archive')).toBeVisible();
|
||||
});
|
||||
|
||||
test('document folder visibility is not visible to user', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Contracts',
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/documents',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: '•••' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Settings' }).click();
|
||||
|
||||
await expect(page.getByRole('menuitem', { name: 'Visibility' })).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('document folder can be moved to another document folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const folder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Clients',
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Contracts',
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/documents',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: '•••' }).nth(0).click();
|
||||
await page.getByRole('menuitem', { name: 'Move' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Clients' }).click();
|
||||
await page.getByRole('button', { name: 'Move Folder' }).click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await page.goto(`/documents/f/${folder.id}`);
|
||||
|
||||
await expect(page.getByText('Contracts')).toBeVisible();
|
||||
});
|
||||
|
||||
test('document folder can be moved to the root', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const parentFolder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Clients',
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Contracts',
|
||||
parentId: parentFolder.id,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/documents',
|
||||
});
|
||||
|
||||
await page.getByText('Clients').click();
|
||||
|
||||
await page.getByRole('button', { name: '•••' }).nth(0).click();
|
||||
await page.getByRole('menuitem', { name: 'Move' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Root' }).click();
|
||||
await page.getByRole('button', { name: 'Move Folder' }).click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await page.goto('/documents');
|
||||
|
||||
await expect(page.getByText('Clients')).toBeVisible();
|
||||
});
|
||||
|
||||
test('document folder and its contents can be deleted', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const folder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Proposals',
|
||||
},
|
||||
});
|
||||
|
||||
const proposal = await seedBlankDocument(user, {
|
||||
createDocumentOptions: {
|
||||
title: 'Proposal 1',
|
||||
folderId: folder.id,
|
||||
},
|
||||
});
|
||||
|
||||
const reportsFolder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Reports',
|
||||
parentId: folder.id,
|
||||
},
|
||||
});
|
||||
|
||||
const report = await seedBlankDocument(user, {
|
||||
createDocumentOptions: {
|
||||
title: 'Report 1',
|
||||
folderId: reportsFolder.id,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/documents',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: '•••' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||
|
||||
await page.getByRole('textbox').fill(`delete ${folder.name}`);
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
|
||||
await page.goto('/documents');
|
||||
|
||||
await expect(page.locator('div').filter({ hasText: folder.name })).not.toBeVisible();
|
||||
await expect(page.getByText(proposal.title)).not.toBeVisible();
|
||||
|
||||
await page.goto(`/documents/f/${folder.id}`);
|
||||
|
||||
await expect(page.getByText(report.title)).not.toBeVisible();
|
||||
await expect(page.locator('div').filter({ hasText: reportsFolder.name })).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('user can move a document to a document folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const folder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Proposals',
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankDocument(user, {
|
||||
createDocumentOptions: {
|
||||
title: 'Proposal 1',
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/documents',
|
||||
});
|
||||
|
||||
await expect(async () => {
|
||||
await page.getByTestId('document-table-action-btn').first().click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(page.getByRole('menuitem', { name: 'Move to Folder' })).toBeVisible();
|
||||
}).toPass();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Move to Folder' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Proposals' }).click();
|
||||
await page.getByRole('button', { name: 'Move' }).click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await page.goto(`/documents/f/${folder.id}`);
|
||||
|
||||
await expect(page.getByText('Proposal 1')).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can move a document from folder to the root', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const folder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Proposals',
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankDocument(user, {
|
||||
createDocumentOptions: {
|
||||
title: 'Proposal 1',
|
||||
folderId: folder.id,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/documents',
|
||||
});
|
||||
|
||||
await page.getByText('Proposals').click();
|
||||
|
||||
await expect(async () => {
|
||||
await page.getByTestId('document-table-action-btn').first().click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(page.getByRole('menuitem', { name: 'Move to Folder' })).toBeVisible();
|
||||
}).toPass();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Move to Folder' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Root' }).click();
|
||||
await page.getByRole('button', { name: 'Move' }).click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await page.goto('/documents');
|
||||
|
||||
await expect(page.getByText('Proposal 1')).toBeVisible();
|
||||
});
|
||||
|
||||
test('create folder button is visible on templates page', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/templates',
|
||||
});
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Create folder' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can create a template folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/templates',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: 'Create folder' }).click();
|
||||
await expect(page.getByRole('dialog', { name: 'Create New folder' })).toBeVisible();
|
||||
|
||||
await page.getByLabel('Folder name').fill('My template folder');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(page.getByText('My template folder')).toBeVisible();
|
||||
|
||||
await page.goto('/templates');
|
||||
await expect(page.locator('div').filter({ hasText: 'My template folder' }).nth(3)).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can create a template subfolder inside a template folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const folder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Client Templates',
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/templates/f/${folder.id}`,
|
||||
});
|
||||
|
||||
await expect(page.getByText('Client Templates')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Create folder' }).click();
|
||||
await expect(page.getByRole('dialog', { name: 'Create New folder' })).toBeVisible();
|
||||
|
||||
await page.getByLabel('Folder name').fill('Contract Templates');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(page.getByText('Contract Templates')).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can create a template inside a template folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const folder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Client Templates',
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/templates/f/${folder.id}`,
|
||||
});
|
||||
|
||||
await expect(page.getByText('Client Templates')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'New Template' }).click();
|
||||
// await expect(page.getByRole('dialog', { name: 'New Template' })).toBeVisible();
|
||||
|
||||
await page
|
||||
.locator('div')
|
||||
.filter({ hasText: /^Upload Template DocumentDrag & drop your PDF here\.$/ })
|
||||
.nth(2)
|
||||
.click();
|
||||
await page.locator('input[type="file"]').waitFor({ state: 'attached' });
|
||||
|
||||
await page
|
||||
.locator('input[type="file"]')
|
||||
.setInputFiles(path.join(__dirname, '../../../assets/documenso-supporter-pledge.pdf'));
|
||||
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(page.getByText('documenso-supporter-pledge.pdf')).toBeVisible();
|
||||
|
||||
await page.goto(`/templates/f/${folder.id}`);
|
||||
await expect(page.getByText('documenso-supporter-pledge.pdf')).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can pin a template folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Contract Templates',
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/templates',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: '•••' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Pin' }).click();
|
||||
|
||||
await page.reload();
|
||||
|
||||
await expect(page.locator('svg.text-documenso.h-3.w-3')).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can unpin a template folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Contract Templates',
|
||||
pinned: true,
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/templates',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: '•••' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Unpin' }).click();
|
||||
|
||||
await page.reload();
|
||||
|
||||
await expect(page.locator('svg.text-documenso.h-3.w-3')).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('user can rename a template folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Contract Templates',
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/templates',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: '•••' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Settings' }).click();
|
||||
|
||||
await page.getByLabel('Name').fill('Updated Template Folder');
|
||||
await page.getByRole('button', { name: 'Save Changes' }).click();
|
||||
|
||||
await expect(page.getByText('Updated Template Folder')).toBeVisible();
|
||||
});
|
||||
|
||||
test('template folder visibility is not visible to user', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Contract Templates',
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/templates',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: '•••' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Settings' }).click();
|
||||
|
||||
await expect(page.getByRole('menuitem', { name: 'Visibility' })).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('template folder can be moved to another template folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const folder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Client Templates',
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Contract Templates',
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/templates',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: '•••' }).nth(0).click();
|
||||
await page.getByRole('menuitem', { name: 'Move' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Client Templates' }).click();
|
||||
await page.getByRole('button', { name: 'Move Folder' }).click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await page.goto(`/templates/f/${folder.id}`);
|
||||
|
||||
await expect(page.getByText('Contract Templates')).toBeVisible();
|
||||
});
|
||||
|
||||
test('template folder can be moved to the root', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const parentFolder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Client Templates',
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Contract Templates',
|
||||
parentId: parentFolder.id,
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/templates',
|
||||
});
|
||||
|
||||
await page.getByText('Client Templates').click();
|
||||
await page.getByRole('button', { name: '•••' }).nth(0).click();
|
||||
await page.getByRole('menuitem', { name: 'Move' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Root' }).click();
|
||||
await page.getByRole('button', { name: 'Move Folder' }).click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await page.goto('/templates');
|
||||
|
||||
await expect(page.getByText('Contract Templates')).toBeVisible();
|
||||
});
|
||||
|
||||
test('template folder and its contents can be deleted', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const folder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Proposal Templates',
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
const template = await seedBlankTemplate(user, {
|
||||
createTemplateOptions: {
|
||||
title: 'Proposal Template 1',
|
||||
folderId: folder.id,
|
||||
},
|
||||
});
|
||||
|
||||
const subfolder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Report Templates',
|
||||
parentId: folder.id,
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
const reportTemplate = await seedBlankTemplate(user, {
|
||||
createTemplateOptions: {
|
||||
title: 'Report Template 1',
|
||||
folderId: subfolder.id,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/templates',
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: '•••' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||
|
||||
await page.getByRole('textbox').fill(`delete ${folder.name}`);
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
|
||||
await page.goto('/templates');
|
||||
|
||||
await expect(page.locator('div').filter({ hasText: folder.name })).not.toBeVisible();
|
||||
await expect(page.getByText(template.title)).not.toBeVisible();
|
||||
|
||||
await page.goto(`/templates/f/${folder.id}`);
|
||||
|
||||
await expect(page.getByText(reportTemplate.title)).not.toBeVisible();
|
||||
await expect(page.locator('div').filter({ hasText: subfolder.name })).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('user can navigate between template folders', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const parentFolder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Client Templates',
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
const subfolder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Contract Templates',
|
||||
parentId: parentFolder.id,
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankTemplate(user, {
|
||||
createTemplateOptions: {
|
||||
title: 'Contract Template 1',
|
||||
folderId: subfolder.id,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/templates',
|
||||
});
|
||||
|
||||
await page.getByText('Client Templates').click();
|
||||
await expect(page.getByText('Contract Templates')).toBeVisible();
|
||||
|
||||
await page.getByText('Contract Templates').click();
|
||||
await expect(page.getByText('Contract Template 1')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: parentFolder.name }).click();
|
||||
await expect(page.getByText('Contract Templates')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: subfolder.name }).click();
|
||||
await expect(page.getByText('Contract Template 1')).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can move a template to a template folder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const folder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Client Templates',
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankTemplate(user, {
|
||||
createTemplateOptions: {
|
||||
title: 'Proposal Template 1',
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/templates',
|
||||
});
|
||||
|
||||
await expect(async () => {
|
||||
await page.getByTestId('template-table-action-btn').first().click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(page.getByRole('menuitem', { name: 'Move to Folder' })).toBeVisible();
|
||||
}).toPass();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Move to Folder' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Client Templates' }).click();
|
||||
await page.getByRole('button', { name: 'Move' }).click();
|
||||
|
||||
await page.goto(`/templates/f/${folder.id}`);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(page.getByText('Proposal Template 1')).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can move a template from a folder to the root', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
const folder = await seedBlankFolder(user, {
|
||||
createFolderOptions: {
|
||||
name: 'Client Templates',
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankTemplate(user, {
|
||||
createTemplateOptions: {
|
||||
title: 'Proposal Template 1',
|
||||
folderId: folder.id,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/templates',
|
||||
});
|
||||
|
||||
await page.getByText('Client Templates').click();
|
||||
|
||||
await expect(async () => {
|
||||
await page.getByTestId('template-table-action-btn').first().click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(page.getByRole('menuitem', { name: 'Move to Folder' })).toBeVisible();
|
||||
}).toPass();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Move to Folder' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Root' }).click();
|
||||
await page.getByRole('button', { name: 'Move' }).click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await page.goto('/templates');
|
||||
|
||||
await expect(page.getByText('Proposal Template 1')).toBeVisible();
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
565
packages/app-tests/e2e/organisations/manage-organisation.spec.ts
Normal file
565
packages/app-tests/e2e/organisations/manage-organisation.spec.ts
Normal file
@ -0,0 +1,565 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { createTeam } from '@documenso/lib/server-only/team/create-team';
|
||||
import { nanoid } from '@documenso/lib/universal/id';
|
||||
import { seedOrganisationMembers } from '@documenso/prisma/seed/organisations';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin, apiSignout } from '../fixtures/authentication';
|
||||
import { expectTextToBeVisible, expectTextToNotBeVisible } from '../fixtures/generic';
|
||||
|
||||
test('[ORGANISATIONS]: create and delete organisation', async ({ page }) => {
|
||||
const { user, organisation } = await seedUser({
|
||||
isPersonalOrganisation: false,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/settings/organisations`,
|
||||
});
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Leave' })).toBeDisabled();
|
||||
|
||||
await page.getByRole('link', { name: 'Manage' }).click();
|
||||
await page.waitForURL(`/o/${organisation.url}/settings/general`);
|
||||
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
await page
|
||||
.getByLabel(`Confirm by typing delete ${organisation.name}`)
|
||||
.fill(`delete ${organisation.name}`);
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
|
||||
await page.waitForURL(`/settings/organisations`);
|
||||
await expect(page.getByText('No results found')).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Create organisation' }).click();
|
||||
|
||||
await page.getByLabel('Organisation Name*').fill('test');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await expect(page.getByText('Your organisation has been created').first()).toBeVisible();
|
||||
await page.reload();
|
||||
|
||||
await page.getByRole('row').filter({ hasText: 'test' }).getByRole('link').nth(1).click();
|
||||
});
|
||||
|
||||
test('[ORGANISATIONS]: manage general settings', async ({ page }) => {
|
||||
const { user, organisation } = await seedUser({
|
||||
isPersonalOrganisation: false,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/o/${organisation.url}/settings/general`,
|
||||
});
|
||||
|
||||
const updatedOrganisationId = `organisation-${Date.now()}`;
|
||||
|
||||
// Update team.
|
||||
await page.getByLabel('Organisation Name*').click();
|
||||
await page.getByLabel('Organisation Name*').clear();
|
||||
await page.getByLabel('Organisation Name*').fill(updatedOrganisationId);
|
||||
await page.getByLabel('Organisation URL*').click();
|
||||
await page.getByLabel('Organisation URL*').clear();
|
||||
await page.getByLabel('Organisation URL*').fill(updatedOrganisationId);
|
||||
|
||||
await page.getByRole('button', { name: 'Update organisation' }).click();
|
||||
|
||||
// Check we have been redirected to the new organisation URL and the name is updated.
|
||||
await page.waitForURL(`/o/${updatedOrganisationId}/settings/general`);
|
||||
});
|
||||
|
||||
test('[ORGANISATIONS]: inherit members', async ({ page }) => {
|
||||
const {
|
||||
user,
|
||||
organisation,
|
||||
team: teamWithInheritMembers,
|
||||
} = await seedUser({
|
||||
isPersonalOrganisation: false,
|
||||
});
|
||||
|
||||
const teamWithoutInheritedMembersUrl = `team-${nanoid()}`;
|
||||
|
||||
await createTeam({
|
||||
userId: user.id,
|
||||
teamName: 'No inherit',
|
||||
teamUrl: teamWithoutInheritedMembersUrl,
|
||||
organisationId: organisation.id,
|
||||
inheritMembers: false,
|
||||
});
|
||||
|
||||
const memberEmail = `member-${nanoid()}@test.documenso.com`;
|
||||
const memberEmail2 = `member-2-${nanoid()}@test.documenso.com`;
|
||||
const memberEmail3 = `member-3-${nanoid()}@test.documenso.com`;
|
||||
const managerEmail = `manager-${nanoid()}@test.documenso.com`;
|
||||
const adminEmail = `admin-${nanoid()}@test.documenso.com`;
|
||||
const ownerEmail = user.email;
|
||||
|
||||
await seedOrganisationMembers({
|
||||
members: [
|
||||
{
|
||||
email: memberEmail,
|
||||
name: 'Member 1',
|
||||
organisationRole: 'MEMBER',
|
||||
},
|
||||
{
|
||||
email: memberEmail2,
|
||||
name: 'Member 2',
|
||||
organisationRole: 'MEMBER',
|
||||
},
|
||||
{
|
||||
email: memberEmail3,
|
||||
name: 'Member 3',
|
||||
organisationRole: 'MEMBER',
|
||||
},
|
||||
{
|
||||
email: managerEmail,
|
||||
name: 'Manager',
|
||||
organisationRole: 'MANAGER',
|
||||
},
|
||||
{
|
||||
email: adminEmail,
|
||||
name: 'Admin',
|
||||
organisationRole: 'ADMIN',
|
||||
},
|
||||
],
|
||||
organisationId: organisation.id,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/t/${teamWithoutInheritedMembersUrl}/settings/members`,
|
||||
});
|
||||
|
||||
// Check from admin POV that member counts are correct
|
||||
// You should only see the manager/admins from the organisation in this table.
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(managerEmail),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(adminEmail),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(ownerEmail),
|
||||
).toBeVisible();
|
||||
await expect(page.getByRole('row').filter({ hasText: memberEmail })).not.toBeVisible();
|
||||
await expect(page.getByRole('row').filter({ hasText: memberEmail2 })).not.toBeVisible();
|
||||
await expect(page.getByRole('row').filter({ hasText: memberEmail3 })).not.toBeVisible();
|
||||
|
||||
// Explicitly add a member to the team.
|
||||
await page.getByRole('button', { name: 'Add members' }).click();
|
||||
await page.getByRole('combobox').click();
|
||||
await page.getByRole('option', { name: 'Member 1' }).first().click();
|
||||
await page.getByRole('button', { name: 'Next' }).click();
|
||||
await page.getByRole('button', { name: 'Add Members' }).click();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Member' }).getByText(memberEmail),
|
||||
).toBeVisible();
|
||||
|
||||
await page.goto(`/t/${teamWithInheritMembers.url}/settings/members`);
|
||||
|
||||
// Check from member POV that member counts are correct for inherit members team.
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(managerEmail),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(adminEmail),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(ownerEmail),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Member' }).getByText(memberEmail),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Member' }).getByText(memberEmail2),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Member' }).getByText(memberEmail3),
|
||||
).toBeVisible();
|
||||
|
||||
// Disable inherit mode.
|
||||
await page.goto(`/t/${teamWithInheritMembers.url}/settings/groups`);
|
||||
await page.getByRole('button', { name: 'Disable access' }).click();
|
||||
await page.getByRole('button', { name: 'Disable' }).click();
|
||||
await expect(page.getByText('Enable Access').first()).toBeVisible();
|
||||
|
||||
// Expect the inherited members to disappear
|
||||
await page.goto(`/t/${teamWithInheritMembers.url}/settings/members`);
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(managerEmail),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(adminEmail),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(ownerEmail),
|
||||
).toBeVisible();
|
||||
await expect(page.getByRole('row').filter({ hasText: memberEmail })).not.toBeVisible();
|
||||
await expect(page.getByRole('row').filter({ hasText: memberEmail2 })).not.toBeVisible();
|
||||
await expect(page.getByRole('row').filter({ hasText: memberEmail3 })).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('[ORGANISATIONS]: manage groups and members', async ({ page }) => {
|
||||
const {
|
||||
user,
|
||||
organisation,
|
||||
team: teamInherit,
|
||||
} = await seedUser({
|
||||
isPersonalOrganisation: false,
|
||||
});
|
||||
|
||||
const teamInheritName = teamInherit.name;
|
||||
|
||||
const teamA = `team-${nanoid()}`;
|
||||
const teamAName = `TeamA - No inherit`;
|
||||
const teamB = `team-${nanoid()}`;
|
||||
const teamBName = `TeamB - No inherit`;
|
||||
|
||||
await createTeam({
|
||||
userId: user.id,
|
||||
teamName: teamAName,
|
||||
teamUrl: teamA,
|
||||
organisationId: organisation.id,
|
||||
inheritMembers: false,
|
||||
});
|
||||
|
||||
await createTeam({
|
||||
userId: user.id,
|
||||
teamName: teamBName,
|
||||
teamUrl: teamB,
|
||||
organisationId: organisation.id,
|
||||
inheritMembers: false,
|
||||
});
|
||||
|
||||
const memberEmail1 = `member-1-${nanoid()}@test.documenso.com`;
|
||||
const memberEmail2 = `member-2-${nanoid()}@test.documenso.com`;
|
||||
const memberEmail3 = `member-3-${nanoid()}@test.documenso.com`;
|
||||
const memberEmail4 = `member-4-${nanoid()}@test.documenso.com`;
|
||||
const memberEmail5 = `member-5-${nanoid()}@test.documenso.com`;
|
||||
const memberEmail6 = `member-6-${nanoid()}@test.documenso.com`;
|
||||
|
||||
const adminEmail1 = `admin-1-${nanoid()}@test.documenso.com`;
|
||||
const adminEmail2 = `admin-2-${nanoid()}@test.documenso.com`;
|
||||
const adminEmail3 = `admin-3-${nanoid()}@test.documenso.com`;
|
||||
|
||||
const ownerEmail = user.email;
|
||||
|
||||
await seedOrganisationMembers({
|
||||
members: [
|
||||
{
|
||||
email: memberEmail1,
|
||||
name: 'Member1',
|
||||
organisationRole: 'MEMBER',
|
||||
},
|
||||
{
|
||||
email: memberEmail2,
|
||||
name: 'Member2',
|
||||
organisationRole: 'MEMBER',
|
||||
},
|
||||
{
|
||||
email: memberEmail3,
|
||||
name: 'Member3',
|
||||
organisationRole: 'MEMBER',
|
||||
},
|
||||
{
|
||||
email: memberEmail4,
|
||||
name: 'Member4',
|
||||
organisationRole: 'MEMBER',
|
||||
},
|
||||
{
|
||||
email: memberEmail5,
|
||||
name: 'Member5',
|
||||
organisationRole: 'MEMBER',
|
||||
},
|
||||
{
|
||||
email: memberEmail6,
|
||||
name: 'Member6',
|
||||
organisationRole: 'MEMBER',
|
||||
},
|
||||
{
|
||||
email: adminEmail1,
|
||||
name: 'Admin1',
|
||||
organisationRole: 'ADMIN',
|
||||
},
|
||||
{
|
||||
email: adminEmail2,
|
||||
name: 'Admin2',
|
||||
organisationRole: 'ADMIN',
|
||||
},
|
||||
{
|
||||
email: adminEmail3,
|
||||
name: 'Admin3',
|
||||
organisationRole: 'ADMIN',
|
||||
},
|
||||
],
|
||||
organisationId: organisation.id,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/o/${organisation.url}/settings/groups`,
|
||||
});
|
||||
|
||||
// Create a custom group A with 3 members "ORGANISATION ADMIN" to check that they get the correct roles.
|
||||
await page.getByRole('button', { name: 'Create group' }).click();
|
||||
await page.getByRole('textbox', { name: 'Group Name *' }).fill('CUSTOM_GROUP');
|
||||
await page.getByRole('combobox').filter({ hasText: 'Organisation Member' }).click();
|
||||
await page.getByRole('option', { name: 'Organisation Admin' }).click();
|
||||
await page.getByRole('combobox').filter({ hasText: 'Select members' }).click();
|
||||
await page.getByRole('option', { name: 'Member1' }).click();
|
||||
await page.getByRole('option', { name: 'Member2' }).click();
|
||||
await page.getByRole('option', { name: 'Member3' }).click();
|
||||
await page.getByTestId('dialog-create-organisation-button').click();
|
||||
await expect(page.getByText('Group has been created.').first()).toBeVisible();
|
||||
|
||||
await page.goto(`/o/${organisation.url}/settings/members`);
|
||||
|
||||
// Confirm org roles have been applied to these members.
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Organisation Admin' }).getByText(memberEmail1),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Organisation Admin' }).getByText(memberEmail2),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Organisation Admin' }).getByText(memberEmail3),
|
||||
).toBeVisible();
|
||||
|
||||
// Test updating the group.
|
||||
await page.goto(`/o/${organisation.url}/settings/groups`);
|
||||
await page.getByRole('link', { name: 'Manage' }).click();
|
||||
await page.getByRole('textbox', { name: 'Group Name *' }).fill('CUSTOM_GROUP_A');
|
||||
await page.getByRole('combobox').filter({ hasText: 'Organisation Admin' }).click();
|
||||
await page.getByRole('option', { name: 'Organisation Member' }).click();
|
||||
await page.getByRole('combobox').filter({ hasText: 'Member1, Member2, Member3' }).click();
|
||||
await page.getByRole('option', { name: 'Member3' }).click();
|
||||
await page.getByRole('button', { name: 'Update' }).click();
|
||||
await expect(page.getByText('Group has been updated successfully').first()).toBeVisible();
|
||||
|
||||
await page.goto(`/o/${organisation.url}/settings/groups`);
|
||||
|
||||
// Create a custom member group with the 3 admins to check that they still get the ADMIN roles.
|
||||
await page.getByRole('button', { name: 'Create group' }).click();
|
||||
await page.getByRole('textbox', { name: 'Group Name *' }).fill('CUSTOM_GROUP_ADMINS');
|
||||
await page.getByRole('combobox').filter({ hasText: 'Select members' }).click();
|
||||
await page.getByRole('option', { name: 'Admin1' }).click();
|
||||
await page.getByRole('option', { name: 'Admin2' }).click();
|
||||
await page.getByRole('option', { name: 'Admin3' }).click();
|
||||
await page.getByTestId('dialog-create-organisation-button').click();
|
||||
await expect(page.getByText('Group has been created.').first()).toBeVisible();
|
||||
|
||||
await page.goto(`/o/${organisation.url}/settings/members`);
|
||||
|
||||
// Confirm admins still get admin roles.
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Organisation Admin' }).getByText(adminEmail1),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Organisation Admin' }).getByText(adminEmail2),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Organisation Admin' }).getByText(adminEmail3),
|
||||
).toBeVisible();
|
||||
|
||||
// Create another custom group with 3 members with "ORGANISATION MEMBER" role.
|
||||
await page.goto(`/o/${organisation.url}/settings/groups`);
|
||||
await page.getByRole('button', { name: 'Create group' }).click();
|
||||
await page.getByRole('textbox', { name: 'Group Name *' }).fill('CUSTOM_GROUP_B');
|
||||
await page.getByRole('combobox').filter({ hasText: 'Organisation Member' }).click();
|
||||
await page.getByRole('option', { name: 'Organisation Admin' }).click();
|
||||
await page.getByRole('combobox').filter({ hasText: 'Select members' }).click();
|
||||
await page.getByRole('option', { name: 'Member4' }).click();
|
||||
await page.getByRole('option', { name: 'Member5' }).click();
|
||||
await page.getByTestId('dialog-create-organisation-button').click();
|
||||
await expect(page.getByText('Group has been created.').first()).toBeVisible();
|
||||
|
||||
// Assign CUSTOM_GROUP_A to TeamA
|
||||
await page.goto(`/t/${teamA}/settings/groups`);
|
||||
await page.getByRole('button', { name: 'Add groups' }).click();
|
||||
await page.getByRole('combobox').click();
|
||||
await page.getByRole('option', { name: 'CUSTOM_GROUP_A', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Next' }).click();
|
||||
await page.getByRole('combobox').click();
|
||||
await page.getByRole('option', { name: 'Manager' }).click();
|
||||
await page.getByRole('button', { name: 'Create Groups' }).click();
|
||||
await expect(page.getByText('Team members have been added').first()).toBeVisible();
|
||||
|
||||
// Assign CUSTOM_GROUP_B to TeamA
|
||||
await page.goto(`/t/${teamA}/settings/groups`);
|
||||
await page.getByRole('button', { name: 'Add groups' }).click();
|
||||
await page.getByRole('combobox').click();
|
||||
await page.getByRole('option', { name: 'CUSTOM_GROUP_B', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Next' }).click();
|
||||
await page.getByRole('combobox').click();
|
||||
await page.getByRole('option', { name: 'Manager' }).click();
|
||||
await page.getByRole('button', { name: 'Create Groups' }).click();
|
||||
await expect(page.getByText('Team members have been added').first()).toBeVisible();
|
||||
|
||||
// Update CUSTOM_GROUP_B
|
||||
await page.getByRole('row', { name: 'CUSTOM_GROUP_B' }).getByRole('button').click();
|
||||
await page.getByRole('menuitem', { name: 'Update role' }).click();
|
||||
await page.getByRole('combobox').click();
|
||||
await page.getByRole('option', { name: 'Team Admin' }).click();
|
||||
await page.getByRole('button', { name: 'Update' }).click();
|
||||
await expectTextToBeVisible(page, 'You have updated the team group');
|
||||
await expect(page.getByText('Team Admin').first()).toBeVisible();
|
||||
await page.reload();
|
||||
|
||||
// Delete CUSTOM_GROUP_B
|
||||
await page.getByRole('row', { name: 'CUSTOM_GROUP_B' }).getByRole('button').click();
|
||||
await page.getByRole('menuitem', { name: 'Remove' }).click();
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
await expectTextToBeVisible(page, 'You have successfully removed this group from the team.');
|
||||
await expect(page.getByText('CUSTOM_GROUP_B')).not.toBeVisible();
|
||||
|
||||
// Navigate to team members and validate members are there.
|
||||
await page.goto(`/t/${teamA}/settings/members`);
|
||||
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Manager' }).getByText(memberEmail1),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: 'Team Manager' }).getByText(memberEmail2),
|
||||
).toBeVisible();
|
||||
|
||||
// Member 1 should see inherit team and teamA
|
||||
await apiSignout({ page });
|
||||
await apiSignin({ page, email: memberEmail1, redirectPath: `/o/${organisation.url}` });
|
||||
await expectTextToBeVisible(page, teamInheritName);
|
||||
await expectTextToBeVisible(page, teamAName);
|
||||
await expectTextToNotBeVisible(page, teamBName);
|
||||
|
||||
// Member 3 should only see inherit team
|
||||
await apiSignout({ page });
|
||||
await apiSignin({ page, email: memberEmail3, redirectPath: `/o/${organisation.url}` });
|
||||
await expectTextToBeVisible(page, teamInheritName);
|
||||
await expectTextToNotBeVisible(page, teamAName);
|
||||
await expectTextToNotBeVisible(page, teamBName);
|
||||
|
||||
// Admin 1 should see all teams.
|
||||
await apiSignout({ page });
|
||||
await apiSignin({ page, email: adminEmail1, redirectPath: `/o/${organisation.url}` });
|
||||
await expectTextToBeVisible(page, teamInheritName);
|
||||
await expectTextToBeVisible(page, teamAName);
|
||||
await expectTextToBeVisible(page, teamBName);
|
||||
});
|
||||
|
||||
test('[ORGANISATIONS]: member invites', async ({ page }) => {
|
||||
const { user, organisation, team } = await seedUser({
|
||||
inheritMembers: false,
|
||||
});
|
||||
|
||||
const { user: user2 } = await seedUser({
|
||||
isPersonalOrganisation: false,
|
||||
});
|
||||
const { user: user3 } = await seedUser({
|
||||
isPersonalOrganisation: false,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/o/${organisation.url}/settings/members`,
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: 'Invite member' }).click();
|
||||
await page.getByRole('textbox', { name: 'Email address *' }).click();
|
||||
await page.getByRole('textbox', { name: 'Email address *' }).fill(user2.email);
|
||||
await page.getByRole('button', { name: 'Add more' }).click();
|
||||
|
||||
await page.locator('input[name="invitations\\.1\\.email"]').fill(user3.email);
|
||||
await page.getByRole('button', { name: 'Invite' }).click();
|
||||
|
||||
await page.getByRole('tab', { name: 'Pending' }).click();
|
||||
await expect(page.getByText(user2.email)).toBeVisible();
|
||||
await expect(page.getByText(user3.email)).toBeVisible();
|
||||
|
||||
await page.getByRole('row', { name: user3.email }).getByRole('button').click();
|
||||
await page.getByRole('menuitem', { name: 'Remove' }).click();
|
||||
await expect(page.getByText('Invitation has been deleted').first()).toBeVisible();
|
||||
await expect(page.getByText(user3.email)).not.toBeVisible();
|
||||
|
||||
// Sign in as member and accept invite
|
||||
await apiSignout({ page });
|
||||
await apiSignin({ page, email: user2.email, redirectPath: `/settings/organisations` });
|
||||
await page.getByRole('button', { name: 'View invites' }).click();
|
||||
await page.getByRole('button', { name: 'Accept' }).click();
|
||||
await expect(page.getByText('Invitation accepted').first()).toBeVisible();
|
||||
|
||||
// Sign back in as org owner.
|
||||
await apiSignout({ page });
|
||||
await apiSignin({ page, email: user.email, redirectPath: `/t/${team.url}/settings/members` });
|
||||
|
||||
// Expect 1 member in team.
|
||||
await expect(page.getByText(user.email)).toBeVisible();
|
||||
await expect(page.getByText(user2.email)).not.toBeVisible();
|
||||
|
||||
// Add member to team.
|
||||
await page.getByRole('button', { name: 'Add members' }).click();
|
||||
await page.getByRole('combobox').click();
|
||||
await page.getByRole('option', { name: user2.name ?? '' }).click();
|
||||
await page.getByRole('button', { name: 'Next' }).click();
|
||||
await page.getByRole('button', { name: 'Add Members' }).click();
|
||||
|
||||
// Expect 2 members to be visible.
|
||||
await expect(page.getByText(user.email)).toBeVisible();
|
||||
await expect(page.getByText(user2.email)).toBeVisible();
|
||||
|
||||
await page.getByRole('row', { name: user2.email }).getByRole('button').click();
|
||||
await page.getByRole('menuitem', { name: 'Remove' }).click();
|
||||
await page.getByRole('button', { name: 'Remove' }).click();
|
||||
await expect(page.getByText('You have successfully removed').first()).toBeVisible();
|
||||
|
||||
// Expect 1 member in team.
|
||||
await expect(page.getByText(user.email)).toBeVisible();
|
||||
await expect(page.getByText(user2.email)).not.toBeVisible();
|
||||
|
||||
// Expect 2 members in organisation.
|
||||
await page.goto(`/o/${organisation.url}/settings/members`);
|
||||
await expect(page.getByText(user.email)).toBeVisible();
|
||||
await expect(page.getByText(user2.email)).toBeVisible();
|
||||
|
||||
await page.getByRole('row', { name: user2.email }).getByRole('button').click();
|
||||
await page.getByRole('menuitem', { name: 'Remove' }).click();
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
await expect(page.getByText('You have successfully removed this user').first()).toBeVisible();
|
||||
|
||||
// Expect 1 member in organisation.
|
||||
await expect(page.getByText(user.email)).toBeVisible();
|
||||
await expect(page.getByText(user2.email)).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('[ORGANISATIONS]: leave organisation', async ({ page }) => {
|
||||
const { organisation } = await seedUser({
|
||||
isPersonalOrganisation: false,
|
||||
});
|
||||
|
||||
const memberEmail = `member-${nanoid()}@test.documenso.com`;
|
||||
|
||||
await seedOrganisationMembers({
|
||||
members: [
|
||||
{
|
||||
email: memberEmail,
|
||||
name: 'Member 1',
|
||||
organisationRole: 'MEMBER',
|
||||
},
|
||||
],
|
||||
organisationId: organisation.id,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: memberEmail,
|
||||
redirectPath: `/settings/organisations`,
|
||||
});
|
||||
await page.getByRole('button', { name: 'Leave' }).click();
|
||||
await page.getByRole('button', { name: 'Leave' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByText('You have successfully left this organisation').first(),
|
||||
).toBeVisible();
|
||||
await expect(page.getByText('No results found').first()).toBeVisible();
|
||||
});
|
||||
@ -0,0 +1,113 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { getTeamSettings } from '@documenso/lib/server-only/team/get-team-settings';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { DocumentVisibility } from '@documenso/prisma/client';
|
||||
import { seedTeamDocumentWithMeta } from '@documenso/prisma/seed/documents';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test('[ORGANISATIONS]: manage preferences', async ({ page }) => {
|
||||
const { user, organisation, team } = await seedUser({
|
||||
isPersonalOrganisation: false,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/o/${organisation.url}/settings/preferences`,
|
||||
});
|
||||
|
||||
// Update document preferences.
|
||||
await page.getByRole('combobox').filter({ hasText: 'Everyone can access and view' }).click();
|
||||
await page.getByRole('option', { name: 'Only managers and above can' }).click();
|
||||
await page.getByRole('combobox').filter({ hasText: 'English' }).click();
|
||||
await page.getByRole('option', { name: 'German' }).click();
|
||||
await page.getByTestId('signature-types-combobox').click();
|
||||
await page.getByRole('option', { name: 'Draw' }).click();
|
||||
await page.getByRole('option', { name: 'Upload' }).click();
|
||||
await page.getByRole('combobox').nth(3).click();
|
||||
await page.getByRole('option', { name: 'No' }).click();
|
||||
await page.getByRole('combobox').filter({ hasText: 'Yes' }).click();
|
||||
await page.getByRole('option', { name: 'No' }).click();
|
||||
await page.getByRole('button', { name: 'Update' }).first().click();
|
||||
await expect(page.getByText('Your document preferences have been updated').first()).toBeVisible();
|
||||
|
||||
// Update branding.
|
||||
await page.getByTestId('enable-branding').click();
|
||||
await page.getByRole('option', { name: 'Yes' }).click();
|
||||
await page.getByRole('textbox', { name: 'Brand Website' }).click();
|
||||
await page.getByRole('textbox', { name: 'Brand Website' }).fill('https://documenso.com');
|
||||
await page.getByRole('textbox', { name: 'Brand Details' }).click();
|
||||
await page.getByRole('textbox', { name: 'Brand Details' }).fill('BrandDetails');
|
||||
await page.getByRole('button', { name: 'Update' }).nth(1).click();
|
||||
await expect(page.getByText('Your branding preferences have been updated').first()).toBeVisible();
|
||||
|
||||
const teamSettings = await getTeamSettings({
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
// Check that the team settings have inherited these values.
|
||||
expect(teamSettings.documentVisibility).toEqual(DocumentVisibility.MANAGER_AND_ABOVE);
|
||||
expect(teamSettings.documentLanguage).toEqual('de');
|
||||
expect(teamSettings.includeSenderDetails).toEqual(false);
|
||||
expect(teamSettings.includeSigningCertificate).toEqual(false);
|
||||
expect(teamSettings.typedSignatureEnabled).toEqual(true);
|
||||
expect(teamSettings.uploadSignatureEnabled).toEqual(false);
|
||||
expect(teamSettings.drawSignatureEnabled).toEqual(false);
|
||||
expect(teamSettings.brandingEnabled).toEqual(true);
|
||||
expect(teamSettings.brandingUrl).toEqual('https://documenso.com');
|
||||
expect(teamSettings.brandingCompanyDetails).toEqual('BrandDetails');
|
||||
|
||||
// Edit the team settings
|
||||
await page.goto(`/t/${team.url}/settings/preferences`);
|
||||
|
||||
await page
|
||||
.getByRole('group')
|
||||
.locator('div')
|
||||
.filter({
|
||||
hasText: 'Default Document Visibility',
|
||||
})
|
||||
.getByRole('combobox')
|
||||
.click();
|
||||
await page.getByRole('option', { name: 'Everyone can access and view' }).click();
|
||||
await page
|
||||
.getByRole('group')
|
||||
.locator('div')
|
||||
.filter({ hasText: 'Default Document Language' })
|
||||
.getByRole('combobox')
|
||||
.click();
|
||||
await page.getByRole('option', { name: 'Polish' }).click();
|
||||
await page.getByRole('button', { name: 'Update' }).first().click();
|
||||
await expect(page.getByText('Your document preferences have been updated').first()).toBeVisible();
|
||||
|
||||
const updatedTeamSettings = await getTeamSettings({
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
// Check that the team settings have inherited/overriden the correct values.
|
||||
expect(updatedTeamSettings.documentVisibility).toEqual(DocumentVisibility.EVERYONE);
|
||||
expect(updatedTeamSettings.documentLanguage).toEqual('pl');
|
||||
expect(updatedTeamSettings.includeSenderDetails).toEqual(false);
|
||||
expect(updatedTeamSettings.includeSigningCertificate).toEqual(false);
|
||||
expect(updatedTeamSettings.typedSignatureEnabled).toEqual(true);
|
||||
expect(updatedTeamSettings.uploadSignatureEnabled).toEqual(false);
|
||||
expect(updatedTeamSettings.drawSignatureEnabled).toEqual(false);
|
||||
|
||||
const document = await seedTeamDocumentWithMeta(team);
|
||||
|
||||
const documentMeta = await prisma.documentMeta.findFirstOrThrow({
|
||||
where: {
|
||||
documentId: document.id,
|
||||
},
|
||||
});
|
||||
|
||||
// Confirm the settings have been applied to a newly created document.
|
||||
expect(document.visibility).toEqual(DocumentVisibility.EVERYONE);
|
||||
|
||||
expect(documentMeta.typedSignatureEnabled).toEqual(true);
|
||||
expect(documentMeta.uploadSignatureEnabled).toEqual(false);
|
||||
expect(documentMeta.drawSignatureEnabled).toEqual(false);
|
||||
expect(documentMeta.language).toEqual('pl');
|
||||
});
|
||||
@ -1,82 +1,13 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { seedTeam } from '@documenso/prisma/seed/teams';
|
||||
import { seedDirectTemplate } from '@documenso/prisma/seed/templates';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test('[PUBLIC_PROFILE]: create profile', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
// Create direct template.
|
||||
const directTemplate = await seedDirectTemplate({
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/settings/public-profile',
|
||||
});
|
||||
|
||||
const publicProfileUrl = Date.now().toString();
|
||||
const publicProfileBio = `public-profile-bio`;
|
||||
|
||||
await page.getByRole('textbox', { name: 'Public profile URL' }).click();
|
||||
await page.getByRole('textbox', { name: 'Public profile URL' }).fill(publicProfileUrl);
|
||||
|
||||
await page.getByRole('textbox', { name: 'Bio' }).click();
|
||||
await page.getByRole('textbox', { name: 'Bio' }).fill(publicProfileBio);
|
||||
|
||||
await page.getByRole('button', { name: 'Update' }).click();
|
||||
|
||||
await expect(page.getByRole('status').first()).toContainText(
|
||||
'Your public profile has been updated.',
|
||||
);
|
||||
|
||||
// Link direct template to public profile.
|
||||
await page.getByRole('button', { name: 'Link template' }).click();
|
||||
await page.getByRole('cell', { name: directTemplate.title }).click();
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
|
||||
await page.getByRole('textbox', { name: 'Title *' }).fill('public-direct-template-title');
|
||||
await page
|
||||
.getByRole('textbox', { name: 'Description *' })
|
||||
.fill('public-direct-template-description');
|
||||
await page.getByRole('button', { name: 'Update' }).click();
|
||||
|
||||
// Check that public profile is disabled.
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/p/${publicProfileUrl}`);
|
||||
await expect(page.locator('body')).toContainText('404 Profile not found');
|
||||
|
||||
// Go back to public profile page.
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/settings/public-profile`);
|
||||
await page.getByRole('switch').click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Assert values.
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/p/${publicProfileUrl}`);
|
||||
await expect(page.getByRole('main')).toContainText(publicProfileBio);
|
||||
await expect(page.locator('body')).toContainText('public-direct-template-title');
|
||||
await expect(page.locator('body')).toContainText('public-direct-template-description');
|
||||
|
||||
await page.getByRole('link', { name: 'Sign' }).click();
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await page.getByRole('button', { name: 'Complete' }).click();
|
||||
await page.getByRole('button', { name: 'Sign' }).click();
|
||||
|
||||
await expect(page.getByRole('heading', { name: 'Document Signed' })).toBeVisible();
|
||||
await expect(page.getByRole('heading')).toContainText('Document Signed');
|
||||
});
|
||||
|
||||
test('[PUBLIC_PROFILE]: create team profile', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const user = team.owner;
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
// Create direct template.
|
||||
const directTemplate = await seedDirectTemplate({
|
||||
@ -84,12 +15,6 @@ test('[PUBLIC_PROFILE]: create team profile', async ({ page }) => {
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
// Create non team template to make sure you can only see the team one.
|
||||
// Will be indirectly asserted because test should fail when 2 elements appear.
|
||||
await seedDirectTemplate({
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { seedTeam } from '@documenso/prisma/seed/teams';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
import { expectTextToBeVisible } from '../fixtures/generic';
|
||||
|
||||
test('[TEAMS]: create team', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const { user, organisation } = await seedUser();
|
||||
|
||||
test.skip(
|
||||
process.env.NEXT_PUBLIC_FEATURE_BILLING_ENABLED === 'true',
|
||||
@ -17,7 +17,7 @@ test('[TEAMS]: create team', async ({ page }) => {
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: '/settings/teams',
|
||||
redirectPath: `/o/${organisation.url}/settings/teams`,
|
||||
});
|
||||
|
||||
const teamId = `team-${Date.now()}`;
|
||||
@ -34,38 +34,32 @@ test('[TEAMS]: create team', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEAMS]: delete team', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const { user, team, organisation } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: team.owner.email,
|
||||
email: user.email,
|
||||
redirectPath: `/t/${team.url}/settings`,
|
||||
});
|
||||
|
||||
// Delete team.
|
||||
await page.getByRole('button', { name: 'Delete team' }).click();
|
||||
await page.getByLabel(`Confirm by typing delete ${team.url}`).fill(`delete ${team.url}`);
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
await page.getByLabel(`Confirm by typing delete ${team.name}`).fill(`delete ${team.name}`);
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
|
||||
// Check that we have been redirected to the teams page.
|
||||
await page.waitForURL(`${NEXT_PUBLIC_WEBAPP_URL()}/settings/teams`);
|
||||
// Your team has been successfully deleted
|
||||
await expectTextToBeVisible(page, 'Your team has been successfully deleted');
|
||||
});
|
||||
|
||||
test('[TEAMS]: update team', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: team.owner.email,
|
||||
email: user.email,
|
||||
redirectPath: `/t/${team.url}/settings`,
|
||||
});
|
||||
|
||||
// Navigate to create team page.
|
||||
await page.getByTestId('menu-switcher').click();
|
||||
await page.getByRole('menuitem', { name: 'Manage teams' }).click();
|
||||
|
||||
// Goto team settings page.
|
||||
await page.getByRole('row').filter({ hasText: team.url }).getByRole('link').nth(1).click();
|
||||
|
||||
const updatedTeamId = `team-${Date.now()}`;
|
||||
|
||||
// Update team.
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { DocumentStatus, TeamMemberRole } from '@prisma/client';
|
||||
import { DocumentStatus, OrganisationMemberRole, TeamMemberRole } from '@prisma/client';
|
||||
|
||||
import { generateDatabaseId } from '@documenso/lib/universal/id';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { seedDocuments, seedTeamDocuments } from '@documenso/prisma/seed/documents';
|
||||
import { seedOrganisationMembers } from '@documenso/prisma/seed/organisations';
|
||||
import { seedTeam, seedTeamMember } from '@documenso/prisma/seed/teams';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
@ -9,38 +12,75 @@ import { apiSignin, apiSignout } from '../fixtures/authentication';
|
||||
import { checkDocumentTabCount } from '../fixtures/documents';
|
||||
|
||||
test('[TEAMS]: search respects team document visibility', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const adminUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.ADMIN });
|
||||
const managerUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.MANAGER });
|
||||
const memberUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.MEMBER });
|
||||
const { user: owner, organisation, team } = await seedUser();
|
||||
|
||||
const [adminUser, managerUser, memberUser] = await seedOrganisationMembers({
|
||||
organisationId: organisation.id,
|
||||
members: [
|
||||
{
|
||||
organisationRole: OrganisationMemberRole.ADMIN,
|
||||
},
|
||||
{
|
||||
organisationRole: OrganisationMemberRole.MEMBER, // Org managers = team admins so need to workaround this.
|
||||
},
|
||||
{
|
||||
organisationRole: OrganisationMemberRole.MEMBER,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const managerTeamGroup = await prisma.teamGroup.findFirstOrThrow({
|
||||
where: {
|
||||
teamId: team.id,
|
||||
teamRole: TeamMemberRole.MANAGER,
|
||||
},
|
||||
include: {
|
||||
organisationGroup: true,
|
||||
},
|
||||
});
|
||||
|
||||
const managerOrganisationMember = await prisma.organisationMember.findFirstOrThrow({
|
||||
where: {
|
||||
organisationId: organisation.id,
|
||||
userId: managerUser.id,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.organisationGroupMember.create({
|
||||
data: {
|
||||
id: generateDatabaseId('group_member'),
|
||||
groupId: managerTeamGroup.organisationGroupId,
|
||||
organisationMemberId: managerOrganisationMember.id,
|
||||
},
|
||||
});
|
||||
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
teamId: team.id,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'EVERYONE',
|
||||
title: 'Searchable Document for Everyone',
|
||||
},
|
||||
},
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
teamId: team.id,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'MANAGER_AND_ABOVE',
|
||||
title: 'Searchable Document for Managers',
|
||||
},
|
||||
},
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
teamId: team.id,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'ADMIN',
|
||||
title: 'Searchable Document for Admins',
|
||||
},
|
||||
@ -70,26 +110,30 @@ test('[TEAMS]: search respects team document visibility', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEAMS]: search does not reveal documents from other teams', async ({ page }) => {
|
||||
const { team: teamA, teamMember2: teamAMember } = await seedTeamDocuments();
|
||||
const { team: teamB } = await seedTeamDocuments();
|
||||
const {
|
||||
team: teamA,
|
||||
teamOwner: teamAOwner,
|
||||
teamMember2: teamAMember,
|
||||
} = await seedTeamDocuments();
|
||||
const { team: teamB, teamOwner: teamBOwner } = await seedTeamDocuments();
|
||||
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: teamA.owner,
|
||||
sender: teamAOwner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: teamA.id,
|
||||
documentOptions: {
|
||||
teamId: teamA.id,
|
||||
visibility: 'EVERYONE',
|
||||
title: 'Unique Team A Document',
|
||||
},
|
||||
},
|
||||
{
|
||||
sender: teamB.owner,
|
||||
sender: teamBOwner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: teamB.id,
|
||||
documentOptions: {
|
||||
teamId: teamB.id,
|
||||
visibility: 'EVERYONE',
|
||||
title: 'Unique Team B Document',
|
||||
},
|
||||
@ -112,60 +156,19 @@ test('[TEAMS]: search does not reveal documents from other teams', async ({ page
|
||||
await apiSignout({ page });
|
||||
});
|
||||
|
||||
test('[PERSONAL]: search does not reveal team documents in personal account', async ({ page }) => {
|
||||
const { team, teamMember2 } = await seedTeamDocuments();
|
||||
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: teamMember2,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: null,
|
||||
title: 'Personal Unique Document',
|
||||
},
|
||||
},
|
||||
{
|
||||
sender: team.owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'EVERYONE',
|
||||
title: 'Team Unique Document',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamMember2.email,
|
||||
redirectPath: '/documents',
|
||||
});
|
||||
|
||||
await page.getByPlaceholder('Search documents...').fill('Unique');
|
||||
await page.waitForURL(/query=Unique/);
|
||||
|
||||
await checkDocumentTabCount(page, 'All', 1);
|
||||
await expect(page.getByRole('link', { name: 'Personal Unique Document' })).toBeVisible();
|
||||
await expect(page.getByRole('link', { name: 'Team Unique Document' })).not.toBeVisible();
|
||||
|
||||
await apiSignout({ page });
|
||||
});
|
||||
|
||||
test('[TEAMS]: search respects recipient visibility regardless of team visibility', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam();
|
||||
const { team, owner } = await seedTeam();
|
||||
const memberUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.MEMBER });
|
||||
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
teamId: team.id,
|
||||
recipients: [memberUser],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'ADMIN',
|
||||
title: 'Admin Document with Member Recipient',
|
||||
},
|
||||
@ -190,7 +193,7 @@ test('[TEAMS]: search respects recipient visibility regardless of team visibilit
|
||||
});
|
||||
|
||||
test('[TEAMS]: search by recipient name respects visibility', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const { team, owner } = await seedTeam();
|
||||
const adminUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.ADMIN });
|
||||
const memberUser = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
@ -198,15 +201,15 @@ test('[TEAMS]: search by recipient name respects visibility', async ({ page }) =
|
||||
name: 'Team Member',
|
||||
});
|
||||
|
||||
const uniqueRecipient = await seedUser();
|
||||
const { user: uniqueRecipient } = await seedUser();
|
||||
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
recipients: [uniqueRecipient],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: team.id,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'ADMIN',
|
||||
title: 'Admin Document for Unique Recipient',
|
||||
},
|
||||
|
||||
@ -8,12 +8,13 @@ import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin, apiSignout } from '../fixtures/authentication';
|
||||
import { checkDocumentTabCount } from '../fixtures/documents';
|
||||
import { expectTextToBeVisible } from '../fixtures/generic';
|
||||
|
||||
test('[TEAMS]: check team documents count', async ({ page }) => {
|
||||
const { team, teamMember2 } = await seedTeamDocuments();
|
||||
const { team, teamOwner, teamMember2 } = await seedTeamDocuments();
|
||||
|
||||
// Run the test twice, once with the team owner and once with a team member to ensure the counts are the same.
|
||||
for (const user of [team.owner, teamMember2]) {
|
||||
for (const user of [teamOwner, teamMember2]) {
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
@ -44,8 +45,12 @@ test('[TEAMS]: check team documents count', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEAMS]: check team documents count with internal team email', async ({ page }) => {
|
||||
const { team, teamMember2, teamMember4 } = await seedTeamDocuments();
|
||||
const { team: team2, teamMember2: team2Member2 } = await seedTeamDocuments();
|
||||
const { team, teamOwner, teamMember2, teamMember4 } = await seedTeamDocuments();
|
||||
const {
|
||||
team: team2,
|
||||
teamOwner: team2Owner,
|
||||
teamMember2: team2Member2,
|
||||
} = await seedTeamDocuments();
|
||||
|
||||
const teamEmailMember = teamMember4;
|
||||
|
||||
@ -54,7 +59,7 @@ test('[TEAMS]: check team documents count with internal team email', async ({ pa
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
const testUser1 = await seedUser();
|
||||
const { user: testUser1, team: testUser1Team } = await seedUser();
|
||||
|
||||
await seedDocuments([
|
||||
// Documents sent from the team email account.
|
||||
@ -62,52 +67,53 @@ test('[TEAMS]: check team documents count with internal team email', async ({ pa
|
||||
sender: teamEmailMember,
|
||||
recipients: [testUser1],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
},
|
||||
teamId: team.id,
|
||||
documentOptions: {},
|
||||
},
|
||||
{
|
||||
sender: teamEmailMember,
|
||||
recipients: [testUser1],
|
||||
type: DocumentStatus.PENDING,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
},
|
||||
teamId: team.id,
|
||||
documentOptions: {},
|
||||
},
|
||||
{
|
||||
sender: teamMember4,
|
||||
recipients: [testUser1],
|
||||
type: DocumentStatus.DRAFT,
|
||||
teamId: team.id,
|
||||
},
|
||||
// Documents sent to the team email account.
|
||||
{
|
||||
sender: testUser1,
|
||||
recipients: [teamEmailMember],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: testUser1Team.id,
|
||||
},
|
||||
{
|
||||
sender: testUser1,
|
||||
recipients: [teamEmailMember],
|
||||
type: DocumentStatus.PENDING,
|
||||
teamId: testUser1Team.id,
|
||||
},
|
||||
{
|
||||
sender: testUser1,
|
||||
recipients: [teamEmailMember],
|
||||
type: DocumentStatus.DRAFT,
|
||||
teamId: testUser1Team.id,
|
||||
},
|
||||
// Document sent to the team email account from another team.
|
||||
{
|
||||
sender: team2Member2,
|
||||
recipients: [teamEmailMember],
|
||||
type: DocumentStatus.PENDING,
|
||||
documentOptions: {
|
||||
teamId: team2.id,
|
||||
},
|
||||
teamId: team2.id,
|
||||
documentOptions: {},
|
||||
},
|
||||
]);
|
||||
|
||||
// Run the test twice, one with the team owner and once with the team member email to ensure the counts are the same.
|
||||
for (const user of [team.owner, teamEmailMember]) {
|
||||
for (const user of [teamOwner, teamEmailMember]) {
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
@ -138,7 +144,8 @@ test('[TEAMS]: check team documents count with internal team email', async ({ pa
|
||||
});
|
||||
|
||||
test('[TEAMS]: check team documents count with external team email', async ({ page }) => {
|
||||
const { team, teamMember2 } = await seedTeamDocuments();
|
||||
const { team, teamOwner, teamMember2 } = await seedTeamDocuments();
|
||||
|
||||
const { team: team2, teamMember2: team2Member2 } = await seedTeamDocuments();
|
||||
|
||||
const teamEmail = `external-team-email-${team.id}@test.documenso.com`;
|
||||
@ -148,7 +155,9 @@ test('[TEAMS]: check team documents count with external team email', async ({ pa
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
const testUser1 = await seedUser();
|
||||
const { user: testUser1, team: testUser1Team } = await seedUser({
|
||||
isPersonalOrganisation: true,
|
||||
});
|
||||
|
||||
await seedDocuments([
|
||||
// Documents sent to the team email account.
|
||||
@ -156,42 +165,39 @@ test('[TEAMS]: check team documents count with external team email', async ({ pa
|
||||
sender: testUser1,
|
||||
recipients: [teamEmail],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: testUser1Team.id,
|
||||
},
|
||||
{
|
||||
sender: testUser1,
|
||||
recipients: [teamEmail],
|
||||
type: DocumentStatus.PENDING,
|
||||
teamId: testUser1Team.id,
|
||||
},
|
||||
{
|
||||
sender: testUser1,
|
||||
recipients: [teamEmail],
|
||||
type: DocumentStatus.DRAFT,
|
||||
teamId: testUser1Team.id,
|
||||
},
|
||||
// Document sent to the team email account from another team.
|
||||
{
|
||||
sender: team2Member2,
|
||||
recipients: [teamEmail],
|
||||
type: DocumentStatus.PENDING,
|
||||
documentOptions: {
|
||||
teamId: team2.id,
|
||||
},
|
||||
teamId: team2.id,
|
||||
},
|
||||
// Document sent to the team email account from an individual user.
|
||||
{
|
||||
sender: testUser1,
|
||||
recipients: [teamEmail],
|
||||
type: DocumentStatus.PENDING,
|
||||
documentOptions: {
|
||||
teamId: team2.id,
|
||||
},
|
||||
teamId: testUser1Team.id,
|
||||
},
|
||||
{
|
||||
sender: testUser1,
|
||||
recipients: [teamEmail],
|
||||
type: DocumentStatus.DRAFT,
|
||||
documentOptions: {
|
||||
teamId: team2.id,
|
||||
},
|
||||
teamId: testUser1Team.id,
|
||||
},
|
||||
]);
|
||||
|
||||
@ -222,7 +228,7 @@ test('[TEAMS]: check team documents count with external team email', async ({ pa
|
||||
});
|
||||
|
||||
test('[TEAMS]: resend pending team document', async ({ page }) => {
|
||||
const { team, teamMember2: currentUser } = await seedTeamDocuments();
|
||||
const { team, teamOwner, teamMember2: currentUser } = await seedTeamDocuments();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
@ -248,7 +254,7 @@ test('[TEAMS]: resend pending team document', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEAMS]: delete draft team document', async ({ page }) => {
|
||||
const { team, teamMember2: teamEmailMember, teamMember3 } = await seedTeamDocuments();
|
||||
const { team, teamOwner, teamMember2: teamEmailMember, teamMember3 } = await seedTeamDocuments();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
@ -273,7 +279,7 @@ test('[TEAMS]: delete draft team document', async ({ page }) => {
|
||||
await apiSignout({ page });
|
||||
|
||||
// Run the test twice, one with the team owner and once with the team member email to ensure the counts are the same.
|
||||
for (const user of [team.owner, teamEmailMember]) {
|
||||
for (const user of [teamOwner, teamEmailMember]) {
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
@ -292,7 +298,7 @@ test('[TEAMS]: delete draft team document', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEAMS]: delete pending team document', async ({ page }) => {
|
||||
const { team, teamMember2: teamEmailMember, teamMember3 } = await seedTeamDocuments();
|
||||
const { team, teamOwner, teamMember2: teamEmailMember, teamMember3 } = await seedTeamDocuments();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
@ -318,7 +324,7 @@ test('[TEAMS]: delete pending team document', async ({ page }) => {
|
||||
await apiSignout({ page });
|
||||
|
||||
// Run the test twice, one with the team owner and once with the team member email to ensure the counts are the same.
|
||||
for (const user of [team.owner, teamEmailMember]) {
|
||||
for (const user of [teamOwner, teamEmailMember]) {
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
@ -337,7 +343,7 @@ test('[TEAMS]: delete pending team document', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEAMS]: delete completed team document', async ({ page }) => {
|
||||
const { team, teamMember2: teamEmailMember, teamMember3 } = await seedTeamDocuments();
|
||||
const { team, teamOwner, teamMember2: teamEmailMember, teamMember3 } = await seedTeamDocuments();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
@ -363,7 +369,7 @@ test('[TEAMS]: delete completed team document', async ({ page }) => {
|
||||
await apiSignout({ page });
|
||||
|
||||
// Run the test twice, one with the team owner and once with the team member email to ensure the counts are the same.
|
||||
for (const user of [team.owner, teamEmailMember]) {
|
||||
for (const user of [teamOwner, teamEmailMember]) {
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
@ -382,7 +388,7 @@ test('[TEAMS]: delete completed team document', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEAMS]: check document visibility based on team member role', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const { team, owner } = await seedTeam();
|
||||
|
||||
// Seed users with different roles
|
||||
const adminUser = await seedTeamMember({
|
||||
@ -400,46 +406,48 @@ test('[TEAMS]: check document visibility based on team member role', async ({ pa
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
const outsideUser = await seedUser();
|
||||
const { user: outsideUser, team: outsideUserTeam } = await seedUser({
|
||||
isPersonalOrganisation: true,
|
||||
});
|
||||
|
||||
// Seed documents with different visibility levels
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: team.id,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'EVERYONE',
|
||||
title: 'Document Visible to Everyone',
|
||||
},
|
||||
},
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: team.id,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'MANAGER_AND_ABOVE',
|
||||
title: 'Document Visible to Manager and Above',
|
||||
},
|
||||
},
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: team.id,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'ADMIN',
|
||||
title: 'Document Visible to Admin',
|
||||
},
|
||||
},
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
recipients: [outsideUser],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: team.id,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'ADMIN',
|
||||
title: 'Document Visible to Admin with Recipient',
|
||||
},
|
||||
@ -470,11 +478,6 @@ test('[TEAMS]: check document visibility based on team member role', async ({ pa
|
||||
path: teamUrlRedirect,
|
||||
expectedDocuments: ['Document Visible to Everyone'],
|
||||
},
|
||||
{
|
||||
user: outsideUser,
|
||||
path: '/documents',
|
||||
expectedDocuments: ['Document Visible to Admin with Recipient'],
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
@ -491,12 +494,20 @@ test('[TEAMS]: check document visibility based on team member role', async ({ pa
|
||||
|
||||
await apiSignout({ page });
|
||||
}
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: outsideUser.email,
|
||||
redirectPath: '/inbox',
|
||||
});
|
||||
|
||||
await expectTextToBeVisible(page, 'Document Visible to Admin with Recipient');
|
||||
});
|
||||
|
||||
test('[TEAMS]: ensure document owner can see document regardless of visibility', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam();
|
||||
const { team, owner } = await seedTeam();
|
||||
|
||||
// Seed a member user
|
||||
const memberUser = await seedTeamMember({
|
||||
@ -510,8 +521,8 @@ test('[TEAMS]: ensure document owner can see document regardless of visibility',
|
||||
sender: memberUser,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: team.id,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'ADMIN',
|
||||
title: 'Admin Document with Member Document Owner',
|
||||
},
|
||||
@ -533,7 +544,7 @@ test('[TEAMS]: ensure document owner can see document regardless of visibility',
|
||||
});
|
||||
|
||||
test('[TEAMS]: ensure recipient can see document regardless of visibility', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const { team, owner } = await seedTeam();
|
||||
|
||||
// Seed a member user
|
||||
const memberUser = await seedTeamMember({
|
||||
@ -544,11 +555,11 @@ test('[TEAMS]: ensure recipient can see document regardless of visibility', asyn
|
||||
// Seed a document with ADMIN visibility but make the member user a recipient
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
recipients: [memberUser],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: team.id,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'ADMIN',
|
||||
title: 'Admin Document with Member Recipient',
|
||||
},
|
||||
@ -570,7 +581,7 @@ test('[TEAMS]: ensure recipient can see document regardless of visibility', asyn
|
||||
});
|
||||
|
||||
test('[TEAMS]: check that MEMBER role cannot see ADMIN-only documents', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const { team, owner } = await seedTeam();
|
||||
|
||||
// Seed a member user
|
||||
const memberUser = await seedTeamMember({
|
||||
@ -581,11 +592,11 @@ test('[TEAMS]: check that MEMBER role cannot see ADMIN-only documents', async ({
|
||||
// Seed an ADMIN-only document
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: team.id,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'ADMIN',
|
||||
title: 'Admin Only Document',
|
||||
},
|
||||
@ -609,7 +620,7 @@ test('[TEAMS]: check that MEMBER role cannot see ADMIN-only documents', async ({
|
||||
test('[TEAMS]: check that MEMBER role cannot see MANAGER_AND_ABOVE-only documents', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam();
|
||||
const { team, owner } = await seedTeam();
|
||||
|
||||
// Seed a member user
|
||||
const memberUser = await seedTeamMember({
|
||||
@ -620,11 +631,11 @@ test('[TEAMS]: check that MEMBER role cannot see MANAGER_AND_ABOVE-only document
|
||||
// Seed an ADMIN-only document
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: team.id,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'MANAGER_AND_ABOVE',
|
||||
title: 'Manager and Above Only Document',
|
||||
},
|
||||
@ -646,7 +657,7 @@ test('[TEAMS]: check that MEMBER role cannot see MANAGER_AND_ABOVE-only document
|
||||
});
|
||||
|
||||
test('[TEAMS]: check that MANAGER role cannot see ADMIN-only documents', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const { team, owner } = await seedTeam();
|
||||
|
||||
// Seed a manager user
|
||||
const managerUser = await seedTeamMember({
|
||||
@ -657,11 +668,11 @@ test('[TEAMS]: check that MANAGER role cannot see ADMIN-only documents', async (
|
||||
// Seed an ADMIN-only document
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: team.id,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'ADMIN',
|
||||
title: 'Admin Only Document',
|
||||
},
|
||||
@ -683,7 +694,7 @@ test('[TEAMS]: check that MANAGER role cannot see ADMIN-only documents', async (
|
||||
});
|
||||
|
||||
test('[TEAMS]: check that ADMIN role can see MANAGER_AND_ABOVE documents', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const { team, owner } = await seedTeam();
|
||||
|
||||
// Seed an admin user
|
||||
const adminUser = await seedTeamMember({
|
||||
@ -694,11 +705,11 @@ test('[TEAMS]: check that ADMIN role can see MANAGER_AND_ABOVE documents', async
|
||||
// Seed a MANAGER_AND_ABOVE document
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: team.owner,
|
||||
sender: owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: team.id,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'MANAGER_AND_ABOVE',
|
||||
title: 'Manager and Above Document',
|
||||
},
|
||||
@ -720,25 +731,16 @@ test('[TEAMS]: check that ADMIN role can see MANAGER_AND_ABOVE documents', async
|
||||
});
|
||||
|
||||
test('[TEAMS]: check that ADMIN role can change document visibility', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamOptions: {
|
||||
teamGlobalSettings: {
|
||||
create: {
|
||||
documentVisibility: DocumentVisibility.MANAGER_AND_ABOVE,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const { team, owner } = await seedTeam();
|
||||
|
||||
const adminUser = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.ADMIN,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(adminUser, {
|
||||
const document = await seedBlankDocument(adminUser, team.id, {
|
||||
createDocumentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: team.teamGlobalSettings?.documentVisibility,
|
||||
visibility: DocumentVisibility.MANAGER_AND_ABOVE,
|
||||
},
|
||||
});
|
||||
|
||||
@ -763,25 +765,16 @@ test('[TEAMS]: check that ADMIN role can change document visibility', async ({ p
|
||||
test('[TEAMS]: check that MEMBER role cannot change visibility of EVERYONE documents', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam({
|
||||
createTeamOptions: {
|
||||
teamGlobalSettings: {
|
||||
create: {
|
||||
documentVisibility: DocumentVisibility.EVERYONE,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const { team, owner } = await seedTeam();
|
||||
|
||||
const teamMember = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(teamMember, {
|
||||
const document = await seedBlankDocument(teamMember, team.id, {
|
||||
createDocumentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: team.teamGlobalSettings?.documentVisibility,
|
||||
visibility: DocumentVisibility.EVERYONE,
|
||||
},
|
||||
});
|
||||
|
||||
@ -798,25 +791,16 @@ test('[TEAMS]: check that MEMBER role cannot change visibility of EVERYONE docum
|
||||
test('[TEAMS]: check that MEMBER role cannot change visibility of MANAGER_AND_ABOVE documents', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam({
|
||||
createTeamOptions: {
|
||||
teamGlobalSettings: {
|
||||
create: {
|
||||
documentVisibility: DocumentVisibility.MANAGER_AND_ABOVE,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const { team, owner } = await seedTeam();
|
||||
|
||||
const teamMember = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(teamMember, {
|
||||
const document = await seedBlankDocument(teamMember, team.id, {
|
||||
createDocumentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: team.teamGlobalSettings?.documentVisibility,
|
||||
visibility: DocumentVisibility.MANAGER_AND_ABOVE,
|
||||
},
|
||||
});
|
||||
|
||||
@ -833,25 +817,16 @@ test('[TEAMS]: check that MEMBER role cannot change visibility of MANAGER_AND_AB
|
||||
test('[TEAMS]: check that MEMBER role cannot change visibility of ADMIN documents', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam({
|
||||
createTeamOptions: {
|
||||
teamGlobalSettings: {
|
||||
create: {
|
||||
documentVisibility: DocumentVisibility.ADMIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const { team, owner } = await seedTeam();
|
||||
|
||||
const teamMember = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(teamMember, {
|
||||
const document = await seedBlankDocument(teamMember, team.id, {
|
||||
createDocumentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: team.teamGlobalSettings?.documentVisibility,
|
||||
visibility: DocumentVisibility.ADMIN,
|
||||
},
|
||||
});
|
||||
|
||||
@ -868,25 +843,16 @@ test('[TEAMS]: check that MEMBER role cannot change visibility of ADMIN document
|
||||
test('[TEAMS]: check that MANAGER role cannot change visibility of ADMIN documents', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam({
|
||||
createTeamOptions: {
|
||||
teamGlobalSettings: {
|
||||
create: {
|
||||
documentVisibility: DocumentVisibility.ADMIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const { team, owner } = await seedTeam();
|
||||
|
||||
const teamManager = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MANAGER,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(teamManager, {
|
||||
const document = await seedBlankDocument(teamManager, team.id, {
|
||||
createDocumentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: team.teamGlobalSettings?.documentVisibility,
|
||||
visibility: DocumentVisibility.ADMIN,
|
||||
},
|
||||
});
|
||||
|
||||
@ -902,17 +868,25 @@ test('[TEAMS]: check that MANAGER role cannot change visibility of ADMIN documen
|
||||
|
||||
test('[TEAMS]: users cannot see documents from other teams', async ({ page }) => {
|
||||
// Seed two teams with documents
|
||||
const { team: teamA, teamMember2: teamAMember } = await seedTeamDocuments();
|
||||
const { team: teamB, teamMember2: teamBMember } = await seedTeamDocuments();
|
||||
const {
|
||||
team: teamA,
|
||||
teamOwner: teamAOwner,
|
||||
teamMember2: teamAMember,
|
||||
} = await seedTeamDocuments();
|
||||
const {
|
||||
team: teamB,
|
||||
teamOwner: teamBOwner,
|
||||
teamMember2: teamBMember,
|
||||
} = await seedTeamDocuments();
|
||||
|
||||
// Seed a document in team B
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: teamB.owner,
|
||||
sender: teamBOwner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: teamB.id,
|
||||
documentOptions: {
|
||||
teamId: teamB.id,
|
||||
visibility: 'EVERYONE',
|
||||
title: 'Team B Document',
|
||||
},
|
||||
@ -934,8 +908,8 @@ test('[TEAMS]: users cannot see documents from other teams', async ({ page }) =>
|
||||
|
||||
test('[TEAMS]: personal documents are not visible in team context', async ({ page }) => {
|
||||
// Seed a team and a user with personal documents
|
||||
const { team, teamMember2 } = await seedTeamDocuments();
|
||||
const personalUser = await seedUser();
|
||||
const { team, teamOwner, teamMember2 } = await seedTeamDocuments();
|
||||
const { user: personalUser, team: personalUserTeam } = await seedUser();
|
||||
|
||||
// Seed a personal document for teamMember2
|
||||
await seedDocuments([
|
||||
@ -943,8 +917,8 @@ test('[TEAMS]: personal documents are not visible in team context', async ({ pag
|
||||
sender: teamMember2,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
teamId: personalUserTeam.id,
|
||||
documentOptions: {
|
||||
teamId: null, // Indicates a personal document
|
||||
visibility: 'EVERYONE',
|
||||
title: 'Personal Document',
|
||||
},
|
||||
@ -965,34 +939,3 @@ test('[TEAMS]: personal documents are not visible in team context', async ({ pag
|
||||
|
||||
await apiSignout({ page });
|
||||
});
|
||||
|
||||
test('[PERSONAL]: team documents are not visible in personal account', async ({ page }) => {
|
||||
// Seed a team and a user with personal documents
|
||||
const { team, teamMember2 } = await seedTeamDocuments();
|
||||
|
||||
// Seed a team document
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: teamMember2,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'EVERYONE',
|
||||
title: 'Team Document',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// Sign in as teamMember2 in the personal context
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamMember2.email,
|
||||
redirectPath: `/documents?status=COMPLETED`,
|
||||
});
|
||||
|
||||
// Verify that the team document is not visible in the personal context
|
||||
await expect(page.getByRole('link', { name: 'Team Document', exact: true })).not.toBeVisible();
|
||||
|
||||
await apiSignout({ page });
|
||||
});
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { seedTeam, seedTeamEmailVerification } from '@documenso/prisma/seed/teams';
|
||||
import { seedTeamEmailVerification } from '@documenso/prisma/seed/teams';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test('[TEAMS]: send team email request', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: team.owner.email,
|
||||
email: user.email,
|
||||
password: 'password',
|
||||
redirectPath: `/t/${team.url}/settings`,
|
||||
});
|
||||
@ -32,9 +32,7 @@ test('[TEAMS]: send team email request', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEAMS]: accept team email request', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const teamEmailVerification = await seedTeamEmailVerification({
|
||||
email: `team-email-verification--${team.url}@test.documenso.com`,
|
||||
@ -46,14 +44,13 @@ test('[TEAMS]: accept team email request', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEAMS]: delete team email', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
createTeamEmail: true,
|
||||
const { user, team } = await seedUser({
|
||||
setTeamEmailAsOwner: true,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: team.owner.email,
|
||||
email: user.email,
|
||||
redirectPath: `/t/${team.url}/settings`,
|
||||
});
|
||||
|
||||
@ -66,23 +63,16 @@ test('[TEAMS]: delete team email', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEAMS]: team email owner removes access', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
createTeamEmail: true,
|
||||
});
|
||||
const teamEmailOwner = await seedUser();
|
||||
|
||||
if (!team.teamEmail) {
|
||||
throw new Error('Not possible');
|
||||
}
|
||||
|
||||
const teamEmailOwner = await seedUser({
|
||||
email: team.teamEmail.email,
|
||||
const { user: secondUser } = await seedUser({
|
||||
teamEmail: teamEmailOwner.user.email,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamEmailOwner.email,
|
||||
redirectPath: `/settings/teams`,
|
||||
email: teamEmailOwner.user.email,
|
||||
redirectPath: `/settings/profile`,
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: 'Revoke access' }).click();
|
||||
|
||||
@ -1,55 +0,0 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { seedTeam } from '@documenso/prisma/seed/teams';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test('[TEAMS]: update the default document visibility in the team global settings', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: team.owner.email,
|
||||
password: 'password',
|
||||
redirectPath: `/t/${team.url}/settings/preferences`,
|
||||
});
|
||||
|
||||
// !: Brittle selector
|
||||
await page.getByRole('combobox').first().click();
|
||||
await page.getByRole('option', { name: 'Admin' }).click();
|
||||
await page.getByRole('button', { name: 'Update' }).first().click();
|
||||
|
||||
const toast = page.locator('li[role="status"][data-state="open"]').first();
|
||||
await expect(toast).toBeVisible();
|
||||
await expect(toast.getByText('Document preferences updated', { exact: true })).toBeVisible();
|
||||
});
|
||||
|
||||
test('[TEAMS]: update the sender details in the team global settings', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: team.owner.email,
|
||||
password: 'password',
|
||||
redirectPath: `/t/${team.url}/settings/preferences`,
|
||||
});
|
||||
|
||||
const checkbox = page.getByLabel('Send on Behalf of Team');
|
||||
await checkbox.check();
|
||||
|
||||
await expect(checkbox).toBeChecked();
|
||||
|
||||
await page.getByRole('button', { name: 'Update' }).first().click();
|
||||
|
||||
const toast = page.locator('li[role="status"][data-state="open"]').first();
|
||||
await expect(toast).toBeVisible();
|
||||
await expect(toast.getByText('Document preferences updated', { exact: true })).toBeVisible();
|
||||
|
||||
await expect(checkbox).toBeChecked();
|
||||
});
|
||||
@ -1,101 +0,0 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { seedTeam, seedTeamInvite } from '@documenso/prisma/seed/teams';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test('[TEAMS]: update team member role', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: team.owner.email,
|
||||
password: 'password',
|
||||
redirectPath: `/t/${team.url}/settings/members`,
|
||||
});
|
||||
|
||||
const teamMemberToUpdate = team.members[1];
|
||||
|
||||
await page
|
||||
.getByRole('row')
|
||||
.filter({ hasText: teamMemberToUpdate.user.email })
|
||||
.getByRole('button')
|
||||
.click();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Update role' }).click();
|
||||
await page.getByRole('combobox').click();
|
||||
await page.getByLabel('Manager').click();
|
||||
await page.getByRole('button', { name: 'Update' }).click();
|
||||
|
||||
await page.reload();
|
||||
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: teamMemberToUpdate.user.email }).first(),
|
||||
).toContainText('Manager');
|
||||
});
|
||||
|
||||
test('[TEAMS]: accept team invitation without account', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
|
||||
const teamInvite = await seedTeamInvite({
|
||||
email: `team-invite-test-${Date.now()}@test.documenso.com`,
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/team/invite/${teamInvite.token}`);
|
||||
await expect(page.getByRole('heading')).toContainText('Team invitation');
|
||||
});
|
||||
|
||||
test('[TEAMS]: accept team invitation with account', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const user = await seedUser();
|
||||
|
||||
const teamInvite = await seedTeamInvite({
|
||||
email: user.email,
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/team/invite/${teamInvite.token}`);
|
||||
await expect(page.getByRole('heading')).toContainText('Invitation accepted!');
|
||||
});
|
||||
|
||||
test('[TEAMS]: member can leave team', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const teamMember = team.members[1];
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamMember.user.email,
|
||||
password: 'password',
|
||||
redirectPath: `/settings/teams`,
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: 'Leave' }).click();
|
||||
await page.getByRole('button', { name: 'Leave' }).click();
|
||||
|
||||
await expect(page.getByRole('status').first()).toContainText(
|
||||
'You have successfully left this team.',
|
||||
);
|
||||
});
|
||||
|
||||
test('[TEAMS]: owner cannot leave team', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: team.owner.email,
|
||||
password: 'password',
|
||||
redirectPath: `/settings/teams`,
|
||||
});
|
||||
|
||||
await expect(page.getByRole('button').getByText('Leave')).toBeDisabled();
|
||||
});
|
||||
@ -3,27 +3,21 @@ import { expect, test } from '@playwright/test';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import {
|
||||
seedTeamDocumentWithMeta,
|
||||
seedTeamDocuments,
|
||||
seedTeamTemplateWithMeta,
|
||||
} from '@documenso/prisma/seed/documents';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test('[TEAMS]: check that default team signature settings are all enabled', async ({ page }) => {
|
||||
const { team } = await seedTeamDocuments();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: team.owner.email,
|
||||
password: 'password',
|
||||
email: user.email,
|
||||
redirectPath: `/t/${team.url}/settings/preferences`,
|
||||
});
|
||||
|
||||
// Verify that the default created team settings has all signatures enabled
|
||||
await expect(page.getByRole('combobox').filter({ hasText: 'Type' })).toBeVisible();
|
||||
await expect(page.getByRole('combobox').filter({ hasText: 'Upload' })).toBeVisible();
|
||||
await expect(page.getByRole('combobox').filter({ hasText: 'Draw' })).toBeVisible();
|
||||
|
||||
const document = await seedTeamDocumentWithMeta(team);
|
||||
|
||||
// Create a document and check the settings
|
||||
@ -46,12 +40,11 @@ test('[TEAMS]: check that default team signature settings are all enabled', asyn
|
||||
});
|
||||
|
||||
test('[TEAMS]: check signature modes can be disabled', async ({ page }) => {
|
||||
const { team } = await seedTeamDocuments();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: team.owner.email,
|
||||
password: 'password',
|
||||
email: user.email,
|
||||
redirectPath: `/t/${team.url}/settings/preferences`,
|
||||
});
|
||||
|
||||
@ -105,12 +98,11 @@ test('[TEAMS]: check signature modes can be disabled', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEAMS]: check signature modes work for templates', async ({ page }) => {
|
||||
const { team } = await seedTeamDocuments();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: team.owner.email,
|
||||
password: 'password',
|
||||
email: user.email,
|
||||
redirectPath: `/t/${team.url}/settings/preferences`,
|
||||
});
|
||||
|
||||
|
||||
@ -1,63 +0,0 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { seedTeam, seedTeamTransfer } from '@documenso/prisma/seed/teams';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test('[TEAMS]: initiate and cancel team transfer', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const teamMember = team.members[1];
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: team.owner.email,
|
||||
password: 'password',
|
||||
redirectPath: `/t/${team.url}/settings`,
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: 'Transfer team' }).click();
|
||||
|
||||
await page.getByRole('combobox').click();
|
||||
await page.getByLabel(teamMember.user.name ?? '').click();
|
||||
await page.getByLabel('Confirm by typing transfer').click();
|
||||
await page.getByLabel('Confirm by typing transfer').fill('transfer');
|
||||
await page.getByRole('button', { name: 'Transfer' }).click();
|
||||
|
||||
await expect(page.locator('[id*="form-item-message"]').first()).toContainText(
|
||||
`You must enter 'transfer ${team.name}' to proceed`,
|
||||
);
|
||||
|
||||
await page.getByLabel('Confirm by typing transfer').click();
|
||||
await page.getByLabel('Confirm by typing transfer').fill(`transfer ${team.name}`);
|
||||
await page.getByRole('button', { name: 'Transfer' }).click();
|
||||
|
||||
await expect(page.getByRole('heading', { name: 'Team transfer in progress' })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Cancel' }).click();
|
||||
|
||||
await expect(page.getByRole('status').first()).toContainText(
|
||||
'The team transfer invitation has been successfully deleted.',
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Current skipped until we disable billing during tests.
|
||||
*/
|
||||
test.skip('[TEAMS]: accept team transfer', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const newOwnerMember = team.members[1];
|
||||
|
||||
const teamTransferRequest = await seedTeamTransfer({
|
||||
teamId: team.id,
|
||||
newOwnerUserId: newOwnerMember.userId,
|
||||
});
|
||||
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/team/verify/transfer/${teamTransferRequest.token}`);
|
||||
await expect(page.getByRole('heading')).toContainText('Team ownership transferred!');
|
||||
});
|
||||
@ -1,138 +1,20 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { TeamMemberRole } from '@prisma/client';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { seedUserSubscription } from '@documenso/prisma/seed/subscriptions';
|
||||
import { seedTeam } from '@documenso/prisma/seed/teams';
|
||||
import { seedTeam, seedTeamMember } from '@documenso/prisma/seed/teams';
|
||||
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test.describe('[EE_ONLY]', () => {
|
||||
const enterprisePriceId = '';
|
||||
|
||||
test.beforeEach(() => {
|
||||
test.skip(
|
||||
process.env.NEXT_PUBLIC_FEATURE_BILLING_ENABLED !== 'true' || !enterprisePriceId,
|
||||
'Billing required for this test',
|
||||
);
|
||||
});
|
||||
|
||||
test('[TEMPLATE_FLOW] add action auth settings', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
|
||||
await seedUserSubscription({
|
||||
userId: user.id,
|
||||
priceId: enterprisePriceId,
|
||||
});
|
||||
|
||||
const template = await seedBlankTemplate(user);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/templates/${template.id}/edit`,
|
||||
});
|
||||
|
||||
// Set EE action auth.
|
||||
await page.getByTestId('documentActionSelectValue').click();
|
||||
await page.getByRole('option').filter({ hasText: 'Require passkey' }).click();
|
||||
await expect(page.getByTestId('documentActionSelectValue')).toContainText('Require passkey');
|
||||
|
||||
// Save the settings by going to the next step.
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Placeholders' })).toBeVisible();
|
||||
|
||||
// Return to the settings step to check that the results are saved correctly.
|
||||
await page.getByRole('button', { name: 'Go Back' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
|
||||
|
||||
await expect(page.getByTestId('documentActionSelectValue')).toContainText('Require passkey');
|
||||
});
|
||||
|
||||
test('[TEMPLATE_FLOW] enterprise team member can add action auth settings', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const owner = team.owner;
|
||||
const teamMemberUser = team.members[1].user;
|
||||
|
||||
// Make the team enterprise by giving the owner the enterprise subscription.
|
||||
await seedUserSubscription({
|
||||
userId: team.ownerUserId,
|
||||
priceId: enterprisePriceId,
|
||||
});
|
||||
|
||||
const template = await seedBlankTemplate(owner, {
|
||||
createTemplateOptions: {
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamMemberUser.email,
|
||||
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
|
||||
});
|
||||
|
||||
// Set EE action auth.
|
||||
await page.getByTestId('documentActionSelectValue').click();
|
||||
await page.getByRole('option').filter({ hasText: 'Require passkey' }).click();
|
||||
await expect(page.getByTestId('documentActionSelectValue')).toContainText('Require passkey');
|
||||
|
||||
// Save the settings by going to the next step.
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Placeholders' })).toBeVisible();
|
||||
|
||||
// Advanced settings should be visible.
|
||||
await expect(page.getByLabel('Show advanced settings')).toBeVisible();
|
||||
});
|
||||
|
||||
test('[TEMPLATE_FLOW] enterprise team member should not have access to enterprise on personal account', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const teamMemberUser = team.members[1].user;
|
||||
|
||||
// Make the team enterprise by giving the owner the enterprise subscription.
|
||||
await seedUserSubscription({
|
||||
userId: team.ownerUserId,
|
||||
priceId: enterprisePriceId,
|
||||
});
|
||||
|
||||
const template = await seedBlankTemplate(teamMemberUser);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamMemberUser.email,
|
||||
redirectPath: `/templates/${template.id}/edit`,
|
||||
});
|
||||
|
||||
// Global action auth should not be visible.
|
||||
await expect(page.getByTestId('documentActionSelectValue')).not.toBeVisible();
|
||||
|
||||
// Next step.
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Placeholders' })).toBeVisible();
|
||||
|
||||
// Advanced settings should not be visible.
|
||||
await expect(page.getByLabel('Show advanced settings')).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('[TEMPLATE_FLOW]: add settings', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const template = await seedBlankTemplate(user);
|
||||
const { user, team } = await seedUser();
|
||||
const template = await seedBlankTemplate(user, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/templates/${template.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
|
||||
});
|
||||
|
||||
// Set title.
|
||||
@ -159,19 +41,13 @@ test('[TEMPLATE_FLOW]: add settings', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEMPLATE_FLOW] add document visibility settings', async ({ page }) => {
|
||||
const { owner, ...team } = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const template = await seedBlankTemplate(owner, {
|
||||
createTemplateOptions: {
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
const template = await seedBlankTemplate(user, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: owner.email,
|
||||
email: user.email,
|
||||
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
|
||||
});
|
||||
|
||||
@ -196,24 +72,21 @@ test('[TEMPLATE_FLOW] add document visibility settings', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEMPLATE_FLOW] team member visibility permissions', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
const { team, owner, organisation } = await seedTeam({
|
||||
createTeamMembers: 2, // Create an additional member to test different roles
|
||||
});
|
||||
|
||||
await prisma.teamMember.update({
|
||||
where: {
|
||||
id: team.members[1].id,
|
||||
},
|
||||
data: {
|
||||
role: TeamMemberRole.MANAGER,
|
||||
},
|
||||
const memberUser = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
const owner = team.owner;
|
||||
const managerUser = team.members[1].user;
|
||||
const memberUser = team.members[2].user;
|
||||
const managerUser = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MANAGER,
|
||||
});
|
||||
|
||||
const template = await seedBlankTemplate(owner, {
|
||||
const template = await seedBlankTemplate(owner, team.id, {
|
||||
createTemplateOptions: {
|
||||
teamId: team.id,
|
||||
},
|
||||
@ -249,7 +122,7 @@ test('[TEMPLATE_FLOW] team member visibility permissions', async ({ page }) => {
|
||||
await expect(page.getByTestId('documentVisibilitySelectValue')).toBeDisabled();
|
||||
|
||||
// Create a new template with 'everyone' visibility
|
||||
const everyoneTemplate = await seedBlankTemplate(owner, {
|
||||
const everyoneTemplate = await seedBlankTemplate(owner, team.id, {
|
||||
createTemplateOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'EVERYONE',
|
||||
|
||||
@ -1,86 +1,85 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { seedUserSubscription } from '@documenso/prisma/seed/subscriptions';
|
||||
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test.describe('[EE_ONLY]', () => {
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
const enterprisePriceId = process.env.NEXT_PUBLIC_STRIPE_ENTERPRISE_PLAN_MONTHLY_PRICE_ID || '';
|
||||
// test.describe('[EE_ONLY]', () => {
|
||||
// // eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
// const enterprisePriceId = process.env.NEXT_PUBLIC_STRIPE_ENTERPRISE_PLAN_MONTHLY_PRICE_ID || '';
|
||||
|
||||
test.beforeEach(() => {
|
||||
test.skip(
|
||||
process.env.NEXT_PUBLIC_FEATURE_BILLING_ENABLED !== 'true' || !enterprisePriceId,
|
||||
'Billing required for this test',
|
||||
);
|
||||
});
|
||||
// test.beforeEach(() => {
|
||||
// test.skip(
|
||||
// process.env.NEXT_PUBLIC_FEATURE_BILLING_ENABLED !== 'true' || !enterprisePriceId,
|
||||
// 'Billing required for this test',
|
||||
// );
|
||||
// });
|
||||
|
||||
test('[TEMPLATE_FLOW] add EE settings', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
// test('[TEMPLATE_FLOW] add EE settings', async ({ page }) => {
|
||||
// const user = await seedUser();
|
||||
|
||||
await seedUserSubscription({
|
||||
userId: user.id,
|
||||
priceId: enterprisePriceId,
|
||||
});
|
||||
// await seedUserSubscription({
|
||||
// userId: user.id,
|
||||
// priceId: enterprisePriceId,
|
||||
// });
|
||||
|
||||
const template = await seedBlankTemplate(user);
|
||||
// const template = await seedBlankTemplate(user);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/templates/${template.id}/edit`,
|
||||
});
|
||||
// await apiSignin({
|
||||
// page,
|
||||
// email: user.email,
|
||||
// redirectPath: `/templates/${template.id}/edit`,
|
||||
// });
|
||||
|
||||
// Save the settings by going to the next step.
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
|
||||
// // Save the settings by going to the next step.
|
||||
// await page.getByRole('button', { name: 'Continue' }).click();
|
||||
// await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
|
||||
|
||||
// Add 2 signers.
|
||||
await page.getByPlaceholder('Email').fill('recipient1@documenso.com');
|
||||
await page.getByPlaceholder('Name').fill('Recipient 1');
|
||||
await page.getByRole('button', { name: 'Add Placeholder Recipient' }).click();
|
||||
await page.getByPlaceholder('Email').nth(1).fill('recipient2@documenso.com');
|
||||
await page.getByPlaceholder('Name').nth(1).fill('Recipient 2');
|
||||
// // Add 2 signers.
|
||||
// await page.getByPlaceholder('Email').fill('recipient1@documenso.com');
|
||||
// await page.getByPlaceholder('Name').fill('Recipient 1');
|
||||
// await page.getByRole('button', { name: 'Add Placeholder Recipient' }).click();
|
||||
// await page.getByPlaceholder('Email').nth(1).fill('recipient2@documenso.com');
|
||||
// await page.getByPlaceholder('Name').nth(1).fill('Recipient 2');
|
||||
|
||||
// Display advanced settings.
|
||||
await page.getByLabel('Show advanced settings').check();
|
||||
// // Display advanced settings.
|
||||
// await page.getByLabel('Show advanced settings').check();
|
||||
|
||||
// Navigate to the next step and back.
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Go Back' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
|
||||
// // Navigate to the next step and back.
|
||||
// await page.getByRole('button', { name: 'Continue' }).click();
|
||||
// await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
|
||||
// await page.getByRole('button', { name: 'Go Back' }).click();
|
||||
// await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
|
||||
|
||||
// Expect that the advanced settings is unchecked, since no advanced settings were applied.
|
||||
await expect(page.getByLabel('Show advanced settings')).toBeChecked({ checked: false });
|
||||
// // Expect that the advanced settings is unchecked, since no advanced settings were applied.
|
||||
// await expect(page.getByLabel('Show advanced settings')).toBeChecked({ checked: false });
|
||||
|
||||
// Add advanced settings for a single recipient.
|
||||
await page.getByLabel('Show advanced settings').check();
|
||||
await page.getByTestId('documentActionSelectValue').click();
|
||||
await page.getByRole('option').filter({ hasText: 'Require passkey' }).click();
|
||||
// // Add advanced settings for a single recipient.
|
||||
// await page.getByLabel('Show advanced settings').check();
|
||||
// await page.getByRole('combobox').first().click();
|
||||
// await page.getByLabel('Require passkey').click();
|
||||
|
||||
// Navigate to the next step and back.
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Go Back' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
|
||||
// // Navigate to the next step and back.
|
||||
// await page.getByRole('button', { name: 'Continue' }).click();
|
||||
// await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
|
||||
// await page.getByRole('button', { name: 'Go Back' }).click();
|
||||
// await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
|
||||
|
||||
// Expect that the advanced settings is visible, and the checkbox is hidden. Since advanced
|
||||
// settings were applied.
|
||||
await expect(page.getByLabel('Show advanced settings')).toBeHidden();
|
||||
});
|
||||
});
|
||||
// // Expect that the advanced settings is visible, and the checkbox is hidden. Since advanced
|
||||
// // settings were applied.
|
||||
// await expect(page.getByLabel('Show advanced settings')).toBeHidden();
|
||||
// });
|
||||
// });
|
||||
|
||||
test('[TEMPLATE_FLOW]: add placeholder', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const template = await seedBlankTemplate(user);
|
||||
const { user, team } = await seedUser();
|
||||
const template = await seedBlankTemplate(user, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/templates/${template.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
|
||||
});
|
||||
|
||||
// Save the settings by going to the next step.
|
||||
|
||||
@ -5,15 +5,12 @@ import path from 'path';
|
||||
|
||||
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { seedUserSubscription } from '@documenso/prisma/seed/subscriptions';
|
||||
import { seedTeam } from '@documenso/prisma/seed/teams';
|
||||
import { seedTeam, seedTeamMember } from '@documenso/prisma/seed/teams';
|
||||
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
const enterprisePriceId = '';
|
||||
|
||||
const EXAMPLE_PDF_PATH = path.join(__dirname, '../../../../assets/example.pdf');
|
||||
|
||||
/**
|
||||
@ -26,21 +23,13 @@ const EXAMPLE_PDF_PATH = path.join(__dirname, '../../../../assets/example.pdf');
|
||||
* If you update this test please update that test as well.
|
||||
*/
|
||||
test('[TEMPLATE]: should create a document from a template', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const template = await seedBlankTemplate(user);
|
||||
|
||||
const isBillingEnabled =
|
||||
process.env.NEXT_PUBLIC_FEATURE_BILLING_ENABLED === 'true' && enterprisePriceId;
|
||||
|
||||
await seedUserSubscription({
|
||||
userId: user.id,
|
||||
priceId: enterprisePriceId,
|
||||
});
|
||||
const { user, team } = await seedUser();
|
||||
const template = await seedBlankTemplate(user, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/templates/${template.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
|
||||
});
|
||||
|
||||
// Set template title.
|
||||
@ -51,13 +40,6 @@ test('[TEMPLATE]: should create a document from a template', async ({ page }) =>
|
||||
await page.getByRole('option').filter({ hasText: 'Require account' }).click();
|
||||
await expect(page.getByTestId('documentAccessSelectValue')).toContainText('Require account');
|
||||
|
||||
// Set EE action auth.
|
||||
if (isBillingEnabled) {
|
||||
await page.getByTestId('documentActionSelectValue').click();
|
||||
await page.getByRole('option').filter({ hasText: 'Require passkey' }).click();
|
||||
await expect(page.getByTestId('documentActionSelectValue')).toContainText('Require passkey');
|
||||
}
|
||||
|
||||
// Set email options.
|
||||
await page.getByRole('button', { name: 'Email Options' }).click();
|
||||
await page.getByLabel('Subject (Optional)').fill('SUBJECT');
|
||||
@ -82,25 +64,18 @@ test('[TEMPLATE]: should create a document from a template', async ({ page }) =>
|
||||
await page.getByPlaceholder('Email').nth(1).fill('recipient2@documenso.com');
|
||||
await page.getByPlaceholder('Name').nth(1).fill('Recipient 2');
|
||||
|
||||
// Apply require passkey for Recipient 1.
|
||||
if (isBillingEnabled) {
|
||||
await page.getByLabel('Show advanced settings').check();
|
||||
await page.getByTestId('documentActionSelectValue').click();
|
||||
await page.getByRole('option').filter({ hasText: 'Require passkey' }).click();
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Save template' }).click();
|
||||
|
||||
// Use template
|
||||
await page.waitForURL('/templates');
|
||||
await page.waitForURL(`/t/${team.url}/templates`);
|
||||
await page.getByRole('button', { name: 'Use Template' }).click();
|
||||
await page.getByRole('button', { name: 'Create as draft' }).click();
|
||||
|
||||
// Review that the document was created with the correct values.
|
||||
await page.waitForURL(/documents/);
|
||||
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
||||
|
||||
const documentId = Number(page.url().split('/').pop());
|
||||
|
||||
@ -121,10 +96,6 @@ test('[TEMPLATE]: should create a document from a template', async ({ page }) =>
|
||||
expect(document.title).toEqual('TEMPLATE_TITLE');
|
||||
expect(documentAuth.documentAuthOption.globalAccessAuth).toContain('ACCOUNT');
|
||||
|
||||
if (isBillingEnabled) {
|
||||
expect(documentAuth.documentAuthOption.globalActionAuth).toContain('PASSKEY');
|
||||
}
|
||||
|
||||
expect(document.documentMeta?.dateFormat).toEqual('dd/MM/yyyy hh:mm a');
|
||||
expect(document.documentMeta?.message).toEqual('MESSAGE');
|
||||
expect(document.documentMeta?.redirectUrl).toEqual('https://documenso.com');
|
||||
@ -144,10 +115,6 @@ test('[TEMPLATE]: should create a document from a template', async ({ page }) =>
|
||||
recipientAuth: recipientTwo.authOptions,
|
||||
});
|
||||
|
||||
if (isBillingEnabled) {
|
||||
expect(recipientOneAuth.derivedRecipientActionAuth).toContain('PASSKEY');
|
||||
}
|
||||
|
||||
expect(recipientOneAuth.derivedRecipientAccessAuth).toContain('ACCOUNT');
|
||||
expect(recipientTwoAuth.derivedRecipientAccessAuth).toContain('ACCOUNT');
|
||||
});
|
||||
@ -156,23 +123,11 @@ test('[TEMPLATE]: should create a document from a template', async ({ page }) =>
|
||||
* This is a direct copy paste of the above test but for teams.
|
||||
*/
|
||||
test('[TEMPLATE]: should create a team document from a team template', async ({ page }) => {
|
||||
const { owner, ...team } = await seedTeam({
|
||||
const { team, owner, organisation } = await seedTeam({
|
||||
createTeamMembers: 2,
|
||||
});
|
||||
|
||||
const template = await seedBlankTemplate(owner, {
|
||||
createTemplateOptions: {
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
|
||||
const isBillingEnabled =
|
||||
process.env.NEXT_PUBLIC_FEATURE_BILLING_ENABLED === 'true' && enterprisePriceId;
|
||||
|
||||
await seedUserSubscription({
|
||||
userId: owner.id,
|
||||
priceId: enterprisePriceId,
|
||||
});
|
||||
const template = await seedBlankTemplate(owner, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
@ -188,13 +143,6 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
|
||||
await page.getByRole('option').filter({ hasText: 'Require account' }).click();
|
||||
await expect(page.getByTestId('documentAccessSelectValue')).toContainText('Require account');
|
||||
|
||||
// Set EE action auth.
|
||||
if (isBillingEnabled) {
|
||||
await page.getByTestId('documentActionSelectValue').click();
|
||||
await page.getByRole('option').filter({ hasText: 'Require passkey' }).click();
|
||||
await expect(page.getByTestId('documentActionSelectValue')).toContainText('Require passkey');
|
||||
}
|
||||
|
||||
// Set email options.
|
||||
await page.getByRole('button', { name: 'Email Options' }).click();
|
||||
await page.getByLabel('Subject (Optional)').fill('SUBJECT');
|
||||
@ -219,13 +167,6 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
|
||||
await page.getByPlaceholder('Email').nth(1).fill('recipient2@documenso.com');
|
||||
await page.getByPlaceholder('Name').nth(1).fill('Recipient 2');
|
||||
|
||||
// Apply require passkey for Recipient 1.
|
||||
if (isBillingEnabled) {
|
||||
await page.getByLabel('Show advanced settings').check();
|
||||
await page.getByTestId('documentActionSelectValue').click();
|
||||
await page.getByRole('option').filter({ hasText: 'Require passkey' }).click();
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
|
||||
|
||||
@ -237,7 +178,7 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
|
||||
await page.getByRole('button', { name: 'Create as draft' }).click();
|
||||
|
||||
// Review that the document was created with the correct values.
|
||||
await page.waitForURL(/documents/);
|
||||
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
||||
|
||||
const documentId = Number(page.url().split('/').pop());
|
||||
|
||||
@ -259,11 +200,6 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
|
||||
|
||||
expect(document.title).toEqual('TEMPLATE_TITLE');
|
||||
expect(documentAuth.documentAuthOption.globalAccessAuth).toContain('ACCOUNT');
|
||||
|
||||
if (isBillingEnabled) {
|
||||
expect(documentAuth.documentAuthOption.globalActionAuth).toContain('PASSKEY');
|
||||
}
|
||||
|
||||
expect(document.documentMeta?.dateFormat).toEqual('dd/MM/yyyy hh:mm a');
|
||||
expect(document.documentMeta?.message).toEqual('MESSAGE');
|
||||
expect(document.documentMeta?.redirectUrl).toEqual('https://documenso.com');
|
||||
@ -283,10 +219,6 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
|
||||
recipientAuth: recipientTwo.authOptions,
|
||||
});
|
||||
|
||||
if (isBillingEnabled) {
|
||||
expect(recipientOneAuth.derivedRecipientActionAuth).toContain('PASSKEY');
|
||||
}
|
||||
|
||||
expect(recipientOneAuth.derivedRecipientAccessAuth).toContain('ACCOUNT');
|
||||
expect(recipientTwoAuth.derivedRecipientAccessAuth).toContain('ACCOUNT');
|
||||
});
|
||||
@ -298,8 +230,8 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
|
||||
test('[TEMPLATE]: should create a document from a template with custom document', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const template = await seedBlankTemplate(user);
|
||||
const { user, team } = await seedUser();
|
||||
const template = await seedBlankTemplate(user, team.id);
|
||||
|
||||
// Create a temporary PDF file for upload
|
||||
|
||||
@ -308,7 +240,7 @@ test('[TEMPLATE]: should create a document from a template with custom document'
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/templates/${template.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
|
||||
});
|
||||
|
||||
// Set template title
|
||||
@ -327,7 +259,7 @@ test('[TEMPLATE]: should create a document from a template with custom document'
|
||||
await page.getByRole('button', { name: 'Save template' }).click();
|
||||
|
||||
// Use template with custom document
|
||||
await page.waitForURL('/templates');
|
||||
await page.waitForURL(`/t/${team.url}/templates`);
|
||||
await page.getByRole('button', { name: 'Use Template' }).click();
|
||||
|
||||
// Enable custom document upload and upload file
|
||||
@ -352,7 +284,7 @@ test('[TEMPLATE]: should create a document from a template with custom document'
|
||||
await page.getByRole('button', { name: 'Create as draft' }).click();
|
||||
|
||||
// Review that the document was created with the custom document data
|
||||
await page.waitForURL(/documents/);
|
||||
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
||||
|
||||
const documentId = Number(page.url().split('/').pop());
|
||||
|
||||
@ -378,15 +310,11 @@ test('[TEMPLATE]: should create a document from a template with custom document'
|
||||
test('[TEMPLATE]: should create a team document from a template with custom document', async ({
|
||||
page,
|
||||
}) => {
|
||||
const { owner, ...team } = await seedTeam({
|
||||
const { team, owner, organisation } = await seedTeam({
|
||||
createTeamMembers: 2,
|
||||
});
|
||||
|
||||
const template = await seedBlankTemplate(owner, {
|
||||
createTemplateOptions: {
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
const template = await seedBlankTemplate(owner, team.id);
|
||||
|
||||
const pdfContent = fs.readFileSync(EXAMPLE_PDF_PATH).toString('base64');
|
||||
|
||||
@ -437,7 +365,7 @@ test('[TEMPLATE]: should create a team document from a template with custom docu
|
||||
await page.getByRole('button', { name: 'Create as draft' }).click();
|
||||
|
||||
// Review that the document was created with the custom document data
|
||||
await page.waitForURL(/documents/);
|
||||
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
||||
|
||||
const documentId = Number(page.url().split('/').pop());
|
||||
|
||||
@ -464,13 +392,13 @@ test('[TEMPLATE]: should create a team document from a template with custom docu
|
||||
test('[TEMPLATE]: should create a document from a template using template document when custom document is not enabled', async ({
|
||||
page,
|
||||
}) => {
|
||||
const user = await seedUser();
|
||||
const template = await seedBlankTemplate(user);
|
||||
const { user, team } = await seedUser();
|
||||
const template = await seedBlankTemplate(user, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/templates/${template.id}/edit`,
|
||||
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
|
||||
});
|
||||
|
||||
// Set template title
|
||||
@ -489,7 +417,7 @@ test('[TEMPLATE]: should create a document from a template using template docume
|
||||
await page.getByRole('button', { name: 'Save template' }).click();
|
||||
|
||||
// Use template without custom document
|
||||
await page.waitForURL('/templates');
|
||||
await page.waitForURL(`/t/${team.url}/templates`);
|
||||
await page.getByRole('button', { name: 'Use Template' }).click();
|
||||
|
||||
// Verify custom document upload is not checked by default
|
||||
@ -499,7 +427,7 @@ test('[TEMPLATE]: should create a document from a template using template docume
|
||||
await page.getByRole('button', { name: 'Create as draft' }).click();
|
||||
|
||||
// Review that the document was created with the template's document data
|
||||
await page.waitForURL(/documents/);
|
||||
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
||||
|
||||
const documentId = Number(page.url().split('/').pop());
|
||||
|
||||
@ -532,15 +460,11 @@ test('[TEMPLATE]: should create a document from a template using template docume
|
||||
test('[TEMPLATE]: should persist document visibility when creating from template', async ({
|
||||
page,
|
||||
}) => {
|
||||
const { owner, ...team } = await seedTeam({
|
||||
const { team, owner, organisation } = await seedTeam({
|
||||
createTeamMembers: 2,
|
||||
});
|
||||
|
||||
const template = await seedBlankTemplate(owner, {
|
||||
createTemplateOptions: {
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
const template = await seedBlankTemplate(owner, team.id);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
@ -569,17 +493,11 @@ test('[TEMPLATE]: should persist document visibility when creating from template
|
||||
await page.getByRole('button', { name: 'Save template' }).click();
|
||||
|
||||
// Test creating document as team manager
|
||||
await prisma.teamMember.update({
|
||||
where: {
|
||||
id: team.members[1].id,
|
||||
},
|
||||
data: {
|
||||
role: TeamMemberRole.MANAGER,
|
||||
},
|
||||
const managerUser = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MANAGER,
|
||||
});
|
||||
|
||||
const managerUser = team.members[1].user;
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: managerUser.email,
|
||||
@ -590,7 +508,7 @@ test('[TEMPLATE]: should persist document visibility when creating from template
|
||||
await page.getByRole('button', { name: 'Create as draft' }).click();
|
||||
|
||||
// Review that the document was created with the correct visibility
|
||||
await page.waitForURL(/documents/);
|
||||
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
||||
|
||||
const documentId = Number(page.url().split('/').pop());
|
||||
|
||||
@ -605,7 +523,11 @@ test('[TEMPLATE]: should persist document visibility when creating from template
|
||||
expect(document.teamId).toEqual(team.id);
|
||||
|
||||
// Test that regular member cannot create document from restricted template
|
||||
const memberUser = team.members[2].user;
|
||||
const memberUser = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: memberUser.email,
|
||||
|
||||
@ -2,10 +2,6 @@ import { expect, test } from '@playwright/test';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import {
|
||||
DIRECT_TEMPLATE_RECIPIENT_EMAIL,
|
||||
DIRECT_TEMPLATE_RECIPIENT_NAME,
|
||||
} from '@documenso/lib/constants/direct-templates';
|
||||
import { createDocumentAuthOptions } from '@documenso/lib/utils/document-auth';
|
||||
import { formatDirectTemplatePath } from '@documenso/lib/utils/templates';
|
||||
import { seedTeam } from '@documenso/prisma/seed/teams';
|
||||
@ -13,29 +9,18 @@ import { seedDirectTemplate, seedTemplate } from '@documenso/prisma/seed/templat
|
||||
import { seedTestEmail, seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
import { checkDocumentTabCount } from '../fixtures/documents';
|
||||
|
||||
// Duped from `packages/lib/utils/teams.ts` due to errors when importing that file.
|
||||
const formatDocumentsPath = (teamUrl?: string) =>
|
||||
teamUrl ? `/t/${teamUrl}/documents` : '/documents';
|
||||
const formatTemplatesPath = (teamUrl?: string) =>
|
||||
teamUrl ? `/t/${teamUrl}/templates` : '/templates';
|
||||
const formatDocumentsPath = (teamUrl: string) => `/t/${teamUrl}/documents`;
|
||||
const formatTemplatesPath = (teamUrl: string) => `/t/${teamUrl}/templates`;
|
||||
|
||||
const nanoid = customAlphabet('1234567890abcdef', 10);
|
||||
|
||||
test('[DIRECT_TEMPLATES]: create direct link for template', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
const { team, owner, organisation } = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const owner = team.owner;
|
||||
|
||||
// Should only be visible to the owner in personal templates.
|
||||
const personalTemplate = await seedTemplate({
|
||||
title: 'Personal template',
|
||||
userId: owner.id,
|
||||
});
|
||||
|
||||
// Should be visible to team members.
|
||||
const teamTemplate = await seedTemplate({
|
||||
title: 'Team template 1',
|
||||
@ -46,49 +31,35 @@ test('[DIRECT_TEMPLATES]: create direct link for template', async ({ page }) =>
|
||||
await apiSignin({
|
||||
page,
|
||||
email: owner.email,
|
||||
redirectPath: '/templates',
|
||||
redirectPath: `/t/${team.url}/templates`,
|
||||
});
|
||||
|
||||
const urls = [
|
||||
`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates/${teamTemplate.id}`,
|
||||
`${NEXT_PUBLIC_WEBAPP_URL()}/templates/${personalTemplate.id}`,
|
||||
];
|
||||
const url = `${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates/${teamTemplate.id}`;
|
||||
|
||||
// Run test for personal and team templates.
|
||||
for (const url of urls) {
|
||||
// Owner should see list of templates with no direct link badge.
|
||||
await page.goto(url);
|
||||
await expect(page.getByRole('button', { name: 'direct link' })).toHaveCount(1);
|
||||
// Owner should see list of templates with no direct link badge.
|
||||
await page.goto(url);
|
||||
await expect(page.getByRole('button', { name: 'direct link' })).toHaveCount(1);
|
||||
|
||||
// Create direct link.
|
||||
await page.getByRole('button', { name: 'Create Direct Link' }).click();
|
||||
await page.getByRole('button', { name: 'Enable direct link signing' }).click();
|
||||
await page.getByRole('button', { name: 'Create one automatically' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Direct Link Signing' })).toBeVisible();
|
||||
// Create direct link.
|
||||
await page.getByRole('button', { name: 'Create Direct Link' }).click();
|
||||
await page.getByRole('button', { name: 'Enable direct link signing' }).click();
|
||||
await page.getByRole('button', { name: 'Create one automatically' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Direct Link Signing' })).toBeVisible();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
await page.getByTestId('btn-dialog-close').click();
|
||||
await page.waitForTimeout(1000);
|
||||
await page.getByTestId('btn-dialog-close').click();
|
||||
|
||||
// Expect badge to appear.
|
||||
await expect(page.getByRole('button', { name: 'direct link' })).toHaveCount(2);
|
||||
}
|
||||
// Expect badge to appear.
|
||||
await expect(page.getByRole('button', { name: 'direct link' })).toHaveCount(2);
|
||||
});
|
||||
|
||||
test('[DIRECT_TEMPLATES]: toggle direct template link', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
const { team, owner, organisation } = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const owner = team.owner;
|
||||
|
||||
// Should only be visible to the owner in personal templates.
|
||||
const personalDirectTemplate = await seedDirectTemplate({
|
||||
title: 'Personal direct template link',
|
||||
userId: owner.id,
|
||||
});
|
||||
|
||||
// Should be visible to team members.
|
||||
const teamDirectTemplate = await seedDirectTemplate({
|
||||
const template = await seedDirectTemplate({
|
||||
title: 'Team direct template link 1',
|
||||
userId: owner.id,
|
||||
teamId: team.id,
|
||||
@ -99,41 +70,30 @@ test('[DIRECT_TEMPLATES]: toggle direct template link', async ({ page }) => {
|
||||
email: owner.email,
|
||||
});
|
||||
|
||||
// Run test for personal and team templates.
|
||||
for (const template of [personalDirectTemplate, teamDirectTemplate]) {
|
||||
// Check that the direct template link is accessible.
|
||||
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
|
||||
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
|
||||
// Check that the direct template link is accessible.
|
||||
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
|
||||
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
|
||||
|
||||
// Navigate to template settings and disable access.
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}${formatTemplatesPath(template.team?.url)}`);
|
||||
await page.getByRole('cell', { name: 'Use Template' }).getByRole('button').nth(1).click();
|
||||
await page.getByRole('menuitem', { name: 'Direct link' }).click();
|
||||
await page.getByRole('switch').click();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await expect(page.getByText('Direct link signing has been').first()).toBeVisible();
|
||||
// Navigate to template settings and disable access.
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}${formatTemplatesPath(template.team?.url)}`);
|
||||
await page.getByRole('cell', { name: 'Use Template' }).getByRole('button').nth(1).click();
|
||||
await page.getByRole('menuitem', { name: 'Direct link' }).click();
|
||||
await page.getByRole('switch').click();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await expect(page.getByText('Direct link signing has been').first()).toBeVisible();
|
||||
|
||||
// Check that the direct template link is no longer accessible.
|
||||
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
|
||||
await expect(page.getByText('404 not found')).toBeVisible();
|
||||
}
|
||||
// Check that the direct template link is no longer accessible.
|
||||
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
|
||||
await expect(page.getByText('404 not found')).toBeVisible();
|
||||
});
|
||||
|
||||
test('[DIRECT_TEMPLATES]: delete direct template link', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
const { team, owner, organisation } = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const owner = team.owner;
|
||||
|
||||
// Should only be visible to the owner in personal templates.
|
||||
const personalDirectTemplate = await seedDirectTemplate({
|
||||
title: 'Personal direct template link',
|
||||
userId: owner.id,
|
||||
});
|
||||
|
||||
// Should be visible to team members.
|
||||
const teamDirectTemplate = await seedDirectTemplate({
|
||||
const template = await seedDirectTemplate({
|
||||
title: 'Team direct template link 1',
|
||||
userId: owner.id,
|
||||
teamId: team.id,
|
||||
@ -144,32 +104,30 @@ test('[DIRECT_TEMPLATES]: delete direct template link', async ({ page }) => {
|
||||
email: owner.email,
|
||||
});
|
||||
|
||||
// Run test for personal and team templates.
|
||||
for (const template of [personalDirectTemplate, teamDirectTemplate]) {
|
||||
// Check that the direct template link is accessible.
|
||||
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
|
||||
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
|
||||
// Check that the direct template link is accessible.
|
||||
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
|
||||
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
|
||||
|
||||
// Navigate to template settings and delete the access.
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}${formatTemplatesPath(template.team?.url)}`);
|
||||
await page.getByRole('cell', { name: 'Use Template' }).getByRole('button').nth(1).click();
|
||||
await page.getByRole('menuitem', { name: 'Direct link' }).click();
|
||||
await page.getByRole('button', { name: 'Remove' }).click();
|
||||
await page.getByRole('button', { name: 'Confirm' }).click();
|
||||
await expect(page.getByText('Direct template link deleted').first()).toBeVisible();
|
||||
// Navigate to template settings and delete the access.
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}${formatTemplatesPath(template.team?.url)}`);
|
||||
await page.getByRole('cell', { name: 'Use Template' }).getByRole('button').nth(1).click();
|
||||
await page.getByRole('menuitem', { name: 'Direct link' }).click();
|
||||
await page.getByRole('button', { name: 'Remove' }).click();
|
||||
await page.getByRole('button', { name: 'Confirm' }).click();
|
||||
await expect(page.getByText('Direct template link deleted').first()).toBeVisible();
|
||||
|
||||
// Check that the direct template link is no longer accessible.
|
||||
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
|
||||
await expect(page.getByText('404 not found')).toBeVisible();
|
||||
}
|
||||
// Check that the direct template link is no longer accessible.
|
||||
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
|
||||
await expect(page.getByText('404 not found')).toBeVisible();
|
||||
});
|
||||
|
||||
test('[DIRECT_TEMPLATES]: direct template link auth access', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const directTemplateWithAuth = await seedDirectTemplate({
|
||||
title: 'Personal direct template link',
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
createTemplateOptions: {
|
||||
authOptions: createDocumentAuthOptions({
|
||||
globalAccessAuth: ['ACCOUNT'],
|
||||
@ -198,136 +156,26 @@ test('[DIRECT_TEMPLATES]: direct template link auth access', async ({ page }) =>
|
||||
});
|
||||
|
||||
test('[DIRECT_TEMPLATES]: use direct template link with 1 recipient', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
const { team, owner, organisation } = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const owner = team.owner;
|
||||
|
||||
// Should only be visible to the owner in personal templates.
|
||||
const personalDirectTemplate = await seedDirectTemplate({
|
||||
title: 'Personal direct template link',
|
||||
userId: owner.id,
|
||||
});
|
||||
|
||||
// Should be visible to team members.
|
||||
const teamDirectTemplate = await seedDirectTemplate({
|
||||
const template = await seedDirectTemplate({
|
||||
title: 'Team direct template link 1',
|
||||
userId: owner.id,
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
// Run test for personal and team templates.
|
||||
for (const template of [personalDirectTemplate, teamDirectTemplate]) {
|
||||
// Check that the direct template link is accessible.
|
||||
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
|
||||
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
|
||||
// Check that the direct template link is accessible.
|
||||
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
|
||||
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
|
||||
|
||||
await page.getByPlaceholder('recipient@documenso.com').fill(seedTestEmail());
|
||||
await page.getByPlaceholder('recipient@documenso.com').fill(seedTestEmail());
|
||||
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await page.getByRole('button', { name: 'Complete' }).click();
|
||||
await page.getByRole('button', { name: 'Sign' }).click();
|
||||
await page.waitForURL(/\/sign/);
|
||||
await expect(page.getByRole('heading', { name: 'Document Signed' })).toBeVisible();
|
||||
}
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: owner.email,
|
||||
});
|
||||
|
||||
// Check that the owner has the documents.
|
||||
for (const template of [personalDirectTemplate, teamDirectTemplate]) {
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}${formatDocumentsPath(template.team?.url)}`);
|
||||
|
||||
await expect(async () => {
|
||||
// Check that the document is in the 'All' tab.
|
||||
await checkDocumentTabCount(page, 'Completed', 1);
|
||||
}).toPass();
|
||||
}
|
||||
});
|
||||
|
||||
test('[DIRECT_TEMPLATES]: use direct template link with 2 recipients', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const owner = team.owner;
|
||||
|
||||
const secondRecipient = await seedUser();
|
||||
|
||||
const createTemplateOptions = {
|
||||
recipients: {
|
||||
createMany: {
|
||||
data: [
|
||||
{
|
||||
email: DIRECT_TEMPLATE_RECIPIENT_EMAIL,
|
||||
name: DIRECT_TEMPLATE_RECIPIENT_NAME,
|
||||
token: nanoid(),
|
||||
},
|
||||
{
|
||||
email: secondRecipient.email,
|
||||
token: nanoid(),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Should only be visible to the owner in personal templates.
|
||||
const personalDirectTemplate = await seedDirectTemplate({
|
||||
title: 'Personal direct template link',
|
||||
userId: owner.id,
|
||||
createTemplateOptions,
|
||||
});
|
||||
|
||||
// Should be visible to team members.
|
||||
const teamDirectTemplate = await seedDirectTemplate({
|
||||
title: 'Team direct template link 1',
|
||||
userId: owner.id,
|
||||
teamId: team.id,
|
||||
createTemplateOptions,
|
||||
});
|
||||
|
||||
// Run test for personal and team templates.
|
||||
for (const template of [personalDirectTemplate, teamDirectTemplate]) {
|
||||
// Check that the direct template link is accessible.
|
||||
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
|
||||
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
await page.getByPlaceholder('recipient@documenso.com').fill(seedTestEmail());
|
||||
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await page.getByRole('button', { name: 'Complete' }).click();
|
||||
await page.getByRole('button', { name: 'Sign' }).click();
|
||||
await page.waitForURL(/\/sign/);
|
||||
await expect(page.getByText('Waiting for others to sign')).toBeVisible();
|
||||
}
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: owner.email,
|
||||
});
|
||||
|
||||
// Check that the owner has the documents.
|
||||
for (const template of [personalDirectTemplate, teamDirectTemplate]) {
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}${formatDocumentsPath(template.team?.url)}`);
|
||||
|
||||
// Check that the document is in the 'All' tab.
|
||||
await checkDocumentTabCount(page, 'All', 1);
|
||||
await checkDocumentTabCount(page, 'Pending', 1);
|
||||
}
|
||||
|
||||
// Check that the second recipient has the 2 pending documents.
|
||||
await apiSignin({
|
||||
page,
|
||||
email: secondRecipient.email,
|
||||
});
|
||||
|
||||
await page.goto('/documents');
|
||||
|
||||
await checkDocumentTabCount(page, 'All', 2);
|
||||
await checkDocumentTabCount(page, 'Inbox', 2);
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await page.getByRole('button', { name: 'Complete' }).click();
|
||||
await page.getByRole('button', { name: 'Sign' }).click();
|
||||
await page.waitForURL(/\/sign/);
|
||||
await expect(page.getByRole('heading', { name: 'Document Signed' })).toBeVisible();
|
||||
});
|
||||
|
||||
@ -1,23 +1,19 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { TeamMemberRole } from '@prisma/client';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { seedTeam } from '@documenso/prisma/seed/teams';
|
||||
import { seedTeam, seedTeamMember } from '@documenso/prisma/seed/teams';
|
||||
import { seedTemplate } from '@documenso/prisma/seed/templates';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test('[TEMPLATES]: view templates', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
const { team, owner, organisation } = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const owner = team.owner;
|
||||
const teamMemberUser = team.members[1].user;
|
||||
|
||||
// Should only be visible to the owner in personal templates.
|
||||
await seedTemplate({
|
||||
title: 'Personal template',
|
||||
userId: owner.id,
|
||||
const teamMemberUser = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
// Should be visible to team members.
|
||||
@ -37,29 +33,21 @@ test('[TEMPLATES]: view templates', async ({ page }) => {
|
||||
await apiSignin({
|
||||
page,
|
||||
email: owner.email,
|
||||
redirectPath: '/templates',
|
||||
redirectPath: `/t/${team.url}/templates`,
|
||||
});
|
||||
|
||||
// Only should only see their personal template.
|
||||
await expect(page.getByTestId('data-table-count')).toContainText('Showing 1 result');
|
||||
|
||||
// Owner should see both team templates.
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates`);
|
||||
await expect(page.getByTestId('data-table-count')).toContainText('Showing 2 results');
|
||||
});
|
||||
|
||||
test('[TEMPLATES]: delete template', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
const { team, owner, organisation } = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const owner = team.owner;
|
||||
const teamMemberUser = team.members[1].user;
|
||||
|
||||
// Should only be visible to the owner in personal templates.
|
||||
await seedTemplate({
|
||||
title: 'Personal template',
|
||||
userId: owner.id,
|
||||
const teamMemberUser = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
// Should be visible to team members.
|
||||
@ -79,18 +67,9 @@ test('[TEMPLATES]: delete template', async ({ page }) => {
|
||||
await apiSignin({
|
||||
page,
|
||||
email: owner.email,
|
||||
redirectPath: '/templates',
|
||||
redirectPath: `/t/${team.url}/templates`,
|
||||
});
|
||||
|
||||
// Owner should be able to delete their personal template.
|
||||
await page.getByRole('cell', { name: 'Use Template' }).getByRole('button').nth(1).click();
|
||||
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
await expect(page.getByText('Template deleted').first()).toBeVisible();
|
||||
|
||||
// Team member should be able to delete all templates.
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates`);
|
||||
|
||||
for (const template of ['Team template 1', 'Team template 2']) {
|
||||
await page
|
||||
.getByRole('row', { name: template })
|
||||
@ -108,17 +87,13 @@ test('[TEMPLATES]: delete template', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEMPLATES]: duplicate template', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
const { team, owner, organisation } = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const owner = team.owner;
|
||||
const teamMemberUser = team.members[1].user;
|
||||
|
||||
// Should only be visible to the owner in personal templates.
|
||||
await seedTemplate({
|
||||
title: 'Personal template',
|
||||
userId: owner.id,
|
||||
const teamMemberUser = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
// Should be visible to team members.
|
||||
@ -131,18 +106,9 @@ test('[TEMPLATES]: duplicate template', async ({ page }) => {
|
||||
await apiSignin({
|
||||
page,
|
||||
email: owner.email,
|
||||
redirectPath: '/templates',
|
||||
redirectPath: `/t/${team.url}/templates`,
|
||||
});
|
||||
|
||||
// Duplicate personal template.
|
||||
await page.getByRole('cell', { name: 'Use Template' }).getByRole('button').nth(1).click();
|
||||
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
|
||||
await page.getByRole('button', { name: 'Duplicate' }).click();
|
||||
await expect(page.getByText('Template duplicated').first()).toBeVisible();
|
||||
await expect(page.getByTestId('data-table-count')).toContainText('Showing 2 results');
|
||||
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates`);
|
||||
|
||||
// Duplicate team template.
|
||||
await page.getByRole('cell', { name: 'Use Template' }).getByRole('button').nth(1).click();
|
||||
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
|
||||
@ -152,17 +118,13 @@ test('[TEMPLATES]: duplicate template', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('[TEMPLATES]: use template', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
const { team, owner, organisation } = await seedTeam({
|
||||
createTeamMembers: 1,
|
||||
});
|
||||
|
||||
const owner = team.owner;
|
||||
const teamMemberUser = team.members[1].user;
|
||||
|
||||
// Should only be visible to the owner in personal templates.
|
||||
await seedTemplate({
|
||||
title: 'Personal template',
|
||||
userId: owner.id,
|
||||
const teamMemberUser = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
// Should be visible to team members.
|
||||
@ -175,27 +137,9 @@ test('[TEMPLATES]: use template', async ({ page }) => {
|
||||
await apiSignin({
|
||||
page,
|
||||
email: owner.email,
|
||||
redirectPath: '/templates',
|
||||
redirectPath: `/t/${team.url}/templates`,
|
||||
});
|
||||
|
||||
// Use personal template.
|
||||
await page.getByRole('button', { name: 'Use Template' }).click();
|
||||
|
||||
// Enter template values.
|
||||
await page.getByPlaceholder('recipient.1@documenso.com').click();
|
||||
await page.getByPlaceholder('recipient.1@documenso.com').fill(teamMemberUser.email);
|
||||
await page.getByPlaceholder('Recipient 1').click();
|
||||
await page.getByPlaceholder('Recipient 1').fill('name');
|
||||
|
||||
await page.getByRole('button', { name: 'Create as draft' }).click();
|
||||
await page.waitForURL(/documents/);
|
||||
await page.getByRole('main').getByRole('link', { name: 'Documents' }).click();
|
||||
await page.waitForURL('/documents');
|
||||
await expect(page.getByTestId('data-table-count')).toContainText('Showing 1 result');
|
||||
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates`);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Use team template.
|
||||
await page.getByRole('button', { name: 'Use Template' }).click();
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { type Page, expect, test } from '@playwright/test';
|
||||
|
||||
import { alphaid } from '@documenso/lib/universal/id';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import {
|
||||
extractUserVerificationToken,
|
||||
seedTestEmail,
|
||||
@ -23,17 +23,26 @@ test('[USER] can sign up with email and password', async ({ page }: { page: Page
|
||||
|
||||
await signSignaturePad(page);
|
||||
|
||||
await page.getByRole('button', { name: 'Next', exact: true }).click();
|
||||
|
||||
await page.getByLabel('Public profile username').fill(alphaid(10));
|
||||
await page.getByLabel('Public profile username').blur();
|
||||
|
||||
await page.getByRole('button', { name: 'Complete' }).click();
|
||||
await page.getByRole('button', { name: 'Complete', exact: true }).click();
|
||||
|
||||
await page.waitForURL('/unverified-account');
|
||||
|
||||
const { token } = await extractUserVerificationToken(email);
|
||||
|
||||
const team = await prisma.team.findFirstOrThrow({
|
||||
where: {
|
||||
organisation: {
|
||||
members: {
|
||||
some: {
|
||||
user: {
|
||||
email,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await page.goto(`/verify-email/${token}`);
|
||||
|
||||
await expect(page.getByRole('heading')).toContainText('Email Confirmed!');
|
||||
@ -41,19 +50,19 @@ test('[USER] can sign up with email and password', async ({ page }: { page: Page
|
||||
// We now automatically redirect to the home page
|
||||
await page.getByRole('link', { name: 'Continue' }).click();
|
||||
|
||||
await page.waitForURL('/documents');
|
||||
|
||||
await expect(page).toHaveURL('/documents');
|
||||
// Expect to be redirected to their only team.
|
||||
await page.waitForURL(`/t/${team.url}/documents`);
|
||||
await expect(page).toHaveURL(`/t/${team.url}/documents`);
|
||||
});
|
||||
|
||||
test('[USER] can sign in using email and password', async ({ page }: { page: Page }) => {
|
||||
const user = await seedUser();
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
await page.goto('/signin');
|
||||
await page.getByLabel('Email').fill(user.email);
|
||||
await page.getByLabel('Password', { exact: true }).fill('password');
|
||||
await page.getByRole('button', { name: 'Sign In' }).click();
|
||||
|
||||
await page.waitForURL('/documents');
|
||||
await expect(page).toHaveURL('/documents');
|
||||
await page.waitForURL(`/t/${team.url}/documents`);
|
||||
await expect(page).toHaveURL(`/t/${team.url}/documents`);
|
||||
});
|
||||
|
||||
@ -7,7 +7,7 @@ import { seedUser } from '@documenso/prisma/seed/users';
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test('[USER] delete account', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const { user } = await seedUser();
|
||||
|
||||
await apiSignin({ page, email: user.email, redirectPath: '/settings' });
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ test('[USER] can reset password via forgot password', async ({ page }: { page: P
|
||||
const oldPassword = 'Test123!';
|
||||
const newPassword = 'Test124!';
|
||||
|
||||
const user = await seedUser({
|
||||
const { user } = await seedUser({
|
||||
password: oldPassword,
|
||||
});
|
||||
|
||||
@ -51,17 +51,18 @@ test('[USER] can reset password via forgot password', async ({ page }: { page: P
|
||||
page,
|
||||
email: user.email,
|
||||
password: newPassword,
|
||||
redirectPath: '/settings/profile',
|
||||
});
|
||||
|
||||
await page.waitForURL('/documents');
|
||||
await expect(page).toHaveURL('/documents');
|
||||
await page.waitForURL('/settings/profile');
|
||||
await expect(page).toHaveURL('/settings/profile');
|
||||
});
|
||||
|
||||
test('[USER] can reset password via user settings', async ({ page }: { page: Page }) => {
|
||||
const oldPassword = 'Test123!';
|
||||
const newPassword = 'Test124!';
|
||||
|
||||
const user = await seedUser({
|
||||
const { user } = await seedUser({
|
||||
password: oldPassword,
|
||||
});
|
||||
|
||||
@ -87,8 +88,9 @@ test('[USER] can reset password via user settings', async ({ page }: { page: Pag
|
||||
page,
|
||||
email: user.email,
|
||||
password: newPassword,
|
||||
redirectPath: '/settings/profile',
|
||||
});
|
||||
|
||||
await page.waitForURL('/documents');
|
||||
await expect(page).toHaveURL('/documents');
|
||||
await page.waitForURL('/settings/profile');
|
||||
await expect(page).toHaveURL('/settings/profile');
|
||||
});
|
||||
|
||||
@ -7,7 +7,7 @@ import { apiSignin } from '../fixtures/authentication';
|
||||
import { signSignaturePad } from '../fixtures/signature';
|
||||
|
||||
test('[USER] update full name', async ({ page }) => {
|
||||
const user = await seedUser();
|
||||
const { user } = await seedUser();
|
||||
|
||||
await apiSignin({ page, email: user.email, redirectPath: '/settings/profile' });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user