Merge branch 'main' into fix/cc-recipient-order-last

This commit is contained in:
Catalin Pit
2026-06-16 13:50:56 +03:00
committed by GitHub
26 changed files with 789 additions and 230 deletions
@@ -0,0 +1,169 @@
import { INTERNAL_CLAIM_ID } from '@documenso/lib/types/subscription';
import { generateDatabaseId } from '@documenso/lib/universal/id';
import { currentMonthlyPeriod } from '@documenso/lib/universal/monthly-period';
import { prisma } from '@documenso/prisma';
import { seedUser } from '@documenso/prisma/seed/users';
import { expect, test } from '@playwright/test';
import { apiSignin } from '../fixtures/authentication';
const BANNER_EXCEEDED_TEXT = 'Your organisation has exceeded a fair use limit';
const BANNER_NEARING_TEXT = 'Your organisation is approaching a fair use limit';
type SeedQuotaStateOptions = {
organisationId: string;
organisationClaimId: string;
/**
* The `originalSubscriptionClaimId` to set on the claim. The banner is suppressed
* for `INTERNAL_CLAIM_ID.FREE`, so use a non-free value to make it render.
*/
subscriptionClaimId: string;
documentQuota: number | null;
documentCount: number;
};
/**
* Point the organisation's document quota and current-period usage at a known
* state. Only the document counter is touched; email/api quotas stay `null`
* (unlimited) so the document counter is the sole driver of the banner.
*/
const seedQuotaState = async ({
organisationId,
organisationClaimId,
subscriptionClaimId,
documentQuota,
documentCount,
}: SeedQuotaStateOptions) => {
await prisma.organisationClaim.update({
where: {
id: organisationClaimId,
},
data: {
originalSubscriptionClaimId: subscriptionClaimId,
documentQuota,
},
});
const period = currentMonthlyPeriod();
await prisma.organisationMonthlyStat.upsert({
where: {
organisationId_period: {
organisationId,
period,
},
},
update: {
documentCount,
},
create: {
id: generateDatabaseId('org_monthly_stat'),
organisationId,
period,
documentCount,
},
});
};
test('[QUOTA BANNER]: shows the approaching state when a quota is nearing', async ({ page }) => {
const { user, organisation } = await seedUser({
isPersonalOrganisation: false,
});
// ceil(10 * 0.8) = 8 → nearing, but not yet exceeded.
await seedQuotaState({
organisationId: organisation.id,
organisationClaimId: organisation.organisationClaim.id,
subscriptionClaimId: INTERNAL_CLAIM_ID.TEAM,
documentQuota: 10,
documentCount: 8,
});
await apiSignin({
page,
email: user.email,
redirectPath: `/o/${organisation.url}/settings/general`,
});
await expect(page.getByText(BANNER_NEARING_TEXT)).toBeVisible();
await expect(page.getByText(BANNER_EXCEEDED_TEXT)).toBeHidden();
});
test('[QUOTA BANNER]: shows the exceeded state when a quota is reached', async ({ page }) => {
const { user, organisation } = await seedUser({
isPersonalOrganisation: false,
});
// usage >= quota → exceeded.
await seedQuotaState({
organisationId: organisation.id,
organisationClaimId: organisation.organisationClaim.id,
subscriptionClaimId: INTERNAL_CLAIM_ID.TEAM,
documentQuota: 10,
documentCount: 10,
});
await apiSignin({
page,
email: user.email,
redirectPath: `/o/${organisation.url}/settings/general`,
});
await expect(page.getByText(BANNER_EXCEEDED_TEXT)).toBeVisible();
await expect(page.getByText(BANNER_NEARING_TEXT)).toBeHidden();
});
test('[QUOTA BANNER]: learn more dialog lists the affected counter', async ({ page }) => {
const { user, organisation } = await seedUser({
isPersonalOrganisation: false,
});
await seedQuotaState({
organisationId: organisation.id,
organisationClaimId: organisation.organisationClaim.id,
subscriptionClaimId: INTERNAL_CLAIM_ID.TEAM,
documentQuota: 10,
documentCount: 10,
});
await apiSignin({
page,
email: user.email,
redirectPath: `/o/${organisation.url}/settings/general`,
});
await page.getByRole('button', { name: 'Learn more' }).click();
const dialog = page.getByRole('dialog');
await expect(dialog.getByText('Fair use limit exceeded')).toBeVisible();
await expect(dialog.getByText('Document creation has been temporarily paused')).toBeVisible();
await expect(dialog.getByRole('link', { name: 'support' })).toHaveAttribute('href', /^mailto:/);
});
test('[QUOTA BANNER]: is hidden for free-claim organisations', async ({ page }) => {
const { user, organisation } = await seedUser({
isPersonalOrganisation: false,
});
// Usage is exceeded, but a free-claim organisation must never see the banner.
await seedQuotaState({
organisationId: organisation.id,
organisationClaimId: organisation.organisationClaim.id,
subscriptionClaimId: INTERNAL_CLAIM_ID.FREE,
documentQuota: 10,
documentCount: 10,
});
await apiSignin({
page,
email: user.email,
redirectPath: `/o/${organisation.url}/settings/general`,
});
// Anchor on a stable element so banner-absence is meaningful (page fully loaded).
await expect(page.getByLabel('Organisation Name*')).toBeVisible();
await expect(page.getByText(BANNER_EXCEEDED_TEXT)).toBeHidden();
await expect(page.getByRole('button', { name: 'Learn more' })).toBeHidden();
});