import fs from 'node:fs/promises'; import path from 'node:path'; import type { TCachedLicense, TLicenseClaim } from '@documenso/lib/types/license'; import { seedUser } from '@documenso/prisma/seed/users'; import { expect, test } from '@playwright/test'; import { apiSignin } from '../fixtures/authentication'; const LICENSE_FILE_NAME = '.documenso-license.json'; const LICENSE_BACKUP_FILE_NAME = '.documenso-license-backup.json'; /** * Get the path to the license file. * * The server reads from process.cwd() which is apps/remix when the dev server runs. * Tests run from packages/app-tests, so we need to go up to the root then into apps/remix. */ const getLicenseFilePath = () => { // From packages/app-tests/e2e/license -> ../../../../apps/remix/.documenso-license.json return path.join(__dirname, '../../../../apps/remix', LICENSE_FILE_NAME); }; /** * Get the path to the backup license file. */ const getBackupLicenseFilePath = () => { return path.join(__dirname, '../../../../apps/remix', LICENSE_BACKUP_FILE_NAME); }; /** * Backup the existing license file if it exists. */ const backupLicenseFile = async () => { const licensePath = getLicenseFilePath(); const backupPath = getBackupLicenseFilePath(); try { await fs.access(licensePath); await fs.rename(licensePath, backupPath); } catch (e) { // File doesn't exist, nothing to backup console.log(e); } }; /** * Restore the backup license file if it exists. */ const restoreLicenseFile = async () => { const licensePath = getLicenseFilePath(); const backupPath = getBackupLicenseFilePath(); try { await fs.access(backupPath); await fs.rename(backupPath, licensePath); } catch (e) { // Backup doesn't exist, nothing to restore console.log(e); } }; /** * Write a license file with the given data. * Pass null to delete the license file. */ const writeLicenseFile = async (data: TCachedLicense | null) => { const licensePath = getLicenseFilePath(); if (data === null) { await fs.unlink(licensePath).catch(() => { // File doesn't exist, ignore }); } else { await fs.writeFile(licensePath, JSON.stringify(data, null, 2), 'utf-8'); } }; /** * Create a mock license object with the given flags. */ const createMockLicenseWithFlags = (flags: TLicenseClaim): TCachedLicense => { return { lastChecked: new Date().toISOString(), license: { status: 'ACTIVE', createdAt: new Date(), name: 'Test License', periodEnd: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days from now cancelAtPeriodEnd: false, licenseKey: 'test-license-key', flags, }, requestedLicenseKey: 'test-license-key', derivedStatus: 'ACTIVE', unauthorizedFlagUsage: false, }; }; // Run tests serially to avoid race conditions with the license file test.describe.configure({ mode: 'serial' }); // SKIPPING TEST UNTIL WE ADD A WAY TO OVERRIDE THE LICENSE FILE. test.describe .skip('Enterprise Feature Restrictions', () => { test.beforeAll(async () => { // Backup any existing license file before running tests await backupLicenseFile(); }); test.afterAll(async () => { // Restore the backup license file after all tests complete await restoreLicenseFile(); }); test.beforeEach(async () => { // Clean up license file before each test to ensure clean state await writeLicenseFile(null); }); test.afterEach(async () => { // Clean up license file after each test await writeLicenseFile(null); }); test('[ADMIN CLAIMS]: shows restricted features with asterisk when no license', async ({ page }) => { // Ensure no license file exists await writeLicenseFile(null); const { user: adminUser } = await seedUser({ isAdmin: true, }); await apiSignin({ page, email: adminUser.email, redirectPath: '/admin/claims', }); // Click Create claim button to open the dialog await page.getByRole('button', { name: 'Create claim' }).click(); // Wait for dialog to open await expect(page.getByRole('dialog')).toBeVisible(); // Check that enterprise features have asterisks (are restricted) // These are the enterprise features that should be marked with * await expect(page.getByText(/Email domains\s¹/)).toBeVisible(); await expect(page.getByText(/Embed authoring\s¹/)).toBeVisible(); await expect(page.getByText(/White label for embed authoring\s¹/)).toBeVisible(); await expect(page.getByText(/21 CFR\s¹/)).toBeVisible(); await expect(page.getByText(/Authentication portal\s¹/)).toBeVisible(); // Check that the alert is visible await expect(page.getByText('Your current license does not include these features.')).toBeVisible(); await expect(page.getByRole('link', { name: 'Learn more' })).toBeVisible(); // Check that enterprise feature checkboxes are disabled const emailDomainsCheckbox = page.locator('#flag-emailDomains'); await expect(emailDomainsCheckbox).toBeDisabled(); const cfr21Checkbox = page.locator('#flag-cfr21'); await expect(cfr21Checkbox).toBeDisabled(); const authPortalCheckbox = page.locator('#flag-authenticationPortal'); await expect(authPortalCheckbox).toBeDisabled(); }); test('[ADMIN CLAIMS]: no restrictions when license has all enterprise features', async ({ page }) => { // Create a license with ALL enterprise features enabled await writeLicenseFile( createMockLicenseWithFlags({ emailDomains: true, embedAuthoring: true, embedAuthoringWhiteLabel: true, cfr21: true, authenticationPortal: true, billing: true, }), ); const { user: adminUser } = await seedUser({ isAdmin: true, }); await apiSignin({ page, email: adminUser.email, redirectPath: '/admin/claims', }); // Click Create claim button to open the dialog await page.getByRole('button', { name: 'Create claim' }).click(); // Wait for dialog to open await expect(page.getByRole('dialog')).toBeVisible(); // Check that enterprise features do NOT have asterisks // They should show without the * since the license covers them await expect(page.getByText(/Email domains\s¹/)).not.toBeVisible(); await expect(page.getByText(/Embed authoring\s¹/)).not.toBeVisible(); await expect(page.getByText(/21 CFR\s¹/)).not.toBeVisible(); await expect(page.getByText(/Authentication portal\s¹/)).not.toBeVisible(); // The plain labels should be visible (without asterisks) await expect(page.locator('label[for="flag-emailDomains"]')).toContainText('Email domains'); await expect(page.locator('label[for="flag-cfr21"]')).toContainText('21 CFR'); // The alert should NOT be visible await expect(page.getByText('Your current license does not include these features.')).not.toBeVisible(); // Check that enterprise feature checkboxes are enabled const emailDomainsCheckbox = page.locator('#flag-emailDomains'); await expect(emailDomainsCheckbox).toBeEnabled(); const cfr21Checkbox = page.locator('#flag-cfr21'); await expect(cfr21Checkbox).toBeEnabled(); const authPortalCheckbox = page.locator('#flag-authenticationPortal'); await expect(authPortalCheckbox).toBeEnabled(); }); test('[ADMIN CLAIMS]: only unlicensed features show asterisk with partial license', async ({ page }) => { // Create a license with SOME enterprise features (emailDomains and cfr21) await writeLicenseFile( createMockLicenseWithFlags({ emailDomains: true, cfr21: true, // embedAuthoring, embedAuthoringWhiteLabel, authenticationPortal are NOT included }), ); const { user: adminUser } = await seedUser({ isAdmin: true, }); await apiSignin({ page, email: adminUser.email, redirectPath: '/admin/claims', }); // Click Create claim button to open the dialog await page.getByRole('button', { name: 'Create claim' }).click(); // Wait for dialog to open await expect(page.getByRole('dialog')).toBeVisible(); // Features NOT in license should have asterisks await expect(page.getByText(/Embed authoring\s¹/)).toBeVisible(); await expect(page.getByText(/White label for embed authoring\s¹/)).toBeVisible(); await expect(page.getByText(/Authentication portal\s¹/)).toBeVisible(); // Features IN license should NOT have asterisks await expect(page.getByText(/Email domains\s¹/)).not.toBeVisible(); await expect(page.getByText(/21 CFR\s¹/)).not.toBeVisible(); // The plain labels for licensed features should be visible await expect(page.locator('label[for="flag-emailDomains"]')).toContainText('Email domains'); await expect(page.locator('label[for="flag-cfr21"]')).toContainText('21 CFR'); // Alert should be visible since some features are restricted await expect(page.getByText('Your current license does not include these features.')).toBeVisible(); // Licensed features should be enabled const emailDomainsCheckbox = page.locator('#flag-emailDomains'); await expect(emailDomainsCheckbox).toBeEnabled(); const cfr21Checkbox = page.locator('#flag-cfr21'); await expect(cfr21Checkbox).toBeEnabled(); // Unlicensed features should be disabled const embedAuthoringCheckbox = page.locator('#flag-embedAuthoring'); await expect(embedAuthoringCheckbox).toBeDisabled(); const authPortalCheckbox = page.locator('#flag-authenticationPortal'); await expect(authPortalCheckbox).toBeDisabled(); }); test('[ADMIN CLAIMS]: non-enterprise features are always enabled', async ({ page }) => { // Ensure no license file exists await writeLicenseFile(null); const { user: adminUser } = await seedUser({ isAdmin: true, }); await apiSignin({ page, email: adminUser.email, redirectPath: '/admin/claims', }); // Click Create claim button to open the dialog await page.getByRole('button', { name: 'Create claim' }).click(); // Wait for dialog to open await expect(page.getByRole('dialog')).toBeVisible(); // Non-enterprise features should NOT have asterisks await expect(page.getByText(/Unlimited documents\s¹/)).not.toBeVisible(); await expect(page.getByText(/Branding\s¹/)).not.toBeVisible(); await expect(page.getByText(/Embed signing\s¹/)).not.toBeVisible(); // Non-enterprise features should always be enabled const unlimitedDocsCheckbox = page.locator('#flag-unlimitedDocuments'); await expect(unlimitedDocsCheckbox).toBeEnabled(); const brandingCheckbox = page.locator('#flag-allowCustomBranding'); await expect(brandingCheckbox).toBeEnabled(); const embedSigningCheckbox = page.locator('#flag-embedSigning'); await expect(embedSigningCheckbox).toBeEnabled(); }); });