+9








6561b92f40
* Upgrade jsforce to 3.6.2
* Refactor connecting to Salesforce
* Revert yarn.lock changes
* Add `TASKER_ENABLE_CRM_EVENT_CREATION` to .env
* Add createCRMEvent scheduler
* Schedule CRM event creating in `EventManager`
* Add calendar event builder for CRM tasks
* Do not write to person record if fields don't exist
* Change type to expect string from tasker
* Create CRM event
* Create booking references
* Type fixes
* Migrate callback endpoint
* Add jsforce node dependency
* Migrate add endpoint
* Import
* Import package into crmService
* Use new package types
* Type fix
* Update vite config
* Push updated lockfile
* Attempt to bump platform/libraries to unlock jsforce
* Also update lockfile, naturally
* bump platform libraries
* feat: salesforce to tasker improvements (#18419)
* feat: salesforce to tasker
* refactor: event manager
* tests: add unit tests for create CRM Event
* Update vite.config
* Add jsforce to vite config
* Revert mint.json changes
* Default to not enabling
* Revert yarn.lock changes
* Remove `TASKER_ENABLE_CRM_EVENT_CREATION` variable
* Revert yarn.lock changes
* feat: Round Robin weights future members toggle (#17782)
Co-authored-by: Omar López <zomars@me.com>
* detailed customer card (#18511)
Co-authored-by: Omar López <zomars@me.com>
* chore: app router - all sub-pages in `/apps` (#16976)
* chore: apps/[slug] remove pages router
* remove apps/[slug] pages from /future
* chore: apps/installed remove pages router
* chore: apps/installation remove pages router
* remove Head element
* fix metadata
* fix test
* fix another test
* chore: apps/categories remove pages router
* revert unneeded changes
* update middleware
* Remove <Head>
* remove unused import and code
* remove unused import and code again
* fix
* fix category page
* add split icon
* add /routing paths to middleware matcher
* wip
* remove HeadSeo from App.tsx
* clean up head-seo test
* add generateAppMetadata
* use generateAppMetadata in apps/[slug] page
* delete file
* remove log
* fix
* fix
* fix apps/installed pages
* fix cateogires pages
* fix
* fix imports
* wip
* fix
* fix
* fix metadata
* fix
* redirect /apps/routing-forms to /routing
* replace all usages of /apps/routing-forms to /routing
* better naming
* /routing -> /routing/forms
* fix
* fix
* fix
* fix
* remove backPath as it is irrelevant when withoutMain is true
* fix type checks
* fix type check in apps/[slug]
* refactors
* fix
* fix test
* fix
* fix
* fix
* Replace multiple leading slashes with a single slash
* migrate routing-forms too
* add re routing
* fix
* add redirection
---------
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* chore: app router 404 page (#18597)
* wip
* wip
* fix not found page
* render middleware for /settings pages
* fix
* remove global-error page
* add metadata to not-found page
* make not-found page static
* remove 404
* adding not-found to middleware is not necessary
* add every routes to config.matcher
* fix test
* fix style
* use i18n string
* fix tests
* fix
* fix
* revert unneeded changes
* fix
* fix
* fix
* fix style
* fix
* remove 404
* remove log
* fix
* fix
* fix
* fix
* better naming
* parallel testing
---------
Co-authored-by: Benny Joo <sldisek783@gmail.com>
* feat: render custom error page for unexpected sever error + remove` pages/_error` (#18606)
* remove page/_error
* refactor app/error
* fix: app/not-found cannot be a static page (#18610)
* Async False (#18611)
* chore: redirect to /500 if pathname does not exist + better error handling (#18615)
* fix lint error
* fix booking page and better error handling
* chore: gracefully handle 404s from pages router's dynamic pages + tests (#18618)
* restore pages/_error
* set custom header in pages/_error
* handle it in middleware
* add test
* remove logs
* better test description
* chore: try using custom 404 in pages/_error (#18622)
* fix: parsing teamId (#18623)
* chore: restore error pages for pages router (#18625)
* disable emails to all guests (#18628)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* revert: "feat: bulk shorten links with dub.links.createMany (#18539)" (#18587)
This reverts commit 4902c6a0f8.
Co-authored-by: Alex van Andel <me@alexvanandel.com>
* chore: release v4.8.18
* fix: disable sending sms when email is present (#18632)
* fix: disable sending sms when email is present
* fix: update test
* fix: main lint errors (#18634)
* fix: error-page.tsx related lint errors
* fix: lint no continue-on-error
* Adding more lint fixes
* Bring back annotate code linting results
* Bring back linting continue-on-error
* Slimline lint
* feat: do not show automation webhooks in webhook list (#18607)
* fix: make ESLint work and fix lint errors that were undetected before (#18639)
* fix eslint config
* fix
* add it to dev dep
* fix
* sync eslint version
* force ts-node to compile our ESLint plugin's TS files into CommonJS (which ESLint requires)
* fix some lint errors
* fix lint errors
* remove duplicate classname
* make @typescript-eslint/ban-types a warn for packages/trpc files
* fix lint errors in trpc
* fix lint errors in trpc - 2
* fix
* fix
* fix lint warnings
* chore: clean up config.matcher in middleware (#18638)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
* Allow lint to error but continue (unblock pipeline)
* fix: potential fix for flaky layout shift (#18651)
* potential fix: layout shift
* fix lint error
* feat: update translations via @replexica (#18598)
* chore: sync with main
* feat: update translations via @replexica
---------
Co-authored-by: Replexica <support@replexica.com>
* Update yarn.lock
* nit: let tasker handle payload stringification
---------
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
Co-authored-by: Morgan Vernay <morgan@cal.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Nizzy <140507264+nizzyabi@users.noreply.github.com>
Co-authored-by: Benny Joo <sldisek783@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: GitHub Actions <github-actions@github.com>
Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
Co-authored-by: Calcom Bot <109866826+calcom-bot@users.noreply.github.com>
Co-authored-by: Replexica <support@replexica.com>
259 lines
6.0 KiB
TypeScript
259 lines
6.0 KiB
TypeScript
import prismaMock from "../../../../../../tests/libs/__mocks__/prismaMock";
|
|
|
|
import { describe, expect, it, beforeEach, vi } from "vitest";
|
|
|
|
import { BookingStatus } from "@calcom/prisma/enums";
|
|
|
|
import { createCRMEvent } from "../createCRMEvent";
|
|
|
|
interface User {
|
|
id: number;
|
|
email: string;
|
|
name: string;
|
|
}
|
|
|
|
interface EventTypeMetadata {
|
|
apps: {
|
|
salesforce: {
|
|
enabled: boolean;
|
|
credentialId: number;
|
|
appCategories: string[];
|
|
};
|
|
};
|
|
}
|
|
|
|
interface Booking {
|
|
id: number;
|
|
uid: string;
|
|
status: BookingStatus;
|
|
title: string;
|
|
startTime: Date;
|
|
endTime: Date;
|
|
user: User;
|
|
eventType: {
|
|
metadata: EventTypeMetadata;
|
|
};
|
|
}
|
|
|
|
interface CRMCredential {
|
|
id: number;
|
|
type: string;
|
|
key: {
|
|
access_token?: string;
|
|
refresh_token?: string;
|
|
expiry_date?: number;
|
|
key1?: string;
|
|
};
|
|
userId: number;
|
|
}
|
|
|
|
interface CRMManagerInterface {
|
|
createEvent: (event: unknown) => Promise<{ id: string }>;
|
|
}
|
|
|
|
vi.mock("../lib/buildCalendarEvent", () => ({
|
|
default: vi.fn().mockResolvedValue({
|
|
title: "Test Event",
|
|
description: "Test Description",
|
|
startTime: new Date(),
|
|
endTime: new Date(),
|
|
}),
|
|
}));
|
|
|
|
const mockCreateEvent = vi.fn().mockResolvedValue({ id: "sf-event-123" });
|
|
vi.mock("@calcom/core/crmManager/crmManager", () => ({
|
|
default: class MockCrmManager {
|
|
private credential: CRMCredential;
|
|
|
|
constructor(credential: CRMCredential) {
|
|
this.credential = credential;
|
|
}
|
|
|
|
createEvent = mockCreateEvent;
|
|
|
|
getManager(): CRMManagerInterface {
|
|
return {
|
|
createEvent: this.createEvent,
|
|
};
|
|
}
|
|
},
|
|
}));
|
|
|
|
describe("createCRMEvent", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockCreateEvent.mockClear();
|
|
// Clear Prisma mocks
|
|
prismaMock.booking.findUnique.mockReset();
|
|
prismaMock.credential.findUnique.mockReset();
|
|
prismaMock.bookingReference.createMany.mockReset();
|
|
});
|
|
|
|
it("should successfully create a Salesforce CRM event", async () => {
|
|
const mockBooking: Booking = {
|
|
id: 1,
|
|
uid: "booking-123",
|
|
status: BookingStatus.ACCEPTED,
|
|
title: "Test Booking",
|
|
startTime: new Date(),
|
|
endTime: new Date(),
|
|
user: {
|
|
id: 1,
|
|
email: "test@example.com",
|
|
name: "Test User",
|
|
},
|
|
eventType: {
|
|
metadata: {
|
|
apps: {
|
|
salesforce: {
|
|
enabled: true,
|
|
credentialId: 1,
|
|
appCategories: ["crm"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const mockCredential: CRMCredential = {
|
|
id: 1,
|
|
type: "salesforce_crm",
|
|
key: {
|
|
access_token: "mock_token",
|
|
refresh_token: "mock_refresh",
|
|
expiry_date: new Date().getTime() + 3600000,
|
|
},
|
|
userId: 1,
|
|
};
|
|
|
|
// Set up Prisma mocks with proper return values
|
|
prismaMock.booking.findUnique.mockResolvedValueOnce(mockBooking);
|
|
prismaMock.credential.findUnique.mockResolvedValueOnce(mockCredential);
|
|
prismaMock.bookingReference.createMany.mockResolvedValueOnce({ count: 1 });
|
|
|
|
const payload = JSON.stringify({
|
|
bookingUid: "booking-123",
|
|
});
|
|
|
|
await createCRMEvent(payload);
|
|
|
|
expect(mockCreateEvent).toHaveBeenCalled();
|
|
expect(prismaMock.bookingReference.createMany).toHaveBeenCalledWith({
|
|
data: [
|
|
{
|
|
type: "salesforce_crm",
|
|
uid: "sf-event-123",
|
|
meetingId: "sf-event-123",
|
|
credentialId: 1,
|
|
bookingId: 1,
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it("should throw error for invalid payload", async () => {
|
|
const invalidPayload = JSON.stringify({
|
|
invalidField: "test",
|
|
});
|
|
|
|
await expect(createCRMEvent(invalidPayload)).rejects.toThrow("malformed payload in createCRMEvent");
|
|
});
|
|
|
|
it("should throw error when booking is not found", async () => {
|
|
prismaMock.booking.findUnique.mockResolvedValue(null);
|
|
|
|
const payload = JSON.stringify({
|
|
bookingUid: "non-existent-booking",
|
|
});
|
|
|
|
await expect(createCRMEvent(payload)).rejects.toThrow("booking not found");
|
|
});
|
|
|
|
it("should handle case when Salesforce CRM is not enabled", async () => {
|
|
const mockBooking: Booking = {
|
|
id: 1,
|
|
uid: "booking-123",
|
|
status: BookingStatus.ACCEPTED,
|
|
title: "Test Booking",
|
|
startTime: new Date(),
|
|
endTime: new Date(),
|
|
user: {
|
|
id: 1,
|
|
email: "test@example.com",
|
|
name: "Test User",
|
|
},
|
|
eventType: {
|
|
metadata: {
|
|
apps: {
|
|
salesforce: {
|
|
enabled: false,
|
|
credentialId: 1,
|
|
appCategories: ["crm"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
prismaMock.booking.findUnique.mockResolvedValue(mockBooking);
|
|
|
|
const payload = JSON.stringify({
|
|
bookingUid: "booking-123",
|
|
});
|
|
|
|
await createCRMEvent(payload);
|
|
|
|
expect(prismaMock.bookingReference.createMany).toHaveBeenCalledWith({
|
|
data: [],
|
|
});
|
|
});
|
|
|
|
it("should handle CRM creation error gracefully", async () => {
|
|
const mockBooking: Booking = {
|
|
id: 1,
|
|
uid: "booking-123",
|
|
status: BookingStatus.ACCEPTED,
|
|
title: "Test Booking",
|
|
startTime: new Date(),
|
|
endTime: new Date(),
|
|
user: {
|
|
id: 1,
|
|
email: "test@example.com",
|
|
name: "Test User",
|
|
},
|
|
eventType: {
|
|
metadata: {
|
|
apps: {
|
|
salesforce: {
|
|
enabled: true,
|
|
credentialId: 1,
|
|
appCategories: ["crm"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const mockCredential: CRMCredential = {
|
|
id: 1,
|
|
type: "salesforce_crm",
|
|
key: { key1: "value1" },
|
|
userId: 1,
|
|
};
|
|
|
|
prismaMock.booking.findUnique.mockResolvedValue(mockBooking);
|
|
prismaMock.credential.findFirst.mockResolvedValue(mockCredential);
|
|
mockCreateEvent.mockRejectedValue(new Error("Salesforce API error"));
|
|
|
|
const payload = JSON.stringify({
|
|
bookingUid: "booking-123",
|
|
});
|
|
|
|
await createCRMEvent(payload);
|
|
|
|
expect(prismaMock.bookingReference.createMany).toHaveBeenCalledWith({
|
|
data: [],
|
|
});
|
|
});
|
|
});
|