Files
calendar/packages/features/ee/workflows/lib/test/compareReminderBodyToTemplate.test.ts
T
Keith WilliamsGitHubkeith@cal.com <keithwillcode@gmail.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>hbjORbj
163c7ff791 refactor: replace i18n HTTP requests with build-time bundling (#22422)
* refactor: replace i18n HTTP requests with build-time bundling

- Create translationBundler.ts for build-time translation loading
- Replace HTTP fetch in loadTranslations with file system reads
- Add CalComVersion cache invalidation to prevent stale translations
- Fix TypeScript errors in booking page components
- Eliminate 60s timeout issues by removing network dependency

Resolves translation timeout issues by bundling translations at build time
instead of making runtime HTTP requests to /static/locales/ endpoints.

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: move i18n files back to packages/lib/server with proper imports

- Move i18n.ts and translationBundler.ts back to packages/lib/server/
- Replace all relative imports with @calcom/lib/server/i18n pattern
- Fix LOCALES_PATH to point to correct directory
- Maintain optimized serverless-friendly translation loading

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* refactor: move locale files to packages/lib/server to eliminate circular deps

- Move all locale files from apps/web/public/static/locales to packages/lib/server/locales
- Create copy-locales-static.js script to copy files during build
- Update all references to use new location for build-time access
- Maintain public folder copying for Next.js runtime access
- Update platform atoms, scripts, and config files
- Fix copy script relative path issue

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: update test imports after locale refactor

- Fix import paths in test files updated by pre-commit hooks
- Ensure all tests use correct locale import paths

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: revert import paths from @calcom/web/lib/i18n to @calcom/lib/server/i18n

- Revert all test file imports back to @calcom/lib/server/i18n as requested
- Addresses GitHub comment feedback to stick with packages/lib/server location
- Fixes import paths in 6 test files that were incorrectly changed

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: update vite config alias to use new locale path

- Update @calcom/web/public/static/locales/en/common.json to @calcom/lib/server/locales/en/common.json
- Addresses GitHub comment about updating platform atoms vite config
- Maintains correct path resolution after locale files moved to packages/lib/server

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: update translationBundler path resolution for production builds

- Use process.cwd() instead of __dirname for locale file path resolution
- Ensures locale files can be found in both development and production environments
- Fixes E2E test failures caused by missing locale files in .next/server/chunks/

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: use relative path resolution for locale files in translationBundler

- Change from process.cwd() to __dirname with relative paths
- Ensures locale files can be found in both development and production environments
- Fixes E2E test failures caused by incorrect path resolution in Next.js builds

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: use process.cwd() for locale path resolution in production builds

- Change from __dirname to process.cwd() with relative paths
- Ensures locale files can be found when bundled into Next.js server chunks
- Fixes E2E test failures caused by incorrect path resolution in production environment
- Follows same pattern used in getStaticProps.tsx for cross-package file access

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: use path.resolve pattern from sendVerificationRequest for locale access

- Change from process.cwd() to path.resolve(process.cwd(), '..', '..', 'packages/lib/server/locales')
- Follows same pattern used in sendVerificationRequest.ts for cross-environment file access
- Should resolve E2E test failures by ensuring locale files can be found when bundled into Next.js server chunks
- Pattern navigates up from current working directory to reach packages directory consistently

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: use dynamic monorepo root detection for locale path resolution

- Replace relative path resolution with dynamic monorepo root finder
- Ensures locale files can be found from any working directory (root, apps/web, apps/api/v2)
- Update API v2 i18n config to use new locale path
- Fixes remaining E2E test failures in API v2 and E2E (1/4) test suites

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: use require.resolve(__filename) for robust path resolution in all contexts

- Replace __dirname with require.resolve(__filename) in monorepo root detection
- Ensures locale files can be found when running from any working directory
- Fixes E2E API v2 test failures where __dirname resolves to '.' instead of actual file path

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* Revert "fix: use require.resolve(__filename) for robust path resolution in all contexts"

This reverts commit b37d8226000da8f7d5fb98b83dd0e95a53d45372.

* fix: update copied locale files after translationBundler path resolution fix

- Copy script updated all locale files in public directory
- Ensures E2E tests have access to latest locale files
- Fixes regression where all E2E tests were failing

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* feat: remove existing locale files to establish copy script as single source of truth

- Delete all common.json files from apps/web/public/static/locales/
- Eliminates developer confusion about which files are authoritative
- copy-locales-static.js script now clearly the only mechanism for populating public folder
- packages/lib/server/locales/ remains the definitive source of truth for translations

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* add comment

* refactor: simplify findMonorepoRoot by removing redundant fallback loop

- Remove unnecessary second while loop using process.cwd()
- The first loop from __dirname will always find the monorepo root
- Add clear error message for fail-fast behavior if repo structure is corrupted
- Improves code clarity and maintainability

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* feat: integrate updated translations from main branch

- Restore all common.json files from main branch to apps/web/public/static/locales/
- Overwrite packages/lib/server/locales/ with up-to-date translation content
- Resolve merge conflicts using Benny's safer 2-step approach
- Ensure translation source of truth remains in packages/lib/server/locales/
- Complete safer conflict resolution to eliminate merge conflicts on PR #22422

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* chore: update package.json and yarn.lock after translation integration

- Update dependencies after ts-node installation for pre-commit hooks
- Ensure yarn.lock reflects current dependency state
- Complete translation integration process

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* feat: remove duplicate locale files from apps/web to establish single source of truth

- Delete all common.json files from apps/web/public/static/locales/
- Maintain packages/lib/server/locales/ as the single source of truth for translations
- copy-locales-static.js script will populate public folder during build process
- Complete Benny's safer 2-step approach: restore from main, then remove duplicates
- Resolve merge conflicts and eliminate developer confusion about translation file locations

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: hbjORbj <sldisek783@gmail.com>
2025-07-12 07:49:27 +00:00

150 lines
5.0 KiB
TypeScript

import { createInstance } from "i18next";
import { expect, test, describe } from "vitest";
import { getTranslation } from "@calcom/lib/server/i18n";
import en from "@calcom/lib/server/locales/en/common.json";
import { TimeFormat } from "@calcom/lib/timeFormat";
import { WorkflowActions, WorkflowTemplates } from "@calcom/prisma/enums";
import { getTemplateBodyForAction } from "../actionHelperFunctions";
import compareReminderBodyToTemplate from "../compareReminderBodyToTemplate";
import plainTextReminderTemplates from "../reminders/templates/plainTextTemplates";
const translation = async () => {
const _i18n = createInstance();
await _i18n.init({
lng: "en",
resources: {
en: {
translation: en,
},
},
});
return _i18n.getFixedT("en");
};
describe("compareReminderBodyToTemplate", () => {
test("should return true if reminderBody and template are the same", () => {
const reminderBody = "<p>Test</p>";
const template = "<p>Test</p>";
expect(compareReminderBodyToTemplate({ reminderBody, template })).toBe(true);
});
test("should return false if reminderBody and template are different", () => {
const reminderBody = "<p>Test</p>";
const template = "<p>Test2</p>";
expect(compareReminderBodyToTemplate({ reminderBody, template })).toBe(false);
});
describe("email templates", () => {
test("reminder", async () => {
const template = getTemplateBodyForAction({
action: WorkflowActions.EMAIL_HOST,
template: WorkflowTemplates.REMINDER,
timeFormat: TimeFormat.TWELVE_HOUR,
locale: "en",
t: await translation(),
});
if (!template) throw new Error("template not found");
const reminderBody = plainTextReminderTemplates.email.reminder;
expect(compareReminderBodyToTemplate({ reminderBody, template })).toBe(true);
});
test("rating", async () => {
const template = getTemplateBodyForAction({
action: WorkflowActions.EMAIL_HOST,
template: WorkflowTemplates.RATING,
timeFormat: TimeFormat.TWELVE_HOUR,
locale: "en",
t: await translation(),
});
if (!template) throw new Error("template not found");
const reminderBody = plainTextReminderTemplates.email?.rating ?? "";
expect(compareReminderBodyToTemplate({ reminderBody, template })).toBe(true);
});
});
describe("sms templates", () => {
test("reminder", async () => {
const template = getTemplateBodyForAction({
action: WorkflowActions.SMS_ATTENDEE,
template: WorkflowTemplates.REMINDER,
timeFormat: TimeFormat.TWELVE_HOUR,
locale: "en",
t: await getTranslation("en", "common"),
});
if (!template) throw new Error("template not found");
const reminderBody = plainTextReminderTemplates.sms.reminder;
expect(compareReminderBodyToTemplate({ reminderBody, template })).toBe(true);
});
});
describe("whatsapp templates", () => {
test("reminder", async () => {
const template = getTemplateBodyForAction({
action: WorkflowActions.WHATSAPP_ATTENDEE,
template: WorkflowTemplates.REMINDER,
timeFormat: TimeFormat.TWELVE_HOUR,
locale: "en",
t: await getTranslation("en", "common"),
});
if (!template) throw new Error("template not found");
const reminderBody = plainTextReminderTemplates.whatsapp.reminder;
expect(compareReminderBodyToTemplate({ reminderBody, template })).toBe(true);
});
test("rescheduled", async () => {
const template = getTemplateBodyForAction({
action: WorkflowActions.WHATSAPP_ATTENDEE,
template: WorkflowTemplates.RESCHEDULED,
timeFormat: TimeFormat.TWELVE_HOUR,
locale: "en",
t: await getTranslation("en", "common"),
});
if (!template) throw new Error("template not found");
const reminderBody = plainTextReminderTemplates.whatsapp.rescheduled;
expect(compareReminderBodyToTemplate({ reminderBody, template })).toBe(true);
});
test("completed", async () => {
const template = getTemplateBodyForAction({
action: WorkflowActions.WHATSAPP_ATTENDEE,
template: WorkflowTemplates.COMPLETED,
timeFormat: TimeFormat.TWELVE_HOUR,
locale: "en",
t: await getTranslation("en", "common"),
});
if (!template) throw new Error("template not found");
const reminderBody = plainTextReminderTemplates.whatsapp.completed;
expect(compareReminderBodyToTemplate({ reminderBody, template })).toBe(true);
});
test("canceled", async () => {
const template = getTemplateBodyForAction({
action: WorkflowActions.WHATSAPP_ATTENDEE,
template: WorkflowTemplates.CANCELLED,
timeFormat: TimeFormat.TWELVE_HOUR,
locale: "en",
t: await getTranslation("en", "common"),
});
if (!template) throw new Error("template not found");
const reminderBody = plainTextReminderTemplates.whatsapp?.canceled ?? "";
expect(compareReminderBodyToTemplate({ reminderBody, template })).toBe(true);
});
});
});