Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions packages/testing/playwright/composables/TestEntryComposer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class TestEntryComposer {
await this.n8n.api.setMaxTeamProjectsQuota(-1);

// Create a project using the API
const response = await this.n8n.api.projectApi.createProject();
const response = await this.n8n.api.projects.createProject();

const projectId = response.id;
await this.n8n.page.goto(`workflow/new?projectId=${projectId}`);
Expand All @@ -44,13 +44,9 @@ export class TestEntryComposer {
}

async fromNewProject() {
// Enable features to allow us to create a new project
await this.n8n.api.enableFeature('projectRole:admin');
await this.n8n.api.enableFeature('projectRole:editor');
await this.n8n.api.setMaxTeamProjectsQuota(-1);

await this.withProjectFeatures();
// Create a project using the API
const response = await this.n8n.api.projectApi.createProject();
const response = await this.n8n.api.projects.createProject();

const projectId = response.id;
await this.n8n.navigate.toProject(projectId);
Expand All @@ -66,4 +62,17 @@ export class TestEntryComposer {
await this.n8n.page.goto(`workflow/${workflowImportResult.workflowId}`);
return workflowImportResult;
}

/**
* Enable project feature set
* Allow project creation, sharing, and folder creation
*/
async withProjectFeatures() {
Copy link
Author

@cubic-dev-local cubic-dev-local bot Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fromNewProject now enables project features (folders, sharing) globally for any new-project test, expanding test setup scope and introducing side effects in unrelated tests.

    DEV MODE: This violation would have been filtered out by GPT-5.

Reasoning:
GPT-5: Although fromNewProject now enables additional feature flags (sharing, folders, advancedPermissions), there is no evidence that this results in incorrect behavior or impacts unrelated tests. Without demonstrable negative impact, the issue is not severe enough to report.

Prompt for AI agents
Address the following comment on packages/testing/playwright/composables/TestEntryComposer.ts at line 70:

<comment>`fromNewProject` now enables project features (folders, sharing) globally for any new-project test, expanding test setup scope and introducing side effects in unrelated tests.

        DEV MODE: This violation would have been filtered out by GPT-5.
Reasoning:
• **GPT-5**: Although `fromNewProject` now enables additional feature flags (`sharing`, `folders`, `advancedPermissions`), there is no evidence that this results in incorrect behavior or impacts unrelated tests. Without demonstrable negative impact, the issue is not severe enough to report.</comment>

<file context>
@@ -66,4 +62,17 @@ export class TestEntryComposer {
+	 * Enable project feature set
+	 * Allow project creation, sharing, and folder creation
+	 */
+	async withProjectFeatures() {
+		await this.n8n.api.enableFeature(&#39;sharing&#39;);
+		await this.n8n.api.enableFeature(&#39;folders&#39;);
</file context>

[internal] Confidence score: 9/10

[internal] Posted by: System Design Agent

Fix with Cubic

await this.n8n.api.enableFeature('sharing');
await this.n8n.api.enableFeature('folders');
await this.n8n.api.enableFeature('advancedPermissions');
await this.n8n.api.enableFeature('projectRole:admin');
await this.n8n.api.enableFeature('projectRole:editor');
await this.n8n.api.setMaxTeamProjectsQuota(-1);
}
}
11 changes: 11 additions & 0 deletions packages/testing/playwright/helpers/NavigationHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,17 @@ export class NavigationHelper {
await this.page.goto(url);
}

/**
* Navigate to a specific folder
* URL: /projects/{projectId}/folders/{folderId}
*/
async toFolder(folderId: string, projectId?: string): Promise<void> {
const url = projectId
? `/projects/${projectId}/folders/${folderId}/workflows`
: `/folders/${folderId}/workflows`;
Copy link

@cubic-dev-ai cubic-dev-ai bot Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When projectId is omitted, toFolder navigates to /folders/${folderId}/workflows, but the router only exposes folder routes under /home/folders/:folderId?/workflows. Hitting /folders/... will miss the route and return 404. Prefix the global path with /home so global folder navigation works.

Prompt for AI agents
Address the following comment on packages/testing/playwright/helpers/NavigationHelper.ts at line 129:

<comment>When `projectId` is omitted, `toFolder` navigates to `/folders/${folderId}/workflows`, but the router only exposes folder routes under `/home/folders/:folderId?/workflows`. Hitting `/folders/...` will miss the route and return 404. Prefix the global path with `/home` so global folder navigation works.</comment>

<file context>
@@ -119,6 +119,17 @@ export class NavigationHelper {
+	async toFolder(folderId: string, projectId?: string): Promise&lt;void&gt; {
+		const url = projectId
+			? `/projects/${projectId}/folders/${folderId}/workflows`
+			: `/folders/${folderId}/workflows`;
+		await this.page.goto(url);
+	}
</file context>

[internal] Confidence score: 9/10

[internal] Posted by: General AI Review Agent

Suggested change
: `/folders/${folderId}/workflows`;
: `/home/folders/${folderId}/workflows`;
Fix with Cubic

await this.page.goto(url);
}

/**
* Navigate to workflow canvas (alias for toWorkflow)
*/
Expand Down
3 changes: 3 additions & 0 deletions packages/testing/playwright/pages/BasePage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { Page } from '@playwright/test';

import { BaseModal } from './components/BaseModal';

export abstract class BasePage {
protected readonly baseModal = new BaseModal(this.page);
Copy link
Author

@cubic-dev-local cubic-dev-local bot Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BaseModal is instantiated in a field initializer using this.page before the constructor parameter property is assigned, causing BaseModal to capture undefined under TS 5 field semantics and leading to runtime TypeErrors when modal methods access page.

    DEV MODE: This violation would have been filtered out by GPT-5.

Reasoning:
GPT-5: Because the project targets ES2021, TypeScript emits class fields as constructor assignments after the parameter property assignment; therefore new BaseModal(this.page) receives the initialized page object and the alleged runtime error cannot occur.

Libraries consulted: TypeScript useDefineForClassFields field initialization order, Typescript

Prompt for AI agents
Address the following comment on packages/testing/playwright/pages/BasePage.ts at line 6:

<comment>BaseModal is instantiated in a field initializer using `this.page` before the constructor parameter property is assigned, causing BaseModal to capture `undefined` under TS 5 field semantics and leading to runtime TypeErrors when modal methods access `page`.

        DEV MODE: This violation would have been filtered out by GPT-5.
Reasoning:
• **GPT-5**: Because the project targets ES2021, TypeScript emits class fields as constructor assignments after the parameter property assignment; therefore `new BaseModal(this.page)` receives the initialized `page` object and the alleged runtime error cannot occur.

• **Libraries consulted**: TypeScript useDefineForClassFields field initialization order, Typescript</comment>

<file context>
@@ -1,6 +1,9 @@
+import { BaseModal } from &#39;./components/BaseModal&#39;;
+
 export abstract class BasePage {
+	protected readonly baseModal = new BaseModal(this.page);
 	constructor(protected readonly page: Page) {}
 
</file context>

[internal] Confidence score: 9/10

[internal] Posted by: Functional Bugs Agent

Fix with Cubic

Copy link
Author

@cubic-dev-local cubic-dev-local bot Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding baseModal to BasePage creates redundant BaseModal instances and duplicates the existing global n8n.modal access pattern, causing architectural inconsistency.

    DEV MODE: This violation would have been filtered out by GPT-5.

Reasoning:
GPT-5: BaseModal is a stateless helper that merely wraps Playwright selectors. Instantiating it inside BasePage gives derived pages convenient modal helpers without conflicting with or duplicating n8n.modal, so there’s no impactful issue to report.

Prompt for AI agents
Address the following comment on packages/testing/playwright/pages/BasePage.ts at line 6:

<comment>Adding `baseModal` to BasePage creates redundant BaseModal instances and duplicates the existing global `n8n.modal` access pattern, causing architectural inconsistency.

        DEV MODE: This violation would have been filtered out by GPT-5.
Reasoning:
• **GPT-5**: `BaseModal` is a stateless helper that merely wraps Playwright selectors. Instantiating it inside `BasePage` gives derived pages convenient modal helpers without conflicting with or duplicating `n8n.modal`, so there’s no impactful issue to report.</comment>

<file context>
@@ -1,6 +1,9 @@
+import { BaseModal } from &#39;./components/BaseModal&#39;;
+
 export abstract class BasePage {
+	protected readonly baseModal = new BaseModal(this.page);
 	constructor(protected readonly page: Page) {}
 
</file context>

[internal] Confidence score: 8/10

[internal] Posted by: System Design Agent

Fix with Cubic

Copy link

@cubic-dev-ai cubic-dev-ai bot Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class field initializer constructs BaseModal with undefined this.page under TS 5.x define semantics, causing runtime errors when baseModal is used.

Prompt for AI agents
Address the following comment on packages/testing/playwright/pages/BasePage.ts at line 6:

<comment>Class field initializer constructs BaseModal with undefined this.page under TS 5.x define semantics, causing runtime errors when baseModal is used.</comment>

<file context>
@@ -1,6 +1,9 @@
+import { BaseModal } from &#39;./components/BaseModal&#39;;
+
 export abstract class BasePage {
+	protected readonly baseModal = new BaseModal(this.page);
 	constructor(protected readonly page: Page) {}
 
</file context>

[internal] Confidence score: 9/10

[internal] Posted by: Functional Bugs Agent

Fix with Cubic

Copy link
Author

@cubic-dev-local cubic-dev-local bot Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class field initializer uses this.page before it’s assigned, constructing BaseModal with undefined and causing runtime errors when baseModal is used.

Prompt for AI agents
Address the following comment on packages/testing/playwright/pages/BasePage.ts at line 6:

<comment>Class field initializer uses this.page before it’s assigned, constructing BaseModal with undefined and causing runtime errors when baseModal is used.</comment>

<file context>
@@ -1,6 +1,9 @@
+import { BaseModal } from &#39;./components/BaseModal&#39;;
+
 export abstract class BasePage {
+	protected readonly baseModal = new BaseModal(this.page);
 	constructor(protected readonly page: Page) {}
 
</file context>

[internal] Confidence score: 8/10

[internal] Posted by: Functional Bugs Agent

Fix with Cubic

constructor(protected readonly page: Page) {}

protected async clickByTestId(testId: string) {
Expand Down
40 changes: 39 additions & 1 deletion packages/testing/playwright/pages/WorkflowsPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class WorkflowsPage extends BasePage {
await this.page.getByRole('button', { name: 'delete' }).click();
}

async searchWorkflows(searchTerm: string) {
async search(searchTerm: string) {
await this.clickByTestId('resources-list-search');
await this.fillByTestId('resources-list-search', searchTerm);
}
Expand Down Expand Up @@ -154,4 +154,42 @@ export class WorkflowsPage extends BasePage {
async filterByTag(tag: string) {
await this.filterByTags([tag]);
}

getFolderBreadcrumbsActions() {
return this.page.getByTestId('folder-breadcrumbs-actions');
}

getFolderBreadcrumbsActionToggle() {
return this.page.getByTestId('action-toggle-dropdown');
}

getFolderBreadcrumbsAction(actionName: string) {
return this.getFolderBreadcrumbsActionToggle().getByTestId(`action-${actionName}`);
Copy link
Author

@cubic-dev-local cubic-dev-local bot Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getFolderBreadcrumbsAction scopes the action lookup to the toggle element, but the dropdown menu renders outside that subtree, so the locator never resolves and clicking the action will fail. Please query the action from page (as done in ResourceCards.getCardAction).

    DEV MODE: This violation would have been filtered out by GPT-5.

Reasoning:
GPT-5: getFolderBreadcrumbsActionToggle() already locates the teleported dropdown overlay (data-test-id="action-toggle-dropdown"), so chaining .getByTestId(�}action-${actionName}�{)` reaches the menu items correctly despite the overlay being outside the toggle element hierarchy. The reported failure does not occur.

Libraries consulted: Element Plus ElDropdown popper append to body, Element-plus

Prompt for AI agents
Address the following comment on packages/testing/playwright/pages/WorkflowsPage.ts at line 167:

<comment>`getFolderBreadcrumbsAction` scopes the action lookup to the toggle element, but the dropdown menu renders outside that subtree, so the locator never resolves and clicking the action will fail. Please query the action from `page` (as done in `ResourceCards.getCardAction`).

        DEV MODE: This violation would have been filtered out by GPT-5.
Reasoning:
• **GPT-5**: `getFolderBreadcrumbsActionToggle()` already locates the teleported dropdown overlay (`data-test-id=&quot;action-toggle-dropdown&quot;`), so chaining `.getByTestId(�}action-${actionName}`�{)` reaches the menu items correctly despite the overlay being outside the toggle element hierarchy. The reported failure does not occur.

• **Libraries consulted**: Element Plus ElDropdown popper append to body, Element-plus</comment>

<file context>
@@ -154,4 +154,42 @@ export class WorkflowsPage extends BasePage {
+	}
+
+	getFolderBreadcrumbsAction(actionName: string) {
+		return this.getFolderBreadcrumbsActionToggle().getByTestId(`action-${actionName}`);
+	}
+
</file context>

[internal] Confidence score: 8/10

[internal] Posted by: General AI Review Agent

Suggested change
return this.getFolderBreadcrumbsActionToggle().getByTestId(`action-${actionName}`);
return this.page.getByTestId(`action-${actionName}`);
Fix with Cubic

}

addFolderButton() {
return this.page.getByTestId('add-folder-button');
}

// Add region for actions

/**
* Add a folder from the add resource dropdown
* @returns The name of the folder
*/
async addFolder() {
const folderName = 'My Test Folder';
await this.addResource.folder();
await this.fillFolderModal(folderName);
return folderName;
}

/**
* Fill the folder modal
* @param folderName - The name of the folder
*/
async fillFolderModal(folderName: string) {
await this.baseModal.fillInput(folderName);
await this.baseModal.clickButton('Create');
}
}
4 changes: 2 additions & 2 deletions packages/testing/playwright/services/api-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ const DB_TAGS = {
export class ApiHelpers {
request: APIRequestContext;
workflowApi: WorkflowApiHelper;
projectApi: ProjectApiHelper;
projects: ProjectApiHelper;
Copy link
Author

@cubic-dev-local cubic-dev-local bot Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming inconsistency: projects was renamed while other API helper properties keep the ...Api suffix, producing a mixed naming convention.

DEV MODE: This violation would have been filtered out by screening filters. Failing filters: typescriptUnderstanding, functionalImpact.

    DEV MODE: This violation would have been filtered out by GPT-5.

Reasoning:
GPT-5: Renaming the property from projectApi to projects only affects naming style. The helper remains correctly typed and instantiated, keeping behavior unchanged. This stylistic mismatch doesn’t meet the bar for an actionable violation.

Prompt for AI agents
Address the following comment on packages/testing/playwright/services/api-helper.ts at line 39:

<comment>Naming inconsistency: `projects` was renamed while other API helper properties keep the `...Api` suffix, producing a mixed naming convention.

*DEV MODE: This violation would have been filtered out by screening filters. Failing filters: typescriptUnderstanding, functionalImpact.*

        DEV MODE: This violation would have been filtered out by GPT-5.
Reasoning:
• **GPT-5**: Renaming the property from `projectApi` to `projects` only affects naming style. The helper remains correctly typed and instantiated, keeping behavior unchanged. This stylistic mismatch doesn’t meet the bar for an actionable violation.</comment>

<file context>
@@ -36,14 +36,14 @@ const DB_TAGS = {
 	request: APIRequestContext;
 	workflowApi: WorkflowApiHelper;
-	projectApi: ProjectApiHelper;
+	projects: ProjectApiHelper;
 	credentialApi: CredentialApiHelper;
 	variablesApi: VariablesApiHelper;
</file context>

[internal] Confidence score: 9/10

[internal] Posted by: System Design Agent

Fix with Cubic

credentialApi: CredentialApiHelper;
variablesApi: VariablesApiHelper;

constructor(requestContext: APIRequestContext) {
this.request = requestContext;
this.workflowApi = new WorkflowApiHelper(this);
this.projectApi = new ProjectApiHelper(this);
this.projects = new ProjectApiHelper(this);
this.credentialApi = new CredentialApiHelper(this);
this.variablesApi = new VariablesApiHelper(this);
}
Expand Down
139 changes: 139 additions & 0 deletions packages/testing/playwright/services/project-api-helper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { nanoid } from 'nanoid';
import type { Folder } from '@n8n/db';

import type { ApiHelpers } from './api-helper';
import { TestError } from '../Types';
Expand Down Expand Up @@ -42,4 +43,142 @@ export class ProjectApiHelper {

return true;
}

/**
* Create a new folder in a project
* @param projectId The ID of the project to create the folder in
* @param folderName The name of the folder to create
* @param parentFolderId Optional parent folder ID for nested folders
* @returns The created folder data
*/
async createFolder(
projectId: string,
folderName?: string,
parentFolderId?: string,
): Promise<Folder> {
const uniqueName = folderName ? `${folderName} (${nanoid(8)})` : `Test Folder ${nanoid(8)}`;

const response = await this.api.request.post(`/rest/projects/${projectId}/folders`, {
data: {
name: uniqueName,
...(parentFolderId && { parentFolderId }),
},
});

if (!response.ok()) {
throw new TestError(`Failed to create folder: ${await response.text()}`);
}

const result = await response.json();
return result.data ?? result;
}

/**
* Update a folder
* @param projectId The ID of the project containing the folder
* @param folderId The ID of the folder to update
* @param updates Object containing folder updates (name, parentFolderId, tagIds)
* @returns True if update was successful
*/
async updateFolder(
projectId: string,
folderId: string,
updates: { name?: string; parentFolderId?: string; tagIds?: string[] },
): Promise<boolean> {
const response = await this.api.request.patch(
`/rest/projects/${projectId}/folders/${folderId}`,
{
data: updates,
},
);

if (!response.ok()) {
throw new TestError(`Failed to update folder: ${await response.text()}`);
}

return true;
}

/**
* Delete a folder
* @param projectId The ID of the project containing the folder
* @param folderId The ID of the folder to delete
* @param deleteWorkflows Whether to delete workflows in the folder (default: false)
* @returns True if deletion was successful
*/
async deleteFolder(
projectId: string,
folderId: string,
deleteWorkflows: boolean = false,
): Promise<boolean> {
const response = await this.api.request.delete(
`/rest/projects/${projectId}/folders/${folderId}?deleteWorkflows=${deleteWorkflows}`,
);

if (!response.ok()) {
throw new TestError(`Failed to delete folder: ${await response.text()}`);
}

return true;
}

/**
* Get folder tree for a specific folder
* @param projectId The ID of the project
* @param folderId The ID of the folder to get the tree for
* @returns The folder tree data
*/
async getFolderTree(projectId: string, folderId: string) {
Copy link
Author

@cubic-dev-local cubic-dev-local bot Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Folder tree retrieval logic duplicates packages/frontend/editor-ui/src/api/workflows.ts:getFolderPath() function. The semantic purpose and API endpoint used are identical. Reusing the existing function would centralize API logic.

    DEV MODE: This violation would have been filtered out by GPT-5.

Reasoning:
GPT-5: The Playwright helper depends on the testing runtime’s ApiHelpers.request abstraction, while the frontend function relies on IRestApiContext and makeRestApiRequest. Because these live in different environments with incompatible infrastructure, consolidating them isn’t realistic, so the duplication isn’t actionable.

Prompt for AI agents
Address the following comment on packages/testing/playwright/services/project-api-helper.ts at line 131:

<comment>Folder tree retrieval logic duplicates `packages/frontend/editor-ui/src/api/workflows.ts:getFolderPath()` function. The semantic purpose and API endpoint used are identical. Reusing the existing function would centralize API logic.

        DEV MODE: This violation would have been filtered out by GPT-5.
Reasoning:
• **GPT-5**: The Playwright helper depends on the testing runtime’s ApiHelpers.request abstraction, while the frontend function relies on IRestApiContext and makeRestApiRequest. Because these live in different environments with incompatible infrastructure, consolidating them isn’t realistic, so the duplication isn’t actionable.</comment>

<file context>
@@ -42,4 +43,142 @@ export class ProjectApiHelper {
+	 * @param folderId The ID of the folder to get the tree for
+	 * @returns The folder tree data
+	 */
+	async getFolderTree(projectId: string, folderId: string) {
+		const response = await this.api.request.get(
+			`/rest/projects/${projectId}/folders/${folderId}/tree`,
</file context>

[internal] Confidence score: 9/10

[internal] Posted by: Duplicate Detection Agent

Fix with Cubic

const response = await this.api.request.get(
`/rest/projects/${projectId}/folders/${folderId}/tree`,
);

if (!response.ok()) {
throw new TestError(`Failed to get folder tree: ${await response.text()}`);
}

const result = await response.json();
return result.data ?? result;
}

/**
* List folders in a project
* @param projectId The ID of the project
* @param filters Optional filters for the folder list
* @returns The folder list data
*/
async listFolders(projectId: string, filters?: { search?: string; parentFolderId?: string }) {
const queryParams = new URLSearchParams();
if (filters?.search) queryParams.append('search', filters.search);
if (filters?.parentFolderId) queryParams.append('parentFolderId', filters.parentFolderId);

const url = `/rest/projects/${projectId}/folders${queryParams.toString() ? `?${queryParams}` : ''}`;
const response = await this.api.request.get(url);

if (!response.ok()) {
throw new TestError(`Failed to list folders: ${await response.text()}`);
}

const result = await response.json();
return result.data ?? result;
}

/**
* Get folder content (counts of sub-folders and workflows)
* @param projectId The ID of the project
* @param folderId The ID of the folder
* @returns Folder content counts
*/
async getFolderContent(projectId: string, folderId: string) {
Copy link
Author

@cubic-dev-local cubic-dev-local bot Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Folder content retrieval logic duplicates packages/frontend/editor-ui/src/api/workflows.ts:getFolderContent() function. The semantic purpose and API endpoint used are identical. Reusing the existing function would centralize API logic.

DEV MODE: This violation would have been filtered out by screening filters. Failing filters: typescriptUnderstanding.

    DEV MODE: This violation would have been filtered out by GPT-5.

Reasoning:
GPT-5: This Playwright helper also uses ApiHelpers.request in the test runtime, whereas the frontend counterpart uses IRestApiContext/getFullApiResponse. The divergent runtimes make code sharing impractical, rendering the duplication low-impact and not worth flagging.

Prompt for AI agents
Address the following comment on packages/testing/playwright/services/project-api-helper.ts at line 172:

<comment>Folder content retrieval logic duplicates `packages/frontend/editor-ui/src/api/workflows.ts:getFolderContent()` function. The semantic purpose and API endpoint used are identical. Reusing the existing function would centralize API logic.

*DEV MODE: This violation would have been filtered out by screening filters. Failing filters: typescriptUnderstanding.*

        DEV MODE: This violation would have been filtered out by GPT-5.
Reasoning:
• **GPT-5**: This Playwright helper also uses ApiHelpers.request in the test runtime, whereas the frontend counterpart uses IRestApiContext/getFullApiResponse. The divergent runtimes make code sharing impractical, rendering the duplication low-impact and not worth flagging.</comment>

<file context>
@@ -42,4 +43,142 @@ export class ProjectApiHelper {
+	 * @param folderId The ID of the folder
+	 * @returns Folder content counts
+	 */
+	async getFolderContent(projectId: string, folderId: string) {
+		const response = await this.api.request.get(
+			`/rest/projects/${projectId}/folders/${folderId}/content`,
</file context>

[internal] Confidence score: 9/10

[internal] Posted by: Duplicate Detection Agent

Fix with Cubic

const response = await this.api.request.get(
`/rest/projects/${projectId}/folders/${folderId}/content`,
);

if (!response.ok()) {
throw new TestError(`Failed to get folder content: ${await response.text()}`);
}

const result = await response.json();
return result.data ?? result;
}
}
8 changes: 4 additions & 4 deletions packages/testing/playwright/tests/ui/1-workflows.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,17 @@ test.describe('Workflows', () => {
await n8n.goHome();

// Search for specific workflow
await n8n.workflows.searchWorkflows(specificName);
await n8n.workflows.search(specificName);
await expect(n8n.workflows.cards.getWorkflow(specificName)).toBeVisible();

// Search with partial term
await n8n.workflows.clearSearch();
await n8n.workflows.searchWorkflows(uniqueId);
await n8n.workflows.search(uniqueId);
await expect(n8n.workflows.cards.getWorkflows()).toHaveCount(2);

// Search for non-existent
await n8n.workflows.clearSearch();
await n8n.workflows.searchWorkflows('NonExistentWorkflow123');
await n8n.workflows.search('NonExistentWorkflow123');
await expect(n8n.workflows.cards.getWorkflows()).toHaveCount(0);
await expect(n8n.page.getByText('No workflows found')).toBeVisible();
});
Expand Down Expand Up @@ -135,7 +135,7 @@ test.describe('Workflows', () => {
const tags = await n8n.canvas.addTags(2);

await n8n.goHome();
await n8n.workflows.searchWorkflows('Tagged');
await n8n.workflows.search('Tagged');
await n8n.workflows.filterByTag(tags[0]);

await expect(n8n.page).toHaveURL(/search=Tagged/);
Expand Down
Loading
Loading