mirror of
https://github.com/documenso/documenso.git
synced 2026-07-25 09:25:08 +10:00
chore: fix merge conflicts
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { type APIRequestContext, expect, test } from '@playwright/test';
|
||||
import type { Team, User } from '@prisma/client';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
@@ -27,6 +27,7 @@ import type {
|
||||
import type { TDistributeEnvelopeRequest } from '@documenso/trpc/server/envelope-router/distribute-envelope.types';
|
||||
import type { TCreateEnvelopeRecipientsRequest } from '@documenso/trpc/server/envelope-router/envelope-recipients/create-envelope-recipients.types';
|
||||
import type { TUpdateEnvelopeRecipientsRequest } from '@documenso/trpc/server/envelope-router/envelope-recipients/update-envelope-recipients.types';
|
||||
import type { TFindEnvelopesResponse } from '@documenso/trpc/server/envelope-router/find-envelopes.types';
|
||||
import type { TGetEnvelopeResponse } from '@documenso/trpc/server/envelope-router/get-envelope.types';
|
||||
import type { TUpdateEnvelopeRequest } from '@documenso/trpc/server/envelope-router/update-envelope.types';
|
||||
|
||||
@@ -562,6 +563,200 @@ test.describe('API V2 Envelopes', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Envelope find endpoint', () => {
|
||||
const createEnvelope = async (
|
||||
request: APIRequestContext,
|
||||
token: string,
|
||||
payload: TCreateEnvelopePayload,
|
||||
) => {
|
||||
const formData = new FormData();
|
||||
formData.append('payload', JSON.stringify(payload));
|
||||
|
||||
const pdfData = fs.readFileSync(
|
||||
path.join(__dirname, '../../../../../assets/field-font-alignment.pdf'),
|
||||
);
|
||||
formData.append('files', new File([pdfData], 'test.pdf', { type: 'application/pdf' }));
|
||||
|
||||
const res = await request.post(`${baseUrl}/envelope/create`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
multipart: formData,
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
return (await res.json()) as TCreateEnvelopeResponse;
|
||||
};
|
||||
|
||||
test('should find envelopes with pagination', async ({ request }) => {
|
||||
// Create 3 envelopes
|
||||
await createEnvelope(request, tokenA, {
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
title: 'Document 1',
|
||||
});
|
||||
await createEnvelope(request, tokenA, {
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
title: 'Document 2',
|
||||
});
|
||||
await createEnvelope(request, tokenA, {
|
||||
type: EnvelopeType.TEMPLATE,
|
||||
title: 'Template 1',
|
||||
});
|
||||
|
||||
// Find all envelopes
|
||||
const res = await request.get(`${baseUrl}/envelope`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const response = (await res.json()) as TFindEnvelopesResponse;
|
||||
|
||||
expect(response.data.length).toBe(3);
|
||||
expect(response.count).toBe(3);
|
||||
expect(response.currentPage).toBe(1);
|
||||
expect(response.totalPages).toBe(1);
|
||||
|
||||
// Test pagination
|
||||
const paginatedRes = await request.get(`${baseUrl}/envelope?perPage=2&page=1`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
});
|
||||
|
||||
expect(paginatedRes.ok()).toBeTruthy();
|
||||
const paginatedResponse = (await paginatedRes.json()) as TFindEnvelopesResponse;
|
||||
|
||||
expect(paginatedResponse.data.length).toBe(2);
|
||||
expect(paginatedResponse.count).toBe(3);
|
||||
expect(paginatedResponse.totalPages).toBe(2);
|
||||
});
|
||||
|
||||
test('should filter envelopes by type', async ({ request }) => {
|
||||
await createEnvelope(request, tokenA, {
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
title: 'Document Only',
|
||||
});
|
||||
await createEnvelope(request, tokenA, {
|
||||
type: EnvelopeType.TEMPLATE,
|
||||
title: 'Template Only',
|
||||
});
|
||||
|
||||
// Filter by DOCUMENT type
|
||||
const documentRes = await request.get(`${baseUrl}/envelope?type=DOCUMENT`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
});
|
||||
|
||||
expect(documentRes.ok()).toBeTruthy();
|
||||
const documentResponse = (await documentRes.json()) as TFindEnvelopesResponse;
|
||||
|
||||
expect(documentResponse.data.every((e) => e.type === EnvelopeType.DOCUMENT)).toBe(true);
|
||||
|
||||
// Filter by TEMPLATE type
|
||||
const templateRes = await request.get(`${baseUrl}/envelope?type=TEMPLATE`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
});
|
||||
|
||||
expect(templateRes.ok()).toBeTruthy();
|
||||
const templateResponse = (await templateRes.json()) as TFindEnvelopesResponse;
|
||||
|
||||
expect(templateResponse.data.every((e) => e.type === EnvelopeType.TEMPLATE)).toBe(true);
|
||||
});
|
||||
|
||||
test('should filter envelopes by status', async ({ request }) => {
|
||||
await createEnvelope(request, tokenA, {
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
title: 'Draft Document',
|
||||
});
|
||||
|
||||
// Filter by DRAFT status (default for new envelopes)
|
||||
const res = await request.get(`${baseUrl}/envelope?status=DRAFT`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const response = (await res.json()) as TFindEnvelopesResponse;
|
||||
|
||||
expect(response.data.every((e) => e.status === DocumentStatus.DRAFT)).toBe(true);
|
||||
});
|
||||
|
||||
test('should search envelopes by query', async ({ request }) => {
|
||||
await createEnvelope(request, tokenA, {
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
title: 'Unique Searchable Title',
|
||||
});
|
||||
await createEnvelope(request, tokenA, {
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
title: 'Another Document',
|
||||
});
|
||||
|
||||
const res = await request.get(`${baseUrl}/envelope?query=Unique%20Searchable`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const response = (await res.json()) as TFindEnvelopesResponse;
|
||||
|
||||
expect(response.data.length).toBe(1);
|
||||
expect(response.data[0].title).toBe('Unique Searchable Title');
|
||||
});
|
||||
|
||||
test('should not return envelopes from other users', async ({ request }) => {
|
||||
// Create envelope for userA
|
||||
await createEnvelope(request, tokenA, {
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
title: 'UserA Document',
|
||||
});
|
||||
|
||||
// Create envelope for userB
|
||||
await createEnvelope(request, tokenB, {
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
title: 'UserB Document',
|
||||
});
|
||||
|
||||
// userA should only see their own envelopes
|
||||
const resA = await request.get(`${baseUrl}/envelope`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
});
|
||||
|
||||
expect(resA.ok()).toBeTruthy();
|
||||
const responseA = (await resA.json()) as TFindEnvelopesResponse;
|
||||
|
||||
expect(responseA.data.every((e) => e.title !== 'UserB Document')).toBe(true);
|
||||
|
||||
// userB should only see their own envelopes
|
||||
const resB = await request.get(`${baseUrl}/envelope`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
});
|
||||
|
||||
expect(resB.ok()).toBeTruthy();
|
||||
const responseB = (await resB.json()) as TFindEnvelopesResponse;
|
||||
|
||||
expect(responseB.data.every((e) => e.title !== 'UserA Document')).toBe(true);
|
||||
});
|
||||
|
||||
test('should return envelope with expected schema fields', async ({ request }) => {
|
||||
await createEnvelope(request, tokenA, {
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
title: 'Schema Test Document',
|
||||
});
|
||||
|
||||
const res = await request.get(`${baseUrl}/envelope`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const response = (await res.json()) as TFindEnvelopesResponse;
|
||||
|
||||
const envelope = response.data.find((e) => e.title === 'Schema Test Document');
|
||||
|
||||
expect(envelope).toBeDefined();
|
||||
expect(envelope?.id).toBeDefined();
|
||||
expect(envelope?.type).toBe(EnvelopeType.DOCUMENT);
|
||||
expect(envelope?.status).toBe(DocumentStatus.DRAFT);
|
||||
expect(envelope?.recipients).toBeDefined();
|
||||
expect(envelope?.user).toBeDefined();
|
||||
expect(envelope?.team).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Empty recipient tests', () => {
|
||||
test('Create template envelope with empty email recipient', async ({ request }) => {
|
||||
const payload = {
|
||||
|
||||
@@ -12,14 +12,17 @@ import {
|
||||
} from '@documenso/lib/utils/envelope';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import {
|
||||
DocumentStatus,
|
||||
DocumentVisibility,
|
||||
EnvelopeType,
|
||||
FieldType,
|
||||
FolderType,
|
||||
Prisma,
|
||||
ReadStatus,
|
||||
RecipientRole,
|
||||
SendStatus,
|
||||
SigningStatus,
|
||||
TeamMemberRole,
|
||||
} from '@documenso/prisma/client';
|
||||
import {
|
||||
seedBlankDocument,
|
||||
@@ -28,14 +31,18 @@ import {
|
||||
seedPendingDocument,
|
||||
} from '@documenso/prisma/seed/documents';
|
||||
import { seedBlankFolder } from '@documenso/prisma/seed/folders';
|
||||
import { seedTeamMember } from '@documenso/prisma/seed/teams';
|
||||
import { seedBlankTemplate, seedTemplate } from '@documenso/prisma/seed/templates';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
import type { TCreateEnvelopeItemsPayload } from '@documenso/trpc/server/envelope-router/create-envelope-items.types';
|
||||
import type { TFindEnvelopesResponse } from '@documenso/trpc/server/envelope-router/find-envelopes.types';
|
||||
import type {
|
||||
TUseEnvelopePayload,
|
||||
TUseEnvelopeResponse,
|
||||
} from '@documenso/trpc/server/envelope-router/use-envelope.types';
|
||||
|
||||
import { apiSignin } from '../../fixtures/authentication';
|
||||
|
||||
const WEBAPP_BASE_URL = NEXT_PUBLIC_WEBAPP_URL();
|
||||
|
||||
test.describe.configure({
|
||||
@@ -2990,6 +2997,566 @@ test.describe('Document API V2', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Envelope get-many endpoint', () => {
|
||||
test('should block unauthorized access to envelope get-many endpoint', async ({
|
||||
request,
|
||||
}) => {
|
||||
const doc1 = await seedBlankDocument(userA, teamA.id);
|
||||
const doc2 = await seedBlankDocument(userA, teamA.id);
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/get-many`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
data: {
|
||||
ids: {
|
||||
type: 'envelopeId',
|
||||
ids: [doc1.id, doc2.id],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const { data } = await res.json();
|
||||
expect(data).toEqual([]);
|
||||
});
|
||||
|
||||
test('should allow authorized access to envelope get-many endpoint', async ({ request }) => {
|
||||
const doc1 = await seedBlankDocument(userA, teamA.id);
|
||||
const doc2 = await seedBlankDocument(userA, teamA.id);
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/get-many`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
data: {
|
||||
ids: {
|
||||
type: 'envelopeId',
|
||||
ids: [doc1.id, doc2.id],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const { data } = await res.json();
|
||||
expect(data.length).toBe(2);
|
||||
expect(data.map((d: { id: string }) => d.id).sort()).toEqual([doc1.id, doc2.id].sort());
|
||||
});
|
||||
|
||||
test('should only return authorized envelopes when mixing owned and unowned', async ({
|
||||
request,
|
||||
}) => {
|
||||
const docA = await seedBlankDocument(userA, teamA.id);
|
||||
const docB = await seedBlankDocument(userB, teamB.id);
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/get-many`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
data: {
|
||||
ids: {
|
||||
type: 'envelopeId',
|
||||
ids: [docA.id, docB.id],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const { data } = await res.json();
|
||||
expect(data.length).toBe(1);
|
||||
expect(data[0].id).toBe(docA.id);
|
||||
});
|
||||
|
||||
test('should block unauthorized access with documentId type', async ({ request }) => {
|
||||
const doc1 = await seedBlankDocument(userA, teamA.id);
|
||||
const doc2 = await seedBlankDocument(userA, teamA.id);
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/get-many`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
data: {
|
||||
ids: {
|
||||
type: 'documentId',
|
||||
ids: [
|
||||
mapSecondaryIdToDocumentId(doc1.secondaryId),
|
||||
mapSecondaryIdToDocumentId(doc2.secondaryId),
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const { data } = await res.json();
|
||||
expect(data).toEqual([]);
|
||||
});
|
||||
|
||||
test('should allow authorized access with documentId type', async ({ request }) => {
|
||||
const doc1 = await seedBlankDocument(userA, teamA.id);
|
||||
const doc2 = await seedBlankDocument(userA, teamA.id);
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/get-many`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
data: {
|
||||
ids: {
|
||||
type: 'documentId',
|
||||
ids: [
|
||||
mapSecondaryIdToDocumentId(doc1.secondaryId),
|
||||
mapSecondaryIdToDocumentId(doc2.secondaryId),
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const { data } = await res.json();
|
||||
expect(data.length).toBe(2);
|
||||
});
|
||||
|
||||
test('should block unauthorized access with templateId type', async ({ request }) => {
|
||||
const template1 = await seedBlankTemplate(userA, teamA.id);
|
||||
const template2 = await seedBlankTemplate(userA, teamA.id);
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/get-many`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
data: {
|
||||
ids: {
|
||||
type: 'templateId',
|
||||
ids: [
|
||||
mapSecondaryIdToTemplateId(template1.secondaryId),
|
||||
mapSecondaryIdToTemplateId(template2.secondaryId),
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const { data } = await res.json();
|
||||
expect(data).toEqual([]);
|
||||
});
|
||||
|
||||
test('should allow authorized access with templateId type', async ({ request }) => {
|
||||
const template1 = await seedBlankTemplate(userA, teamA.id);
|
||||
const template2 = await seedBlankTemplate(userA, teamA.id);
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/get-many`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
data: {
|
||||
ids: {
|
||||
type: 'templateId',
|
||||
ids: [
|
||||
mapSecondaryIdToTemplateId(template1.secondaryId),
|
||||
mapSecondaryIdToTemplateId(template2.secondaryId),
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const { data } = await res.json();
|
||||
expect(data.length).toBe(2);
|
||||
});
|
||||
|
||||
test('should reject requests exceeding max ID limit', async ({ request }) => {
|
||||
const ids = Array.from({ length: 21 }, () => 'envelope_fake123');
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/get-many`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
data: {
|
||||
ids: {
|
||||
type: 'envelopeId',
|
||||
ids,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeFalsy();
|
||||
expect(res.status()).toBe(400);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Envelope get-many tRPC endpoint (teamId manipulation)', () => {
|
||||
test('should block access when user manipulates x-team-id to another team', async ({
|
||||
page,
|
||||
}) => {
|
||||
// Create documents for userA in teamA
|
||||
const doc1 = await seedBlankDocument(userA, teamA.id);
|
||||
const doc2 = await seedBlankDocument(userA, teamA.id);
|
||||
|
||||
// Sign in as userB
|
||||
await apiSignin({ page, email: userB.email });
|
||||
|
||||
const res = await page
|
||||
.context()
|
||||
.request.post(`${WEBAPP_BASE_URL}/api/trpc/envelope.getMany`, {
|
||||
headers: {
|
||||
'x-team-id': String(teamA.id),
|
||||
},
|
||||
data: {
|
||||
json: {
|
||||
ids: {
|
||||
type: 'envelopeId',
|
||||
ids: [doc1.id, doc2.id],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Make tRPC request with manipulated x-team-id pointing to teamA (which userB doesn't belong to)
|
||||
expect(res.ok()).toBeFalsy();
|
||||
// Team not found
|
||||
expect(res.status()).toBe(404);
|
||||
});
|
||||
|
||||
test('should allow access when user uses their own team id', async ({ page }) => {
|
||||
// Create documents for userA in teamA
|
||||
const doc1 = await seedBlankDocument(userA, teamA.id);
|
||||
const doc2 = await seedBlankDocument(userA, teamA.id);
|
||||
|
||||
// Sign in as userA
|
||||
await apiSignin({ page, email: userA.email });
|
||||
|
||||
const res = await page
|
||||
.context()
|
||||
.request.post(`${WEBAPP_BASE_URL}/api/trpc/envelope.getMany`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-team-id': String(teamA.id),
|
||||
},
|
||||
data: {
|
||||
json: {
|
||||
ids: {
|
||||
type: 'envelopeId',
|
||||
ids: [doc1.id, doc2.id],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
const items = data.result.data.json.data;
|
||||
|
||||
expect(items.length).toBe(2);
|
||||
expect(items.map((d: { id: string }) => d.id).sort()).toEqual([doc1.id, doc2.id].sort());
|
||||
});
|
||||
|
||||
test('should block access when switching team id mid-request to access other team data', async ({
|
||||
page,
|
||||
}) => {
|
||||
// Create a document for userA in teamA
|
||||
const docA = await seedBlankDocument(userA, teamA.id);
|
||||
// Create a document for userB in teamB
|
||||
const docB = await seedBlankDocument(userB, teamB.id);
|
||||
|
||||
// Sign in as userB
|
||||
await apiSignin({ page, email: userB.email });
|
||||
|
||||
const res = await page
|
||||
.context()
|
||||
.request.post(`${WEBAPP_BASE_URL}/api/trpc/envelope.getMany`, {
|
||||
headers: {
|
||||
'x-team-id': String(teamA.id),
|
||||
},
|
||||
data: {
|
||||
json: {
|
||||
ids: {
|
||||
type: 'envelopeId',
|
||||
ids: [docA.id, docB.id],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// UserB tries to access both documents by manipulating teamId to teamA
|
||||
// Should fail - userB is not a member of teamA
|
||||
expect(res.ok()).toBeFalsy();
|
||||
// Team not found
|
||||
expect(res.status()).toBe(404);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Envelope find endpoint', () => {
|
||||
test('should block unauthorized access to envelope find endpoint', async ({ request }) => {
|
||||
await seedBlankDocument(userA, teamA.id);
|
||||
|
||||
const res = await request.get(`${WEBAPP_BASE_URL}/api/v2-beta/envelope`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const data = (await res.json()) as TFindEnvelopesResponse;
|
||||
expect(data.data.every((doc) => doc.userId !== userA.id)).toBe(true);
|
||||
});
|
||||
|
||||
test('should allow authorized access to envelope find endpoint', async ({ request }) => {
|
||||
await seedBlankDocument(userA, teamA.id);
|
||||
|
||||
const res = await request.get(`${WEBAPP_BASE_URL}/api/v2-beta/envelope`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const data = (await res.json()) as TFindEnvelopesResponse;
|
||||
expect(data.data.length).toBeGreaterThan(0);
|
||||
expect(data.data.some((doc) => doc.userId === userA.id)).toBe(true);
|
||||
});
|
||||
|
||||
test('should respect team document visibility for ADMIN role', async ({ request }) => {
|
||||
const adminMember = await seedTeamMember({
|
||||
teamId: teamA.id,
|
||||
role: TeamMemberRole.ADMIN,
|
||||
});
|
||||
|
||||
const { token: adminToken } = await createApiToken({
|
||||
userId: adminMember.id,
|
||||
teamId: teamA.id,
|
||||
tokenName: 'adminMember',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
await seedBlankDocument(userA, teamA.id, {
|
||||
createDocumentOptions: {
|
||||
visibility: DocumentVisibility.ADMIN,
|
||||
title: 'Admin Only Document',
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankDocument(userA, teamA.id, {
|
||||
createDocumentOptions: {
|
||||
visibility: DocumentVisibility.MANAGER_AND_ABOVE,
|
||||
title: 'Manager and Above Document',
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankDocument(userA, teamA.id, {
|
||||
createDocumentOptions: {
|
||||
visibility: DocumentVisibility.EVERYONE,
|
||||
title: 'Everyone Document',
|
||||
},
|
||||
});
|
||||
|
||||
const res = await request.get(`${WEBAPP_BASE_URL}/api/v2-beta/envelope`, {
|
||||
headers: { Authorization: `Bearer ${adminToken}` },
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const data = (await res.json()) as TFindEnvelopesResponse;
|
||||
|
||||
const titles = data.data.map((doc) => doc.title);
|
||||
expect(titles).toContain('Admin Only Document');
|
||||
expect(titles).toContain('Manager and Above Document');
|
||||
expect(titles).toContain('Everyone Document');
|
||||
});
|
||||
|
||||
test('should respect team document visibility for MANAGER role', async ({ request }) => {
|
||||
const managerMember = await seedTeamMember({
|
||||
teamId: teamA.id,
|
||||
role: TeamMemberRole.MANAGER,
|
||||
});
|
||||
|
||||
const { token: managerToken } = await createApiToken({
|
||||
userId: managerMember.id,
|
||||
teamId: teamA.id,
|
||||
tokenName: 'managerMember',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
await seedBlankDocument(userA, teamA.id, {
|
||||
createDocumentOptions: {
|
||||
visibility: DocumentVisibility.ADMIN,
|
||||
title: 'Admin Only Document',
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankDocument(userA, teamA.id, {
|
||||
createDocumentOptions: {
|
||||
visibility: DocumentVisibility.MANAGER_AND_ABOVE,
|
||||
title: 'Manager and Above Document',
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankDocument(userA, teamA.id, {
|
||||
createDocumentOptions: {
|
||||
visibility: DocumentVisibility.EVERYONE,
|
||||
title: 'Everyone Document',
|
||||
},
|
||||
});
|
||||
|
||||
const res = await request.get(`${WEBAPP_BASE_URL}/api/v2-beta/envelope`, {
|
||||
headers: { Authorization: `Bearer ${managerToken}` },
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const data = (await res.json()) as TFindEnvelopesResponse;
|
||||
|
||||
const titles = data.data.map((doc) => doc.title);
|
||||
expect(titles).not.toContain('Admin Only Document');
|
||||
expect(titles).toContain('Manager and Above Document');
|
||||
expect(titles).toContain('Everyone Document');
|
||||
});
|
||||
|
||||
test('should filter envelopes by folderId with authorization', async ({ request }) => {
|
||||
const folder = await prisma.folder.create({
|
||||
data: {
|
||||
userId: userA.id,
|
||||
teamId: teamA.id,
|
||||
name: 'Test Folder',
|
||||
type: FolderType.DOCUMENT,
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankDocument(userA, teamA.id, {
|
||||
createDocumentOptions: {
|
||||
folderId: folder.id,
|
||||
title: 'Document in Folder',
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankDocument(userA, teamA.id, {
|
||||
createDocumentOptions: {
|
||||
title: 'Document Not in Folder',
|
||||
},
|
||||
});
|
||||
|
||||
const resWithFolder = await request.get(
|
||||
`${WEBAPP_BASE_URL}/api/v2-beta/envelope?folderId=${folder.id}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
},
|
||||
);
|
||||
|
||||
expect(resWithFolder.ok()).toBeTruthy();
|
||||
const dataWithFolder = (await resWithFolder.json()) as TFindEnvelopesResponse;
|
||||
expect(dataWithFolder.data.every((doc) => doc.folderId === folder.id)).toBe(true);
|
||||
expect(dataWithFolder.data.some((doc) => doc.title === 'Document in Folder')).toBe(true);
|
||||
|
||||
const resUnauthorized = await request.get(
|
||||
`${WEBAPP_BASE_URL}/api/v2-beta/envelope?folderId=${folder.id}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
},
|
||||
);
|
||||
|
||||
expect(resUnauthorized.ok()).toBeTruthy();
|
||||
const dataUnauthorized = (await resUnauthorized.json()) as TFindEnvelopesResponse;
|
||||
expect(
|
||||
dataUnauthorized.data.every(
|
||||
(doc) => doc.folderId !== folder.id || doc.userId !== userA.id,
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('should filter envelopes by type with authorization', async ({ request }) => {
|
||||
await seedBlankDocument(userA, teamA.id, {
|
||||
createDocumentOptions: {
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
title: 'UserA Document',
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankDocument(userA, teamA.id, {
|
||||
createDocumentOptions: {
|
||||
type: EnvelopeType.TEMPLATE,
|
||||
title: 'UserA Template',
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankDocument(userB, teamB.id, {
|
||||
createDocumentOptions: {
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
title: 'UserB Document',
|
||||
},
|
||||
});
|
||||
|
||||
const res = await request.get(`${WEBAPP_BASE_URL}/api/v2-beta/envelope?type=DOCUMENT`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const data = (await res.json()) as TFindEnvelopesResponse;
|
||||
expect(data.data.every((doc) => doc.type === EnvelopeType.DOCUMENT)).toBe(true);
|
||||
expect(data.data.every((doc) => doc.userId === userA.id)).toBe(true);
|
||||
expect(data.data.some((doc) => doc.title === 'UserA Document')).toBe(true);
|
||||
expect(data.data.every((doc) => doc.title !== 'UserB Document')).toBe(true);
|
||||
});
|
||||
|
||||
test('should filter envelopes by status with authorization', async ({ request }) => {
|
||||
await seedBlankDocument(userA, teamA.id, {
|
||||
createDocumentOptions: {
|
||||
title: 'Draft Document',
|
||||
status: DocumentStatus.DRAFT,
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankDocument(userA, teamA.id, {
|
||||
createDocumentOptions: {
|
||||
title: 'Completed Document',
|
||||
status: DocumentStatus.COMPLETED,
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankDocument(userB, teamB.id, {
|
||||
createDocumentOptions: {
|
||||
title: 'UserB Draft',
|
||||
status: DocumentStatus.DRAFT,
|
||||
},
|
||||
});
|
||||
|
||||
const res = await request.get(`${WEBAPP_BASE_URL}/api/v2-beta/envelope?status=DRAFT`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const data = (await res.json()) as TFindEnvelopesResponse;
|
||||
expect(data.data.every((doc) => doc.status === DocumentStatus.DRAFT)).toBe(true);
|
||||
expect(data.data.every((doc) => doc.userId === userA.id)).toBe(true);
|
||||
expect(data.data.some((doc) => doc.title === 'Draft Document')).toBe(true);
|
||||
expect(data.data.every((doc) => doc.title !== 'UserB Draft')).toBe(true);
|
||||
expect(data.data.every((doc) => doc.title !== 'Completed Document')).toBe(true);
|
||||
});
|
||||
|
||||
test('should search envelopes by query with authorization', async ({ request }) => {
|
||||
await seedBlankDocument(userA, teamA.id, {
|
||||
createDocumentOptions: {
|
||||
title: 'Unique Searchable Title UserA',
|
||||
},
|
||||
});
|
||||
|
||||
await seedBlankDocument(userB, teamB.id, {
|
||||
createDocumentOptions: {
|
||||
title: 'Unique Searchable Title UserB',
|
||||
},
|
||||
});
|
||||
|
||||
const res = await request.get(
|
||||
`${WEBAPP_BASE_URL}/api/v2-beta/envelope?query=Unique%20Searchable`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
},
|
||||
);
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const data = (await res.json()) as TFindEnvelopesResponse;
|
||||
expect(data.data.every((doc) => doc.userId === userA.id)).toBe(true);
|
||||
expect(data.data.some((doc) => doc.title.includes('UserA'))).toBe(true);
|
||||
expect(data.data.every((doc) => !doc.title.includes('UserB'))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Envelope update endpoint', () => {
|
||||
test('should block unauthorized access to envelope update endpoint', async ({ request }) => {
|
||||
const doc = await seedBlankDocument(userA, teamA.id);
|
||||
|
||||
@@ -364,6 +364,25 @@ test('[TEAMS]: can create a template subfolder inside a template folder', async
|
||||
test('[TEAMS]: can create a template inside a template folder', async ({ page }) => {
|
||||
const { team, teamOwner } = await seedTeamDocuments();
|
||||
|
||||
const organisationClaim = await prisma.organisationClaim.findFirstOrThrow({
|
||||
where: {
|
||||
organisation: {
|
||||
id: team.organisationId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.organisationClaim.update({
|
||||
where: {
|
||||
id: organisationClaim.id,
|
||||
},
|
||||
data: {
|
||||
flags: {
|
||||
allowLegacyEnvelopes: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const folder = await seedBlankFolder(teamOwner, team.id, {
|
||||
createFolderOptions: {
|
||||
name: 'Team Client Templates',
|
||||
@@ -380,7 +399,11 @@ test('[TEAMS]: can create a template inside a template folder', async ({ page })
|
||||
|
||||
await expect(page.getByText('Team Client Templates')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Template (Legacy)' }).click();
|
||||
// Upload document.
|
||||
const [fileChooser] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.getByRole('button', { name: 'Template (Legacy)' }).click(),
|
||||
]);
|
||||
|
||||
await page.getByText('Upload Template Document').click();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user