mirror of
https://github.com/hanzoai/sign.git
synced 2026-11-02 23:26:15 +00:00
Emails, the signing page's title, the sealed PDF's reason, the sign-in refusal and the dialogs still said Hanzo Sign. The catalogs change with the strings, message for message, so every translation keeps its id.
393 lines
13 KiB
TypeScript
393 lines
13 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
import fs from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
|
|
import type { TCachedLicense } from '@hanzo/esign-lib/types/license';
|
|
import { seedUser } from '@hanzo/esign-prisma/seed/users';
|
|
|
|
import { apiSignin } from '../fixtures/authentication';
|
|
|
|
const LICENSE_FILE_NAME = '.sign-license.json';
|
|
const LICENSE_BACKUP_FILE_NAME = '.sign-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/.sign-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 status and unauthorized flag.
|
|
*/
|
|
const createMockLicense = (
|
|
status: 'ACTIVE' | 'EXPIRED' | 'PAST_DUE',
|
|
unauthorizedFlagUsage: boolean,
|
|
): TCachedLicense => {
|
|
return {
|
|
lastChecked: new Date().toISOString(),
|
|
license: {
|
|
status,
|
|
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: unauthorizedFlagUsage ? 'UNAUTHORIZED' : status,
|
|
unauthorizedFlagUsage,
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Create a mock license object with no license data (only unauthorized flag).
|
|
*/
|
|
const createMockUnauthorizedWithoutLicense = (): TCachedLicense => {
|
|
return {
|
|
lastChecked: new Date().toISOString(),
|
|
license: null,
|
|
unauthorizedFlagUsage: true,
|
|
derivedStatus: 'UNAUTHORIZED',
|
|
};
|
|
};
|
|
|
|
// 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('License Status Banner', () => {
|
|
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]: no banner when license file is missing', async ({ page }) => {
|
|
// Ensure no license file exists BEFORE any page loads
|
|
await writeLicenseFile(null);
|
|
|
|
const { user: adminUser } = await seedUser({
|
|
isAdmin: true,
|
|
});
|
|
|
|
// Navigate to admin page - license is read during page load
|
|
await apiSignin({
|
|
page,
|
|
email: adminUser.email,
|
|
redirectPath: '/admin',
|
|
});
|
|
|
|
// Verify we're on the admin page
|
|
await expect(page.getByRole('heading', { name: 'Admin Panel' })).toBeVisible();
|
|
|
|
// Global banner should not be visible (no license file)
|
|
await expect(
|
|
page.getByText('This is an expired license instance of Hanzo eSign'),
|
|
).not.toBeVisible();
|
|
|
|
// Admin banner messages should not be visible (no license file means no banner)
|
|
await expect(page.getByText('License payment overdue')).not.toBeVisible();
|
|
await expect(page.getByText('License expired')).not.toBeVisible();
|
|
await expect(page.getByText('Invalid License Type')).not.toBeVisible();
|
|
await expect(page.getByText('Missing License')).not.toBeVisible();
|
|
});
|
|
|
|
test('[ADMIN]: no banner when license is ACTIVE', async ({ page }) => {
|
|
// Create an ACTIVE license BEFORE any page loads
|
|
await writeLicenseFile(createMockLicense('ACTIVE', false));
|
|
|
|
const { user: adminUser } = await seedUser({
|
|
isAdmin: true,
|
|
});
|
|
|
|
// Navigate to admin page - license is read during page load
|
|
await apiSignin({
|
|
page,
|
|
email: adminUser.email,
|
|
redirectPath: '/admin',
|
|
});
|
|
|
|
// Verify we're on the admin page
|
|
await expect(page.getByRole('heading', { name: 'Admin Panel' })).toBeVisible();
|
|
|
|
// Global banner should not be visible (license is ACTIVE)
|
|
await expect(
|
|
page.getByText('This is an expired license instance of Hanzo eSign'),
|
|
).not.toBeVisible();
|
|
|
|
// Admin banner messages should not be visible (license is ACTIVE)
|
|
await expect(page.getByText('License payment overdue')).not.toBeVisible();
|
|
await expect(page.getByText('License expired')).not.toBeVisible();
|
|
await expect(page.getByText('Invalid License Type')).not.toBeVisible();
|
|
});
|
|
|
|
test('[ADMIN]: admin banner shows PAST_DUE warning', async ({ page }) => {
|
|
// Create a PAST_DUE license BEFORE any page loads
|
|
await writeLicenseFile(createMockLicense('PAST_DUE', false));
|
|
|
|
const { user: adminUser } = await seedUser({
|
|
isAdmin: true,
|
|
});
|
|
|
|
// Navigate to admin page - license is read during page load
|
|
await apiSignin({
|
|
page,
|
|
email: adminUser.email,
|
|
redirectPath: '/admin',
|
|
});
|
|
|
|
// Verify we're on the admin page
|
|
await expect(page.getByRole('heading', { name: 'Admin Panel' })).toBeVisible();
|
|
|
|
// Global banner should NOT be visible (only shows for EXPIRED + unauthorized)
|
|
await expect(
|
|
page.getByText('This is an expired license instance of Hanzo eSign'),
|
|
).not.toBeVisible();
|
|
|
|
// Admin banner should show PAST_DUE message
|
|
await expect(page.getByText('License payment overdue')).toBeVisible();
|
|
await expect(
|
|
page.getByText('Please update your payment to avoid service disruptions.'),
|
|
).toBeVisible();
|
|
|
|
// Should have the "See Documentation" link
|
|
await expect(page.getByRole('link', { name: 'See Documentation' })).toBeVisible();
|
|
});
|
|
|
|
test('[ADMIN]: admin banner shows EXPIRED error', async ({ page }) => {
|
|
// Create an EXPIRED license WITHOUT unauthorized usage BEFORE any page loads
|
|
await writeLicenseFile(createMockLicense('EXPIRED', false));
|
|
|
|
const { user: adminUser } = await seedUser({
|
|
isAdmin: true,
|
|
});
|
|
|
|
// Navigate to admin page - license is read during page load
|
|
await apiSignin({
|
|
page,
|
|
email: adminUser.email,
|
|
redirectPath: '/admin',
|
|
});
|
|
|
|
// Verify we're on the admin page
|
|
await expect(page.getByRole('heading', { name: 'Admin Panel' })).toBeVisible();
|
|
|
|
// Global banner should NOT be visible (requires BOTH expired AND unauthorized)
|
|
await expect(
|
|
page.getByText('This is an expired license instance of Hanzo eSign'),
|
|
).not.toBeVisible();
|
|
|
|
// Admin banner should show EXPIRED message
|
|
await expect(page.getByText('License expired')).toBeVisible();
|
|
await expect(
|
|
page.getByText('Please renew your license to continue using enterprise features.'),
|
|
).toBeVisible();
|
|
|
|
// Should have the "See Documentation" link
|
|
await expect(page.getByRole('link', { name: 'See Documentation' })).toBeVisible();
|
|
});
|
|
|
|
test.skip('[ADMIN]: global banner shows when EXPIRED with unauthorized usage', async ({
|
|
page,
|
|
}) => {
|
|
// Create an EXPIRED license WITH unauthorized usage BEFORE any page loads
|
|
await writeLicenseFile(createMockLicense('EXPIRED', true));
|
|
|
|
const { user: adminUser } = await seedUser({
|
|
isAdmin: true,
|
|
});
|
|
|
|
// Navigate to admin page - license is read during page load
|
|
await apiSignin({
|
|
page,
|
|
email: adminUser.email,
|
|
redirectPath: '/admin',
|
|
});
|
|
|
|
// Verify we're on the admin page
|
|
await expect(page.getByRole('heading', { name: 'Admin Panel' })).toBeVisible();
|
|
|
|
// Global banner SHOULD be visible (EXPIRED + unauthorized)
|
|
await expect(page.getByText('This is an expired license instance of Hanzo eSign')).toBeVisible();
|
|
|
|
// Admin banner should show UNAUTHORIZED message (takes precedence over EXPIRED)
|
|
await expect(page.getByText('Invalid License Type')).toBeVisible();
|
|
await expect(
|
|
page.getByText(
|
|
'Your Hanzo eSign instance is using features that are not part of your license.',
|
|
),
|
|
).toBeVisible();
|
|
});
|
|
|
|
test('[ADMIN]: admin banner shows UNAUTHORIZED when flags are misused with license', async ({
|
|
page,
|
|
}) => {
|
|
// Create an ACTIVE license but WITH unauthorized flag usage BEFORE any page loads
|
|
await writeLicenseFile(createMockLicense('ACTIVE', true));
|
|
|
|
const { user: adminUser } = await seedUser({
|
|
isAdmin: true,
|
|
});
|
|
|
|
// Navigate to admin page - license is read during page load
|
|
await apiSignin({
|
|
page,
|
|
email: adminUser.email,
|
|
redirectPath: '/admin',
|
|
});
|
|
|
|
// Verify we're on the admin page
|
|
await expect(page.getByRole('heading', { name: 'Admin Panel' })).toBeVisible();
|
|
|
|
// Global banner should NOT be visible (requires EXPIRED status)
|
|
await expect(
|
|
page.getByText('This is an expired license instance of Hanzo eSign'),
|
|
).not.toBeVisible();
|
|
|
|
// Admin banner should show UNAUTHORIZED message
|
|
await expect(page.getByText('Invalid License Type')).toBeVisible();
|
|
await expect(
|
|
page.getByText(
|
|
'Your Hanzo eSign instance is using features that are not part of your license.',
|
|
),
|
|
).toBeVisible();
|
|
|
|
// Should have the "See Documentation" link
|
|
await expect(page.getByRole('link', { name: 'See Documentation' })).toBeVisible();
|
|
});
|
|
|
|
test('[ADMIN]: admin banner shows Invalid License Type when unauthorized without license data', async ({
|
|
page,
|
|
}) => {
|
|
// Create a license file with unauthorized flag but no license data BEFORE any page loads
|
|
// Note: Even without license data, the banner shows "Invalid License Type" because the
|
|
// license file exists (just with license: null). The "Missing License" message would only
|
|
// show if the entire license prop was null, which doesn't happen with a valid file.
|
|
await writeLicenseFile(createMockUnauthorizedWithoutLicense());
|
|
|
|
const { user: adminUser } = await seedUser({
|
|
isAdmin: true,
|
|
});
|
|
|
|
// Navigate to admin page - license is read during page load
|
|
await apiSignin({
|
|
page,
|
|
email: adminUser.email,
|
|
redirectPath: '/admin',
|
|
});
|
|
|
|
// Verify we're on the admin page
|
|
await expect(page.getByRole('heading', { name: 'Admin Panel' })).toBeVisible();
|
|
|
|
// Global banner should NOT be visible (no EXPIRED status, only unauthorized flag)
|
|
await expect(
|
|
page.getByText('This is an expired license instance of Hanzo eSign'),
|
|
).not.toBeVisible();
|
|
|
|
// Admin banner should show Invalid License Type message (unauthorized flag is set)
|
|
await expect(page.getByText('Invalid License Type')).toBeVisible();
|
|
await expect(
|
|
page.getByText(
|
|
'Your Hanzo eSign instance is using features that are not part of your license.',
|
|
),
|
|
).toBeVisible();
|
|
|
|
// Should have the "See Documentation" link
|
|
await expect(page.getByRole('link', { name: 'See Documentation' })).toBeVisible();
|
|
});
|
|
|
|
test.skip('[ADMIN]: global banner visible on non-admin pages when EXPIRED with unauthorized', async ({
|
|
page,
|
|
}) => {
|
|
// Create an EXPIRED license WITH unauthorized usage BEFORE any page loads
|
|
await writeLicenseFile(createMockLicense('EXPIRED', true));
|
|
|
|
const { user } = await seedUser();
|
|
|
|
// Navigate to documents page - license is read during page load
|
|
await apiSignin({
|
|
page,
|
|
email: user.email,
|
|
redirectPath: '/documents',
|
|
});
|
|
|
|
// Global banner SHOULD be visible on any authenticated page (EXPIRED + unauthorized)
|
|
await expect(page.getByText('This is an expired license instance of Hanzo eSign')).toBeVisible();
|
|
});
|
|
});
|