mirror of
https://github.com/n8n-io/n8n.git
synced 2026-09-01 15:47:41 +08:00
chore: Route composable navigation & URL reads through NavigationHelper (clear no-page-in-flow drift) (#34982)
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:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"generated": "2026-07-17T15:21:33.743Z",
|
||||
"totalViolations": 61,
|
||||
"generated": "2026-07-27T03:21:27.407Z",
|
||||
"totalViolations": 56,
|
||||
"violations": {
|
||||
"pages/AIAssistantPage.ts": [
|
||||
{
|
||||
@@ -217,20 +217,6 @@
|
||||
"hash": "ceda398a2fc4"
|
||||
}
|
||||
],
|
||||
"composables/CanvasComposer.ts": [
|
||||
{
|
||||
"rule": "no-page-in-flow",
|
||||
"line": 138,
|
||||
"message": "Direct page access in composable: this.n8n.page.url",
|
||||
"hash": "c978979c3eac"
|
||||
},
|
||||
{
|
||||
"rule": "no-page-in-flow",
|
||||
"line": 148,
|
||||
"message": "Direct page access in composable: this.n8n.page.url",
|
||||
"hash": "c978979c3eac"
|
||||
}
|
||||
],
|
||||
"composables/ExecutionsComposer.ts": [
|
||||
{
|
||||
"rule": "no-page-in-flow",
|
||||
@@ -245,27 +231,7 @@
|
||||
"hash": "ff02b5de4505"
|
||||
}
|
||||
],
|
||||
"composables/ProjectComposer.ts": [
|
||||
{
|
||||
"rule": "no-page-in-flow",
|
||||
"line": 51,
|
||||
"message": "Direct page access in composable: this.n8n.page.url",
|
||||
"hash": "378ec8b3ce94"
|
||||
}
|
||||
],
|
||||
"composables/TestEntryComposer.ts": [
|
||||
{
|
||||
"rule": "no-page-in-flow",
|
||||
"line": 45,
|
||||
"message": "Direct page access in composable: this.n8n.page.goto",
|
||||
"hash": "bdb7602d2cb2"
|
||||
},
|
||||
{
|
||||
"rule": "no-page-in-flow",
|
||||
"line": 63,
|
||||
"message": "Direct page access in composable: this.n8n.page.goto",
|
||||
"hash": "bdb7602d2cb2"
|
||||
},
|
||||
{
|
||||
"rule": "no-page-in-flow",
|
||||
"line": 90,
|
||||
|
||||
@@ -135,7 +135,7 @@ export class CanvasComposer {
|
||||
* @returns The workflow URL after save
|
||||
*/
|
||||
async waitForWorkflowSaveAndUrl(): Promise<string> {
|
||||
const isNewWorkflow = this.n8n.page.url().includes('/workflow/new');
|
||||
const isNewWorkflow = this.n8n.navigate.currentUrl().includes('/workflow/new');
|
||||
|
||||
if (isNewWorkflow) {
|
||||
await this.n8n.canvas.waitForSaveWorkflowCompleted();
|
||||
@@ -145,6 +145,6 @@ export class CanvasComposer {
|
||||
await this.n8n.canvas.waitForSaveWorkflowCompleted();
|
||||
}
|
||||
|
||||
return this.n8n.page.url();
|
||||
return this.n8n.navigate.currentUrl();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,6 @@ export class ProjectComposer {
|
||||
}
|
||||
|
||||
extractProjectIdFromPage(beforeWord: string, afterWord: string): string {
|
||||
return this.extractIdFromUrl(this.n8n.page.url(), beforeWord, afterWord);
|
||||
return this.extractIdFromUrl(this.n8n.navigate.currentUrl(), beforeWord, afterWord);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export class TestEntryComposer {
|
||||
const response = await this.n8n.api.projects.createProject();
|
||||
|
||||
const projectId = response.id;
|
||||
await this.n8n.page.goto(`workflow/new?projectId=${projectId}`);
|
||||
await this.n8n.navigate.toWorkflow('new', { projectId });
|
||||
await this.n8n.canvas.waitForBlankCanvasReady();
|
||||
return projectId;
|
||||
}
|
||||
@@ -60,7 +60,7 @@ export class TestEntryComposer {
|
||||
*/
|
||||
async fromImportedWorkflow(workflowFile: string) {
|
||||
const workflowImportResult = await this.n8n.api.workflows.importWorkflowFromFile(workflowFile);
|
||||
await this.n8n.page.goto(`workflow/${workflowImportResult.workflowId}`);
|
||||
await this.n8n.navigate.toWorkflow(workflowImportResult.workflowId);
|
||||
// Wait for the canvas loading overlay to clear and the imported nodes to
|
||||
// render before returning, so tests don't interact with a canvas that is
|
||||
// still covered by the full-screen loader.
|
||||
|
||||
@@ -88,7 +88,7 @@ export async function viewExecutionsListAsAdmin(
|
||||
(r) => r.url().includes('/rest/executions') && r.status() === 200,
|
||||
{ timeout: 120_000 },
|
||||
);
|
||||
await n8n.page.goto(`/projects/${ctx.project.id}/executions`, {
|
||||
await n8n.navigate.toProjectExecutions(ctx.project.id, {
|
||||
waitUntil: 'commit',
|
||||
timeout: 120_000,
|
||||
});
|
||||
|
||||
@@ -98,13 +98,27 @@ export class NavigationHelper {
|
||||
* URLs:
|
||||
* - New workflow: /workflow/new
|
||||
* - Existing workflow: /workflow/{workflowId}
|
||||
* - Project workflow: /projects/{projectId}/workflow/{workflowId}
|
||||
* - New workflow in a project: /workflow/new?projectId={projectId}
|
||||
*/
|
||||
async toWorkflow(workflowId: string = 'new'): Promise<void> {
|
||||
const url = `/workflow/${workflowId}`;
|
||||
async toWorkflow(workflowId: string = 'new', options?: { projectId?: string }): Promise<void> {
|
||||
let url = `/workflow/${workflowId}`;
|
||||
if (options?.projectId) {
|
||||
url += `?projectId=${options.projectId}`;
|
||||
}
|
||||
await this.page.goto(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a project's executions list
|
||||
* URL: /projects/{projectId}/executions
|
||||
*/
|
||||
async toProjectExecutions(
|
||||
projectId: string,
|
||||
options?: Parameters<Page['goto']>[1],
|
||||
): Promise<void> {
|
||||
await this.page.goto(`/projects/${projectId}/executions`, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a specific execution within a workflow
|
||||
* URLs:
|
||||
@@ -268,4 +282,9 @@ export class NavigationHelper {
|
||||
async toExternalSecrets(): Promise<void> {
|
||||
await this.secretsProviderSettings.goto();
|
||||
}
|
||||
|
||||
/** Current page URL — use instead of reaching into n8n.page.url() from flows. */
|
||||
currentUrl(): string {
|
||||
return this.page.url();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user