Files
Benny JooandDevin AI ab21c7f805 refactor: Cal.diy (#28903)
* feat: Cal.diy — community-driven MIT-licensed fork of Cal.com

This squashed commit contains all Cal.diy changes applied on top of calcom/cal.com main:

- Rebrand Cal.com to Cal.diy across the entire codebase
- Remove Enterprise Edition (EE) features, license checks, and AGPL restrictions
- Switch license from AGPL-3.0 to MIT
- Remove docs/ directory (migrated to Nextra at cal.diy)
- Remove dead code: org tests, EE tips, platform nav, premium username, SAML/SSO, etc.
- Clean up .env.example for self-hosted Cal.diy
- Update Docker image references to calcom/cal.diy
- Update README, CONTRIBUTING.md, and issue templates for Cal.diy community fork
- Add PR welcome bot for Cal.diy contributors
- Fix API v2 breaking changes oasdiff ignore entries
- Replace Blacksmith CI runners with default GitHub Actions

3893 files changed, 20789 insertions(+), 411020 deletions(-)

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* refactor: remove org-specific /organizations/:orgId endpoints from API v2 atoms controllers (#1701)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: revert Cal.diy Inc to Cal.com, Inc. in license files, copyright notices, and package metadata (#1702)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* rip out org related comments in api v2

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-04-15 09:52:36 -03:00

189 lines
7.0 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import { TranslationService } from "./TranslationService";
vi.mock("@calcom/lib/logger", () => ({
default: { error: vi.fn() },
}));
describe("TranslationService", () => {
let mockLocalizeText: ReturnType<typeof vi.fn>;
let mockEventTypeTranslationRepository: {
findByLocale: ReturnType<typeof vi.fn>;
};
let service: TranslationService;
beforeEach(() => {
vi.resetAllMocks();
mockLocalizeText = vi.fn();
mockEventTypeTranslationRepository = {
findByLocale: vi.fn(),
};
service = new TranslationService({
localizeText: mockLocalizeText,
eventTypeTranslationRepository: mockEventTypeTranslationRepository as never,
});
});
describe("getTargetLocales", () => {
it("should exclude source locale from target locales", () => {
const targets = service.getTargetLocales("en");
expect(targets).not.toContain("en");
});
it("should return supported locales that are in i18n", () => {
const targets = service.getTargetLocales("en");
expect(targets.length).toBeGreaterThan(0);
expect(targets).toContain("es");
expect(targets).toContain("de");
});
it("should exclude different source locale", () => {
const targets = service.getTargetLocales("es");
expect(targets).not.toContain("es");
expect(targets).toContain("en");
});
});
describe("translateText", () => {
it("should return empty results for empty text", async () => {
const result = await service.translateText({ text: "", sourceLocale: "en" });
expect(result.translations).toHaveLength(0);
expect(result.failedLocales).toHaveLength(0);
expect(mockLocalizeText).not.toHaveBeenCalled();
});
it("should return empty results for whitespace-only text", async () => {
const result = await service.translateText({ text: " ", sourceLocale: "en" });
expect(result.translations).toHaveLength(0);
expect(result.failedLocales).toHaveLength(0);
expect(mockLocalizeText).not.toHaveBeenCalled();
});
it("should return empty results for null text", async () => {
const result = await service.translateText({ text: null as unknown as string, sourceLocale: "en" });
expect(result.translations).toHaveLength(0);
expect(result.failedLocales).toHaveLength(0);
});
it("should translate text to all supported locales", async () => {
mockLocalizeText.mockResolvedValue("Translated");
const result = await service.translateText({ text: "Hello", sourceLocale: "en" });
expect(result.translations.length).toBeGreaterThan(0);
expect(result.translations[0]).toEqual(
expect.objectContaining({
translatedText: "Translated",
})
);
});
it("should filter out null translations and track failed locales", async () => {
mockLocalizeText
.mockResolvedValueOnce("Hola")
.mockResolvedValueOnce(null)
.mockResolvedValue("Translated");
const result = await service.translateText({ text: "Hello", sourceLocale: "en" });
expect(result.failedLocales.length).toBe(1);
expect(result.translations.every((t) => t.translatedText !== null)).toBe(true);
});
it("should skip source locale when translating", async () => {
mockLocalizeText.mockResolvedValue("Translation");
await service.translateText({ text: "Hello", sourceLocale: "es" });
const calls = mockLocalizeText.mock.calls;
const targetLocales = calls.map((call) => call[2]);
expect(targetLocales).not.toContain("es");
});
it("should handle translation service errors gracefully", async () => {
mockLocalizeText.mockRejectedValue(new Error("API Error"));
const result = await service.translateText({ text: "Hello", sourceLocale: "en" });
expect(result.translations).toHaveLength(0);
expect(result.failedLocales.length).toBeGreaterThan(0);
});
it("should preserve locale alignment in results", async () => {
mockLocalizeText.mockImplementation((_text, _source, target) =>
Promise.resolve(`Translated to ${target}`)
);
const result = await service.translateText({ text: "Hello", sourceLocale: "en" });
for (const translation of result.translations) {
expect(translation.translatedText).toBe(`Translated to ${translation.targetLocale}`);
}
});
it("should call localizeText with correct parameters", async () => {
mockLocalizeText.mockResolvedValue("Translated");
await service.translateText({ text: "Hello World", sourceLocale: "en" });
expect(mockLocalizeText).toHaveBeenCalledWith("Hello World", "en", expect.any(String));
});
});
describe("getEventTypeTranslation", () => {
it("should return translated description when includeDescription is true", async () => {
mockEventTypeTranslationRepository.findByLocale.mockResolvedValue({
translatedText: "Translated description",
});
const result = await service.getEventTypeTranslation(1, "es", { includeDescription: true });
expect(result.translatedDescription).toBe("Translated description");
expect(mockEventTypeTranslationRepository.findByLocale).toHaveBeenCalledWith(1, "DESCRIPTION", "es");
});
it("should return translated title when includeTitle is true", async () => {
mockEventTypeTranslationRepository.findByLocale.mockResolvedValue({
translatedText: "Translated title",
});
const result = await service.getEventTypeTranslation(1, "es", { includeTitle: true });
expect(result.translatedTitle).toBe("Translated title");
expect(mockEventTypeTranslationRepository.findByLocale).toHaveBeenCalledWith(1, "TITLE", "es");
});
it("should return both title and description when both options are true", async () => {
mockEventTypeTranslationRepository.findByLocale
.mockResolvedValueOnce({ translatedText: "Translated title" })
.mockResolvedValueOnce({ translatedText: "Translated description" });
const result = await service.getEventTypeTranslation(1, "es", {
includeTitle: true,
includeDescription: true,
});
expect(result.translatedTitle).toBe("Translated title");
expect(result.translatedDescription).toBe("Translated description");
});
it("should return empty object when no translation found", async () => {
mockEventTypeTranslationRepository.findByLocale.mockResolvedValue(null);
const result = await service.getEventTypeTranslation(1, "es", { includeDescription: true });
expect(result.translatedDescription).toBeUndefined();
});
it("should default to includeDescription: true when no options provided", async () => {
mockEventTypeTranslationRepository.findByLocale.mockResolvedValue({
translatedText: "Translated description",
});
await service.getEventTypeTranslation(1, "es");
expect(mockEventTypeTranslationRepository.findByLocale).toHaveBeenCalledWith(1, "DESCRIPTION", "es");
});
});
});