feat: add organisations (#1820)

This commit is contained in:
David Nguyen
2025-06-10 11:49:52 +10:00
committed by GitHub
parent 0b37f19641
commit e6dc237ad2
631 changed files with 37616 additions and 25695 deletions

View File

@ -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,

View File

@ -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();
});

View File

@ -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();