diff --git a/packages/testing/playwright/.janitor-baseline.json b/packages/testing/playwright/.janitor-baseline.json index c6ab0438226..a4968bf0e37 100644 --- a/packages/testing/playwright/.janitor-baseline.json +++ b/packages/testing/playwright/.janitor-baseline.json @@ -1,7 +1,7 @@ { "version": 1, - "generated": "2026-05-12T08:06:17.170Z", - "totalViolations": 416, + "generated": "2026-05-12T11:24:54.376Z", + "totalViolations": 410, "violations": { "pages/AIAssistantPage.ts": [ { @@ -82,13 +82,7 @@ }, { "rule": "scope-lockdown", - "line": 274, - "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", - "hash": "4087a9cf20cb" - }, - { - "rule": "scope-lockdown", - "line": 323, + "line": 278, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, @@ -106,7 +100,7 @@ }, { "rule": "scope-lockdown", - "line": 351, + "line": 335, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, @@ -136,31 +130,25 @@ }, { "rule": "scope-lockdown", - "line": 373, + "line": 371, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, { "rule": "scope-lockdown", - "line": 399, + "line": 377, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, { "rule": "scope-lockdown", - "line": 435, + "line": 403, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, { "rule": "scope-lockdown", - "line": 463, - "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", - "hash": "4087a9cf20cb" - }, - { - "rule": "scope-lockdown", - "line": 467, + "line": 439, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, @@ -176,6 +164,12 @@ "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, + { + "rule": "scope-lockdown", + "line": 471, + "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", + "hash": "4087a9cf20cb" + }, { "rule": "scope-lockdown", "line": 475, @@ -184,7 +178,7 @@ }, { "rule": "scope-lockdown", - "line": 493, + "line": 479, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, @@ -196,49 +190,43 @@ }, { "rule": "scope-lockdown", - "line": 497, + "line": 501, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, { "rule": "scope-lockdown", - "line": 508, + "line": 501, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, { "rule": "scope-lockdown", - "line": 517, + "line": 512, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, { "rule": "scope-lockdown", - "line": 579, + "line": 521, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, { "rule": "scope-lockdown", - "line": 624, + "line": 583, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, { "rule": "scope-lockdown", - "line": 630, + "line": 628, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, { "rule": "scope-lockdown", - "line": 654, - "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", - "hash": "4087a9cf20cb" - }, - { - "rule": "scope-lockdown", - "line": 654, + "line": 634, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, @@ -250,19 +238,25 @@ }, { "rule": "scope-lockdown", - "line": 765, + "line": 658, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, { "rule": "scope-lockdown", - "line": 810, + "line": 662, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, { "rule": "scope-lockdown", - "line": 864, + "line": 769, + "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", + "hash": "4087a9cf20cb" + }, + { + "rule": "scope-lockdown", + "line": 814, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, @@ -274,13 +268,19 @@ }, { "rule": "scope-lockdown", - "line": 877, + "line": 872, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" }, { "rule": "scope-lockdown", - "line": 883, + "line": 885, + "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", + "hash": "4087a9cf20cb" + }, + { + "rule": "scope-lockdown", + "line": 891, "message": "NodeDetailsViewPage: Unscoped locator - use this.container instead of this.page", "hash": "4087a9cf20cb" } @@ -1547,20 +1547,6 @@ "hash": "e286b40da00e" } ], - "tests/e2e/workflows/editor/expressions/transformation.spec.ts": [ - { - "rule": "selector-purity", - "line": 114, - "message": "Chained locator call in test: n8n.ndv.getOutputDataContainer().locator('[class*=value_]')", - "hash": "5ce9a307e353" - }, - { - "rule": "selector-purity", - "line": 134, - "message": "Chained locator call in test: n8n.ndv.getOutputDataContainer().locator('[class*=value_]')", - "hash": "5ce9a307e353" - } - ], "tests/e2e/workflows/editor/ndv/io-filter.spec.ts": [ { "rule": "selector-purity", @@ -1853,14 +1839,6 @@ "hash": "6ccf615ddb0b" } ], - "tests/e2e/workflows/editor/subworkflows/workflow-selector.spec.ts": [ - { - "rule": "selector-purity", - "line": 92, - "message": "Chained locator call in test: addResourceItem.getByText(/Create a/)", - "hash": "46294c103c93" - } - ], "tests/e2e/workflows/editor/tags.spec.ts": [ { "rule": "selector-purity", @@ -2471,26 +2449,6 @@ "hash": "0acea681877b" } ], - "utils/benchmark/instance-ai-driver.ts": [ - { - "rule": "dead-code", - "line": 263, - "message": "Unused method: InstanceAiDriver.deleteAllThreads()", - "hash": "84fa4accc8fa" - }, - { - "rule": "dead-code", - "line": 302, - "message": "Unused method: InstanceAiDriver.measureHeap()", - "hash": "40ed597f87ba" - }, - { - "rule": "dead-code", - "line": 307, - "message": "Unused method: InstanceAiDriver.snapshot()", - "hash": "70bd4d633512" - } - ], "pages/ChatHubSettingsPage.ts": [ { "rule": "deduplication", diff --git a/packages/testing/playwright/pages/NodeDetailsViewPage.ts b/packages/testing/playwright/pages/NodeDetailsViewPage.ts index eda2a30185b..9669e5162c5 100644 --- a/packages/testing/playwright/pages/NodeDetailsViewPage.ts +++ b/packages/testing/playwright/pages/NodeDetailsViewPage.ts @@ -143,6 +143,10 @@ export class NodeDetailsViewPage extends BasePage { return this.getOutputPanel().getByTestId('ndv-data-container'); } + getOutputDataValues() { + return this.getOutputDataContainer().locator('[class*=value_]'); + } + async setPinnedData(data: object | string) { const pinnedData = typeof data === 'string' ? data : JSON.stringify(data); await this.getEditPinnedDataButton().click(); @@ -868,6 +872,10 @@ export class NodeDetailsViewPage extends BasePage { return this.page.getByTestId('rlc-item-add-resource'); } + getAddResourceCreateOption() { + return this.getAddResourceItem().getByText(/Create a/); + } + getExpressionModeToggle(index: number = 1) { return this.container.getByTestId('radio-button-expression').nth(index); } diff --git a/packages/testing/playwright/tests/e2e/workflows/editor/expressions/transformation.spec.ts b/packages/testing/playwright/tests/e2e/workflows/editor/expressions/transformation.spec.ts index 5022b3c3308..7250b112cd2 100644 --- a/packages/testing/playwright/tests/e2e/workflows/editor/expressions/transformation.spec.ts +++ b/packages/testing/playwright/tests/e2e/workflows/editor/expressions/transformation.spec.ts @@ -111,7 +111,7 @@ test.describe( await expect(n8n.ndv.getInlineExpressionEditorOutput()).toContainText(output); await n8n.ndv.execute(); - const valueElements = n8n.ndv.getOutputDataContainer().locator('[class*=value_]'); + const valueElements = n8n.ndv.getOutputDataValues(); await expect(valueElements).toBeVisible(); await expect(valueElements).toContainText(output); }); @@ -131,7 +131,7 @@ test.describe( await expect(n8n.ndv.getInlineExpressionEditorOutput()).toContainText(output); await n8n.ndv.execute(); - const valueElements = n8n.ndv.getOutputDataContainer().locator('[class*=value_]'); + const valueElements = n8n.ndv.getOutputDataValues(); await expect(valueElements).toBeVisible(); await expect(valueElements).toContainText(output); }); diff --git a/packages/testing/playwright/tests/e2e/workflows/editor/subworkflows/workflow-selector.spec.ts b/packages/testing/playwright/tests/e2e/workflows/editor/subworkflows/workflow-selector.spec.ts index 81720e37981..473ac065270 100644 --- a/packages/testing/playwright/tests/e2e/workflows/editor/subworkflows/workflow-selector.spec.ts +++ b/packages/testing/playwright/tests/e2e/workflows/editor/subworkflows/workflow-selector.spec.ts @@ -89,7 +89,7 @@ test.describe( const addResourceItem = n8n.ndv.getAddResourceItem(); await expect(addResourceItem).toHaveCount(1); - await expect(addResourceItem.getByText(/Create a/)).toBeVisible(); + await expect(n8n.ndv.getAddResourceCreateOption()).toBeVisible(); const secondPage = await n8n.start.fromNewPage(async () => { await n8n.ndvComposer.createNewSubworkflow('workflowId'); diff --git a/packages/testing/playwright/tests/infrastructure/benchmarks-local/instance-ai/fixtures.ts b/packages/testing/playwright/tests/infrastructure/benchmarks-local/instance-ai/fixtures.ts index 49e72f23186..e01ae366b7f 100644 --- a/packages/testing/playwright/tests/infrastructure/benchmarks-local/instance-ai/fixtures.ts +++ b/packages/testing/playwright/tests/infrastructure/benchmarks-local/instance-ai/fixtures.ts @@ -17,11 +17,10 @@ export const instanceAiTestConfig = { } as const; export const test = base.extend({ - instanceAiDriver: async ({ n8n, backendUrl, services }, use) => { + instanceAiDriver: async ({ n8n, backendUrl }, use) => { const config: InstanceAiDriverConfig = { n8n, baseUrl: backendUrl, - metrics: services.observability.metrics, }; const driver = new InstanceAiDriver(config); diff --git a/packages/testing/playwright/utils/benchmark/instance-ai-driver.ts b/packages/testing/playwright/utils/benchmark/instance-ai-driver.ts index bd2ffbde6a6..ec9501e67b5 100644 --- a/packages/testing/playwright/utils/benchmark/instance-ai-driver.ts +++ b/packages/testing/playwright/utils/benchmark/instance-ai-driver.ts @@ -1,14 +1,7 @@ -import type { Page, TestInfo } from '@playwright/test'; -import type { MetricsHelper } from 'n8n-containers'; +import type { Page } from '@playwright/test'; import { InstanceAiPage } from '../../pages/InstanceAiPage'; import type { n8nPage } from '../../pages/n8nPage'; -import { - getStableHeap, - takeHeapSnapshot, - type StableHeapOptions, - type StableHeapResult, -} from '../performance-helper'; /** Lightweight prompt for warmup — exercises the instance-ai path without building a workflow. */ export const WARMUP_PROMPT = 'List my current credentials.'; @@ -42,8 +35,6 @@ export interface InstanceAiDriverConfig { n8n: n8nPage; /** Backend base URL for REST API calls (GC, snapshots, thread management) */ baseUrl: string; - /** VictoriaMetrics helper for heap queries */ - metrics: MetricsHelper; } export interface RunParallelOptions { @@ -74,14 +65,12 @@ export interface TabRunResult { export class InstanceAiDriver { private readonly n8n: n8nPage; private readonly baseUrl: string; - private readonly metrics: MetricsHelper; private createdThreadIds: string[] = []; private openedPages: Page[] = []; constructor(config: InstanceAiDriverConfig) { this.n8n = config.n8n; this.baseUrl = config.baseUrl; - this.metrics = config.metrics; } /** @@ -259,14 +248,6 @@ export class InstanceAiDriver { this.createdThreadIds = this.createdThreadIds.filter((id) => id !== threadId); } - /** Delete all threads created by this driver instance. */ - async deleteAllThreads(): Promise { - const threadIds = [...this.createdThreadIds]; - for (const threadId of threadIds) { - await this.deleteThread(threadId); - } - } - /** Delete all workflows via the REST API to prevent cross-round interference. */ async deleteAllWorkflows(): Promise { const cookies = await this.n8n.page.context().cookies(); @@ -298,16 +279,6 @@ export class InstanceAiDriver { await this.deleteAllWorkflows(); } - /** Take a stable server-side heap measurement (triggers GC + polls VictoriaMetrics). */ - async measureHeap(options?: StableHeapOptions): Promise { - return await getStableHeap(this.baseUrl, this.metrics, options); - } - - /** Trigger a V8 heap snapshot on the server. */ - async snapshot(testInfo: TestInfo, label: string): Promise { - await takeHeapSnapshot(this.baseUrl, testInfo, label); - } - private extractThreadId(page: Page): string { const url = new URL(page.url()); const match = url.pathname.match(/\/instance-ai\/([0-9a-f-]+)/);