From c3343d3600598912df473b2252dea0614acc62e8 Mon Sep 17 00:00:00 2001 From: BOHEUS <56270748+BOHEUS@users.noreply.github.com> Date: Thu, 7 Nov 2024 14:34:53 +0000 Subject: [PATCH 001/100] Playwright basic utils (#7930) Related to #6641 --- .../drivers/env_variables.ts | 22 +++++ .../lib/utils/keyboardShortcuts.ts | 94 +++++++++++++++++++ .../lib/utils/pasteCodeToCodeEditor.ts | 14 +++ .../lib/utils/uploadFile.ts | 15 +++ 4 files changed, 145 insertions(+) create mode 100644 packages/twenty-e2e-testing/drivers/env_variables.ts create mode 100644 packages/twenty-e2e-testing/lib/utils/keyboardShortcuts.ts create mode 100644 packages/twenty-e2e-testing/lib/utils/pasteCodeToCodeEditor.ts create mode 100644 packages/twenty-e2e-testing/lib/utils/uploadFile.ts diff --git a/packages/twenty-e2e-testing/drivers/env_variables.ts b/packages/twenty-e2e-testing/drivers/env_variables.ts new file mode 100644 index 000000000..2bb7f57d8 --- /dev/null +++ b/packages/twenty-e2e-testing/drivers/env_variables.ts @@ -0,0 +1,22 @@ +import * as fs from 'fs'; +import path from 'path'; + +export const envVariables = (variables: string) => { + let payload = ` + PG_DATABASE_URL=postgres://twenty:twenty@localhost:5432/default + FRONT_BASE_URL=http://localhost:3001 + ACCESS_TOKEN_SECRET=replace_me_with_a_random_string_access + LOGIN_TOKEN_SECRET=replace_me_with_a_random_string_login + REFRESH_TOKEN_SECRET=replace_me_with_a_random_string_refresh + FILE_TOKEN_SECRET=replace_me_with_a_random_string_refresh + REDIS_URL=redis://localhost:6379 + `; + payload = payload.concat(variables); + fs.writeFile( + path.join(__dirname, '..', '..', 'twenty-server', '.env'), + payload, + (err) => { + throw err; + }, + ); +}; diff --git a/packages/twenty-e2e-testing/lib/utils/keyboardShortcuts.ts b/packages/twenty-e2e-testing/lib/utils/keyboardShortcuts.ts new file mode 100644 index 000000000..470e63c1a --- /dev/null +++ b/packages/twenty-e2e-testing/lib/utils/keyboardShortcuts.ts @@ -0,0 +1,94 @@ +import { Page } from '@playwright/test'; + +const MAC = process.platform === 'darwin'; + +async function keyDownCtrlOrMeta(page: Page) { + if (MAC) { + await page.keyboard.down('Meta'); + } else { + await page.keyboard.down('Control'); + } +} + +async function keyUpCtrlOrMeta(page: Page) { + if (MAC) { + await page.keyboard.up('Meta'); + } else { + await page.keyboard.up('Control'); + } +} + +export async function withCtrlOrMeta(page: Page, key: () => Promise) { + await keyDownCtrlOrMeta(page); + await key(); + await keyUpCtrlOrMeta(page); +} + +export async function selectAllByKeyboard(page: Page) { + await keyDownCtrlOrMeta(page); + await page.keyboard.press('a', { delay: 50 }); + await keyUpCtrlOrMeta(page); +} + +export async function copyByKeyboard(page: Page) { + await keyDownCtrlOrMeta(page); + await page.keyboard.press('c', { delay: 50 }); + await keyUpCtrlOrMeta(page); +} + +export async function cutByKeyboard(page: Page) { + await keyDownCtrlOrMeta(page); + await page.keyboard.press('x', { delay: 50 }); + await keyUpCtrlOrMeta(page); +} + +export async function pasteByKeyboard(page: Page) { + await keyDownCtrlOrMeta(page); + await page.keyboard.press('v', { delay: 50 }); + await keyUpCtrlOrMeta(page); +} + +export async function companiesShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('c'); +} + +export async function notesShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('n'); +} + +export async function opportunitiesShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('o'); +} + +export async function peopleShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('p'); +} + +export async function rocketsShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('r'); +} + +export async function tasksShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('t'); +} + +export async function workflowsShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('w'); +} + +export async function settingsShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('s'); +} + +export async function customShortcut(page: Page, shortcut: string) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press(shortcut); +} diff --git a/packages/twenty-e2e-testing/lib/utils/pasteCodeToCodeEditor.ts b/packages/twenty-e2e-testing/lib/utils/pasteCodeToCodeEditor.ts new file mode 100644 index 000000000..f67defef9 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/utils/pasteCodeToCodeEditor.ts @@ -0,0 +1,14 @@ +import { Locator, Page } from '@playwright/test'; +import { selectAllByKeyboard } from './keyboardShortcuts'; + +// https://github.com/microsoft/playwright/issues/14126 +// code must have \n at the end of lines otherwise everything will be in one line +export const pasteCodeToCodeEditor = async ( + page: Page, + locator: Locator, + code: string, +) => { + await locator.click(); + await selectAllByKeyboard(page); + await page.keyboard.type(code); +}; diff --git a/packages/twenty-e2e-testing/lib/utils/uploadFile.ts b/packages/twenty-e2e-testing/lib/utils/uploadFile.ts new file mode 100644 index 000000000..81898bc2b --- /dev/null +++ b/packages/twenty-e2e-testing/lib/utils/uploadFile.ts @@ -0,0 +1,15 @@ +import { Page } from '@playwright/test'; +import path from 'path'; + +export const fileUploader = async ( + page: Page, + trigger: () => Promise, + filename: string, +) => { + const fileChooserPromise = page.waitForEvent('filechooser'); + await trigger(); + const fileChooser = await fileChooserPromise; + await fileChooser.setFiles( + path.join(__dirname, '..', 'test_files', filename), + ); +}; From c571c9bdcaed1f401f52ecf280a49c416c13a378 Mon Sep 17 00:00:00 2001 From: BOHEUS <56270748+BOHEUS@users.noreply.github.com> Date: Thu, 7 Nov 2024 14:38:28 +0000 Subject: [PATCH 002/100] Playwright POM (#8109) Related to #6641 --- .../lib/pom/helper/confirmationModal.ts | 36 +++ .../lib/pom/helper/formatDate.function.ts | 28 ++ .../lib/pom/helper/googleLogin.ts | 6 + .../lib/pom/helper/iconSelect.ts | 23 ++ .../lib/pom/helper/insertFieldData.ts | 267 ++++++++++++++++++ .../lib/pom/helper/stripePage.ts | 5 + .../lib/pom/helper/uploadImage.ts | 25 ++ .../twenty-e2e-testing/lib/pom/leftMenu.ts | 115 ++++++++ .../twenty-e2e-testing/lib/pom/loginPage.ts | 187 ++++++++++++ .../twenty-e2e-testing/lib/pom/mainPage.ts | 196 +++++++++++++ .../lib/pom/recordDetails.ts | 150 ++++++++++ .../lib/pom/settings/accountsSection.ts | 54 ++++ .../lib/pom/settings/calendarSection.ts | 30 ++ .../lib/pom/settings/dataModelSection.ts | 189 +++++++++++++ .../lib/pom/settings/developersSection.ts | 123 ++++++++ .../lib/pom/settings/emailsSection.ts | 61 ++++ .../lib/pom/settings/experienceSection.ts | 55 ++++ .../lib/pom/settings/functionsSection.ts | 159 +++++++++++ .../lib/pom/settings/generalSection.ts | 29 ++ .../lib/pom/settings/membersSection.ts | 48 ++++ .../lib/pom/settings/newFieldSection.ts | 250 ++++++++++++++++ .../lib/pom/settings/profileSection.ts | 44 +++ .../lib/pom/settings/securitySection.ts | 13 + .../lib/pom/settingsPage.ts | 104 +++++++ .../tests/companies.spec.ts | 3 - 25 files changed, 2197 insertions(+), 3 deletions(-) create mode 100644 packages/twenty-e2e-testing/lib/pom/helper/confirmationModal.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/helper/formatDate.function.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/helper/googleLogin.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/helper/iconSelect.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/helper/insertFieldData.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/helper/stripePage.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/helper/uploadImage.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/leftMenu.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/loginPage.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/mainPage.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/recordDetails.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/settings/accountsSection.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/settings/calendarSection.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/settings/dataModelSection.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/settings/developersSection.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/settings/emailsSection.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/settings/experienceSection.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/settings/functionsSection.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/settings/generalSection.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/settings/membersSection.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/settings/newFieldSection.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/settings/profileSection.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/settings/securitySection.ts create mode 100644 packages/twenty-e2e-testing/lib/pom/settingsPage.ts diff --git a/packages/twenty-e2e-testing/lib/pom/helper/confirmationModal.ts b/packages/twenty-e2e-testing/lib/pom/helper/confirmationModal.ts new file mode 100644 index 000000000..225c6733b --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/confirmationModal.ts @@ -0,0 +1,36 @@ +import { Locator, Page } from '@playwright/test'; + +export class ConfirmationModal { + private readonly input: Locator; + private readonly cancelButton: Locator; + private readonly confirmButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.input = page.getByTestId('confirmation-modal-input'); + this.cancelButton = page.getByRole('button', { name: 'Cancel' }); + this.confirmButton = page.getByTestId('confirmation-modal-confirm-button'); + } + + async typePlaceholderToInput() { + await this.page + .getByTestId('confirmation-modal-input') + .fill( + await this.page + .getByTestId('confirmation-modal-input') + .getAttribute('placeholder'), + ); + } + + async typePhraseToInput(value: string) { + await this.page.getByTestId('confirmation-modal-input').fill(value); + } + + async clickCancelButton() { + await this.page.getByRole('button', { name: 'Cancel' }).click(); + } + + async clickConfirmButton() { + await this.page.getByTestId('confirmation-modal-confirm-button').click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/helper/formatDate.function.ts b/packages/twenty-e2e-testing/lib/pom/helper/formatDate.function.ts new file mode 100644 index 000000000..bffa490e8 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/formatDate.function.ts @@ -0,0 +1,28 @@ +const nth = (d: number) => { + if (d > 3 && d < 21) return 'th'; + switch (d % 10) { + case 1: + return 'st'; + case 2: + return 'nd'; + case 3: + return 'rd'; + default: + return 'th'; + } +}; + +// label looks like this: Choose Wednesday, October 30th, 2024 +// eslint-disable-next-line prefer-arrow/prefer-arrow-functions +export function formatDate(value: string): string { + const date = new Date(value); + return 'Choose '.concat( + date.toLocaleDateString('en-US', { weekday: 'long' }), + ', ', + date.toLocaleDateString('en-US', { month: 'long' }), + ' ', + nth(date.getDate()), + ', ', + date.toLocaleDateString('en-US', { year: 'numeric' }), + ); +} diff --git a/packages/twenty-e2e-testing/lib/pom/helper/googleLogin.ts b/packages/twenty-e2e-testing/lib/pom/helper/googleLogin.ts new file mode 100644 index 000000000..ca57fd636 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/googleLogin.ts @@ -0,0 +1,6 @@ +import { Locator, Page } from '@playwright/test'; + +export class GoogleLogin { + // TODO: map all things like inputs and buttons + // (what's the correct way for proceeding with Google interaction? log in each time test is performed?) +} diff --git a/packages/twenty-e2e-testing/lib/pom/helper/iconSelect.ts b/packages/twenty-e2e-testing/lib/pom/helper/iconSelect.ts new file mode 100644 index 000000000..82b30c53c --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/iconSelect.ts @@ -0,0 +1,23 @@ +import { Locator, Page } from '@playwright/test'; + +export class IconSelect { + private readonly iconSelectButton: Locator; + private readonly iconSearchInput: Locator; + + constructor(public readonly page: Page) { + this.iconSelectButton = page.getByLabel('Click to select icon ('); + this.iconSearchInput = page.getByPlaceholder('Search icon'); + } + + async selectIcon(name: string) { + await this.iconSelectButton.click(); + await this.iconSearchInput.fill(name); + await this.page.getByTitle(name).click(); + } + + async selectRelationIcon(name: string) { + await this.iconSelectButton.nth(1).click(); + await this.iconSearchInput.fill(name); + await this.page.getByTitle(name).click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/helper/insertFieldData.ts b/packages/twenty-e2e-testing/lib/pom/helper/insertFieldData.ts new file mode 100644 index 000000000..a052eff68 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/insertFieldData.ts @@ -0,0 +1,267 @@ +import { Locator, Page } from '@playwright/test'; +import { formatDate } from './formatDate.function'; + +export class InsertFieldData { + private readonly address1Input: Locator; + private readonly address2Input: Locator; + private readonly cityInput: Locator; + private readonly stateInput: Locator; + private readonly postCodeInput: Locator; + private readonly countrySelect: Locator; + private readonly arrayValueInput: Locator; + private readonly arrayAddValueButton: Locator; + // boolean react after click so no need to write special locator + private readonly currencySelect: Locator; + private readonly currencyAmountInput: Locator; + private readonly monthSelect: Locator; + private readonly yearSelect: Locator; + private readonly previousMonthButton: Locator; + private readonly nextMonthButton: Locator; + private readonly clearDateButton: Locator; + private readonly dateInput: Locator; + private readonly firstNameInput: Locator; + private readonly lastNameInput: Locator; + private readonly addURLButton: Locator; + private readonly setAsPrimaryButton: Locator; + private readonly addPhoneButton: Locator; + private readonly addMailButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.address1Input = page.locator( + '//label[contains(., "ADDRESS 1")]/../div[last()]/input', + ); + this.address2Input = page.locator( + '//label[contains(., "ADDRESS 2")]/../div[last()]/input', + ); + this.cityInput = page.locator( + '//label[contains(., "CITY")]/../div[last()]/input', + ); + this.stateInput = page.locator( + '//label[contains(., "STATE")]/../div[last()]/input', + ); + this.postCodeInput = page.locator( + '//label[contains(., "POST CODE")]/../div[last()]/input', + ); + this.countrySelect = page.locator( + '//span[contains(., "COUNTRY")]/../div[last()]/input', + ); + this.arrayValueInput = page.locator("//input[@placeholder='Enter value']"); + this.arrayAddValueButton = page.locator( + "//div[@data-testid='tooltip' and contains(.,'Add item')]", + ); + this.currencySelect = page.locator( + '//body/div[last()]/div/div/div[first()]/div/div', + ); + this.currencyAmountInput = page.locator("//input[@placeholder='Currency']"); + this.monthSelect; // TODO: add once some other attributes are added + this.yearSelect; + this.previousMonthButton; + this.nextMonthButton; + this.clearDateButton = page.locator( + "//div[@data-testid='tooltip' and contains(., 'Clear')]", + ); + this.dateInput = page.locator("//input[@placeholder='Type date and time']"); + this.firstNameInput = page.locator("//input[@placeholder='First name']"); // may fail if placeholder is `F‌‌irst name` instead of `First name` + this.lastNameInput = page.locator("//input[@placeholder='Last name']"); // may fail if placeholder is `L‌‌ast name` instead of `Last name` + this.addURLButton = page.locator( + "//div[@data-testid='tooltip' and contains(., 'Add URL')]", + ); + this.setAsPrimaryButton = page.locator( + "//div[@data-testid='tooltip' and contains(., 'Set as primary')]", + ); + this.addPhoneButton = page.locator( + "//div[@data-testid='tooltip' and contains(., 'Add Phone')]", + ); + this.addMailButton = page.locator( + "//div[@data-testid='tooltip' and contains(., 'Add Email')]", + ); + } + + // address + async typeAddress1(value: string) { + await this.address1Input.fill(value); + } + + async typeAddress2(value: string) { + await this.address2Input.fill(value); + } + + async typeCity(value: string) { + await this.cityInput.fill(value); + } + + async typeState(value: string) { + await this.stateInput.fill(value); + } + + async typePostCode(value: string) { + await this.postCodeInput.fill(value); + } + + async selectCountry(value: string) { + await this.countrySelect.click(); + await this.page + .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`) + .click(); + } + + // array + async typeArrayValue(value: string) { + await this.arrayValueInput.fill(value); + } + + async clickAddItemButton() { + await this.arrayAddValueButton.click(); + } + + // currency + async selectCurrency(value: string) { + await this.currencySelect.click(); + await this.page + .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`) + .click(); + } + + async typeCurrencyAmount(value: string) { + await this.currencyAmountInput.fill(value); + } + + // date(-time) + async typeDate(value: string) { + await this.dateInput.fill(value); + } + + async selectMonth(value: string) { + await this.monthSelect.click(); + await this.page + .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`) + .click(); + } + + async selectYear(value: string) { + await this.yearSelect.click(); + await this.page + .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`) + .click(); + } + + async clickPreviousMonthButton() { + await this.previousMonthButton.click(); + } + + async clickNextMonthButton() { + await this.nextMonthButton.click(); + } + + async selectDay(value: string) { + await this.page + .locator(`//div[@aria-label='${formatDate(value)}']`) + .click(); + } + + async clearDate() { + await this.clearDateButton.click(); + } + + // email + async typeEmail(value: string) { + await this.page.locator(`//input[@placeholder='Email']`).fill(value); + } + + async clickAddMailButton() { + await this.addMailButton.click(); + } + + // full name + async typeFirstName(name: string) { + await this.firstNameInput.fill(name); + } + + async typeLastName(name: string) { + await this.lastNameInput.fill(name); + } + + // JSON + // placeholder is dependent on the name of field + async typeJSON(placeholder: string, value: string) { + await this.page + .locator(`//input[@placeholder='${placeholder}']`) + .fill(value); + } + + // link + async typeLink(value: string) { + await this.page.locator("//input[@placeholder='URL']").fill(value); + } + + async clickAddURL() { + await this.addURLButton.click(); + } + + // (multi-)select + async selectValue(value: string) { + await this.page + .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`) + .click(); + } + + // number + // placeholder is dependent on the name of field + async typeNumber(placeholder: string, value: string) { + await this.page + .locator(`//input[@placeholder='${placeholder}']`) + .fill(value); + } + + // phones + async selectCountryPhoneCode(countryCode: string) { + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${countryCode}')]`, + ) + .click(); + } + + async typePhoneNumber(value: string) { + await this.page.locator(`//input[@placeholder='Phone']`).fill(value); + } + + async clickAddPhoneButton() { + await this.addPhoneButton.click(); + } + + // rating + // if adding rating for the first time, hover must be used + async selectRating(rating: number) { + await this.page.locator(`//div[@role='slider']/div[${rating}]`).click(); + } + + // text + // placeholder is dependent on the name of field + async typeText(placeholder: string, value: string) { + await this.page + .locator(`//input[@placeholder='${placeholder}']`) + .fill(value); + } + + async clickSetAsPrimaryButton() { + await this.setAsPrimaryButton.click(); + } + + async searchValue(value: string) { + await this.page.locator(`//div[@placeholder='Search']`).fill(value); + } + + async clickEditButton() { + await this.page + .locator("//div[@data-testid='tooltip' and contains(., 'Edit')]") + .click(); + } + + async clickDeleteButton() { + await this.page + .locator("//div[@data-testid='tooltip' and contains(., 'Delete')]") + .click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/helper/stripePage.ts b/packages/twenty-e2e-testing/lib/pom/helper/stripePage.ts new file mode 100644 index 000000000..ccef759f9 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/stripePage.ts @@ -0,0 +1,5 @@ +import { Locator, Page } from '@playwright/test'; + +export class StripePage { + // TODO: implement all necessary methods (staging/sandbox page - does it differ anyhow from normal page?) +} diff --git a/packages/twenty-e2e-testing/lib/pom/helper/uploadImage.ts b/packages/twenty-e2e-testing/lib/pom/helper/uploadImage.ts new file mode 100644 index 000000000..41493fdc0 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/uploadImage.ts @@ -0,0 +1,25 @@ +import { Locator, Page } from '@playwright/test'; + +export class UploadImage { + private readonly imagePreview: Locator; + private readonly uploadButton: Locator; + private readonly removeButton: Locator; + + constructor(public readonly page: Page) { + this.imagePreview = page.locator('.css-6eut39'); //TODO: add attribute to make it independent of theme + this.uploadButton = page.getByRole('button', { name: 'Upload' }); + this.removeButton = page.getByRole('button', { name: 'Remove' }); + } + + async clickImagePreview() { + await this.imagePreview.click(); + } + + async clickUploadButton() { + await this.uploadButton.click(); + } + + async clickRemoveButton() { + await this.removeButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/leftMenu.ts b/packages/twenty-e2e-testing/lib/pom/leftMenu.ts new file mode 100644 index 000000000..c58c3f7f1 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/leftMenu.ts @@ -0,0 +1,115 @@ +import { Locator, Page } from '@playwright/test'; + +export class LeftMenu { + private readonly workspaceDropdown: Locator; + private readonly leftMenu: Locator; + private readonly searchSubTab: Locator; + private readonly settingsTab: Locator; + private readonly peopleTab: Locator; + private readonly companiesTab: Locator; + private readonly opportunitiesTab: Locator; + private readonly opportunitiesTabAll: Locator; + private readonly opportunitiesTabByStage: Locator; + private readonly tasksTab: Locator; + private readonly tasksTabAll: Locator; + private readonly tasksTabByStatus: Locator; + private readonly notesTab: Locator; + private readonly rocketsTab: Locator; + private readonly workflowsTab: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.workspaceDropdown = page.getByTestId('workspace-dropdown'); + this.leftMenu = page.getByRole('button').first(); + this.searchSubTab = page.getByText('Search'); + this.settingsTab = page.getByRole('link', { name: 'Settings' }); + this.peopleTab = page.getByRole('link', { name: 'People' }); + this.companiesTab = page.getByRole('link', { name: 'Companies' }); + this.opportunitiesTab = page.getByRole('link', { name: 'Opportunities' }); + this.opportunitiesTabAll = page.getByRole('link', { + name: 'All', + exact: true, + }); + this.opportunitiesTabByStage = page.getByRole('link', { name: 'By Stage' }); + this.tasksTab = page.getByRole('link', { name: 'Tasks' }); + this.tasksTabAll = page.getByRole('link', { name: 'All tasks' }); + this.tasksTabByStatus = page.getByRole('link', { name: 'Notes' }); + this.notesTab = page.getByRole('link', { name: 'Notes' }); + this.rocketsTab = page.getByRole('link', { name: 'Rockets' }); + this.workflowsTab = page.getByRole('link', { name: 'Workflows' }); + } + + async selectWorkspace(workspaceName: string) { + await this.workspaceDropdown.click(); + await this.page + .getByTestId('tooltip') + .filter({ hasText: workspaceName }) + .click(); + } + + async changeLeftMenu() { + await this.leftMenu.click(); + } + + async openSearchTab() { + await this.searchSubTab.click(); + } + + async goToSettings() { + await this.settingsTab.click(); + } + + async goToPeopleTab() { + await this.peopleTab.click(); + } + + async goToCompaniesTab() { + await this.companiesTab.click(); + } + + async goToOpportunitiesTab() { + await this.opportunitiesTab.click(); + } + + async goToOpportunitiesTableView() { + await this.opportunitiesTabAll.click(); + } + + async goToOpportunitiesKanbanView() { + await this.opportunitiesTabByStage.click(); + } + + async goToTasksTab() { + await this.tasksTab.click(); + } + + async goToTasksTableView() { + await this.tasksTabAll.click(); + } + + async goToTasksKanbanView() { + await this.tasksTabByStatus.click(); + } + + async goToNotesTab() { + await this.notesTab.click(); + } + + async goToRocketsTab() { + await this.rocketsTab.click(); + } + + async goToWorkflowsTab() { + await this.workflowsTab.click(); + } + + async goToCustomObject(customObjectName: string) { + await this.page.getByRole('link', { name: customObjectName }).click(); + } + + async goToCustomObjectView(name: string) { + await this.page.getByRole('link', { name: name }).click(); + } +} + +export default LeftMenu; diff --git a/packages/twenty-e2e-testing/lib/pom/loginPage.ts b/packages/twenty-e2e-testing/lib/pom/loginPage.ts new file mode 100644 index 000000000..dc60d3f7a --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/loginPage.ts @@ -0,0 +1,187 @@ +import { Locator, Page } from '@playwright/test'; + +export class LoginPage { + private readonly loginWithGoogleButton: Locator; + private readonly loginWithEmailButton: Locator; + private readonly termsOfServiceLink: Locator; + private readonly privacyPolicyLink: Locator; + private readonly emailField: Locator; + private readonly continueButton: Locator; + private readonly forgotPasswordButton: Locator; + private readonly passwordField: Locator; + private readonly revealPasswordButton: Locator; + private readonly signInButton: Locator; + private readonly signUpButton: Locator; + private readonly previewImageButton: Locator; + private readonly uploadImageButton: Locator; + private readonly deleteImageButton: Locator; + private readonly workspaceNameField: Locator; + private readonly firstNameField: Locator; + private readonly lastNameField: Locator; + private readonly syncEverythingWithGoogleRadio: Locator; + private readonly syncSubjectAndMetadataWithGoogleRadio: Locator; + private readonly syncMetadataWithGoogleRadio: Locator; + private readonly syncWithGoogleButton: Locator; + private readonly noSyncButton: Locator; + private readonly inviteLinkField1: Locator; + private readonly inviteLinkField2: Locator; + private readonly inviteLinkField3: Locator; + private readonly copyInviteLink: Locator; + private readonly finishButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.loginWithGoogleButton = page.getByRole('button', { + name: 'Continue with Google', + }); + this.loginWithEmailButton = page.getByRole('button', { + name: 'Continue With Email', + }); + this.termsOfServiceLink = page.getByRole('link', { + name: 'Terms of Service', + }); + this.privacyPolicyLink = page.getByRole('link', { name: 'Privacy Policy' }); + this.emailField = page.getByPlaceholder('Email'); + this.continueButton = page.getByRole('button', { + name: 'Continue', + exact: true, + }); + this.forgotPasswordButton = page.getByText('Forgot your password?'); + this.passwordField = page.getByPlaceholder('Password'); + this.revealPasswordButton = page.getByTestId('reveal-password-button'); + this.signInButton = page.getByRole('button', { name: 'Sign in' }); + this.signUpButton = page.getByRole('button', { name: 'Sign up' }); + this.previewImageButton = page.locator('.css-1qzw107'); // TODO: fix + this.uploadImageButton = page.getByRole('button', { name: 'Upload' }); + this.deleteImageButton = page.getByRole('button', { name: 'Remove' }); + this.workspaceNameField = page.getByPlaceholder('Apple'); + this.firstNameField = page.getByPlaceholder('Tim'); + this.lastNameField = page.getByPlaceholder('Cook'); + this.syncEverythingWithGoogleRadio = page.locator( + 'input[value="SHARE_EVERYTHING"]', + ); + this.syncSubjectAndMetadataWithGoogleRadio = page.locator( + 'input[value="SUBJECT"]', + ); + this.syncMetadataWithGoogleRadio = page.locator('input[value="METADATA"]'); + this.syncWithGoogleButton = page.getByRole('button', { + name: 'Sync with Google', + }); + this.noSyncButton = page.getByText('Continue without sync'); + this.inviteLinkField1 = page.getByPlaceholder('tim@apple.dev'); + this.inviteLinkField2 = page.getByPlaceholder('craig@apple.dev'); + this.inviteLinkField3 = page.getByPlaceholder('mike@apple.dev'); + this.copyInviteLink = page.getByRole('button', { + name: 'Copy invitation link', + }); + this.finishButton = page.getByRole('button', { name: 'Finish' }); + } + + async loginWithGoogle() { + await this.loginWithGoogleButton.click(); + } + + async clickLoginWithEmail() { + await this.loginWithEmailButton.click(); + } + + async clickContinueButton() { + await this.continueButton.click(); + } + + async clickTermsLink() { + await this.termsOfServiceLink.click(); + } + + async clickPrivacyPolicyLink() { + await this.privacyPolicyLink.click(); + } + + async typeEmail(email: string) { + await this.emailField.fill(email); + } + + async typePassword(email: string) { + await this.passwordField.fill(email); + } + + async clickSignInButton() { + await this.signInButton.click(); + } + + async clickSignUpButton() { + await this.signUpButton.click(); + } + + async clickForgotPassword() { + await this.forgotPasswordButton.click(); + } + + async revealPassword() { + await this.revealPasswordButton.click(); + } + + async previewImage() { + await this.previewImageButton.click(); + } + + async clickUploadImage() { + await this.uploadImageButton.click(); + } + + async deleteImage() { + await this.deleteImageButton.click(); + } + + async typeWorkspaceName(workspaceName: string) { + await this.workspaceNameField.fill(workspaceName); + } + + async typeFirstName(firstName: string) { + await this.firstNameField.fill(firstName); + } + + async typeLastName(lastName: string) { + await this.lastNameField.fill(lastName); + } + + async clickSyncEverythingWithGoogleRadio() { + await this.syncEverythingWithGoogleRadio.click(); + } + + async clickSyncSubjectAndMetadataWithGoogleRadio() { + await this.syncSubjectAndMetadataWithGoogleRadio.click(); + } + + async clickSyncMetadataWithGoogleRadio() { + await this.syncMetadataWithGoogleRadio.click(); + } + + async clickSyncWithGoogleButton() { + await this.syncWithGoogleButton.click(); + } + + async noSyncWithGoogle() { + await this.noSyncButton.click(); + } + + async typeInviteLink1(email: string) { + await this.inviteLinkField1.fill(email); + } + + async typeInviteLink2(email: string) { + await this.inviteLinkField2.fill(email); + } + + async typeInviteLink3(email: string) { + await this.inviteLinkField3.fill(email); + } + + async clickCopyInviteLink() { + await this.copyInviteLink.click(); + } + + async clickFinishButton() { + await this.finishButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/mainPage.ts b/packages/twenty-e2e-testing/lib/pom/mainPage.ts new file mode 100644 index 000000000..a28e133b5 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/mainPage.ts @@ -0,0 +1,196 @@ +import { Locator, Page } from '@playwright/test'; + +export class MainPage { + // TODO: add missing elements (advanced filters, import/export popups) + private readonly tableViews: Locator; + private readonly addViewButton: Locator; + private readonly viewIconSelect: Locator; + private readonly viewNameInput: Locator; + private readonly viewTypeSelect: Locator; + private readonly createViewButton: Locator; + private readonly deleteViewButton: Locator; + private readonly filterButton: Locator; + private readonly searchFieldInput: Locator; + private readonly advancedFilterButton: Locator; + private readonly addFilterButton: Locator; + private readonly resetFilterButton: Locator; + private readonly saveFilterAsViewButton: Locator; + private readonly sortButton: Locator; + private readonly sortOrderButton: Locator; + private readonly optionsButton: Locator; + private readonly fieldsButton: Locator; + private readonly goBackButton: Locator; + private readonly hiddenFieldsButton: Locator; + private readonly editFieldsButton: Locator; + private readonly importButton: Locator; + private readonly exportButton: Locator; + private readonly deletedRecordsButton: Locator; + private readonly createNewRecordButton: Locator; + private readonly addToFavoritesButton: Locator; + private readonly deleteFromFavoritesButton: Locator; + private readonly exportBottomBarButton: Locator; + private readonly deleteRecordsButton: Locator; + + constructor(public readonly page: Page) { + this.tableViews = page.getByText('·'); + this.addViewButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Add view$/ }); + this.viewIconSelect = page.getByLabel('Click to select icon ('); + this.viewNameInput; // can be selected using only actual value + this.viewTypeSelect = page.locator( + "//span[contains(., 'View type')]/../div", + ); + this.createViewButton = page.getByRole('button', { name: 'Create' }); + this.deleteViewButton = page.getByRole('button', { name: 'Delete' }); + this.filterButton = page.getByText('Filter'); + this.searchFieldInput = page.getByPlaceholder('Search fields'); + this.advancedFilterButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Advanced filter$/ }); + this.addFilterButton = page.getByRole('button', { name: 'Add Filter' }); + this.resetFilterButton = page.getByTestId('cancel-button'); + this.saveFilterAsViewButton = page.getByRole('button', { + name: 'Save as new view', + }); + this.sortButton = page.getByText('Sort'); + this.sortOrderButton = page.locator('//li'); + this.optionsButton = page.getByText('Options'); + this.fieldsButton = page.getByText('Fields'); + this.goBackButton = page.getByTestId('dropdown-menu-header-end-icon'); + this.hiddenFieldsButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Hidden Fields$/ }); + this.editFieldsButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Edit Fields$/ }); + this.importButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Import$/ }); + this.exportButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Export$/ }); + this.deletedRecordsButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Deleted */ }); + this.createNewRecordButton = page.getByTestId('add-button'); + this.addToFavoritesButton = page.getByText('Add to favorites'); + this.deleteFromFavoritesButton = page.getByText('Delete from favorites'); + this.exportBottomBarButton = page.getByText('Export'); + this.deleteRecordsButton = page.getByText('Delete'); + } + + async clickTableViews() { + await this.tableViews.click(); + } + + async clickAddViewButton() { + await this.addViewButton.click(); + } + + async typeViewName(name: string) { + await this.viewNameInput.clear(); + await this.viewNameInput.fill(name); + } + + // name can be either be 'Table' or 'Kanban' + async selectViewType(name: string) { + await this.viewTypeSelect.click(); + await this.page.getByTestId('tooltip').filter({ hasText: name }).click(); + } + + async createView() { + await this.createViewButton.click(); + } + + async deleteView() { + await this.deleteViewButton.click(); + } + + async clickFilterButton() { + await this.filterButton.click(); + } + + async searchFields(name: string) { + await this.searchFieldInput.clear(); + await this.searchFieldInput.fill(name); + } + + async clickAdvancedFilterButton() { + await this.advancedFilterButton.click(); + } + + async addFilter() { + await this.addFilterButton.click(); + } + + async resetFilter() { + await this.resetFilterButton.click(); + } + + async saveFilterAsView() { + await this.saveFilterAsViewButton.click(); + } + + async clickSortButton() { + await this.sortButton.click(); + } + + //can be Ascending or Descending + async setSortOrder(name: string) { + await this.sortOrderButton.click(); + await this.page.getByTestId('tooltip').filter({ hasText: name }).click(); + } + + async clickOptionsButton() { + await this.optionsButton.click(); + } + + async clickFieldsButton() { + await this.fieldsButton.click(); + } + + async clickBackButton() { + await this.goBackButton.click(); + } + + async clickHiddenFieldsButton() { + await this.hiddenFieldsButton.click(); + } + + async clickEditFieldsButton() { + await this.editFieldsButton.click(); + } + + async clickImportButton() { + await this.importButton.click(); + } + + async clickExportButton() { + await this.exportButton.click(); + } + + async clickDeletedRecordsButton() { + await this.deletedRecordsButton.click(); + } + + async clickCreateNewRecordButton() { + await this.createNewRecordButton.click(); + } + + async clickAddToFavoritesButton() { + await this.addToFavoritesButton.click(); + } + + async clickDeleteFromFavoritesButton() { + await this.deleteFromFavoritesButton.click(); + } + + async clickExportBottomBarButton() { + await this.exportBottomBarButton.click(); + } + + async clickDeleteRecordsButton() { + await this.deleteRecordsButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/recordDetails.ts b/packages/twenty-e2e-testing/lib/pom/recordDetails.ts new file mode 100644 index 000000000..f22dd8a45 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/recordDetails.ts @@ -0,0 +1,150 @@ +import { Locator, Page } from '@playwright/test'; + +export class RecordDetails { + // TODO: add missing components in tasks, notes, files, emails, calendar tabs + private readonly closeRecordButton: Locator; + private readonly previousRecordButton: Locator; + private readonly nextRecordButton: Locator; + private readonly favoriteRecordButton: Locator; + private readonly addShowPageButton: Locator; + private readonly moreOptionsButton: Locator; + private readonly deleteButton: Locator; + private readonly uploadProfileImageButton: Locator; + private readonly timelineTab: Locator; + private readonly tasksTab: Locator; + private readonly notesTab: Locator; + private readonly filesTab: Locator; + private readonly emailsTab: Locator; + private readonly calendarTab: Locator; + private readonly detachRelationButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + } + + async clickCloseRecordButton() { + await this.closeRecordButton.click(); + } + + async clickPreviousRecordButton() { + await this.previousRecordButton.click(); + } + + async clickNextRecordButton() { + await this.nextRecordButton.click(); + } + + async clickFavoriteRecordButton() { + await this.favoriteRecordButton.click(); + } + + async createRelatedNote() { + await this.addShowPageButton.click(); + await this.page + .locator('//div[@data-testid="tooltip" and contains(., "Note")]') + .click(); + } + + async createRelatedTask() { + await this.addShowPageButton.click(); + await this.page + .locator('//div[@data-testid="tooltip" and contains(., "Task")]') + .click(); + } + + async clickMoreOptionsButton() { + await this.moreOptionsButton.click(); + } + + async clickDeleteRecordButton() { + await this.deleteButton.click(); + } + + async clickUploadProfileImageButton() { + await this.uploadProfileImageButton.click(); + } + + async goToTimelineTab() { + await this.timelineTab.click(); + } + + async goToTasksTab() { + await this.tasksTab.click(); + } + + async goToNotesTab() { + await this.notesTab.click(); + } + + async goToFilesTab() { + await this.filesTab.click(); + } + + async goToEmailsTab() { + await this.emailsTab.click(); + } + + async goToCalendarTab() { + await this.calendarTab.click(); + } + + async clickField(name: string) { + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${name}']/../../../div[last()]/div/div`, + ) + .click(); + } + + async clickFieldWithButton(name: string) { + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${name}']/../../../div[last()]/div/div`, + ) + .hover(); + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${name}']/../../../div[last()]/div/div[last()]/div/button`, + ) + .click(); + } + + async clickRelationEditButton(name: string) { + await this.page.getByRole('heading').filter({ hasText: name }).hover(); + await this.page + .locator(`//header[contains(., "${name}")]/div[last()]/div/button`) + .click(); + } + + async detachRelation(name: string) { + await this.page.locator(`//a[contains(., "${name}")]`).hover(); + await this.page + .locator(`, //a[contains(., "${name}")]/../div[last()]/div/div/button`) + .hover(); + await this.detachRelationButton.click(); + } + + async deleteRelationRecord(name: string) { + await this.page.locator(`//a[contains(., "${name}")]`).hover(); + await this.page + .locator(`, //a[contains(., "${name}")]/../div[last()]/div/div/button`) + .hover(); + await this.deleteButton.click(); + } + + async selectRelationRecord(name: string) { + await this.page + .locator(`//div[@data-testid="tooltip" and contains(., "${name}")]`) + .click(); + } + + async searchRelationRecord(name: string) { + await this.page.getByPlaceholder('Search').fill(name); + } + + async createNewRelationRecord() { + await this.page + .locator('//div[@data-testid="tooltip" and contains(., "Add New")]') + .click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/accountsSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/accountsSection.ts new file mode 100644 index 000000000..703cdffa6 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/accountsSection.ts @@ -0,0 +1,54 @@ +import { Locator, Page } from '@playwright/test'; + +export class AccountsSection { + private readonly addAccountButton: Locator; + private readonly deleteAccountButton: Locator; + private readonly addBlocklistField: Locator; + private readonly addBlocklistButton: Locator; + private readonly connectWithGoogleButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.addAccountButton = page.getByRole('button', { name: 'Add account' }); + this.deleteAccountButton = page + .getByTestId('tooltip') + .getByText('Remove account'); + this.addBlocklistField = page.getByPlaceholder( + 'eddy@gmail.com, @apple.com', + ); + this.addBlocklistButton = page.getByRole('button', { + name: 'Add to blocklist', + }); + this.connectWithGoogleButton = page.getByRole('button', { + name: 'Connect with Google', + }); + } + + async clickAddAccount() { + await this.addAccountButton.click(); + } + + async deleteAccount(email: string) { + await this.page + .locator(`//span[contains(., "${email}")]/../div/div/div/button`) + .click(); + await this.deleteAccountButton.click(); + } + + async addToBlockList(domain: string) { + await this.addBlocklistField.fill(domain); + await this.addBlocklistButton.click(); + } + + async removeFromBlocklist(domain: string) { + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${domain}')]/../../div[last()]/button`, + ) + .click(); + } + + async linkGoogleAccount() { + await this.connectWithGoogleButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/calendarSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/calendarSection.ts new file mode 100644 index 000000000..98ccba0d0 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/calendarSection.ts @@ -0,0 +1,30 @@ +import { Locator, Page } from '@playwright/test'; + +export class CalendarSection { + private readonly eventVisibilityEverythingOption: Locator; + private readonly eventVisibilityMetadataOption: Locator; + private readonly contactAutoCreation: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.eventVisibilityEverythingOption = page.locator( + 'input[value="SHARE_EVERYTHING"]', + ); + this.eventVisibilityMetadataOption = page.locator( + 'input[value="METADATA"]', + ); + this.contactAutoCreation = page.getByRole('checkbox').nth(1); + } + + async changeVisibilityToEverything() { + await this.eventVisibilityEverythingOption.click(); + } + + async changeVisibilityToMetadata() { + await this.eventVisibilityMetadataOption.click(); + } + + async toggleAutoCreation() { + await this.contactAutoCreation.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/dataModelSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/dataModelSection.ts new file mode 100644 index 000000000..0e36bd351 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/dataModelSection.ts @@ -0,0 +1,189 @@ +import { Locator, Page } from '@playwright/test'; + +export class DataModelSection { + private readonly searchObjectInput: Locator; + private readonly addObjectButton: Locator; + private readonly objectSingularNameInput: Locator; + private readonly objectPluralNameInput: Locator; + private readonly objectDescription: Locator; + private readonly synchronizeLabelAPIToggle: Locator; + private readonly objectAPISingularNameInput: Locator; + private readonly objectAPIPluralNameInput: Locator; + private readonly objectMoreOptionsButton: Locator; + private readonly editObjectButton: Locator; + private readonly deleteObjectButton: Locator; + private readonly activeSection: Locator; + private readonly inactiveSection: Locator; + private readonly searchFieldInput: Locator; + private readonly addFieldButton: Locator; + private readonly viewFieldDetailsMoreOptionsButton: Locator; + private readonly nameFieldInput: Locator; + private readonly descriptionFieldInput: Locator; + private readonly deactivateMoreOptionsButton: Locator; + private readonly activateMoreOptionsButton: Locator; + private readonly deactivateButton: Locator; // TODO: add attribute to make it one button + private readonly activateButton: Locator; + private readonly cancelButton: Locator; + private readonly saveButton: Locator; + + constructor(public readonly page: Page) { + this.searchObjectInput = page.getByPlaceholder('Search an object...'); + this.addObjectButton = page.getByRole('button', { name: 'Add object' }); + this.objectSingularNameInput = page.getByPlaceholder('Listing', { + exact: true, + }); + this.objectPluralNameInput = page.getByPlaceholder('Listings', { + exact: true, + }); + this.objectDescription = page.getByPlaceholder('Write a description'); + this.synchronizeLabelAPIToggle = page.getByRole('checkbox').nth(1); + this.objectAPISingularNameInput = page.getByPlaceholder('listing', { + exact: true, + }); + this.objectAPIPluralNameInput = page.getByPlaceholder('listings', { + exact: true, + }); + this.objectMoreOptionsButton = page.getByLabel('Object Options'); + this.editObjectButton = page.getByTestId('tooltip').getByText('Edit'); + this.deactivateMoreOptionsButton = page + .getByTestId('tooltip') + .getByText('Deactivate'); + this.activateMoreOptionsButton = page + .getByTestId('tooltip') + .getByText('Activate'); + this.deleteObjectButton = page.getByTestId('tooltip').getByText('Delete'); + this.activeSection = page.getByText('Active', { exact: true }); + this.inactiveSection = page.getByText('Inactive'); + this.searchFieldInput = page.getByPlaceholder('Search a field...'); + this.addFieldButton = page.getByRole('button', { name: 'Add field' }); + this.viewFieldDetailsMoreOptionsButton = page + .getByTestId('tooltip') + .getByText('View'); + this.nameFieldInput = page.getByPlaceholder('Employees'); + this.descriptionFieldInput = page.getByPlaceholder('Write a description'); + this.deactivateButton = page.getByRole('button', { name: 'Deactivate' }); + this.activateButton = page.getByRole('button', { name: 'Activate' }); + this.cancelButton = page.getByRole('button', { name: 'Cancel' }); + this.saveButton = page.getByRole('button', { name: 'Save' }); + } + + async searchObject(name: string) { + await this.searchObjectInput.fill(name); + } + + async clickAddObjectButton() { + await this.addObjectButton.click(); + } + + async typeObjectSingularName(name: string) { + await this.objectSingularNameInput.fill(name); + } + + async typeObjectPluralName(name: string) { + await this.objectPluralNameInput.fill(name); + } + + async typeObjectDescription(name: string) { + await this.objectDescription.fill(name); + } + + async toggleSynchronizeLabelAPI() { + await this.synchronizeLabelAPIToggle.click(); + } + + async typeObjectSingularAPIName(name: string) { + await this.objectAPISingularNameInput.fill(name); + } + + async typeObjectPluralAPIName(name: string) { + await this.objectAPIPluralNameInput.fill(name); + } + + async checkObjectDetails(name: string) { + await this.page.getByRole('link').filter({ hasText: name }).click(); + } + + async activateInactiveObject(name: string) { + await this.page + .locator(`//div[@title="${name}"]/../../div[last()]`) + .click(); + await this.activateButton.click(); + } + + // object can be deleted only if is custom and inactive + async deleteInactiveObject(name: string) { + await this.page + .locator(`//div[@title="${name}"]/../../div[last()]`) + .click(); + await this.deleteObjectButton.click(); + } + + async editObjectDetails() { + await this.objectMoreOptionsButton.click(); + await this.editObjectButton.click(); + } + + async deactivateObjectWithMoreOptions() { + await this.objectMoreOptionsButton.click(); + await this.deactivateButton.click(); + } + + async searchField(name: string) { + await this.searchFieldInput.fill(name); + } + + async checkFieldDetails(name: string) { + await this.page.locator(`//div[@title="${name}"]`).click(); + } + + async checkFieldDetailsWithButton(name: string) { + await this.page + .locator(`//div[@title="${name}"]/../../div[last()]`) + .click(); + await this.viewFieldDetailsMoreOptionsButton.click(); + } + + async deactivateFieldWithButton(name: string) { + await this.page + .locator(`//div[@title="${name}"]/../../div[last()]`) + .click(); + await this.deactivateMoreOptionsButton.click(); + } + + async activateFieldWithButton(name: string) { + await this.page + .locator(`//div[@title="${name}"]/../../div[last()]`) + .click(); + await this.activateMoreOptionsButton.click(); + } + + async clickAddFieldButton() { + await this.addFieldButton.click(); + } + + async typeFieldName(name: string) { + await this.nameFieldInput.clear(); + await this.nameFieldInput.fill(name); + } + + async typeFieldDescription(description: string) { + await this.descriptionFieldInput.clear(); + await this.descriptionFieldInput.fill(description); + } + + async clickInactiveSection() { + await this.inactiveSection.click(); + } + + async clickActiveSection() { + await this.activeSection.click(); + } + + async clickCancelButton() { + await this.cancelButton.click(); + } + + async clickSaveButton() { + await this.saveButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/developersSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/developersSection.ts new file mode 100644 index 000000000..0f7fec96f --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/developersSection.ts @@ -0,0 +1,123 @@ +import { Locator, Page } from '@playwright/test'; + +export class DevelopersSection { + private readonly readDocumentationButton: Locator; + private readonly createAPIKeyButton: Locator; + private readonly regenerateAPIKeyButton: Locator; + private readonly nameOfAPIKeyInput: Locator; + private readonly expirationDateAPIKeySelect: Locator; + private readonly createWebhookButton: Locator; + private readonly webhookURLInput: Locator; + private readonly webhookDescription: Locator; + private readonly webhookFilterObjectSelect: Locator; + private readonly webhookFilterActionSelect: Locator; + private readonly cancelButton: Locator; + private readonly saveButton: Locator; + private readonly deleteButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.readDocumentationButton = page.getByRole('link', { + name: 'Read documentation', + }); + this.createAPIKeyButton = page.getByRole('link', { + name: 'Create API Key', + }); + this.createWebhookButton = page.getByRole('link', { + name: 'Create Webhook', + }); + this.nameOfAPIKeyInput = page + .getByPlaceholder('E.g. backoffice integration') + .first(); + this.expirationDateAPIKeySelect = page + .locator('div') + .filter({ hasText: /^Never$/ }) + .nth(3); // good enough if expiration date will change only 1 time + this.regenerateAPIKeyButton = page.getByRole('button', { + name: 'Regenerate Key', + }); + this.webhookURLInput = page.getByPlaceholder('URL'); + this.webhookDescription = page.getByPlaceholder('Write a description'); + this.webhookFilterObjectSelect = page + .locator('div') + .filter({ hasText: /^All Objects$/ }) + .nth(3); // works only for first filter + this.webhookFilterActionSelect = page + .locator('div') + .filter({ hasText: /^All Actions$/ }) + .nth(3); // works only for first filter + this.cancelButton = page.getByRole('button', { name: 'Cancel' }); + this.saveButton = page.getByRole('button', { name: 'Save' }); + this.deleteButton = page.getByRole('button', { name: 'Delete' }); + } + + async openDocumentation() { + await this.readDocumentationButton.click(); + } + + async createAPIKey() { + await this.createAPIKeyButton.click(); + } + + async typeAPIKeyName(name: string) { + await this.nameOfAPIKeyInput.clear(); + await this.nameOfAPIKeyInput.fill(name); + } + + async selectAPIExpirationDate(date: string) { + await this.expirationDateAPIKeySelect.click(); + await this.page.getByText(date, { exact: true }).click(); + } + + async regenerateAPIKey() { + await this.regenerateAPIKeyButton.click(); + } + + async deleteAPIKey() { + await this.deleteButton.click(); + } + + async deleteWebhook() { + await this.deleteButton.click(); + } + + async createWebhook() { + await this.createWebhookButton.click(); + } + + async typeWebhookURL(url: string) { + await this.webhookURLInput.fill(url); + } + + async typeWebhookDescription(description: string) { + await this.webhookDescription.fill(description); + } + + async selectWebhookObject(object: string) { + // TODO: finish + } + + async selectWebhookAction(action: string) { + // TODO: finish + } + + async deleteWebhookFilter() { + // TODO: finish + } + + async clickCancelButton() { + await this.cancelButton.click(); + } + + async clickSaveButton() { + await this.saveButton.click(); + } + + async checkAPIKeyDetails(name: string) { + await this.page.locator(`//a/div[contains(.,'${name}')][first()]`).click(); + } + + async checkWebhookDetails(name: string) { + await this.page.locator(`//a/div[contains(.,'${name}')][first()]`).click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/emailsSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/emailsSection.ts new file mode 100644 index 000000000..23a83f8db --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/emailsSection.ts @@ -0,0 +1,61 @@ +import { Locator, Page } from '@playwright/test'; + +export class EmailsSection { + private readonly visibilityEverythingRadio: Locator; + private readonly visibilitySubjectRadio: Locator; + private readonly visibilityMetadataRadio: Locator; + private readonly autoCreationReceivedRadio: Locator; + private readonly autoCreationSentRadio: Locator; + private readonly autoCreationNoneRadio: Locator; + private readonly excludeNonProfessionalToggle: Locator; + private readonly excludeGroupToggle: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.visibilityEverythingRadio = page.locator( + 'input[value="SHARE_EVERYTHING"]', + ); + this.visibilitySubjectRadio = page.locator('input[value="SUBJECT"]'); + this.visibilityMetadataRadio = page.locator('input[value="METADATA"]'); + this.autoCreationReceivedRadio = page.locator( + 'input[value="SENT_AND_RECEIVED"]', + ); + this.autoCreationSentRadio = page.locator('input[value="SENT"]'); + this.autoCreationNoneRadio = page.locator('input[value="NONE"]'); + // first checkbox is advanced settings toggle + this.excludeNonProfessionalToggle = page.getByRole('checkbox').nth(1); + this.excludeGroupToggle = page.getByRole('checkbox').nth(2); + } + + async changeVisibilityToEverything() { + await this.visibilityEverythingRadio.click(); + } + + async changeVisibilityToSubject() { + await this.visibilitySubjectRadio.click(); + } + + async changeVisibilityToMetadata() { + await this.visibilityMetadataRadio.click(); + } + + async changeAutoCreationToAll() { + await this.autoCreationReceivedRadio.click(); + } + + async changeAutoCreationToSent() { + await this.autoCreationSentRadio.click(); + } + + async changeAutoCreationToNone() { + await this.autoCreationNoneRadio.click(); + } + + async toggleExcludeNonProfessional() { + await this.excludeNonProfessionalToggle.click(); + } + + async toggleExcludeGroup() { + await this.excludeGroupToggle.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/experienceSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/experienceSection.ts new file mode 100644 index 000000000..4ed6606c1 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/experienceSection.ts @@ -0,0 +1,55 @@ +import { Locator, Page } from '@playwright/test'; + +export class ExperienceSection { + private readonly lightThemeButton: Locator; + private readonly darkThemeButton: Locator; + private readonly timezoneDropdown: Locator; + private readonly dateFormatDropdown: Locator; + private readonly timeFormatDropdown: Locator; + private readonly searchInput: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.lightThemeButton = page.getByText('AaLight'); // it works + this.darkThemeButton = page.getByText('AaDark'); + this.timezoneDropdown = page.locator( + '//span[contains(., "Time zone")]/../div/div/div', + ); + this.dateFormatDropdown = page.locator( + '//span[contains(., "Date format")]/../div/div/div', + ); + this.timeFormatDropdown = page.locator( + '//span[contains(., "Time format")]/../div/div/div', + ); + this.searchInput = page.getByPlaceholder('Search'); + } + + async changeThemeToLight() { + await this.lightThemeButton.click(); + } + + async changeThemeToDark() { + await this.darkThemeButton.click(); + } + + async selectTimeZone(timezone: string) { + await this.timezoneDropdown.click(); + await this.page.getByText(timezone, { exact: true }).click(); + } + + async selectTimeZoneWithSearch(timezone: string) { + await this.timezoneDropdown.click(); + await this.searchInput.fill(timezone); + await this.page.getByText(timezone, { exact: true }).click(); + } + + async selectDateFormat(dateFormat: string) { + await this.dateFormatDropdown.click(); + await this.page.getByText(dateFormat, { exact: true }).click(); + } + + async selectTimeFormat(timeFormat: string) { + await this.timeFormatDropdown.click(); + await this.page.getByText(timeFormat, { exact: true }).click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/functionsSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/functionsSection.ts new file mode 100644 index 000000000..f8f42418a --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/functionsSection.ts @@ -0,0 +1,159 @@ +import { Locator, Page } from '@playwright/test'; + +export class FunctionsSection { + private readonly newFunctionButton: Locator; + private readonly functionNameInput: Locator; + private readonly functionDescriptionInput: Locator; + private readonly editorTab: Locator; + private readonly codeEditorField: Locator; + private readonly resetButton: Locator; + private readonly publishButton: Locator; + private readonly testButton: Locator; + private readonly testTab: Locator; + private readonly runFunctionButton: Locator; + private readonly inputField: Locator; + private readonly settingsTab: Locator; + private readonly searchVariableInput: Locator; + private readonly addVariableButton: Locator; + private readonly nameVariableInput: Locator; + private readonly valueVariableInput: Locator; + private readonly cancelVariableButton: Locator; + private readonly saveVariableButton: Locator; + private readonly editVariableButton: Locator; + private readonly deleteVariableButton: Locator; + private readonly cancelButton: Locator; + private readonly saveButton: Locator; + private readonly deleteButton: Locator; + + constructor(public readonly page: Page) { + this.newFunctionButton = page.getByRole('button', { name: 'New Function' }); + this.functionNameInput = page.getByPlaceholder('Name'); + this.functionDescriptionInput = page.getByPlaceholder('Description'); + this.editorTab = page.getByTestId('tab-editor'); + this.codeEditorField = page.getByTestId('dummyInput'); // TODO: fix + this.resetButton = page.getByRole('button', { name: 'Reset' }); + this.publishButton = page.getByRole('button', { name: 'Publish' }); + this.testButton = page.getByRole('button', { name: 'Test' }); + this.testTab = page.getByTestId('tab-test'); + this.runFunctionButton = page.getByRole('button', { name: 'Run Function' }); + this.inputField = page.getByTestId('dummyInput'); // TODO: fix + this.settingsTab = page.getByTestId('tab-settings'); + this.searchVariableInput = page.getByPlaceholder('Search a variable'); + this.addVariableButton = page.getByRole('button', { name: 'Add Variable' }); + this.nameVariableInput = page.getByPlaceholder('Name').nth(1); + this.valueVariableInput = page.getByPlaceholder('Value'); + this.cancelVariableButton = page.locator('.css-uwqduk').first(); // TODO: fix + this.saveVariableButton = page.locator('.css-uwqduk').nth(1); // TODO: fix + this.editVariableButton = page.getByText('Edit', { exact: true }); + this.deleteVariableButton = page.getByText('Delete', { exact: true }); + this.cancelButton = page.getByRole('button', { name: 'Cancel' }); + this.saveButton = page.getByRole('button', { name: 'Save' }); + this.deleteButton = page.getByRole('button', { name: 'Delete function' }); + } + + async clickNewFunction() { + await this.newFunctionButton.click(); + } + + async typeFunctionName(name: string) { + await this.functionNameInput.fill(name); + } + + async typeFunctionDescription(description: string) { + await this.functionDescriptionInput.fill(description); + } + + async checkFunctionDetails(name: string) { + await this.page.getByRole('link', { name: `${name} nodejs18.x` }).click(); + } + + async clickEditorTab() { + await this.editorTab.click(); + } + + async clickResetButton() { + await this.resetButton.click(); + } + + async clickPublishButton() { + await this.publishButton.click(); + } + + async clickTestButton() { + await this.testButton.click(); + } + + async typeFunctionCode() { + // TODO: finish once utils are merged + } + + async clickTestTab() { + await this.testTab.click(); + } + + async runFunction() { + await this.runFunctionButton.click(); + } + + async typeFunctionInput() { + // TODO: finish once utils are merged + } + + async clickSettingsTab() { + await this.settingsTab.click(); + } + + async searchVariable(name: string) { + await this.searchVariableInput.fill(name); + } + + async addVariable() { + await this.addVariableButton.click(); + } + + async typeVariableName(name: string) { + await this.nameVariableInput.fill(name); + } + + async typeVariableValue(value: string) { + await this.valueVariableInput.fill(value); + } + + async editVariable(name: string) { + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${name}')]/../../div[last()]/div/div/button`, + ) + .click(); + await this.editVariableButton.click(); + } + + async deleteVariable(name: string) { + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${name}')]/../../div[last()]/div/div/button`, + ) + .click(); + await this.deleteVariableButton.click(); + } + + async cancelVariable() { + await this.cancelVariableButton.click(); + } + + async saveVariable() { + await this.saveVariableButton.click(); + } + + async clickCancelButton() { + await this.cancelButton.click(); + } + + async clickSaveButton() { + await this.saveButton.click(); + } + + async clickDeleteButton() { + await this.deleteButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/generalSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/generalSection.ts new file mode 100644 index 000000000..d936a2e9e --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/generalSection.ts @@ -0,0 +1,29 @@ +import { Locator, Page } from '@playwright/test'; + +export class GeneralSection { + private readonly workspaceNameField: Locator; + private readonly supportSwitch: Locator; + private readonly deleteWorkspaceButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.workspaceNameField = page.getByPlaceholder('Apple'); + this.supportSwitch = page.getByRole('checkbox').nth(1); + this.deleteWorkspaceButton = page.getByRole('button', { + name: 'Delete workspace', + }); + } + + async changeWorkspaceName(workspaceName: string) { + await this.workspaceNameField.clear(); + await this.workspaceNameField.fill(workspaceName); + } + + async changeSupportSwitchState() { + await this.supportSwitch.click(); + } + + async clickDeleteWorkSpaceButton() { + await this.deleteWorkspaceButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/membersSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/membersSection.ts new file mode 100644 index 000000000..4f1086657 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/membersSection.ts @@ -0,0 +1,48 @@ +import { Locator, Page } from '@playwright/test'; + +export class MembersSection { + private readonly inviteMembersField: Locator; + private readonly inviteMembersButton: Locator; + private readonly inviteLinkButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.inviteMembersField = page.getByPlaceholder( + 'tim@apple.com, jony.ive@apple', + ); + this.inviteMembersButton = page.getByRole('button', { name: 'Invite' }); + this.inviteLinkButton = page.getByRole('button', { name: 'Copy link' }); + } + + async copyInviteLink() { + await this.inviteLinkButton.click(); + } + + async sendInviteEmail(email: string) { + await this.inviteMembersField.click(); + await this.inviteMembersField.fill(email); + await this.inviteMembersButton.click(); + } + + async deleteMember(email: string) { + await this.page + .locator(`//div[contains(., '${email}')]/../../div[last()]/div/button`) + .click(); + } + + async deleteInviteEmail(email: string) { + await this.page + .locator( + `//div[contains(., '${email}')]/../../div[last()]/div/button[first()]`, + ) + .click(); + } + + async refreshInviteEmail(email: string) { + await this.page + .locator( + `//div[contains(., '${email}')]/../../div[last()]/div/button[last()]`, + ) + .click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/newFieldSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/newFieldSection.ts new file mode 100644 index 000000000..50b37e03a --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/newFieldSection.ts @@ -0,0 +1,250 @@ +import { Locator, Page } from '@playwright/test'; + +export class NewFieldSection { + private readonly searchTypeFieldInput: Locator; + private readonly currencyFieldLink: Locator; + private readonly currencyDefaultUnitSelect: Locator; + private readonly emailsFieldLink: Locator; + private readonly linksFieldLink: Locator; + private readonly phonesFieldLink: Locator; + private readonly addressFieldLink: Locator; + private readonly textFieldLink: Locator; + private readonly numberFieldLink: Locator; + private readonly decreaseDecimalsButton: Locator; + private readonly decimalsNumberInput: Locator; + private readonly increaseDecimalsButton: Locator; + private readonly booleanFieldLink: Locator; + private readonly defaultBooleanSelect: Locator; + private readonly dateTimeFieldLink: Locator; + private readonly dateFieldLink: Locator; + private readonly relativeDateToggle: Locator; + private readonly selectFieldLink: Locator; + private readonly multiSelectFieldLink: Locator; + private readonly setAsDefaultOptionButton: Locator; + private readonly removeOptionButton: Locator; + private readonly addOptionButton: Locator; + private readonly ratingFieldLink: Locator; + private readonly JSONFieldLink: Locator; + private readonly arrayFieldLink: Locator; + private readonly relationFieldLink: Locator; + private readonly relationTypeSelect: Locator; + private readonly objectDestinationSelect: Locator; + private readonly relationFieldNameInput: Locator; + private readonly fullNameFieldLink: Locator; + private readonly UUIDFieldLink: Locator; + private readonly nameFieldInput: Locator; + private readonly descriptionFieldInput: Locator; + + constructor(public readonly page: Page) { + this.searchTypeFieldInput = page.getByPlaceholder('Search a type'); + this.currencyFieldLink = page.getByRole('link', { name: 'Currency' }); + this.currencyDefaultUnitSelect = page.locator( + "//span[contains(., 'Default Unit')]/../div", + ); + this.emailsFieldLink = page.getByRole('link', { name: 'Emails' }).nth(1); + this.linksFieldLink = page.getByRole('link', { name: 'Links' }); + this.phonesFieldLink = page.getByRole('link', { name: 'Phones' }); + this.addressFieldLink = page.getByRole('link', { name: 'Address' }); + this.textFieldLink = page.getByRole('link', { name: 'Text' }); + this.numberFieldLink = page.getByRole('link', { name: 'Number' }); + this.decreaseDecimalsButton = page.locator( + "//div[contains(., 'Number of decimals')]/../div[last()]/div/div/button[2]", + ); + this.decimalsNumberInput = page.locator( + // would be better if first div was span tag + "//div[contains(., 'Number of decimals')]/../div[last()]/div/div/div/div/input[2]", + ); + this.increaseDecimalsButton = page.locator( + "//div[contains(., 'Number of decimals')]/../div[last()]/div/div/button[3]", + ); + this.booleanFieldLink = page.getByRole('link', { name: 'True/False' }); + this.defaultBooleanSelect = page.locator( + "//span[contains(., 'Default Value')]/../div", + ); + this.dateTimeFieldLink = page.getByRole('link', { name: 'Date and Time' }); + this.dateFieldLink = page.getByRole('link', { name: 'Date' }); + this.relativeDateToggle = page.getByRole('checkbox').nth(1); + this.selectFieldLink = page.getByRole('link', { name: 'Select' }); + this.multiSelectFieldLink = page.getByRole('link', { + name: 'Multi-select', + }); + this.setAsDefaultOptionButton = page + .getByTestId('tooltip') + .getByText('Set as default'); + this.removeOptionButton = page + .getByTestId('tooltip') + .getByText('Remove option'); + this.addOptionButton = page.getByRole('button', { name: 'Add option' }); + this.ratingFieldLink = page.getByRole('link', { name: 'Rating' }); + this.JSONFieldLink = page.getByRole('link', { name: 'JSON' }); + this.arrayFieldLink = page.getByRole('link', { name: 'Array' }); + this.relationFieldLink = page.getByRole('link', { name: 'Relation' }); + this.relationTypeSelect = page.locator( + "//span[contains(., 'Relation type')]/../div", + ); + this.objectDestinationSelect = page.locator( + "//span[contains(., 'Object destination')]/../div", + ); + this.relationIconSelect = page.getByLabel('Click to select icon (').nth(1); + this.relationFieldNameInput = page.getByPlaceholder('Field name'); + this.fullNameFieldLink = page.getByRole('link', { name: 'Full Name' }); + this.UUIDFieldLink = page.getByRole('link', { name: 'Unique ID' }); + this.nameFieldInput = page.getByPlaceholder('Employees'); + this.descriptionFieldInput = page.getByPlaceholder('Write a description'); + } + + async searchTypeField(name: string) { + await this.searchTypeFieldInput.fill(name); + } + + async clickCurrencyType() { + await this.currencyFieldLink.click(); + } + + async selectDefaultUnit(name: string) { + await this.currencyDefaultUnitSelect.click(); + await this.page.getByTestId('tooltip').filter({ hasText: name }).click(); + } + + async clickEmailsType() { + await this.emailsFieldLink.click(); + } + + async clickLinksType() { + await this.linksFieldLink.click(); + } + + async clickPhonesType() { + await this.phonesFieldLink.click(); + } + + async clickAddressType() { + await this.addressFieldLink.click(); + } + + async clickTextType() { + await this.textFieldLink.click(); + } + + async clickNumberType() { + await this.numberFieldLink.click(); + } + + async decreaseDecimals() { + await this.decreaseDecimalsButton.click(); + } + + async typeNumberOfDecimals(amount: number) { + await this.decimalsNumberInput.fill(String(amount)); + } + + async increaseDecimals() { + await this.increaseDecimalsButton.click(); + } + + async clickBooleanType() { + await this.booleanFieldLink.click(); + } + + // either True of False + async selectDefaultBooleanValue(value: string) { + await this.defaultBooleanSelect.click(); + await this.page.getByTestId('tooltip').filter({ hasText: value }).click(); + } + + async clickDateTimeType() { + await this.dateTimeFieldLink.click(); + } + + async clickDateType() { + await this.dateFieldLink.click(); + } + + async toggleRelativeDate() { + await this.relativeDateToggle.click(); + } + + async clickSelectType() { + await this.selectFieldLink.click(); + } + + async clickMultiSelectType() { + await this.multiSelectFieldLink.click(); + } + + async addSelectOption() { + await this.addOptionButton.click(); + } + + async setOptionAsDefault() { + // TODO: finish + await this.setAsDefaultOptionButton.click(); + } + + async deleteSelectOption() { + // TODO: finish + await this.removeOptionButton.click(); + } + + async changeOptionAPIName() { + // TODO: finish + } + + async changeOptionColor() { + // TODO: finish + } + + async changeOptionName() { + // TODO: finish + } + + async clickRatingType() { + await this.ratingFieldLink.click(); + } + + async clickJSONType() { + await this.JSONFieldLink.click(); + } + + async clickArrayType() { + await this.arrayFieldLink.click(); + } + + async clickRelationType() { + await this.relationFieldLink.click(); + } + + // either 'Has many' or 'Belongs to one' + async selectRelationType(name: string) { + await this.relationTypeSelect.click(); + await this.page.getByTestId('tooltip').filter({ hasText: name }).click(); + } + + async selectObjectDestination(name: string) { + await this.objectDestinationSelect.click(); + await this.page.getByTestId('tooltip').filter({ hasText: name }).click(); + } + + async typeRelationName(name: string) { + await this.relationFieldNameInput.clear(); + await this.relationFieldNameInput.fill(name); + } + + async clickFullNameType() { + await this.fullNameFieldLink.click(); + } + + async clickUUIDType() { + await this.UUIDFieldLink.click(); + } + + async typeFieldName(name: string) { + await this.nameFieldInput.clear(); + await this.nameFieldInput.fill(name); + } + + async typeFieldDescription(description: string) { + await this.descriptionFieldInput.clear(); + await this.descriptionFieldInput.fill(description); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/profileSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/profileSection.ts new file mode 100644 index 000000000..32feb9ac5 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/profileSection.ts @@ -0,0 +1,44 @@ +import { Locator, Page } from '@playwright/test'; + +export class ProfileSection { + private readonly firstNameField: Locator; + private readonly lastNameField: Locator; + private readonly emailField: Locator; + private readonly changePasswordButton: Locator; + private readonly deleteAccountButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.firstNameField = page.getByPlaceholder('Tim'); + this.lastNameField = page.getByPlaceholder('Cook'); + this.emailField = page.getByRole('textbox').nth(2); + this.changePasswordButton = page.getByRole('button', { + name: 'Change Password', + }); + this.deleteAccountButton = page.getByRole('button', { + name: 'Delete account', + }); + } + + async changeFirstName(firstName: string) { + await this.firstNameField.clear(); + await this.firstNameField.fill(firstName); + } + + async changeLastName(lastName: string) { + await this.lastNameField.clear(); + await this.lastNameField.fill(lastName); + } + + async getEmail() { + await this.emailField.textContent(); + } + + async sendChangePasswordEmail() { + await this.changePasswordButton.click(); + } + + async deleteAccount() { + await this.deleteAccountButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/securitySection.ts b/packages/twenty-e2e-testing/lib/pom/settings/securitySection.ts new file mode 100644 index 000000000..01b83b515 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/securitySection.ts @@ -0,0 +1,13 @@ +import { Locator, Page } from '@playwright/test'; + +export class SecuritySection { + private readonly inviteByLinkToggle: Locator; + + constructor(public readonly page: Page) { + this.inviteByLinkToggle = page.locator('input[type="checkbox"]').nth(1); + } + + async toggleInviteByLink() { + await this.inviteByLinkToggle.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settingsPage.ts b/packages/twenty-e2e-testing/lib/pom/settingsPage.ts new file mode 100644 index 000000000..c753bb8d0 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settingsPage.ts @@ -0,0 +1,104 @@ +import { Locator, Page } from '@playwright/test'; + +export class SettingsPage { + private readonly exitSettingsLink: Locator; + private readonly profileLink: Locator; + private readonly experienceLink: Locator; + private readonly accountsLink: Locator; + private readonly emailsLink: Locator; + private readonly calendarsLink: Locator; + private readonly generalLink: Locator; + private readonly membersLink: Locator; + private readonly dataModelLink: Locator; + private readonly developersLink: Locator; + private readonly functionsLink: Locator; + private readonly securityLink: Locator; + private readonly integrationsLink: Locator; + private readonly releasesLink: Locator; + private readonly logoutLink: Locator; + private readonly advancedToggle: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.exitSettingsLink = page.getByRole('button', { name: 'Exit Settings' }); + this.profileLink = page.getByRole('link', { name: 'Profile' }); + this.experienceLink = page.getByRole('link', { name: 'Experience' }); + this.accountsLink = page.getByRole('link', { name: 'Accounts' }); + this.emailsLink = page.getByRole('link', { name: 'Emails', exact: true }); + this.calendarsLink = page.getByRole('link', { name: 'Calendars' }); + this.generalLink = page.getByRole('link', { name: 'General' }); + this.membersLink = page.getByRole('link', { name: 'Members' }); + this.dataModelLink = page.getByRole('link', { name: 'Data model' }); + this.developersLink = page.getByRole('link', { name: 'Developers' }); + this.functionsLink = page.getByRole('link', { name: 'Functions' }); + this.integrationsLink = page.getByRole('link', { name: 'Integrations' }); + this.securityLink = page.getByRole('link', { name: 'Security' }); + this.releasesLink = page.getByRole('link', { name: 'Releases' }); + this.logoutLink = page.getByText('Logout'); + this.advancedToggle = page.locator('input[type="checkbox"]').first(); + } + + async leaveSettingsPage() { + await this.exitSettingsLink.click(); + } + + async goToProfileSection() { + await this.profileLink.click(); + } + + async goToExperienceSection() { + await this.experienceLink.click(); + } + + async goToAccountsSection() { + await this.accountsLink.click(); + } + + async goToEmailsSection() { + await this.emailsLink.click(); + } + + async goToCalendarsSection() { + await this.calendarsLink.click(); + } + + async goToGeneralSection() { + await this.generalLink.click(); + } + + async goToMembersSection() { + await this.membersLink.click(); + } + + async goToDataModelSection() { + await this.dataModelLink.click(); + } + + async goToDevelopersSection() { + await this.developersLink.click(); + } + + async goToFunctionsSection() { + await this.functionsLink.click(); + } + + async goToSecuritySection() { + await this.securityLink.click(); + } + + async goToIntegrationsSection() { + await this.integrationsLink.click(); + } + + async goToReleasesIntegration() { + await this.releasesLink.click(); + } + + async logout() { + await this.logoutLink.click(); + } + + async toggleAdvancedSettings() { + await this.advancedToggle.click(); + } +} diff --git a/packages/twenty-e2e-testing/tests/companies.spec.ts b/packages/twenty-e2e-testing/tests/companies.spec.ts index b8f78c7ec..1aa53d613 100644 --- a/packages/twenty-e2e-testing/tests/companies.spec.ts +++ b/packages/twenty-e2e-testing/tests/companies.spec.ts @@ -1,7 +1,4 @@ import { test, expect } from '../lib/fixtures/screenshot'; -import { config } from 'dotenv'; -import path = require('path'); -config({ path: path.resolve(__dirname, '..', '.env') }); test.describe('Basic check', () => { test('Checking if table in Companies is visible', async ({ page }) => { From 2ab4aa13778fc8e484684098d094030aeb339e16 Mon Sep 17 00:00:00 2001 From: Guillim Date: Thu, 7 Nov 2024 15:57:12 +0100 Subject: [PATCH 003/100] Fix npx nx run start (twenty:server:worker was not triggered) (#8384) twenty:server:worker was not triggered in npx nx run start Co-authored-by: guillim --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2d4484289..41e8e4b35 100644 --- a/package.json +++ b/package.json @@ -350,7 +350,7 @@ "version": "0.2.1", "nx": {}, "scripts": { - "start": "npx nx run-many -t start worker -p twenty-server twenty-front" + "start": "npx concurrently --kill-others 'npx nx run-many -t start -p twenty-server twenty-front' 'npx wait-on tcp:3000 && npx nx run twenty-server:worker'" }, "workspaces": { "packages": [ From ac233b771c509da6f448a622547542dc46497e3d Mon Sep 17 00:00:00 2001 From: Marie <51697796+ijreilly@users.noreply.github.com> Date: Thu, 7 Nov 2024 17:09:19 +0100 Subject: [PATCH 004/100] Simplify multi-object picker logic with search (#8010) Simplifying the logic around multi-object pickers and search by getting rid of the behaviour that keeped selected elements even when they did not match the search filter (eg keeping selected record "Brian Chesky" in dropdown even when search input is "Qonto"). This allows us to simplify the fetch queries around the search to only do one query. --------- Co-authored-by: Lucas Bordeau --- .../ActivityTargetInlineCellEditMode.tsx | 2 + ...onFromManyFieldInputMultiRecordsEffect.tsx | 2 +- ...ctRecordMultiSelectComponentFamilyState.ts | 2 +- ...ctMatchesFilterRecordsIdsComponentState.ts | 8 + ...etInlineCellEditModeMultiRecordsEffect.tsx | 67 +++------ ...EditModeMultiRecordsSearchFilterEffect.tsx | 54 +++++++ .../__tests__/useMultiObjectSearch.test.tsx | 141 ------------------ ...ltFormattedAsObjectRecordForSelectArray.ts | 40 +++-- .../hooks/useMultiObjectSearch.ts | 76 ---------- ...atchesSearchFilterAndSelectedItemsQuery.ts | 120 --------------- ...archMatchesSearchFilterAndToSelectQuery.ts | 110 -------------- ...ltiObjectSearchMatchesSearchFilterQuery.ts | 75 ++++++++++ .../useMultiObjectSearchSelectedItemsQuery.ts | 2 +- .../formatMultiObjectRecordSearchResults.ts | 16 ++ .../types/ObjectRecordForSelect.ts | 9 ++ .../types/SelectedObjectRecordId.ts | 4 + 16 files changed, 214 insertions(+), 514 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/record-field/states/objectRecordMultiSelectMatchesFilterRecordsIdsComponentState.ts create mode 100644 packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect.tsx delete mode 100644 packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectSearch.test.tsx delete mode 100644 packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearch.ts delete mode 100644 packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery.ts delete mode 100644 packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndToSelectQuery.ts create mode 100644 packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterQuery.ts create mode 100644 packages/twenty-front/src/modules/object-record/relation-picker/utils/formatMultiObjectRecordSearchResults.ts create mode 100644 packages/twenty-front/src/modules/object-record/types/ObjectRecordForSelect.ts create mode 100644 packages/twenty-front/src/modules/object-record/types/SelectedObjectRecordId.ts diff --git a/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx b/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx index 5b9d62b6c..7719f4576 100644 --- a/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx +++ b/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx @@ -27,6 +27,7 @@ import { import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { ActivityTargetInlineCellEditModeMultiRecordsEffect } from '@/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsEffect'; +import { ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect } from '@/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect'; import { MultiRecordSelect } from '@/object-record/relation-picker/components/MultiRecordSelect'; import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope'; import { prefillRecord } from '@/object-record/utils/prefillRecord'; @@ -287,6 +288,7 @@ export const ActivityTargetInlineCellEditMode = ({ + diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationFromManyFieldInputMultiRecordsEffect.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationFromManyFieldInputMultiRecordsEffect.tsx index 6f42a7fba..866edfdad 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationFromManyFieldInputMultiRecordsEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationFromManyFieldInputMultiRecordsEffect.tsx @@ -5,10 +5,10 @@ import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useOb import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useRelationField } from '@/object-record/record-field/meta-types/hooks/useRelationField'; import { objectRecordMultiSelectComponentFamilyState } from '@/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState'; -import { ObjectRecordForSelect } from '@/object-record/relation-picker/hooks/useMultiObjectSearch'; import { useRelationPickerEntitiesOptions } from '@/object-record/relation-picker/hooks/useRelationPickerEntitiesOptions'; import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; diff --git a/packages/twenty-front/src/modules/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState.ts index 0e15c962e..e4150ae02 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState.ts @@ -1,4 +1,4 @@ -import { ObjectRecordForSelect } from '@/object-record/relation-picker/hooks/useMultiObjectSearch'; +import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect'; import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; export type ObjectRecordAndSelected = ObjectRecordForSelect & { diff --git a/packages/twenty-front/src/modules/object-record/record-field/states/objectRecordMultiSelectMatchesFilterRecordsIdsComponentState.ts b/packages/twenty-front/src/modules/object-record/record-field/states/objectRecordMultiSelectMatchesFilterRecordsIdsComponentState.ts new file mode 100644 index 000000000..bfaebeaa8 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/states/objectRecordMultiSelectMatchesFilterRecordsIdsComponentState.ts @@ -0,0 +1,8 @@ +import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect'; +import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; + +export const objectRecordMultiSelectMatchesFilterRecordsIdsComponentState = + createComponentState({ + key: 'objectRecordMultiSelectMatchesFilterRecordsIdsComponentState', + defaultValue: [], + }); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsEffect.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsEffect.tsx index 425877c20..26981f3c0 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsEffect.tsx @@ -7,15 +7,11 @@ import { } from 'recoil'; import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useObjectRecordMultiSelectScopedStates'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { objectRecordMultiSelectComponentFamilyState } from '@/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState'; -import { useRelationPickerScopedStates } from '@/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates'; -import { - ObjectRecordForSelect, - SelectedObjectRecordId, - useMultiObjectSearch, -} from '@/object-record/relation-picker/hooks/useMultiObjectSearch'; +import { objectRecordMultiSelectMatchesFilterRecordsIdsComponentState } from '@/object-record/record-field/states/objectRecordMultiSelectMatchesFilterRecordsIdsComponentState'; import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; +import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect'; +import { SelectedObjectRecordId } from '@/object-record/types/SelectedObjectRecordId'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; @@ -30,43 +26,14 @@ export const ActivityTargetInlineCellEditModeMultiRecordsEffect = ({ const { objectRecordsIdsMultiSelectState, objectRecordMultiSelectCheckedRecordsIdsState, - recordMultiSelectIsLoadingState, } = useObjectRecordMultiSelectScopedStates(scopeId); const [objectRecordsIdsMultiSelect, setObjectRecordsIdsMultiSelect] = useRecoilState(objectRecordsIdsMultiSelectState); - const setRecordMultiSelectIsLoading = useSetRecoilState( - recordMultiSelectIsLoadingState, + const setObjectRecordMultiSelectCheckedRecordsIds = useSetRecoilState( + objectRecordMultiSelectCheckedRecordsIdsState, ); - const relationPickerScopedId = useAvailableScopeIdOrThrow( - RelationPickerScopeInternalContext, - ); - - const { relationPickerSearchFilterState } = useRelationPickerScopedStates({ - relationPickerScopedId, - }); - const relationPickerSearchFilter = useRecoilValue( - relationPickerSearchFilterState, - ); - - const { filteredSelectedObjectRecords, loading, objectRecordsToSelect } = - useMultiObjectSearch({ - searchFilterValue: relationPickerSearchFilter, - excludedObjects: [ - CoreObjectNameSingular.Task, - CoreObjectNameSingular.Note, - ], - selectedObjectRecordIds, - excludedObjectRecordIds: [], - limit: 10, - }); - - const [ - objectRecordMultiSelectCheckedRecordsIds, - setObjectRecordMultiSelectCheckedRecordsIds, - ] = useRecoilState(objectRecordMultiSelectCheckedRecordsIdsState); - const updateRecords = useRecoilCallback( ({ snapshot, set }) => (newRecords: ObjectRecordForSelect[]) => { @@ -80,6 +47,10 @@ export const ActivityTargetInlineCellEditModeMultiRecordsEffect = ({ ) .getValue(); + const objectRecordMultiSelectCheckedRecordsIds = snapshot + .getLoadable(objectRecordMultiSelectCheckedRecordsIdsState) + .getValue(); + const newRecordWithSelected = { ...newRecord, selected: objectRecordMultiSelectCheckedRecordsIds.some( @@ -103,23 +74,25 @@ export const ActivityTargetInlineCellEditModeMultiRecordsEffect = ({ } } }, - [objectRecordMultiSelectCheckedRecordsIds, scopeId], + [objectRecordMultiSelectCheckedRecordsIdsState, scopeId], + ); + + const matchesSearchFilterObjectRecords = useRecoilValue( + objectRecordMultiSelectMatchesFilterRecordsIdsComponentState({ + scopeId, + }), ); useEffect(() => { - const allRecords = [ - ...(filteredSelectedObjectRecords ?? []), - ...(objectRecordsToSelect ?? []), - ]; + const allRecords = matchesSearchFilterObjectRecords ?? []; updateRecords(allRecords); const allRecordsIds = allRecords.map((record) => record.record.id); if (!isDeeplyEqual(allRecordsIds, objectRecordsIdsMultiSelect)) { setObjectRecordsIdsMultiSelect(allRecordsIds); } }, [ - filteredSelectedObjectRecords, + matchesSearchFilterObjectRecords, objectRecordsIdsMultiSelect, - objectRecordsToSelect, setObjectRecordsIdsMultiSelect, updateRecords, ]); @@ -130,9 +103,5 @@ export const ActivityTargetInlineCellEditModeMultiRecordsEffect = ({ ); }, [selectedObjectRecordIds, setObjectRecordMultiSelectCheckedRecordsIds]); - useEffect(() => { - setRecordMultiSelectIsLoading(loading); - }, [loading, setRecordMultiSelectIsLoading]); - return <>; }; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect.tsx new file mode 100644 index 000000000..c216122c1 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect.tsx @@ -0,0 +1,54 @@ +import { useEffect } from 'react'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; + +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { objectRecordMultiSelectMatchesFilterRecordsIdsComponentState } from '@/object-record/record-field/states/objectRecordMultiSelectMatchesFilterRecordsIdsComponentState'; +import { useRelationPickerScopedStates } from '@/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates'; +import { useMultiObjectSearchMatchesSearchFilterQuery } from '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterQuery'; +import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; +import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; + +export const ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect = + () => { + const scopeId = useAvailableScopeIdOrThrow( + RelationPickerScopeInternalContext, + ); + + const setRecordMultiSelectMatchesFilterRecords = useSetRecoilState( + objectRecordMultiSelectMatchesFilterRecordsIdsComponentState({ + scopeId, + }), + ); + + const relationPickerScopedId = useAvailableScopeIdOrThrow( + RelationPickerScopeInternalContext, + ); + + const { relationPickerSearchFilterState } = useRelationPickerScopedStates({ + relationPickerScopedId, + }); + const relationPickerSearchFilter = useRecoilValue( + relationPickerSearchFilterState, + ); + + const { matchesSearchFilterObjectRecords } = + useMultiObjectSearchMatchesSearchFilterQuery({ + excludedObjects: [ + CoreObjectNameSingular.Task, + CoreObjectNameSingular.Note, + ], + searchFilterValue: relationPickerSearchFilter, + limit: 10, + }); + + useEffect(() => { + setRecordMultiSelectMatchesFilterRecords( + matchesSearchFilterObjectRecords, + ); + }, [ + setRecordMultiSelectMatchesFilterRecords, + matchesSearchFilterObjectRecords, + ]); + + return <>; + }; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectSearch.test.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectSearch.test.tsx deleted file mode 100644 index 0f27bd796..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectSearch.test.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { - MultiObjectSearch, - ObjectRecordForSelect, - SelectedObjectRecordId, - useMultiObjectSearch, -} from '@/object-record/relation-picker/hooks/useMultiObjectSearch'; -import { useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery } from '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery'; -import { useMultiObjectSearchMatchesSearchFilterAndToSelectQuery } from '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndToSelectQuery'; -import { useMultiObjectSearchSelectedItemsQuery } from '@/object-record/relation-picker/hooks/useMultiObjectSearchSelectedItemsQuery'; -import { renderHook } from '@testing-library/react'; -import { FieldMetadataType } from '~/generated/graphql'; - -jest.mock( - '@/object-record/relation-picker/hooks/useMultiObjectSearchSelectedItemsQuery', -); -jest.mock( - '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery', -); -jest.mock( - '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndToSelectQuery', -); - -const objectData: ObjectMetadataItem[] = [ - { - createdAt: 'createdAt', - id: 'id', - isActive: true, - isCustom: true, - isSystem: false, - isRemote: false, - labelPlural: 'labelPlural', - labelSingular: 'labelSingular', - namePlural: 'namePlural', - nameSingular: 'nameSingular', - isLabelSyncedWithName: false, - updatedAt: 'updatedAt', - fields: [ - { - id: 'f6a0a73a-5ee6-442e-b764-39b682471240', - name: 'id', - label: 'id', - type: FieldMetadataType.Uuid, - createdAt: '2024-01-01T00:00:00.000Z', - updatedAt: '2024-01-01T00:00:00.000Z', - isActive: true, - }, - ], - indexMetadatas: [], - }, -]; - -describe('useMultiObjectSearch', () => { - const selectedObjectRecordIds: SelectedObjectRecordId[] = [ - { objectNameSingular: 'object1', id: '1' }, - { objectNameSingular: 'object2', id: '2' }, - ]; - const searchFilterValue = 'searchValue'; - const limit = 5; - const excludedObjectRecordIds: SelectedObjectRecordId[] = [ - { objectNameSingular: 'object3', id: '3' }, - { objectNameSingular: 'object4', id: '4' }, - ]; - const excludedObjects: CoreObjectNameSingular[] = []; - - const selectedObjectRecords: ObjectRecordForSelect[] = [ - { - objectMetadataItem: objectData[0], - record: { - __typename: 'ObjectRecord', - id: '1', - createdAt: 'createdAt', - updatedAt: 'updatedAt', - }, - recordIdentifier: { - id: '1', - name: 'name', - }, - }, - ]; - const selectedObjectRecordsLoading = false; - - const selectedAndMatchesSearchFilterObjectRecords: ObjectRecordForSelect[] = - []; - const selectedAndMatchesSearchFilterObjectRecordsLoading = false; - - const toSelectAndMatchesSearchFilterObjectRecords: ObjectRecordForSelect[] = - []; - const toSelectAndMatchesSearchFilterObjectRecordsLoading = false; - - beforeEach(() => { - (useMultiObjectSearchSelectedItemsQuery as jest.Mock).mockReturnValue({ - selectedObjectRecords, - selectedObjectRecordsLoading, - }); - - ( - useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery as jest.Mock - ).mockReturnValue({ - selectedAndMatchesSearchFilterObjectRecords, - selectedAndMatchesSearchFilterObjectRecordsLoading, - }); - - ( - useMultiObjectSearchMatchesSearchFilterAndToSelectQuery as jest.Mock - ).mockReturnValue({ - toSelectAndMatchesSearchFilterObjectRecords, - toSelectAndMatchesSearchFilterObjectRecordsLoading, - }); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('should return the correct object records and loading state', () => { - const { result } = renderHook(() => - useMultiObjectSearch({ - searchFilterValue, - selectedObjectRecordIds, - limit, - excludedObjectRecordIds, - excludedObjects, - }), - ); - - const expected: MultiObjectSearch = { - selectedObjectRecords, - filteredSelectedObjectRecords: - selectedAndMatchesSearchFilterObjectRecords, - objectRecordsToSelect: toSelectAndMatchesSearchFilterObjectRecords, - loading: - selectedAndMatchesSearchFilterObjectRecordsLoading || - toSelectAndMatchesSearchFilterObjectRecordsLoading || - selectedObjectRecordsLoading, - }; - - expect(result.current).toEqual(expected); - }); -}); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray.ts b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray.ts index a1047478a..ebac778c4 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray.ts +++ b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray.ts @@ -4,7 +4,8 @@ import { useRecoilValue } from 'recoil'; import { objectMetadataItemsByNamePluralMapSelector } from '@/object-metadata/states/objectMetadataItemsByNamePluralMapSelector'; import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier'; import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection'; -import { ObjectRecordForSelect } from '@/object-record/relation-picker/hooks/useMultiObjectSearch'; +import { formatMultiObjectRecordSearchResults } from '@/object-record/relation-picker/utils/formatMultiObjectRecordSearchResults'; +import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect'; import { isDefined } from '~/utils/isDefined'; export type MultiObjectRecordQueryResult = { @@ -24,25 +25,34 @@ export const useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArr objectMetadataItemsByNamePluralMapSelector, ); + const formattedMultiObjectRecordsQueryResult = useMemo(() => { + return formatMultiObjectRecordSearchResults( + multiObjectRecordsQueryResult, + ); + }, [multiObjectRecordsQueryResult]); + const objectRecordForSelectArray = useMemo(() => { - return Object.entries(multiObjectRecordsQueryResult ?? {}).flatMap( - ([namePlural, objectRecordConnection]) => { - const objectMetadataItem = - objectMetadataItemsByNamePluralMap.get(namePlural); + return Object.entries( + formattedMultiObjectRecordsQueryResult ?? {}, + ).flatMap(([namePlural, objectRecordConnection]) => { + const objectMetadataItem = + objectMetadataItemsByNamePluralMap.get(namePlural); - if (!isDefined(objectMetadataItem)) return []; + if (!isDefined(objectMetadataItem)) return []; - return objectRecordConnection.edges.map(({ node }) => ({ + return objectRecordConnection.edges.map(({ node }) => ({ + objectMetadataItem, + record: node, + recordIdentifier: getObjectRecordIdentifier({ objectMetadataItem, record: node, - recordIdentifier: getObjectRecordIdentifier({ - objectMetadataItem, - record: node, - }), - })) as ObjectRecordForSelect[]; - }, - ); - }, [multiObjectRecordsQueryResult, objectMetadataItemsByNamePluralMap]); + }), + })) as ObjectRecordForSelect[]; + }); + }, [ + formattedMultiObjectRecordsQueryResult, + objectMetadataItemsByNamePluralMap, + ]); return { objectRecordForSelectArray, diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearch.ts b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearch.ts deleted file mode 100644 index 8651e7f42..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearch.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery } from '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery'; -import { useMultiObjectSearchMatchesSearchFilterAndToSelectQuery } from '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndToSelectQuery'; -import { useMultiObjectSearchSelectedItemsQuery } from '@/object-record/relation-picker/hooks/useMultiObjectSearchSelectedItemsQuery'; -import { ObjectRecord } from '@/object-record/types/ObjectRecord'; -import { ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier'; - -export const MULTI_OBJECT_SEARCH_REQUEST_LIMIT = 5; - -export type ObjectRecordForSelect = { - objectMetadataItem: ObjectMetadataItem; - record: ObjectRecord; - recordIdentifier: ObjectRecordIdentifier; -}; - -export type SelectedObjectRecordId = { - objectNameSingular: string; - id: string; -}; - -export type MultiObjectSearch = { - selectedObjectRecords: ObjectRecordForSelect[]; - filteredSelectedObjectRecords: ObjectRecordForSelect[]; - objectRecordsToSelect: ObjectRecordForSelect[]; - loading: boolean; -}; - -export const useMultiObjectSearch = ({ - searchFilterValue, - selectedObjectRecordIds, - limit, - excludedObjectRecordIds = [], - excludedObjects, -}: { - searchFilterValue: string; - selectedObjectRecordIds: SelectedObjectRecordId[]; - limit?: number; - excludedObjectRecordIds?: SelectedObjectRecordId[]; - excludedObjects?: CoreObjectNameSingular[]; -}): MultiObjectSearch => { - const { selectedObjectRecords, selectedObjectRecordsLoading } = - useMultiObjectSearchSelectedItemsQuery({ - selectedObjectRecordIds, - }); - - const { - selectedAndMatchesSearchFilterObjectRecords, - selectedAndMatchesSearchFilterObjectRecordsLoading, - } = useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery({ - searchFilterValue, - selectedObjectRecordIds, - limit, - }); - - const { - toSelectAndMatchesSearchFilterObjectRecords, - toSelectAndMatchesSearchFilterObjectRecordsLoading, - } = useMultiObjectSearchMatchesSearchFilterAndToSelectQuery({ - excludedObjects, - excludedObjectRecordIds, - searchFilterValue, - selectedObjectRecordIds, - limit, - }); - - return { - selectedObjectRecords, - filteredSelectedObjectRecords: selectedAndMatchesSearchFilterObjectRecords, - objectRecordsToSelect: toSelectAndMatchesSearchFilterObjectRecords, - loading: - selectedAndMatchesSearchFilterObjectRecordsLoading || - toSelectAndMatchesSearchFilterObjectRecordsLoading || - selectedObjectRecordsLoading, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery.ts b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery.ts deleted file mode 100644 index b69ef1f40..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { useQuery } from '@apollo/client'; -import { isNonEmptyArray } from '@sniptt/guards'; -import { useRecoilValue } from 'recoil'; - -import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; -import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery'; -import { useGenerateCombinedSearchRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedSearchRecordsQuery'; -import { useLimitPerMetadataItem } from '@/object-record/relation-picker/hooks/useLimitPerMetadataItem'; -import { - MultiObjectRecordQueryResult, - useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray, -} from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray'; -import { SelectedObjectRecordId } from '@/object-record/relation-picker/hooks/useMultiObjectSearch'; -import { useMemo } from 'react'; -import { isDefined } from '~/utils/isDefined'; -import { capitalize } from '~/utils/string/capitalize'; - -export const formatSearchResults = ( - searchResults: MultiObjectRecordQueryResult | undefined, -): MultiObjectRecordQueryResult => { - if (!searchResults) { - return {}; - } - - return Object.entries(searchResults).reduce((acc, [key, value]) => { - let newKey = key.replace(/^search/, ''); - newKey = newKey.charAt(0).toLowerCase() + newKey.slice(1); - acc[newKey] = value; - return acc; - }, {} as MultiObjectRecordQueryResult); -}; - -export const useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery = ({ - selectedObjectRecordIds, - searchFilterValue, - limit, -}: { - selectedObjectRecordIds: SelectedObjectRecordId[]; - searchFilterValue: string; - limit?: number; -}) => { - const objectMetadataItems = useRecoilValue(objectMetadataItemsState); - - const objectMetadataItemsUsedInSelectedIdsQuery = useMemo( - () => - objectMetadataItems.filter(({ nameSingular }) => { - return selectedObjectRecordIds.some(({ objectNameSingular }) => { - return objectNameSingular === nameSingular; - }); - }), - [objectMetadataItems, selectedObjectRecordIds], - ); - - const selectedAndMatchesSearchFilterTextFilterPerMetadataItem = - Object.fromEntries( - objectMetadataItems - .map(({ nameSingular }) => { - const selectedIds = selectedObjectRecordIds - .filter( - ({ objectNameSingular }) => objectNameSingular === nameSingular, - ) - .map(({ id }) => id); - - if (!isNonEmptyArray(selectedIds)) return null; - - return [ - `filter${capitalize(nameSingular)}`, - { - id: { - in: selectedIds, - }, - }, - ]; - }) - .filter(isDefined), - ); - - const { limitPerMetadataItem } = useLimitPerMetadataItem({ - objectMetadataItems: objectMetadataItemsUsedInSelectedIdsQuery, - limit, - }); - - const multiSelectSearchQueryForSelectedIds = - useGenerateCombinedSearchRecordsQuery({ - operationSignatures: objectMetadataItemsUsedInSelectedIdsQuery.map( - (objectMetadataItem) => ({ - objectNameSingular: objectMetadataItem.nameSingular, - variables: {}, - }), - ), - }); - - const { - loading: selectedAndMatchesSearchFilterObjectRecordsLoading, - data: selectedAndMatchesSearchFilterObjectRecordsQueryResult, - } = useQuery( - multiSelectSearchQueryForSelectedIds ?? EMPTY_QUERY, - { - variables: { - search: searchFilterValue, - ...selectedAndMatchesSearchFilterTextFilterPerMetadataItem, - ...limitPerMetadataItem, - }, - skip: !isDefined(multiSelectSearchQueryForSelectedIds), - }, - ); - - const { - objectRecordForSelectArray: selectedAndMatchesSearchFilterObjectRecords, - } = useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray({ - multiObjectRecordsQueryResult: formatSearchResults( - selectedAndMatchesSearchFilterObjectRecordsQueryResult, - ), - }); - - return { - selectedAndMatchesSearchFilterObjectRecordsLoading, - selectedAndMatchesSearchFilterObjectRecords, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndToSelectQuery.ts b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndToSelectQuery.ts deleted file mode 100644 index c3150cd44..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndToSelectQuery.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { useQuery } from '@apollo/client'; -import { useRecoilValue } from 'recoil'; - -import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery'; -import { useGenerateCombinedSearchRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedSearchRecordsQuery'; -import { useLimitPerMetadataItem } from '@/object-record/relation-picker/hooks/useLimitPerMetadataItem'; -import { - MultiObjectRecordQueryResult, - useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray, -} from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray'; -import { SelectedObjectRecordId } from '@/object-record/relation-picker/hooks/useMultiObjectSearch'; -import { formatSearchResults } from '@/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery'; -import { isObjectMetadataItemSearchableInCombinedRequest } from '@/object-record/utils/isObjectMetadataItemSearchableInCombinedRequest'; -import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables'; -import { isDefined } from '~/utils/isDefined'; -import { capitalize } from '~/utils/string/capitalize'; - -export const useMultiObjectSearchMatchesSearchFilterAndToSelectQuery = ({ - selectedObjectRecordIds, - excludedObjectRecordIds, - searchFilterValue, - limit, - excludedObjects, -}: { - selectedObjectRecordIds: SelectedObjectRecordId[]; - excludedObjectRecordIds: SelectedObjectRecordId[]; - searchFilterValue: string; - limit?: number; - excludedObjects?: CoreObjectNameSingular[]; -}) => { - const objectMetadataItems = useRecoilValue(objectMetadataItemsState); - - const selectableObjectMetadataItems = objectMetadataItems - .filter(({ isSystem, isRemote }) => !isSystem && !isRemote) - .filter(({ nameSingular }) => { - return !excludedObjects?.includes(nameSingular as CoreObjectNameSingular); - }) - .filter((object) => - isObjectMetadataItemSearchableInCombinedRequest(object), - ); - - const objectRecordsToSelectAndMatchesSearchFilterTextFilterPerMetadataItem = - Object.fromEntries( - selectableObjectMetadataItems - .map(({ nameSingular }) => { - const selectedIds = selectedObjectRecordIds - .filter( - ({ objectNameSingular }) => objectNameSingular === nameSingular, - ) - .map(({ id }) => id); - - const excludedIds = excludedObjectRecordIds - .filter( - ({ objectNameSingular }) => objectNameSingular === nameSingular, - ) - .map(({ id }) => id); - - const excludedIdsUnion = [...selectedIds, ...excludedIds]; - const excludedIdsFilter = excludedIdsUnion.length - ? { not: { id: { in: excludedIdsUnion } } } - : undefined; - - return [ - `filter${capitalize(nameSingular)}`, - makeAndFilterVariables([excludedIdsFilter]), - ]; - }) - .filter(isDefined), - ); - const { limitPerMetadataItem } = useLimitPerMetadataItem({ - objectMetadataItems: selectableObjectMetadataItems, - limit, - }); - - const multiSelectQuery = useGenerateCombinedSearchRecordsQuery({ - operationSignatures: selectableObjectMetadataItems.map( - (objectMetadataItem) => ({ - objectNameSingular: objectMetadataItem.nameSingular, - variables: {}, - }), - ), - }); - - const { - loading: toSelectAndMatchesSearchFilterObjectRecordsLoading, - data: toSelectAndMatchesSearchFilterObjectRecordsQueryResult, - } = useQuery(multiSelectQuery ?? EMPTY_QUERY, { - variables: { - search: searchFilterValue, - ...objectRecordsToSelectAndMatchesSearchFilterTextFilterPerMetadataItem, - ...limitPerMetadataItem, - }, - skip: !isDefined(multiSelectQuery), - }); - - const { - objectRecordForSelectArray: toSelectAndMatchesSearchFilterObjectRecords, - } = useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray({ - multiObjectRecordsQueryResult: formatSearchResults( - toSelectAndMatchesSearchFilterObjectRecordsQueryResult, - ), - }); - - return { - toSelectAndMatchesSearchFilterObjectRecordsLoading, - toSelectAndMatchesSearchFilterObjectRecords, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterQuery.ts b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterQuery.ts new file mode 100644 index 000000000..a8bdfec35 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchMatchesSearchFilterQuery.ts @@ -0,0 +1,75 @@ +import { useQuery } from '@apollo/client'; +import { useRecoilValue } from 'recoil'; + +import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery'; +import { useGenerateCombinedSearchRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedSearchRecordsQuery'; +import { useLimitPerMetadataItem } from '@/object-record/relation-picker/hooks/useLimitPerMetadataItem'; +import { + MultiObjectRecordQueryResult, + useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray, +} from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray'; +import { isObjectMetadataItemSearchableInCombinedRequest } from '@/object-record/utils/isObjectMetadataItemSearchableInCombinedRequest'; +import { isDefined } from '~/utils/isDefined'; + +export const useMultiObjectSearchMatchesSearchFilterQuery = ({ + searchFilterValue, + limit, + excludedObjects, +}: { + searchFilterValue: string; + limit?: number; + excludedObjects?: CoreObjectNameSingular[]; +}) => { + const objectMetadataItems = useRecoilValue(objectMetadataItemsState); + + const selectableObjectMetadataItems = objectMetadataItems + .filter(({ isSystem, isRemote }) => !isSystem && !isRemote) + .filter(({ nameSingular }) => { + return !excludedObjects?.includes(nameSingular as CoreObjectNameSingular); + }) + .filter((objectMetadataItem) => + isObjectMetadataItemSearchableInCombinedRequest(objectMetadataItem), + ); + + const { limitPerMetadataItem } = useLimitPerMetadataItem({ + objectMetadataItems, + limit, + }); + + const multiSelectSearchQueryForSelectedIds = + useGenerateCombinedSearchRecordsQuery({ + operationSignatures: selectableObjectMetadataItems.map( + (objectMetadataItem) => ({ + objectNameSingular: objectMetadataItem.nameSingular, + variables: {}, + }), + ), + }); + + const { + loading: matchesSearchFilterObjectRecordsLoading, + data: matchesSearchFilterObjectRecordsQueryResult, + } = useQuery( + multiSelectSearchQueryForSelectedIds ?? EMPTY_QUERY, + { + variables: { + search: searchFilterValue, + ...limitPerMetadataItem, + }, + skip: !isDefined(multiSelectSearchQueryForSelectedIds), + }, + ); + + const { objectRecordForSelectArray: matchesSearchFilterObjectRecords } = + useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray({ + multiObjectRecordsQueryResult: + matchesSearchFilterObjectRecordsQueryResult, + }); + + return { + matchesSearchFilterObjectRecordsLoading, + matchesSearchFilterObjectRecords, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchSelectedItemsQuery.ts b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchSelectedItemsQuery.ts index 5656cb77f..a4fd29ddc 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchSelectedItemsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useMultiObjectSearchSelectedItemsQuery.ts @@ -9,8 +9,8 @@ import { MultiObjectRecordQueryResult, useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray, } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray'; -import { SelectedObjectRecordId } from '@/object-record/relation-picker/hooks/useMultiObjectSearch'; import { useOrderByFieldPerMetadataItem } from '@/object-record/relation-picker/hooks/useOrderByFieldPerMetadataItem'; +import { SelectedObjectRecordId } from '@/object-record/types/SelectedObjectRecordId'; import { isDefined } from '~/utils/isDefined'; import { capitalize } from '~/utils/string/capitalize'; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/utils/formatMultiObjectRecordSearchResults.ts b/packages/twenty-front/src/modules/object-record/relation-picker/utils/formatMultiObjectRecordSearchResults.ts new file mode 100644 index 000000000..318f12ab7 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/relation-picker/utils/formatMultiObjectRecordSearchResults.ts @@ -0,0 +1,16 @@ +import { MultiObjectRecordQueryResult } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray'; + +export const formatMultiObjectRecordSearchResults = ( + searchResults: MultiObjectRecordQueryResult | undefined | null, +): MultiObjectRecordQueryResult => { + if (!searchResults) { + return {}; + } + + return Object.entries(searchResults).reduce((acc, [key, value]) => { + let newKey = key.replace(/^search/, ''); + newKey = newKey.charAt(0).toLowerCase() + newKey.slice(1); + acc[newKey] = value; + return acc; + }, {} as MultiObjectRecordQueryResult); +}; diff --git a/packages/twenty-front/src/modules/object-record/types/ObjectRecordForSelect.ts b/packages/twenty-front/src/modules/object-record/types/ObjectRecordForSelect.ts new file mode 100644 index 000000000..a1266fb6f --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/types/ObjectRecordForSelect.ts @@ -0,0 +1,9 @@ +import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { ObjectRecord } from '@/object-record/types/ObjectRecord'; +import { ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier'; + +export type ObjectRecordForSelect = { + objectMetadataItem: ObjectMetadataItem; + record: ObjectRecord; + recordIdentifier: ObjectRecordIdentifier; +}; diff --git a/packages/twenty-front/src/modules/object-record/types/SelectedObjectRecordId.ts b/packages/twenty-front/src/modules/object-record/types/SelectedObjectRecordId.ts new file mode 100644 index 000000000..2c9bb2353 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/types/SelectedObjectRecordId.ts @@ -0,0 +1,4 @@ +export type SelectedObjectRecordId = { + objectNameSingular: string; + id: string; +}; From d9c0530dd35670c37e3b06af03d7d7929c95fdf5 Mon Sep 17 00:00:00 2001 From: Guillim Date: Thu, 7 Nov 2024 17:22:47 +0100 Subject: [PATCH 005/100] Integration test : Sync mode + db reset option + cleaning (#8376) Run the CI integrationin sync mode and add the option to run it without db reset cleaning all the useless integration test --------- Co-authored-by: guillim --- .github/workflows/ci-server.yaml | 2 +- .../twenty-server/jest-integration.config.ts | 1 + packages/twenty-server/project.json | 12 +- .../auth/strategies/jwt.auth.strategy.spec.ts | 2 +- ...all-api-keys-resolvers.integration-spec.ts | 404 ------------- ...-attachments-resolvers.integration-spec.ts | 435 -------------- ...l-audit-logs-resolvers.integration-spec.ts | 421 -------------- ...associations-resolvers.integration-spec.ts | 535 ------------------ ...participants-resolvers.integration-spec.ts | 479 ---------------- ...ted-accounts-resolvers.integration-spec.ts | 420 -------------- ...ll-favorites-resolvers.integration-spec.ts | 409 ------------- ...associations-resolvers.integration-spec.ts | 493 ---------------- ...participants-resolvers.integration-spec.ts | 466 --------------- ...sage-threads-resolvers.integration-spec.ts | 397 ------------- ...note-targets-resolvers.integration-spec.ts | 444 --------------- .../all-notes-resolvers.integration-spec.ts | 403 ------------- ...pportunities-resolvers.integration-spec.ts | 414 -------------- .../suites/all-resolvers.integration-spec.ts | 430 -------------- ...task-targets-resolvers.integration-spec.ts | 444 --------------- .../all-tasks-resolvers.integration-spec.ts | 403 ------------- ...ll-timeline-activities.integration-spec.ts | 477 ---------------- .../all-view-fields.integration-spec.ts | 407 ------------- .../all-view-filters.integration-spec.ts | 407 ------------- .../suites/all-view-sorts.integration-spec.ts | 399 ------------- .../suites/all-views.integration-spec.ts | 429 -------------- .../suites/all-webhooks.integration-spec.ts | 403 ------------- .../backend-development/server-commands.mdx | 4 +- 27 files changed, 16 insertions(+), 9524 deletions(-) delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-api-keys-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-attachments-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-audit-logs-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-calendar-channel-event-associations-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-calendar-event-participants-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-connected-accounts-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-favorites-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-message-channel-message-associations-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-message-participants-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-message-threads-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-note-targets-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-notes-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-opportunities-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-task-targets-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-tasks-resolvers.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-timeline-activities.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-view-fields.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-view-filters.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-view-sorts.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-views.integration-spec.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/all-webhooks.integration-spec.ts diff --git a/.github/workflows/ci-server.yaml b/.github/workflows/ci-server.yaml index 9b24cacc4..b4fb89e4d 100644 --- a/.github/workflows/ci-server.yaml +++ b/.github/workflows/ci-server.yaml @@ -146,7 +146,7 @@ jobs: uses: ./.github/workflows/actions/nx-affected with: tag: scope:backend - tasks: "test:integration" + tasks: "test:integration:with-db-reset" - name: Server / Upload reset-logs file if: always() uses: actions/upload-artifact@v4 diff --git a/packages/twenty-server/jest-integration.config.ts b/packages/twenty-server/jest-integration.config.ts index 9dc26ba51..5cf3ce61f 100644 --- a/packages/twenty-server/jest-integration.config.ts +++ b/packages/twenty-server/jest-integration.config.ts @@ -14,6 +14,7 @@ const jestConfig: JestConfigWithTsJest = { globalSetup: '/test/integration/utils/setup-test.ts', globalTeardown: '/test/integration/utils/teardown-test.ts', testTimeout: 15000, + maxWorkers: 1, moduleNameMapper: { ...pathsToModuleNameMapper(tsConfig.compilerOptions.paths, { prefix: '/../..', diff --git a/packages/twenty-server/project.json b/packages/twenty-server/project.json index de8dec76a..b3c45f24a 100644 --- a/packages/twenty-server/project.json +++ b/packages/twenty-server/project.json @@ -16,10 +16,18 @@ "options": { "cwd": "packages/twenty-server", "commands": [ - "NODE_ENV=test nx database:reset > reset-logs.log && NODE_ENV=test nx jest --config ./jest-integration.config.ts" + "NODE_ENV=test nx jest --config ./jest-integration.config.ts" ] }, - "parallel": false + "parallel": false, + "configurations": { + "with-db-reset": { + "cwd": "packages/twenty-server", + "commands": [ + "NODE_ENV=test nx database:reset > reset-logs.log && NODE_ENV=test nx jest --config ./jest-integration.config.ts" + ] + } + } }, "build:packageJson": { "executor": "@nx/js:tsc", diff --git a/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.spec.ts b/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.spec.ts index fdc4e0646..e924cd167 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.spec.ts @@ -7,7 +7,7 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { JwtAuthStrategy } from './jwt.auth.strategy'; -xdescribe('JwtAuthStrategy', () => { +describe('JwtAuthStrategy', () => { let strategy: JwtAuthStrategy; let workspaceRepository: any; diff --git a/packages/twenty-server/test/integration/graphql/suites/all-api-keys-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-api-keys-resolvers.integration-spec.ts deleted file mode 100644 index 369ea8430..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-api-keys-resolvers.integration-spec.ts +++ /dev/null @@ -1,404 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const API_KEY_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const API_KEY_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const API_KEY_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; -const API_KEY_GQL_FIELDS = ` - id - name - expiresAt - revokedAt - createdAt - updatedAt - deletedAt -`; - -describe('apiKeys resolvers (integration)', () => { - it('1. should create and return API keys', async () => { - const apiKeyName1 = generateRecordName(API_KEY_1_ID); - const apiKeyName2 = generateRecordName(API_KEY_2_ID); - - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'apiKey', - objectMetadataPluralName: 'apiKeys', - gqlFields: API_KEY_GQL_FIELDS, - data: [ - { - id: API_KEY_1_ID, - name: apiKeyName1, - expiresAt: new Date(), - }, - { - id: API_KEY_2_ID, - name: apiKeyName2, - expiresAt: new Date(), - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createApiKeys).toHaveLength(2); - - response.body.data.createApiKeys.forEach((apiKey) => { - expect(apiKey).toHaveProperty('name'); - expect([apiKeyName1, apiKeyName2]).toContain(apiKey.name); - expect(apiKey).toHaveProperty('expiresAt'); - expect(apiKey).toHaveProperty('revokedAt'); - expect(apiKey).toHaveProperty('id'); - expect(apiKey).toHaveProperty('createdAt'); - expect(apiKey).toHaveProperty('updatedAt'); - expect(apiKey).toHaveProperty('deletedAt'); - }); - }); - - it('1b. should create and return one API key', async () => { - const apiKeyName = generateRecordName(API_KEY_3_ID); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'apiKey', - gqlFields: API_KEY_GQL_FIELDS, - data: { - id: API_KEY_3_ID, - name: apiKeyName, - expiresAt: new Date(), - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdApiKey = response.body.data.createApiKey; - - expect(createdApiKey).toHaveProperty('name'); - expect(createdApiKey.name).toEqual(apiKeyName); - expect(createdApiKey).toHaveProperty('expiresAt'); - expect(createdApiKey).toHaveProperty('revokedAt'); - expect(createdApiKey).toHaveProperty('id'); - expect(createdApiKey).toHaveProperty('createdAt'); - expect(createdApiKey).toHaveProperty('updatedAt'); - expect(createdApiKey).toHaveProperty('deletedAt'); - }); - - it('2. should find many API keys', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'apiKey', - objectMetadataPluralName: 'apiKeys', - gqlFields: API_KEY_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.apiKeys; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - const edges = data.edges; - - if (edges.length > 0) { - const apiKeys = edges[0].node; - - expect(apiKeys).toHaveProperty('name'); - expect(apiKeys).toHaveProperty('expiresAt'); - expect(apiKeys).toHaveProperty('revokedAt'); - expect(apiKeys).toHaveProperty('id'); - expect(apiKeys).toHaveProperty('createdAt'); - expect(apiKeys).toHaveProperty('updatedAt'); - expect(apiKeys).toHaveProperty('deletedAt'); - } - }); - - it('2b. should find one API key', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'apiKey', - gqlFields: API_KEY_GQL_FIELDS, - filter: { - id: { - eq: API_KEY_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const apiKey = response.body.data.apiKey; - - expect(apiKey).toHaveProperty('name'); - expect(apiKey).toHaveProperty('expiresAt'); - expect(apiKey).toHaveProperty('revokedAt'); - expect(apiKey).toHaveProperty('id'); - expect(apiKey).toHaveProperty('createdAt'); - expect(apiKey).toHaveProperty('updatedAt'); - expect(apiKey).toHaveProperty('deletedAt'); - }); - - it('3. should update many API keys', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'apiKey', - objectMetadataPluralName: 'apiKeys', - gqlFields: API_KEY_GQL_FIELDS, - data: { - name: 'Updated Name', - }, - filter: { - id: { - in: [API_KEY_1_ID, API_KEY_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedApiKeys = response.body.data.updateApiKeys; - - expect(updatedApiKeys).toHaveLength(2); - - updatedApiKeys.forEach((apiKey) => { - expect(apiKey.name).toEqual('Updated Name'); - }); - }); - - it('3b. should update one API key', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'apiKey', - gqlFields: API_KEY_GQL_FIELDS, - data: { - name: 'New Name', - }, - recordId: API_KEY_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedApiKey = response.body.data.updateApiKey; - - expect(updatedApiKey.name).toEqual('New Name'); - }); - - it('4. should find many API keys with updated name', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'apiKey', - objectMetadataPluralName: 'apiKeys', - gqlFields: API_KEY_GQL_FIELDS, - filter: { - name: { - eq: 'Updated Name', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.apiKeys.edges).toHaveLength(2); - }); - - it('4b. should find one API key with updated name', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'apiKey', - gqlFields: API_KEY_GQL_FIELDS, - filter: { - name: { - eq: 'New Name', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.apiKey.name).toEqual('New Name'); - }); - - it('5. should delete many API keys', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'apiKey', - objectMetadataPluralName: 'apiKeys', - gqlFields: API_KEY_GQL_FIELDS, - filter: { - id: { - in: [API_KEY_1_ID, API_KEY_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deletedApiKeys = response.body.data.deleteApiKeys; - - expect(deletedApiKeys).toHaveLength(2); - - deletedApiKeys.forEach((apiKey) => { - expect(apiKey.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one API key', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'apiKey', - gqlFields: API_KEY_GQL_FIELDS, - recordId: API_KEY_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteApiKey.deletedAt).toBeTruthy(); - }); - - it('6. should not find many API keys anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'apiKey', - objectMetadataPluralName: 'apiKeys', - gqlFields: API_KEY_GQL_FIELDS, - filter: { - id: { - in: [API_KEY_1_ID, API_KEY_2_ID], - }, - }, - }); - - const findApiKeysResponse = await makeGraphqlAPIRequest(graphqlOperation); - - expect(findApiKeysResponse.body.data.apiKeys.edges).toHaveLength(0); - }); - - it('6b. should not find one API key anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'apiKey', - gqlFields: API_KEY_GQL_FIELDS, - filter: { - id: { - eq: API_KEY_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.apiKey).toBeNull(); - }); - - it('7. should find many deleted API keys with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'apiKey', - objectMetadataPluralName: 'apiKeys', - gqlFields: API_KEY_GQL_FIELDS, - filter: { - id: { - in: [API_KEY_1_ID, API_KEY_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.apiKeys.edges).toHaveLength(2); - }); - - it('7b. should find one deleted API key with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'apiKey', - gqlFields: API_KEY_GQL_FIELDS, - filter: { - id: { - eq: API_KEY_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.apiKey.id).toEqual(API_KEY_3_ID); - }); - - it('8. should destroy many API keys', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'apiKey', - objectMetadataPluralName: 'apiKeys', - gqlFields: API_KEY_GQL_FIELDS, - filter: { - id: { - in: [API_KEY_1_ID, API_KEY_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyApiKeys).toHaveLength(2); - }); - - it('8b. should destroy one API key', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'apiKey', - gqlFields: API_KEY_GQL_FIELDS, - recordId: API_KEY_3_ID, - }); - - const destroyApiKeyResponse = await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyApiKeyResponse.body.data.destroyApiKey).toBeTruthy(); - }); - - it('9. should not find many API keys anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'apiKey', - objectMetadataPluralName: 'apiKeys', - gqlFields: API_KEY_GQL_FIELDS, - filter: { - id: { - in: [API_KEY_1_ID, API_KEY_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.apiKeys.edges).toHaveLength(0); - }); - - it('9b. should not find one API key anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'apiKey', - gqlFields: API_KEY_GQL_FIELDS, - filter: { - id: { - eq: API_KEY_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.apiKey).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-attachments-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-attachments-resolvers.integration-spec.ts deleted file mode 100644 index 8bfd572d4..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-attachments-resolvers.integration-spec.ts +++ /dev/null @@ -1,435 +0,0 @@ -import { TIM_ACCOUNT_ID } from 'test/integration/graphql/integration.constants'; -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const ATTACHMENT_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const ATTACHMENT_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const ATTACHMENT_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; - -const ATTACHMENT_GQL_FIELDS = ` - id - name - fullPath - type - createdAt - updatedAt - deletedAt - authorId - taskId - noteId - personId - companyId - opportunityId -`; - -describe('attachments resolvers (integration)', () => { - it('1. should create and return multiple attachments', async () => { - const attachmentName1 = generateRecordName(ATTACHMENT_1_ID); - const attachmentName2 = generateRecordName(ATTACHMENT_2_ID); - - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'attachment', - objectMetadataPluralName: 'attachments', - gqlFields: ATTACHMENT_GQL_FIELDS, - data: [ - { - id: ATTACHMENT_1_ID, - name: attachmentName1, - authorId: TIM_ACCOUNT_ID, - }, - { - id: ATTACHMENT_2_ID, - name: attachmentName2, - authorId: TIM_ACCOUNT_ID, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createAttachments).toHaveLength(2); - - response.body.data.createAttachments.forEach((attachment) => { - expect(attachment).toHaveProperty('name'); - expect([attachmentName1, attachmentName2]).toContain(attachment.name); - expect(attachment).toHaveProperty('fullPath'); - expect(attachment).toHaveProperty('type'); - expect(attachment).toHaveProperty('id'); - expect(attachment).toHaveProperty('createdAt'); - expect(attachment).toHaveProperty('updatedAt'); - expect(attachment).toHaveProperty('deletedAt'); - expect(attachment).toHaveProperty('authorId'); - expect(attachment).toHaveProperty('taskId'); - expect(attachment).toHaveProperty('noteId'); - expect(attachment).toHaveProperty('personId'); - expect(attachment).toHaveProperty('companyId'); - expect(attachment).toHaveProperty('opportunityId'); - }); - }); - - it('2. should create and return one attachment', async () => { - const attachmentName = generateRecordName(ATTACHMENT_3_ID); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'attachment', - gqlFields: ATTACHMENT_GQL_FIELDS, - data: { - id: ATTACHMENT_3_ID, - name: attachmentName, - authorId: TIM_ACCOUNT_ID, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdAttachment = response.body.data.createAttachment; - - expect(createdAttachment).toHaveProperty('name', attachmentName); - expect(createdAttachment).toHaveProperty('fullPath'); - expect(createdAttachment).toHaveProperty('type'); - expect(createdAttachment).toHaveProperty('id'); - expect(createdAttachment).toHaveProperty('createdAt'); - expect(createdAttachment).toHaveProperty('updatedAt'); - expect(createdAttachment).toHaveProperty('deletedAt'); - expect(createdAttachment).toHaveProperty('authorId'); - expect(createdAttachment).toHaveProperty('taskId'); - expect(createdAttachment).toHaveProperty('noteId'); - expect(createdAttachment).toHaveProperty('personId'); - expect(createdAttachment).toHaveProperty('companyId'); - expect(createdAttachment).toHaveProperty('opportunityId'); - }); - - it('2. should find many attachments', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'attachment', - objectMetadataPluralName: 'attachments', - gqlFields: ATTACHMENT_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.attachments; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - if (data.edges.length > 0) { - const attachments = data.edges[0].node; - - expect(attachments).toHaveProperty('name'); - expect(attachments).toHaveProperty('fullPath'); - expect(attachments).toHaveProperty('type'); - expect(attachments).toHaveProperty('id'); - expect(attachments).toHaveProperty('createdAt'); - expect(attachments).toHaveProperty('updatedAt'); - expect(attachments).toHaveProperty('deletedAt'); - expect(attachments).toHaveProperty('authorId'); - expect(attachments).toHaveProperty('taskId'); - expect(attachments).toHaveProperty('noteId'); - expect(attachments).toHaveProperty('personId'); - expect(attachments).toHaveProperty('companyId'); - expect(attachments).toHaveProperty('opportunityId'); - } - }); - - it('2b. should find one attachment', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'attachment', - gqlFields: ATTACHMENT_GQL_FIELDS, - filter: { - id: { - eq: ATTACHMENT_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const attachment = response.body.data.attachment; - - expect(attachment).toHaveProperty('name'); - expect(attachment).toHaveProperty('fullPath'); - expect(attachment).toHaveProperty('type'); - expect(attachment).toHaveProperty('id'); - expect(attachment).toHaveProperty('createdAt'); - expect(attachment).toHaveProperty('updatedAt'); - expect(attachment).toHaveProperty('deletedAt'); - expect(attachment).toHaveProperty('authorId'); - expect(attachment).toHaveProperty('taskId'); - expect(attachment).toHaveProperty('noteId'); - expect(attachment).toHaveProperty('personId'); - expect(attachment).toHaveProperty('companyId'); - expect(attachment).toHaveProperty('opportunityId'); - }); - - it('3. should update many attachments', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'attachment', - objectMetadataPluralName: 'attachments', - gqlFields: ATTACHMENT_GQL_FIELDS, - data: { - name: 'Updated Name', - }, - filter: { - id: { - in: [ATTACHMENT_1_ID, ATTACHMENT_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedAttachments = response.body.data.updateAttachments; - - expect(updatedAttachments).toHaveLength(2); - - updatedAttachments.forEach((attachment) => { - expect(attachment.name).toEqual('Updated Name'); - }); - }); - - it('3b. should update one attachment', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'attachment', - gqlFields: ATTACHMENT_GQL_FIELDS, - data: { - name: 'New Name', - }, - recordId: ATTACHMENT_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedAttachment = response.body.data.updateAttachment; - - expect(updatedAttachment.name).toEqual('New Name'); - }); - - it('4. should find many attachments with updated name', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'attachment', - objectMetadataPluralName: 'attachments', - gqlFields: ATTACHMENT_GQL_FIELDS, - filter: { - name: { - eq: 'Updated Name', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.attachments.edges).toHaveLength(2); - }); - - it('4b. should find one attachment with updated name', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'attachment', - gqlFields: ATTACHMENT_GQL_FIELDS, - filter: { - name: { - eq: 'New Name', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.attachment.name).toEqual('New Name'); - }); - - it('5. should delete many attachments', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'attachment', - objectMetadataPluralName: 'attachments', - gqlFields: ATTACHMENT_GQL_FIELDS, - filter: { - id: { - in: [ATTACHMENT_1_ID, ATTACHMENT_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deletedAttachments = response.body.data.deleteAttachments; - - expect(deletedAttachments).toHaveLength(2); - - deletedAttachments.forEach((attachment) => { - expect(attachment.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one attachment', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'attachment', - gqlFields: ATTACHMENT_GQL_FIELDS, - recordId: ATTACHMENT_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteAttachment.deletedAt).toBeTruthy(); - }); - - it('6. should not find many attachments anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'attachment', - objectMetadataPluralName: 'attachments', - gqlFields: ATTACHMENT_GQL_FIELDS, - filter: { - id: { - in: [ATTACHMENT_1_ID, ATTACHMENT_2_ID], - }, - }, - }); - - const findAttachmentsResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(findAttachmentsResponse.body.data.attachments.edges).toHaveLength(0); - }); - - it('6b. should not find one attachment anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'attachment', - gqlFields: ATTACHMENT_GQL_FIELDS, - filter: { - id: { - eq: ATTACHMENT_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.attachment).toBeNull(); - }); - - it('7. should find many deleted attachments with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'attachment', - objectMetadataPluralName: 'attachments', - gqlFields: ATTACHMENT_GQL_FIELDS, - filter: { - id: { - in: [ATTACHMENT_1_ID, ATTACHMENT_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.attachments.edges).toHaveLength(2); - }); - - it('7b. should find one deleted attachment with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'attachment', - gqlFields: ATTACHMENT_GQL_FIELDS, - filter: { - id: { - eq: ATTACHMENT_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.attachment.id).toEqual(ATTACHMENT_3_ID); - }); - - it('8. should destroy many attachments', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'attachment', - objectMetadataPluralName: 'attachments', - gqlFields: ATTACHMENT_GQL_FIELDS, - filter: { - id: { - in: [ATTACHMENT_1_ID, ATTACHMENT_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyAttachments).toHaveLength(2); - }); - - it('8b. should destroy one attachment', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'attachment', - gqlFields: ATTACHMENT_GQL_FIELDS, - recordId: ATTACHMENT_3_ID, - }); - - const destroyAttachmentResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyAttachmentResponse.body.data.destroyAttachment).toBeTruthy(); - }); - - it('9. should not find many attachments anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'attachment', - objectMetadataPluralName: 'attachments', - gqlFields: ATTACHMENT_GQL_FIELDS, - filter: { - id: { - in: [ATTACHMENT_1_ID, ATTACHMENT_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.attachments.edges).toHaveLength(0); - }); - - it('9b. should not find one attachment anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'attachment', - gqlFields: ATTACHMENT_GQL_FIELDS, - filter: { - id: { - eq: ATTACHMENT_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.attachment).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-audit-logs-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-audit-logs-resolvers.integration-spec.ts deleted file mode 100644 index 4e5c74c22..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-audit-logs-resolvers.integration-spec.ts +++ /dev/null @@ -1,421 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const AUDIT_LOG_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const AUDIT_LOG_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const AUDIT_LOG_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; - -const AUDIT_LOG_GQL_FIELDS = ` - id - name - properties - context - objectName - objectMetadataId - recordId - createdAt - updatedAt - deletedAt - workspaceMemberId -`; - -describe('auditLogs resolvers (integration)', () => { - it('1. should create and return auditLogs', async () => { - const auditLogName1 = generateRecordName(AUDIT_LOG_1_ID); - const auditLogName2 = generateRecordName(AUDIT_LOG_2_ID); - - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'auditLog', - objectMetadataPluralName: 'auditLogs', - gqlFields: AUDIT_LOG_GQL_FIELDS, - data: [ - { - id: AUDIT_LOG_1_ID, - name: auditLogName1, - }, - { - id: AUDIT_LOG_2_ID, - name: auditLogName2, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createAuditLogs).toHaveLength(2); - - response.body.data.createAuditLogs.forEach((auditLog) => { - expect(auditLog).toHaveProperty('name'); - expect([auditLogName1, auditLogName2]).toContain(auditLog.name); - expect(auditLog).toHaveProperty('properties'); - expect(auditLog).toHaveProperty('context'); - expect(auditLog).toHaveProperty('objectName'); - expect(auditLog).toHaveProperty('objectMetadataId'); - expect(auditLog).toHaveProperty('recordId'); - expect(auditLog).toHaveProperty('id'); - expect(auditLog).toHaveProperty('createdAt'); - expect(auditLog).toHaveProperty('updatedAt'); - expect(auditLog).toHaveProperty('deletedAt'); - expect(auditLog).toHaveProperty('workspaceMemberId'); - }); - }); - - it('1b. should create and return one auditLog', async () => { - const auditLogName = generateRecordName(AUDIT_LOG_3_ID); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'auditLog', - gqlFields: AUDIT_LOG_GQL_FIELDS, - data: { - id: AUDIT_LOG_3_ID, - name: auditLogName, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdAuditLog = response.body.data.createAuditLog; - - expect(createdAuditLog).toHaveProperty('name'); - expect(createdAuditLog.name).toEqual(auditLogName); - expect(createdAuditLog).toHaveProperty('properties'); - expect(createdAuditLog).toHaveProperty('context'); - expect(createdAuditLog).toHaveProperty('objectName'); - expect(createdAuditLog).toHaveProperty('objectMetadataId'); - expect(createdAuditLog).toHaveProperty('recordId'); - expect(createdAuditLog).toHaveProperty('id'); - expect(createdAuditLog).toHaveProperty('createdAt'); - expect(createdAuditLog).toHaveProperty('updatedAt'); - expect(createdAuditLog).toHaveProperty('deletedAt'); - expect(createdAuditLog).toHaveProperty('workspaceMemberId'); - }); - - it('2. should find many auditLogs', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'auditLog', - objectMetadataPluralName: 'auditLogs', - gqlFields: AUDIT_LOG_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.auditLogs; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - if (data.edges.length > 0) { - const auditLogs = data.edges[0].node; - - expect(auditLogs).toHaveProperty('name'); - expect(auditLogs).toHaveProperty('properties'); - expect(auditLogs).toHaveProperty('context'); - expect(auditLogs).toHaveProperty('objectName'); - expect(auditLogs).toHaveProperty('objectMetadataId'); - expect(auditLogs).toHaveProperty('recordId'); - expect(auditLogs).toHaveProperty('id'); - expect(auditLogs).toHaveProperty('createdAt'); - expect(auditLogs).toHaveProperty('updatedAt'); - expect(auditLogs).toHaveProperty('deletedAt'); - expect(auditLogs).toHaveProperty('workspaceMemberId'); - } - }); - - it('2b. should find one auditLog', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'auditLog', - gqlFields: AUDIT_LOG_GQL_FIELDS, - filter: { - id: { - eq: AUDIT_LOG_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const auditLog = response.body.data.auditLog; - - expect(auditLog).toHaveProperty('name'); - expect(auditLog).toHaveProperty('properties'); - expect(auditLog).toHaveProperty('context'); - expect(auditLog).toHaveProperty('objectName'); - expect(auditLog).toHaveProperty('objectMetadataId'); - expect(auditLog).toHaveProperty('recordId'); - expect(auditLog).toHaveProperty('id'); - expect(auditLog).toHaveProperty('createdAt'); - expect(auditLog).toHaveProperty('updatedAt'); - expect(auditLog).toHaveProperty('deletedAt'); - expect(auditLog).toHaveProperty('workspaceMemberId'); - }); - - it('3. should update many auditLogs', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'auditLog', - objectMetadataPluralName: 'auditLogs', - gqlFields: AUDIT_LOG_GQL_FIELDS, - data: { - name: 'Updated Name', - }, - filter: { - id: { - in: [AUDIT_LOG_1_ID, AUDIT_LOG_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedAuditLogs = response.body.data.updateAuditLogs; - - expect(updatedAuditLogs).toHaveLength(2); - - updatedAuditLogs.forEach((auditLog) => { - expect(auditLog.name).toEqual('Updated Name'); - }); - }); - - it('3b. should update one auditLog', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'auditLog', - gqlFields: AUDIT_LOG_GQL_FIELDS, - data: { - name: 'New Name', - }, - recordId: AUDIT_LOG_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedAuditLog = response.body.data.updateAuditLog; - - expect(updatedAuditLog.name).toEqual('New Name'); - }); - - it('4. should find many auditLogs with updated name', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'auditLog', - objectMetadataPluralName: 'auditLogs', - gqlFields: AUDIT_LOG_GQL_FIELDS, - filter: { - name: { - eq: 'Updated Name', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.auditLogs.edges).toHaveLength(2); - }); - - it('4b. should find one auditLog with updated name', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'auditLog', - gqlFields: AUDIT_LOG_GQL_FIELDS, - filter: { - name: { - eq: 'New Name', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.auditLog.name).toEqual('New Name'); - }); - - it('5. should delete many auditLogs', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'auditLog', - objectMetadataPluralName: 'auditLogs', - gqlFields: AUDIT_LOG_GQL_FIELDS, - filter: { - id: { - in: [AUDIT_LOG_1_ID, AUDIT_LOG_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deletedAuditLogs = response.body.data.deleteAuditLogs; - - expect(deletedAuditLogs).toHaveLength(2); - - deletedAuditLogs.forEach((auditLog) => { - expect(auditLog.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one auditLog', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'auditLog', - gqlFields: AUDIT_LOG_GQL_FIELDS, - recordId: AUDIT_LOG_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteAuditLog.deletedAt).toBeTruthy(); - }); - - it('6. should not find many auditLogs anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'auditLog', - objectMetadataPluralName: 'auditLogs', - gqlFields: AUDIT_LOG_GQL_FIELDS, - filter: { - id: { - in: [AUDIT_LOG_1_ID, AUDIT_LOG_2_ID], - }, - }, - }); - - const findAuditLogsResponse = await makeGraphqlAPIRequest(graphqlOperation); - - expect(findAuditLogsResponse.body.data.auditLogs.edges).toHaveLength(0); - }); - - it('6b. should not find one auditLog anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'auditLog', - gqlFields: AUDIT_LOG_GQL_FIELDS, - filter: { - id: { - eq: AUDIT_LOG_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.auditLog).toBeNull(); - }); - - it('7. should find many deleted auditLogs with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'auditLog', - objectMetadataPluralName: 'auditLogs', - gqlFields: AUDIT_LOG_GQL_FIELDS, - filter: { - id: { - in: [AUDIT_LOG_1_ID, AUDIT_LOG_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.auditLogs.edges).toHaveLength(2); - }); - - it('7b. should find one deleted auditLog with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'auditLog', - gqlFields: AUDIT_LOG_GQL_FIELDS, - filter: { - id: { - eq: AUDIT_LOG_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.auditLog.id).toEqual(AUDIT_LOG_3_ID); - }); - - it('8. should destroy many auditLogs', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'auditLog', - objectMetadataPluralName: 'auditLogs', - gqlFields: AUDIT_LOG_GQL_FIELDS, - filter: { - id: { - in: [AUDIT_LOG_1_ID, AUDIT_LOG_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyAuditLogs).toHaveLength(2); - }); - - it('8b. should destroy one auditLog', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'auditLog', - gqlFields: AUDIT_LOG_GQL_FIELDS, - recordId: AUDIT_LOG_3_ID, - }); - - const destroyAuditLogResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyAuditLogResponse.body.data.destroyAuditLog).toBeTruthy(); - }); - - it('9. should not find many auditLogs anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'auditLog', - objectMetadataPluralName: 'auditLogs', - gqlFields: AUDIT_LOG_GQL_FIELDS, - filter: { - id: { - in: [AUDIT_LOG_1_ID, AUDIT_LOG_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.auditLogs.edges).toHaveLength(0); - }); - - it('9b. should not find one auditLog anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'auditLog', - gqlFields: AUDIT_LOG_GQL_FIELDS, - filter: { - id: { - eq: AUDIT_LOG_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.auditLog).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-calendar-channel-event-associations-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-calendar-channel-event-associations-resolvers.integration-spec.ts deleted file mode 100644 index e24c7af99..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-calendar-channel-event-associations-resolvers.integration-spec.ts +++ /dev/null @@ -1,535 +0,0 @@ -import { TIM_ACCOUNT_ID } from 'test/integration/graphql/integration.constants'; -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const CALENDAR_CHANNEL_EVENT_ASSOCIATION_1_ID = - '777a8457-eb2d-40ac-a707-551b615b6987'; -const CALENDAR_CHANNEL_EVENT_ASSOCIATION_2_ID = - '777a8457-eb2d-40ac-a707-551b615b6988'; -const CALENDAR_CHANNEL_EVENT_ASSOCIATION_3_ID = - '777a8457-eb2d-40ac-a707-551b615b6989'; -const CALENDAR_EVENT_ID = '777a8457-eb2d-40ac-a707-221b615b6989'; -const CALENDAR_CHANNEL_ID = '777a8457-eb2d-40ac-a707-331b615b6989'; -const CONNECTED_ACCOUNT_ID = '777a8457-eb2d-40ac-a707-441b615b6989'; - -const CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS = ` - id - eventExternalId - createdAt - updatedAt - deletedAt - calendarChannelId - calendarEventId -`; - -describe('calendarChannelEventAssociations resolvers (integration)', () => { - beforeAll(async () => { - const connectedAccountHandle = generateRecordName(CONNECTED_ACCOUNT_ID); - const createConnectedAccountgraphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - gqlFields: `id`, - data: { - id: CONNECTED_ACCOUNT_ID, - accountOwnerId: TIM_ACCOUNT_ID, - handle: connectedAccountHandle, - }, - }); - - const calendarChannelHandle = generateRecordName(CALENDAR_CHANNEL_ID); - const createCalendarChannelgraphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'calendarChannel', - gqlFields: `id`, - data: { - id: CALENDAR_CHANNEL_ID, - handle: calendarChannelHandle, - connectedAccountId: CONNECTED_ACCOUNT_ID, - }, - }); - - const calendarEventTitle = generateRecordName(CALENDAR_EVENT_ID); - const createCalendarEventgraphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'calendarEvent', - gqlFields: `id`, - data: { - id: CALENDAR_EVENT_ID, - title: calendarEventTitle, - }, - }); - - await makeGraphqlAPIRequest(createConnectedAccountgraphqlOperation); - - await makeGraphqlAPIRequest(createCalendarChannelgraphqlOperation); - await makeGraphqlAPIRequest(createCalendarEventgraphqlOperation); - }); - - afterAll(async () => { - const destroyConnectedAccountGraphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - gqlFields: `id`, - recordId: CONNECTED_ACCOUNT_ID, - }); - - const destroyCalendarChannelGraphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'calendarChannel', - gqlFields: `id`, - recordId: CALENDAR_CHANNEL_ID, - }); - - const destroyCalendarEventGraphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'calendarEvent', - gqlFields: `id`, - recordId: CALENDAR_EVENT_ID, - }); - - await makeGraphqlAPIRequest(destroyConnectedAccountGraphqlOperation); - await makeGraphqlAPIRequest(destroyCalendarChannelGraphqlOperation); - await makeGraphqlAPIRequest(destroyCalendarEventGraphqlOperation); - }); - - it('1. should create and return calendarChannelEventAssociations', async () => { - const eventExternalId1 = generateRecordName( - CALENDAR_CHANNEL_EVENT_ASSOCIATION_1_ID, - ); - const eventExternalId2 = generateRecordName( - CALENDAR_CHANNEL_EVENT_ASSOCIATION_2_ID, - ); - - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - objectMetadataPluralName: 'calendarChannelEventAssociations', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - data: [ - { - id: CALENDAR_CHANNEL_EVENT_ASSOCIATION_1_ID, - eventExternalId: eventExternalId1, - calendarChannelId: CALENDAR_CHANNEL_ID, - calendarEventId: CALENDAR_EVENT_ID, - }, - { - id: CALENDAR_CHANNEL_EVENT_ASSOCIATION_2_ID, - eventExternalId: eventExternalId2, - calendarChannelId: CALENDAR_CHANNEL_ID, - calendarEventId: CALENDAR_EVENT_ID, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.createCalendarChannelEventAssociations, - ).toHaveLength(2); - - response.body.data.createCalendarChannelEventAssociations.forEach( - (association) => { - expect(association).toHaveProperty('eventExternalId'); - expect([eventExternalId1, eventExternalId2]).toContain( - association.eventExternalId, - ); - expect(association).toHaveProperty('id'); - expect(association).toHaveProperty('createdAt'); - expect(association).toHaveProperty('updatedAt'); - expect(association).toHaveProperty('deletedAt'); - expect(association).toHaveProperty('calendarChannelId'); - expect(association).toHaveProperty('calendarEventId'); - }, - ); - }); - - it('1b. should create and return one calendarChannelEventAssociation', async () => { - const eventExternalId = generateRecordName( - CALENDAR_CHANNEL_EVENT_ASSOCIATION_3_ID, - ); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - data: { - id: CALENDAR_CHANNEL_EVENT_ASSOCIATION_3_ID, - eventExternalId: eventExternalId, - calendarChannelId: CALENDAR_CHANNEL_ID, - calendarEventId: CALENDAR_EVENT_ID, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdAssociation = - response.body.data.createCalendarChannelEventAssociation; - - expect(createdAssociation).toHaveProperty('eventExternalId'); - expect(createdAssociation.eventExternalId).toEqual(eventExternalId); - expect(createdAssociation).toHaveProperty('id'); - expect(createdAssociation).toHaveProperty('createdAt'); - expect(createdAssociation).toHaveProperty('updatedAt'); - expect(createdAssociation).toHaveProperty('deletedAt'); - expect(createdAssociation).toHaveProperty('calendarChannelId'); - expect(createdAssociation).toHaveProperty('calendarEventId'); - }); - - it('2. should find many calendarChannelEventAssociations', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - objectMetadataPluralName: 'calendarChannelEventAssociations', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.calendarChannelEventAssociations; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - if (data.edges.length > 0) { - const associations = data.edges[0].node; - - expect(associations).toHaveProperty('eventExternalId'); - expect(associations).toHaveProperty('id'); - expect(associations).toHaveProperty('createdAt'); - expect(associations).toHaveProperty('updatedAt'); - expect(associations).toHaveProperty('deletedAt'); - expect(associations).toHaveProperty('calendarChannelId'); - expect(associations).toHaveProperty('calendarEventId'); - } - }); - - it('2b. should find one calendarChannelEventAssociation', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - eq: CALENDAR_CHANNEL_EVENT_ASSOCIATION_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const association = response.body.data.calendarChannelEventAssociation; - - expect(association).toHaveProperty('eventExternalId'); - expect(association).toHaveProperty('id'); - expect(association).toHaveProperty('createdAt'); - expect(association).toHaveProperty('updatedAt'); - expect(association).toHaveProperty('deletedAt'); - expect(association).toHaveProperty('calendarChannelId'); - expect(association).toHaveProperty('calendarEventId'); - }); - - it('3. should update many calendarChannelEventAssociations', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - objectMetadataPluralName: 'calendarChannelEventAssociations', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - data: { - eventExternalId: 'updated-message-external-id', - }, - filter: { - id: { - in: [ - CALENDAR_CHANNEL_EVENT_ASSOCIATION_1_ID, - CALENDAR_CHANNEL_EVENT_ASSOCIATION_2_ID, - ], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedAssociations = - response.body.data.updateCalendarChannelEventAssociations; - - expect(updatedAssociations).toHaveLength(2); - - updatedAssociations.forEach((association) => { - expect(association.eventExternalId).toEqual( - 'updated-message-external-id', - ); - }); - }); - - it('3b. should update one calendarChannelEventAssociation', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - data: { - eventExternalId: 'new-message-external-id', - }, - recordId: CALENDAR_CHANNEL_EVENT_ASSOCIATION_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedAssociation = - response.body.data.updateCalendarChannelEventAssociation; - - expect(updatedAssociation.eventExternalId).toEqual( - 'new-message-external-id', - ); - }); - - it('4. should find many calendarChannelEventAssociations with updated eventExternalId', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - objectMetadataPluralName: 'calendarChannelEventAssociations', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - filter: { - eventExternalId: { - eq: 'updated-message-external-id', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.calendarChannelEventAssociations.edges, - ).toHaveLength(2); - }); - - it('4b. should find one calendarChannelEventAssociation with updated eventExternalId', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - filter: { - eventExternalId: { - eq: 'new-message-external-id', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.calendarChannelEventAssociation.eventExternalId, - ).toEqual('new-message-external-id'); - }); - - it('5. should delete many calendarChannelEventAssociations', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - objectMetadataPluralName: 'calendarChannelEventAssociations', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - in: [ - CALENDAR_CHANNEL_EVENT_ASSOCIATION_1_ID, - CALENDAR_CHANNEL_EVENT_ASSOCIATION_2_ID, - ], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deletedAssociations = - response.body.data.deleteCalendarChannelEventAssociations; - - expect(deletedAssociations).toHaveLength(2); - - deletedAssociations.forEach((association) => { - expect(association.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one calendarChannelEventAssociation', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - recordId: CALENDAR_CHANNEL_EVENT_ASSOCIATION_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.deleteCalendarChannelEventAssociation.deletedAt, - ).toBeTruthy(); - }); - - it('6. should not find many calendarChannelEventAssociations anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - objectMetadataPluralName: 'calendarChannelEventAssociations', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - in: [ - CALENDAR_CHANNEL_EVENT_ASSOCIATION_1_ID, - CALENDAR_CHANNEL_EVENT_ASSOCIATION_2_ID, - ], - }, - }, - }); - - const findAssociationsResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - findAssociationsResponse.body.data.calendarChannelEventAssociations.edges, - ).toHaveLength(0); - }); - - it('6b. should not find one calendarChannelEventAssociation anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - eq: CALENDAR_CHANNEL_EVENT_ASSOCIATION_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.calendarChannelEventAssociation).toBeNull(); - }); - - it('7. should find many deleted calendarChannelEventAssociations with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - objectMetadataPluralName: 'calendarChannelEventAssociations', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - in: [ - CALENDAR_CHANNEL_EVENT_ASSOCIATION_1_ID, - CALENDAR_CHANNEL_EVENT_ASSOCIATION_2_ID, - ], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.calendarChannelEventAssociations.edges, - ).toHaveLength(2); - }); - - it('7b. should find one deleted calendarChannelEventAssociation with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - eq: CALENDAR_CHANNEL_EVENT_ASSOCIATION_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.calendarChannelEventAssociation.id).toEqual( - CALENDAR_CHANNEL_EVENT_ASSOCIATION_3_ID, - ); - }); - - it('8. should destroy many calendarChannelEventAssociations', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - objectMetadataPluralName: 'calendarChannelEventAssociations', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - in: [ - CALENDAR_CHANNEL_EVENT_ASSOCIATION_1_ID, - CALENDAR_CHANNEL_EVENT_ASSOCIATION_2_ID, - ], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.destroyCalendarChannelEventAssociations, - ).toHaveLength(2); - }); - - it('8b. should destroy one calendarChannelEventAssociation', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - recordId: CALENDAR_CHANNEL_EVENT_ASSOCIATION_3_ID, - }); - - const destroyAssociationResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - destroyAssociationResponse.body.data - .destroyCalendarChannelEventAssociation, - ).toBeTruthy(); - }); - - it('9. should not find many calendarChannelEventAssociations anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - objectMetadataPluralName: 'calendarChannelEventAssociations', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - in: [ - CALENDAR_CHANNEL_EVENT_ASSOCIATION_1_ID, - CALENDAR_CHANNEL_EVENT_ASSOCIATION_2_ID, - ], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.calendarChannelEventAssociations.edges, - ).toHaveLength(0); - }); - - it('9b. should not find one calendarChannelEventAssociation anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'calendarChannelEventAssociation', - gqlFields: CALENDAR_CHANNEL_EVENT_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - eq: CALENDAR_CHANNEL_EVENT_ASSOCIATION_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.calendarChannelEventAssociation).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-calendar-event-participants-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-calendar-event-participants-resolvers.integration-spec.ts deleted file mode 100644 index 118533f2f..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-calendar-event-participants-resolvers.integration-spec.ts +++ /dev/null @@ -1,479 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const CALENDAR_EVENT_PARTICIPANT_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const CALENDAR_EVENT_PARTICIPANT_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const CALENDAR_EVENT_PARTICIPANT_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; -const CALENDAR_EVENT_ID = '777a8457-eb2d-40ac-a707-441b615b6989'; -const CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS = ` - id - handle - displayName - isOrganizer - responseStatus - deletedAt -`; - -describe('calendarEventParticipants resolvers (integration)', () => { - beforeAll(async () => { - const calendarEventTitle = generateRecordName(CALENDAR_EVENT_ID); - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'calendarEvent', - gqlFields: `id`, - data: { - id: CALENDAR_EVENT_ID, - title: calendarEventTitle, - }, - }); - - await makeGraphqlAPIRequest(graphqlOperation); - }); - - afterAll(async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'calendarEvent', - gqlFields: `id`, - recordId: CALENDAR_EVENT_ID, - }); - - await makeGraphqlAPIRequest(graphqlOperation); - }); - - it('1. should create and return calendarEventParticipants', async () => { - const calendarEventParticipantDisplayName1 = generateRecordName( - CALENDAR_EVENT_PARTICIPANT_1_ID, - ); - const calendarEventParticipantDisplayName2 = generateRecordName( - CALENDAR_EVENT_PARTICIPANT_2_ID, - ); - - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - objectMetadataPluralName: 'calendarEventParticipants', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - data: [ - { - id: CALENDAR_EVENT_PARTICIPANT_1_ID, - displayName: calendarEventParticipantDisplayName1, - calendarEventId: CALENDAR_EVENT_ID, - }, - { - id: CALENDAR_EVENT_PARTICIPANT_2_ID, - displayName: calendarEventParticipantDisplayName2, - calendarEventId: CALENDAR_EVENT_ID, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createCalendarEventParticipants).toHaveLength(2); - - response.body.data.createCalendarEventParticipants.forEach( - (calendarEventParticipant) => { - expect(calendarEventParticipant).toHaveProperty('displayName'); - expect([ - calendarEventParticipantDisplayName1, - calendarEventParticipantDisplayName2, - ]).toContain(calendarEventParticipant.displayName); - - expect(calendarEventParticipant).toHaveProperty('id'); - expect(calendarEventParticipant).toHaveProperty('handle'); - expect(calendarEventParticipant).toHaveProperty('isOrganizer'); - expect(calendarEventParticipant).toHaveProperty('responseStatus'); - expect(calendarEventParticipant).toHaveProperty('deletedAt'); - }, - ); - }); - - it('1b. should create and return one calendarEventParticipant', async () => { - const calendarEventParticipantDisplayName = generateRecordName( - CALENDAR_EVENT_PARTICIPANT_3_ID, - ); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - data: { - id: CALENDAR_EVENT_PARTICIPANT_3_ID, - displayName: calendarEventParticipantDisplayName, - calendarEventId: CALENDAR_EVENT_ID, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdCalendarEventParticipant = - response.body.data.createCalendarEventParticipant; - - expect(createdCalendarEventParticipant).toHaveProperty('displayName'); - expect(createdCalendarEventParticipant.displayName).toEqual( - calendarEventParticipantDisplayName, - ); - - expect(createdCalendarEventParticipant).toHaveProperty('id'); - expect(createdCalendarEventParticipant).toHaveProperty('handle'); - expect(createdCalendarEventParticipant).toHaveProperty('isOrganizer'); - expect(createdCalendarEventParticipant).toHaveProperty('responseStatus'); - expect(createdCalendarEventParticipant).toHaveProperty('deletedAt'); - }); - - it('2. should find many calendarEventParticipants', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - objectMetadataPluralName: 'calendarEventParticipants', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.calendarEventParticipants; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - const edges = data.edges; - - if (edges.length > 0) { - const calendarEventParticipants = edges[0].node; - - expect(calendarEventParticipants).toHaveProperty('displayName'); - expect(calendarEventParticipants).toHaveProperty('id'); - expect(calendarEventParticipants).toHaveProperty('handle'); - expect(calendarEventParticipants).toHaveProperty('isOrganizer'); - expect(calendarEventParticipants).toHaveProperty('responseStatus'); - expect(calendarEventParticipants).toHaveProperty('deletedAt'); - } - }); - - it('2b. should find one calendarEventParticipant', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - eq: CALENDAR_EVENT_PARTICIPANT_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const calendarEventParticipant = - response.body.data.calendarEventParticipant; - - expect(calendarEventParticipant).toHaveProperty('displayName'); - - expect(calendarEventParticipant).toHaveProperty('id'); - expect(calendarEventParticipant).toHaveProperty('handle'); - expect(calendarEventParticipant).toHaveProperty('isOrganizer'); - expect(calendarEventParticipant).toHaveProperty('responseStatus'); - expect(calendarEventParticipant).toHaveProperty('deletedAt'); - }); - - it('3. should update many calendarEventParticipants', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - objectMetadataPluralName: 'calendarEventParticipants', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - data: { - displayName: 'New DisplayName', - }, - filter: { - id: { - in: [ - CALENDAR_EVENT_PARTICIPANT_1_ID, - CALENDAR_EVENT_PARTICIPANT_2_ID, - ], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedcalendarEventParticipants = - response.body.data.updateCalendarEventParticipants; - - expect(updatedcalendarEventParticipants).toHaveLength(2); - - updatedcalendarEventParticipants.forEach((calendarEventParticipant) => { - expect(calendarEventParticipant.displayName).toEqual('New DisplayName'); - }); - }); - - it('3b. should update one calendarEventParticipant', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - data: { - displayName: 'Updated DisplayName', - }, - recordId: CALENDAR_EVENT_PARTICIPANT_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedcalendarEventParticipant = - response.body.data.updateCalendarEventParticipant; - - expect(updatedcalendarEventParticipant.displayName).toEqual( - 'Updated DisplayName', - ); - }); - - it('4. should find many calendarEventParticipants with updated displayName', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - objectMetadataPluralName: 'calendarEventParticipants', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - filter: { - displayName: { - eq: 'New DisplayName', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.calendarEventParticipants.edges).toHaveLength(2); - }); - - it('4b. should find one calendarEventParticipant with updated displayName', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - filter: { - displayName: { - eq: 'Updated DisplayName', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.calendarEventParticipant.displayName).toEqual( - 'Updated DisplayName', - ); - }); - - it('5. should delete many calendarEventParticipants', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - objectMetadataPluralName: 'calendarEventParticipants', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - in: [ - CALENDAR_EVENT_PARTICIPANT_1_ID, - CALENDAR_EVENT_PARTICIPANT_2_ID, - ], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deleteCalendarEventParticipants = - response.body.data.deleteCalendarEventParticipants; - - expect(deleteCalendarEventParticipants).toHaveLength(2); - - deleteCalendarEventParticipants.forEach((calendarEventParticipant) => { - expect(calendarEventParticipant.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one calendarEventParticipant', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - recordId: CALENDAR_EVENT_PARTICIPANT_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.deleteCalendarEventParticipant.deletedAt, - ).toBeTruthy(); - }); - - it('6. should not find many calendarEventParticipants anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - objectMetadataPluralName: 'calendarEventParticipants', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - in: [ - CALENDAR_EVENT_PARTICIPANT_1_ID, - CALENDAR_EVENT_PARTICIPANT_2_ID, - ], - }, - }, - }); - - const findCalendarEventParticipantsResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - findCalendarEventParticipantsResponse.body.data.calendarEventParticipants - .edges, - ).toHaveLength(0); - }); - - it('6b. should not find one calendarEventParticipant anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - eq: CALENDAR_EVENT_PARTICIPANT_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.calendarEventParticipant).toBeNull(); - }); - - it('7. should find many deleted calendarEventParticipants with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - objectMetadataPluralName: 'calendarEventParticipants', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - in: [ - CALENDAR_EVENT_PARTICIPANT_1_ID, - CALENDAR_EVENT_PARTICIPANT_2_ID, - ], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.calendarEventParticipants.edges).toHaveLength(2); - }); - - it('7b. should find one deleted calendarEventParticipant with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - eq: CALENDAR_EVENT_PARTICIPANT_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.calendarEventParticipant.id).toEqual( - CALENDAR_EVENT_PARTICIPANT_3_ID, - ); - }); - - it('8. should destroy many calendarEventParticipants', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - objectMetadataPluralName: 'calendarEventParticipants', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - in: [ - CALENDAR_EVENT_PARTICIPANT_1_ID, - CALENDAR_EVENT_PARTICIPANT_2_ID, - ], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyCalendarEventParticipants).toHaveLength(2); - }); - - it('8b. should destroy one calendarEventParticipant', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - recordId: CALENDAR_EVENT_PARTICIPANT_3_ID, - }); - - const destroyCalendarEventParticipantResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - destroyCalendarEventParticipantResponse.body.data - .destroyCalendarEventParticipant, - ).toBeTruthy(); - }); - - it('9. should not find many calendarEventParticipants anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - objectMetadataPluralName: 'calendarEventParticipants', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - in: [ - CALENDAR_EVENT_PARTICIPANT_1_ID, - CALENDAR_EVENT_PARTICIPANT_2_ID, - ], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.calendarEventParticipants.edges).toHaveLength(0); - }); - - it('9b. should not find one calendarEventParticipant anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'calendarEventParticipant', - gqlFields: CALENDAR_EVENT_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - eq: CALENDAR_EVENT_PARTICIPANT_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.calendarEventParticipant).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-connected-accounts-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-connected-accounts-resolvers.integration-spec.ts deleted file mode 100644 index d64e55900..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-connected-accounts-resolvers.integration-spec.ts +++ /dev/null @@ -1,420 +0,0 @@ -import { TIM_ACCOUNT_ID } from 'test/integration/graphql/integration.constants'; -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const CONNECTED_ACCOUNT_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const CONNECTED_ACCOUNT_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const CONNECTED_ACCOUNT_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; -const CONNECTED_ACCOUNT_GQL_FIELDS = ` - id - handle - deletedAt - createdAt - provider - accessToken - scopes -`; - -describe('connectedAccounts resolvers (integration)', () => { - it('1. should create and return connectedAccounts', async () => { - const connectedAccountHandle1 = generateRecordName(CONNECTED_ACCOUNT_1_ID); - const connectedAccountHandle2 = generateRecordName(CONNECTED_ACCOUNT_2_ID); - - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - objectMetadataPluralName: 'connectedAccounts', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - data: [ - { - id: CONNECTED_ACCOUNT_1_ID, - handle: connectedAccountHandle1, - accountOwnerId: TIM_ACCOUNT_ID, - }, - { - id: CONNECTED_ACCOUNT_2_ID, - handle: connectedAccountHandle2, - accountOwnerId: TIM_ACCOUNT_ID, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createConnectedAccounts).toHaveLength(2); - - response.body.data.createConnectedAccounts.forEach((connectedAccount) => { - expect(connectedAccount).toHaveProperty('handle'); - expect([connectedAccountHandle1, connectedAccountHandle2]).toContain( - connectedAccount.handle, - ); - - expect(connectedAccount).toHaveProperty('id'); - expect(connectedAccount).toHaveProperty('deletedAt'); - expect(connectedAccount).toHaveProperty('createdAt'); - expect(connectedAccount).toHaveProperty('provider'); - expect(connectedAccount).toHaveProperty('accessToken'); - expect(connectedAccount).toHaveProperty('scopes'); - }); - }); - - it('1b. should create and return one connectedAccount', async () => { - const connectedAccountHandle = generateRecordName(CONNECTED_ACCOUNT_3_ID); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - data: { - id: CONNECTED_ACCOUNT_3_ID, - handle: connectedAccountHandle, - accountOwnerId: TIM_ACCOUNT_ID, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdConnectedAccount = response.body.data.createConnectedAccount; - - expect(createdConnectedAccount).toHaveProperty('handle'); - expect(createdConnectedAccount.handle).toEqual(connectedAccountHandle); - - expect(createdConnectedAccount).toHaveProperty('id'); - expect(createdConnectedAccount).toHaveProperty('deletedAt'); - expect(createdConnectedAccount).toHaveProperty('createdAt'); - expect(createdConnectedAccount).toHaveProperty('provider'); - expect(createdConnectedAccount).toHaveProperty('accessToken'); - expect(createdConnectedAccount).toHaveProperty('scopes'); - }); - - it('2. should find many connectedAccounts', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - objectMetadataPluralName: 'connectedAccounts', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.connectedAccounts; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - const edges = data.edges; - - if (edges.length > 0) { - const connectedAccounts = edges[0].node; - - expect(connectedAccounts).toHaveProperty('handle'); - expect(connectedAccounts).toHaveProperty('id'); - expect(connectedAccounts).toHaveProperty('deletedAt'); - expect(connectedAccounts).toHaveProperty('createdAt'); - expect(connectedAccounts).toHaveProperty('provider'); - expect(connectedAccounts).toHaveProperty('accessToken'); - expect(connectedAccounts).toHaveProperty('scopes'); - } - }); - - it('2b. should find one connectedAccount', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - filter: { - id: { - eq: CONNECTED_ACCOUNT_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const connectedAccount = response.body.data.connectedAccount; - - expect(connectedAccount).toHaveProperty('handle'); - - expect(connectedAccount).toHaveProperty('id'); - expect(connectedAccount).toHaveProperty('deletedAt'); - expect(connectedAccount).toHaveProperty('createdAt'); - expect(connectedAccount).toHaveProperty('provider'); - expect(connectedAccount).toHaveProperty('accessToken'); - expect(connectedAccount).toHaveProperty('scopes'); - }); - - it('3. should update many connectedAccounts', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - objectMetadataPluralName: 'connectedAccounts', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - data: { - handle: 'New Handle', - }, - filter: { - id: { - in: [CONNECTED_ACCOUNT_1_ID, CONNECTED_ACCOUNT_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedconnectedAccounts = response.body.data.updateConnectedAccounts; - - expect(updatedconnectedAccounts).toHaveLength(2); - - updatedconnectedAccounts.forEach((connectedAccount) => { - expect(connectedAccount.handle).toEqual('New Handle'); - }); - }); - - it('3b. should update one connectedAccount', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - data: { - handle: 'Updated Handle', - }, - recordId: CONNECTED_ACCOUNT_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedconnectedAccount = response.body.data.updateConnectedAccount; - - expect(updatedconnectedAccount.handle).toEqual('Updated Handle'); - }); - - it('4. should find many connectedAccounts with updated handle', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - objectMetadataPluralName: 'connectedAccounts', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - filter: { - handle: { - eq: 'New Handle', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.connectedAccounts.edges).toHaveLength(2); - }); - - it('4b. should find one connectedAccount with updated handle', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - filter: { - handle: { - eq: 'Updated Handle', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.connectedAccount.handle).toEqual( - 'Updated Handle', - ); - }); - - it('5. should delete many connectedAccounts', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - objectMetadataPluralName: 'connectedAccounts', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - filter: { - id: { - in: [CONNECTED_ACCOUNT_1_ID, CONNECTED_ACCOUNT_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deleteConnectedAccounts = response.body.data.deleteConnectedAccounts; - - expect(deleteConnectedAccounts).toHaveLength(2); - - deleteConnectedAccounts.forEach((connectedAccount) => { - expect(connectedAccount.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one connectedAccount', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - recordId: CONNECTED_ACCOUNT_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteConnectedAccount.deletedAt).toBeTruthy(); - }); - - it('6. should not find many connectedAccounts anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - objectMetadataPluralName: 'connectedAccounts', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - filter: { - id: { - in: [CONNECTED_ACCOUNT_1_ID, CONNECTED_ACCOUNT_2_ID], - }, - }, - }); - - const findConnectedAccountsResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - findConnectedAccountsResponse.body.data.connectedAccounts.edges, - ).toHaveLength(0); - }); - - it('6b. should not find one connectedAccount anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - filter: { - id: { - eq: CONNECTED_ACCOUNT_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.connectedAccount).toBeNull(); - }); - - it('7. should find many deleted connectedAccounts with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - objectMetadataPluralName: 'connectedAccounts', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - filter: { - id: { - in: [CONNECTED_ACCOUNT_1_ID, CONNECTED_ACCOUNT_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.connectedAccounts.edges).toHaveLength(2); - }); - - it('7b. should find one deleted connectedAccount with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - filter: { - id: { - eq: CONNECTED_ACCOUNT_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.connectedAccount.id).toEqual( - CONNECTED_ACCOUNT_3_ID, - ); - }); - - it('8. should destroy many connectedAccounts', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - objectMetadataPluralName: 'connectedAccounts', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - filter: { - id: { - in: [CONNECTED_ACCOUNT_1_ID, CONNECTED_ACCOUNT_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyConnectedAccounts).toHaveLength(2); - }); - - it('8b. should destroy one connectedAccount', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - recordId: CONNECTED_ACCOUNT_3_ID, - }); - - const destroyConnectedAccountResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - destroyConnectedAccountResponse.body.data.destroyConnectedAccount, - ).toBeTruthy(); - }); - - it('9. should not find many connectedAccounts anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - objectMetadataPluralName: 'connectedAccounts', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - filter: { - id: { - in: [CONNECTED_ACCOUNT_1_ID, CONNECTED_ACCOUNT_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.connectedAccounts.edges).toHaveLength(0); - }); - - it('9b. should not find one connectedAccount anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'connectedAccount', - gqlFields: CONNECTED_ACCOUNT_GQL_FIELDS, - filter: { - id: { - eq: CONNECTED_ACCOUNT_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.connectedAccount).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-favorites-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-favorites-resolvers.integration-spec.ts deleted file mode 100644 index 5ad485258..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-favorites-resolvers.integration-spec.ts +++ /dev/null @@ -1,409 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; - -const FAVORITE_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const FAVORITE_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const FAVORITE_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; -const INITIAL_FAVORITE_POSITION_1 = 1111111; -const INITIAL_FAVORITE_POSITION_2 = 2222222; -const INITIAL_FAVORITE_POSITION_3 = 3333333; -const NEW_FAVORITE_POSITION_1 = 4444444; -const NEW_FAVORITE_POSITION_2 = 5555555; -const FAVORITE_GQL_FIELDS = ` - id - position - createdAt - updatedAt - deletedAt - companyId - personId -`; - -describe('favorites resolvers (integration)', () => { - it('1. should create and return favorites', async () => { - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'favorite', - objectMetadataPluralName: 'favorites', - gqlFields: FAVORITE_GQL_FIELDS, - data: [ - { - id: FAVORITE_1_ID, - position: INITIAL_FAVORITE_POSITION_1, - }, - { - id: FAVORITE_2_ID, - position: INITIAL_FAVORITE_POSITION_2, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createFavorites).toHaveLength(2); - - response.body.data.createFavorites.forEach((favorite) => { - expect(favorite).toHaveProperty('position'); - expect([ - INITIAL_FAVORITE_POSITION_1, - INITIAL_FAVORITE_POSITION_2, - ]).toContain(favorite.position); - - expect(favorite).toHaveProperty('id'); - expect(favorite).toHaveProperty('createdAt'); - expect(favorite).toHaveProperty('updatedAt'); - expect(favorite).toHaveProperty('deletedAt'); - expect(favorite).toHaveProperty('companyId'); - expect(favorite).toHaveProperty('personId'); - }); - }); - - it('1b. should create and return one favorite', async () => { - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'favorite', - gqlFields: FAVORITE_GQL_FIELDS, - data: { - id: FAVORITE_3_ID, - position: INITIAL_FAVORITE_POSITION_3, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdFavorite = response.body.data.createFavorite; - - expect(createdFavorite).toHaveProperty('position'); - expect(createdFavorite.position).toEqual(INITIAL_FAVORITE_POSITION_3); - - expect(createdFavorite).toHaveProperty('id'); - expect(createdFavorite).toHaveProperty('createdAt'); - expect(createdFavorite).toHaveProperty('updatedAt'); - expect(createdFavorite).toHaveProperty('deletedAt'); - expect(createdFavorite).toHaveProperty('companyId'); - expect(createdFavorite).toHaveProperty('personId'); - }); - - it('2. should find many favorites', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'favorite', - objectMetadataPluralName: 'favorites', - gqlFields: FAVORITE_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.favorites; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - const edges = data.edges; - - if (edges.length > 0) { - const favorite = edges[0].node; - - expect(favorite).toHaveProperty('position'); - expect(favorite).toHaveProperty('id'); - expect(favorite).toHaveProperty('createdAt'); - expect(favorite).toHaveProperty('updatedAt'); - expect(favorite).toHaveProperty('deletedAt'); - expect(favorite).toHaveProperty('companyId'); - expect(favorite).toHaveProperty('personId'); - } - }); - - it('2b. should find one favorite', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'favorite', - gqlFields: FAVORITE_GQL_FIELDS, - filter: { - id: { - eq: FAVORITE_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const favorite = response.body.data.favorite; - - expect(favorite).toHaveProperty('position'); - - expect(favorite).toHaveProperty('id'); - expect(favorite).toHaveProperty('createdAt'); - expect(favorite).toHaveProperty('updatedAt'); - expect(favorite).toHaveProperty('deletedAt'); - expect(favorite).toHaveProperty('companyId'); - expect(favorite).toHaveProperty('personId'); - }); - - it('3. should update many favorites', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'favorite', - objectMetadataPluralName: 'favorites', - gqlFields: FAVORITE_GQL_FIELDS, - data: { - position: NEW_FAVORITE_POSITION_1, - }, - filter: { - id: { - in: [FAVORITE_1_ID, FAVORITE_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedFavorites = response.body.data.updateFavorites; - - expect(updatedFavorites).toHaveLength(2); - - updatedFavorites.forEach((favorite) => { - expect(favorite.position).toEqual(NEW_FAVORITE_POSITION_1); - }); - }); - - it('3b. should update one favorite', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'favorite', - gqlFields: FAVORITE_GQL_FIELDS, - data: { - position: NEW_FAVORITE_POSITION_2, - }, - recordId: FAVORITE_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedFavorite = response.body.data.updateFavorite; - - expect(updatedFavorite.position).toEqual(NEW_FAVORITE_POSITION_2); - }); - - it('4. should find many favorites with updated position', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'favorite', - objectMetadataPluralName: 'favorites', - gqlFields: FAVORITE_GQL_FIELDS, - filter: { - position: { - eq: NEW_FAVORITE_POSITION_1, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.favorites.edges).toHaveLength(2); - }); - - it('4b. should find one favorite with updated position', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'favorite', - gqlFields: FAVORITE_GQL_FIELDS, - filter: { - position: { - eq: NEW_FAVORITE_POSITION_2, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.favorite.position).toEqual( - NEW_FAVORITE_POSITION_2, - ); - }); - - it('5. should delete many favorites', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'favorite', - objectMetadataPluralName: 'favorites', - gqlFields: FAVORITE_GQL_FIELDS, - filter: { - id: { - in: [FAVORITE_1_ID, FAVORITE_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deleteFavorites = response.body.data.deleteFavorites; - - expect(deleteFavorites).toHaveLength(2); - - deleteFavorites.forEach((favorite) => { - expect(favorite.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one favorite', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'favorite', - gqlFields: FAVORITE_GQL_FIELDS, - recordId: FAVORITE_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteFavorite.deletedAt).toBeTruthy(); - }); - - it('6. should not find many favorites anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'favorite', - objectMetadataPluralName: 'favorites', - gqlFields: FAVORITE_GQL_FIELDS, - filter: { - id: { - in: [FAVORITE_1_ID, FAVORITE_2_ID], - }, - }, - }); - - const findFavoritesResponse = await makeGraphqlAPIRequest(graphqlOperation); - - expect(findFavoritesResponse.body.data.favorites.edges).toHaveLength(0); - }); - - it('6b. should not find one favorite anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'favorite', - gqlFields: FAVORITE_GQL_FIELDS, - filter: { - id: { - eq: FAVORITE_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.favorite).toBeNull(); - }); - - it('7. should find many deleted favorites with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'favorite', - objectMetadataPluralName: 'favorites', - gqlFields: FAVORITE_GQL_FIELDS, - filter: { - id: { - in: [FAVORITE_1_ID, FAVORITE_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.favorites.edges).toHaveLength(2); - }); - - it('7b. should find one deleted favorite with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'favorite', - gqlFields: FAVORITE_GQL_FIELDS, - filter: { - id: { - eq: FAVORITE_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.favorite.id).toEqual(FAVORITE_3_ID); - }); - - it('8. should destroy many favorites', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'favorite', - objectMetadataPluralName: 'favorites', - gqlFields: FAVORITE_GQL_FIELDS, - filter: { - id: { - in: [FAVORITE_1_ID, FAVORITE_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyFavorites).toHaveLength(2); - }); - - it('8b. should destroy one favorite', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'favorite', - gqlFields: FAVORITE_GQL_FIELDS, - recordId: FAVORITE_3_ID, - }); - - const destroyFavoriteResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyFavoriteResponse.body.data.destroyFavorite).toBeTruthy(); - }); - - it('9. should not find many favorites anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'favorite', - objectMetadataPluralName: 'favorites', - gqlFields: FAVORITE_GQL_FIELDS, - filter: { - id: { - in: [FAVORITE_1_ID, FAVORITE_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.favorites.edges).toHaveLength(0); - }); - - it('9b. should not find one favorite anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'favorite', - gqlFields: FAVORITE_GQL_FIELDS, - filter: { - id: { - eq: FAVORITE_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.favorite).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-message-channel-message-associations-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-message-channel-message-associations-resolvers.integration-spec.ts deleted file mode 100644 index 261331106..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-message-channel-message-associations-resolvers.integration-spec.ts +++ /dev/null @@ -1,493 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_1_ID = - '777a8457-eb2d-40ac-a707-551b615b6987'; -const MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_2_ID = - '777a8457-eb2d-40ac-a707-551b615b6988'; -const MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_3_ID = - '777a8457-eb2d-40ac-a707-551b615b6989'; - -const MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS = ` - id - messageExternalId - createdAt - updatedAt - deletedAt - messageChannelId - messageId - direction -`; - -describe('messageChannelMessageAssociations resolvers (integration)', () => { - it('1. should create and return messageChannelMessageAssociations', async () => { - const messageExternalId1 = generateRecordName( - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_1_ID, - ); - const messageExternalId2 = generateRecordName( - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_2_ID, - ); - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - objectMetadataPluralName: 'messageChannelMessageAssociations', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - data: [ - { - id: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_1_ID, - messageExternalId: messageExternalId1, - }, - { - id: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_2_ID, - messageExternalId: messageExternalId2, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.createMessageChannelMessageAssociations, - ).toHaveLength(2); - - response.body.data.createMessageChannelMessageAssociations.forEach( - (messageChannelMessageAssociation) => { - expect(messageChannelMessageAssociation).toHaveProperty( - 'messageExternalId', - ); - expect([messageExternalId1, messageExternalId2]).toContain( - messageChannelMessageAssociation.messageExternalId, - ); - - expect(messageChannelMessageAssociation).toHaveProperty('id'); - expect(messageChannelMessageAssociation).toHaveProperty('createdAt'); - expect(messageChannelMessageAssociation).toHaveProperty('updatedAt'); - expect(messageChannelMessageAssociation).toHaveProperty('deletedAt'); - expect(messageChannelMessageAssociation).toHaveProperty( - 'messageChannelId', - ); - expect(messageChannelMessageAssociation).toHaveProperty('messageId'); - expect(messageChannelMessageAssociation).toHaveProperty('direction'); - }, - ); - }); - - it('1b. should create and return one messageChannelMessageAssociation', async () => { - const messageExternalId3 = generateRecordName( - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_3_ID, - ); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - data: { - id: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_3_ID, - messageExternalId: messageExternalId3, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdMessageChannelMessageAssociation = - response.body.data.createMessageChannelMessageAssociation; - - expect(createdMessageChannelMessageAssociation).toHaveProperty( - 'messageExternalId', - ); - expect(createdMessageChannelMessageAssociation.messageExternalId).toEqual( - messageExternalId3, - ); - - expect(createdMessageChannelMessageAssociation).toHaveProperty('id'); - expect(createdMessageChannelMessageAssociation).toHaveProperty('createdAt'); - expect(createdMessageChannelMessageAssociation).toHaveProperty('updatedAt'); - expect(createdMessageChannelMessageAssociation).toHaveProperty('deletedAt'); - expect(createdMessageChannelMessageAssociation).toHaveProperty( - 'messageChannelId', - ); - expect(createdMessageChannelMessageAssociation).toHaveProperty('messageId'); - expect(createdMessageChannelMessageAssociation).toHaveProperty('direction'); - }); - - it('2. should find many messageChannelMessageAssociations', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - objectMetadataPluralName: 'messageChannelMessageAssociations', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.messageChannelMessageAssociations; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - const edges = data.edges; - - if (edges.length > 0) { - const messageChannelMessageAssociation = edges[0].node; - - expect(messageChannelMessageAssociation).toHaveProperty( - 'messageExternalId', - ); - expect(messageChannelMessageAssociation).toHaveProperty('id'); - expect(messageChannelMessageAssociation).toHaveProperty('createdAt'); - expect(messageChannelMessageAssociation).toHaveProperty('updatedAt'); - expect(messageChannelMessageAssociation).toHaveProperty('deletedAt'); - expect(messageChannelMessageAssociation).toHaveProperty( - 'messageChannelId', - ); - expect(messageChannelMessageAssociation).toHaveProperty('messageId'); - expect(messageChannelMessageAssociation).toHaveProperty('direction'); - } - }); - - it('2b. should find one messageChannelMessageAssociation', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - eq: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const messageChannelMessageAssociation = - response.body.data.messageChannelMessageAssociation; - - expect(messageChannelMessageAssociation).toHaveProperty( - 'messageExternalId', - ); - - expect(messageChannelMessageAssociation).toHaveProperty('id'); - expect(messageChannelMessageAssociation).toHaveProperty('createdAt'); - expect(messageChannelMessageAssociation).toHaveProperty('updatedAt'); - expect(messageChannelMessageAssociation).toHaveProperty('deletedAt'); - expect(messageChannelMessageAssociation).toHaveProperty('messageChannelId'); - expect(messageChannelMessageAssociation).toHaveProperty('messageId'); - expect(messageChannelMessageAssociation).toHaveProperty('direction'); - }); - - it('3. should update many messageChannelMessageAssociations', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - objectMetadataPluralName: 'messageChannelMessageAssociations', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - data: { - messageExternalId: 'updated-message-external-id', - }, - filter: { - id: { - in: [ - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_1_ID, - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_2_ID, - ], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedmessageChannelMessageAssociations = - response.body.data.updateMessageChannelMessageAssociations; - - expect(updatedmessageChannelMessageAssociations).toHaveLength(2); - - updatedmessageChannelMessageAssociations.forEach( - (messageChannelMessageAssociation) => { - expect(messageChannelMessageAssociation.messageExternalId).toEqual( - 'updated-message-external-id', - ); - }, - ); - }); - - it('3b. should update one messageChannelMessageAssociation', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - data: { - messageExternalId: 'new-message-external-id', - }, - recordId: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedmessageChannelMessageAssociation = - response.body.data.updateMessageChannelMessageAssociation; - - expect(updatedmessageChannelMessageAssociation.messageExternalId).toEqual( - 'new-message-external-id', - ); - }); - - it('4. should find many messageChannelMessageAssociations with updated messageExternalId', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - objectMetadataPluralName: 'messageChannelMessageAssociations', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - filter: { - messageExternalId: { - eq: 'updated-message-external-id', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.messageChannelMessageAssociations.edges, - ).toHaveLength(2); - }); - - it('4b. should find one messageChannelMessageAssociation with updated messageExternalId', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - filter: { - messageExternalId: { - eq: 'new-message-external-id', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.messageChannelMessageAssociation.messageExternalId, - ).toEqual('new-message-external-id'); - }); - - it('5. should delete many messageChannelMessageAssociations', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - objectMetadataPluralName: 'messageChannelMessageAssociations', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - in: [ - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_1_ID, - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_2_ID, - ], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deleteMessageChannelMessageAssociations = - response.body.data.deleteMessageChannelMessageAssociations; - - expect(deleteMessageChannelMessageAssociations).toHaveLength(2); - - deleteMessageChannelMessageAssociations.forEach( - (messageChannelMessageAssociation) => { - expect(messageChannelMessageAssociation.deletedAt).toBeTruthy(); - }, - ); - }); - - it('5b. should delete one messageChannelMessageAssociation', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - recordId: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.deleteMessageChannelMessageAssociation.deletedAt, - ).toBeTruthy(); - }); - - it('6. should not find many messageChannelMessageAssociations anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - objectMetadataPluralName: 'messageChannelMessageAssociations', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - in: [ - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_1_ID, - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_2_ID, - ], - }, - }, - }); - - const findMessageChannelMessageAssociationsResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - findMessageChannelMessageAssociationsResponse.body.data - .messageChannelMessageAssociations.edges, - ).toHaveLength(0); - }); - - it('6b. should not find one messageChannelMessageAssociation anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - eq: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageChannelMessageAssociation).toBeNull(); - }); - - it('7. should find many deleted messageChannelMessageAssociations with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - objectMetadataPluralName: 'messageChannelMessageAssociations', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - in: [ - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_1_ID, - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_2_ID, - ], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.messageChannelMessageAssociations.edges, - ).toHaveLength(2); - }); - - it('7b. should find one deleted messageChannelMessageAssociation with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - eq: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageChannelMessageAssociation.id).toEqual( - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_3_ID, - ); - }); - - it('8. should destroy many messageChannelMessageAssociations', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - objectMetadataPluralName: 'messageChannelMessageAssociations', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - in: [ - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_1_ID, - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_2_ID, - ], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.destroyMessageChannelMessageAssociations, - ).toHaveLength(2); - }); - - it('8b. should destroy one messageChannelMessageAssociation', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - recordId: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_3_ID, - }); - - const destroyMessageChannelMessageAssociationResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - destroyMessageChannelMessageAssociationResponse.body.data - .destroyMessageChannelMessageAssociation, - ).toBeTruthy(); - }); - - it('9. should not find many messageChannelMessageAssociations anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - objectMetadataPluralName: 'messageChannelMessageAssociations', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - in: [ - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_1_ID, - MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_2_ID, - ], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect( - response.body.data.messageChannelMessageAssociations.edges, - ).toHaveLength(0); - }); - - it('9b. should not find one messageChannelMessageAssociation anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageChannelMessageAssociation', - gqlFields: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_GQL_FIELDS, - filter: { - id: { - eq: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageChannelMessageAssociation).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-message-participants-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-message-participants-resolvers.integration-spec.ts deleted file mode 100644 index 3c064564b..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-message-participants-resolvers.integration-spec.ts +++ /dev/null @@ -1,466 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const MESSAGE_PARTICIPANT_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const MESSAGE_PARTICIPANT_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const MESSAGE_PARTICIPANT_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; -const MESSAGE_ID = '777a8457-eb2d-40ac-a707-441b615b6989'; -const MESSAGE_PARTICIPANT_GQL_FIELDS = ` - id - displayName - handle - role - messageId - workspaceMemberId - createdAt - deletedAt -`; - -describe('messageParticipants resolvers (integration)', () => { - beforeAll(async () => { - const messageSubject = generateRecordName(MESSAGE_ID); - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'message', - gqlFields: `id`, - data: { - id: MESSAGE_ID, - subject: messageSubject, - }, - }); - - await makeGraphqlAPIRequest(graphqlOperation); - }); - - afterAll(async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'message', - gqlFields: `id`, - recordId: MESSAGE_ID, - }); - - await makeGraphqlAPIRequest(graphqlOperation); - }); - - it('1. should create and return messageParticipants', async () => { - const messageParticipantDisplayName1 = generateRecordName( - MESSAGE_PARTICIPANT_1_ID, - ); - const messageParticipantDisplayName2 = generateRecordName( - MESSAGE_PARTICIPANT_2_ID, - ); - - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - objectMetadataPluralName: 'messageParticipants', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - data: [ - { - id: MESSAGE_PARTICIPANT_1_ID, - displayName: messageParticipantDisplayName1, - messageId: MESSAGE_ID, - }, - { - id: MESSAGE_PARTICIPANT_2_ID, - displayName: messageParticipantDisplayName2, - messageId: MESSAGE_ID, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createMessageParticipants).toHaveLength(2); - - response.body.data.createMessageParticipants.forEach( - (messageParticipant) => { - expect(messageParticipant).toHaveProperty('displayName'); - expect([ - messageParticipantDisplayName1, - messageParticipantDisplayName2, - ]).toContain(messageParticipant.displayName); - - expect(messageParticipant).toHaveProperty('id'); - expect(messageParticipant).toHaveProperty('handle'); - expect(messageParticipant).toHaveProperty('role'); - expect(messageParticipant).toHaveProperty('messageId'); - expect(messageParticipant).toHaveProperty('workspaceMemberId'); - expect(messageParticipant).toHaveProperty('createdAt'); - expect(messageParticipant).toHaveProperty('deletedAt'); - }, - ); - }); - - it('1b. should create and return one messageParticipant', async () => { - const messageParticipantDisplayName = generateRecordName( - MESSAGE_PARTICIPANT_3_ID, - ); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - data: { - id: MESSAGE_PARTICIPANT_3_ID, - displayName: messageParticipantDisplayName, - messageId: MESSAGE_ID, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdMessageParticipant = - response.body.data.createMessageParticipant; - - expect(createdMessageParticipant).toHaveProperty('displayName'); - expect(createdMessageParticipant.displayName).toEqual( - messageParticipantDisplayName, - ); - - expect(createdMessageParticipant).toHaveProperty('id'); - expect(createdMessageParticipant).toHaveProperty('handle'); - expect(createdMessageParticipant).toHaveProperty('role'); - expect(createdMessageParticipant).toHaveProperty('messageId'); - expect(createdMessageParticipant).toHaveProperty('workspaceMemberId'); - expect(createdMessageParticipant).toHaveProperty('createdAt'); - expect(createdMessageParticipant).toHaveProperty('deletedAt'); - }); - - it('2. should find many messageParticipants', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - objectMetadataPluralName: 'messageParticipants', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.messageParticipants; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - const edges = data.edges; - - if (edges.length > 0) { - const messageParticipant = edges[0].node; - - expect(messageParticipant).toHaveProperty('displayName'); - expect(messageParticipant).toHaveProperty('id'); - expect(messageParticipant).toHaveProperty('handle'); - expect(messageParticipant).toHaveProperty('role'); - expect(messageParticipant).toHaveProperty('messageId'); - expect(messageParticipant).toHaveProperty('workspaceMemberId'); - expect(messageParticipant).toHaveProperty('createdAt'); - expect(messageParticipant).toHaveProperty('deletedAt'); - } - }); - - it('2b. should find one messageParticipant', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - eq: MESSAGE_PARTICIPANT_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const messageParticipant = response.body.data.messageParticipant; - - expect(messageParticipant).toHaveProperty('displayName'); - - expect(messageParticipant).toHaveProperty('id'); - expect(messageParticipant).toHaveProperty('handle'); - expect(messageParticipant).toHaveProperty('role'); - expect(messageParticipant).toHaveProperty('messageId'); - expect(messageParticipant).toHaveProperty('workspaceMemberId'); - expect(messageParticipant).toHaveProperty('createdAt'); - expect(messageParticipant).toHaveProperty('deletedAt'); - }); - - it('3. should update many messageParticipants', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - objectMetadataPluralName: 'messageParticipants', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - data: { - displayName: 'New DisplayName', - }, - filter: { - id: { - in: [MESSAGE_PARTICIPANT_1_ID, MESSAGE_PARTICIPANT_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedmessageParticipants = - response.body.data.updateMessageParticipants; - - expect(updatedmessageParticipants).toHaveLength(2); - - updatedmessageParticipants.forEach((messageParticipant) => { - expect(messageParticipant.displayName).toEqual('New DisplayName'); - }); - }); - - it('3b. should update one messageParticipant', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - data: { - displayName: 'Updated DisplayName', - }, - recordId: MESSAGE_PARTICIPANT_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedmessageParticipant = - response.body.data.updateMessageParticipant; - - expect(updatedmessageParticipant.displayName).toEqual( - 'Updated DisplayName', - ); - }); - - it('4. should find many messageParticipants with updated displayName', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - objectMetadataPluralName: 'messageParticipants', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - filter: { - displayName: { - eq: 'New DisplayName', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageParticipants.edges).toHaveLength(2); - }); - - it('4b. should find one messageParticipant with updated displayName', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - filter: { - displayName: { - eq: 'Updated DisplayName', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageParticipant.displayName).toEqual( - 'Updated DisplayName', - ); - }); - - it('5. should delete many messageParticipants', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - objectMetadataPluralName: 'messageParticipants', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - in: [MESSAGE_PARTICIPANT_1_ID, MESSAGE_PARTICIPANT_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deleteMessageParticipants = - response.body.data.deleteMessageParticipants; - - expect(deleteMessageParticipants).toHaveLength(2); - - deleteMessageParticipants.forEach((messageParticipant) => { - expect(messageParticipant.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one messageParticipant', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - recordId: MESSAGE_PARTICIPANT_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteMessageParticipant.deletedAt).toBeTruthy(); - }); - - it('6. should not find many messageParticipants anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - objectMetadataPluralName: 'messageParticipants', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - in: [MESSAGE_PARTICIPANT_1_ID, MESSAGE_PARTICIPANT_2_ID], - }, - }, - }); - - const findMessageParticipantsResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - findMessageParticipantsResponse.body.data.messageParticipants.edges, - ).toHaveLength(0); - }); - - it('6b. should not find one messageParticipant anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - eq: MESSAGE_PARTICIPANT_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageParticipant).toBeNull(); - }); - - it('7. should find many deleted messageParticipants with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - objectMetadataPluralName: 'messageParticipants', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - in: [MESSAGE_PARTICIPANT_1_ID, MESSAGE_PARTICIPANT_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageParticipants.edges).toHaveLength(2); - }); - - it('7b. should find one deleted messageParticipant with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - eq: MESSAGE_PARTICIPANT_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageParticipant.id).toEqual( - MESSAGE_PARTICIPANT_3_ID, - ); - }); - - it('8. should destroy many messageParticipants', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - objectMetadataPluralName: 'messageParticipants', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - in: [MESSAGE_PARTICIPANT_1_ID, MESSAGE_PARTICIPANT_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyMessageParticipants).toHaveLength(2); - }); - - it('8b. should destroy one messageParticipant', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - recordId: MESSAGE_PARTICIPANT_3_ID, - }); - - const destroyMessageParticipantResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - destroyMessageParticipantResponse.body.data.destroyMessageParticipant, - ).toBeTruthy(); - }); - - it('9. should not find many messageParticipants anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - objectMetadataPluralName: 'messageParticipants', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - in: [MESSAGE_PARTICIPANT_1_ID, MESSAGE_PARTICIPANT_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageParticipants.edges).toHaveLength(0); - }); - - it('9b. should not find one messageParticipant anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageParticipant', - gqlFields: MESSAGE_PARTICIPANT_GQL_FIELDS, - filter: { - id: { - eq: MESSAGE_PARTICIPANT_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageParticipant).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-message-threads-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-message-threads-resolvers.integration-spec.ts deleted file mode 100644 index a2d905cd9..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-message-threads-resolvers.integration-spec.ts +++ /dev/null @@ -1,397 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; - -const MESSAGE_THREAD_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const MESSAGE_THREAD_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const MESSAGE_THREAD_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; -const UPDATED_AT_1 = new Date('10/10/2024'); -const UPDATED_AT_2 = new Date('10/20/2024'); - -const MESSAGE_THREAD_GQL_FIELDS = ` - id - updatedAt - createdAt - deletedAt - messages{ - edges{ - node{ - id - } - } - } -`; - -describe('messageThreads resolvers (integration)', () => { - it('1. should create and return messageThreads', async () => { - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'messageThread', - objectMetadataPluralName: 'messageThreads', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - data: [ - { - id: MESSAGE_THREAD_1_ID, - }, - { - id: MESSAGE_THREAD_2_ID, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createMessageThreads).toHaveLength(2); - - response.body.data.createMessageThreads.forEach((messageThread) => { - expect(messageThread).toHaveProperty('id'); - expect(messageThread).toHaveProperty('updatedAt'); - expect(messageThread).toHaveProperty('createdAt'); - expect(messageThread).toHaveProperty('deletedAt'); - expect(messageThread).toHaveProperty('messages'); - }); - }); - - it('1b. should create and return one messageThread', async () => { - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'messageThread', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - data: { - id: MESSAGE_THREAD_3_ID, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdMessageThread = response.body.data.createMessageThread; - - expect(createdMessageThread).toHaveProperty('id'); - expect(createdMessageThread).toHaveProperty('updatedAt'); - expect(createdMessageThread).toHaveProperty('createdAt'); - expect(createdMessageThread).toHaveProperty('deletedAt'); - expect(createdMessageThread).toHaveProperty('messages'); - }); - - it('2. should find many messageThreads', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageThread', - objectMetadataPluralName: 'messageThreads', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.messageThreads; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - const edges = data.edges; - - if (edges.length > 0) { - const messageThread = edges[0].node; - - expect(messageThread).toHaveProperty('id'); - expect(messageThread).toHaveProperty('updatedAt'); - expect(messageThread).toHaveProperty('createdAt'); - expect(messageThread).toHaveProperty('deletedAt'); - expect(messageThread).toHaveProperty('messages'); - } - }); - - it('2b. should find one messageThread', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageThread', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - filter: { - id: { - eq: MESSAGE_THREAD_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const messageThread = response.body.data.messageThread; - - expect(messageThread).toHaveProperty('id'); - expect(messageThread).toHaveProperty('updatedAt'); - expect(messageThread).toHaveProperty('createdAt'); - expect(messageThread).toHaveProperty('deletedAt'); - expect(messageThread).toHaveProperty('messages'); - }); - - it('3. should update many messageThreads', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'messageThread', - objectMetadataPluralName: 'messageThreads', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - data: { - updatedAt: UPDATED_AT_1, - }, - filter: { - id: { - in: [MESSAGE_THREAD_1_ID, MESSAGE_THREAD_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedMessageThreads = response.body.data.updateMessageThreads; - - expect(updatedMessageThreads).toHaveLength(2); - - updatedMessageThreads.forEach((messageThread) => { - expect(messageThread.updatedAt).toEqual(UPDATED_AT_1.toISOString()); - }); - }); - - it('3b. should update one messageThread', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'messageThread', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - data: { - updatedAt: UPDATED_AT_2, - }, - recordId: MESSAGE_THREAD_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedMessageThread = response.body.data.updateMessageThread; - - expect(updatedMessageThread.updatedAt).toEqual(UPDATED_AT_2.toISOString()); - }); - - it('4. should find many messageThreads with updated updatedAt', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageThread', - objectMetadataPluralName: 'messageThreads', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - filter: { - updatedAt: { - eq: UPDATED_AT_1, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageThreads.edges).toHaveLength(2); - }); - - it('4b. should find one messageThread with updated updatedAt', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageThread', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - filter: { - updatedAt: { - eq: UPDATED_AT_2, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageThread.updatedAt).toEqual( - UPDATED_AT_2.toISOString(), - ); - }); - - it('5. should delete many messageThreads', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'messageThread', - objectMetadataPluralName: 'messageThreads', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - filter: { - id: { - in: [MESSAGE_THREAD_1_ID, MESSAGE_THREAD_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deleteMessageThreads = response.body.data.deleteMessageThreads; - - expect(deleteMessageThreads).toHaveLength(2); - - deleteMessageThreads.forEach((messageThread) => { - expect(messageThread.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one messageThread', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'messageThread', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - recordId: MESSAGE_THREAD_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteMessageThread.deletedAt).toBeTruthy(); - }); - - it('6. should not find many messageThreads anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageThread', - objectMetadataPluralName: 'messageThreads', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - filter: { - id: { - in: [MESSAGE_THREAD_1_ID, MESSAGE_THREAD_2_ID], - }, - }, - }); - - const findMessageThreadsResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - findMessageThreadsResponse.body.data.messageThreads.edges, - ).toHaveLength(0); - }); - - it('6b. should not find one messageThread anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageThread', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - filter: { - id: { - eq: MESSAGE_THREAD_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageThread).toBeNull(); - }); - - it('7. should find many deleted messageThreads with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageThread', - objectMetadataPluralName: 'messageThreads', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - filter: { - id: { - in: [MESSAGE_THREAD_1_ID, MESSAGE_THREAD_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageThreads.edges).toHaveLength(2); - }); - - it('7b. should find one deleted messageThread with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageThread', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - filter: { - id: { - eq: MESSAGE_THREAD_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageThread.id).toEqual(MESSAGE_THREAD_3_ID); - }); - - it('8. should destroy many messageThreads', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'messageThread', - objectMetadataPluralName: 'messageThreads', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - filter: { - id: { - in: [MESSAGE_THREAD_1_ID, MESSAGE_THREAD_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyMessageThreads).toHaveLength(2); - }); - - it('8b. should destroy one messageThread', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'messageThread', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - recordId: MESSAGE_THREAD_3_ID, - }); - - const destroyMessageThreadsResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - destroyMessageThreadsResponse.body.data.destroyMessageThread, - ).toBeTruthy(); - }); - - it('9. should not find many messageThreads anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'messageThread', - objectMetadataPluralName: 'messageThreads', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - filter: { - id: { - in: [MESSAGE_THREAD_1_ID, MESSAGE_THREAD_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageThreads.edges).toHaveLength(0); - }); - - it('9b. should not find one messageThread anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'messageThread', - gqlFields: MESSAGE_THREAD_GQL_FIELDS, - filter: { - id: { - eq: MESSAGE_THREAD_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.messageThread).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-note-targets-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-note-targets-resolvers.integration-spec.ts deleted file mode 100644 index 27faccfe6..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-note-targets-resolvers.integration-spec.ts +++ /dev/null @@ -1,444 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const NOTE_TARGET_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const NOTE_TARGET_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const NOTE_TARGET_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; -const PERSON_1_ID = 'f875ff46-b881-41ba-973a-b9cd5345e8f0'; -const PERSON_2_ID = '1fe0f78c-8c59-4ce6-ae02-56571331b252'; -const NOTE_TARGET_GQL_FIELDS = ` - id - createdAt - deletedAt - noteId - personId - companyId - opportunityId - person{ - id - } -`; - -describe('noteTargets resolvers (integration)', () => { - beforeAll(async () => { - const personName1 = generateRecordName(PERSON_1_ID); - const personName2 = generateRecordName(PERSON_2_ID); - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'person', - objectMetadataPluralName: 'people', - gqlFields: `id`, - data: [ - { - id: PERSON_1_ID, - name: { - firstName: personName1, - lastName: personName1, - }, - }, - { - id: PERSON_2_ID, - name: { - firstName: personName2, - lastName: personName2, - }, - }, - ], - }); - - await makeGraphqlAPIRequest(graphqlOperation); - }); - - afterAll(async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'person', - objectMetadataPluralName: 'people', - gqlFields: `id`, - filter: { - id: { - in: [PERSON_1_ID, PERSON_2_ID], - }, - }, - }); - - await makeGraphqlAPIRequest(graphqlOperation); - }); - it('1. should create and return noteTargets', async () => { - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'noteTarget', - objectMetadataPluralName: 'noteTargets', - gqlFields: NOTE_TARGET_GQL_FIELDS, - data: [ - { - id: NOTE_TARGET_1_ID, - }, - { - id: NOTE_TARGET_2_ID, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createNoteTargets).toHaveLength(2); - - response.body.data.createNoteTargets.forEach((noteTarget) => { - expect(noteTarget).toHaveProperty('id'); - expect(noteTarget).toHaveProperty('createdAt'); - expect(noteTarget).toHaveProperty('deletedAt'); - expect(noteTarget).toHaveProperty('noteId'); - expect(noteTarget).toHaveProperty('personId'); - expect(noteTarget).toHaveProperty('companyId'); - expect(noteTarget).toHaveProperty('opportunityId'); - expect(noteTarget).toHaveProperty('person'); - }); - }); - - it('1b. should create and return one noteTarget', async () => { - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'noteTarget', - gqlFields: NOTE_TARGET_GQL_FIELDS, - data: { - id: NOTE_TARGET_3_ID, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdNoteTarget = response.body.data.createNoteTarget; - - expect(createdNoteTarget).toHaveProperty('id'); - expect(createdNoteTarget).toHaveProperty('createdAt'); - expect(createdNoteTarget).toHaveProperty('deletedAt'); - expect(createdNoteTarget).toHaveProperty('noteId'); - expect(createdNoteTarget).toHaveProperty('personId'); - expect(createdNoteTarget).toHaveProperty('companyId'); - expect(createdNoteTarget).toHaveProperty('opportunityId'); - expect(createdNoteTarget).toHaveProperty('person'); - }); - - it('2. should find many noteTargets', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'noteTarget', - objectMetadataPluralName: 'noteTargets', - gqlFields: NOTE_TARGET_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.noteTargets; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - const edges = data.edges; - - if (edges.length > 0) { - const noteTarget = edges[0].node; - - expect(noteTarget).toHaveProperty('id'); - expect(noteTarget).toHaveProperty('createdAt'); - expect(noteTarget).toHaveProperty('deletedAt'); - expect(noteTarget).toHaveProperty('noteId'); - expect(noteTarget).toHaveProperty('personId'); - expect(noteTarget).toHaveProperty('companyId'); - expect(noteTarget).toHaveProperty('opportunityId'); - expect(noteTarget).toHaveProperty('person'); - } - }); - - it('2b. should find one noteTarget', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'noteTarget', - gqlFields: NOTE_TARGET_GQL_FIELDS, - filter: { - id: { - eq: NOTE_TARGET_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const noteTarget = response.body.data.noteTarget; - - expect(noteTarget).toHaveProperty('id'); - expect(noteTarget).toHaveProperty('createdAt'); - expect(noteTarget).toHaveProperty('deletedAt'); - expect(noteTarget).toHaveProperty('noteId'); - expect(noteTarget).toHaveProperty('personId'); - expect(noteTarget).toHaveProperty('companyId'); - expect(noteTarget).toHaveProperty('opportunityId'); - expect(noteTarget).toHaveProperty('person'); - }); - - it('3. should update many noteTargets', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'noteTarget', - objectMetadataPluralName: 'noteTargets', - gqlFields: NOTE_TARGET_GQL_FIELDS, - data: { - personId: PERSON_1_ID, - }, - filter: { - id: { - in: [NOTE_TARGET_1_ID, NOTE_TARGET_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedNoteTargets = response.body.data.updateNoteTargets; - - expect(updatedNoteTargets).toHaveLength(2); - - updatedNoteTargets.forEach((noteTarget) => { - expect(noteTarget.person.id).toEqual(PERSON_1_ID); - }); - }); - - it('3b. should update one noteTarget', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'noteTarget', - gqlFields: NOTE_TARGET_GQL_FIELDS, - data: { - personId: PERSON_2_ID, - }, - recordId: NOTE_TARGET_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedNoteTarget = response.body.data.updateNoteTarget; - - expect(updatedNoteTarget.person.id).toEqual(PERSON_2_ID); - }); - - it('4. should find many noteTargets with updated personId', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'noteTarget', - objectMetadataPluralName: 'noteTargets', - gqlFields: NOTE_TARGET_GQL_FIELDS, - filter: { - personId: { - eq: PERSON_1_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.noteTargets.edges).toHaveLength(2); - }); - - it('4b. should find one noteTarget with updated personId', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'noteTarget', - gqlFields: NOTE_TARGET_GQL_FIELDS, - filter: { - personId: { - eq: PERSON_2_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.noteTarget.person.id).toEqual(PERSON_2_ID); - }); - - it('5. should delete many noteTargets', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'noteTarget', - objectMetadataPluralName: 'noteTargets', - gqlFields: NOTE_TARGET_GQL_FIELDS, - filter: { - id: { - in: [NOTE_TARGET_1_ID, NOTE_TARGET_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deleteNoteTargets = response.body.data.deleteNoteTargets; - - expect(deleteNoteTargets).toHaveLength(2); - - deleteNoteTargets.forEach((noteTarget) => { - expect(noteTarget.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one noteTarget', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'noteTarget', - gqlFields: NOTE_TARGET_GQL_FIELDS, - recordId: NOTE_TARGET_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteNoteTarget.deletedAt).toBeTruthy(); - }); - - it('6. should not find many noteTargets anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'noteTarget', - objectMetadataPluralName: 'noteTargets', - gqlFields: NOTE_TARGET_GQL_FIELDS, - filter: { - id: { - in: [NOTE_TARGET_1_ID, NOTE_TARGET_2_ID], - }, - }, - }); - - const findNoteTargetsResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(findNoteTargetsResponse.body.data.noteTargets.edges).toHaveLength(0); - }); - - it('6b. should not find one noteTarget anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'noteTarget', - gqlFields: NOTE_TARGET_GQL_FIELDS, - filter: { - id: { - eq: NOTE_TARGET_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.noteTarget).toBeNull(); - }); - - it('7. should find many deleted noteTargets with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'noteTarget', - objectMetadataPluralName: 'noteTargets', - gqlFields: NOTE_TARGET_GQL_FIELDS, - filter: { - id: { - in: [NOTE_TARGET_1_ID, NOTE_TARGET_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.noteTargets.edges).toHaveLength(2); - }); - - it('7b. should find one deleted noteTarget with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'noteTarget', - gqlFields: NOTE_TARGET_GQL_FIELDS, - filter: { - id: { - eq: NOTE_TARGET_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.noteTarget.id).toEqual(NOTE_TARGET_3_ID); - }); - - it('8. should destroy many noteTargets', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'noteTarget', - objectMetadataPluralName: 'noteTargets', - gqlFields: NOTE_TARGET_GQL_FIELDS, - filter: { - id: { - in: [NOTE_TARGET_1_ID, NOTE_TARGET_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyNoteTargets).toHaveLength(2); - }); - - it('8b. should destroy one noteTarget', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'noteTarget', - gqlFields: NOTE_TARGET_GQL_FIELDS, - recordId: NOTE_TARGET_3_ID, - }); - - const destroyNoteTargetsResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyNoteTargetsResponse.body.data.destroyNoteTarget).toBeTruthy(); - }); - - it('9. should not find many noteTargets anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'noteTarget', - objectMetadataPluralName: 'noteTargets', - gqlFields: NOTE_TARGET_GQL_FIELDS, - filter: { - id: { - in: [NOTE_TARGET_1_ID, NOTE_TARGET_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.noteTargets.edges).toHaveLength(0); - }); - - it('9b. should not find one noteTarget anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'noteTarget', - gqlFields: NOTE_TARGET_GQL_FIELDS, - filter: { - id: { - eq: NOTE_TARGET_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.noteTarget).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-notes-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-notes-resolvers.integration-spec.ts deleted file mode 100644 index 29fbe7602..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-notes-resolvers.integration-spec.ts +++ /dev/null @@ -1,403 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const NOTE_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const NOTE_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const NOTE_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; - -const NOTE_GQL_FIELDS = ` - id - title - createdAt - updatedAt - deletedAt - body - position -`; - -describe('notes resolvers (integration)', () => { - it('1. should create and return notes', async () => { - const noteTitle1 = generateRecordName(NOTE_1_ID); - const noteTitle2 = generateRecordName(NOTE_2_ID); - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'note', - objectMetadataPluralName: 'notes', - gqlFields: NOTE_GQL_FIELDS, - data: [ - { - id: NOTE_1_ID, - title: noteTitle1, - }, - { - id: NOTE_2_ID, - title: noteTitle2, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createNotes).toHaveLength(2); - - response.body.data.createNotes.forEach((note) => { - expect(note).toHaveProperty('title'); - expect([noteTitle1, noteTitle2]).toContain(note.title); - - expect(note).toHaveProperty('id'); - expect(note).toHaveProperty('createdAt'); - expect(note).toHaveProperty('updatedAt'); - expect(note).toHaveProperty('deletedAt'); - expect(note).toHaveProperty('body'); - expect(note).toHaveProperty('position'); - }); - }); - - it('1b. should create and return one note', async () => { - const noteTitle3 = generateRecordName(NOTE_3_ID); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'note', - gqlFields: NOTE_GQL_FIELDS, - data: { - id: NOTE_3_ID, - title: noteTitle3, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdNote = response.body.data.createNote; - - expect(createdNote).toHaveProperty('title'); - expect(createdNote.title).toEqual(noteTitle3); - - expect(createdNote).toHaveProperty('id'); - expect(createdNote).toHaveProperty('createdAt'); - expect(createdNote).toHaveProperty('updatedAt'); - expect(createdNote).toHaveProperty('deletedAt'); - expect(createdNote).toHaveProperty('body'); - expect(createdNote).toHaveProperty('position'); - }); - - it('2. should find many notes', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'note', - objectMetadataPluralName: 'notes', - gqlFields: NOTE_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.notes; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - const edges = data.edges; - - if (edges.length > 0) { - const note = edges[0].node; - - expect(note).toHaveProperty('id'); - expect(note).toHaveProperty('createdAt'); - expect(note).toHaveProperty('updatedAt'); - expect(note).toHaveProperty('deletedAt'); - expect(note).toHaveProperty('body'); - expect(note).toHaveProperty('position'); - } - }); - - it('2b. should find one note', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'note', - gqlFields: NOTE_GQL_FIELDS, - filter: { - id: { - eq: NOTE_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const note = response.body.data.note; - - expect(note).toHaveProperty('title'); - - expect(note).toHaveProperty('id'); - expect(note).toHaveProperty('createdAt'); - expect(note).toHaveProperty('updatedAt'); - expect(note).toHaveProperty('deletedAt'); - expect(note).toHaveProperty('body'); - expect(note).toHaveProperty('position'); - }); - - it('3. should update many notes', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'note', - objectMetadataPluralName: 'notes', - gqlFields: NOTE_GQL_FIELDS, - data: { - title: 'Updated Title', - }, - filter: { - id: { - in: [NOTE_1_ID, NOTE_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedNotes = response.body.data.updateNotes; - - expect(updatedNotes).toHaveLength(2); - - updatedNotes.forEach((note) => { - expect(note.title).toEqual('Updated Title'); - }); - }); - - it('3b. should update one note', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'note', - gqlFields: NOTE_GQL_FIELDS, - data: { - title: 'New Title', - }, - recordId: NOTE_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedNote = response.body.data.updateNote; - - expect(updatedNote.title).toEqual('New Title'); - }); - - it('4. should find many notes with updated title', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'note', - objectMetadataPluralName: 'notes', - gqlFields: NOTE_GQL_FIELDS, - filter: { - title: { - eq: 'Updated Title', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.notes.edges).toHaveLength(2); - }); - - it('4b. should find one note with updated title', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'note', - gqlFields: NOTE_GQL_FIELDS, - filter: { - title: { - eq: 'New Title', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.note.title).toEqual('New Title'); - }); - - it('5. should delete many notes', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'note', - objectMetadataPluralName: 'notes', - gqlFields: NOTE_GQL_FIELDS, - filter: { - id: { - in: [NOTE_1_ID, NOTE_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deleteNotes = response.body.data.deleteNotes; - - expect(deleteNotes).toHaveLength(2); - - deleteNotes.forEach((note) => { - expect(note.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one note', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'note', - gqlFields: NOTE_GQL_FIELDS, - recordId: NOTE_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteNote.deletedAt).toBeTruthy(); - }); - - it('6. should not find many notes anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'note', - objectMetadataPluralName: 'notes', - gqlFields: NOTE_GQL_FIELDS, - filter: { - id: { - in: [NOTE_1_ID, NOTE_2_ID], - }, - }, - }); - - const findNotesResponse = await makeGraphqlAPIRequest(graphqlOperation); - - expect(findNotesResponse.body.data.notes.edges).toHaveLength(0); - }); - - it('6b. should not find one note anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'note', - gqlFields: NOTE_GQL_FIELDS, - filter: { - id: { - eq: NOTE_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.note).toBeNull(); - }); - - it('7. should find many deleted notes with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'note', - objectMetadataPluralName: 'notes', - gqlFields: NOTE_GQL_FIELDS, - filter: { - id: { - in: [NOTE_1_ID, NOTE_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.notes.edges).toHaveLength(2); - }); - - it('7b. should find one deleted note with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'note', - gqlFields: NOTE_GQL_FIELDS, - filter: { - id: { - eq: NOTE_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.note.id).toEqual(NOTE_3_ID); - }); - - it('8. should destroy many notes', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'note', - objectMetadataPluralName: 'notes', - gqlFields: NOTE_GQL_FIELDS, - filter: { - id: { - in: [NOTE_1_ID, NOTE_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyNotes).toHaveLength(2); - }); - - it('8b. should destroy one note', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'note', - gqlFields: NOTE_GQL_FIELDS, - recordId: NOTE_3_ID, - }); - - const destroyNotesResponse = await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyNotesResponse.body.data.destroyNote).toBeTruthy(); - }); - - it('9. should not find many notes anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'note', - objectMetadataPluralName: 'notes', - gqlFields: NOTE_GQL_FIELDS, - filter: { - id: { - in: [NOTE_1_ID, NOTE_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.notes.edges).toHaveLength(0); - }); - - it('9b. should not find one note anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'note', - gqlFields: NOTE_GQL_FIELDS, - filter: { - id: { - eq: NOTE_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.note).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-opportunities-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-opportunities-resolvers.integration-spec.ts deleted file mode 100644 index 6c1a279be..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-opportunities-resolvers.integration-spec.ts +++ /dev/null @@ -1,414 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const OPPORTUNITY_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const OPPORTUNITY_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const OPPORTUNITY_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; - -const OPPORTUNITY_GQL_FIELDS = ` - id - name - createdAt - updatedAt - deletedAt - searchVector - stage - position -`; - -describe('opportunities resolvers (integration)', () => { - it('1. should create and return opportunities', async () => { - const opportunityName1 = generateRecordName(OPPORTUNITY_1_ID); - const opportunityName2 = generateRecordName(OPPORTUNITY_2_ID); - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'opportunity', - objectMetadataPluralName: 'opportunities', - gqlFields: OPPORTUNITY_GQL_FIELDS, - data: [ - { - id: OPPORTUNITY_1_ID, - name: opportunityName1, - }, - { - id: OPPORTUNITY_2_ID, - name: opportunityName2, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createOpportunities).toHaveLength(2); - - response.body.data.createOpportunities.forEach((opportunity) => { - expect(opportunity).toHaveProperty('name'); - expect([opportunityName1, opportunityName2]).toContain(opportunity.name); - - expect(opportunity).toHaveProperty('id'); - expect(opportunity).toHaveProperty('createdAt'); - expect(opportunity).toHaveProperty('updatedAt'); - expect(opportunity).toHaveProperty('deletedAt'); - expect(opportunity).toHaveProperty('searchVector'); - expect(opportunity).toHaveProperty('stage'); - expect(opportunity).toHaveProperty('position'); - }); - }); - - it('1b. should create and return one opportunity', async () => { - const opportunityName3 = generateRecordName(OPPORTUNITY_3_ID); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'opportunity', - gqlFields: OPPORTUNITY_GQL_FIELDS, - data: { - id: OPPORTUNITY_3_ID, - name: opportunityName3, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdOpportunity = response.body.data.createOpportunity; - - expect(createdOpportunity).toHaveProperty('name'); - expect(createdOpportunity.name).toEqual(opportunityName3); - - expect(createdOpportunity).toHaveProperty('id'); - expect(createdOpportunity).toHaveProperty('createdAt'); - expect(createdOpportunity).toHaveProperty('updatedAt'); - expect(createdOpportunity).toHaveProperty('deletedAt'); - expect(createdOpportunity).toHaveProperty('searchVector'); - expect(createdOpportunity).toHaveProperty('stage'); - expect(createdOpportunity).toHaveProperty('position'); - }); - - it('2. should find many opportunities', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'opportunity', - objectMetadataPluralName: 'opportunities', - gqlFields: OPPORTUNITY_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.opportunities; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - const edges = data.edges; - - if (edges.length > 0) { - const opportunity = edges[0].node; - - expect(opportunity).toHaveProperty('id'); - expect(opportunity).toHaveProperty('createdAt'); - expect(opportunity).toHaveProperty('updatedAt'); - expect(opportunity).toHaveProperty('deletedAt'); - expect(opportunity).toHaveProperty('searchVector'); - expect(opportunity).toHaveProperty('stage'); - expect(opportunity).toHaveProperty('position'); - } - }); - - it('2b. should find one opportunity', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'opportunity', - gqlFields: OPPORTUNITY_GQL_FIELDS, - filter: { - id: { - eq: OPPORTUNITY_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const opportunity = response.body.data.opportunity; - - expect(opportunity).toHaveProperty('name'); - - expect(opportunity).toHaveProperty('id'); - expect(opportunity).toHaveProperty('createdAt'); - expect(opportunity).toHaveProperty('updatedAt'); - expect(opportunity).toHaveProperty('deletedAt'); - expect(opportunity).toHaveProperty('searchVector'); - expect(opportunity).toHaveProperty('stage'); - expect(opportunity).toHaveProperty('position'); - }); - - it('3. should update many opportunities', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'opportunity', - objectMetadataPluralName: 'opportunities', - gqlFields: OPPORTUNITY_GQL_FIELDS, - data: { - name: 'Updated Name', - }, - filter: { - id: { - in: [OPPORTUNITY_1_ID, OPPORTUNITY_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedOpportunities = response.body.data.updateOpportunities; - - expect(updatedOpportunities).toHaveLength(2); - - updatedOpportunities.forEach((opportunity) => { - expect(opportunity.name).toEqual('Updated Name'); - }); - }); - - it('3b. should update one opportunity', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'opportunity', - gqlFields: OPPORTUNITY_GQL_FIELDS, - data: { - name: 'New Name', - }, - recordId: OPPORTUNITY_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedOpportunity = response.body.data.updateOpportunity; - - expect(updatedOpportunity.name).toEqual('New Name'); - }); - - it('4. should find many opportunities with updated name', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'opportunity', - objectMetadataPluralName: 'opportunities', - gqlFields: OPPORTUNITY_GQL_FIELDS, - filter: { - name: { - eq: 'Updated Name', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.opportunities.edges).toHaveLength(2); - }); - - it('4b. should find one opportunity with updated name', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'opportunity', - gqlFields: OPPORTUNITY_GQL_FIELDS, - filter: { - name: { - eq: 'New Name', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.opportunity.name).toEqual('New Name'); - }); - - it('5. should delete many opportunities', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'opportunity', - objectMetadataPluralName: 'opportunities', - gqlFields: OPPORTUNITY_GQL_FIELDS, - filter: { - id: { - in: [OPPORTUNITY_1_ID, OPPORTUNITY_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deleteOpportunities = response.body.data.deleteOpportunities; - - expect(deleteOpportunities).toHaveLength(2); - - deleteOpportunities.forEach((opportunity) => { - expect(opportunity.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one opportunity', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'opportunity', - gqlFields: OPPORTUNITY_GQL_FIELDS, - recordId: OPPORTUNITY_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteOpportunity.deletedAt).toBeTruthy(); - }); - - it('6. should not find many opportunities anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'opportunity', - objectMetadataPluralName: 'opportunities', - gqlFields: OPPORTUNITY_GQL_FIELDS, - filter: { - id: { - in: [OPPORTUNITY_1_ID, OPPORTUNITY_2_ID], - }, - }, - }); - - const findOpportunitiesResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - findOpportunitiesResponse.body.data.opportunities.edges, - ).toHaveLength(0); - }); - - it('6b. should not find one opportunity anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'opportunity', - gqlFields: OPPORTUNITY_GQL_FIELDS, - filter: { - id: { - eq: OPPORTUNITY_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.opportunity).toBeNull(); - }); - - it('7. should find many deleted opportunities with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'opportunity', - objectMetadataPluralName: 'opportunities', - gqlFields: OPPORTUNITY_GQL_FIELDS, - filter: { - id: { - in: [OPPORTUNITY_1_ID, OPPORTUNITY_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.opportunities.edges).toHaveLength(2); - }); - - it('7b. should find one deleted opportunity with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'opportunity', - gqlFields: OPPORTUNITY_GQL_FIELDS, - filter: { - id: { - eq: OPPORTUNITY_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.opportunity.id).toEqual(OPPORTUNITY_3_ID); - }); - - it('8. should destroy many opportunities', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'opportunity', - objectMetadataPluralName: 'opportunities', - gqlFields: OPPORTUNITY_GQL_FIELDS, - filter: { - id: { - in: [OPPORTUNITY_1_ID, OPPORTUNITY_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyOpportunities).toHaveLength(2); - }); - - it('8b. should destroy one opportunity', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'opportunity', - gqlFields: OPPORTUNITY_GQL_FIELDS, - recordId: OPPORTUNITY_3_ID, - }); - - const destroyOpportunitiesResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - destroyOpportunitiesResponse.body.data.destroyOpportunity, - ).toBeTruthy(); - }); - - it('9. should not find many opportunities anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'opportunity', - objectMetadataPluralName: 'opportunities', - gqlFields: OPPORTUNITY_GQL_FIELDS, - filter: { - id: { - in: [OPPORTUNITY_1_ID, OPPORTUNITY_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.opportunities.edges).toHaveLength(0); - }); - - it('9b. should not find one opportunity anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'opportunity', - gqlFields: OPPORTUNITY_GQL_FIELDS, - filter: { - id: { - eq: OPPORTUNITY_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.opportunity).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-resolvers.integration-spec.ts deleted file mode 100644 index 3b1a46f89..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-resolvers.integration-spec.ts +++ /dev/null @@ -1,430 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const COMPANY_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const COMPANY_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const COMPANY_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; -const COMPANY_GQL_FIELDS = ` - id - name - employees - idealCustomerProfile - position - createdAt - updatedAt - deletedAt - accountOwnerId - tagline - workPolicy - visaSponsorship -`; - -describe('companies resolvers (integration)', () => { - it('1. should create and return companies', async () => { - const companyName1 = generateRecordName(COMPANY_1_ID); - const companyName2 = generateRecordName(COMPANY_2_ID); - - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'company', - objectMetadataPluralName: 'companies', - gqlFields: COMPANY_GQL_FIELDS, - data: [ - { - id: COMPANY_1_ID, - name: companyName1, - }, - { - id: COMPANY_2_ID, - name: companyName2, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createCompanies).toHaveLength(2); - - response.body.data.createCompanies.forEach((company) => { - expect(company).toHaveProperty('name'); - expect([companyName1, companyName2]).toContain(company.name); - - expect(company).toHaveProperty('employees'); - expect(company).toHaveProperty('idealCustomerProfile'); - expect(company).toHaveProperty('position'); - expect(company).toHaveProperty('id'); - expect(company).toHaveProperty('createdAt'); - expect(company).toHaveProperty('updatedAt'); - expect(company).toHaveProperty('deletedAt'); - expect(company).toHaveProperty('accountOwnerId'); - expect(company).toHaveProperty('tagline'); - expect(company).toHaveProperty('workPolicy'); - expect(company).toHaveProperty('visaSponsorship'); - }); - }); - - it('1b. should create and return one company', async () => { - const companyName = generateRecordName(COMPANY_3_ID); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'company', - gqlFields: COMPANY_GQL_FIELDS, - data: { - id: COMPANY_3_ID, - name: companyName, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdCompany = response.body.data.createCompany; - - expect(createdCompany).toHaveProperty('name'); - expect(createdCompany.name).toEqual(companyName); - - expect(createdCompany).toHaveProperty('employees'); - expect(createdCompany).toHaveProperty('idealCustomerProfile'); - expect(createdCompany).toHaveProperty('position'); - expect(createdCompany).toHaveProperty('id'); - expect(createdCompany).toHaveProperty('createdAt'); - expect(createdCompany).toHaveProperty('updatedAt'); - expect(createdCompany).toHaveProperty('deletedAt'); - expect(createdCompany).toHaveProperty('accountOwnerId'); - expect(createdCompany).toHaveProperty('tagline'); - expect(createdCompany).toHaveProperty('workPolicy'); - expect(createdCompany).toHaveProperty('visaSponsorship'); - }); - - it('2. should find many companies', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'company', - objectMetadataPluralName: 'companies', - gqlFields: COMPANY_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.companies; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - const edges = data.edges; - - if (edges.length > 0) { - const companies = edges[0].node; - - expect(companies).toHaveProperty('name'); - expect(companies).toHaveProperty('employees'); - expect(companies).toHaveProperty('idealCustomerProfile'); - expect(companies).toHaveProperty('position'); - expect(companies).toHaveProperty('id'); - expect(companies).toHaveProperty('createdAt'); - expect(companies).toHaveProperty('updatedAt'); - expect(companies).toHaveProperty('deletedAt'); - expect(companies).toHaveProperty('accountOwnerId'); - expect(companies).toHaveProperty('tagline'); - expect(companies).toHaveProperty('workPolicy'); - expect(companies).toHaveProperty('visaSponsorship'); - } - }); - - it('2b. should find one company', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'company', - gqlFields: COMPANY_GQL_FIELDS, - filter: { - id: { - eq: COMPANY_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const company = response.body.data.company; - - expect(company).toHaveProperty('name'); - - expect(company).toHaveProperty('employees'); - expect(company).toHaveProperty('idealCustomerProfile'); - expect(company).toHaveProperty('position'); - expect(company).toHaveProperty('id'); - expect(company).toHaveProperty('createdAt'); - expect(company).toHaveProperty('updatedAt'); - expect(company).toHaveProperty('deletedAt'); - expect(company).toHaveProperty('accountOwnerId'); - expect(company).toHaveProperty('tagline'); - expect(company).toHaveProperty('workPolicy'); - expect(company).toHaveProperty('visaSponsorship'); - }); - - it('3. should update many companies', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'company', - objectMetadataPluralName: 'companies', - gqlFields: COMPANY_GQL_FIELDS, - data: { - employees: 123, - }, - filter: { - id: { - in: [COMPANY_1_ID, COMPANY_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedCompanies = response.body.data.updateCompanies; - - expect(updatedCompanies).toHaveLength(2); - - updatedCompanies.forEach((company) => { - expect(company.employees).toEqual(123); - }); - }); - - it('3b. should update one company', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'company', - gqlFields: COMPANY_GQL_FIELDS, - data: { - employees: 122, - }, - recordId: COMPANY_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedCompany = response.body.data.updateCompany; - - expect(updatedCompany.employees).toEqual(122); - }); - - it('4. should find many companies with updated employees', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'company', - objectMetadataPluralName: 'companies', - gqlFields: COMPANY_GQL_FIELDS, - filter: { - employees: { - eq: 123, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.companies.edges).toHaveLength(2); - }); - - it('4b. should find one company with updated employees', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'company', - gqlFields: COMPANY_GQL_FIELDS, - filter: { - employees: { - eq: 122, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.company.employees).toEqual(122); - }); - - it('5. should delete many companies', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'company', - objectMetadataPluralName: 'companies', - gqlFields: COMPANY_GQL_FIELDS, - filter: { - id: { - in: [COMPANY_1_ID, COMPANY_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deleteCompanies = response.body.data.deleteCompanies; - - expect(deleteCompanies).toHaveLength(2); - - deleteCompanies.forEach((company) => { - expect(company.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one company', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'company', - gqlFields: COMPANY_GQL_FIELDS, - recordId: COMPANY_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteCompany.deletedAt).toBeTruthy(); - }); - - it('6. should not find many companies anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'company', - objectMetadataPluralName: 'companies', - gqlFields: COMPANY_GQL_FIELDS, - filter: { - id: { - in: [COMPANY_1_ID, COMPANY_2_ID], - }, - }, - }); - - const findCompaniesResponse = await makeGraphqlAPIRequest(graphqlOperation); - - expect(findCompaniesResponse.body.data.companies.edges).toHaveLength(0); - }); - - it('6b. should not find one company anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'company', - gqlFields: COMPANY_GQL_FIELDS, - filter: { - id: { - eq: COMPANY_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.company).toBeNull(); - }); - - it('7. should find many deleted companies with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'company', - objectMetadataPluralName: 'companies', - gqlFields: COMPANY_GQL_FIELDS, - filter: { - id: { - in: [COMPANY_1_ID, COMPANY_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.companies.edges).toHaveLength(2); - }); - - it('7b. should find one deleted company with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'company', - gqlFields: COMPANY_GQL_FIELDS, - filter: { - id: { - eq: COMPANY_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.company.id).toEqual(COMPANY_3_ID); - }); - - it('8. should destroy many companies', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'company', - objectMetadataPluralName: 'companies', - gqlFields: COMPANY_GQL_FIELDS, - filter: { - id: { - in: [COMPANY_1_ID, COMPANY_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyCompanies).toHaveLength(2); - }); - - it('8b. should destroy one company', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'company', - gqlFields: COMPANY_GQL_FIELDS, - recordId: COMPANY_3_ID, - }); - - const destroyCompanyResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyCompanyResponse.body.data.destroyCompany).toBeTruthy(); - }); - - it('9. should not find many companies anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'company', - objectMetadataPluralName: 'companies', - gqlFields: COMPANY_GQL_FIELDS, - filter: { - id: { - in: [COMPANY_1_ID, COMPANY_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.companies.edges).toHaveLength(0); - }); - - it('9b. should not find one company anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'company', - gqlFields: COMPANY_GQL_FIELDS, - filter: { - id: { - eq: COMPANY_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.company).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-task-targets-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-task-targets-resolvers.integration-spec.ts deleted file mode 100644 index b50f16fc6..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-task-targets-resolvers.integration-spec.ts +++ /dev/null @@ -1,444 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const TASK_TARGET_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const TASK_TARGET_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const TASK_TARGET_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; -const PERSON_1_ID = 'f875ff46-b881-41ba-973a-b9cd5345e8f0'; -const PERSON_2_ID = '1fe0f78c-8c59-4ce6-ae02-56571331b252'; -const TASK_TARGET_GQL_FIELDS = ` - id - createdAt - deletedAt - rocketId - personId - companyId - opportunityId - person{ - id - } -`; - -describe('taskTargets resolvers (integration)', () => { - beforeAll(async () => { - const personName1 = generateRecordName(PERSON_1_ID); - const personName2 = generateRecordName(PERSON_2_ID); - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'person', - objectMetadataPluralName: 'people', - gqlFields: `id`, - data: [ - { - id: PERSON_1_ID, - name: { - firstName: personName1, - lastName: personName1, - }, - }, - { - id: PERSON_2_ID, - name: { - firstName: personName2, - lastName: personName2, - }, - }, - ], - }); - - await makeGraphqlAPIRequest(graphqlOperation); - }); - - afterAll(async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'person', - objectMetadataPluralName: 'people', - gqlFields: `id`, - filter: { - id: { - in: [PERSON_1_ID, PERSON_2_ID], - }, - }, - }); - - await makeGraphqlAPIRequest(graphqlOperation); - }); - it('1. should create and return taskTargets', async () => { - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'taskTarget', - objectMetadataPluralName: 'taskTargets', - gqlFields: TASK_TARGET_GQL_FIELDS, - data: [ - { - id: TASK_TARGET_1_ID, - }, - { - id: TASK_TARGET_2_ID, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createTaskTargets).toHaveLength(2); - - response.body.data.createTaskTargets.forEach((taskTarget) => { - expect(taskTarget).toHaveProperty('id'); - expect(taskTarget).toHaveProperty('createdAt'); - expect(taskTarget).toHaveProperty('deletedAt'); - expect(taskTarget).toHaveProperty('rocketId'); - expect(taskTarget).toHaveProperty('personId'); - expect(taskTarget).toHaveProperty('companyId'); - expect(taskTarget).toHaveProperty('opportunityId'); - expect(taskTarget).toHaveProperty('person'); - }); - }); - - it('1b. should create and return one taskTarget', async () => { - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'taskTarget', - gqlFields: TASK_TARGET_GQL_FIELDS, - data: { - id: TASK_TARGET_3_ID, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdTaskTarget = response.body.data.createTaskTarget; - - expect(createdTaskTarget).toHaveProperty('id'); - expect(createdTaskTarget).toHaveProperty('createdAt'); - expect(createdTaskTarget).toHaveProperty('deletedAt'); - expect(createdTaskTarget).toHaveProperty('rocketId'); - expect(createdTaskTarget).toHaveProperty('personId'); - expect(createdTaskTarget).toHaveProperty('companyId'); - expect(createdTaskTarget).toHaveProperty('opportunityId'); - expect(createdTaskTarget).toHaveProperty('person'); - }); - - it('2. should find many taskTargets', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'taskTarget', - objectMetadataPluralName: 'taskTargets', - gqlFields: TASK_TARGET_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.taskTargets; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - const edges = data.edges; - - if (edges.length > 0) { - const taskTarget = edges[0].node; - - expect(taskTarget).toHaveProperty('id'); - expect(taskTarget).toHaveProperty('createdAt'); - expect(taskTarget).toHaveProperty('deletedAt'); - expect(taskTarget).toHaveProperty('rocketId'); - expect(taskTarget).toHaveProperty('personId'); - expect(taskTarget).toHaveProperty('companyId'); - expect(taskTarget).toHaveProperty('opportunityId'); - expect(taskTarget).toHaveProperty('person'); - } - }); - - it('2b. should find one taskTarget', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'taskTarget', - gqlFields: TASK_TARGET_GQL_FIELDS, - filter: { - id: { - eq: TASK_TARGET_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const taskTarget = response.body.data.taskTarget; - - expect(taskTarget).toHaveProperty('id'); - expect(taskTarget).toHaveProperty('createdAt'); - expect(taskTarget).toHaveProperty('deletedAt'); - expect(taskTarget).toHaveProperty('rocketId'); - expect(taskTarget).toHaveProperty('personId'); - expect(taskTarget).toHaveProperty('companyId'); - expect(taskTarget).toHaveProperty('opportunityId'); - expect(taskTarget).toHaveProperty('person'); - }); - - it('3. should update many taskTargets', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'taskTarget', - objectMetadataPluralName: 'taskTargets', - gqlFields: TASK_TARGET_GQL_FIELDS, - data: { - personId: PERSON_1_ID, - }, - filter: { - id: { - in: [TASK_TARGET_1_ID, TASK_TARGET_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedTaskTargets = response.body.data.updateTaskTargets; - - expect(updatedTaskTargets).toHaveLength(2); - - updatedTaskTargets.forEach((taskTarget) => { - expect(taskTarget.person.id).toEqual(PERSON_1_ID); - }); - }); - - it('3b. should update one taskTarget', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'taskTarget', - gqlFields: TASK_TARGET_GQL_FIELDS, - data: { - personId: PERSON_2_ID, - }, - recordId: TASK_TARGET_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedTaskTarget = response.body.data.updateTaskTarget; - - expect(updatedTaskTarget.person.id).toEqual(PERSON_2_ID); - }); - - it('4. should find many taskTargets with updated personId', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'taskTarget', - objectMetadataPluralName: 'taskTargets', - gqlFields: TASK_TARGET_GQL_FIELDS, - filter: { - personId: { - eq: PERSON_1_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.taskTargets.edges).toHaveLength(2); - }); - - it('4b. should find one taskTarget with updated personId', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'taskTarget', - gqlFields: TASK_TARGET_GQL_FIELDS, - filter: { - personId: { - eq: PERSON_2_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.taskTarget.person.id).toEqual(PERSON_2_ID); - }); - - it('5. should delete many taskTargets', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'taskTarget', - objectMetadataPluralName: 'taskTargets', - gqlFields: TASK_TARGET_GQL_FIELDS, - filter: { - id: { - in: [TASK_TARGET_1_ID, TASK_TARGET_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deleteTaskTargets = response.body.data.deleteTaskTargets; - - expect(deleteTaskTargets).toHaveLength(2); - - deleteTaskTargets.forEach((taskTarget) => { - expect(taskTarget.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one taskTarget', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'taskTarget', - gqlFields: TASK_TARGET_GQL_FIELDS, - recordId: TASK_TARGET_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteTaskTarget.deletedAt).toBeTruthy(); - }); - - it('6. should not find many taskTargets anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'taskTarget', - objectMetadataPluralName: 'taskTargets', - gqlFields: TASK_TARGET_GQL_FIELDS, - filter: { - id: { - in: [TASK_TARGET_1_ID, TASK_TARGET_2_ID], - }, - }, - }); - - const findTaskTargetsResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(findTaskTargetsResponse.body.data.taskTargets.edges).toHaveLength(0); - }); - - it('6b. should not find one taskTarget anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'taskTarget', - gqlFields: TASK_TARGET_GQL_FIELDS, - filter: { - id: { - eq: TASK_TARGET_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.taskTarget).toBeNull(); - }); - - it('7. should find many deleted taskTargets with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'taskTarget', - objectMetadataPluralName: 'taskTargets', - gqlFields: TASK_TARGET_GQL_FIELDS, - filter: { - id: { - in: [TASK_TARGET_1_ID, TASK_TARGET_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.taskTargets.edges).toHaveLength(2); - }); - - it('7b. should find one deleted taskTarget with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'taskTarget', - gqlFields: TASK_TARGET_GQL_FIELDS, - filter: { - id: { - eq: TASK_TARGET_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.taskTarget.id).toEqual(TASK_TARGET_3_ID); - }); - - it('8. should destroy many taskTargets', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'taskTarget', - objectMetadataPluralName: 'taskTargets', - gqlFields: TASK_TARGET_GQL_FIELDS, - filter: { - id: { - in: [TASK_TARGET_1_ID, TASK_TARGET_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyTaskTargets).toHaveLength(2); - }); - - it('8b. should destroy one taskTarget', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'taskTarget', - gqlFields: TASK_TARGET_GQL_FIELDS, - recordId: TASK_TARGET_3_ID, - }); - - const destroyTaskTargetsResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyTaskTargetsResponse.body.data.destroyTaskTarget).toBeTruthy(); - }); - - it('9. should not find many taskTargets anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'taskTarget', - objectMetadataPluralName: 'taskTargets', - gqlFields: TASK_TARGET_GQL_FIELDS, - filter: { - id: { - in: [TASK_TARGET_1_ID, TASK_TARGET_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.taskTargets.edges).toHaveLength(0); - }); - - it('9b. should not find one taskTarget anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'taskTarget', - gqlFields: TASK_TARGET_GQL_FIELDS, - filter: { - id: { - eq: TASK_TARGET_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.taskTarget).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-tasks-resolvers.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-tasks-resolvers.integration-spec.ts deleted file mode 100644 index f2c5261e9..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-tasks-resolvers.integration-spec.ts +++ /dev/null @@ -1,403 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const TASK_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const TASK_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const TASK_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; - -const TASK_GQL_FIELDS = ` - id - title - createdAt - updatedAt - deletedAt - body - position -`; - -describe('tasks resolvers (integration)', () => { - it('1. should create and return tasks', async () => { - const taskTitle1 = generateRecordName(TASK_1_ID); - const taskTitle2 = generateRecordName(TASK_2_ID); - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'task', - objectMetadataPluralName: 'tasks', - gqlFields: TASK_GQL_FIELDS, - data: [ - { - id: TASK_1_ID, - title: taskTitle1, - }, - { - id: TASK_2_ID, - title: taskTitle2, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createTasks).toHaveLength(2); - - response.body.data.createTasks.forEach((task) => { - expect(task).toHaveProperty('title'); - expect([taskTitle1, taskTitle2]).toContain(task.title); - - expect(task).toHaveProperty('id'); - expect(task).toHaveProperty('createdAt'); - expect(task).toHaveProperty('updatedAt'); - expect(task).toHaveProperty('deletedAt'); - expect(task).toHaveProperty('body'); - expect(task).toHaveProperty('position'); - }); - }); - - it('1b. should create and return one task', async () => { - const taskTitle3 = generateRecordName(TASK_3_ID); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'task', - gqlFields: TASK_GQL_FIELDS, - data: { - id: TASK_3_ID, - title: taskTitle3, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdTask = response.body.data.createTask; - - expect(createdTask).toHaveProperty('title'); - expect(createdTask.title).toEqual(taskTitle3); - - expect(createdTask).toHaveProperty('id'); - expect(createdTask).toHaveProperty('createdAt'); - expect(createdTask).toHaveProperty('updatedAt'); - expect(createdTask).toHaveProperty('deletedAt'); - expect(createdTask).toHaveProperty('body'); - expect(createdTask).toHaveProperty('position'); - }); - - it('2. should find many tasks', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'task', - objectMetadataPluralName: 'tasks', - gqlFields: TASK_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.tasks; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - const edges = data.edges; - - if (edges.length > 0) { - const task = edges[0].node; - - expect(task).toHaveProperty('id'); - expect(task).toHaveProperty('createdAt'); - expect(task).toHaveProperty('updatedAt'); - expect(task).toHaveProperty('deletedAt'); - expect(task).toHaveProperty('body'); - expect(task).toHaveProperty('position'); - } - }); - - it('2b. should find one task', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'task', - gqlFields: TASK_GQL_FIELDS, - filter: { - id: { - eq: TASK_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const task = response.body.data.task; - - expect(task).toHaveProperty('title'); - - expect(task).toHaveProperty('id'); - expect(task).toHaveProperty('createdAt'); - expect(task).toHaveProperty('updatedAt'); - expect(task).toHaveProperty('deletedAt'); - expect(task).toHaveProperty('body'); - expect(task).toHaveProperty('position'); - }); - - it('3. should update many tasks', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'task', - objectMetadataPluralName: 'tasks', - gqlFields: TASK_GQL_FIELDS, - data: { - title: 'Updated Title', - }, - filter: { - id: { - in: [TASK_1_ID, TASK_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedTasks = response.body.data.updateTasks; - - expect(updatedTasks).toHaveLength(2); - - updatedTasks.forEach((task) => { - expect(task.title).toEqual('Updated Title'); - }); - }); - - it('3b. should update one task', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'task', - gqlFields: TASK_GQL_FIELDS, - data: { - title: 'New Title', - }, - recordId: TASK_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedTask = response.body.data.updateTask; - - expect(updatedTask.title).toEqual('New Title'); - }); - - it('4. should find many tasks with updated title', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'task', - objectMetadataPluralName: 'tasks', - gqlFields: TASK_GQL_FIELDS, - filter: { - title: { - eq: 'Updated Title', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.tasks.edges).toHaveLength(2); - }); - - it('4b. should find one task with updated title', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'task', - gqlFields: TASK_GQL_FIELDS, - filter: { - title: { - eq: 'New Title', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.task.title).toEqual('New Title'); - }); - - it('5. should delete many tasks', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'task', - objectMetadataPluralName: 'tasks', - gqlFields: TASK_GQL_FIELDS, - filter: { - id: { - in: [TASK_1_ID, TASK_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deleteTasks = response.body.data.deleteTasks; - - expect(deleteTasks).toHaveLength(2); - - deleteTasks.forEach((task) => { - expect(task.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one task', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'task', - gqlFields: TASK_GQL_FIELDS, - recordId: TASK_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteTask.deletedAt).toBeTruthy(); - }); - - it('6. should not find many tasks anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'task', - objectMetadataPluralName: 'tasks', - gqlFields: TASK_GQL_FIELDS, - filter: { - id: { - in: [TASK_1_ID, TASK_2_ID], - }, - }, - }); - - const findTasksResponse = await makeGraphqlAPIRequest(graphqlOperation); - - expect(findTasksResponse.body.data.tasks.edges).toHaveLength(0); - }); - - it('6b. should not find one task anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'task', - gqlFields: TASK_GQL_FIELDS, - filter: { - id: { - eq: TASK_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.task).toBeNull(); - }); - - it('7. should find many deleted tasks with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'task', - objectMetadataPluralName: 'tasks', - gqlFields: TASK_GQL_FIELDS, - filter: { - id: { - in: [TASK_1_ID, TASK_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.tasks.edges).toHaveLength(2); - }); - - it('7b. should find one deleted task with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'task', - gqlFields: TASK_GQL_FIELDS, - filter: { - id: { - eq: TASK_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.task.id).toEqual(TASK_3_ID); - }); - - it('8. should destroy many tasks', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'task', - objectMetadataPluralName: 'tasks', - gqlFields: TASK_GQL_FIELDS, - filter: { - id: { - in: [TASK_1_ID, TASK_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyTasks).toHaveLength(2); - }); - - it('8b. should destroy one task', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'task', - gqlFields: TASK_GQL_FIELDS, - recordId: TASK_3_ID, - }); - - const destroyTasksResponse = await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyTasksResponse.body.data.destroyTask).toBeTruthy(); - }); - - it('9. should not find many tasks anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'task', - objectMetadataPluralName: 'tasks', - gqlFields: TASK_GQL_FIELDS, - filter: { - id: { - in: [TASK_1_ID, TASK_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.tasks.edges).toHaveLength(0); - }); - - it('9b. should not find one task anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'task', - gqlFields: TASK_GQL_FIELDS, - filter: { - id: { - eq: TASK_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.task).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-timeline-activities.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-timeline-activities.integration-spec.ts deleted file mode 100644 index 38a468ec7..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-timeline-activities.integration-spec.ts +++ /dev/null @@ -1,477 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const TIMELINE_ACTIVITY_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const TIMELINE_ACTIVITY_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const TIMELINE_ACTIVITY_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; - -const TIMELINE_ACTIVITY_GQL_FIELDS = ` - id - happensAt - name - properties - linkedRecordCachedName - linkedRecordId - linkedObjectMetadataId - createdAt - updatedAt - deletedAt - workspaceMemberId - personId - companyId - opportunityId - noteId - taskId - workflowId - workflowVersionId - workflowRunId - rocketId -`; - -describe('timelineActivities resolvers (integration)', () => { - it('1. should create and return timelineActivities', async () => { - const timelineActivityName1 = generateRecordName(TIMELINE_ACTIVITY_1_ID); - const timelineActivityName2 = generateRecordName(TIMELINE_ACTIVITY_2_ID); - - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - objectMetadataPluralName: 'timelineActivities', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - data: [ - { - id: TIMELINE_ACTIVITY_1_ID, - name: timelineActivityName1, - }, - { - id: TIMELINE_ACTIVITY_2_ID, - name: timelineActivityName2, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createTimelineActivities).toHaveLength(2); - - response.body.data.createTimelineActivities.forEach((timelineActivity) => { - expect(timelineActivity).toHaveProperty('name'); - expect([timelineActivityName1, timelineActivityName2]).toContain( - timelineActivity.name, - ); - expect(timelineActivity).toHaveProperty('id'); - expect(timelineActivity).toHaveProperty('happensAt'); - expect(timelineActivity).toHaveProperty('properties'); - expect(timelineActivity).toHaveProperty('linkedRecordCachedName'); - expect(timelineActivity).toHaveProperty('linkedRecordId'); - expect(timelineActivity).toHaveProperty('linkedObjectMetadataId'); - expect(timelineActivity).toHaveProperty('createdAt'); - expect(timelineActivity).toHaveProperty('updatedAt'); - expect(timelineActivity).toHaveProperty('deletedAt'); - expect(timelineActivity).toHaveProperty('workspaceMemberId'); - expect(timelineActivity).toHaveProperty('personId'); - expect(timelineActivity).toHaveProperty('companyId'); - expect(timelineActivity).toHaveProperty('opportunityId'); - expect(timelineActivity).toHaveProperty('noteId'); - expect(timelineActivity).toHaveProperty('taskId'); - expect(timelineActivity).toHaveProperty('workflowId'); - expect(timelineActivity).toHaveProperty('workflowVersionId'); - expect(timelineActivity).toHaveProperty('workflowRunId'); - expect(timelineActivity).toHaveProperty('rocketId'); - }); - }); - - it('1b. should create and return one timelineActivity', async () => { - const timelineActivityName = generateRecordName(TIMELINE_ACTIVITY_3_ID); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - data: { - id: TIMELINE_ACTIVITY_3_ID, - name: timelineActivityName, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdTimelineActivity = response.body.data.createTimelineActivity; - - expect(createdTimelineActivity).toHaveProperty('name'); - expect(createdTimelineActivity.name).toEqual(timelineActivityName); - expect(createdTimelineActivity).toHaveProperty('id'); - expect(createdTimelineActivity).toHaveProperty('happensAt'); - expect(createdTimelineActivity).toHaveProperty('properties'); - expect(createdTimelineActivity).toHaveProperty('linkedRecordCachedName'); - expect(createdTimelineActivity).toHaveProperty('linkedRecordId'); - expect(createdTimelineActivity).toHaveProperty('linkedObjectMetadataId'); - expect(createdTimelineActivity).toHaveProperty('createdAt'); - expect(createdTimelineActivity).toHaveProperty('updatedAt'); - expect(createdTimelineActivity).toHaveProperty('deletedAt'); - expect(createdTimelineActivity).toHaveProperty('workspaceMemberId'); - expect(createdTimelineActivity).toHaveProperty('personId'); - expect(createdTimelineActivity).toHaveProperty('companyId'); - expect(createdTimelineActivity).toHaveProperty('opportunityId'); - expect(createdTimelineActivity).toHaveProperty('noteId'); - expect(createdTimelineActivity).toHaveProperty('taskId'); - expect(createdTimelineActivity).toHaveProperty('workflowId'); - expect(createdTimelineActivity).toHaveProperty('workflowVersionId'); - expect(createdTimelineActivity).toHaveProperty('workflowRunId'); - expect(createdTimelineActivity).toHaveProperty('rocketId'); - }); - - it('2. should find many timelineActivities', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - objectMetadataPluralName: 'timelineActivities', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.timelineActivities; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - if (data.edges.length > 0) { - const timelineActivities = data.edges[0].node; - - expect(timelineActivities).toHaveProperty('happensAt'); - expect(timelineActivities).toHaveProperty('name'); - expect(timelineActivities).toHaveProperty('properties'); - expect(timelineActivities).toHaveProperty('linkedRecordCachedName'); - expect(timelineActivities).toHaveProperty('linkedRecordId'); - expect(timelineActivities).toHaveProperty('linkedObjectMetadataId'); - expect(timelineActivities).toHaveProperty('id'); - expect(timelineActivities).toHaveProperty('createdAt'); - expect(timelineActivities).toHaveProperty('updatedAt'); - expect(timelineActivities).toHaveProperty('deletedAt'); - expect(timelineActivities).toHaveProperty('workspaceMemberId'); - expect(timelineActivities).toHaveProperty('personId'); - expect(timelineActivities).toHaveProperty('companyId'); - expect(timelineActivities).toHaveProperty('opportunityId'); - expect(timelineActivities).toHaveProperty('noteId'); - expect(timelineActivities).toHaveProperty('taskId'); - expect(timelineActivities).toHaveProperty('workflowId'); - expect(timelineActivities).toHaveProperty('workflowVersionId'); - expect(timelineActivities).toHaveProperty('workflowRunId'); - expect(timelineActivities).toHaveProperty('rocketId'); - } - }); - - it('2b. should find one timelineActivity', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - filter: { - id: { - eq: TIMELINE_ACTIVITY_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const timelineActivity = response.body.data.timelineActivity; - - expect(timelineActivity).toHaveProperty('happensAt'); - expect(timelineActivity).toHaveProperty('name'); - expect(timelineActivity).toHaveProperty('properties'); - expect(timelineActivity).toHaveProperty('linkedRecordCachedName'); - expect(timelineActivity).toHaveProperty('linkedRecordId'); - expect(timelineActivity).toHaveProperty('linkedObjectMetadataId'); - expect(timelineActivity).toHaveProperty('id'); - expect(timelineActivity).toHaveProperty('createdAt'); - expect(timelineActivity).toHaveProperty('updatedAt'); - expect(timelineActivity).toHaveProperty('deletedAt'); - expect(timelineActivity).toHaveProperty('workspaceMemberId'); - expect(timelineActivity).toHaveProperty('personId'); - expect(timelineActivity).toHaveProperty('companyId'); - expect(timelineActivity).toHaveProperty('opportunityId'); - expect(timelineActivity).toHaveProperty('noteId'); - expect(timelineActivity).toHaveProperty('taskId'); - expect(timelineActivity).toHaveProperty('workflowId'); - expect(timelineActivity).toHaveProperty('workflowVersionId'); - expect(timelineActivity).toHaveProperty('workflowRunId'); - expect(timelineActivity).toHaveProperty('rocketId'); - }); - - it('3. should update many timelineActivities', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - objectMetadataPluralName: 'timelineActivities', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - data: { - name: 'Updated Name', - }, - filter: { - id: { - in: [TIMELINE_ACTIVITY_1_ID, TIMELINE_ACTIVITY_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedTimelineActivities = - response.body.data.updateTimelineActivities; - - expect(updatedTimelineActivities).toHaveLength(2); - - updatedTimelineActivities.forEach((timelineActivity) => { - expect(timelineActivity.name).toEqual('Updated Name'); - }); - }); - - it('3b. should update one timelineActivity', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - data: { - name: 'New Name', - }, - recordId: TIMELINE_ACTIVITY_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedTimelineActivity = response.body.data.updateTimelineActivity; - - expect(updatedTimelineActivity.name).toEqual('New Name'); - }); - - it('4. should find many timelineActivities with updated name', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - objectMetadataPluralName: 'timelineActivities', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - filter: { - name: { - eq: 'Updated Name', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.timelineActivities.edges).toHaveLength(2); - }); - - it('4b. should find one timelineActivity with updated name', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - filter: { - name: { - eq: 'New Name', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.timelineActivity.name).toEqual('New Name'); - }); - - it('5. should delete many timelineActivities', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - objectMetadataPluralName: 'timelineActivities', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - filter: { - id: { - in: [TIMELINE_ACTIVITY_1_ID, TIMELINE_ACTIVITY_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deletedTimelineActivities = - response.body.data.deleteTimelineActivities; - - expect(deletedTimelineActivities).toHaveLength(2); - - deletedTimelineActivities.forEach((timelineActivity) => { - expect(timelineActivity.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one timelineActivity', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - recordId: TIMELINE_ACTIVITY_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteTimelineActivity.deletedAt).toBeTruthy(); - }); - - it('6. should not find many timelineActivities anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - objectMetadataPluralName: 'timelineActivities', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - filter: { - id: { - in: [TIMELINE_ACTIVITY_1_ID, TIMELINE_ACTIVITY_2_ID], - }, - }, - }); - - const findTimelineActivitiesResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - findTimelineActivitiesResponse.body.data.timelineActivities.edges, - ).toHaveLength(0); - }); - - it('6b. should not find one timelineActivity anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - filter: { - id: { - eq: TIMELINE_ACTIVITY_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.timelineActivity).toBeNull(); - }); - - it('7. should find many deleted timelineActivities with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - objectMetadataPluralName: 'timelineActivities', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - filter: { - id: { - in: [TIMELINE_ACTIVITY_1_ID, TIMELINE_ACTIVITY_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.timelineActivities.edges).toHaveLength(2); - }); - - it('7b. should find one deleted timelineActivity with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - filter: { - id: { - eq: TIMELINE_ACTIVITY_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.timelineActivity.id).toEqual( - TIMELINE_ACTIVITY_3_ID, - ); - }); - - it('8. should destroy many timelineActivities', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - objectMetadataPluralName: 'timelineActivities', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - filter: { - id: { - in: [TIMELINE_ACTIVITY_1_ID, TIMELINE_ACTIVITY_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyTimelineActivities).toHaveLength(2); - }); - - it('8b. should destroy one timelineActivity', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - recordId: TIMELINE_ACTIVITY_3_ID, - }); - - const destroyTimelineActivityResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect( - destroyTimelineActivityResponse.body.data.destroyTimelineActivity, - ).toBeTruthy(); - }); - - it('9. should not find many timelineActivities anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - objectMetadataPluralName: 'timelineActivities', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - filter: { - id: { - in: [TIMELINE_ACTIVITY_1_ID, TIMELINE_ACTIVITY_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.timelineActivities.edges).toHaveLength(0); - }); - - it('9b. should not find one timelineActivity anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'timelineActivity', - gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS, - filter: { - id: { - eq: TIMELINE_ACTIVITY_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.timelineActivity).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-view-fields.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-view-fields.integration-spec.ts deleted file mode 100644 index 168bad037..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-view-fields.integration-spec.ts +++ /dev/null @@ -1,407 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; - -const VIEW_FIELD_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const VIEW_FIELD_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const VIEW_FIELD_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; -const FIELD_METADATA_ID = '20202020-0c28-43d8-8ba5-3659924d3489'; - -const VIEW_FIELD_GQL_FIELDS = ` - id - fieldMetadataId - isVisible - size - position - createdAt - updatedAt - deletedAt - viewId -`; - -describe('viewFields resolvers (integration)', () => { - it('1. should create and return viewFields', async () => { - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'viewField', - objectMetadataPluralName: 'viewFields', - gqlFields: VIEW_FIELD_GQL_FIELDS, - data: [ - { - id: VIEW_FIELD_1_ID, - fieldMetadataId: FIELD_METADATA_ID, - }, - { - id: VIEW_FIELD_2_ID, - fieldMetadataId: FIELD_METADATA_ID, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createViewFields).toHaveLength(2); - - response.body.data.createViewFields.forEach((viewField) => { - expect(viewField).toHaveProperty('fieldMetadataId'); - expect(viewField.fieldMetadataId).toEqual(FIELD_METADATA_ID); - expect(viewField).toHaveProperty('id'); - expect(viewField).toHaveProperty('isVisible'); - expect(viewField).toHaveProperty('size'); - expect(viewField).toHaveProperty('position'); - expect(viewField).toHaveProperty('createdAt'); - expect(viewField).toHaveProperty('updatedAt'); - expect(viewField).toHaveProperty('deletedAt'); - expect(viewField).toHaveProperty('viewId'); - }); - }); - - it('1b. should create and return one viewField', async () => { - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'viewField', - gqlFields: VIEW_FIELD_GQL_FIELDS, - data: { - id: VIEW_FIELD_3_ID, - fieldMetadataId: FIELD_METADATA_ID, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdViewField = response.body.data.createViewField; - - expect(createdViewField).toHaveProperty('fieldMetadataId'); - expect(createdViewField.fieldMetadataId).toEqual(FIELD_METADATA_ID); - expect(createdViewField).toHaveProperty('id'); - expect(createdViewField).toHaveProperty('isVisible'); - expect(createdViewField).toHaveProperty('size'); - expect(createdViewField).toHaveProperty('position'); - expect(createdViewField).toHaveProperty('createdAt'); - expect(createdViewField).toHaveProperty('updatedAt'); - expect(createdViewField).toHaveProperty('deletedAt'); - expect(createdViewField).toHaveProperty('viewId'); - }); - - it('2. should find many viewFields', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewField', - objectMetadataPluralName: 'viewFields', - gqlFields: VIEW_FIELD_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.viewFields; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - if (data.edges.length > 0) { - const viewFields = data.edges[0].node; - - expect(viewFields).toHaveProperty('fieldMetadataId'); - expect(viewFields).toHaveProperty('isVisible'); - expect(viewFields).toHaveProperty('size'); - expect(viewFields).toHaveProperty('position'); - expect(viewFields).toHaveProperty('id'); - expect(viewFields).toHaveProperty('createdAt'); - expect(viewFields).toHaveProperty('updatedAt'); - expect(viewFields).toHaveProperty('deletedAt'); - expect(viewFields).toHaveProperty('viewId'); - } - }); - - it('2b. should find one viewField', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewField', - gqlFields: VIEW_FIELD_GQL_FIELDS, - filter: { - id: { - eq: VIEW_FIELD_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const viewField = response.body.data.viewField; - - expect(viewField).toHaveProperty('fieldMetadataId'); - expect(viewField).toHaveProperty('isVisible'); - expect(viewField).toHaveProperty('size'); - expect(viewField).toHaveProperty('position'); - expect(viewField).toHaveProperty('id'); - expect(viewField).toHaveProperty('createdAt'); - expect(viewField).toHaveProperty('updatedAt'); - expect(viewField).toHaveProperty('deletedAt'); - expect(viewField).toHaveProperty('viewId'); - }); - - it('3. should update many viewFields', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'viewField', - objectMetadataPluralName: 'viewFields', - gqlFields: VIEW_FIELD_GQL_FIELDS, - data: { - isVisible: false, - }, - filter: { - id: { - in: [VIEW_FIELD_1_ID, VIEW_FIELD_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedViewFields = response.body.data.updateViewFields; - - expect(updatedViewFields).toHaveLength(2); - - updatedViewFields.forEach((viewField) => { - expect(viewField.isVisible).toEqual(false); - }); - }); - - it('3b. should update one viewField', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'viewField', - gqlFields: VIEW_FIELD_GQL_FIELDS, - data: { - isVisible: true, - }, - recordId: VIEW_FIELD_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedViewField = response.body.data.updateViewField; - - expect(updatedViewField.isVisible).toEqual(true); - }); - - it('4. should find many viewFields with updated visibility', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewField', - objectMetadataPluralName: 'viewFields', - gqlFields: VIEW_FIELD_GQL_FIELDS, - filter: { - isVisible: { - eq: false, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewFields.edges).toHaveLength(2); - }); - - it('4b. should find one viewField with updated visibility', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewField', - gqlFields: VIEW_FIELD_GQL_FIELDS, - filter: { - isVisible: { - eq: true, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewField.isVisible).toEqual(true); - }); - - it('5. should delete many viewFields', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'viewField', - objectMetadataPluralName: 'viewFields', - gqlFields: VIEW_FIELD_GQL_FIELDS, - filter: { - id: { - in: [VIEW_FIELD_1_ID, VIEW_FIELD_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deletedViewFields = response.body.data.deleteViewFields; - - expect(deletedViewFields).toHaveLength(2); - - deletedViewFields.forEach((viewField) => { - expect(viewField.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one viewField', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'viewField', - gqlFields: VIEW_FIELD_GQL_FIELDS, - recordId: VIEW_FIELD_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteViewField.deletedAt).toBeTruthy(); - }); - - it('6. should not find many viewFields anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewField', - objectMetadataPluralName: 'viewFields', - gqlFields: VIEW_FIELD_GQL_FIELDS, - filter: { - id: { - in: [VIEW_FIELD_1_ID, VIEW_FIELD_2_ID], - }, - }, - }); - - const findViewFieldsResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(findViewFieldsResponse.body.data.viewFields.edges).toHaveLength(0); - }); - - it('6b. should not find one viewField anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewField', - gqlFields: VIEW_FIELD_GQL_FIELDS, - filter: { - id: { - eq: VIEW_FIELD_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewField).toBeNull(); - }); - - it('7. should find many deleted viewFields with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewField', - objectMetadataPluralName: 'viewFields', - gqlFields: VIEW_FIELD_GQL_FIELDS, - filter: { - id: { - in: [VIEW_FIELD_1_ID, VIEW_FIELD_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewFields.edges).toHaveLength(2); - }); - - it('7b. should find one deleted viewField with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewField', - gqlFields: VIEW_FIELD_GQL_FIELDS, - filter: { - id: { - eq: VIEW_FIELD_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewField.id).toEqual(VIEW_FIELD_3_ID); - }); - - it('8. should destroy many viewFields', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'viewField', - objectMetadataPluralName: 'viewFields', - gqlFields: VIEW_FIELD_GQL_FIELDS, - filter: { - id: { - in: [VIEW_FIELD_1_ID, VIEW_FIELD_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyViewFields).toHaveLength(2); - }); - - it('8b. should destroy one viewField', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'viewField', - gqlFields: VIEW_FIELD_GQL_FIELDS, - recordId: VIEW_FIELD_3_ID, - }); - - const destroyViewFieldResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyViewFieldResponse.body.data.destroyViewField).toBeTruthy(); - }); - - it('9. should not find many viewFields anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewField', - objectMetadataPluralName: 'viewFields', - gqlFields: VIEW_FIELD_GQL_FIELDS, - filter: { - id: { - in: [VIEW_FIELD_1_ID, VIEW_FIELD_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewFields.edges).toHaveLength(0); - }); - - it('9b. should not find one viewField anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewField', - gqlFields: VIEW_FIELD_GQL_FIELDS, - filter: { - id: { - eq: VIEW_FIELD_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewField).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-view-filters.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-view-filters.integration-spec.ts deleted file mode 100644 index e55c654aa..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-view-filters.integration-spec.ts +++ /dev/null @@ -1,407 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; - -const VIEW_FILTER_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const VIEW_FILTER_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const VIEW_FILTER_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; -const FIELD_METADATA_ID = '20202020-0c28-43d8-8ba5-3659924d3489'; - -const VIEW_FILTER_FIELDS = ` - id - fieldMetadataId - operand - value - displayValue - createdAt - updatedAt - deletedAt - viewId -`; - -describe('viewFilters resolvers (integration)', () => { - it('1. should create and return viewFilters', async () => { - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'viewFilter', - objectMetadataPluralName: 'viewFilters', - gqlFields: VIEW_FILTER_FIELDS, - data: [ - { - id: VIEW_FILTER_1_ID, - fieldMetadataId: FIELD_METADATA_ID, - }, - { - id: VIEW_FILTER_2_ID, - fieldMetadataId: FIELD_METADATA_ID, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createViewFilters).toHaveLength(2); - - response.body.data.createViewFilters.forEach((viewFilter) => { - expect(viewFilter).toHaveProperty('fieldMetadataId'); - expect(viewFilter.fieldMetadataId).toEqual(FIELD_METADATA_ID); - expect(viewFilter).toHaveProperty('id'); - expect(viewFilter).toHaveProperty('operand'); - expect(viewFilter).toHaveProperty('value'); - expect(viewFilter).toHaveProperty('displayValue'); - expect(viewFilter).toHaveProperty('createdAt'); - expect(viewFilter).toHaveProperty('updatedAt'); - expect(viewFilter).toHaveProperty('deletedAt'); - expect(viewFilter).toHaveProperty('viewId'); - }); - }); - - it('1b. should create and return one viewFilter', async () => { - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'viewFilter', - gqlFields: VIEW_FILTER_FIELDS, - data: { - id: VIEW_FILTER_3_ID, - fieldMetadataId: FIELD_METADATA_ID, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdViewFilter = response.body.data.createViewFilter; - - expect(createdViewFilter).toHaveProperty('fieldMetadataId'); - expect(createdViewFilter.fieldMetadataId).toEqual(FIELD_METADATA_ID); - expect(createdViewFilter).toHaveProperty('id'); - expect(createdViewFilter).toHaveProperty('operand'); - expect(createdViewFilter).toHaveProperty('value'); - expect(createdViewFilter).toHaveProperty('displayValue'); - expect(createdViewFilter).toHaveProperty('createdAt'); - expect(createdViewFilter).toHaveProperty('updatedAt'); - expect(createdViewFilter).toHaveProperty('deletedAt'); - expect(createdViewFilter).toHaveProperty('viewId'); - }); - - it('2. should find many viewFilters', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewFilter', - objectMetadataPluralName: 'viewFilters', - gqlFields: VIEW_FILTER_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.viewFilters; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - if (data.edges.length > 0) { - const viewFilters = data.edges[0].node; - - expect(viewFilters).toHaveProperty('fieldMetadataId'); - expect(viewFilters).toHaveProperty('operand'); - expect(viewFilters).toHaveProperty('value'); - expect(viewFilters).toHaveProperty('displayValue'); - expect(viewFilters).toHaveProperty('id'); - expect(viewFilters).toHaveProperty('createdAt'); - expect(viewFilters).toHaveProperty('updatedAt'); - expect(viewFilters).toHaveProperty('deletedAt'); - expect(viewFilters).toHaveProperty('viewId'); - } - }); - - it('2b. should find one viewFilter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewFilter', - gqlFields: VIEW_FILTER_FIELDS, - filter: { - id: { - eq: VIEW_FILTER_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const viewFilter = response.body.data.viewFilter; - - expect(viewFilter).toHaveProperty('fieldMetadataId'); - expect(viewFilter).toHaveProperty('operand'); - expect(viewFilter).toHaveProperty('value'); - expect(viewFilter).toHaveProperty('displayValue'); - expect(viewFilter).toHaveProperty('id'); - expect(viewFilter).toHaveProperty('createdAt'); - expect(viewFilter).toHaveProperty('updatedAt'); - expect(viewFilter).toHaveProperty('deletedAt'); - expect(viewFilter).toHaveProperty('viewId'); - }); - - it('3. should update many viewFilters', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'viewFilter', - objectMetadataPluralName: 'viewFilters', - gqlFields: VIEW_FILTER_FIELDS, - data: { - operand: 'Updated Operand', - }, - filter: { - id: { - in: [VIEW_FILTER_1_ID, VIEW_FILTER_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedViewFilters = response.body.data.updateViewFilters; - - expect(updatedViewFilters).toHaveLength(2); - - updatedViewFilters.forEach((viewFilter) => { - expect(viewFilter.operand).toEqual('Updated Operand'); - }); - }); - - it('3b. should update one viewFilter', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'viewFilter', - gqlFields: VIEW_FILTER_FIELDS, - data: { - operand: 'Updated Operand 3', - }, - recordId: VIEW_FILTER_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedViewFilter = response.body.data.updateViewFilter; - - expect(updatedViewFilter.operand).toEqual('Updated Operand 3'); - }); - - it('4. should find many viewFilters with updated operand', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewFilter', - objectMetadataPluralName: 'viewFilters', - gqlFields: VIEW_FILTER_FIELDS, - filter: { - operand: { - eq: 'Updated Operand', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewFilters.edges).toHaveLength(2); - }); - - it('4b. should find one viewFilter with updated operand', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewFilter', - gqlFields: VIEW_FILTER_FIELDS, - filter: { - operand: { - eq: 'Updated Operand 3', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewFilter.operand).toEqual('Updated Operand 3'); - }); - - it('5. should delete many viewFilters', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'viewFilter', - objectMetadataPluralName: 'viewFilters', - gqlFields: VIEW_FILTER_FIELDS, - filter: { - id: { - in: [VIEW_FILTER_1_ID, VIEW_FILTER_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deletedViewFilters = response.body.data.deleteViewFilters; - - expect(deletedViewFilters).toHaveLength(2); - - deletedViewFilters.forEach((viewFilter) => { - expect(viewFilter.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one viewFilter', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'viewFilter', - gqlFields: VIEW_FILTER_FIELDS, - recordId: VIEW_FILTER_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteViewFilter.deletedAt).toBeTruthy(); - }); - - it('6. should not find many viewFilters anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewFilter', - objectMetadataPluralName: 'viewFilters', - gqlFields: VIEW_FILTER_FIELDS, - filter: { - id: { - in: [VIEW_FILTER_1_ID, VIEW_FILTER_2_ID], - }, - }, - }); - - const findViewFiltersResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(findViewFiltersResponse.body.data.viewFilters.edges).toHaveLength(0); - }); - - it('6b. should not find one viewFilter anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewFilter', - gqlFields: VIEW_FILTER_FIELDS, - filter: { - id: { - eq: VIEW_FILTER_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewFilter).toBeNull(); - }); - - it('7. should find many deleted viewFilters with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewFilter', - objectMetadataPluralName: 'viewFilters', - gqlFields: VIEW_FILTER_FIELDS, - filter: { - id: { - in: [VIEW_FILTER_1_ID, VIEW_FILTER_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewFilters.edges).toHaveLength(2); - }); - - it('7b. should find one deleted viewFilter with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewFilter', - gqlFields: VIEW_FILTER_FIELDS, - filter: { - id: { - eq: VIEW_FILTER_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewFilter.id).toEqual(VIEW_FILTER_3_ID); - }); - - it('8. should destroy many viewFilters', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'viewFilter', - objectMetadataPluralName: 'viewFilters', - gqlFields: VIEW_FILTER_FIELDS, - filter: { - id: { - in: [VIEW_FILTER_1_ID, VIEW_FILTER_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyViewFilters).toHaveLength(2); - }); - - it('8b. should destroy one viewFilter', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'viewFilter', - gqlFields: VIEW_FILTER_FIELDS, - recordId: VIEW_FILTER_3_ID, - }); - - const destroyViewFilterResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyViewFilterResponse.body.data.destroyViewFilter).toBeTruthy(); - }); - - it('9. should not find many viewFilters anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewFilter', - objectMetadataPluralName: 'viewFilters', - gqlFields: VIEW_FILTER_FIELDS, - filter: { - id: { - in: [VIEW_FILTER_1_ID, VIEW_FILTER_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewFilters.edges).toHaveLength(0); - }); - - it('9b. should not find one viewFilter anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewFilter', - gqlFields: VIEW_FILTER_FIELDS, - filter: { - id: { - eq: VIEW_FILTER_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewFilter).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-view-sorts.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-view-sorts.integration-spec.ts deleted file mode 100644 index 0a3830e1e..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-view-sorts.integration-spec.ts +++ /dev/null @@ -1,399 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; - -const VIEW_SORT_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const VIEW_SORT_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const VIEW_SORT_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; -const FIELD_METADATA_ID = '20202020-0c28-43d8-8ba5-3659924d3489'; - -const VIEW_SORT_GQL_FIELDS = ` - id - fieldMetadataId - direction - createdAt - updatedAt - deletedAt - viewId -`; - -describe('viewSorts resolvers (integration)', () => { - it('1. should create and return viewSorts', async () => { - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'viewSort', - objectMetadataPluralName: 'viewSorts', - gqlFields: VIEW_SORT_GQL_FIELDS, - data: [ - { - id: VIEW_SORT_1_ID, - fieldMetadataId: FIELD_METADATA_ID, - direction: 'ASC', - }, - { - id: VIEW_SORT_2_ID, - fieldMetadataId: FIELD_METADATA_ID, - direction: 'DESC', - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createViewSorts).toHaveLength(2); - - response.body.data.createViewSorts.forEach((viewSort) => { - expect(viewSort).toHaveProperty('fieldMetadataId'); - expect(viewSort.fieldMetadataId).toEqual(FIELD_METADATA_ID); - expect(viewSort).toHaveProperty('direction'); - expect(viewSort).toHaveProperty('id'); - expect(viewSort).toHaveProperty('createdAt'); - expect(viewSort).toHaveProperty('updatedAt'); - expect(viewSort).toHaveProperty('deletedAt'); - expect(viewSort).toHaveProperty('viewId'); - }); - }); - - it('1b. should create and return one viewSort', async () => { - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'viewSort', - gqlFields: VIEW_SORT_GQL_FIELDS, - data: { - id: VIEW_SORT_3_ID, - fieldMetadataId: FIELD_METADATA_ID, - direction: 'ASC', - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdViewSort = response.body.data.createViewSort; - - expect(createdViewSort).toHaveProperty('fieldMetadataId'); - expect(createdViewSort.fieldMetadataId).toEqual(FIELD_METADATA_ID); - expect(createdViewSort).toHaveProperty('direction'); - expect(createdViewSort).toHaveProperty('id'); - expect(createdViewSort).toHaveProperty('createdAt'); - expect(createdViewSort).toHaveProperty('updatedAt'); - expect(createdViewSort).toHaveProperty('deletedAt'); - expect(createdViewSort).toHaveProperty('viewId'); - }); - - it('2. should find many viewSorts', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewSort', - objectMetadataPluralName: 'viewSorts', - gqlFields: VIEW_SORT_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.viewSorts; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - if (data.edges.length > 0) { - const viewSorts = data.edges[0].node; - - expect(viewSorts).toHaveProperty('fieldMetadataId'); - expect(viewSorts).toHaveProperty('direction'); - expect(viewSorts).toHaveProperty('id'); - expect(viewSorts).toHaveProperty('createdAt'); - expect(viewSorts).toHaveProperty('updatedAt'); - expect(viewSorts).toHaveProperty('deletedAt'); - expect(viewSorts).toHaveProperty('viewId'); - } - }); - - it('2b. should find one viewSort', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewSort', - gqlFields: VIEW_SORT_GQL_FIELDS, - filter: { - id: { - eq: VIEW_SORT_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const viewSort = response.body.data.viewSort; - - expect(viewSort).toHaveProperty('fieldMetadataId'); - expect(viewSort).toHaveProperty('direction'); - expect(viewSort).toHaveProperty('id'); - expect(viewSort).toHaveProperty('createdAt'); - expect(viewSort).toHaveProperty('updatedAt'); - expect(viewSort).toHaveProperty('deletedAt'); - expect(viewSort).toHaveProperty('viewId'); - }); - - it('3. should update many viewSorts', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'viewSort', - objectMetadataPluralName: 'viewSorts', - gqlFields: VIEW_SORT_GQL_FIELDS, - data: { - direction: 'DESC', - }, - filter: { - id: { - in: [VIEW_SORT_1_ID, VIEW_SORT_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedViewSorts = response.body.data.updateViewSorts; - - expect(updatedViewSorts).toHaveLength(2); - - updatedViewSorts.forEach((viewSort) => { - expect(viewSort.direction).toEqual('DESC'); - }); - }); - - it('3b. should update one viewSort', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'viewSort', - gqlFields: VIEW_SORT_GQL_FIELDS, - data: { - direction: 'ASC', - }, - recordId: VIEW_SORT_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedViewSort = response.body.data.updateViewSort; - - expect(updatedViewSort.direction).toEqual('ASC'); - }); - - it('4. should find many viewSorts with updated direction', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewSort', - objectMetadataPluralName: 'viewSorts', - gqlFields: VIEW_SORT_GQL_FIELDS, - filter: { - direction: { - eq: 'DESC', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewSorts.edges).toHaveLength(2); - }); - - it('4b. should find one viewSort with updated direction', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewSort', - gqlFields: VIEW_SORT_GQL_FIELDS, - filter: { - direction: { - eq: 'ASC', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewSort.direction).toEqual('ASC'); - }); - - it('5. should delete many viewSorts', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'viewSort', - objectMetadataPluralName: 'viewSorts', - gqlFields: VIEW_SORT_GQL_FIELDS, - filter: { - id: { - in: [VIEW_SORT_1_ID, VIEW_SORT_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deletedViewSorts = response.body.data.deleteViewSorts; - - expect(deletedViewSorts).toHaveLength(2); - - deletedViewSorts.forEach((viewSort) => { - expect(viewSort.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one viewSort', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'viewSort', - gqlFields: VIEW_SORT_GQL_FIELDS, - recordId: VIEW_SORT_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteViewSort.deletedAt).toBeTruthy(); - }); - - it('6. should not find many viewSorts anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewSort', - objectMetadataPluralName: 'viewSorts', - gqlFields: VIEW_SORT_GQL_FIELDS, - filter: { - id: { - in: [VIEW_SORT_1_ID, VIEW_SORT_2_ID], - }, - }, - }); - - const findViewSortsResponse = await makeGraphqlAPIRequest(graphqlOperation); - - expect(findViewSortsResponse.body.data.viewSorts.edges).toHaveLength(0); - }); - - it('6b. should not find one viewSort anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewSort', - gqlFields: VIEW_SORT_GQL_FIELDS, - filter: { - id: { - eq: VIEW_SORT_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewSort).toBeNull(); - }); - - it('7. should find many deleted viewSorts with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewSort', - objectMetadataPluralName: 'viewSorts', - gqlFields: VIEW_SORT_GQL_FIELDS, - filter: { - id: { - in: [VIEW_SORT_1_ID, VIEW_SORT_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewSorts.edges).toHaveLength(2); - }); - - it('7b. should find one deleted viewSort with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewSort', - gqlFields: VIEW_SORT_GQL_FIELDS, - filter: { - id: { - eq: VIEW_SORT_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewSort.id).toEqual(VIEW_SORT_3_ID); - }); - - it('8. should destroy many viewSorts', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'viewSort', - objectMetadataPluralName: 'viewSorts', - gqlFields: VIEW_SORT_GQL_FIELDS, - filter: { - id: { - in: [VIEW_SORT_1_ID, VIEW_SORT_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyViewSorts).toHaveLength(2); - }); - - it('8b. should destroy one viewSort', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'viewSort', - gqlFields: VIEW_SORT_GQL_FIELDS, - recordId: VIEW_SORT_3_ID, - }); - - const destroyViewSortResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyViewSortResponse.body.data.destroyViewSort).toBeTruthy(); - }); - - it('9. should not find many viewSorts anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'viewSort', - objectMetadataPluralName: 'viewSorts', - gqlFields: VIEW_SORT_GQL_FIELDS, - filter: { - id: { - in: [VIEW_SORT_1_ID, VIEW_SORT_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewSorts.edges).toHaveLength(0); - }); - - it('9b. should not find one viewSort anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'viewSort', - gqlFields: VIEW_SORT_GQL_FIELDS, - filter: { - id: { - eq: VIEW_SORT_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.viewSort).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-views.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-views.integration-spec.ts deleted file mode 100644 index 4caa88084..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-views.integration-spec.ts +++ /dev/null @@ -1,429 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const VIEW_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const VIEW_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const VIEW_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; -const OBJECT_METADATA_ID = '20202020-b374-4779-a561-80086cb2e17f'; - -const VIEW_GQL_FIELDS = ` - id - name - objectMetadataId - type - key - icon - kanbanFieldMetadataId - position - isCompact - createdAt - updatedAt - deletedAt -`; - -describe('views resolvers (integration)', () => { - it('1. should create and return views', async () => { - const viewName1 = generateRecordName(VIEW_1_ID); - const viewName2 = generateRecordName(VIEW_2_ID); - - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'view', - objectMetadataPluralName: 'views', - gqlFields: VIEW_GQL_FIELDS, - data: [ - { - id: VIEW_1_ID, - name: viewName1, - objectMetadataId: OBJECT_METADATA_ID, - }, - { - id: VIEW_2_ID, - name: viewName2, - objectMetadataId: OBJECT_METADATA_ID, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createViews).toHaveLength(2); - - response.body.data.createViews.forEach((view) => { - expect(view).toHaveProperty('name'); - expect([viewName1, viewName2]).toContain(view.name); - expect(view).toHaveProperty('id'); - expect(view).toHaveProperty('objectMetadataId'); - expect(view).toHaveProperty('type'); - expect(view).toHaveProperty('key'); - expect(view).toHaveProperty('icon'); - expect(view).toHaveProperty('kanbanFieldMetadataId'); - expect(view).toHaveProperty('position'); - expect(view).toHaveProperty('isCompact'); - expect(view).toHaveProperty('createdAt'); - expect(view).toHaveProperty('updatedAt'); - expect(view).toHaveProperty('deletedAt'); - }); - }); - - it('1b. should create and return one view', async () => { - const viewName = generateRecordName(VIEW_3_ID); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'view', - gqlFields: VIEW_GQL_FIELDS, - data: { - id: VIEW_3_ID, - name: viewName, - objectMetadataId: OBJECT_METADATA_ID, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdView = response.body.data.createView; - - expect(createdView).toHaveProperty('name'); - expect(createdView.name).toEqual(viewName); - expect(createdView).toHaveProperty('id'); - expect(createdView).toHaveProperty('objectMetadataId'); - expect(createdView).toHaveProperty('type'); - expect(createdView).toHaveProperty('key'); - expect(createdView).toHaveProperty('icon'); - expect(createdView).toHaveProperty('kanbanFieldMetadataId'); - expect(createdView).toHaveProperty('position'); - expect(createdView).toHaveProperty('isCompact'); - expect(createdView).toHaveProperty('createdAt'); - expect(createdView).toHaveProperty('updatedAt'); - expect(createdView).toHaveProperty('deletedAt'); - }); - - it('2. should find many views', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'view', - objectMetadataPluralName: 'views', - gqlFields: VIEW_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.views; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - if (data.edges.length > 0) { - const views = data.edges[0].node; - - expect(views).toHaveProperty('name'); - expect(views).toHaveProperty('objectMetadataId'); - expect(views).toHaveProperty('type'); - expect(views).toHaveProperty('key'); - expect(views).toHaveProperty('icon'); - expect(views).toHaveProperty('kanbanFieldMetadataId'); - expect(views).toHaveProperty('position'); - expect(views).toHaveProperty('isCompact'); - expect(views).toHaveProperty('id'); - expect(views).toHaveProperty('createdAt'); - expect(views).toHaveProperty('updatedAt'); - expect(views).toHaveProperty('deletedAt'); - } - }); - - it('2b. should find one view', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'view', - gqlFields: VIEW_GQL_FIELDS, - filter: { - id: { - eq: VIEW_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const view = response.body.data.view; - - expect(view).toHaveProperty('name'); - expect(view).toHaveProperty('objectMetadataId'); - expect(view).toHaveProperty('type'); - expect(view).toHaveProperty('key'); - expect(view).toHaveProperty('icon'); - expect(view).toHaveProperty('kanbanFieldMetadataId'); - expect(view).toHaveProperty('position'); - expect(view).toHaveProperty('isCompact'); - expect(view).toHaveProperty('id'); - expect(view).toHaveProperty('createdAt'); - expect(view).toHaveProperty('updatedAt'); - expect(view).toHaveProperty('deletedAt'); - }); - - it('3. should update many views', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'view', - objectMetadataPluralName: 'views', - gqlFields: VIEW_GQL_FIELDS, - data: { - isCompact: true, - }, - filter: { - id: { - in: [VIEW_1_ID, VIEW_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedViews = response.body.data.updateViews; - - expect(updatedViews).toHaveLength(2); - - updatedViews.forEach((view) => { - expect(view.isCompact).toEqual(true); - }); - }); - - it('3b. should update one view', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'view', - gqlFields: VIEW_GQL_FIELDS, - data: { - isCompact: false, - }, - recordId: VIEW_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedView = response.body.data.updateView; - - expect(updatedView.isCompact).toEqual(false); - }); - - it('4. should find many views with updated isCompact', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'view', - objectMetadataPluralName: 'views', - gqlFields: VIEW_GQL_FIELDS, - filter: { - isCompact: { - eq: true, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.views.edges).toHaveLength(2); - }); - - it('4b. should find one view with updated isCompact', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'view', - gqlFields: VIEW_GQL_FIELDS, - filter: { - isCompact: { - eq: false, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.view.isCompact).toEqual(false); - }); - - it('5. should delete many views', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'view', - objectMetadataPluralName: 'views', - gqlFields: VIEW_GQL_FIELDS, - filter: { - id: { - in: [VIEW_1_ID, VIEW_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deletedViews = response.body.data.deleteViews; - - expect(deletedViews).toHaveLength(2); - - deletedViews.forEach((view) => { - expect(view.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one view', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'view', - gqlFields: VIEW_GQL_FIELDS, - recordId: VIEW_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteView.deletedAt).toBeTruthy(); - }); - - it('6. should not find many views anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'view', - objectMetadataPluralName: 'views', - gqlFields: VIEW_GQL_FIELDS, - filter: { - id: { - in: [VIEW_1_ID, VIEW_2_ID], - }, - }, - }); - - const findViewsResponse = await makeGraphqlAPIRequest(graphqlOperation); - - expect(findViewsResponse.body.data.views.edges).toHaveLength(0); - }); - - it('6b. should not find one view anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'view', - gqlFields: VIEW_GQL_FIELDS, - filter: { - id: { - eq: VIEW_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.view).toBeNull(); - }); - - it('7. should find many deleted views with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'view', - objectMetadataPluralName: 'views', - gqlFields: VIEW_GQL_FIELDS, - filter: { - id: { - in: [VIEW_1_ID, VIEW_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.views.edges).toHaveLength(2); - }); - - it('7b. should find one deleted view with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'view', - gqlFields: VIEW_GQL_FIELDS, - filter: { - id: { - eq: VIEW_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.view.id).toEqual(VIEW_3_ID); - }); - - it('8. should destroy many views', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'view', - objectMetadataPluralName: 'views', - gqlFields: VIEW_GQL_FIELDS, - filter: { - id: { - in: [VIEW_1_ID, VIEW_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyViews).toHaveLength(2); - }); - - it('8b. should destroy one view', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'view', - gqlFields: VIEW_GQL_FIELDS, - recordId: VIEW_3_ID, - }); - - const destroyViewResponse = await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyViewResponse.body.data.destroyView).toBeTruthy(); - }); - - it('9. should not find many views anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'view', - objectMetadataPluralName: 'views', - gqlFields: VIEW_GQL_FIELDS, - filter: { - id: { - in: [VIEW_1_ID, VIEW_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.views.edges).toHaveLength(0); - }); - - it('9b. should not find one view anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'view', - gqlFields: VIEW_GQL_FIELDS, - filter: { - id: { - eq: VIEW_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.view).toBeNull(); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/all-webhooks.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/all-webhooks.integration-spec.ts deleted file mode 100644 index 7de793dbd..000000000 --- a/packages/twenty-server/test/integration/graphql/suites/all-webhooks.integration-spec.ts +++ /dev/null @@ -1,403 +0,0 @@ -import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util'; -import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util'; -import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util'; -import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util'; -import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util'; -import { generateRecordName } from 'test/integration/utils/generate-record-name'; - -const WEBHOOK_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987'; -const WEBHOOK_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988'; -const WEBHOOK_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989'; - -const WEBHOOK_GQL_FIELDS = ` - id - targetUrl - operations - description - createdAt - updatedAt - deletedAt -`; - -describe('webhooks resolvers (integration)', () => { - it('1. should create and return webhooks', async () => { - const webhookDescription1 = generateRecordName(WEBHOOK_1_ID); - const webhookDescription2 = generateRecordName(WEBHOOK_2_ID); - - const graphqlOperation = createManyOperationFactory({ - objectMetadataSingularName: 'webhook', - objectMetadataPluralName: 'webhooks', - gqlFields: WEBHOOK_GQL_FIELDS, - data: [ - { - id: WEBHOOK_1_ID, - description: webhookDescription1, - }, - { - id: WEBHOOK_2_ID, - description: webhookDescription2, - }, - ], - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.createWebhooks).toHaveLength(2); - - response.body.data.createWebhooks.forEach((webhook) => { - expect(webhook).toHaveProperty('description'); - expect([webhookDescription1, webhookDescription2]).toContain( - webhook.description, - ); - expect(webhook).toHaveProperty('operations'); - expect(webhook).toHaveProperty('id'); - expect(webhook).toHaveProperty('targetUrl'); - expect(webhook).toHaveProperty('createdAt'); - expect(webhook).toHaveProperty('updatedAt'); - expect(webhook).toHaveProperty('deletedAt'); - }); - }); - - it('1b. should create and return one webhook', async () => { - const webhookDescription = generateRecordName(WEBHOOK_3_ID); - - const graphqlOperation = createOneOperationFactory({ - objectMetadataSingularName: 'webhook', - gqlFields: WEBHOOK_GQL_FIELDS, - data: { - id: WEBHOOK_3_ID, - description: webhookDescription, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const createdWebhook = response.body.data.createWebhook; - - expect(createdWebhook).toHaveProperty('description'); - expect(createdWebhook.description).toEqual(webhookDescription); - expect(createdWebhook).toHaveProperty('operations'); - expect(createdWebhook).toHaveProperty('id'); - expect(createdWebhook).toHaveProperty('targetUrl'); - expect(createdWebhook).toHaveProperty('createdAt'); - expect(createdWebhook).toHaveProperty('updatedAt'); - expect(createdWebhook).toHaveProperty('deletedAt'); - }); - - it('2. should find many webhooks', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'webhook', - objectMetadataPluralName: 'webhooks', - gqlFields: WEBHOOK_GQL_FIELDS, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const data = response.body.data.webhooks; - - expect(data).toBeDefined(); - expect(Array.isArray(data.edges)).toBe(true); - - if (data.edges.length > 0) { - const webhooks = data.edges[0].node; - - expect(webhooks).toHaveProperty('targetUrl'); - expect(webhooks).toHaveProperty('operations'); - expect(webhooks).toHaveProperty('id'); - expect(webhooks).toHaveProperty('description'); - expect(webhooks).toHaveProperty('createdAt'); - expect(webhooks).toHaveProperty('updatedAt'); - expect(webhooks).toHaveProperty('deletedAt'); - } - }); - - it('2b. should find one webhook', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'webhook', - gqlFields: WEBHOOK_GQL_FIELDS, - filter: { - id: { - eq: WEBHOOK_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const webhook = response.body.data.webhook; - - expect(webhook).toHaveProperty('targetUrl'); - expect(webhook).toHaveProperty('operations'); - expect(webhook).toHaveProperty('id'); - expect(webhook).toHaveProperty('description'); - expect(webhook).toHaveProperty('createdAt'); - expect(webhook).toHaveProperty('updatedAt'); - expect(webhook).toHaveProperty('deletedAt'); - }); - - it('3. should update many webhooks', async () => { - const graphqlOperation = updateManyOperationFactory({ - objectMetadataSingularName: 'webhook', - objectMetadataPluralName: 'webhooks', - gqlFields: WEBHOOK_GQL_FIELDS, - data: { - description: 'Updated Description', - }, - filter: { - id: { - in: [WEBHOOK_1_ID, WEBHOOK_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedWebhooks = response.body.data.updateWebhooks; - - expect(updatedWebhooks).toHaveLength(2); - - updatedWebhooks.forEach((webhook) => { - expect(webhook.description).toEqual('Updated Description'); - }); - }); - - it('3b. should update one webhook', async () => { - const graphqlOperation = updateOneOperationFactory({ - objectMetadataSingularName: 'webhook', - gqlFields: WEBHOOK_GQL_FIELDS, - data: { - description: 'New Description', - }, - recordId: WEBHOOK_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const updatedWebhook = response.body.data.updateWebhook; - - expect(updatedWebhook.description).toEqual('New Description'); - }); - - it('4. should find many webhooks with updated description', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'webhook', - objectMetadataPluralName: 'webhooks', - gqlFields: WEBHOOK_GQL_FIELDS, - filter: { - description: { - eq: 'Updated Description', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.webhooks.edges).toHaveLength(2); - }); - - it('4b. should find one webhook with updated description', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'webhook', - gqlFields: WEBHOOK_GQL_FIELDS, - filter: { - description: { - eq: 'New Description', - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.webhook.description).toEqual('New Description'); - }); - - it('5. should delete many webhooks', async () => { - const graphqlOperation = deleteManyOperationFactory({ - objectMetadataSingularName: 'webhook', - objectMetadataPluralName: 'webhooks', - gqlFields: WEBHOOK_GQL_FIELDS, - filter: { - id: { - in: [WEBHOOK_1_ID, WEBHOOK_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deletedWebhooks = response.body.data.deleteWebhooks; - - expect(deletedWebhooks).toHaveLength(2); - - deletedWebhooks.forEach((webhook) => { - expect(webhook.deletedAt).toBeTruthy(); - }); - }); - - it('5b. should delete one webhook', async () => { - const graphqlOperation = deleteOneOperationFactory({ - objectMetadataSingularName: 'webhook', - gqlFields: WEBHOOK_GQL_FIELDS, - recordId: WEBHOOK_3_ID, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.deleteWebhook.deletedAt).toBeTruthy(); - }); - - it('6. should not find many webhooks anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'webhook', - objectMetadataPluralName: 'webhooks', - gqlFields: WEBHOOK_GQL_FIELDS, - filter: { - id: { - in: [WEBHOOK_1_ID, WEBHOOK_2_ID], - }, - }, - }); - - const findWebhooksResponse = await makeGraphqlAPIRequest(graphqlOperation); - - expect(findWebhooksResponse.body.data.webhooks.edges).toHaveLength(0); - }); - - it('6b. should not find one webhook anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'webhook', - gqlFields: WEBHOOK_GQL_FIELDS, - filter: { - id: { - eq: WEBHOOK_3_ID, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.webhook).toBeNull(); - }); - - it('7. should find many deleted webhooks with deletedAt filter', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'webhook', - objectMetadataPluralName: 'webhooks', - gqlFields: WEBHOOK_GQL_FIELDS, - filter: { - id: { - in: [WEBHOOK_1_ID, WEBHOOK_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.webhooks.edges).toHaveLength(2); - }); - - it('7b. should find one deleted webhook with deletedAt filter', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'webhook', - gqlFields: WEBHOOK_GQL_FIELDS, - filter: { - id: { - eq: WEBHOOK_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.webhook.id).toEqual(WEBHOOK_3_ID); - }); - - it('8. should destroy many webhooks', async () => { - const graphqlOperation = destroyManyOperationFactory({ - objectMetadataSingularName: 'webhook', - objectMetadataPluralName: 'webhooks', - gqlFields: WEBHOOK_GQL_FIELDS, - filter: { - id: { - in: [WEBHOOK_1_ID, WEBHOOK_2_ID], - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.destroyWebhooks).toHaveLength(2); - }); - - it('8b. should destroy one webhook', async () => { - const graphqlOperation = destroyOneOperationFactory({ - objectMetadataSingularName: 'webhook', - gqlFields: WEBHOOK_GQL_FIELDS, - recordId: WEBHOOK_3_ID, - }); - - const destroyWebhookResponse = - await makeGraphqlAPIRequest(graphqlOperation); - - expect(destroyWebhookResponse.body.data.destroyWebhook).toBeTruthy(); - }); - - it('9. should not find many webhooks anymore', async () => { - const graphqlOperation = findManyOperationFactory({ - objectMetadataSingularName: 'webhook', - objectMetadataPluralName: 'webhooks', - gqlFields: WEBHOOK_GQL_FIELDS, - filter: { - id: { - in: [WEBHOOK_1_ID, WEBHOOK_2_ID], - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.webhooks.edges).toHaveLength(0); - }); - - it('9b. should not find one webhook anymore', async () => { - const graphqlOperation = findOneOperationFactory({ - objectMetadataSingularName: 'webhook', - gqlFields: WEBHOOK_GQL_FIELDS, - filter: { - id: { - eq: WEBHOOK_3_ID, - }, - not: { - deletedAt: { - is: 'NULL', - }, - }, - }, - }); - - const response = await makeGraphqlAPIRequest(graphqlOperation); - - expect(response.body.data.webhook).toBeNull(); - }); -}); diff --git a/packages/twenty-website/src/content/developers/backend-development/server-commands.mdx b/packages/twenty-website/src/content/developers/backend-development/server-commands.mdx index 075058132..424ef37a3 100644 --- a/packages/twenty-website/src/content/developers/backend-development/server-commands.mdx +++ b/packages/twenty-website/src/content/developers/backend-development/server-commands.mdx @@ -30,8 +30,10 @@ npx nx run twenty-server:lint # pass --fix to fix lint errors ### Test ``` -npx nx run twenty-server:test:unit +npx nx run twenty-server:test:unit # run unit tests +npx nx run twenty-server:test:integration # run integration tests ``` +Note : you can run `npx nx run twenty-server:test:integration:with-db-reset` in case you need to reset the database before running the integration tests. ### Resetting the database From fb22efd9e94a28200f36f18161f6df634d733d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Thu, 7 Nov 2024 17:29:00 +0100 Subject: [PATCH 006/100] Fix CalendarOngoingStaleJob being in wrong queue (#8385) Fix CalendarOngoingStaleJob being in the wrong queue (`MessageQueue.messagingQueue` instead of `MessageQueue.calendarQueue`) --- .../jobs/calendar-ongoing-stale.job.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job.ts index 0ddd02960..c81a7d800 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job.ts @@ -18,7 +18,7 @@ export type CalendarOngoingStaleJobData = { }; @Processor({ - queueName: MessageQueue.messagingQueue, + queueName: MessageQueue.calendarQueue, scope: Scope.REQUEST, }) export class CalendarOngoingStaleJob { From f9a136ab6de8340347bd8fe2eaa7e2fe81ba2dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Thu, 7 Nov 2024 17:32:41 +0100 Subject: [PATCH 007/100] Update CanAccessMessageThreadService (#8388) Trying to fix #7830 --- .../can-access-message-thread.service.ts | 59 ++++++++----------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/packages/twenty-server/src/modules/messaging/common/query-hooks/message/can-access-message-thread.service.ts b/packages/twenty-server/src/modules/messaging/common/query-hooks/message/can-access-message-thread.service.ts index 0e3022584..19fdb153f 100644 --- a/packages/twenty-server/src/modules/messaging/common/query-hooks/message/can-access-message-thread.service.ts +++ b/packages/twenty-server/src/modules/messaging/common/query-hooks/message/can-access-message-thread.service.ts @@ -1,12 +1,12 @@ import { ForbiddenException } from '@nestjs/common'; -import groupBy from 'lodash.groupby'; -import { Any } from 'typeorm'; +import { In } from 'typeorm'; import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; -import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; +import { MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity'; +import { MessageChannelVisibility } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @@ -20,32 +20,12 @@ export class CanAccessMessageThreadService { public async canAccessMessageThread( userId: string, workspaceId: string, - messageChannelMessageAssociations: any[], + messageChannelMessageAssociations: MessageChannelMessageAssociationWorkspaceEntity[], ) { - const messageChannelRepository = - await this.twentyORMManager.getRepository( - 'messageChannel', - ); - const messageChannels = await messageChannelRepository.find({ - select: ['id', 'visibility'], - where: { - id: Any( - messageChannelMessageAssociations.map( - (association) => association.messageChannelId, - ), - ), - }, - }); - - const messageChannelsGroupByVisibility = groupBy( - messageChannels, - (channel) => channel.visibility, + const messageChannelIds = messageChannelMessageAssociations.map( + (association) => association.messageChannelId, ); - if (messageChannelsGroupByVisibility.SHARE_EVERYTHING) { - return; - } - const currentWorkspaceMember = await this.workspaceMemberRepository.getByIdOrFail(userId, workspaceId); @@ -55,17 +35,28 @@ export class CanAccessMessageThreadService { ); const connectedAccounts = await connectedAccountRepository.find({ - select: ['id'], - where: { - messageChannels: Any(messageChannels.map((channel) => channel.id)), - accountOwnerId: currentWorkspaceMember.id, + select: { + id: true, }, + where: [ + { + messageChannels: { + id: In(messageChannelIds), + visibility: MessageChannelVisibility.SHARE_EVERYTHING, + }, + }, + { + messageChannels: { + id: In(messageChannelIds), + }, + accountOwnerId: currentWorkspaceMember.id, + }, + ], + take: 1, }); - if (connectedAccounts.length > 0) { - return; + if (connectedAccounts.length === 0) { + throw new ForbiddenException(); } - - throw new ForbiddenException(); } } From 6264d509bd8b469f3a10b1f1e6e4c9b3fef44995 Mon Sep 17 00:00:00 2001 From: "gitstart-app[bot]" <57568882+gitstart-app[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:51:39 +0000 Subject: [PATCH 008/100] Migrate to twenty-ui - navigation/menu-item (#8213) This PR was created by [GitStart](https://gitstart.com/) to address the requirements from this ticket: [TWNTY-7536](https://clients.gitstart.com/twenty/5449/tickets/TWNTY-7536). --- ### Description Migrate all menu items components to twenty ui and update imports. ```typescript MenuItem MenuItemAvata MenuItemCommand MenuItemCommandHotKeys MenuItemDraggable MenuItemMultiSelect MenuItemMultiSelectAvatar MenuItemMultiSelectTag MenuItemNavigate MenuItemSelect MenuItemSelectAvatar MenuItemSelectColor MenuItemSelectTag MenuItemSuggestion MenuItemToggle ``` \ Also migrate all other dependent components and utilities like `Checkbox` & `Toggle`\ \ Fixes twentyhq/private-issues#82 --------- Co-authored-by: gitstart-twenty Co-authored-by: gitstart-twenty <140154534+gitstart-twenty@users.noreply.github.com> Co-authored-by: Lucas Bordeau --- .../RecordIndexActionMenuDropdown.tsx | 2 +- .../RecordShowActionMenuBarEntry.tsx | 5 +- .../RecordShowActionMenuBar.stories.tsx | 2 +- .../action-menu/types/ActionMenuEntry.ts | 4 +- ...ubscriberDropdownAddSubscriberMenuItem.tsx | 3 +- ...MessageThreadSubscribersDropdownButton.tsx | 4 +- .../files/components/AttachmentDropdown.tsx | 2 +- .../components/CommandMenuItem.tsx | 3 +- .../AdvancedFilterAddFilterRuleSelect.tsx | 9 ++- .../AdvancedFilterRuleOptionsDropdown.tsx | 3 +- .../AdvancedFilterViewFilterOperandSelect.tsx | 3 +- .../components/AdvancedFilterButton.tsx | 9 ++- ...pdownFilterSelectCompositeFieldSubMenu.tsx | 9 ++- ...jectFilterDropdownFilterSelectMenuItem.tsx | 3 +- .../ObjectFilterDropdownOperandSelect.tsx | 2 +- .../ObjectFilterDropdownOptionSelect.tsx | 4 +- ...lterDropdownRecordRemoveFilterMenuItem.tsx | 3 +- .../components/ObjectSortDropdownButton.tsx | 3 +- .../RecordBoardColumnDropdownMenu.tsx | 2 +- .../input/components/MultiItemFieldInput.tsx | 3 +- .../components/MultiItemFieldMenuItem.tsx | 2 +- .../components/MultiSelectFieldInput.tsx | 2 +- .../RecordIndexPageKanbanAddMenuItem.tsx | 3 +- .../RecordIndexOptionsDropdownContent.tsx | 6 +- .../RecordDetailRelationRecordsListItem.tsx | 2 +- .../RecordTableColumnHeadDropdownMenu.tsx | 2 +- .../RecordTableHeaderPlusButtonContent.tsx | 3 +- .../MultipleObjectRecordSelectItem.tsx | 3 +- .../components/SelectableMenuItemSelect.tsx | 3 +- .../SingleEntitySelectMenuItems.tsx | 4 +- .../components/MultipleSelectDropdown.tsx | 4 +- ...SettingsAccountsCalendarChannelDetails.tsx | 2 +- .../SettingsAccountsRowDropdownMenu.tsx | 2 +- .../SettingsAccountsToggleSettingCard.tsx | 60 +++++++++++++++++++ ...ngsDataModelNewFieldBreadcrumbDropDown.tsx | 3 +- ...tingsDataModelFieldSelectFormOptionRow.tsx | 4 +- ...ettingsObjectFieldActiveActionDropdown.tsx | 2 +- ...tingsObjectFieldDisabledActionDropdown.tsx | 2 +- .../components/SettingsObjectSummaryCard.tsx | 2 +- .../SettingsObjectInactiveMenuDropDown.tsx | 2 +- ...tegrationDatabaseConnectionSummaryCard.tsx | 2 +- .../SettingsSecurityOptionsList.tsx | 2 +- .../SettingsSecuritySSORowDropdownMenu.tsx | 2 +- ...FunctionTabEnvironmentVariableTableRow.tsx | 2 +- .../components/MatchColumnSelect.tsx | 8 +-- .../support/components/SupportDropdown.tsx | 3 +- .../modules/ui/input/components/Select.tsx | 3 +- .../ui/input/components/SelectInput.tsx | 3 +- .../CurrencyPickerDropdownSelect.tsx | 3 +- .../date/components/InternalDatePicker.tsx | 9 ++- .../PhoneCountryPickerDropdownSelect.tsx | 5 +- .../editor/components/CustomSlashMenu.tsx | 3 +- .../components/CreateNewButton.tsx | 3 +- .../__stories__/DraggableItem.stories.tsx | 3 +- .../__stories__/DraggableList.stories.tsx | 8 +-- .../__stories__/DropdownMenu.stories.tsx | 12 ++-- .../components/ShowPageAddButton.tsx | 9 ++- .../components/ShowPageMoreButton.tsx | 2 +- .../components/MenuItemWithOptionDropdown.tsx | 13 ++-- .../MultiWorkspaceDropdownButton.tsx | 3 +- .../components/UpdateViewButtonGroup.tsx | 9 ++- .../ViewFieldsVisibilityDropdownSection.tsx | 2 +- .../ViewGroupsVisibilityDropdownSection.tsx | 3 +- .../components/ViewPickerListContent.tsx | 4 +- ...RightDrawerWorkflowSelectActionContent.tsx | 2 +- ...DrawerWorkflowSelectTriggerTypeContent.tsx | 2 +- .../SearchVariablesDropdownStepItem.tsx | 3 +- .../SearchVariablesDropdownStepSubItem.tsx | 3 +- packages/twenty-front/tsup.ui.index.tsx | 10 ---- .../src/input/components/Checkbox.tsx | 2 +- packages/twenty-ui/src/navigation/index.ts | 18 ++++++ .../menu-item/components/MenuItem.tsx | 9 +-- .../menu-item/components/MenuItemAvatar.tsx | 19 ++---- .../menu-item/components/MenuItemCommand.tsx | 5 +- .../components/MenuItemCommandHotKeys.tsx | 0 .../components/MenuItemDraggable.tsx | 6 +- .../components/MenuItemMultiSelect.tsx | 7 ++- .../components/MenuItemMultiSelectAvatar.tsx | 3 +- .../components/MenuItemMultiSelectTag.tsx | 11 +--- .../menu-item/components/MenuItemNavigate.tsx | 2 +- .../menu-item/components/MenuItemSelect.tsx | 2 +- .../components/MenuItemSelectAvatar.tsx | 4 +- .../components/MenuItemSelectColor.tsx | 8 +-- .../components/MenuItemSelectTag.tsx | 3 +- .../components/MenuItemSuggestion.tsx | 3 +- .../menu-item/components/MenuItemToggle.tsx | 4 +- .../__stories__/MenuItem.stories.tsx | 6 +- .../__stories__/MenuItemCommand.stories.tsx | 6 +- .../__stories__/MenuItemDraggable.stories.tsx | 7 +-- .../MenuItemMultiSelect.stories.tsx | 6 +- .../MenuItemMultiSelectAvatar.stories.tsx | 13 ++-- .../__stories__/MenuItemNavigate.stories.tsx | 9 +-- .../__stories__/MenuItemSelect.stories.tsx | 6 +- .../MenuItemSelectAvatar.stories.tsx | 14 ++--- .../MenuItemSelectColor.stories.tsx | 9 ++- .../__stories__/MenuItemSelectTag.stories.tsx | 6 +- .../__stories__/MenuItemToggle.stories.tsx | 6 +- .../navigation/menu-item/components/index.ts | 15 +++++ .../src/navigation/menu-item/index.ts | 4 ++ .../components/MenuItemLeftContent.tsx | 4 +- .../components/StyledMenuItemBase.tsx | 2 +- .../menu-item/types/MenuItemAccent.ts | 0 packages/twenty-ui/src/utilities/index.ts | 1 + .../hooks/__tests__/isMobile.test.tsx | 10 ++++ .../utilities/responsive/hooks/useIsMobile.ts | 5 ++ .../twenty-ui/navigation/menu-item.mdx | 20 +++---- 106 files changed, 326 insertions(+), 256 deletions(-) create mode 100644 packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsToggleSettingCard.tsx rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItem.tsx (91%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemAvatar.tsx (86%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemCommand.tsx (96%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemCommandHotKeys.tsx (100%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemDraggable.tsx (94%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemMultiSelect.tsx (82%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemMultiSelectAvatar.tsx (92%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemMultiSelectTag.tsx (85%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemNavigate.tsx (93%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemSelect.tsx (96%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemSelectAvatar.tsx (94%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemSelectColor.tsx (92%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemSelectTag.tsx (91%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemSuggestion.tsx (95%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/MenuItemToggle.tsx (90%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/__stories__/MenuItem.stories.tsx (97%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/__stories__/MenuItemCommand.stories.tsx (97%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/__stories__/MenuItemDraggable.stories.tsx (97%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/__stories__/MenuItemMultiSelect.stories.tsx (96%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/__stories__/MenuItemMultiSelectAvatar.stories.tsx (90%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/__stories__/MenuItemNavigate.stories.tsx (96%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/__stories__/MenuItemSelect.stories.tsx (96%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/__stories__/MenuItemSelectAvatar.stories.tsx (90%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/__stories__/MenuItemSelectColor.stories.tsx (94%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/__stories__/MenuItemSelectTag.stories.tsx (97%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/components/__stories__/MenuItemToggle.stories.tsx (96%) create mode 100644 packages/twenty-ui/src/navigation/menu-item/components/index.ts create mode 100644 packages/twenty-ui/src/navigation/menu-item/index.ts rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/internals/components/MenuItemLeftContent.tsx (98%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/internals/components/StyledMenuItemBase.tsx (98%) rename packages/{twenty-front/src/modules/ui => twenty-ui/src}/navigation/menu-item/types/MenuItemAccent.ts (100%) create mode 100644 packages/twenty-ui/src/utilities/responsive/hooks/__tests__/isMobile.test.tsx create mode 100644 packages/twenty-ui/src/utilities/responsive/hooks/useIsMobile.ts diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx index 7bd2b0898..1a62cab35 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx @@ -10,10 +10,10 @@ import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDro import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; +import { MenuItem } from 'twenty-ui'; type StyledContainerProps = { position: PositionType; diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordShowActionMenuBarEntry.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordShowActionMenuBarEntry.tsx index db751adaa..b075565f4 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordShowActionMenuBarEntry.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordShowActionMenuBarEntry.tsx @@ -1,8 +1,7 @@ +import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { MOBILE_VIEWPORT } from 'twenty-ui'; -import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry'; -import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent'; +import { MOBILE_VIEWPORT, MenuItemAccent } from 'twenty-ui'; type RecordShowActionMenuBarEntryProps = { entry: ActionMenuEntry; diff --git a/packages/twenty-front/src/modules/action-menu/components/__stories__/RecordShowActionMenuBar.stories.tsx b/packages/twenty-front/src/modules/action-menu/components/__stories__/RecordShowActionMenuBar.stories.tsx index f2391b558..a1f4422b6 100644 --- a/packages/twenty-front/src/modules/action-menu/components/__stories__/RecordShowActionMenuBar.stories.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/__stories__/RecordShowActionMenuBar.stories.tsx @@ -8,13 +8,13 @@ import { ActionMenuComponentInstanceContext } from '@/action-menu/states/context import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry'; import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; -import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent'; import { userEvent, waitFor, within } from '@storybook/test'; import { ComponentDecorator, IconFileExport, IconHeart, IconTrash, + MenuItemAccent, } from 'twenty-ui'; const deleteMock = jest.fn(); diff --git a/packages/twenty-front/src/modules/action-menu/types/ActionMenuEntry.ts b/packages/twenty-front/src/modules/action-menu/types/ActionMenuEntry.ts index 7cef4b2fe..568bd3a33 100644 --- a/packages/twenty-front/src/modules/action-menu/types/ActionMenuEntry.ts +++ b/packages/twenty-front/src/modules/action-menu/types/ActionMenuEntry.ts @@ -1,7 +1,5 @@ import { MouseEvent, ReactNode } from 'react'; -import { IconComponent } from 'twenty-ui'; - -import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent'; +import { IconComponent, MenuItemAccent } from 'twenty-ui'; export type ActionMenuEntry = { type: 'standard' | 'workflow-run'; diff --git a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscriberDropdownAddSubscriberMenuItem.tsx b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscriberDropdownAddSubscriberMenuItem.tsx index b56cdc080..04de8d718 100644 --- a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscriberDropdownAddSubscriberMenuItem.tsx +++ b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscriberDropdownAddSubscriberMenuItem.tsx @@ -1,9 +1,8 @@ import { MessageThreadSubscriber } from '@/activities/emails/types/MessageThreadSubscriber'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord'; -import { MenuItemAvatar } from '@/ui/navigation/menu-item/components/MenuItemAvatar'; import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; -import { IconPlus } from 'twenty-ui'; +import { IconPlus, MenuItemAvatar } from 'twenty-ui'; export const MessageThreadSubscriberDropdownAddSubscriberMenuItem = ({ workspaceMember, diff --git a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersDropdownButton.tsx b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersDropdownButton.tsx index 7f7c42e7a..2fdf1d634 100644 --- a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersDropdownButton.tsx +++ b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersDropdownButton.tsx @@ -1,5 +1,5 @@ import { offset } from '@floating-ui/react'; -import { IconMinus, IconPlus } from 'twenty-ui'; +import { IconMinus, IconPlus, MenuItem, MenuItemAvatar } from 'twenty-ui'; import { MessageThreadSubscriberDropdownAddSubscriber } from '@/activities/emails/components/MessageThreadSubscriberDropdownAddSubscriber'; import { MessageThreadSubscribersChip } from '@/activities/emails/components/MessageThreadSubscribersChip'; @@ -10,8 +10,6 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useListenRightDrawerClose } from '@/ui/layout/right-drawer/hooks/useListenRightDrawerClose'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { MenuItemAvatar } from '@/ui/navigation/menu-item/components/MenuItemAvatar'; import { useState } from 'react'; export const MESSAGE_THREAD_SUBSCRIBER_DROPDOWN_ID = diff --git a/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx b/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx index a52aa4c91..128b4a205 100644 --- a/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx +++ b/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx @@ -4,13 +4,13 @@ import { IconPencil, IconTrash, LightIconButton, + MenuItem, } from 'twenty-ui'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; type AttachmentDropdownProps = { onDownload: () => void; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItem.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItem.tsx index e275f6c5c..0895015e2 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItem.tsx @@ -1,9 +1,8 @@ import { isNonEmptyString } from '@sniptt/guards'; import { useRecoilValue } from 'recoil'; -import { IconArrowUpRight, IconComponent } from 'twenty-ui'; +import { IconArrowUpRight, IconComponent, MenuItemCommand } from 'twenty-ui'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { MenuItemCommand } from '@/ui/navigation/menu-item/components/MenuItemCommand'; import { useCommandMenu } from '../hooks/useCommandMenu'; diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx index 2e82b72fc..6dc68a8cd 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx @@ -4,7 +4,6 @@ import { getOperandsForFilterDefinition } from '@/object-record/object-filter-dr import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; @@ -13,7 +12,13 @@ import { availableFilterDefinitionsComponentState } from '@/views/states/availab import { ViewFilterGroup } from '@/views/types/ViewFilterGroup'; import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator'; import { useCallback } from 'react'; -import { IconLibraryPlus, IconPlus, isDefined, LightButton } from 'twenty-ui'; +import { + IconLibraryPlus, + IconPlus, + isDefined, + LightButton, + MenuItem, +} from 'twenty-ui'; import { v4 } from 'uuid'; type AdvancedFilterAddFilterRuleSelectProps = { diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRuleOptionsDropdown.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRuleOptionsDropdown.tsx index d77747b87..f4b8af642 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRuleOptionsDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRuleOptionsDropdown.tsx @@ -4,10 +4,9 @@ import { useCurrentViewViewFilterGroup } from '@/object-record/advanced-filter/h import { useDeleteCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useDeleteCombinedViewFilterGroup'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId'; import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters'; -import { isDefined } from 'twenty-ui'; +import { isDefined, MenuItem } from 'twenty-ui'; type AdvancedFilterRuleOptionsDropdownProps = | { diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterOperandSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterOperandSelect.tsx index e34dd713f..17ddc5c64 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterOperandSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterOperandSelect.tsx @@ -6,12 +6,11 @@ import { SelectControl } from '@/ui/input/components/SelectControl'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId'; import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import styled from '@emotion/styled'; -import { isDefined } from 'twenty-ui'; +import { isDefined, MenuItem } from 'twenty-ui'; const StyledContainer = styled.div` flex: 1; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/AdvancedFilterButton.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/AdvancedFilterButton.tsx index ee96509bb..542aed2e1 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/AdvancedFilterButton.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/AdvancedFilterButton.tsx @@ -3,8 +3,6 @@ import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filte import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId'; import { getOperandsForFilterDefinition } from '@/object-record/object-filter-dropdown/utils/getOperandsForFilterType'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItemLeftContent } from '@/ui/navigation/menu-item/internals/components/MenuItemLeftContent'; -import { StyledMenuItemBase } from '@/ui/navigation/menu-item/internals/components/StyledMenuItemBase'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; @@ -12,7 +10,12 @@ import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedVie import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState'; import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator'; import styled from '@emotion/styled'; -import { IconFilter, Pill } from 'twenty-ui'; +import { + IconFilter, + MenuItemLeftContent, + Pill, + StyledMenuItemBase, +} from 'twenty-ui'; import { v4 } from 'uuid'; export const StyledContainer = styled.div` diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx index adeaaa1cd..c364bb90e 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx @@ -12,11 +12,16 @@ import { getOperandsForFilterDefinition } from '@/object-record/object-filter-dr import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useState } from 'react'; import { useRecoilValue } from 'recoil'; -import { IconApps, IconChevronLeft, isDefined, useIcons } from 'twenty-ui'; +import { + IconApps, + IconChevronLeft, + isDefined, + MenuItem, + useIcons, +} from 'twenty-ui'; export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => { const [searchText] = useState(''); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx index b6025cddd..84f9addb4 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx @@ -13,11 +13,10 @@ import { getOperandsForFilterDefinition } from '@/object-record/object-filter-dr import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField'; import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useRecoilValue } from 'recoil'; -import { useIcons } from 'twenty-ui'; +import { MenuItemSelect, useIcons } from 'twenty-ui'; export type ObjectFilterDropdownFilterSelectMenuItemProps = { filterDefinition: FilterDefinition; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect.tsx index b33fcbc16..ec90ebe93 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect.tsx @@ -3,8 +3,8 @@ import { v4 } from 'uuid'; import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; +import { MenuItem } from 'twenty-ui'; import { isDefined } from '~/utils/isDefined'; import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue'; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx index 6bac9e530..13a32ca4b 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx @@ -13,10 +13,10 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { MenuItemMultiSelect } from '@/ui/navigation/menu-item/components/MenuItemMultiSelect'; + import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; +import { MenuItem, MenuItemMultiSelect } from 'twenty-ui'; import { isDefined } from '~/utils/isDefined'; export const EMPTY_FILTER_VALUE = ''; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordRemoveFilterMenuItem.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordRemoveFilterMenuItem.tsx index 74f3ed364..7251fab80 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordRemoveFilterMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordRemoveFilterMenuItem.tsx @@ -1,9 +1,8 @@ -import { IconFilterOff } from 'twenty-ui'; +import { IconFilterOff, MenuItem } from 'twenty-ui'; import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; export const ObjectFilterDropdownRecordRemoveFilterMenuItem = () => { const { emptyFilterButKeepDefinition } = useFilterDropdown(); diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx index d6c04c398..f069f854a 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx @@ -1,6 +1,6 @@ import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; -import { IconChevronDown, useIcons } from 'twenty-ui'; +import { IconChevronDown, MenuItem, useIcons } from 'twenty-ui'; import { OBJECT_SORT_DROPDOWN_ID } from '@/object-record/object-sort-dropdown/constants/ObjectSortDropdownId'; import { useObjectSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useObjectSortDropdown'; @@ -14,7 +14,6 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu.tsx index ec0152f88..bfbfb7e9d 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu.tsx @@ -4,8 +4,8 @@ import { useCallback, useRef } from 'react'; import { useRecordGroupActions } from '@/object-record/record-group/hooks/useRecordGroupActions'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; +import { MenuItem } from 'twenty-ui'; const StyledMenuContainer = styled.div` position: absolute; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiItemFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiItemFieldInput.tsx index 5cea5ffe7..dd383e347 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiItemFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiItemFieldInput.tsx @@ -1,7 +1,7 @@ import styled from '@emotion/styled'; import React, { useRef, useState } from 'react'; import { Key } from 'ts-key-enum'; -import { IconCheck, IconPlus, LightIconButton } from 'twenty-ui'; +import { IconCheck, IconPlus, LightIconButton, MenuItem } from 'twenty-ui'; import { PhoneRecord } from '@/object-record/record-field/types/FieldMetadata'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; @@ -11,7 +11,6 @@ import { } from '@/ui/layout/dropdown/components/DropdownMenuInput'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { FieldMetadataType } from '~/generated-metadata/graphql'; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiItemFieldMenuItem.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiItemFieldMenuItem.tsx index 150efe882..b980ff0bc 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiItemFieldMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiItemFieldMenuItem.tsx @@ -1,6 +1,5 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { MenuItemWithOptionDropdown } from '@/ui/navigation/menu-item/components/MenuItemWithOptionDropdown'; import { useState } from 'react'; import { @@ -8,6 +7,7 @@ import { IconBookmarkPlus, IconPencil, IconTrash, + MenuItem, } from 'twenty-ui'; type MultiItemFieldMenuItemProps = { diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiSelectFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiSelectFieldInput.tsx index 7ebe1951e..acebdcda9 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiSelectFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiSelectFieldInput.tsx @@ -12,9 +12,9 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { MenuItemMultiSelectTag } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectTag'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; +import { MenuItemMultiSelectTag } from 'twenty-ui'; import { isDefined } from '~/utils/isDefined'; import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly'; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem.tsx index facb36608..effa5ce3a 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem.tsx @@ -1,8 +1,7 @@ import { RecordGroupDefinitionType } from '@/object-record/record-group/types/RecordGroupDefinition'; import { useRecordIndexPageKanbanAddMenuItem } from '@/object-record/record-index/hooks/useRecordIndexPageKanbanAddMenuItem'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import styled from '@emotion/styled'; -import { Tag } from 'twenty-ui'; +import { MenuItem, Tag } from 'twenty-ui'; const StyledMenuItem = styled(MenuItem)` width: calc(100% - 2 * var(--horizontal-padding)); diff --git a/packages/twenty-front/src/modules/object-record/record-index/options/components/RecordIndexOptionsDropdownContent.tsx b/packages/twenty-front/src/modules/object-record/record-index/options/components/RecordIndexOptionsDropdownContent.tsx index 67cfa73d5..1ed208d4e 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/options/components/RecordIndexOptionsDropdownContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/options/components/RecordIndexOptionsDropdownContent.tsx @@ -9,6 +9,9 @@ import { IconRotate2, IconSettings, IconTag, + MenuItem, + MenuItemNavigate, + MenuItemToggle, UndecoratedLink, useIcons, } from 'twenty-ui'; @@ -36,9 +39,6 @@ import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenu import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { MenuItemNavigate } from '@/ui/navigation/menu-item/components/MenuItemNavigate'; -import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; diff --git a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationRecordsListItem.tsx b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationRecordsListItem.tsx index b1f16b08c..899c0ddd3 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationRecordsListItem.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationRecordsListItem.tsx @@ -10,6 +10,7 @@ import { IconTrash, IconUnlink, LightIconButton, + MenuItem, } from 'twenty-ui'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; @@ -38,7 +39,6 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { RelationDefinitionType } from '~/generated-metadata/graphql'; const StyledListItem = styled(RecordDetailRecordsListItem)<{ diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableColumnHeadDropdownMenu.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableColumnHeadDropdownMenu.tsx index ca64ae1ec..124d9a388 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableColumnHeadDropdownMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableColumnHeadDropdownMenu.tsx @@ -4,13 +4,13 @@ import { IconEyeOff, IconFilter, IconSortDescending, + MenuItem, } from 'twenty-ui'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { onToggleColumnFilterComponentState } from '@/object-record/record-table/states/onToggleColumnFilterComponentState'; import { onToggleColumnSortComponentState } from '@/object-record/record-table/states/onToggleColumnSortComponentState'; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderPlusButtonContent.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderPlusButtonContent.tsx index 650d574ae..6ffb331a0 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderPlusButtonContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderPlusButtonContent.tsx @@ -1,7 +1,7 @@ import { useCallback, useContext } from 'react'; import { useLocation } from 'react-router-dom'; import { useSetRecoilState } from 'recoil'; -import { IconSettings, UndecoratedLink, useIcons } from 'twenty-ui'; +import { IconSettings, MenuItem, UndecoratedLink, useIcons } from 'twenty-ui'; import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; @@ -12,7 +12,6 @@ import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefin import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/MultipleObjectRecordSelectItem.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/MultipleObjectRecordSelectItem.tsx index 0f2356e63..dbc0302b9 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/MultipleObjectRecordSelectItem.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/MultipleObjectRecordSelectItem.tsx @@ -1,13 +1,12 @@ import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; -import { Avatar } from 'twenty-ui'; +import { Avatar, MenuItemMultiSelectAvatar } from 'twenty-ui'; import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useObjectRecordMultiSelectScopedStates'; import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordSelectSelectableListId'; import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { isDefined } from '~/utils/isDefined'; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SelectableMenuItemSelect.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SelectableMenuItemSelect.tsx index eef1523d1..986a570e8 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SelectableMenuItemSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SelectableMenuItemSelect.tsx @@ -1,11 +1,10 @@ import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; -import { Avatar } from 'twenty-ui'; +import { Avatar, MenuItemSelectAvatar } from 'twenty-ui'; import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar'; type SelectableMenuItemSelectProps = { entity: EntityForSelect; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx index 50db8588c..8367ca8ce 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx @@ -2,7 +2,7 @@ import { isNonEmptyString } from '@sniptt/guards'; import { Fragment, useRef } from 'react'; import { useRecoilValue } from 'recoil'; import { Key } from 'ts-key-enum'; -import { IconComponent, IconPlus } from 'twenty-ui'; +import { IconComponent, IconPlus, MenuItem, MenuItemSelect } from 'twenty-ui'; import { SelectableMenuItemSelect } from '@/object-record/relation-picker/components/SelectableMenuItemSelect'; import { SINGLE_ENTITY_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleEntitySelectBaseList'; @@ -12,8 +12,6 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { isDefined } from '~/utils/isDefined'; diff --git a/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx b/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx index fba130110..3a96cc523 100644 --- a/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx @@ -2,7 +2,7 @@ import styled from '@emotion/styled'; import { useEffect, useState } from 'react'; import { useRecoilValue } from 'recoil'; import { Key } from 'ts-key-enum'; -import { AvatarChip } from 'twenty-ui'; +import { AvatarChip, MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui'; import { SelectableItem } from '@/object-record/select/types/SelectableItem'; import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem'; @@ -11,8 +11,6 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; const StyledAvatarChip = styled(AvatarChip)` diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelDetails.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelDetails.tsx index 5d0fab05d..7232d712d 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelDetails.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelDetails.tsx @@ -5,7 +5,7 @@ import { SettingsAccountsEventVisibilitySettingsCard } from '@/settings/accounts import { SettingsOptionCardContent } from '@/settings/components/SettingsOptionCardContent'; import styled from '@emotion/styled'; import { Section } from '@react-email/components'; -import { H2Title, Toggle, Card } from 'twenty-ui'; +import { Card, H2Title, Toggle } from 'twenty-ui'; import { CalendarChannelVisibility } from '~/generated-metadata/graphql'; const StyledDetailsContainer = styled.div` diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRowDropdownMenu.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRowDropdownMenu.tsx index 743d191a6..de3da4661 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRowDropdownMenu.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRowDropdownMenu.tsx @@ -6,6 +6,7 @@ import { IconRefresh, IconTrash, LightIconButton, + MenuItem, } from 'twenty-ui'; import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; @@ -16,7 +17,6 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; type SettingsAccountsRowDropdownMenuProps = { account: ConnectedAccount; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsToggleSettingCard.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsToggleSettingCard.tsx new file mode 100644 index 000000000..d5d5cfc90 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsToggleSettingCard.tsx @@ -0,0 +1,60 @@ +import styled from '@emotion/styled'; + +import { Card, CardContent, Toggle } from 'twenty-ui'; + +type Parameter = { + value: boolean; + title: string; + description: string; + onToggle: (value: boolean) => void; +}; + +type SettingsAccountsToggleSettingCardProps = { + parameters: Parameter[]; +}; + +const StyledCardContent = styled(CardContent)` + align-items: center; + display: flex; + gap: ${({ theme }) => theme.spacing(4)}; + cursor: pointer; + + &:hover { + background: ${({ theme }) => theme.background.transparent.lighter}; + } +`; + +const StyledTitle = styled.div` + color: ${({ theme }) => theme.font.color.primary}; + font-weight: ${({ theme }) => theme.font.weight.medium}; + margin-bottom: ${({ theme }) => theme.spacing(2)}; +`; + +const StyledDescription = styled.div` + color: ${({ theme }) => theme.font.color.tertiary}; + font-size: ${({ theme }) => theme.font.size.sm}; +`; + +const StyledToggle = styled(Toggle)` + margin-left: auto; +`; + +export const SettingsAccountsToggleSettingCard = ({ + parameters, +}: SettingsAccountsToggleSettingCardProps) => ( + + {parameters.map((parameter, index) => ( + parameter.onToggle(!parameter.value)} + > +
+ {parameter.title} + {parameter.description} +
+ +
+ ))} +
+); diff --git a/packages/twenty-front/src/modules/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown.tsx b/packages/twenty-front/src/modules/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown.tsx index 4bdf2ee86..a927dba42 100644 --- a/packages/twenty-front/src/modules/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown.tsx @@ -3,7 +3,6 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { @@ -12,7 +11,7 @@ import { useParams, useSearchParams, } from 'react-router-dom'; -import { Button, IconChevronDown, isDefined } from 'twenty-ui'; +import { Button, IconChevronDown, isDefined, MenuItem } from 'twenty-ui'; const StyledContainer = styled.div` align-items: center; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx index e41c012c1..fd169354b 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx @@ -10,6 +10,8 @@ import { IconX, LightIconButton, MAIN_COLOR_NAMES, + MenuItem, + MenuItemSelectColor, } from 'twenty-ui'; import { v4 } from 'uuid'; @@ -22,8 +24,6 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { MenuItemSelectColor } from '@/ui/navigation/menu-item/components/MenuItemSelectColor'; import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState'; import { AnimatePresence, motion } from 'framer-motion'; import { useRecoilValue } from 'recoil'; diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldActiveActionDropdown.tsx b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldActiveActionDropdown.tsx index 685198769..4391d783d 100644 --- a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldActiveActionDropdown.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldActiveActionDropdown.tsx @@ -5,13 +5,13 @@ import { IconPencil, IconTextSize, LightIconButton, + MenuItem, } from 'twenty-ui'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; type SettingsObjectFieldActiveActionDropdownProps = { isCustomField?: boolean; diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDisabledActionDropdown.tsx b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDisabledActionDropdown.tsx index 0dcd5be86..fc5384fbe 100644 --- a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDisabledActionDropdown.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDisabledActionDropdown.tsx @@ -5,13 +5,13 @@ import { IconPencil, IconTrash, LightIconButton, + MenuItem, } from 'twenty-ui'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { FieldMetadataType } from '~/generated-metadata/graphql'; type SettingsObjectFieldInactiveActionDropdownProps = { diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectSummaryCard.tsx b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectSummaryCard.tsx index 6a0eb4823..f818cded8 100644 --- a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectSummaryCard.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectSummaryCard.tsx @@ -5,6 +5,7 @@ import { IconDotsVertical, IconPencil, LightIconButton, + MenuItem, useIcons, } from 'twenty-ui'; @@ -18,7 +19,6 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; type SettingsObjectSummaryCardProps = { objectMetadataItem: ObjectMetadataItem; diff --git a/packages/twenty-front/src/modules/settings/data-model/objects/components/SettingsObjectInactiveMenuDropDown.tsx b/packages/twenty-front/src/modules/settings/data-model/objects/components/SettingsObjectInactiveMenuDropDown.tsx index 7761b11b7..ab99c9a61 100644 --- a/packages/twenty-front/src/modules/settings/data-model/objects/components/SettingsObjectInactiveMenuDropDown.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/objects/components/SettingsObjectInactiveMenuDropDown.tsx @@ -3,13 +3,13 @@ import { IconDotsVertical, IconTrash, LightIconButton, + MenuItem, } from 'twenty-ui'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; type SettingsObjectInactiveMenuDropDownProps = { isCustomObject: boolean; diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard.tsx index b5d217790..8d0b1bbc1 100644 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard.tsx +++ b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard.tsx @@ -3,13 +3,13 @@ import { SettingsIntegrationDatabaseConnectionSyncStatus } from '@/settings/inte import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import styled from '@emotion/styled'; import { IconDotsVertical, IconPencil, IconTrash, LightIconButton, + MenuItem, UndecoratedLink, } from 'twenty-ui'; diff --git a/packages/twenty-front/src/modules/settings/security/components/SettingsSecurityOptionsList.tsx b/packages/twenty-front/src/modules/settings/security/components/SettingsSecurityOptionsList.tsx index 73c8aa42d..f7b9dc2ad 100644 --- a/packages/twenty-front/src/modules/settings/security/components/SettingsSecurityOptionsList.tsx +++ b/packages/twenty-front/src/modules/settings/security/components/SettingsSecurityOptionsList.tsx @@ -4,7 +4,7 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import styled from '@emotion/styled'; import { useRecoilState } from 'recoil'; -import { IconLink, Toggle, Card } from 'twenty-ui'; +import { Card, IconLink, Toggle } from 'twenty-ui'; import { useUpdateWorkspaceMutation } from '~/generated/graphql'; const StyledToggle = styled(Toggle)` diff --git a/packages/twenty-front/src/modules/settings/security/components/SettingsSecuritySSORowDropdownMenu.tsx b/packages/twenty-front/src/modules/settings/security/components/SettingsSecuritySSORowDropdownMenu.tsx index 3212c6dec..55b43525d 100644 --- a/packages/twenty-front/src/modules/settings/security/components/SettingsSecuritySSORowDropdownMenu.tsx +++ b/packages/twenty-front/src/modules/settings/security/components/SettingsSecuritySSORowDropdownMenu.tsx @@ -5,6 +5,7 @@ import { IconDotsVertical, IconTrash, LightIconButton, + MenuItem, } from 'twenty-ui'; import { useDeleteSSOIdentityProvider } from '@/settings/security/hooks/useDeleteSSOIdentityProvider'; @@ -16,7 +17,6 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { UnwrapRecoilValue } from 'recoil'; import { SsoIdentityProviderStatus } from '~/generated/graphql'; import { isDefined } from '~/utils/isDefined'; diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTabEnvironmentVariableTableRow.tsx b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTabEnvironmentVariableTableRow.tsx index eb8e380b7..e39a9c38b 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTabEnvironmentVariableTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTabEnvironmentVariableTableRow.tsx @@ -6,7 +6,6 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { TableCell } from '@/ui/layout/table/components/TableCell'; import { TableRow } from '@/ui/layout/table/components/TableRow'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import styled from '@emotion/styled'; import { useState } from 'react'; import { @@ -16,6 +15,7 @@ import { IconTrash, IconX, LightIconButton, + MenuItem, OverflowingTextWithTooltip, } from 'twenty-ui'; diff --git a/packages/twenty-front/src/modules/spreadsheet-import/components/MatchColumnSelect.tsx b/packages/twenty-front/src/modules/spreadsheet-import/components/MatchColumnSelect.tsx index 9908d66a9..217ab31d5 100644 --- a/packages/twenty-front/src/modules/spreadsheet-import/components/MatchColumnSelect.tsx +++ b/packages/twenty-front/src/modules/spreadsheet-import/components/MatchColumnSelect.tsx @@ -1,5 +1,3 @@ -import React, { useCallback, useRef, useState } from 'react'; -import { createPortal } from 'react-dom'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { @@ -9,7 +7,9 @@ import { size, useFloating, } from '@floating-ui/react'; -import { AppTooltip } from 'twenty-ui'; +import React, { useCallback, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { AppTooltip, MenuItem, MenuItemSelect } from 'twenty-ui'; import { ReadonlyDeep } from 'type-fest'; import { useDebouncedCallback } from 'use-debounce'; @@ -18,8 +18,6 @@ import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useUpdateEffect } from '~/hooks/useUpdateEffect'; diff --git a/packages/twenty-front/src/modules/support/components/SupportDropdown.tsx b/packages/twenty-front/src/modules/support/components/SupportDropdown.tsx index ff2399678..ee7b6886e 100644 --- a/packages/twenty-front/src/modules/support/components/SupportDropdown.tsx +++ b/packages/twenty-front/src/modules/support/components/SupportDropdown.tsx @@ -3,8 +3,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { IconHelpCircle, IconMessage } from 'twenty-ui'; +import { IconHelpCircle, IconMessage, MenuItem } from 'twenty-ui'; export const SupportDropdown = () => { const dropdownId = `support-field-active-action-dropdown`; diff --git a/packages/twenty-front/src/modules/ui/input/components/Select.tsx b/packages/twenty-front/src/modules/ui/input/components/Select.tsx index 54c9b1a79..bbf53e668 100644 --- a/packages/twenty-front/src/modules/ui/input/components/Select.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/Select.tsx @@ -1,13 +1,12 @@ import styled from '@emotion/styled'; import { MouseEvent, useMemo, useRef, useState } from 'react'; -import { IconComponent } from 'twenty-ui'; +import { IconComponent, MenuItem } from 'twenty-ui'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { SelectControl } from '@/ui/input/components/SelectControl'; import { isDefined } from '~/utils/isDefined'; diff --git a/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx b/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx index 67c9ffa03..bc6faecfd 100644 --- a/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx @@ -6,7 +6,6 @@ import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { MenuItemSelectTag } from '@/ui/navigation/menu-item/components/MenuItemSelectTag'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useTheme } from '@emotion/react'; @@ -20,7 +19,7 @@ import { } from '@floating-ui/react'; import { useEffect, useMemo, useRef, useState } from 'react'; import { Key } from 'ts-key-enum'; -import { TagColor, isDefined } from 'twenty-ui'; +import { MenuItemSelectTag, TagColor, isDefined } from 'twenty-ui'; const StyledRelationPickerContainer = styled.div` left: -1px; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/currency/components/CurrencyPickerDropdownSelect.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/currency/components/CurrencyPickerDropdownSelect.tsx index 7dfeaebe7..0762391bf 100644 --- a/packages/twenty-front/src/modules/ui/input/components/internal/currency/components/CurrencyPickerDropdownSelect.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/internal/currency/components/CurrencyPickerDropdownSelect.tsx @@ -4,9 +4,8 @@ import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar'; +import { MenuItem, MenuItemSelectAvatar } from 'twenty-ui'; import { Currency } from './CurrencyPickerDropdownButton'; export const CurrencyPickerDropdownSelect = ({ diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/InternalDatePicker.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/InternalDatePicker.tsx index 77475ce63..e5f16a69d 100644 --- a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/InternalDatePicker.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/InternalDatePicker.tsx @@ -2,12 +2,15 @@ import styled from '@emotion/styled'; import { DateTime } from 'luxon'; import ReactDatePicker from 'react-datepicker'; import { Key } from 'ts-key-enum'; -import { IconCalendarX, OVERLAY_BACKGROUND } from 'twenty-ui'; +import { + IconCalendarX, + MenuItemLeftContent, + OVERLAY_BACKGROUND, + StyledHoverableMenuItemBase, +} from 'twenty-ui'; import { DateTimeInput } from '@/ui/input/components/internal/date/components/DateTimeInput'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItemLeftContent } from '@/ui/navigation/menu-item/internals/components/MenuItemLeftContent'; -import { StyledHoverableMenuItemBase } from '@/ui/navigation/menu-item/internals/components/StyledMenuItemBase'; import { isDefined } from '~/utils/isDefined'; import { AbsoluteDatePickerHeader } from '@/ui/input/components/internal/date/components/AbsoluteDatePickerHeader'; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/phone/components/PhoneCountryPickerDropdownSelect.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/phone/components/PhoneCountryPickerDropdownSelect.tsx index d5f46a71a..5f1bf9618 100644 --- a/packages/twenty-front/src/modules/ui/input/components/internal/phone/components/PhoneCountryPickerDropdownSelect.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/internal/phone/components/PhoneCountryPickerDropdownSelect.tsx @@ -1,15 +1,14 @@ -import { useMemo, useState } from 'react'; import styled from '@emotion/styled'; +import { useMemo, useState } from 'react'; import { Country } from '@/ui/input/components/internal/types/Country'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar'; import 'react-phone-number-input/style.css'; +import { MenuItem, MenuItemSelectAvatar } from 'twenty-ui'; const StyledIconContainer = styled.div` align-items: center; diff --git a/packages/twenty-front/src/modules/ui/input/editor/components/CustomSlashMenu.tsx b/packages/twenty-front/src/modules/ui/input/editor/components/CustomSlashMenu.tsx index 6a7fd920e..7850cff2e 100644 --- a/packages/twenty-front/src/modules/ui/input/editor/components/CustomSlashMenu.tsx +++ b/packages/twenty-front/src/modules/ui/input/editor/components/CustomSlashMenu.tsx @@ -1,10 +1,9 @@ import { SuggestionMenuProps } from '@blocknote/react'; import styled from '@emotion/styled'; -import { IconComponent } from 'twenty-ui'; +import { IconComponent, MenuItemSuggestion } from 'twenty-ui'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { MenuItemSuggestion } from '@/ui/navigation/menu-item/components/MenuItemSuggestion'; export type SuggestionItem = { title: string; diff --git a/packages/twenty-front/src/modules/ui/input/relation-picker/components/CreateNewButton.tsx b/packages/twenty-front/src/modules/ui/input/relation-picker/components/CreateNewButton.tsx index 9cdade903..378ed8a87 100644 --- a/packages/twenty-front/src/modules/ui/input/relation-picker/components/CreateNewButton.tsx +++ b/packages/twenty-front/src/modules/ui/input/relation-picker/components/CreateNewButton.tsx @@ -1,7 +1,6 @@ import { css } from '@emotion/react'; import styled from '@emotion/styled'; - -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; +import { MenuItem } from 'twenty-ui'; const StyledCreateNewButton = styled(MenuItem)<{ hovered?: boolean }>` ${({ hovered, theme }) => diff --git a/packages/twenty-front/src/modules/ui/layout/draggable-list/components/__stories__/DraggableItem.stories.tsx b/packages/twenty-front/src/modules/ui/layout/draggable-list/components/__stories__/DraggableItem.stories.tsx index 810be399c..89b0e982f 100644 --- a/packages/twenty-front/src/modules/ui/layout/draggable-list/components/__stories__/DraggableItem.stories.tsx +++ b/packages/twenty-front/src/modules/ui/layout/draggable-list/components/__stories__/DraggableItem.stories.tsx @@ -1,9 +1,8 @@ import { DragDropContext, Droppable } from '@hello-pangea/dnd'; import { Meta, StoryObj } from '@storybook/react'; -import { ComponentDecorator, IconBell } from 'twenty-ui'; +import { ComponentDecorator, IconBell, MenuItemDraggable } from 'twenty-ui'; import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem'; -import { MenuItemDraggable } from '@/ui/navigation/menu-item/components/MenuItemDraggable'; const meta: Meta = { title: 'UI/Layout/DraggableList/DraggableItem', diff --git a/packages/twenty-front/src/modules/ui/layout/draggable-list/components/__stories__/DraggableList.stories.tsx b/packages/twenty-front/src/modules/ui/layout/draggable-list/components/__stories__/DraggableList.stories.tsx index 813481958..dc55480af 100644 --- a/packages/twenty-front/src/modules/ui/layout/draggable-list/components/__stories__/DraggableList.stories.tsx +++ b/packages/twenty-front/src/modules/ui/layout/draggable-list/components/__stories__/DraggableList.stories.tsx @@ -1,10 +1,8 @@ -import { action } from '@storybook/addon-actions'; -import { Meta, StoryObj } from '@storybook/react'; -import { ComponentDecorator, IconBell } from 'twenty-ui'; - import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem'; import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList'; -import { MenuItemDraggable } from '@/ui/navigation/menu-item/components/MenuItemDraggable'; +import { action } from '@storybook/addon-actions'; +import { Meta, StoryObj } from '@storybook/react'; +import { ComponentDecorator, IconBell, MenuItemDraggable } from 'twenty-ui'; const meta: Meta = { title: 'UI/Layout/DraggableList/DraggableList', diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenu.stories.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenu.stories.tsx index 73b1c068a..142608390 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenu.stories.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenu.stories.tsx @@ -3,12 +3,16 @@ import { Decorator, Meta, StoryObj } from '@storybook/react'; import { expect, userEvent, waitFor, within } from '@storybook/test'; import { PlayFunction } from '@storybook/types'; import { useState } from 'react'; -import { Avatar, Button, ComponentDecorator } from 'twenty-ui'; +import { + Avatar, + Button, + ComponentDecorator, + MenuItem, + MenuItemMultiSelectAvatar, + MenuItemSelectAvatar, +} from 'twenty-ui'; import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar'; -import { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar'; import { Dropdown } from '../Dropdown'; import { DropdownMenuHeader } from '../DropdownMenuHeader'; diff --git a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx index eb0373618..2f4bae71a 100644 --- a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx +++ b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx @@ -1,5 +1,11 @@ import styled from '@emotion/styled'; -import { IconButton, IconCheckbox, IconNotes, IconPlus } from 'twenty-ui'; +import { + IconButton, + IconCheckbox, + IconNotes, + IconPlus, + MenuItem, +} from 'twenty-ui'; import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer'; import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity'; @@ -7,7 +13,6 @@ import { PageHotkeyScope } from '@/types/PageHotkeyScope'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { SHOW_PAGE_ADD_BUTTON_DROPDOWN_ID } from '@/ui/layout/show-page/constants/ShowPageAddButtonDropdownId'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { Dropdown } from '../../dropdown/components/Dropdown'; diff --git a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageMoreButton.tsx b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageMoreButton.tsx index 8cf27704d..9c82c08b6 100644 --- a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageMoreButton.tsx +++ b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageMoreButton.tsx @@ -6,13 +6,13 @@ import { IconDotsVertical, IconRestore, IconTrash, + MenuItem, } from 'twenty-ui'; import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord'; import { PageHotkeyScope } from '@/types/PageHotkeyScope'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; import { useDestroyOneRecord } from '@/object-record/hooks/useDestroyOneRecord'; diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemWithOptionDropdown.tsx b/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemWithOptionDropdown.tsx index a00ee075f..d1196392f 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemWithOptionDropdown.tsx +++ b/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemWithOptionDropdown.tsx @@ -1,3 +1,5 @@ +import { SelectHotkeyScope } from '@/ui/input/types/SelectHotkeyScope'; +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { useTheme } from '@emotion/react'; import { FunctionComponent, MouseEvent, ReactElement, ReactNode } from 'react'; import { @@ -6,16 +8,11 @@ import { IconDotsVertical, LightIconButton, LightIconButtonProps, -} from 'twenty-ui'; - -import { SelectHotkeyScope } from '@/ui/input/types/SelectHotkeyScope'; -import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; -import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent'; -import { + MenuItemAccent, + MenuItemLeftContent, StyledHoverableMenuItemBase, StyledMenuItemLeftContent, -} from '../internals/components/StyledMenuItemBase'; -import { MenuItemAccent } from '../types/MenuItemAccent'; +} from 'twenty-ui'; export type MenuItemIconButton = { Wrapper?: FunctionComponent<{ iconButton: ReactElement }>; diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdownButton.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdownButton.tsx index fd1cc17bd..3a03e32f7 100644 --- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdownButton.tsx +++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdownButton.tsx @@ -3,7 +3,6 @@ import { Workspaces } from '@/auth/states/workspaces'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar'; import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper'; import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo'; import { MULTI_WORKSPACE_DROPDOWN_ID } from '@/ui/navigation/navigation-drawer/constants/MulitWorkspaceDropdownId'; @@ -14,7 +13,7 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useState } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; -import { IconChevronDown } from 'twenty-ui'; +import { IconChevronDown, MenuItemSelectAvatar } from 'twenty-ui'; import { getImageAbsoluteURI } from '~/utils/image/getImageAbsoluteURI'; const StyledLogo = styled.div<{ logo: string }>` diff --git a/packages/twenty-front/src/modules/views/components/UpdateViewButtonGroup.tsx b/packages/twenty-front/src/modules/views/components/UpdateViewButtonGroup.tsx index 958e29ff9..e9001faf7 100644 --- a/packages/twenty-front/src/modules/views/components/UpdateViewButtonGroup.tsx +++ b/packages/twenty-front/src/modules/views/components/UpdateViewButtonGroup.tsx @@ -1,10 +1,15 @@ import styled from '@emotion/styled'; -import { Button, ButtonGroup, IconChevronDown, IconPlus } from 'twenty-ui'; +import { + Button, + ButtonGroup, + IconChevronDown, + IconPlus, + MenuItem, +} from 'twenty-ui'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; diff --git a/packages/twenty-front/src/modules/views/components/ViewFieldsVisibilityDropdownSection.tsx b/packages/twenty-front/src/modules/views/components/ViewFieldsVisibilityDropdownSection.tsx index d8ba50b09..15f3caa60 100644 --- a/packages/twenty-front/src/modules/views/components/ViewFieldsVisibilityDropdownSection.tsx +++ b/packages/twenty-front/src/modules/views/components/ViewFieldsVisibilityDropdownSection.tsx @@ -10,6 +10,7 @@ import { IconEye, IconEyeOff, IconInfoCircle, + MenuItemDraggable, useIcons, } from 'twenty-ui'; @@ -19,7 +20,6 @@ import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableIt import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { StyledDropdownMenuSubheader } from '@/ui/layout/dropdown/components/StyledDropdownMenuSubheader'; -import { MenuItemDraggable } from '@/ui/navigation/menu-item/components/MenuItemDraggable'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { groupArrayItemsBy } from '~/utils/array/groupArrayItemsBy'; import { isDefined } from '~/utils/isDefined'; diff --git a/packages/twenty-front/src/modules/views/components/ViewGroupsVisibilityDropdownSection.tsx b/packages/twenty-front/src/modules/views/components/ViewGroupsVisibilityDropdownSection.tsx index c7040f9ff..795639f51 100644 --- a/packages/twenty-front/src/modules/views/components/ViewGroupsVisibilityDropdownSection.tsx +++ b/packages/twenty-front/src/modules/views/components/ViewGroupsVisibilityDropdownSection.tsx @@ -4,7 +4,7 @@ import { ResponderProvided, } from '@hello-pangea/dnd'; import { useRef } from 'react'; -import { IconEye, IconEyeOff, Tag } from 'twenty-ui'; +import { IconEye, IconEyeOff, MenuItemDraggable, Tag } from 'twenty-ui'; import { RecordGroupDefinition, @@ -14,7 +14,6 @@ import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableIt import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { StyledDropdownMenuSubheader } from '@/ui/layout/dropdown/components/StyledDropdownMenuSubheader'; -import { MenuItemDraggable } from '@/ui/navigation/menu-item/components/MenuItemDraggable'; import { isDefined } from '~/utils/isDefined'; type ViewGroupsVisibilityDropdownSectionProps = { diff --git a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerListContent.tsx b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerListContent.tsx index f63cdc10d..002004488 100644 --- a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerListContent.tsx +++ b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerListContent.tsx @@ -6,6 +6,8 @@ import { IconPencil, IconPlus, LightIconButtonAccent, + MenuItem, + MenuItemDraggable, useIcons, } from 'twenty-ui'; @@ -14,8 +16,6 @@ import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableLi import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { MenuItemDraggable } from '@/ui/navigation/menu-item/components/MenuItemDraggable'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useChangeView } from '@/views/hooks/useChangeView'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; diff --git a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectActionContent.tsx b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectActionContent.tsx index c3ac8483c..0d13a0b39 100644 --- a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectActionContent.tsx +++ b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectActionContent.tsx @@ -1,8 +1,8 @@ -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { ACTIONS } from '@/workflow/constants/Actions'; import { useCreateStep } from '@/workflow/hooks/useCreateStep'; import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; import styled from '@emotion/styled'; +import { MenuItem } from 'twenty-ui'; const StyledActionListContainer = styled.div` display: flex; diff --git a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectTriggerTypeContent.tsx b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectTriggerTypeContent.tsx index daf169c27..abe47dd1e 100644 --- a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectTriggerTypeContent.tsx +++ b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectTriggerTypeContent.tsx @@ -1,7 +1,6 @@ import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer'; import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { TRIGGER_STEP_ID } from '@/workflow/constants/TriggerStepId'; import { TRIGGER_TYPES } from '@/workflow/constants/TriggerTypes'; import { useUpdateWorkflowVersionTrigger } from '@/workflow/hooks/useUpdateWorkflowVersionTrigger'; @@ -10,6 +9,7 @@ import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; import { getTriggerDefaultDefinition } from '@/workflow/utils/getTriggerDefaultDefinition'; import styled from '@emotion/styled'; import { useSetRecoilState } from 'recoil'; +import { MenuItem } from 'twenty-ui'; const StyledActionListContainer = styled.div` display: flex; diff --git a/packages/twenty-front/src/modules/workflow/search-variables/components/SearchVariablesDropdownStepItem.tsx b/packages/twenty-front/src/modules/workflow/search-variables/components/SearchVariablesDropdownStepItem.tsx index c641f5460..2a62fa8cf 100644 --- a/packages/twenty-front/src/modules/workflow/search-variables/components/SearchVariablesDropdownStepItem.tsx +++ b/packages/twenty-front/src/modules/workflow/search-variables/components/SearchVariablesDropdownStepItem.tsx @@ -1,6 +1,5 @@ -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect'; import { StepOutputSchema } from '@/workflow/search-variables/types/StepOutputSchema'; +import { MenuItem, MenuItemSelect } from 'twenty-ui'; type SearchVariablesDropdownStepItemProps = { steps: StepOutputSchema[]; diff --git a/packages/twenty-front/src/modules/workflow/search-variables/components/SearchVariablesDropdownStepSubItem.tsx b/packages/twenty-front/src/modules/workflow/search-variables/components/SearchVariablesDropdownStepSubItem.tsx index 89b726b10..6c71aca80 100644 --- a/packages/twenty-front/src/modules/workflow/search-variables/components/SearchVariablesDropdownStepSubItem.tsx +++ b/packages/twenty-front/src/modules/workflow/search-variables/components/SearchVariablesDropdownStepSubItem.tsx @@ -1,9 +1,8 @@ import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; -import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect'; import { StepOutputSchema } from '@/workflow/search-variables/types/StepOutputSchema'; import { isObject } from '@sniptt/guards'; import { useState } from 'react'; -import { IconChevronLeft } from 'twenty-ui'; +import { IconChevronLeft, MenuItemSelect } from 'twenty-ui'; type SearchVariablesDropdownStepSubItemProps = { step: StepOutputSchema; diff --git a/packages/twenty-front/tsup.ui.index.tsx b/packages/twenty-front/tsup.ui.index.tsx index 8a5968cf1..85c47c03f 100644 --- a/packages/twenty-front/tsup.ui.index.tsx +++ b/packages/twenty-front/tsup.ui.index.tsx @@ -10,16 +10,6 @@ export * from './src/modules/ui/input/components/Select'; export * from './src/modules/ui/input/components/TextArea'; export * from './src/modules/ui/input/components/TextInput'; export * from './src/modules/ui/input/editor/components/BlockEditor'; -export * from './src/modules/ui/navigation/menu-item/components/MenuItem'; -export * from './src/modules/ui/navigation/menu-item/components/MenuItemCommand'; -export * from './src/modules/ui/navigation/menu-item/components/MenuItemDraggable'; -export * from './src/modules/ui/navigation/menu-item/components/MenuItemMultiSelect'; -export * from './src/modules/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar'; -export * from './src/modules/ui/navigation/menu-item/components/MenuItemNavigate'; -export * from './src/modules/ui/navigation/menu-item/components/MenuItemSelect'; -export * from './src/modules/ui/navigation/menu-item/components/MenuItemSelectAvatar'; -export * from './src/modules/ui/navigation/menu-item/components/MenuItemSelectColor'; -export * from './src/modules/ui/navigation/menu-item/components/MenuItemToggle'; export * from './src/modules/ui/navigation/step-bar/components/StepBar'; declare module '@emotion/react' { diff --git a/packages/twenty-ui/src/input/components/Checkbox.tsx b/packages/twenty-ui/src/input/components/Checkbox.tsx index b8b7ad3f6..f4842ba12 100644 --- a/packages/twenty-ui/src/input/components/Checkbox.tsx +++ b/packages/twenty-ui/src/input/components/Checkbox.tsx @@ -1,6 +1,6 @@ import styled from '@emotion/styled'; +import { IconCheck, IconMinus } from '@ui/display'; import * as React from 'react'; -import { IconCheck, IconMinus } from '@ui/display/icon/components/TablerIcons'; export enum CheckboxVariant { Primary = 'primary', diff --git a/packages/twenty-ui/src/navigation/index.ts b/packages/twenty-ui/src/navigation/index.ts index 31e797cd5..f4b07da31 100644 --- a/packages/twenty-ui/src/navigation/index.ts +++ b/packages/twenty-ui/src/navigation/index.ts @@ -8,5 +8,23 @@ export * from './link/components/SocialLink'; export * from './link/components/UndecoratedLink'; export * from './link/constants/Cal'; export * from './link/constants/GithubLink'; +export * from './menu-item/components/MenuItem'; +export * from './menu-item/components/MenuItemAvatar'; +export * from './menu-item/components/MenuItemCommand'; +export * from './menu-item/components/MenuItemCommandHotKeys'; +export * from './menu-item/components/MenuItemDraggable'; +export * from './menu-item/components/MenuItemMultiSelect'; +export * from './menu-item/components/MenuItemMultiSelectAvatar'; +export * from './menu-item/components/MenuItemMultiSelectTag'; +export * from './menu-item/components/MenuItemNavigate'; +export * from './menu-item/components/MenuItemSelect'; +export * from './menu-item/components/MenuItemSelectAvatar'; +export * from './menu-item/components/MenuItemSelectColor'; +export * from './menu-item/components/MenuItemSelectTag'; +export * from './menu-item/components/MenuItemSuggestion'; +export * from './menu-item/components/MenuItemToggle'; +export * from './menu-item/internals/components/MenuItemLeftContent'; +export * from './menu-item/internals/components/StyledMenuItemBase'; +export * from './menu-item/types/MenuItemAccent'; export * from './navigation-bar/components/NavigationBar'; export * from './navigation-bar/components/NavigationBarItem'; diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItem.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItem.tsx similarity index 91% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItem.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItem.tsx index bb54ce02f..8115b1570 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItem.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItem.tsx @@ -1,11 +1,8 @@ import { useTheme } from '@emotion/react'; +import { IconChevronRight, IconComponent } from '@ui/display'; +import { LightIconButtonProps } from '@ui/input/button/components/LightIconButton'; +import { LightIconButtonGroup } from '@ui/input/button/components/LightIconButtonGroup'; import { FunctionComponent, MouseEvent, ReactElement, ReactNode } from 'react'; -import { - IconChevronRight, - IconComponent, - LightIconButtonGroup, - LightIconButtonProps, -} from 'twenty-ui'; import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent'; import { diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemAvatar.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemAvatar.tsx similarity index 86% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemAvatar.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemAvatar.tsx index a5829298b..5e845696d 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemAvatar.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemAvatar.tsx @@ -1,29 +1,20 @@ import { useTheme } from '@emotion/react'; -import { FunctionComponent, MouseEvent, ReactElement } from 'react'; import { Avatar, AvatarProps, IconChevronRight, - IconComponent, - LightIconButtonGroup, - LightIconButtonProps, OverflowingTextWithTooltip, - isDefined, -} from 'twenty-ui'; - +} from '@ui/display'; +import { LightIconButtonGroup } from '@ui/input'; +import { MenuItemIconButton } from '@ui/navigation/menu-item/components/MenuItem'; +import { isDefined } from '@ui/utilities'; +import { MouseEvent } from 'react'; import { StyledHoverableMenuItemBase, StyledMenuItemLeftContent, } from '../internals/components/StyledMenuItemBase'; import { MenuItemAccent } from '../types/MenuItemAccent'; -export type MenuItemIconButton = { - Wrapper?: FunctionComponent<{ iconButton: ReactElement }>; - Icon: IconComponent; - accent?: LightIconButtonProps['accent']; - onClick?: (event: MouseEvent) => void; -}; - export type MenuItemAvatarProps = { accent?: MenuItemAccent; className?: string; diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemCommand.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemCommand.tsx similarity index 96% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemCommand.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemCommand.tsx index 7f363eed7..410965ce9 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemCommand.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemCommand.tsx @@ -1,14 +1,13 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { IconComponent } from 'twenty-ui'; - -import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { StyledMenuItemLabel, StyledMenuItemLeftContent, } from '../internals/components/StyledMenuItemBase'; +import { IconComponent } from '@ui/display'; +import { useIsMobile } from '@ui/utilities/responsive/hooks/useIsMobile'; import { MenuItemCommandHotKeys } from './MenuItemCommandHotKeys'; const StyledMenuItemLabelText = styled(StyledMenuItemLabel)` diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemCommandHotKeys.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemCommandHotKeys.tsx similarity index 100% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemCommandHotKeys.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemCommandHotKeys.tsx diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemDraggable.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemDraggable.tsx similarity index 94% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemDraggable.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemDraggable.tsx index 430d45973..140522040 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemDraggable.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemDraggable.tsx @@ -1,11 +1,11 @@ -import { IconComponent, LightIconButtonGroup } from 'twenty-ui'; - import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent'; import { StyledHoverableMenuItemBase } from '../internals/components/StyledMenuItemBase'; import { MenuItemAccent } from '../types/MenuItemAccent'; -import { MenuItemIconButton } from './MenuItem'; +import { IconComponent } from '@ui/display'; +import { LightIconButtonGroup } from '@ui/input'; import { ReactNode } from 'react'; +import { MenuItemIconButton } from './MenuItem'; export type MenuItemDraggableProps = { LeftIcon?: IconComponent | undefined; diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelect.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemMultiSelect.tsx similarity index 82% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelect.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemMultiSelect.tsx index dbeb958ab..e049fa2d3 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelect.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemMultiSelect.tsx @@ -1,8 +1,9 @@ import styled from '@emotion/styled'; -import { Checkbox, IconComponent, Tag, ThemeColor } from 'twenty-ui'; - -import { MenuItemLeftContent } from '@/ui/navigation/menu-item/internals/components/MenuItemLeftContent'; +import { IconComponent, Tag } from '@ui/display'; +import { Checkbox } from '@ui/input/components/Checkbox'; +import { MenuItemLeftContent } from '@ui/navigation/menu-item/internals/components/MenuItemLeftContent'; +import { ThemeColor } from '@ui/theme'; import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase'; const StyledLeftContentWithCheckboxContainer = styled.div` diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemMultiSelectAvatar.tsx similarity index 92% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemMultiSelectAvatar.tsx index 4d829db05..d96433698 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemMultiSelectAvatar.tsx @@ -1,7 +1,8 @@ import styled from '@emotion/styled'; import { ReactNode } from 'react'; -import { Checkbox, OverflowingTextWithTooltip } from 'twenty-ui'; +import { OverflowingTextWithTooltip } from '@ui/display'; +import { Checkbox } from '@ui/input/components/Checkbox'; import { StyledMenuItemBase, StyledMenuItemLabel, diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelectTag.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemMultiSelectTag.tsx similarity index 85% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelectTag.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemMultiSelectTag.tsx index 30b8fcb36..a659ca7ca 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemMultiSelectTag.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemMultiSelectTag.tsx @@ -1,11 +1,6 @@ -import { - Checkbox, - CheckboxShape, - CheckboxSize, - Tag, - ThemeColor, -} from 'twenty-ui'; - +import { Tag } from '@ui/display'; +import { Checkbox, CheckboxShape, CheckboxSize } from '@ui/input'; +import { ThemeColor } from '@ui/theme'; import { StyledMenuItemBase, StyledMenuItemLeftContent, diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemNavigate.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemNavigate.tsx similarity index 93% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemNavigate.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemNavigate.tsx index dd847b304..76bde9c48 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemNavigate.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemNavigate.tsx @@ -1,6 +1,6 @@ import { useTheme } from '@emotion/react'; -import { IconChevronRight, IconComponent } from 'twenty-ui'; +import { IconChevronRight, IconComponent } from '@ui/display'; import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent'; import { StyledMenuItemBase, diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSelect.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelect.tsx similarity index 96% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSelect.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelect.tsx index 7a5a97670..3c91c42fb 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSelect.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelect.tsx @@ -1,7 +1,7 @@ import { css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { IconCheck, IconChevronRight, IconComponent } from 'twenty-ui'; +import { IconCheck, IconChevronRight, IconComponent } from '@ui/display'; import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent'; import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase'; diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSelectAvatar.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelectAvatar.tsx similarity index 94% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSelectAvatar.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelectAvatar.tsx index 90addee3b..477f6c037 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSelectAvatar.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelectAvatar.tsx @@ -1,12 +1,12 @@ -import { ReactNode } from 'react'; import { useTheme } from '@emotion/react'; -import { IconCheck, OverflowingTextWithTooltip } from 'twenty-ui'; +import { ReactNode } from 'react'; import { StyledMenuItemLabel, StyledMenuItemLeftContent, } from '../internals/components/StyledMenuItemBase'; +import { IconCheck, OverflowingTextWithTooltip } from '@ui/display'; import { StyledMenuItemSelect } from './MenuItemSelect'; type MenuItemSelectAvatarProps = { diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSelectColor.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelectColor.tsx similarity index 92% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSelectColor.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelectColor.tsx index f0c8cd4dd..60f2f81ff 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSelectColor.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelectColor.tsx @@ -1,16 +1,12 @@ import { useTheme } from '@emotion/react'; -import { - ColorSample, - ColorSampleVariant, - IconCheck, - ThemeColor, -} from 'twenty-ui'; import { StyledMenuItemLabel, StyledMenuItemLeftContent, } from '../internals/components/StyledMenuItemBase'; +import { ColorSample, ColorSampleVariant, IconCheck } from '@ui/display'; +import { ThemeColor } from '@ui/theme'; import { StyledMenuItemSelect } from './MenuItemSelect'; type MenuItemSelectColorProps = { diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSelectTag.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelectTag.tsx similarity index 91% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSelectTag.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelectTag.tsx index 01c782b89..aec752d80 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSelectTag.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelectTag.tsx @@ -1,8 +1,9 @@ import { useTheme } from '@emotion/react'; -import { IconCheck, Tag, ThemeColor } from 'twenty-ui'; import { StyledMenuItemLeftContent } from '../internals/components/StyledMenuItemBase'; +import { IconCheck, Tag } from '@ui/display'; +import { ThemeColor } from '@ui/theme'; import { StyledMenuItemSelect } from './MenuItemSelect'; type MenuItemSelectTagProps = { diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSuggestion.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSuggestion.tsx similarity index 95% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSuggestion.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemSuggestion.tsx index 1f6548e49..24811d75c 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemSuggestion.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSuggestion.tsx @@ -1,7 +1,8 @@ import styled from '@emotion/styled'; import { MouseEvent } from 'react'; -import { HOVER_BACKGROUND, IconComponent } from 'twenty-ui'; +import { IconComponent } from '@ui/display'; +import { HOVER_BACKGROUND } from '@ui/theme'; import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent'; import { StyledMenuItemLeftContent } from '../internals/components/StyledMenuItemBase'; diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemToggle.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemToggle.tsx similarity index 90% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemToggle.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/MenuItemToggle.tsx index 256c038e8..485df1268 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/MenuItemToggle.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemToggle.tsx @@ -1,5 +1,5 @@ -import { IconComponent, Toggle, ToggleSize } from 'twenty-ui'; - +import { IconComponent } from '@ui/display'; +import { Toggle, ToggleSize } from '@ui/input'; import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent'; import { StyledMenuItemBase, diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItem.stories.tsx b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItem.stories.tsx similarity index 97% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItem.stories.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItem.stories.tsx index a4eb66117..261c901f4 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItem.stories.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItem.stories.tsx @@ -1,12 +1,12 @@ import { action } from '@storybook/addon-actions'; import { Meta, StoryObj } from '@storybook/react'; + +import { IconBell } from '@ui/display'; import { CatalogDecorator, CatalogStory, ComponentDecorator, - IconBell, -} from 'twenty-ui'; - +} from '@ui/testing'; import { MenuItemAccent } from '../../types/MenuItemAccent'; import { MenuItem } from '../MenuItem'; diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemCommand.stories.tsx b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemCommand.stories.tsx similarity index 97% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemCommand.stories.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemCommand.stories.tsx index 802524805..3e8ccfe9c 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemCommand.stories.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemCommand.stories.tsx @@ -1,11 +1,11 @@ import { Meta, StoryObj } from '@storybook/react'; + +import { IconBell } from '@ui/display'; import { CatalogDecorator, CatalogStory, ComponentDecorator, - IconBell, -} from 'twenty-ui'; - +} from '@ui/testing'; import { MenuItemCommand } from '../MenuItemCommand'; const meta: Meta = { diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemDraggable.stories.tsx b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemDraggable.stories.tsx similarity index 97% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemDraggable.stories.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemDraggable.stories.tsx index 05885c00a..a66000df4 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemDraggable.stories.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemDraggable.stories.tsx @@ -1,14 +1,13 @@ import { action } from '@storybook/addon-actions'; import { Meta, StoryObj } from '@storybook/react'; + +import { IconBell, IconMinus } from '@ui/display'; import { CatalogDecorator, CatalogDimension, CatalogOptions, ComponentDecorator, - IconBell, - IconMinus, -} from 'twenty-ui'; - +} from '@ui/testing'; import { MenuItemAccent } from '../../types/MenuItemAccent'; import { MenuItemDraggable } from '../MenuItemDraggable'; diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemMultiSelect.stories.tsx b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemMultiSelect.stories.tsx similarity index 96% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemMultiSelect.stories.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemMultiSelect.stories.tsx index 8bcdc3ec9..a69104d56 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemMultiSelect.stories.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemMultiSelect.stories.tsx @@ -1,13 +1,13 @@ import { Meta, StoryObj } from '@storybook/react'; + +import { IconBell } from '@ui/display'; import { CatalogDecorator, CatalogDimension, CatalogOptions, CatalogStory, ComponentDecorator, - IconBell, -} from 'twenty-ui'; - +} from '@ui/testing'; import { MenuItemMultiSelect } from '../MenuItemMultiSelect'; const meta: Meta = { diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemMultiSelectAvatar.stories.tsx b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemMultiSelectAvatar.stories.tsx similarity index 90% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemMultiSelectAvatar.stories.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemMultiSelectAvatar.stories.tsx index f51c55a63..f56489065 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemMultiSelectAvatar.stories.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemMultiSelectAvatar.stories.tsx @@ -1,15 +1,14 @@ import { Meta, StoryObj } from '@storybook/react'; + +import { Avatar } from '@ui/display'; import { - Avatar, + AVATAR_URL_MOCK, CatalogDecorator, CatalogDimension, CatalogOptions, CatalogStory, ComponentDecorator, -} from 'twenty-ui'; - -import { avatarUrl } from '~/testing/mock-data/users'; - +} from '@ui/testing'; import { MenuItemMultiSelectAvatar } from '../MenuItemMultiSelectAvatar'; const meta: Meta = { @@ -24,7 +23,7 @@ type Story = StoryObj; export const Default: Story = { args: { text: 'First option', - avatar: , + avatar: , }, decorators: [ComponentDecorator], }; @@ -43,7 +42,7 @@ export const Catalog: CatalogStory = { values: [true, false], props: (withAvatar: boolean) => ({ avatar: withAvatar ? ( - + ) : ( ), diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemNavigate.stories.tsx b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemNavigate.stories.tsx similarity index 96% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemNavigate.stories.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemNavigate.stories.tsx index 72c097f6f..c9a8b2739 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemNavigate.stories.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemNavigate.stories.tsx @@ -1,14 +1,15 @@ import { Meta, StoryObj } from '@storybook/react'; + +import { IconBell } from '@ui/display'; +import { MenuItemNavigate } from '../MenuItemNavigate'; + import { CatalogDecorator, CatalogDimension, CatalogOptions, CatalogStory, ComponentDecorator, - IconBell, -} from 'twenty-ui'; - -import { MenuItemNavigate } from '../MenuItemNavigate'; +} from '@ui/testing'; const meta: Meta = { title: 'UI/Navigation/MenuItem/MenuItemNavigate', diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemSelect.stories.tsx b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemSelect.stories.tsx similarity index 96% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemSelect.stories.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemSelect.stories.tsx index 21d73d963..65e05b3b0 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemSelect.stories.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemSelect.stories.tsx @@ -1,15 +1,15 @@ import { Meta, StoryObj } from '@storybook/react'; + import { CatalogDecorator, CatalogDimension, CatalogOptions, CatalogStory, ComponentDecorator, - IconBell, -} from 'twenty-ui'; - +} from '@ui/testing'; import { MenuItemSelect } from '../MenuItemSelect'; +import { IconBell } from '@ui/display'; const meta: Meta = { title: 'UI/Navigation/MenuItem/MenuItemSelect', component: MenuItemSelect, diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemSelectAvatar.stories.tsx b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemSelectAvatar.stories.tsx similarity index 90% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemSelectAvatar.stories.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemSelectAvatar.stories.tsx index 4d2895bb3..0d8748b37 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemSelectAvatar.stories.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemSelectAvatar.stories.tsx @@ -1,15 +1,14 @@ import { Meta, StoryObj } from '@storybook/react'; + +import { Avatar } from '@ui/display'; import { - Avatar, + AVATAR_URL_MOCK, CatalogDecorator, CatalogDimension, CatalogOptions, CatalogStory, ComponentDecorator, -} from 'twenty-ui'; - -import { avatarUrl } from '~/testing/mock-data/users'; - +} from '@ui/testing'; import { MenuItemSelectAvatar } from '../MenuItemSelectAvatar'; const meta: Meta = { @@ -20,11 +19,10 @@ const meta: Meta = { export default meta; type Story = StoryObj; - export const Default: Story = { args: { text: 'First option', - avatar: , + avatar: , }, argTypes: { className: { control: false }, @@ -46,7 +44,7 @@ export const Catalog: CatalogStory = { values: [true, false], props: (withAvatar: boolean) => ({ avatar: withAvatar ? ( - + ) : ( ), diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemSelectColor.stories.tsx b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemSelectColor.stories.tsx similarity index 94% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemSelectColor.stories.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemSelectColor.stories.tsx index 6f95af499..a5f1060d7 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemSelectColor.stories.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemSelectColor.stories.tsx @@ -1,15 +1,14 @@ import { Meta, StoryObj } from '@storybook/react'; + +import { ColorSampleVariant } from '@ui/display'; import { CatalogDecorator, CatalogDimension, CatalogOptions, CatalogStory, - ColorSampleVariant, ComponentDecorator, - MAIN_COLOR_NAMES, - ThemeColor, -} from 'twenty-ui'; - +} from '@ui/testing'; +import { MAIN_COLOR_NAMES, ThemeColor } from '@ui/theme'; import { MenuItemSelectColor } from '../MenuItemSelectColor'; const meta: Meta = { diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemSelectTag.stories.tsx b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemSelectTag.stories.tsx similarity index 97% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemSelectTag.stories.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemSelectTag.stories.tsx index 3a1314756..f1f4b7c54 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemSelectTag.stories.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemSelectTag.stories.tsx @@ -1,13 +1,13 @@ import { Meta, StoryObj } from '@storybook/react'; + import { CatalogDecorator, CatalogDimension, CatalogOptions, CatalogStory, ComponentDecorator, - ThemeColor, -} from 'twenty-ui'; - +} from '@ui/testing'; +import { ThemeColor } from '@ui/theme'; import { MenuItemSelectTag } from '../MenuItemSelectTag'; const meta: Meta = { diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemToggle.stories.tsx b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemToggle.stories.tsx similarity index 96% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemToggle.stories.tsx rename to packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemToggle.stories.tsx index c46ef3dfc..a55476a55 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/components/__stories__/MenuItemToggle.stories.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/__stories__/MenuItemToggle.stories.tsx @@ -1,13 +1,13 @@ import { Meta, StoryObj } from '@storybook/react'; + +import { IconBell } from '@ui/display'; import { CatalogDecorator, CatalogDimension, CatalogOptions, CatalogStory, ComponentDecorator, - IconBell, -} from 'twenty-ui'; - +} from '@ui/testing'; import { MenuItemToggle } from '../MenuItemToggle'; const meta: Meta = { diff --git a/packages/twenty-ui/src/navigation/menu-item/components/index.ts b/packages/twenty-ui/src/navigation/menu-item/components/index.ts new file mode 100644 index 000000000..9325e1534 --- /dev/null +++ b/packages/twenty-ui/src/navigation/menu-item/components/index.ts @@ -0,0 +1,15 @@ +export * from './MenuItem'; +export * from './MenuItemAvatar'; +export * from './MenuItemCommand'; +export * from './MenuItemCommandHotKeys'; +export * from './MenuItemDraggable'; +export * from './MenuItemMultiSelect'; +export * from './MenuItemMultiSelectAvatar'; +export * from './MenuItemMultiSelectTag'; +export * from './MenuItemNavigate'; +export * from './MenuItemSelect'; +export * from './MenuItemSelectAvatar'; +export * from './MenuItemSelectColor'; +export * from './MenuItemSelectTag'; +export * from './MenuItemSuggestion'; +export * from './MenuItemToggle'; diff --git a/packages/twenty-ui/src/navigation/menu-item/index.ts b/packages/twenty-ui/src/navigation/menu-item/index.ts new file mode 100644 index 000000000..41664f3f2 --- /dev/null +++ b/packages/twenty-ui/src/navigation/menu-item/index.ts @@ -0,0 +1,4 @@ +export * from './components'; +export * from './internals/components/MenuItemLeftContent'; +export * from './internals/components/StyledMenuItemBase'; +export * from './types/MenuItemAccent'; diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/internals/components/MenuItemLeftContent.tsx b/packages/twenty-ui/src/navigation/menu-item/internals/components/MenuItemLeftContent.tsx similarity index 98% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/internals/components/MenuItemLeftContent.tsx rename to packages/twenty-ui/src/navigation/menu-item/internals/components/MenuItemLeftContent.tsx index f93f5a005..89e142a81 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/internals/components/MenuItemLeftContent.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/internals/components/MenuItemLeftContent.tsx @@ -1,12 +1,12 @@ import { useTheme } from '@emotion/react'; import { isString } from '@sniptt/guards'; import { ReactNode } from 'react'; + import { IconComponent, IconGripVertical, OverflowingTextWithTooltip, -} from 'twenty-ui'; - +} from '@ui/display'; import { StyledDraggableItem, StyledMenuItemLabel, diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/internals/components/StyledMenuItemBase.tsx b/packages/twenty-ui/src/navigation/menu-item/internals/components/StyledMenuItemBase.tsx similarity index 98% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/internals/components/StyledMenuItemBase.tsx rename to packages/twenty-ui/src/navigation/menu-item/internals/components/StyledMenuItemBase.tsx index 85d639138..8bfe6d220 100644 --- a/packages/twenty-front/src/modules/ui/navigation/menu-item/internals/components/StyledMenuItemBase.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/internals/components/StyledMenuItemBase.tsx @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import styled from '@emotion/styled'; -import { HOVER_BACKGROUND } from 'twenty-ui'; +import { HOVER_BACKGROUND } from '@ui/theme'; import { MenuItemAccent } from '../../types/MenuItemAccent'; export type MenuItemBaseProps = { diff --git a/packages/twenty-front/src/modules/ui/navigation/menu-item/types/MenuItemAccent.ts b/packages/twenty-ui/src/navigation/menu-item/types/MenuItemAccent.ts similarity index 100% rename from packages/twenty-front/src/modules/ui/navigation/menu-item/types/MenuItemAccent.ts rename to packages/twenty-ui/src/navigation/menu-item/types/MenuItemAccent.ts diff --git a/packages/twenty-ui/src/utilities/index.ts b/packages/twenty-ui/src/utilities/index.ts index 377997ec3..f0cc4c26a 100644 --- a/packages/twenty-ui/src/utilities/index.ts +++ b/packages/twenty-ui/src/utilities/index.ts @@ -8,6 +8,7 @@ export * from './color/utils/stringToHslColor'; export * from './dimensions/components/ComputeNodeDimensions'; export * from './image/getImageAbsoluteURI'; export * from './isDefined'; +export * from './responsive/hooks/useIsMobile'; export * from './screen-size/hooks/useScreenSize'; export * from './state/utils/createState'; export * from './types/Nullable'; diff --git a/packages/twenty-ui/src/utilities/responsive/hooks/__tests__/isMobile.test.tsx b/packages/twenty-ui/src/utilities/responsive/hooks/__tests__/isMobile.test.tsx new file mode 100644 index 000000000..708733136 --- /dev/null +++ b/packages/twenty-ui/src/utilities/responsive/hooks/__tests__/isMobile.test.tsx @@ -0,0 +1,10 @@ +import { renderHook } from '@testing-library/react'; +import { useIsMobile } from '@ui/utilities/responsive/hooks/useIsMobile'; + +describe('useIsMobile', () => { + it('should trigger the callback when clicking outside the specified refs', () => { + const { result } = renderHook(() => useIsMobile()); + + expect(result.current).toBe(false); + }); +}); diff --git a/packages/twenty-ui/src/utilities/responsive/hooks/useIsMobile.ts b/packages/twenty-ui/src/utilities/responsive/hooks/useIsMobile.ts new file mode 100644 index 000000000..69458b891 --- /dev/null +++ b/packages/twenty-ui/src/utilities/responsive/hooks/useIsMobile.ts @@ -0,0 +1,5 @@ +import { MOBILE_VIEWPORT } from '@ui/theme'; +import { useMediaQuery } from 'react-responsive'; + +export const useIsMobile = () => + useMediaQuery({ query: `(max-width: ${MOBILE_VIEWPORT}px)` }); diff --git a/packages/twenty-website/src/content/twenty-ui/navigation/menu-item.mdx b/packages/twenty-website/src/content/twenty-ui/navigation/menu-item.mdx index 84db450d2..b6bdf65bb 100644 --- a/packages/twenty-website/src/content/twenty-ui/navigation/menu-item.mdx +++ b/packages/twenty-website/src/content/twenty-ui/navigation/menu-item.mdx @@ -11,7 +11,7 @@ A versatile menu item designed to be used in a menu or navigation list. { const handleMenuItemClick = (event) => { @@ -68,7 +68,7 @@ A command-style menu item within a menu to indicate keyboard shortcuts. { const handleCommandClick = () => { @@ -114,7 +114,7 @@ A draggable menu item component designed to be used in a menu or list where item { const handleMenuItemClick = (event) => { @@ -162,7 +162,7 @@ Provides a way to implement multi-select functionality with an associated checkb { @@ -199,7 +199,7 @@ A multi-select menu item with an avatar, a checkbox for selection, and textual c - { const imageUrl = @@ -239,7 +239,7 @@ A menu item featuring an optional left icon, textual content, and a right-chevro { const handleNavigation = () => { @@ -279,7 +279,7 @@ A selectable menu item, featuring optional left content (icon and text) and an i { const handleSelection = () => { @@ -323,7 +323,7 @@ A selectable menu item with an avatar, featuring optional left content (avatar a - { const imageUrl = @@ -374,7 +374,7 @@ A selectable menu item with a color sample for scenarios where you want users to - { const handleSelection = () => { @@ -420,7 +420,7 @@ A menu item with an associated toggle switch to allow users to enable or disable { From 83f3963bfb99fce31691890afcb2367ca5e1491d Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Thu, 7 Nov 2024 18:11:51 +0100 Subject: [PATCH 009/100] Be able to specify front port (#8382) - Added REACT_APP_PORT in front .env - Use value if specified otherwise 3001 by default --------- Co-authored-by: Charles Bochet --- packages/twenty-front/.env.example | 1 + packages/twenty-front/vite.config.ts | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/twenty-front/.env.example b/packages/twenty-front/.env.example index 3fccb201c..345d0fb92 100644 --- a/packages/twenty-front/.env.example +++ b/packages/twenty-front/.env.example @@ -2,6 +2,7 @@ REACT_APP_SERVER_BASE_URL=http://localhost:3000 GENERATE_SOURCEMAP=false # ———————— Optional ———————— +# REACT_APP_PORT=3001 # CHROMATIC_PROJECT_TOKEN= # VITE_DISABLE_TYPESCRIPT_CHECKER=true # VITE_DISABLE_ESLINT_CHECKER=true \ No newline at end of file diff --git a/packages/twenty-front/vite.config.ts b/packages/twenty-front/vite.config.ts index a3a7af054..4239d483e 100644 --- a/packages/twenty-front/vite.config.ts +++ b/packages/twenty-front/vite.config.ts @@ -1,4 +1,5 @@ /* eslint-disable no-console */ +import { isNonEmptyString } from '@sniptt/guards'; import react from '@vitejs/plugin-react-swc'; import wyw from '@wyw-in-js/vite'; import path from 'path'; @@ -17,8 +18,11 @@ export default defineConfig(({ command, mode }) => { VITE_BUILD_SOURCEMAP, VITE_DISABLE_TYPESCRIPT_CHECKER, VITE_DISABLE_ESLINT_CHECKER, + REACT_APP_PORT } = env; + const port = isNonEmptyString(REACT_APP_PORT) ? parseInt(REACT_APP_PORT) : 3001; + const isBuildCommand = command === 'build'; const tsConfigPath = isBuildCommand @@ -61,7 +65,7 @@ export default defineConfig(({ command, mode }) => { cacheDir: '../../node_modules/.vite/packages/twenty-front', server: { - port: 3001, + port, host: 'localhost', fs: { allow: [ From f9c076df319609e530dc8ac0431dd028564d5332 Mon Sep 17 00:00:00 2001 From: brendanlaschke Date: Thu, 7 Nov 2024 18:13:22 +0100 Subject: [PATCH 010/100] o365 calendar sync (#8044) Implemented: * Account Connect * Calendar sync via delta ids then requesting single events I think I would split the messaging part into a second pr - that's a step more complex then the calendar :) --------- Co-authored-by: bosiraphael --- package.json | 2 + ...tionBannerReconnectAccountEmailAliases.tsx | 6 +- ...econnectAccountInsufficientPermissions.tsx | 6 +- ...tingsAccountsConnectedAccountsListCard.tsx | 9 +- .../SettingsAccountsListEmptyStateCard.tsx | 33 ++- .../SettingsAccountsRowDropdownMenu.tsx | 7 +- ...ogleApisOAuth.ts => useTriggerApiOAuth.ts} | 44 ++-- .../WorkflowEditActionFormSendEmail.tsx | 9 +- .../modules/workspace/types/FeatureFlagKey.ts | 1 + .../src/pages/onboarding/SyncEmails.tsx | 6 +- packages/twenty-server/.env.example | 1 + .../typeorm-seeds/core/feature-flags.ts | 5 + .../engine/core-modules/auth/auth.module.ts | 4 + .../microsoft-apis-auth.controller.ts | 105 +++++++++ ...pis-oauth-exchange-code-for-token.guard.ts | 31 +++ ...mircosoft-apis-oauth-request-code.guard.ts | 62 +++++ .../auth/services/google-apis.service.ts | 10 +- .../auth/services/microsoft-apis.service.ts | 212 ++++++++++++++++++ ...crosoft-apis-oauth-common.auth.strategy.ts | 31 +++ ...h-exchange-code-for-token.auth.strategy.ts | 48 ++++ ...t-apis-oauth-request-code.auth.strategy.ts | 28 +++ .../auth/types/microsoft-api-request.type.ts | 23 ++ .../utils/get-microsoft-apis-oauth-scopes.ts | 12 + .../environment/environment-variables.ts | 4 + .../enums/feature-flag-key.enum.ts | 1 + .../engine/core-modules/user/user.resolver.ts | 1 - .../calendar-event-import-manager.module.ts | 12 +- .../calendar-event-import-batch-size.ts | 1 + .../calendar-event-list-fetch.cron.command.ts | 6 +- .../commands/calendar-import.cron.command.ts | 32 +++ .../calendar-event-list-fetch.cron.job.ts | 8 +- .../jobs/calendar-events-import.cron.job.ts | 87 +++++++ .../google-calendar-get-events.service.ts | 1 + .../microsoft-calendar-driver.module.ts | 20 ++ .../microsoft-calendar-get-events.service.ts | 62 +++++ ...icrosoft-calendar-import-events.service.ts | 45 ++++ .../format-microsoft-calendar-event.util.ts | 60 +++++ .../parse-microsoft-calendar-error.util.ts | 65 ++++++ .../jobs/calendar-event-list-fetch.job.ts | 12 +- .../jobs/calendar-events-import.job.ts | 75 +++++++ .../calendar-events-import.service.ts | 87 ++++--- .../services/calendar-fetch-events.service.ts | 129 +++++++++++ .../services/calendar-get-events.service.ts | 17 +- .../utils/filter-events.util.ts | 2 +- ...microsoft-oauth2-client-manager.service.ts | 62 +++++ .../oauth2-client-manager.module.ts | 9 +- .../connected-account.workspace-entity.ts | 1 + .../messaging-get-message-list.service.ts | 12 + .../self-hosting/self-hosting-var.mdx | 1 + yarn.lock | 28 +++ 50 files changed, 1417 insertions(+), 118 deletions(-) rename packages/twenty-front/src/modules/settings/accounts/hooks/{useTriggerGoogleApisOAuth.ts => useTriggerApiOAuth.ts} (55%) create mode 100644 packages/twenty-server/src/engine/core-modules/auth/controllers/microsoft-apis-auth.controller.ts create mode 100644 packages/twenty-server/src/engine/core-modules/auth/guards/microsoft-apis-oauth-exchange-code-for-token.guard.ts create mode 100644 packages/twenty-server/src/engine/core-modules/auth/guards/mircosoft-apis-oauth-request-code.guard.ts create mode 100644 packages/twenty-server/src/engine/core-modules/auth/services/microsoft-apis.service.ts create mode 100644 packages/twenty-server/src/engine/core-modules/auth/strategies/microsoft-apis-oauth-common.auth.strategy.ts create mode 100644 packages/twenty-server/src/engine/core-modules/auth/strategies/microsoft-apis-oauth-exchange-code-for-token.auth.strategy.ts create mode 100644 packages/twenty-server/src/engine/core-modules/auth/strategies/microsoft-apis-oauth-request-code.auth.strategy.ts create mode 100644 packages/twenty-server/src/engine/core-modules/auth/types/microsoft-api-request.type.ts create mode 100644 packages/twenty-server/src/engine/core-modules/auth/utils/get-microsoft-apis-oauth-scopes.ts create mode 100644 packages/twenty-server/src/modules/calendar/calendar-event-import-manager/constants/calendar-event-import-batch-size.ts create mode 100644 packages/twenty-server/src/modules/calendar/calendar-event-import-manager/crons/commands/calendar-import.cron.command.ts create mode 100644 packages/twenty-server/src/modules/calendar/calendar-event-import-manager/crons/jobs/calendar-events-import.cron.job.ts create mode 100644 packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/microsoft-calendar-driver.module.ts create mode 100644 packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/services/microsoft-calendar-get-events.service.ts create mode 100644 packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/services/microsoft-calendar-import-events.service.ts create mode 100644 packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/format-microsoft-calendar-event.util.ts create mode 100644 packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/parse-microsoft-calendar-error.util.ts create mode 100644 packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-events-import.job.ts create mode 100644 packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-fetch-events.service.ts create mode 100644 packages/twenty-server/src/modules/connected-account/oauth2-client-manager/drivers/microsoft/microsoft-oauth2-client-manager.service.ts diff --git a/package.json b/package.json index 41e8e4b35..c9c7f6f1e 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@linaria/core": "^6.2.0", "@linaria/react": "^6.2.1", "@mdx-js/react": "^3.0.0", + "@microsoft/microsoft-graph-client": "^3.0.7", "@nestjs/apollo": "^11.0.5", "@nestjs/axios": "^3.0.1", "@nestjs/cli": "^9.0.0", @@ -201,6 +202,7 @@ "@graphql-codegen/typescript": "^3.0.4", "@graphql-codegen/typescript-operations": "^3.0.4", "@graphql-codegen/typescript-react-apollo": "^3.3.7", + "@microsoft/microsoft-graph-types": "^2.40.0", "@nestjs/cli": "^9.0.0", "@nestjs/schematics": "^9.0.0", "@nestjs/testing": "^9.0.0", diff --git a/packages/twenty-front/src/modules/information-banner/components/reconnect-account/InformationBannerReconnectAccountEmailAliases.tsx b/packages/twenty-front/src/modules/information-banner/components/reconnect-account/InformationBannerReconnectAccountEmailAliases.tsx index c3f381c1b..67fc042a9 100644 --- a/packages/twenty-front/src/modules/information-banner/components/reconnect-account/InformationBannerReconnectAccountEmailAliases.tsx +++ b/packages/twenty-front/src/modules/information-banner/components/reconnect-account/InformationBannerReconnectAccountEmailAliases.tsx @@ -1,7 +1,7 @@ import { InformationBanner } from '@/information-banner/components/InformationBanner'; import { useAccountToReconnect } from '@/information-banner/hooks/useAccountToReconnect'; import { InformationBannerKeys } from '@/information-banner/types/InformationBannerKeys'; -import { useTriggerGoogleApisOAuth } from '@/settings/accounts/hooks/useTriggerGoogleApisOAuth'; +import { useTriggerApisOAuth } from '@/settings/accounts/hooks/useTriggerApiOAuth'; import { IconRefresh } from 'twenty-ui'; export const InformationBannerReconnectAccountEmailAliases = () => { @@ -9,7 +9,7 @@ export const InformationBannerReconnectAccountEmailAliases = () => { InformationBannerKeys.ACCOUNTS_TO_RECONNECT_EMAIL_ALIASES, ); - const { triggerGoogleApisOAuth } = useTriggerGoogleApisOAuth(); + const { triggerApisOAuth } = useTriggerApisOAuth(); if (!accountToReconnect) { return null; @@ -20,7 +20,7 @@ export const InformationBannerReconnectAccountEmailAliases = () => { message={`Please reconnect your mailbox ${accountToReconnect?.handle} to update your email aliases:`} buttonTitle="Reconnect" buttonIcon={IconRefresh} - buttonOnClick={() => triggerGoogleApisOAuth()} + buttonOnClick={() => triggerApisOAuth(accountToReconnect.provider)} /> ); }; diff --git a/packages/twenty-front/src/modules/information-banner/components/reconnect-account/InformationBannerReconnectAccountInsufficientPermissions.tsx b/packages/twenty-front/src/modules/information-banner/components/reconnect-account/InformationBannerReconnectAccountInsufficientPermissions.tsx index 7f74a129b..306452b56 100644 --- a/packages/twenty-front/src/modules/information-banner/components/reconnect-account/InformationBannerReconnectAccountInsufficientPermissions.tsx +++ b/packages/twenty-front/src/modules/information-banner/components/reconnect-account/InformationBannerReconnectAccountInsufficientPermissions.tsx @@ -1,7 +1,7 @@ import { InformationBanner } from '@/information-banner/components/InformationBanner'; import { useAccountToReconnect } from '@/information-banner/hooks/useAccountToReconnect'; import { InformationBannerKeys } from '@/information-banner/types/InformationBannerKeys'; -import { useTriggerGoogleApisOAuth } from '@/settings/accounts/hooks/useTriggerGoogleApisOAuth'; +import { useTriggerApisOAuth } from '@/settings/accounts/hooks/useTriggerApiOAuth'; import { IconRefresh } from 'twenty-ui'; export const InformationBannerReconnectAccountInsufficientPermissions = () => { @@ -9,7 +9,7 @@ export const InformationBannerReconnectAccountInsufficientPermissions = () => { InformationBannerKeys.ACCOUNTS_TO_RECONNECT_INSUFFICIENT_PERMISSIONS, ); - const { triggerGoogleApisOAuth } = useTriggerGoogleApisOAuth(); + const { triggerApisOAuth } = useTriggerApisOAuth(); if (!accountToReconnect) { return null; @@ -21,7 +21,7 @@ export const InformationBannerReconnectAccountInsufficientPermissions = () => { reconnect for updates:`} buttonTitle="Reconnect" buttonIcon={IconRefresh} - buttonOnClick={() => triggerGoogleApisOAuth()} + buttonOnClick={() => triggerApisOAuth(accountToReconnect.provider)} /> ); }; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsListCard.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsListCard.tsx index 238371241..6000afa1f 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsListCard.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsListCard.tsx @@ -1,5 +1,5 @@ import { useNavigate } from 'react-router-dom'; -import { IconGoogle } from 'twenty-ui'; +import { IconComponent, IconGoogle, IconMicrosoft } from 'twenty-ui'; import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; import { SettingsAccountsListEmptyStateCard } from '@/settings/accounts/components/SettingsAccountsListEmptyStateCard'; @@ -9,6 +9,11 @@ import { SettingsPath } from '@/types/SettingsPath'; import { SettingsAccountsConnectedAccountsRowRightContainer } from '@/settings/accounts/components/SettingsAccountsConnectedAccountsRowRightContainer'; import { SettingsListCard } from '../../components/SettingsListCard'; +const ProviderIcons: { [k: string]: IconComponent } = { + google: IconGoogle, + microsoft: IconMicrosoft, +}; + export const SettingsAccountsConnectedAccountsListCard = ({ accounts, loading, @@ -27,7 +32,7 @@ export const SettingsAccountsConnectedAccountsListCard = ({ items={accounts} getItemLabel={(account) => account.handle} isLoading={loading} - RowIcon={IconGoogle} + RowIconFn={(row) => ProviderIcons[row.provider]} RowRightComponent={({ item: account }) => ( )} diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListEmptyStateCard.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListEmptyStateCard.tsx index 8500264c4..d532691fc 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListEmptyStateCard.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsListEmptyStateCard.tsx @@ -1,7 +1,14 @@ +import { useTriggerApisOAuth } from '@/settings/accounts/hooks/useTriggerApiOAuth'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import styled from '@emotion/styled'; -import { Button, Card, CardContent, CardHeader, IconGoogle } from 'twenty-ui'; - -import { useTriggerGoogleApisOAuth } from '@/settings/accounts/hooks/useTriggerGoogleApisOAuth'; +import { + Button, + Card, + CardContent, + CardHeader, + IconGoogle, + IconMicrosoft, +} from 'twenty-ui'; const StyledHeader = styled(CardHeader)` align-items: center; @@ -12,6 +19,7 @@ const StyledHeader = styled(CardHeader)` const StyledBody = styled(CardContent)` display: flex; justify-content: center; + gap: ${({ theme }) => theme.spacing(2)}; `; type SettingsAccountsListEmptyStateCardProps = { @@ -21,11 +29,10 @@ type SettingsAccountsListEmptyStateCardProps = { export const SettingsAccountsListEmptyStateCard = ({ label, }: SettingsAccountsListEmptyStateCardProps) => { - const { triggerGoogleApisOAuth } = useTriggerGoogleApisOAuth(); - - const handleOnClick = async () => { - await triggerGoogleApisOAuth(); - }; + const { triggerApisOAuth } = useTriggerApisOAuth(); + const isMicrosoftSyncEnabled = useIsFeatureEnabled( + 'IS_MICROSOFT_SYNC_ENABLED', + ); return ( @@ -35,8 +42,16 @@ export const SettingsAccountsListEmptyStateCard = ({ Icon={IconGoogle} title="Connect with Google" variant="secondary" - onClick={handleOnClick} + onClick={() => triggerApisOAuth('google')} /> + {isMicrosoftSyncEnabled && ( +