chore: Route instance-ai benchmark tabs through n8n facade (#32512)

Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Declan Carroll <declan@n8n.io>
This commit is contained in:
n8n-cat-bot[bot]
2026-06-18 11:32:50 +00:00
committed by GitHub
parent 7230fa3840
commit 051a20e9f9
5 changed files with 280 additions and 441 deletions
+245 -421
View File
@@ -1,7 +1,7 @@
{
"version": 1,
"generated": "2026-05-25T15:20:35.251Z",
"totalViolations": 432,
"generated": "2026-06-18T10:40:46.686Z",
"totalViolations": 406,
"violations": {
"pages/AIAssistantPage.ts": [
{
@@ -59,6 +59,18 @@
"line": 140,
"message": "InstanceAiPage: Unscoped locator - use this.container instead of this.page",
"hash": "713916b2be0f"
},
{
"rule": "scope-lockdown",
"line": 259,
"message": "InstanceAiPage: Unscoped locator - use this.container instead of this.page",
"hash": "713916b2be0f"
},
{
"rule": "scope-lockdown",
"line": 259,
"message": "InstanceAiPage: Unscoped locator - use this.container instead of this.page",
"hash": "713916b2be0f"
}
],
"pages/NodeDetailsViewPage.ts": [
@@ -404,25 +416,49 @@
},
{
"rule": "scope-lockdown",
"line": 99,
"line": 120,
"message": "WorkflowSettingsModal: Unscoped locator - use this.container instead of this.page",
"hash": "d38a3afc984b"
},
{
"rule": "scope-lockdown",
"line": 125,
"line": 124,
"message": "WorkflowSettingsModal: Unscoped locator - use this.container instead of this.page",
"hash": "d38a3afc984b"
},
{
"rule": "scope-lockdown",
"line": 141,
"line": 128,
"message": "WorkflowSettingsModal: Unscoped locator - use this.container instead of this.page",
"hash": "d38a3afc984b"
},
{
"rule": "scope-lockdown",
"line": 145,
"line": 128,
"message": "WorkflowSettingsModal: Unscoped locator - use this.container instead of this.page",
"hash": "d38a3afc984b"
},
{
"rule": "scope-lockdown",
"line": 132,
"message": "WorkflowSettingsModal: Unscoped locator - use this.container instead of this.page",
"hash": "d38a3afc984b"
},
{
"rule": "scope-lockdown",
"line": 164,
"message": "WorkflowSettingsModal: Unscoped locator - use this.container instead of this.page",
"hash": "d38a3afc984b"
},
{
"rule": "scope-lockdown",
"line": 180,
"message": "WorkflowSettingsModal: Unscoped locator - use this.container instead of this.page",
"hash": "d38a3afc984b"
},
{
"rule": "scope-lockdown",
"line": 184,
"message": "WorkflowSettingsModal: Unscoped locator - use this.container instead of this.page",
"hash": "d38a3afc984b"
}
@@ -491,24 +527,6 @@
"line": 126,
"message": "Raw API call detected: fetch(",
"hash": "eb977c9c5bb7"
},
{
"rule": "no-raw-editor-navigation",
"line": 24,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "994c98dcd129"
},
{
"rule": "no-raw-editor-navigation",
"line": 102,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "994c98dcd129"
},
{
"rule": "no-raw-editor-navigation",
"line": 150,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "994c98dcd129"
}
],
"tests/e2e/ai/hitl-for-tools.spec.ts": [
@@ -586,45 +604,15 @@
"tests/e2e/ai/workflow-builder.spec.ts": [
{
"rule": "selector-purity",
"line": 88,
"line": 86,
"message": "Direct page locator call: n8n.page.getByRole('button', { name: 'Execute and refine' })",
"hash": "c6ba18da37ea"
},
{
"rule": "selector-purity",
"line": 119,
"line": 117,
"message": "Direct page locator call: n8n.page.getByText('Task aborted')",
"hash": "6ee85a61151a"
},
{
"rule": "no-raw-editor-navigation",
"line": 48,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "a499a217bb1e"
},
{
"rule": "no-raw-editor-navigation",
"line": 56,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "a499a217bb1e"
},
{
"rule": "no-raw-editor-navigation",
"line": 74,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "a499a217bb1e"
},
{
"rule": "no-raw-editor-navigation",
"line": 93,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "a499a217bb1e"
},
{
"rule": "no-raw-editor-navigation",
"line": 107,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "a499a217bb1e"
}
],
"tests/e2e/api/wait-form-resume.spec.ts": [
@@ -935,18 +923,6 @@
"line": 326,
"message": "Chained locator call in test: formPage.getByText('This worked')",
"hash": "28200563c463"
},
{
"rule": "no-raw-editor-navigation",
"line": 190,
"message": "Raw navigation to the workflow editor: n8n.page.goto(`/workflow/${workflowId}`)",
"hash": "6720dfdb4c5d"
},
{
"rule": "no-raw-editor-navigation",
"line": 304,
"message": "Raw navigation to the workflow editor: n8n.page.goto(`/workflow/${workflowId}`)",
"hash": "6720dfdb4c5d"
}
],
"tests/e2e/nodes/send-and-wait.spec.ts": [
@@ -1002,31 +978,31 @@
"tests/e2e/projects/folders-basic.spec.ts": [
{
"rule": "selector-purity",
"line": 156,
"line": 146,
"message": "Chained locator call in test: n8n.modal.container.getByText(errorMessage, { exact: fals...",
"hash": "4c6fb24eb5b2"
},
{
"rule": "selector-purity",
"line": 160,
"line": 150,
"message": "Chained locator call in test: n8n.modal.container.getByText(emptyErrorMessage)",
"hash": "c524bac65c97"
},
{
"rule": "selector-purity",
"line": 163,
"line": 153,
"message": "Chained locator call in test: n8n.modal.container.getByText(tooLongErrorMessage)",
"hash": "98fd475ead3c"
},
{
"rule": "selector-purity",
"line": 166,
"line": 156,
"message": "Chained locator call in test: n8n.modal.container.getByText(dotsErrorMessage)",
"hash": "73fce4c260f3"
},
{
"rule": "selector-purity",
"line": 182,
"line": 172,
"message": "Chained locator call in test: n8n.notifications .getNotificationByTitleOrContent(FOLDER...",
"hash": "fcfd7b7736c9"
}
@@ -1146,49 +1122,49 @@
"tests/e2e/projects/projects.spec.ts": [
{
"rule": "selector-purity",
"line": 90,
"line": 111,
"message": "Chained locator call in test: n8n.workflows.cards.getWorkflow('My workflow').getByText(...",
"hash": "69f6ad53e671"
},
{
"rule": "selector-purity",
"line": 167,
"line": 188,
"message": "Direct page locator call: subn8n.page.getByRole('heading', { name: 'My Sub-Workflow...",
"hash": "e4e2c9dc4c76"
},
{
"rule": "selector-purity",
"line": 167,
"line": 188,
"message": "Chained locator call in test: subn8n.page.getByRole('heading', { name: 'My Sub-Workflow...",
"hash": "b6043abb9fdc"
},
{
"rule": "selector-purity",
"line": 172,
"line": 193,
"message": "Direct page locator call: subn8n.page.getByRole('heading', { name: 'Notion account' })",
"hash": "28156d4663d0"
},
{
"rule": "selector-purity",
"line": 172,
"line": 193,
"message": "Chained locator call in test: subn8n.page.getByRole('heading', { name: 'Notion account' })",
"hash": "f2522e66d3fb"
},
{
"rule": "selector-purity",
"line": 212,
"line": 233,
"message": "Chained locator call in test: n8n.projectSettings.getIconPickerButton().locator('svg')",
"hash": "c3b46b88129a"
},
{
"rule": "selector-purity",
"line": 249,
"line": 270,
"message": "Direct page locator call: n8n.page.locator('body').click()",
"hash": "9a5bc33e5ad4"
},
{
"rule": "selector-purity",
"line": 249,
"line": 270,
"message": "Direct page locator call: n8n.page.locator('body')",
"hash": "779bc814648c"
}
@@ -1245,62 +1221,6 @@
"hash": "8f8319b575f7"
}
],
"tests/e2e/settings/environments/source-control.spec.ts": [
{
"rule": "selector-purity",
"line": 57,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'main' })",
"hash": "badb2e5e3d4c"
},
{
"rule": "selector-purity",
"line": 76,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'main' })",
"hash": "badb2e5e3d4c"
},
{
"rule": "selector-purity",
"line": 77,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'development' })",
"hash": "86b81f99aaa9"
},
{
"rule": "selector-purity",
"line": 78,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'staging' })",
"hash": "3e006a300719"
},
{
"rule": "selector-purity",
"line": 79,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'production' })",
"hash": "75c1cdb98932"
},
{
"rule": "selector-purity",
"line": 80,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'development' }).cli...",
"hash": "6d02b5aa3222"
},
{
"rule": "selector-purity",
"line": 80,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'development' })",
"hash": "86b81f99aaa9"
},
{
"rule": "api-purity",
"line": 84,
"message": "Raw API call detected: request.get(",
"hash": "6f173bb174c9"
},
{
"rule": "api-purity",
"line": 93,
"message": "Raw API call detected: request.get(",
"hash": "6f173bb174c9"
}
],
"tests/e2e/sharing/access-control.spec.ts": [
{
"rule": "selector-purity",
@@ -1383,62 +1303,6 @@
"hash": "d59a8ba2103d"
}
],
"tests/e2e/workflows/checklist/production-checklist.spec.ts": [
{
"rule": "selector-purity",
"line": 68,
"message": "Direct page locator call: n8n.page.getByTestId('workflow-settings-dialog')",
"hash": "dcfe3de9f694"
},
{
"rule": "selector-purity",
"line": 69,
"message": "Direct page locator call: n8n.page.getByTestId('workflow-settings-error-workflow')",
"hash": "e577a775be00"
},
{
"rule": "selector-purity",
"line": 86,
"message": "Direct page locator call: n8n.page.getByTestId('workflow-settings-dialog')",
"hash": "dcfe3de9f694"
},
{
"rule": "selector-purity",
"line": 98,
"message": "Chained locator call in test: n8n.canvas.getProductionChecklistActionItem().first().get...",
"hash": "9dd8a54c4eb2"
},
{
"rule": "selector-purity",
"line": 101,
"message": "Direct page locator call: n8n.page.locator('body').click({ position: { x: 0, y: 0 } })",
"hash": "4d45ef0d0614"
},
{
"rule": "selector-purity",
"line": 101,
"message": "Direct page locator call: n8n.page.locator('body')",
"hash": "977cc8c274c9"
},
{
"rule": "selector-purity",
"line": 139,
"message": "Direct page locator call: n8n.page.getByTestId('workflow-settings-dialog')",
"hash": "dcfe3de9f694"
},
{
"rule": "selector-purity",
"line": 145,
"message": "Chained locator call in test: n8n.canvas .getProductionChecklistActionItem() .first() ....",
"hash": "b461f186b890"
},
{
"rule": "selector-purity",
"line": 162,
"message": "Direct page locator call: n8n.page.locator('.el-message-box')",
"hash": "daa8dd6de062"
}
],
"tests/e2e/workflows/demo-diff.spec.ts": [
{
"rule": "selector-purity",
@@ -1567,6 +1431,176 @@
"hash": "025c4a9f2140"
}
],
"tests/e2e/settings/environments/source-control.spec.ts": [
{
"rule": "selector-purity",
"line": 82,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'main' })",
"hash": "badb2e5e3d4c"
},
{
"rule": "selector-purity",
"line": 101,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'main' })",
"hash": "badb2e5e3d4c"
},
{
"rule": "selector-purity",
"line": 102,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'development' })",
"hash": "86b81f99aaa9"
},
{
"rule": "selector-purity",
"line": 103,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'staging' })",
"hash": "3e006a300719"
},
{
"rule": "selector-purity",
"line": 104,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'production' })",
"hash": "75c1cdb98932"
},
{
"rule": "selector-purity",
"line": 105,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'development' }).cli...",
"hash": "6d02b5aa3222"
},
{
"rule": "selector-purity",
"line": 105,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'development' })",
"hash": "86b81f99aaa9"
},
{
"rule": "api-purity",
"line": 109,
"message": "Raw API call detected: request.get(",
"hash": "6f173bb174c9"
},
{
"rule": "api-purity",
"line": 118,
"message": "Raw API call detected: request.get(",
"hash": "6f173bb174c9"
}
],
"tests/e2e/workflows/checklist/production-checklist.spec.ts": [
{
"rule": "selector-purity",
"line": 68,
"message": "Direct page locator call: n8n.page.getByTestId('workflow-settings-dialog')",
"hash": "dcfe3de9f694"
},
{
"rule": "selector-purity",
"line": 69,
"message": "Direct page locator call: n8n.page.getByTestId('workflow-settings-error-workflow')",
"hash": "e577a775be00"
},
{
"rule": "selector-purity",
"line": 86,
"message": "Direct page locator call: n8n.page.getByTestId('workflow-settings-dialog')",
"hash": "dcfe3de9f694"
},
{
"rule": "selector-purity",
"line": 98,
"message": "Chained locator call in test: n8n.canvas.getProductionChecklistActionItem().first().get...",
"hash": "9dd8a54c4eb2"
},
{
"rule": "selector-purity",
"line": 101,
"message": "Direct page locator call: n8n.page.locator('body').click({ position: { x: 0, y: 0 } })",
"hash": "4d45ef0d0614"
},
{
"rule": "selector-purity",
"line": 101,
"message": "Direct page locator call: n8n.page.locator('body')",
"hash": "977cc8c274c9"
},
{
"rule": "selector-purity",
"line": 139,
"message": "Direct page locator call: n8n.page.getByTestId('workflow-settings-dialog')",
"hash": "dcfe3de9f694"
},
{
"rule": "selector-purity",
"line": 145,
"message": "Chained locator call in test: n8n.canvas .getProductionChecklistActionItem() .first() ....",
"hash": "b461f186b890"
},
{
"rule": "selector-purity",
"line": 162,
"message": "Direct page locator call: n8n.page.locator('.el-message-box')",
"hash": "daa8dd6de062"
}
],
"tests/e2e/workflows/editor/tags.spec.ts": [
{
"rule": "selector-purity",
"line": 64,
"message": "Chained locator call in test: n8n.canvas.tagsManagerModal.getTable().getByText(tag1)",
"hash": "162205d16d7b"
},
{
"rule": "selector-purity",
"line": 69,
"message": "Chained locator call in test: n8n.canvas.tagsManagerModal.getTable().getByText(tag2)",
"hash": "4158ea621594"
},
{
"rule": "selector-purity",
"line": 215,
"message": "Chained locator call in test: n8n.canvas.getVisibleDropdown().locator('li')",
"hash": "0eb6824192ab"
}
],
"tests/e2e/workflows/executions/list.spec.ts": [
{
"rule": "selector-purity",
"line": 51,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'Success' }).click()",
"hash": "b6e254523b81"
},
{
"rule": "selector-purity",
"line": 51,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'Success' })",
"hash": "553272ee7950"
},
{
"rule": "selector-purity",
"line": 324,
"message": "Direct page locator call: n8n.page.getByTestId('workflow-execution-no-trigger-conte...",
"hash": "9f1081df0a8a"
},
{
"rule": "selector-purity",
"line": 326,
"message": "Direct page locator call: n8n.page.getByRole('button', { name: 'Add first step' })....",
"hash": "3b12669e6a7f"
},
{
"rule": "selector-purity",
"line": 326,
"message": "Direct page locator call: n8n.page.getByRole('button', { name: 'Add first step' })",
"hash": "ff575079b421"
},
{
"rule": "selector-purity",
"line": 332,
"message": "Direct page locator call: n8n.page.getByTestId('workflow-execution-no-content')",
"hash": "10cc160abd6b"
}
],
"tests/e2e/workflows/editor/canvas/canvas-nodes.spec.ts": [
{
"rule": "selector-purity",
@@ -1876,55 +1910,35 @@
"tests/e2e/workflows/editor/ndv/resource-locator.spec.ts": [
{
"rule": "selector-purity",
"line": 90,
"line": 93,
"message": "Direct page locator call: n8n.page.locator('.el-message-box').locator('button:has-t...",
"hash": "68c3ed65550b"
},
{
"rule": "selector-purity",
"line": 90,
"line": 93,
"message": "Chained locator call in test: n8n.page.locator('.el-message-box').locator('button:has-t...",
"hash": "189e3887535d"
},
{
"rule": "selector-purity",
"line": 90,
"line": 93,
"message": "Direct page locator call: n8n.page.locator('.el-message-box')",
"hash": "423a23988aea"
},
{
"rule": "selector-purity",
"line": 131,
"line": 136,
"message": "Chained locator call in test: visiblePopper.getByTestId('rlc-item')",
"hash": "7e16cbf0596b"
},
{
"rule": "selector-purity",
"line": 145,
"line": 149,
"message": "Chained locator call in test: visiblePopperAfter.getByTestId('rlc-item')",
"hash": "6ccf615ddb0b"
}
],
"tests/e2e/workflows/editor/tags.spec.ts": [
{
"rule": "selector-purity",
"line": 64,
"message": "Chained locator call in test: n8n.canvas.tagsManagerModal.getTable().getByText(tag1)",
"hash": "162205d16d7b"
},
{
"rule": "selector-purity",
"line": 69,
"message": "Chained locator call in test: n8n.canvas.tagsManagerModal.getTable().getByText(tag2)",
"hash": "4158ea621594"
},
{
"rule": "selector-purity",
"line": 215,
"message": "Chained locator call in test: n8n.canvas.getVisibleDropdown().locator('li')",
"hash": "0eb6824192ab"
}
],
"tests/e2e/workflows/editor/workflow-actions/archive.spec.ts": [
{
"rule": "selector-purity",
@@ -2109,92 +2123,6 @@
"hash": "50ecfdb062fd"
}
],
"tests/e2e/workflows/executions/list.spec.ts": [
{
"rule": "selector-purity",
"line": 51,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'Success' }).click()",
"hash": "b6e254523b81"
},
{
"rule": "selector-purity",
"line": 51,
"message": "Direct page locator call: n8n.page.getByRole('option', { name: 'Success' })",
"hash": "553272ee7950"
},
{
"rule": "selector-purity",
"line": 196,
"message": "Chained locator call in test: iframe.locator('body')",
"hash": "6b73753fc26c"
},
{
"rule": "selector-purity",
"line": 237,
"message": "Chained locator call in test: iframe.locator('body')",
"hash": "6b73753fc26c"
},
{
"rule": "selector-purity",
"line": 240,
"message": "Chained locator call in test: iframe.locator('body')",
"hash": "6b73753fc26c"
},
{
"rule": "selector-purity",
"line": 243,
"message": "Chained locator call in test: iframe.locator('body')",
"hash": "6b73753fc26c"
},
{
"rule": "selector-purity",
"line": 246,
"message": "Chained locator call in test: iframe.locator('body')",
"hash": "6b73753fc26c"
},
{
"rule": "selector-purity",
"line": 249,
"message": "Chained locator call in test: iframe.locator('body')",
"hash": "6b73753fc26c"
},
{
"rule": "selector-purity",
"line": 252,
"message": "Chained locator call in test: iframe.locator('body')",
"hash": "6b73753fc26c"
},
{
"rule": "selector-purity",
"line": 255,
"message": "Chained locator call in test: iframe.locator('body')",
"hash": "6b73753fc26c"
},
{
"rule": "selector-purity",
"line": 325,
"message": "Direct page locator call: n8n.page.getByTestId('workflow-execution-no-trigger-conte...",
"hash": "9f1081df0a8a"
},
{
"rule": "selector-purity",
"line": 327,
"message": "Direct page locator call: n8n.page.getByRole('button', { name: 'Add first step' })....",
"hash": "3b12669e6a7f"
},
{
"rule": "selector-purity",
"line": 327,
"message": "Direct page locator call: n8n.page.getByRole('button', { name: 'Add first step' })",
"hash": "ff575079b421"
},
{
"rule": "selector-purity",
"line": 333,
"message": "Direct page locator call: n8n.page.getByTestId('workflow-execution-no-content')",
"hash": "10cc160abd6b"
}
],
"composables/BuilderWizardComposer.ts": [
{
"rule": "no-page-in-flow",
@@ -2274,25 +2202,31 @@
"composables/TestEntryComposer.ts": [
{
"rule": "no-page-in-flow",
"line": 44,
"line": 45,
"message": "Direct page access in composable: this.n8n.page.goto",
"hash": "bdb7602d2cb2"
},
{
"rule": "no-page-in-flow",
"line": 62,
"line": 63,
"message": "Direct page access in composable: this.n8n.page.goto",
"hash": "bdb7602d2cb2"
},
{
"rule": "no-page-in-flow",
"line": 72,
"line": 90,
"message": "Direct page access in composable: this.n8n.page.waitForEvent",
"hash": "28286eb6437c"
},
{
"rule": "no-page-in-flow",
"line": 102,
"line": 106,
"message": "Direct page access in composable: this.n8n.page.context",
"hash": "786c6592ceb9"
},
{
"rule": "no-page-in-flow",
"line": 132,
"message": "Direct page access in composable: this.n8n.page.context",
"hash": "786c6592ceb9"
}
@@ -2584,7 +2518,7 @@
"tests/e2e/instance-ai/instance-ai-timeline.spec.ts": [
{
"rule": "duplicate-logic",
"line": 11,
"line": 12,
"message": "Duplicate test logic: \"should show artifact cards after workflow build completes\" has identical structure to tests/e2e/instance-ai/instance-ai-artifacts.spec.ts:\"should display artifact card in timeline after workflow build\"",
"hash": "da9226aef0eb"
}
@@ -2624,7 +2558,7 @@
"tests/infrastructure/benchmarks/webhook/webhook-dedicated-proc-2wp-2w.spec.ts": [
{
"rule": "duplicate-logic",
"line": 44,
"line": 33,
"message": "Duplicate test logic: \"anonymous\" has identical structure to tests/infrastructure/benchmarks/webhook/webhook-dedicated-proc-2wp-1w.spec.ts:\"anonymous\"",
"hash": "bc07737c363b"
}
@@ -2632,7 +2566,7 @@
"tests/infrastructure/benchmarks/webhook/webhook-dedicated-proc-baseline.spec.ts": [
{
"rule": "duplicate-logic",
"line": 38,
"line": 33,
"message": "Duplicate test logic: \"anonymous\" has identical structure to tests/infrastructure/benchmarks/webhook/webhook-dedicated-proc-2wp-1w.spec.ts:\"anonymous\"",
"hash": "20c97ee82b2c"
}
@@ -2660,116 +2594,6 @@
"message": "Duplicate test logic: \"heap returns to baseline after parallel build rounds\" has identical structure to tests/infrastructure/benchmarks-local/instance-ai/thread-churn-datatables.spec.ts:\"heap returns to baseline after data table create/delete rounds\"",
"hash": "3ac5475c5814"
}
],
"tests/infrastructure/benchmarks-local/instance-ai/cancel-abort.spec.ts": [
{
"rule": "no-direct-page-instantiation",
"line": 38,
"message": "Direct page instantiation: new InstanceAiPage(page)",
"hash": "4464b47c5e2c"
}
],
"tests/infrastructure/benchmarks-local/instance-ai/sse-reconnection.spec.ts": [
{
"rule": "no-direct-page-instantiation",
"line": 44,
"message": "Direct page instantiation: new InstanceAiPage(threadPage)",
"hash": "99e5606866b2"
}
],
"tests/e2e/ai/assistant-basic.spec.ts": [
{
"rule": "no-raw-editor-navigation",
"line": 33,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "e6d03454a502"
},
{
"rule": "no-raw-editor-navigation",
"line": 43,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "e6d03454a502"
},
{
"rule": "no-raw-editor-navigation",
"line": 66,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "e6d03454a502"
},
{
"rule": "no-raw-editor-navigation",
"line": 199,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "e6d03454a502"
},
{
"rule": "no-raw-editor-navigation",
"line": 257,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "e6d03454a502"
},
{
"rule": "no-raw-editor-navigation",
"line": 278,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "e6d03454a502"
}
],
"tests/e2e/ai/assistant-support-chat.spec.ts": [
{
"rule": "no-raw-editor-navigation",
"line": 27,
"message": "Raw navigation to the workflow editor: n8n.page.goto('/workflow/new')",
"hash": "b027466aeb23"
}
],
"tests/e2e/nodes/email-send-node.spec.ts": [
{
"rule": "no-raw-editor-navigation",
"line": 77,
"message": "Raw navigation to the workflow editor: n8n.page.goto(`/workflow/${workflowId}`)",
"hash": "888bc0a82e21"
}
],
"tests/e2e/nodes/kafka-nodes.spec.ts": [
{
"rule": "no-raw-editor-navigation",
"line": 94,
"message": "Raw navigation to the workflow editor: n8n.page.goto(`/workflow/${workflowId}`)",
"hash": "fda6b212bcdb"
}
],
"tests/performance/canvas/canvas-execution.spec.ts": [
{
"rule": "no-raw-editor-navigation",
"line": 112,
"message": "Raw navigation to the workflow editor: n8n.page.goto(`/workflow/${workflowId}`)",
"hash": "8abdbe84c922"
}
],
"tests/performance/canvas/canvas-interactions.spec.ts": [
{
"rule": "no-raw-editor-navigation",
"line": 52,
"message": "Raw navigation to the workflow editor: n8n.page.goto(`/workflow/${workflowId}`)",
"hash": "66d9fb632834"
}
],
"tests/performance/canvas/canvas-load.spec.ts": [
{
"rule": "no-raw-editor-navigation",
"line": 48,
"message": "Raw navigation to the workflow editor: n8n.page.goto(`/workflow/${workflowId}`)",
"hash": "9c2ed8cbab2a"
}
],
"tests/e2e/workflows/editor/routing.spec.ts": [
{
"rule": "no-raw-editor-navigation",
"line": 110,
"message": "Raw navigation to the workflow editor: n8n.page.goto(`/workflow/${workflowId}?new=true`)",
"hash": "c42880a071b0"
}
]
}
}
@@ -96,6 +96,18 @@ export class TestEntryComposer {
return new n8nPageConstructor(newPage);
}
/**
* Open a fresh tab in the current browser context (shared session) and
* return an n8nPage facade bound to it. Use for multi-tab scenarios such
* as the instance-ai memory benchmarks that drive several threads in
* parallel within the same authenticated context.
*/
async newTab(): Promise<n8nPage> {
const newPage = await this.n8n.page.context().newPage();
const n8nPageConstructor = this.n8n.constructor as new (page: Page) => n8nPage;
return new n8nPageConstructor(newPage);
}
/**
* Enable project feature set
* Allow project creation, sharing, and folder creation
@@ -1,5 +1,4 @@
import { test, expect, instanceAiTestConfig } from './fixtures';
import { InstanceAiPage } from '../../../../pages/InstanceAiPage';
import { BENCHMARK_PROMPTS, WARMUP_PROMPT } from '../../../../utils/benchmark/instance-ai-driver';
import { runMemoryBenchmark, type MemoryPhase } from '../harness/memory-harness';
@@ -34,8 +33,8 @@ test.describe(
name: `cancel-${i + 1}`,
action: async () => {
// Open a tab, send prompt, then cancel mid-flight
const page = await n8n.page.context().newPage();
const ai = new InstanceAiPage(page);
const tab = await n8n.start.newTab();
const { page, instanceAi: ai } = tab;
await page.goto('/instance-ai');
await ai.getContainer().waitFor({ state: 'visible', timeout: 15_000 });
@@ -1,5 +1,4 @@
import { test, expect, instanceAiTestConfig } from './fixtures';
import { InstanceAiPage } from '../../../../pages/InstanceAiPage';
import { BENCHMARK_PROMPTS } from '../../../../utils/benchmark/instance-ai-driver';
import { runMemoryBenchmark, type MemoryPhase } from '../harness/memory-harness';
@@ -29,7 +28,7 @@ test.describe(
// First: build one workflow so the thread has content
const results = await driver.runParallel([BENCHMARK_PROMPTS[0]]);
const threadId = results[0]?.threadId;
const threadPage = driver['openedPages'][0];
const threadTab = driver.tabs[0];
const phases: MemoryPhase[] = [];
@@ -38,11 +37,12 @@ test.describe(
phases.push({
name: `reconnect-${i + 1}`,
action: async () => {
await threadPage.goto('/home/workflows');
await threadPage.waitForLoadState('load');
await threadPage.goto(`/instance-ai/${threadId}`);
const ai = new InstanceAiPage(threadPage);
await ai.getChatInput().waitFor({ state: 'visible', timeout: 10_000 });
await threadTab.page.goto('/home/workflows');
await threadTab.page.waitForLoadState('load');
await threadTab.page.goto(`/instance-ai/${threadId}`);
await threadTab.instanceAi
.getChatInput()
.waitFor({ state: 'visible', timeout: 10_000 });
},
measureAfter: (i + 1) % 5 === 0 || i === RECONNECT_CYCLES - 1,
});
@@ -1,6 +1,5 @@
import type { Page } from '@playwright/test';
import { InstanceAiPage } from '../../pages/InstanceAiPage';
import type { n8nPage } from '../../pages/n8nPage';
/** Lightweight prompt for warmup — exercises the instance-ai path without building a workflow. */
@@ -66,13 +65,18 @@ export class InstanceAiDriver {
private readonly n8n: n8nPage;
private readonly baseUrl: string;
private createdThreadIds: string[] = [];
private openedPages: Page[] = [];
private openedTabs: n8nPage[] = [];
constructor(config: InstanceAiDriverConfig) {
this.n8n = config.n8n;
this.baseUrl = config.baseUrl;
}
/** n8nPage facades for tabs opened by `runParallel`, in creation order. */
get tabs(): readonly n8nPage[] {
return this.openedTabs;
}
/**
* Run prompts in parallel tabs. Each prompt gets its own tab and thread.
* Returns when ALL prompts have finished building (or timed out).
@@ -82,16 +86,16 @@ export class InstanceAiDriver {
options: RunParallelOptions = {},
): Promise<TabRunResult[]> {
const { timeoutMs = 180_000, settleMs = 5_000 } = options;
const context = this.n8n.page.context();
console.log(`[INSTANCE-AI] Starting ${prompts.length} parallel builds`);
// Open tabs and send prompts
const tabHandles = await Promise.all(
prompts.map(async (prompt, i) => {
const page = await context.newPage();
this.openedPages.push(page);
const ai = new InstanceAiPage(page);
const tab = await this.n8n.start.newTab();
this.openedTabs.push(tab);
const page = tab.page;
const ai = tab.instanceAi;
// Navigate and wait for UI ready
await page.goto('/instance-ai');
@@ -221,12 +225,12 @@ export class InstanceAiDriver {
/** Close all tabs opened by `runParallel`. */
async closeAllTabs(): Promise<void> {
for (const page of this.openedPages) {
if (!page.isClosed()) {
await page.close();
for (const tab of this.openedTabs) {
if (!tab.page.isClosed()) {
await tab.page.close();
}
}
this.openedPages = [];
this.openedTabs = [];
}
/** Delete a thread via the REST API. */