Update playwright (#10927)

Update twenty-e2e-testing to reflect actual state of app
This commit is contained in:
BOHEUS
2025-03-19 10:29:36 +00:00
committed by GitHub
parent ecc21656cb
commit 15a2cb5141
17 changed files with 310 additions and 332 deletions

View File

@ -3,18 +3,4 @@ FRONTEND_BASE_URL=http://localhost:3001
BACKEND_BASE_URL=http://localhost:3000
DEFAULT_LOGIN=tim@apple.dev
DEFAULT_PASSWORD=Applecar2025
WEBSITE_URL=https://twenty.com
# === DO NOT USE, WORK IN PROGRESS ===
# This URL must have trailing forward slash as all REST API endpoints have object after it
# Documentation for REST API: https://twenty.com/developers/rest-api/core#/
# REST_API_BASE_URL=http://localhost:3000/rest/
# Documentation for GraphQL API: https://twenty.com/developers/graphql/core
# GRAPHQL_BASE_URL=http://localhost:3000/graphql
# Without this key, all API tests will fail, to generate this key
# Log in to Twenty workspace, go to Settings > Developers, generate new key and paste it here
# In order to use it, header Authorization: Bearer token must be used
# This key works for both REST and GraphQL API
# API_DEV_KEY=fill_with_proper_key
WEBSITE_URL=https://twenty.com

View File

@ -25,7 +25,7 @@ export const test = base.extend<{ screenshotHook: void }>({
),
});
},
{ auto: true }, // automatic fixture runs with every test
{ auto: true },
],
});

View File

@ -44,7 +44,7 @@ export class InsertFieldData {
'//label[contains(., "POST CODE")]/../div[last()]/input',
);
this.countrySelect = page.locator(
'//span[contains(., "COUNTRY")]/../div[last()]/input',
'//span[contains(., "COUNTRY")]/../div[last()]/div',
);
this.arrayValueInput = page.locator("//input[@placeholder='Enter value']");
this.arrayAddValueButton = page.locator(

View File

@ -1,5 +1,55 @@
import { Locator, Page } from '@playwright/test';
export class StripePage {
// TODO: implement all necessary methods (staging/sandbox page - does it differ anyhow from normal page?)
private readonly cardNumberInput: Locator;
private readonly cardExpiryInput: Locator;
private readonly cardCvcInput: Locator;
private readonly cardholderNameInput: Locator;
private readonly startTrialButton: Locator;
private readonly cancelSubscriptionButton: Locator;
private readonly confirmButton: Locator;
private readonly returnToTwentyLink: Locator;
constructor(public readonly page: Page) {
this.cardNumberInput = page.getByPlaceholder('1234 1234 1234 1234');
this.cardExpiryInput = page.getByPlaceholder('MM / YY');
this.cardCvcInput = page.getByPlaceholder('CVC');
this.cardholderNameInput = page.getByPlaceholder('Full name on card');
this.startTrialButton = page.getByTestId('hosted-payment-submit-button');
this.cancelSubscriptionButton = page.locator(
'a[data-test="cancel-subscription"]',
);
this.confirmButton = page.getByTestId('confirm');
this.returnToTwentyLink = page.getByTestId('return-to-business-link');
}
async fillCardNumber(number: string) {
await this.cardNumberInput.fill(number);
}
async fillCardExpiry(expiry: string) {
await this.cardExpiryInput.fill(expiry);
}
async fillCardCvc(cvc: string) {
await this.cardCvcInput.fill(cvc);
}
async fillCardholderName(name: string) {
await this.cardholderNameInput.fill(name);
}
async startTrial() {
await this.startTrialButton.click();
}
async clickCancelSubscription() {
await this.cancelSubscriptionButton.click();
await this.confirmButton.click();
await this.page.getByTestId('cancellation_reason_cancel').click();
}
async returnToTwenty() {
await this.returnToTwentyLink.click();
}
}

View File

@ -6,6 +6,7 @@ export class AccountsSection {
private readonly addBlocklistField: Locator;
private readonly addBlocklistButton: Locator;
private readonly connectWithGoogleButton: Locator;
private readonly connectWithMicrosoftButton: Locator;
constructor(public readonly page: Page) {
this.page = page;
@ -22,6 +23,9 @@ export class AccountsSection {
this.connectWithGoogleButton = page.getByRole('button', {
name: 'Connect with Google',
});
this.connectWithMicrosoftButton = page.getByRole('button', {
name: 'Connect with Microsoft',
});
}
async clickAddAccount() {
@ -51,4 +55,8 @@ export class AccountsSection {
async linkGoogleAccount() {
await this.connectWithGoogleButton.click();
}
async linkMicrosoftAccount() {
await this.connectWithMicrosoftButton.click();
}
}

View File

@ -0,0 +1,64 @@
import { Locator, Page } from '@playwright/test';
export class APIsSection {
private readonly createAPIKeyButton: Locator;
private readonly regenerateAPIKeyButton: Locator;
private readonly nameOfAPIKeyInput: Locator;
private readonly expirationDateAPIKeySelect: Locator;
private readonly cancelButton: Locator;
private readonly saveButton: Locator;
private readonly deleteButton: Locator;
constructor(public readonly page: Page) {
this.page = page;
this.createAPIKeyButton = page.getByRole('link', {
name: 'Create API Key',
});
this.nameOfAPIKeyInput = page
.getByPlaceholder('E.g. backoffice integration')
.first();
this.expirationDateAPIKeySelect = page.locator(
'div[aria-controls="object-field-type-select-options"]',
);
this.regenerateAPIKeyButton = page.getByRole('button', {
name: 'Regenerate Key',
});
this.cancelButton = page.getByRole('button', { name: 'Cancel' });
this.saveButton = page.getByRole('button', { name: 'Save' });
this.deleteButton = page.getByRole('button', { name: 'Delete' });
}
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 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();
}
}

View File

@ -1,123 +0,0 @@
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();
}
}

View File

@ -7,11 +7,12 @@ export class ExperienceSection {
private readonly dateFormatDropdown: Locator;
private readonly timeFormatDropdown: Locator;
private readonly searchInput: Locator;
private readonly languageDropdown: Locator;
constructor(public readonly page: Page) {
this.page = page;
this.lightThemeButton = page.getByText('AaLight'); // it works
this.darkThemeButton = page.getByText('AaDark');
this.lightThemeButton = page.locator('div[variant="Light"]').first();
this.darkThemeButton = page.locator('div[variant="Dark"]').first();
this.timezoneDropdown = page.locator(
'//span[contains(., "Time zone")]/../div/div/div',
);
@ -22,6 +23,9 @@ export class ExperienceSection {
'//span[contains(., "Time format")]/../div/div/div',
);
this.searchInput = page.getByPlaceholder('Search');
this.languageDropdown = page.locator(
'//h2[contains(., "Language")]/../../../div/div/div',
);
}
async changeThemeToLight() {
@ -52,4 +56,9 @@ export class ExperienceSection {
await this.timeFormatDropdown.click();
await this.page.getByText(timeFormat, { exact: true }).click();
}
async selectLanguage(language: string) {
await this.languageDropdown.click();
await this.page.getByText(language, { exact: true }).click();
}
}

View File

@ -1,159 +0,0 @@
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();
}
}

View File

@ -4,6 +4,8 @@ export class GeneralSection {
private readonly workspaceNameField: Locator;
private readonly supportSwitch: Locator;
private readonly deleteWorkspaceButton: Locator;
private readonly customizeDomainButton: Locator;
private readonly subdomainInput: Locator;
constructor(public readonly page: Page) {
this.page = page;
@ -12,6 +14,12 @@ export class GeneralSection {
this.deleteWorkspaceButton = page.getByRole('button', {
name: 'Delete workspace',
});
this.customizeDomainButton = page.getByRole('button', {
name: 'Customize domain',
});
this.subdomainInput = page.locator(
'//div[contains(.,".twenty-main.com")]/../input',
);
}
async changeWorkspaceName(workspaceName: string) {
@ -26,4 +34,10 @@ export class GeneralSection {
async clickDeleteWorkSpaceButton() {
await this.deleteWorkspaceButton.click();
}
async changeSubdomain(subdomain: string) {
await this.customizeDomainButton.click();
await this.subdomainInput.fill(subdomain);
await this.page.getByRole('button', { name: 'Save' }).click();
}
}

View File

@ -29,6 +29,7 @@ export class NewFieldSection {
private readonly relationFieldLink: Locator;
private readonly relationTypeSelect: Locator;
private readonly objectDestinationSelect: Locator;
private readonly relationIconSelect: Locator;
private readonly relationFieldNameInput: Locator;
private readonly fullNameFieldLink: Locator;
private readonly UUIDFieldLink: Locator;
@ -225,6 +226,11 @@ export class NewFieldSection {
await this.page.getByTestId('tooltip').filter({ hasText: name }).click();
}
async selectRelationIcon(name: string) {
await this.relationIconSelect.click();
await this.page.getByTitle(name).click();
}
async typeRelationName(name: string) {
await this.relationFieldNameInput.clear();
await this.relationFieldNameInput.fill(name);

View File

@ -0,0 +1,22 @@
import { Locator, Page } from '@playwright/test';
export class RolesSection {
private readonly page: Page;
private readonly createRoleButton: Locator;
private readonly defaultRoleDropdown: Locator;
constructor(page: Page) {
this.page = page;
this.createRoleButton = page.getByRole('button', { name: 'Create Role' });
this.defaultRoleDropdown = page.getByTestId('tooltip');
}
async clickCreateRoleButton() {
await this.createRoleButton.click();
}
async selectDefaultRole(role: string) {
await this.defaultRoleDropdown.click();
await this.page.getByTestId('tooltip').getByText(role).click();
}
}

View File

@ -1,10 +1,28 @@
import { Locator, Page } from '@playwright/test';
export class SecuritySection {
private readonly googleToggle: Locator;
private readonly microsoftToggle: Locator;
private readonly passwordToggle: Locator;
private readonly inviteByLinkToggle: Locator;
constructor(public readonly page: Page) {
this.inviteByLinkToggle = page.locator('input[type="checkbox"]').nth(1);
this.googleToggle = page.getByLabel('Google');
this.microsoftToggle = page.getByLabel('Microsoft');
this.passwordToggle = page.getByLabel('Password');
this.inviteByLinkToggle = page.getByLabel('Invite by Link');
}
async toggleGoogle() {
await this.googleToggle.click();
}
async toggleMicrosoft() {
await this.microsoftToggle.click();
}
async togglePassword() {
await this.passwordToggle.click();
}
async toggleInviteByLink() {

View File

@ -0,0 +1,65 @@
import { Locator, Page } from '@playwright/test';
export class WebhooksSection {
private readonly createWebhookButton: Locator;
private readonly webhookURLInput: Locator;
private readonly webhookDescription: Locator;
private readonly deleteButton: Locator;
constructor(public readonly page: Page) {
this.page = page;
this.createWebhookButton = page.getByRole('link', {
name: 'Create Webhook',
});
this.webhookURLInput = page.getByPlaceholder('URL');
this.webhookDescription = page.getByPlaceholder('Write a description');
this.deleteButton = page.getByRole('button', { name: 'Delete' });
}
async deleteWebhook() {
await this.deleteButton.click();
}
async createWebhook() {
await this.createWebhookButton.click();
}
async typeWebhookURL(url: string) {
await this.webhookURLInput.clear();
await this.webhookURLInput.fill(url);
}
async typeWebhookDescription(description: string) {
await this.webhookDescription.fill(description);
}
async selectWebhookObject(index: number, object: string) {
await this.page
.locator(
`//div[aria-controls="object-webhook-type-select-${index}-options"]`,
)
.click();
await this.page.getByRole('option').getByText(object).click();
}
async selectWebhookAction(index: number, action: string) {
await this.page
.locator(
`//div[aria-controls="operation-webhook-type-select-${index}-options"]`,
)
.click();
await this.page.getByRole('option').getByText(action).click();
}
async deleteWebhookFilter(index: number) {
await this.page
.locator(
`//div[aria-controls="object-webhook-type-select-${index}-options"]/../..//button`,
)
.click();
}
async checkWebhookDetails(name: string) {
await this.page.locator(`//a/div[contains(.,'${name}')][first()]`).click();
}
}

View File

@ -9,11 +9,14 @@ export class SettingsPage {
private readonly calendarsLink: Locator;
private readonly generalLink: Locator;
private readonly membersLink: Locator;
private readonly rolesLink: Locator;
private readonly dataModelLink: Locator;
private readonly developersLink: Locator;
private readonly functionsLink: Locator;
private readonly securityLink: Locator;
private readonly integrationsLink: Locator;
private readonly securityLink: Locator;
private readonly apisLink: Locator;
private readonly webhooksLink: Locator;
private readonly adminPanelLink: Locator;
private readonly labLink: Locator;
private readonly releasesLink: Locator;
private readonly logoutLink: Locator;
private readonly advancedToggle: Locator;
@ -28,11 +31,14 @@ export class SettingsPage {
this.calendarsLink = page.getByRole('link', { name: 'Calendars' });
this.generalLink = page.getByRole('link', { name: 'General' });
this.membersLink = page.getByRole('link', { name: 'Members' });
this.rolesLink = page.getByRole('link', { name: 'Roles' });
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.apisLink = page.getByRole('link', { name: 'APIs' });
this.webhooksLink = page.getByRole('link', { name: 'Webhooks' });
this.adminPanelLink = page.getByRole('link', { name: 'Admin Panel' });
this.labLink = page.getByRole('link', { name: 'Lab' });
this.releasesLink = page.getByRole('link', { name: 'Releases' });
this.logoutLink = page.getByText('Logout');
this.advancedToggle = page.locator('input[type="checkbox"]').first();
@ -70,24 +76,36 @@ export class SettingsPage {
await this.membersLink.click();
}
async goToRolesSection() {
await this.rolesLink.click();
}
async goToDataModelSection() {
await this.dataModelLink.click();
}
async goToDevelopersSection() {
await this.developersLink.click();
}
async goToFunctionsSection() {
await this.functionsLink.click();
async goToIntegrationsSection() {
await this.integrationsLink.click();
}
async goToSecuritySection() {
await this.securityLink.click();
}
async goToIntegrationsSection() {
await this.integrationsLink.click();
async goToAPIsSection() {
await this.apisLink.click();
}
async goToWebhooksSection() {
await this.webhooksLink.click();
}
async goToAdminPanelSection() {
await this.adminPanelLink.click();
}
async goToLabSection() {
await this.labLink.click();
}
async goToReleasesIntegration() {

View File

@ -6,6 +6,6 @@
"private": true,
"license": "AGPL-3.0",
"devDependencies": {
"@playwright/test": "^1.49.0"
"@playwright/test": "^1.51.0"
}
}

View File

@ -11360,14 +11360,14 @@ __metadata:
languageName: node
linkType: hard
"@playwright/test@npm:^1.49.0":
version: 1.49.1
resolution: "@playwright/test@npm:1.49.1"
"@playwright/test@npm:^1.51.0":
version: 1.51.0
resolution: "@playwright/test@npm:1.51.0"
dependencies:
playwright: "npm:1.49.1"
playwright: "npm:1.51.0"
bin:
playwright: cli.js
checksum: 10c0/2fca0bb7b334f7a23c7c5dfa5dbe37b47794c56f39b747c8d74a2f95c339e7902a296f2f1dd32c47bdd723cfa92cee05219f1a5876725dc89a1871b9137a286d
checksum: 10c0/ae83dd2c3a32133de58f44a9dbcd73a8059155ebd8acc736ba8bd0a7ca99b194afe2e8f5a500861d18b1c8f06b4e4ea8de4a2402297c59053d4becc404b47e0a
languageName: node
linkType: hard
@ -44555,12 +44555,12 @@ __metadata:
languageName: node
linkType: hard
"playwright-core@npm:1.49.1":
version: 1.49.1
resolution: "playwright-core@npm:1.49.1"
"playwright-core@npm:1.51.0":
version: 1.51.0
resolution: "playwright-core@npm:1.51.0"
bin:
playwright-core: cli.js
checksum: 10c0/990b619c75715cd98b2c10c1180a126e3a454b247063b8352bc67792fe01183ec07f31d30c8714c3768cefed12886d1d64ac06da701f2baafc2cad9b439e3919
checksum: 10c0/8f5de23088c5e97c00327f356b17e0223181e921baf99f4e38d9a3b18d0693db288f8b5389e96d0cb4a1b55f03870f140dd7346128a0c02ce36d11eb92153841
languageName: node
linkType: hard
@ -44579,18 +44579,18 @@ __metadata:
languageName: node
linkType: hard
"playwright@npm:1.49.1":
version: 1.49.1
resolution: "playwright@npm:1.49.1"
"playwright@npm:1.51.0":
version: 1.51.0
resolution: "playwright@npm:1.51.0"
dependencies:
fsevents: "npm:2.3.2"
playwright-core: "npm:1.49.1"
playwright-core: "npm:1.51.0"
dependenciesMeta:
fsevents:
optional: true
bin:
playwright: cli.js
checksum: 10c0/2368762c898920d4a0a5788b153dead45f9c36c3f5cf4d2af5228d0b8ea65823e3bbe998877950a2b9bb23a211e4633996f854c6188769dc81a25543ac818ab5
checksum: 10c0/e8509ea500e03e8051fd243f2347ac3196ff8dde4c20ae3aba4cf723e2b647a0158d209fba062995dab90590229a483d723562cf1ea8b2fc11698617027416fd
languageName: node
linkType: hard
@ -51273,7 +51273,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "twenty-e2e-testing@workspace:packages/twenty-e2e-testing"
dependencies:
"@playwright/test": "npm:^1.49.0"
"@playwright/test": "npm:^1.51.0"
languageName: unknown
linkType: soft