diff --git a/packages/testing/playwright/.janitor-baseline.json b/packages/testing/playwright/.janitor-baseline.json index 7939ecc1a2b..b75d793ade4 100644 --- a/packages/testing/playwright/.janitor-baseline.json +++ b/packages/testing/playwright/.janitor-baseline.json @@ -1,7 +1,7 @@ { "version": 1, - "generated": "2026-07-15T03:17:41.383Z", - "totalViolations": 65, + "generated": "2026-07-17T06:17:55.330Z", + "totalViolations": 63, "violations": { "pages/AIAssistantPage.ts": [ { @@ -367,22 +367,6 @@ "hash": "974d7953dd83" } ], - "pages/WorkflowsPage.ts": [ - { - "rule": "deduplication", - "line": 40, - "message": "Duplicate locator: this.page.getByTestId(\"project-name\") in pages scope", - "hash": "206b89bd1594" - } - ], - "pages/WorkerViewPage.ts": [ - { - "rule": "deduplication", - "line": 13, - "message": "Duplicate locator: this.page.getByTestId(\"menu-item\") in pages scope", - "hash": "5a1818f5f511" - } - ], "tests/infrastructure/benchmarks/kafka/queue-mode-sustained-rate.spec.ts": [ { "rule": "duplicate-logic", diff --git a/packages/testing/playwright/pages/ProjectSettingsPage.ts b/packages/testing/playwright/pages/ProjectSettingsPage.ts index 9791f498647..81fc1cae54f 100644 --- a/packages/testing/playwright/pages/ProjectSettingsPage.ts +++ b/packages/testing/playwright/pages/ProjectSettingsPage.ts @@ -2,8 +2,11 @@ import type { Locator } from '@playwright/test'; import { expect } from '@playwright/test'; import { BasePage } from './BasePage'; +import { ProjectHeader } from './components/ProjectHeader'; export class ProjectSettingsPage extends BasePage { + readonly projectHeader = new ProjectHeader(this.page); + async goto(projectId: string) { await this.page.goto(`/projects/${projectId}/settings`); } @@ -93,7 +96,7 @@ export class ProjectSettingsPage extends BasePage { } getTitle() { - return this.page.getByTestId('project-name'); + return this.projectHeader.getProjectName(); } // Robust value assertions on inner form controls diff --git a/packages/testing/playwright/pages/SettingsPersonalPage.ts b/packages/testing/playwright/pages/SettingsPersonalPage.ts index bab311b3331..c8c60d10ec4 100644 --- a/packages/testing/playwright/pages/SettingsPersonalPage.ts +++ b/packages/testing/playwright/pages/SettingsPersonalPage.ts @@ -1,13 +1,16 @@ import type { Locator } from '@playwright/test'; import { BasePage } from './BasePage'; +import { SettingsSidebar } from './components/SettingsSidebar'; /** * Page object for Settings including Personal Settings where users can update their profile and manage MFA. */ export class SettingsPersonalPage extends BasePage { + readonly settingsSidebar = new SettingsSidebar(this.page); + getMenuItems() { - return this.page.getByTestId('menu-item'); + return this.settingsSidebar.getMenuItems(); } async gotoSettings() { diff --git a/packages/testing/playwright/pages/WorkerViewPage.ts b/packages/testing/playwright/pages/WorkerViewPage.ts index e9a79375ec5..3747f69df17 100644 --- a/packages/testing/playwright/pages/WorkerViewPage.ts +++ b/packages/testing/playwright/pages/WorkerViewPage.ts @@ -1,6 +1,9 @@ import { BasePage } from './BasePage'; +import { SettingsSidebar } from './components/SettingsSidebar'; export class WorkerViewPage extends BasePage { + readonly settingsSidebar = new SettingsSidebar(this.page); + getWorkerViewLicensed() { return this.page.getByTestId('worker-view-licensed'); } @@ -10,7 +13,7 @@ export class WorkerViewPage extends BasePage { } getWorkerMenuItem() { - return this.page.getByTestId('menu-item').getByText('Workers', { exact: true }); + return this.settingsSidebar.getMenuItems().getByText('Workers', { exact: true }); } async goto() { diff --git a/packages/testing/playwright/pages/WorkflowsPage.ts b/packages/testing/playwright/pages/WorkflowsPage.ts index fd4123f41f8..6db844e0761 100644 --- a/packages/testing/playwright/pages/WorkflowsPage.ts +++ b/packages/testing/playwright/pages/WorkflowsPage.ts @@ -3,6 +3,7 @@ import type { Locator } from '@playwright/test'; import { BasePage } from './BasePage'; import { ActionToggle } from './components/ActionToggle'; import { AddResource } from './components/AddResource'; +import { ProjectHeader } from './components/ProjectHeader'; import { ResourceCards } from './components/ResourceCards'; import { ResourceMoveModal } from './components/ResourceMoveModal'; @@ -15,6 +16,7 @@ export class WorkflowsPage extends BasePage { readonly cards = new ResourceCards(this.page); readonly actionToggle = new ActionToggle(this.page); readonly resourceMoveModal = new ResourceMoveModal(this.page); + readonly projectHeader = new ProjectHeader(this.page); private async openWorkflowCardActions(workflowItem: Locator) { await workflowItem.getByTestId('workflow-card-actions').getByRole('button').click(); @@ -37,7 +39,7 @@ export class WorkflowsPage extends BasePage { } getProjectName() { - return this.page.getByTestId('project-name'); + return this.projectHeader.getProjectName(); } getSearchBar() { diff --git a/packages/testing/playwright/pages/components/ProjectHeader.ts b/packages/testing/playwright/pages/components/ProjectHeader.ts new file mode 100644 index 00000000000..86b1594d1e2 --- /dev/null +++ b/packages/testing/playwright/pages/components/ProjectHeader.ts @@ -0,0 +1,14 @@ +import type { Page } from '@playwright/test'; + +/** + * ProjectHeader component - the project title/breadcrumb header shown at the top + * of project views (workflows list, project settings, etc.). + * Mirrors the ProjectHeader.vue component in the frontend. + */ +export class ProjectHeader { + constructor(private readonly page: Page) {} + + getProjectName() { + return this.page.getByTestId('project-name'); + } +} diff --git a/packages/testing/playwright/pages/components/SettingsSidebar.ts b/packages/testing/playwright/pages/components/SettingsSidebar.ts new file mode 100644 index 00000000000..19022bee384 --- /dev/null +++ b/packages/testing/playwright/pages/components/SettingsSidebar.ts @@ -0,0 +1,13 @@ +import type { Page } from '@playwright/test'; + +/** + * SettingsSidebar component - the left-hand menu shared across the `/settings*` + * routes (personal, workers, etc.). + */ +export class SettingsSidebar { + constructor(private readonly page: Page) {} + + getMenuItems() { + return this.page.getByTestId('menu-item'); + } +}