mirror of
https://github.com/n8n-io/n8n.git
synced 2026-07-28 03:24:59 +02:00
refactor: Centralize Element Plus el-message-box selectors into a (#32879)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
9dce29821b
commit
192b5b8436
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"version": 1,
|
||||
"generated": "2026-06-23T13:28:32.925Z",
|
||||
"totalViolations": 145,
|
||||
"generated": "2026-06-23T16:27:44.350Z",
|
||||
"totalViolations": 142,
|
||||
"violations": {
|
||||
"pages/AIAssistantPage.ts": [
|
||||
{
|
||||
|
|
@ -577,26 +577,6 @@
|
|||
"hash": "e8fc423eeeb7"
|
||||
}
|
||||
],
|
||||
"tests/e2e/workflows/editor/ndv/resource-locator.spec.ts": [
|
||||
{
|
||||
"rule": "selector-purity",
|
||||
"line": 93,
|
||||
"message": "Direct page locator call: n8n.page.locator('.el-message-box').locator('button:has-t...",
|
||||
"hash": "68c3ed65550b"
|
||||
},
|
||||
{
|
||||
"rule": "selector-purity",
|
||||
"line": 93,
|
||||
"message": "Chained locator call in test: n8n.page.locator('.el-message-box').locator('button:has-t...",
|
||||
"hash": "189e3887535d"
|
||||
},
|
||||
{
|
||||
"rule": "selector-purity",
|
||||
"line": 93,
|
||||
"message": "Direct page locator call: n8n.page.locator('.el-message-box')",
|
||||
"hash": "423a23988aea"
|
||||
}
|
||||
],
|
||||
"tests/e2e/workflows/editor/workflow-actions/archive.spec.ts": [
|
||||
{
|
||||
"rule": "selector-purity",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { ConvertToSubworkflowModal } from './components/ConvertToSubworkflowModa
|
|||
import { CredentialModal } from './components/CredentialModal';
|
||||
import { FocusPanel } from './components/FocusPanel';
|
||||
import { LogsPanel } from './components/LogsPanel';
|
||||
import { MessageBox } from './components/messageBoxLocators';
|
||||
import { NodeCreator } from './components/NodeCreator';
|
||||
import { SaveChangesModal } from './components/SaveChangesModal';
|
||||
import { StickyComponent } from './components/StickyComponent';
|
||||
|
|
@ -487,11 +488,9 @@ export class CanvasPage extends BasePage {
|
|||
}
|
||||
|
||||
async confirmIgnoreAllForAllWorkflows(): Promise<void> {
|
||||
await expect(this.page.locator('.el-message-box')).toBeVisible();
|
||||
await this.page
|
||||
.locator('.el-message-box__btns button')
|
||||
.filter({ hasText: /ignore for all workflows/i })
|
||||
.click();
|
||||
const messageBox = new MessageBox(this.page);
|
||||
await expect(messageBox.root).toBeVisible();
|
||||
await messageBox.buttonByText(/ignore for all workflows/i).click();
|
||||
}
|
||||
|
||||
async duplicateNode(nodeName: string): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { expect } from '@playwright/test';
|
|||
import type { Locator } from '@playwright/test';
|
||||
|
||||
import { BasePage } from './BasePage';
|
||||
import { MessageBox } from './components/messageBoxLocators';
|
||||
|
||||
export class DataTableDetails extends BasePage {
|
||||
async goto(datatableId: string) {
|
||||
|
|
@ -153,7 +154,7 @@ export class DataTableDetails extends BasePage {
|
|||
|
||||
async deleteSelectedRows() {
|
||||
await this.getDeleteSelectedButton().click();
|
||||
const confirmButton = this.page.locator('.btn--confirm');
|
||||
const confirmButton = new MessageBox(this.page).confirmButton;
|
||||
await confirmButton.click();
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +183,7 @@ export class DataTableDetails extends BasePage {
|
|||
.filter({ visible: true })
|
||||
.click();
|
||||
|
||||
const confirmButton = this.page.locator('.btn--confirm');
|
||||
const confirmButton = new MessageBox(this.page).confirmButton;
|
||||
await confirmButton.click();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { BasePage } from './BasePage';
|
||||
import { AddResource } from './components/AddResource';
|
||||
import { MessageBox } from './components/messageBoxLocators';
|
||||
|
||||
export class DataTableView extends BasePage {
|
||||
async goto(projectId?: string) {
|
||||
|
|
@ -50,11 +51,11 @@ export class DataTableView extends BasePage {
|
|||
}
|
||||
|
||||
getDeleteDataTableModal() {
|
||||
return this.page.locator('.el-message-box').filter({ hasText: 'Delete Data table' });
|
||||
return new MessageBox(this.page).root.filter({ hasText: 'Delete Data table' });
|
||||
}
|
||||
|
||||
getDeleteDataTableConfirmButton() {
|
||||
return this.getDeleteDataTableModal().locator('.btn--confirm');
|
||||
return new MessageBox(this.getDeleteDataTableModal()).confirmButton;
|
||||
}
|
||||
|
||||
getDataTablePageSizeSelect() {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import type { Locator } from '@playwright/test';
|
|||
|
||||
import { BasePage } from './BasePage';
|
||||
import { LogsPanel } from './components/LogsPanel';
|
||||
import { MessageBox } from './components/messageBoxLocators';
|
||||
|
||||
export class ExecutionsPage extends BasePage {
|
||||
async goto(projectId?: string) {
|
||||
|
|
@ -98,7 +99,7 @@ export class ExecutionsPage extends BasePage {
|
|||
|
||||
async deleteExecutionInPreview(): Promise<void> {
|
||||
await this.page.getByTestId('execution-preview-delete-button').click();
|
||||
await this.page.locator('button.btn--confirm').click();
|
||||
await new MessageBox(this.page).confirmButton.click();
|
||||
}
|
||||
|
||||
// Filter methods
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { expect, type Locator } from '@playwright/test';
|
||||
|
||||
import { BasePage } from './BasePage';
|
||||
import { MessageBox } from './components/messageBoxLocators';
|
||||
|
||||
export class SettingsEnvironmentPage extends BasePage {
|
||||
async goto(): Promise<void> {
|
||||
|
|
@ -64,6 +65,6 @@ export class SettingsEnvironmentPage extends BasePage {
|
|||
.getByRole('dialog')
|
||||
.filter({ hasText: 'Disconnect Git repository' });
|
||||
await expect(confirmModal).toBeVisible();
|
||||
await confirmModal.locator('.btn--confirm').click();
|
||||
await new MessageBox(confirmModal).confirmButton.click();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import type { Locator } from '@playwright/test';
|
||||
|
||||
import { BasePage } from './BasePage';
|
||||
import { MessageBox } from './components/messageBoxLocators';
|
||||
|
||||
export class SettingsLogStreamingPage extends BasePage {
|
||||
async goto(): Promise<void> {
|
||||
|
|
@ -68,15 +69,15 @@ export class SettingsLogStreamingPage extends BasePage {
|
|||
}
|
||||
|
||||
getConfirmationDialog(): Locator {
|
||||
return this.page.locator('.el-message-box');
|
||||
return new MessageBox(this.page).root;
|
||||
}
|
||||
|
||||
getCancelButton(): Locator {
|
||||
return this.page.locator('.btn--cancel');
|
||||
return new MessageBox(this.page).cancelButton;
|
||||
}
|
||||
|
||||
getConfirmButton(): Locator {
|
||||
return this.page.locator('.btn--confirm');
|
||||
return new MessageBox(this.page).confirmButton;
|
||||
}
|
||||
|
||||
async addDestination(): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import type { Locator } from '@playwright/test';
|
|||
|
||||
import { BasePage } from './BasePage';
|
||||
import { CredentialModal } from './components/CredentialModal';
|
||||
import { MessageBox } from './components/messageBoxLocators';
|
||||
import { SetupWorkflowCredentialsModal } from './components/SetupWorkflowCredentialsModal';
|
||||
|
||||
export class TemplateCredentialSetupPage extends BasePage {
|
||||
|
|
@ -58,8 +59,7 @@ export class TemplateCredentialSetupPage extends BasePage {
|
|||
}
|
||||
|
||||
getMessageBox(): Locator {
|
||||
// Using class selector as Element UI message box doesn't have semantic attributes
|
||||
return this.page.locator('.el-message-box');
|
||||
return new MessageBox(this.page).root;
|
||||
}
|
||||
|
||||
/** Opens credential creation modal and waits for it to be visible */
|
||||
|
|
@ -72,6 +72,6 @@ export class TemplateCredentialSetupPage extends BasePage {
|
|||
async dismissMessageBox(): Promise<void> {
|
||||
const messageBox = this.getMessageBox();
|
||||
await messageBox.waitFor({ state: 'visible' });
|
||||
await messageBox.locator('.btn--cancel').click();
|
||||
await new MessageBox(messageBox).cancelButton.click();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { expect } from '@playwright/test';
|
||||
|
||||
import { BasePage } from './BasePage';
|
||||
import { MessageBox } from './components/messageBoxLocators';
|
||||
import { VariableModal } from './components/VariableModal';
|
||||
|
||||
export class VariablesPage extends BasePage {
|
||||
|
|
@ -78,7 +79,7 @@ export class VariablesPage extends BasePage {
|
|||
// Use a more specific selector to avoid strict mode violation with other dialogs
|
||||
const modal = this.page.getByRole('dialog').filter({ hasText: 'Delete variable' });
|
||||
await expect(modal).toBeVisible();
|
||||
await modal.locator('.btn--confirm').click();
|
||||
await new MessageBox(modal).confirmButton.click();
|
||||
}
|
||||
|
||||
async editVariable(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import type { Locator } from '@playwright/test';
|
||||
|
||||
import { MessageBox } from './messageBoxLocators';
|
||||
|
||||
/**
|
||||
* Save Changes Modal component for handling unsaved changes dialogs.
|
||||
* Appears when navigating away from workflow with unsaved changes.
|
||||
|
|
@ -12,11 +14,11 @@ export class SaveChangesModal {
|
|||
}
|
||||
|
||||
getCancelButton(): Locator {
|
||||
return this.root.locator('.btn--cancel');
|
||||
return new MessageBox(this.root).cancelButton;
|
||||
}
|
||||
|
||||
getCloseButton(): Locator {
|
||||
return this.root.locator('.el-message-box__headerbtn');
|
||||
return new MessageBox(this.root).closeIcon;
|
||||
}
|
||||
|
||||
async clickCancel(): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import type { Locator, Page } from '@playwright/test';
|
||||
|
||||
import { MessageBox } from './messageBoxLocators';
|
||||
|
||||
/**
|
||||
* Workflow menu on the editor header (`workflow-menu` trigger) plus the
|
||||
* teleported (Element Plus) dropdown items and confirmation surfaces it opens.
|
||||
|
|
@ -104,7 +106,7 @@ export class WorkflowMenu {
|
|||
}
|
||||
|
||||
async confirmArchiveModal(): Promise<void> {
|
||||
await this.page.locator('.btn--confirm').click();
|
||||
await new MessageBox(this.page).confirmButton.click();
|
||||
}
|
||||
|
||||
// Duplicate modal (opened by openDuplicate)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import type { Locator, Page } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Locator helpers for Element Plus `.el-message-box*` (ElMessageBox confirm/prompt).
|
||||
*
|
||||
* Element Plus teleports the confirm/prompt box to `<body>` and exposes class-only
|
||||
* action buttons (`.btn--confirm`, `.btn--cancel`) and a class-only close icon
|
||||
* (`.el-message-box__headerbtn`). Centralising the raw selectors here keeps them
|
||||
* out of tests, page objects, and components.
|
||||
*
|
||||
* Construct with the teleport scope (`this.page`, an injected `root` Locator, or a
|
||||
* `<body>`-rooted Locator) — never a page object `container`, since the box renders
|
||||
* outside it. Takes `Page | Locator` directly and extends nothing, so it also works
|
||||
* for Locator-injected leaf components that have no `Page` to pass to a base class.
|
||||
*/
|
||||
export class MessageBox {
|
||||
constructor(private readonly scope: Page | Locator) {}
|
||||
|
||||
get root(): Locator {
|
||||
return this.scope.locator('.el-message-box');
|
||||
}
|
||||
|
||||
get confirmButton(): Locator {
|
||||
return this.scope.locator('.btn--confirm');
|
||||
}
|
||||
|
||||
get cancelButton(): Locator {
|
||||
return this.scope.locator('.btn--cancel');
|
||||
}
|
||||
|
||||
get closeIcon(): Locator {
|
||||
return this.scope.locator('.el-message-box__headerbtn');
|
||||
}
|
||||
|
||||
buttonByText(text: string | RegExp): Locator {
|
||||
return this.scope.locator('.el-message-box__btns button').filter({ hasText: text });
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { E2E_TEST_NODE_NAME } from '../../../../../config/constants';
|
||||
import { test, expect } from '../../../../../fixtures/base';
|
||||
import { MessageBox } from '../../../../../pages/components/messageBoxLocators';
|
||||
|
||||
const NO_CREDENTIALS_MESSAGE = 'Add your credential';
|
||||
const INVALID_CREDENTIALS_MESSAGE = 'Check your credential';
|
||||
|
|
@ -90,7 +91,7 @@ test.describe(
|
|||
|
||||
await n8n.canvas.credentialModal.close();
|
||||
// Close warning modal about not connecting the OAuth credentials
|
||||
const closeButton = n8n.page.locator('.el-message-box').locator('button:has-text("Close")');
|
||||
const closeButton = new MessageBox(n8n.page).buttonByText('Close');
|
||||
await closeButton.click();
|
||||
|
||||
await n8n.ndv.getResourceLocatorInput('documentId').click();
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user