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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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