Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: download doc without signing certificate #1477

Merged
merged 9 commits into from
Nov 25, 2024

Conversation

catalinpit
Copy link
Contributor

@catalinpit catalinpit commented Nov 15, 2024

Description

I added the option of downloading a document without the signing certificate for teams. They can disable/enable the option in the preferences tab.

The signing certificate can still be downloaded separately from the logs page.

Checklist

  • I have tested these changes locally and they work as expected.
  • I have added/updated tests that prove the effectiveness of these changes.
  • I have updated the documentation to reflect these changes, if applicable.
  • I have followed the project's coding style guidelines.
  • I have addressed the code review feedback from the previous submission, if applicable.

Summary by CodeRabbit

  • New Features

    • Introduced a new option to include a signing certificate in document preferences, enhancing user control over document settings.
    • Added a toggle for the signing certificate in the document preferences form.
  • Bug Fixes

    • Improved logic for document sealing to conditionally include signing certificates based on team settings.
  • Translations

    • Updated translations for German, Spanish, and French to include new entries and improve existing phrases related to document actions and settings.
    • Added new translation for "Include the Signing Certificate in the Document" across multiple languages.

Copy link

vercel bot commented Nov 15, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
prd-app 🔄 Building (Inspect) Visit Preview 💬 Add feedback Nov 25, 2024 3:10am
prd-marketing 🔄 Building (Inspect) Visit Preview 💬 Add feedback Nov 25, 2024 3:10am
prd-openpage-api ❌ Failed (Inspect) Nov 25, 2024 3:10am
stg-app ✅ Ready (Inspect) Visit Preview 💬 Add feedback Nov 25, 2024 3:10am
stg-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Nov 25, 2024 3:10am
stg-marketing ✅ Ready (Inspect) Visit Preview 💬 Add feedback Nov 25, 2024 3:10am
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
prd-docs ⬜️ Ignored (Inspect) Visit Preview Nov 25, 2024 3:10am

Copy link
Contributor

coderabbitai bot commented Nov 15, 2024

Walkthrough

The changes in this pull request introduce a new boolean field, includeSigningCertificate, across various components related to document preferences and settings. This field is added to the validation schema for document preferences, the job definition schema for sending team deletion emails, and the team global settings model. Modifications are made to the document sealing logic to conditionally include the signing certificate based on this new setting. The updates also include enhancements to translation files for various languages to reflect these changes.

Changes

File Path Change Summary
apps/web/src/app/(teams)/t/[teamUrl]/settings/preferences/document-preferences.tsx Added includeSigningCertificate to ZTeamDocumentPreferencesFormSchema, default value set to true, new FormField component for toggling.
packages/lib/jobs/definitions/emails/send-team-deleted-email.ts Added includeSigningCertificate to SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION_SCHEMA.
packages/lib/jobs/definitions/internal/seal-document.ts Enhanced logic to include includeSigningCertificate in document sealing operations.
packages/lib/server-only/document/seal-document.ts Updated sealDocument function to check includeSigningCertificate before appending the signing certificate.
packages/lib/server-only/team/update-team-document-settings.ts Added includeSigningCertificate to UpdateTeamDocumentSettingsOptions and included in database operations.
packages/lib/translations/de/common.po Updated German translations for document actions and added new entries.
packages/lib/translations/de/marketing.po Updated German marketing translations for various phrases.
packages/lib/translations/de/web.po Adjusted line numbers and added new translation for "Include the Signing Certificate in the Document."
packages/lib/translations/en/web.po Adjusted line numbers and added new translation for "Include the Signing Certificate in the Document."
packages/lib/translations/es/common.po Updated Spanish translations for document actions and added new entries.
packages/lib/translations/es/marketing.po Updated Spanish marketing translations for various phrases.
packages/lib/translations/es/web.po Adjusted line numbers and added new translation for "Include the Signing Certificate in the Document."
packages/lib/translations/fr/common.po Updated French translations for document actions and added new entries.
packages/lib/translations/fr/marketing.po Updated French marketing translations for various phrases.
packages/lib/translations/fr/web.po Adjusted line numbers and added new translation for "Include the Signing Certificate in the Document."
packages/prisma/migrations/20241122081012_download_document_without_certificate/migration.sql Added includeSigningCertificate column to TeamGlobalSettings table with default value true.
packages/prisma/schema.prisma Added includeSigningCertificate field to TeamGlobalSettings model with default value true.
packages/trpc/server/team-router/schema.ts Added includeSigningCertificate to ZUpdateTeamDocumentSettingsMutationSchema with default value true.
packages/lib/server-only/htmltopdf/get-certificate-pdf.ts Updated GetCertificatePdfOptions type to accept flexible language types and improved language validation logic.
packages/app-tests/e2e/features/include-document-certificate.spec.ts Introduced end-to-end tests for signing certificate functionality in documents.
packages/app-tests/package.json Updated test:e2e script and added new dependencies for testing.

Possibly related PRs

  • feat: documentation site #1101: The main PR introduces a new boolean field includeSigningCertificate to a form schema, while this PR updates a job definition schema to include the same field, indicating a related change in document preferences.
  • feat: force signature fields for document signers #1139: The main PR enhances document preferences with a new field, and this PR adds a dialog to ensure all required signature fields are present, which may relate to the overall document management process.
  • chore: add more field types #1141: The main PR's changes to document preferences may connect with this PR's addition of more field types, as both involve enhancing document handling features.
  • feat: document visibility #1262: The main PR's focus on document preferences aligns with this PR's introduction of visibility settings for documents, indicating a broader enhancement in document management.
  • feat: signing order #1290: The main PR's updates to document preferences could relate to this PR's feature for signing order, as both involve user interactions with document settings.
  • feat: add french #1355: The main PR's changes to document preferences may connect with this PR's addition of French translations, particularly if the new field requires localization.
  • feat: add certificate translations #1440: The main PR's introduction of new document features may relate to this PR's focus on certificate translations, as both involve document management enhancements.
  • chore: add translations #1444: The main PR's updates to document preferences could be related to this PR's changes in German translations for document actions, ensuring consistency in user experience.
  • feat: add signing link copy #1449: The main PR's enhancements to document preferences may connect with this PR's addition of signing link copy functionality, as both improve document interaction features.
  • fix: certificate translations #1460: The main PR's updates to document preferences could relate to this PR's focus on fixing certificate translations, as both involve document-related functionalities.
  • chore: add translations #1461: The main PR's changes to document preferences may connect with this PR's updates to German translations, ensuring that new features are accurately represented in the language.
  • chore: add translations #1463: The main PR's updates to document preferences could relate to this PR's addition of Polish translations, enhancing the application's multilingual capabilities.
  • feat: automatically sign fields in large documents #1484: The main PR's focus on document preferences may connect with this PR's feature for automatically signing fields, as both enhance document handling processes.
  • chore: add translations #1485: The main PR's updates to document preferences could relate to this PR's changes in translations, ensuring that new features are accurately represented in multiple languages.

Suggested reviewers

  • dguyen

🐰 In the meadow, where documents lay,
A new toggle is here, hip-hip-hooray!
With signing certificates, we now can choose,
Preferences set, no more to lose!
So hop along, let’s make it right,
Document dreams take flight tonight! 🌙📄

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 eslint (1.23.1)

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/lib/server-only/htmltopdf/get-certificate-pdf.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the config "@documenso/eslint-config" to extend from. Please check that the name of the config is correct.

The config "@documenso/eslint-config" was referenced from the config file in "/.eslintrc.cjs".

If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added the apps: web Issues related to the webapp label Nov 15, 2024
Copy link

🚨 @catalinpit has 7 pull requests awaiting review. Please consider reviewing them when possible. 🚨

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 2 out of 2 changed files in this pull request and generated no suggestions.

Copy link

🚨 @catalinpit has 6 pull requests awaiting review. Please consider reviewing them when possible. 🚨

@@ -127,7 +136,11 @@ export const SEAL_DOCUMENT_JOB_DEFINITION = {
flattenForm(pdfDoc);
flattenAnnotations(pdfDoc);

if (certificateData) {
if (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably could have been a ternary on certificateData e.g.

const certificateData =
      document.team?.teamGlobalSettings?.includeSignerCertificate === false
        ? null
        : await getCertificatePdf({ documentId }).catch(() => null);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then you wouldn't need the below which seems like it would break on individual documents anyways 🤔

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
packages/lib/server-only/htmltopdf/get-certificate-pdf.ts (2)

10-11: Justify the ESLint disable comment

The @typescript-eslint/ban-types disable comment needs explanation. Consider adding a comment explaining why this exception is necessary, or explore alternative type definitions that don't require disabling the lint rule.


42-43: Add logging for invalid language codes

When an invalid language code is provided, it silently falls back to 'en'. Consider adding debug logging to help track such occurrences.

- const lang = isValidLanguageCode(language) ? language : 'en';
+ const lang = isValidLanguageCode(language) ? language : 'en';
+ if (language && !isValidLanguageCode(language)) {
+   console.debug(`Invalid language code provided: ${language}, falling back to 'en'`);
+ }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 119b060 and e129e5a.

📒 Files selected for processing (3)
  • packages/lib/jobs/definitions/internal/seal-document.ts (2 hunks)
  • packages/lib/server-only/document/seal-document.ts (3 hunks)
  • packages/lib/server-only/htmltopdf/get-certificate-pdf.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/lib/jobs/definitions/internal/seal-document.ts
  • packages/lib/server-only/document/seal-document.ts
🔇 Additional comments (1)
packages/lib/server-only/htmltopdf/get-certificate-pdf.ts (1)

Line range hint 14-66: Verify the necessity of certificate generation

Based on the PR objectives, this function should only be called when signing certificates are required. Let's verify that the calling code properly handles this condition.

✅ Verification successful

Let me gather more context about the condition check.


Certificate generation is properly conditioned by team settings

The code correctly checks document.team?.teamGlobalSettings?.includeSigningCertificate with a fallback to true before calling getCertificatePdf. This ensures certificates are only generated when required by the team's settings.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for calls to getCertificatePdf to ensure they respect the includeSigningCertificate setting
rg -A 5 "getCertificatePdf" | grep -v "get-certificate-pdf.ts"

# Search for the condition check before calling this function
ast-grep --pattern 'if ($CONDITION) {
  $$$
  getCertificatePdf($$$)
  $$$
}'

Length of output: 2674


Script:

#!/bin/bash
# Search for the condition that determines if certificate generation is needed
rg -B 5 -A 2 "getCertificatePdf\(" packages/lib/server-only/document/seal-document.ts packages/lib/jobs/definitions/internal/seal-document.ts

Length of output: 1598

Comment on lines 44 to 50
if (language) {
await page.context().addCookies([
{
name: 'language',
value: language,
value: lang,
url: NEXT_PUBLIC_WEBAPP_URL(),
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Optimize language cookie setting logic

The code sets the language cookie even when the provided language is invalid. Consider only setting the cookie when we have a valid language code.

- if (language) {
+ if (language && isValidLanguageCode(language)) {
   await page.context().addCookies([
     {
       name: 'language',
       value: lang,
       url: NEXT_PUBLIC_WEBAPP_URL(),
     },
   ]);
 }

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (2)
packages/app-tests/e2e/features/include-document-certificate.spec.ts (2)

157-232: Improve test reliability by using explicit wait conditions

The test relies on implicit timing which could lead to flaky tests.

Consider using explicit wait conditions instead of relying on URL changes:

-    await page.waitForURL(`/sign/${recipient.token}/complete`);
+    await Promise.all([
+      page.waitForURL(`/sign/${recipient.token}/complete`),
+      page.waitForResponse(response => 
+        response.url().includes('/api/documents') && 
+        response.status() === 200
+      )
+    ]);

1-271: Good test coverage with room for improvement

The tests comprehensively cover the signing certificate feature, including both individual and team document scenarios. While functional, the implementation could benefit from:

  1. Helper functions to reduce code duplication
  2. More robust wait conditions instead of timeouts
  3. Better error handling for canvas interactions
  4. Additional edge cases:
    • Document with multiple signers
    • Handling of large documents
    • Network error scenarios

Consider creating a shared test utilities file for common operations like document signing simulation and status verification.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e129e5a and d7ff1cf.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • packages/app-tests/e2e/features/include-document-certificate.spec.ts (1 hunks)
  • packages/app-tests/package.json (1 hunks)
🔇 Additional comments (3)
packages/app-tests/package.json (2)

17-20: Review dependency versioning strategy

A few observations about the dependencies:

  1. Internal dependencies (@documenso/*) use * which is typical for monorepos
  2. pdf-lib is pinned to ^1.17.1 which is good for external dependencies

Let's verify if pdf-lib version is the latest stable and check for any security advisories:

✅ Verification successful

Dependency version is up-to-date and secure

The version of pdf-lib (^1.17.1) used in the project is:

  • Currently the latest stable version available on npm
  • Has no known security vulnerabilities according to GitHub's advisory database
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check latest version and security advisories for pdf-lib

# Check NPM for latest versions
curl -s https://registry.npmjs.org/pdf-lib | jq '.["dist-tags"].latest'

# Check for security advisories
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "pdf-lib") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 423


10-10: LGTM: Test script enhancement

The addition of $E2E_TEST_PATH variable allows for more flexible test execution by enabling specific test file targeting.

Let's verify the existence of the referenced E2E tests:

✅ Verification successful

E2E tests are present and properly implemented

The review comment's concern has been addressed as there are comprehensive E2E tests for the document certificate functionality in packages/app-tests/e2e/features/include-document-certificate.spec.ts. The tests cover:

  • Individual documents with signing certificates
  • Team documents with signing certificates enabled/disabled
  • Team settings for toggling signing certificates

The $E2E_TEST_PATH variable in the test script is well justified as it allows running specific test files, which is particularly useful for these new certificate-related tests.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for E2E test files related to document certificate functionality

# Look for E2E test files
fd . "packages/app-tests/e2e" -e ts -e tsx

Length of output: 1508


Script:

#!/bin/bash
# Let's check the content of the include-document-certificate.spec.ts file
cat "packages/app-tests/e2e/features/include-document-certificate.spec.ts"

Length of output: 8808

packages/app-tests/e2e/features/include-document-certificate.spec.ts (1)

1-13: LGTM! Well-organized imports

The imports are properly organized, separating external dependencies from internal ones.

Comment on lines +80 to +155
test('team document with signing certificate enabled should include certificate', async ({
page,
}) => {
const team = await seedTeam();

const { document, recipients } = await seedPendingDocumentWithFullFields({
owner: team.owner,
recipients: ['signer@example.com'],
fields: [FieldType.SIGNATURE],
updateDocumentOptions: {
teamId: team.id,
},
});

await prisma.teamGlobalSettings.create({
data: {
teamId: team.id,
includeSigningCertificate: true,
},
});

const documentData = await prisma.documentData
.findFirstOrThrow({
where: {
id: document.documentDataId,
},
})
.then(async (data) => getFile(data));

const originalPdf = await PDFDocument.load(documentData);

const recipient = recipients[0];

// Sign the document
await page.goto(`/sign/${recipient.token}`);

const canvas = page.locator('canvas');
const box = await canvas.boundingBox();
if (box) {
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
await page.mouse.down();
await page.mouse.move(box.x + box.width / 4, box.y + box.height / 4);
await page.mouse.up();
}

for (const field of recipient.Field) {
await page.locator(`#field-${field.id}`).getByRole('button').click();

await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
}

await page.getByRole('button', { name: 'Complete' }).click();
await page.getByRole('button', { name: 'Sign' }).click();
await page.waitForURL(`/sign/${recipient.token}/complete`);

await expect(async () => {
const { status } = await getDocumentByToken({
token: recipient.token,
});

expect(status).toBe(DocumentStatus.COMPLETED);
}).toPass();

// Get the completed document
const completedDocument = await prisma.document.findFirstOrThrow({
where: { id: document.id },
include: { documentData: true },
});

const completedDocumentData = await getFile(completedDocument.documentData);

// Load the PDF and check number of pages
const completedPdf = await PDFDocument.load(completedDocumentData);

expect(completedPdf.getPageCount()).toBe(originalPdf.getPageCount() + 1); // Original + Certificate
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Reduce code duplication by extracting common test logic

There's significant duplication in the document signing simulation logic across all tests.

Consider extracting common functionality into helper functions:

async function simulateSignature(page: Page, canvas: Locator) {
  await canvas.waitFor({ state: 'visible' });
  const box = await canvas.boundingBox();
  if (!box) {
    throw new Error('Canvas not found');
  }
  
  // Signature simulation logic...
}

async function completeDocumentSigning(page: Page, recipient: any) {
  await page.goto(`/sign/${recipient.token}`);
  
  // Sign document
  await simulateSignature(page, page.locator('canvas'));
  
  // Fill fields
  for (const field of recipient.Field) {
    await page.locator(`#field-${field.id}`).getByRole('button').click();
    await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
  }
  
  // Complete signing
  await page.getByRole('button', { name: 'Complete' }).click();
  await page.getByRole('button', { name: 'Sign' }).click();
  await page.waitForURL(`/sign/${recipient.token}/complete`);
}

async function verifyDocumentStatus(recipient: any) {
  await expect(async () => {
    const { status } = await getDocumentByToken({ token: recipient.token });
    expect(status).toBe(DocumentStatus.COMPLETED);
  }).toPass();
}

Comment on lines +234 to +271
test('team can toggle signing certificate setting', async ({ page }) => {
const team = await seedTeam();

await apiSignin({
page,
email: team.owner.email,
redirectPath: `/t/${team.url}/settings/preferences`,
});

// Toggle signing certificate setting
await page.getByLabel('Include the Signing Certificate in the Document').click();
await page.getByRole('button', { name: /Save/ }).first().click();

await page.waitForTimeout(1000);

// Verify the setting was saved
const updatedTeam = await prisma.team.findFirstOrThrow({
where: { id: team.id },
include: { teamGlobalSettings: true },
});

expect(updatedTeam.teamGlobalSettings?.includeSigningCertificate).toBe(false);

// Toggle the setting back to true
await page.getByLabel('Include the Signing Certificate in the Document').click();
await page.getByRole('button', { name: /Save/ }).first().click();

await page.waitForTimeout(1000);

// Verify the setting was saved
const updatedTeam2 = await prisma.team.findFirstOrThrow({
where: { id: team.id },
include: { teamGlobalSettings: true },
});

expect(updatedTeam2.teamGlobalSettings?.includeSigningCertificate).toBe(true);
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace arbitrary timeouts with proper wait conditions

The test uses waitForTimeout which could make it flaky.

Consider using network or state conditions instead:

-    await page.waitForTimeout(1000);
+    await page.waitForResponse(response => 
+      response.url().includes('/api/teams') && 
+      response.status() === 200
+    );

Also, consider adding assertions for the UI state:

await expect(
  page.getByLabel('Include the Signing Certificate in the Document')
).toBeChecked({ checked: false });

Comment on lines +15 to +78
test('individual document should always include signing certificate', async ({ page }) => {
const user = await seedUser();

const { document, recipients } = await seedPendingDocumentWithFullFields({
owner: user,
recipients: ['signer@example.com'],
fields: [FieldType.SIGNATURE],
});

const documentData = await prisma.documentData
.findFirstOrThrow({
where: {
id: document.documentDataId,
},
})
.then(async (data) => getFile(data));

const originalPdf = await PDFDocument.load(documentData);

const recipient = recipients[0];

// Sign the document
await page.goto(`/sign/${recipient.token}`);

const canvas = page.locator('canvas');
const box = await canvas.boundingBox();
if (box) {
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
await page.mouse.down();
await page.mouse.move(box.x + box.width / 4, box.y + box.height / 4);
await page.mouse.up();
}

for (const field of recipient.Field) {
await page.locator(`#field-${field.id}`).getByRole('button').click();

await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
}

await page.getByRole('button', { name: 'Complete' }).click();
await page.getByRole('button', { name: 'Sign' }).click();
await page.waitForURL(`/sign/${recipient.token}/complete`);

await expect(async () => {
const { status } = await getDocumentByToken({
token: recipient.token,
});

expect(status).toBe(DocumentStatus.COMPLETED);
}).toPass();

// Get the completed document
const completedDocument = await prisma.document.findFirstOrThrow({
where: { id: document.id },
include: { documentData: true },
});

const completedDocumentData = await getFile(completedDocument.documentData);

// Load the PDF and check number of pages
const pdfDoc = await PDFDocument.load(completedDocumentData);

expect(pdfDoc.getPageCount()).toBe(originalPdf.getPageCount() + 1); // Original + Certificate
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider making canvas interaction more robust

The canvas interaction for signature simulation might be flaky due to timing and positioning issues.

Consider these improvements:

-    const box = await canvas.boundingBox();
-    if (box) {
-      await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
-      await page.mouse.down();
-      await page.mouse.move(box.x + box.width / 4, box.y + box.height / 4);
-      await page.mouse.up();
-    }
+    await canvas.waitFor({ state: 'visible' });
+    const box = await canvas.boundingBox();
+    if (!box) {
+      throw new Error('Canvas not found');
+    }
+    
+    // Center point
+    const centerX = box.x + box.width / 2;
+    const centerY = box.y + box.height / 2;
+    
+    await page.mouse.move(centerX, centerY);
+    await page.mouse.down();
+    await page.waitForTimeout(100); // Ensure mouse down is registered
+    
+    // Draw signature
+    const endX = box.x + box.width / 4;
+    const endY = box.y + box.height / 4;
+    await page.mouse.move(endX, endY);
+    await page.waitForTimeout(100); // Ensure movement is registered
+    await page.mouse.up();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('individual document should always include signing certificate', async ({ page }) => {
const user = await seedUser();
const { document, recipients } = await seedPendingDocumentWithFullFields({
owner: user,
recipients: ['signer@example.com'],
fields: [FieldType.SIGNATURE],
});
const documentData = await prisma.documentData
.findFirstOrThrow({
where: {
id: document.documentDataId,
},
})
.then(async (data) => getFile(data));
const originalPdf = await PDFDocument.load(documentData);
const recipient = recipients[0];
// Sign the document
await page.goto(`/sign/${recipient.token}`);
const canvas = page.locator('canvas');
const box = await canvas.boundingBox();
if (box) {
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
await page.mouse.down();
await page.mouse.move(box.x + box.width / 4, box.y + box.height / 4);
await page.mouse.up();
}
for (const field of recipient.Field) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
}
await page.getByRole('button', { name: 'Complete' }).click();
await page.getByRole('button', { name: 'Sign' }).click();
await page.waitForURL(`/sign/${recipient.token}/complete`);
await expect(async () => {
const { status } = await getDocumentByToken({
token: recipient.token,
});
expect(status).toBe(DocumentStatus.COMPLETED);
}).toPass();
// Get the completed document
const completedDocument = await prisma.document.findFirstOrThrow({
where: { id: document.id },
include: { documentData: true },
});
const completedDocumentData = await getFile(completedDocument.documentData);
// Load the PDF and check number of pages
const pdfDoc = await PDFDocument.load(completedDocumentData);
expect(pdfDoc.getPageCount()).toBe(originalPdf.getPageCount() + 1); // Original + Certificate
});
test('individual document should always include signing certificate', async ({ page }) => {
const user = await seedUser();
const { document, recipients } = await seedPendingDocumentWithFullFields({
owner: user,
recipients: ['signer@example.com'],
fields: [FieldType.SIGNATURE],
});
const documentData = await prisma.documentData
.findFirstOrThrow({
where: {
id: document.documentDataId,
},
})
.then(async (data) => getFile(data));
const originalPdf = await PDFDocument.load(documentData);
const recipient = recipients[0];
// Sign the document
await page.goto(`/sign/${recipient.token}`);
const canvas = page.locator('canvas');
await canvas.waitFor({ state: 'visible' });
const box = await canvas.boundingBox();
if (!box) {
throw new Error('Canvas not found');
}
// Center point
const centerX = box.x + box.width / 2;
const centerY = box.y + box.height / 2;
await page.mouse.move(centerX, centerY);
await page.mouse.down();
await page.waitForTimeout(100); // Ensure mouse down is registered
// Draw signature
const endX = box.x + box.width / 4;
const endY = box.y + box.height / 4;
await page.mouse.move(endX, endY);
await page.waitForTimeout(100); // Ensure movement is registered
await page.mouse.up();
for (const field of recipient.Field) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
}
await page.getByRole('button', { name: 'Complete' }).click();
await page.getByRole('button', { name: 'Sign' }).click();
await page.waitForURL(`/sign/${recipient.token}/complete`);
await expect(async () => {
const { status } = await getDocumentByToken({
token: recipient.token,
});
expect(status).toBe(DocumentStatus.COMPLETED);
}).toPass();
// Get the completed document
const completedDocument = await prisma.document.findFirstOrThrow({
where: { id: document.id },
include: { documentData: true },
});
const completedDocumentData = await getFile(completedDocument.documentData);
// Load the PDF and check number of pages
const pdfDoc = await PDFDocument.load(completedDocumentData);
expect(pdfDoc.getPageCount()).toBe(originalPdf.getPageCount() + 1); // Original + Certificate
});

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
packages/lib/translations/en/web.po (2)

1055-1057: Improve clarity of signing certificate message

The message about signing certificate inclusion could be clearer and more concise.

-msgid "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately."
+msgid "Controls whether to include the signing certificate in downloaded documents. The certificate can still be downloaded separately from the logs page."

1043-1051: Improve document preferences messages

The document preferences messages could be more concise while maintaining clarity.

-msgid "Controls the default language of an uploaded document. This will be used as the language in email communications with the recipients."
+msgid "Sets the default document language, which will be used in recipient email communications."

-msgid "Controls the default visibility of an uploaded document."
+msgid "Sets who can view uploaded documents by default."
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d7ff1cf and 3ee6a1a.

📒 Files selected for processing (5)
  • packages/lib/server-only/htmltopdf/get-certificate-pdf.ts (2 hunks)
  • packages/lib/translations/de/web.po (32 hunks)
  • packages/lib/translations/en/web.po (32 hunks)
  • packages/lib/translations/es/web.po (32 hunks)
  • packages/lib/translations/fr/web.po (32 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/lib/server-only/htmltopdf/get-certificate-pdf.ts
  • packages/lib/translations/de/web.po
  • packages/lib/translations/es/web.po
  • packages/lib/translations/fr/web.po

@@ -785,7 +785,7 @@ msgstr "Bulk Copy"
msgid "Bulk Import"
msgstr "Bulk Import"

#: apps/web/src/app/(dashboard)/documents/delete-document-dialog.tsx:156
#: apps/web/src/app/(dashboard)/documents/delete-document-dialog.tsx:158
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Standardize document deletion messages

The document deletion messages have inconsistent formatting and wording across different contexts.

Standardize the messages to use consistent language:

-msgid "All inserted signatures will be voided"
+msgid "All signatures will be voided"

-msgid "All recipients will be notified"
+msgid "All recipients will receive a notification"

-msgid "Once confirmed, the following will occur:"
+msgid "The following actions will occur:"

-msgid "Please note that this action is <0>irreversible</0>."
+msgid "This action cannot be undone."

-msgid "Please note that this action is <0>irreversible</0>. Once confirmed, this document will be permanently deleted."
+msgid "This action cannot be undone. The document will be permanently deleted."

Also applies to: 809-809, 2521-2521, 2713-2713, 2717-2717

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants