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] 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 }) => {