Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ const ManageWorkspace = () => {
{ id: 'remove', label: 'Remove', onClick: () => handleCloseClick(workspace) }
]}
>
<button className="more-actions-btn">
<button data-testid="more-actions-btn" className="more-actions-btn">
<IconDots size={14} strokeWidth={1.5} />
</button>
</MenuDropdown>
Expand Down
67 changes: 67 additions & 0 deletions tests/utils/page/workspace/manage-workspace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Page, test } from '../../../../playwright';

import { buildTitleBarLocators } from '../title-bar';

export const buildManageWorkspaceLocators = (page: Page) => ({
myWorkspaceDropdownItem: () => page.locator('.dropdown-item'),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

locaters structure and name can be improved.

export const buildManageWorkspaceLocators = (page: Page) => ({
  // The Manage Workspace panel heading.
  manageWorkspaceTitle: () => page.getByText('Manage Workspace', { exact: true }),
  // All workspace rows, or a single row matched by name.
  workspaceItem: (name?: string) => {
    const rows = page.locator('.workspace-item');
    return name ? rows.filter({ hasText: name }) : rows;
  },
  // The "..." actions button, scoped to a given workspace row.
  moreActionsBtn: (row: Locator) => row.getByTestId('more-actions-btn'),
  // Rename modal's name field.
  workspaceNameInput: () => page.locator('#workspace-name'),
  // The active workspace name shown in the title bar (updates after a rename).
  activeWorkspaceName: () => buildTitleBarLocators(page).activeWorkspaceName(),
  // First terminal session entry opened via "Open in Terminal".
  terminalSession: () => page.getByTestId('session-list-0')
})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Further minor enhancement

  1. use plural => workspaceItems: (name?: string) => {
  2. enhance => terminalSession: (index: number = 0) => page.getByTestId(`session-list-${index}`)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  1. myWorkspaceDropdownItem: () => page.locator('.dropdown-item'), should be part of tests/utils/page/title-bar.ts
  2. rename myWorkspaceDropdownItem => importWorkspaceOption refer: importWorkspaceOption
  3. We can also add a dynamic option selector workspaceMenuOption: (name: string) => page.getByTestId('workspace-menu').locator('.dropdown-item');

manageWorkspaceTitle: () => page.getByText('Manage Workspace', { exact: true }),
defaultWorkspaceItem: () => page.locator('.workspace-item'),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is not reliable. page.locator('.workspace-item') is not always default item

moreActionsBtn: () => page.locator('.more-actions-btn'),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can we use datatest-id

workspaceListItem: () => page.locator('.dropdown-item'),
terminalSession: () => page.getByTestId('session-list-0'),
workspaceItem: () => page.locator('.workspace-item'),
dropdownItem: () => page.locator('.dropdown-item'),
modalCard: () => page.locator('.bruno-modal-card'),
workspaceFileNameInput: () => page.locator('#workspace-name'),
submitRenameBtn: () => page.getByTestId('modal-submit-btn'),
renamedWorkspaceItem: () => page.getByTestId('workspace-name'),
submitRemoveBtn: () => page.getByTestId('modal-submit-btn')
Comment on lines +16 to +18

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

submitRenameBtn, submitRemoveBtn are both relying on the default testid from the modal component.
I suggest adding proper testid for the modals and use them.

// rename modal
<Modal 
    data-testid="rename-workspace-modal"
    ...
// usage becomes
submitRemoveBtn: () => page.getByTestId('rename-workspace-modal-submit-btn')

});

export const manageWorkspace = async (page: Page) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this function name can be more specific like openManageWorkspace or something else.

const locators = buildManageWorkspaceLocators(page);
await test.step('Open manage workspaces', async () => {
await buildTitleBarLocators(page).workspaceMenuTrigger().click();
await locators.myWorkspaceDropdownItem().filter({ hasText: 'Manage workspaces' }).click();
});
};

export const openTerminalFromWorkspaceActions = async (page: Page, workspaceName: string) => {
const locators = buildManageWorkspaceLocators(page);
await test.step('Open terminal from workspace actions', async () => {
const workspaceItem = locators.defaultWorkspaceItem().filter({ hasText: workspaceName });
await workspaceItem.locator(locators.moreActionsBtn()).click();
await locators.workspaceListItem().filter({ hasText: 'Open in Terminal' }).click();
});
};

export const clickOnMoreActionsButton = async (page: Page, workspaceName: string) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can we rename it to openWorkspaceActionsMenu?

const locators = buildManageWorkspaceLocators(page);
await test.step('Click on the more actions button', async () => {
const workspaceItem = locators.workspaceItem().filter({ hasText: workspaceName });
await workspaceItem.locator(locators.moreActionsBtn()).click();
});
};

export const renameOrRemoveWorkspaceOnPopup = async (page: Page, optionName: string) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

change it to selectWorkspaceAction. More generic, and won't need renaming when new menu actions are added.

const locators = buildManageWorkspaceLocators(page);
await test.step('Rename or remove workspace on popup', async () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

step description is not accurate to action performed

await locators.dropdownItem().filter({ hasText: optionName }).click();
await locators.modalCard().waitFor({ state: 'visible' });
});
};

export const enterNewWorkspaceFilename = async (page: Page, workspaceName: string, btnName: string) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please change the name to enterNewWorkspaceName

const locators = buildManageWorkspaceLocators(page);
await test.step('Enter a new name for the workspace in the editing field.', async () => {
await locators.workspaceFileNameInput().fill(workspaceName);
await locators.submitRenameBtn().filter({ hasText: btnName }).click();
});
};

export const clickOnRemoveButtonOnPopup = async (page: Page) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

please rename it to confirmRemoveWorkspace.

const locators = buildManageWorkspaceLocators(page);
await test.step('Verify the workspace name is updated in the workspace list.', async () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Step description is not accurate to action performed. Actions cannot have verify steps.

await locators.submitRemoveBtn().click();
});
};
111 changes: 49 additions & 62 deletions tests/workspace/manage-workspace/manage-workspace.spec.ts

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

All tests are passing when run in parallel with multiple workers. But when run using single worker (npm run test:e2e tests/workspace/manage-workspace/manage-workspace.spec.ts -- --workers=1) it fails due to stale state from the previous test case is not cleaned up.

Original file line number Diff line number Diff line change
@@ -1,68 +1,55 @@
import path from 'path';
import fs from 'fs';
import { test, expect, closeElectronApp } from '../../../playwright';

const initUserDataPath = path.join(__dirname, '../create-workspace/init-user-data');

function findCreatedWorkspaceDirs(location: string): string[] {
return fs.readdirSync(location).filter((e) => {
const fullPath = path.join(location, e);
return (
fs.statSync(fullPath).isDirectory()
&& e !== 'default-workspace'
&& fs.existsSync(path.join(fullPath, 'workspace.yml'))
);
import { test, expect } from '../../../playwright';
import { createWorkspace } from '../../utils/page/actions';
import {
clickOnMoreActionsButton,
manageWorkspace,
buildManageWorkspaceLocators,
openTerminalFromWorkspaceActions,
renameOrRemoveWorkspaceOnPopup,
enterNewWorkspaceFilename,
clickOnRemoveButtonOnPopup
} from '../../utils/page/workspace/manage-workspace';

test.describe('Manage workspace', () => {
test.beforeEach(async ({ page }) => {
await test.step('Create a workspace', async () => {
await createWorkspace(page, 'Custom Workspace');
});

await test.step('Open manage workspaces', async () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Repeated/Duplicate step.
await manageWorkspace(page); internally have the same step.

const locators = buildManageWorkspaceLocators(page);
await manageWorkspace(page);
await expect(locators.manageWorkspaceTitle()).toBeVisible();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

beforeEach block cannot have test assertions.
use .waitFor({ state: 'visible' }) instead, if this is added as a dynamic wait logic.

});
});
}

test.describe('Manage Workspace', () => {
test('should open terminal from the workspace actions menu', async ({ launchElectronApp, createTmpDir }) => {
const wsLocation = await createTmpDir('ws-location-terminal');

const app = await launchElectronApp({ initUserDataPath, templateVars: { wsLocation } });
const page = await app.firstWindow();

try {
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });

await test.step('Create a workspace', async () => {
await page.locator('.workspace-name-container').click();
await page.locator('.dropdown-item').filter({ hasText: 'Create workspace' }).click();
const renameInput = page.locator('.workspace-name-input');
await expect(renameInput).toBeVisible({ timeout: 5000 });
await renameInput.fill('Terminal Workspace');
await renameInput.press('Enter');
await expect(page.getByText('Workspace created!')).toBeVisible({ timeout: 10000 });
});

const wsDirs = findCreatedWorkspaceDirs(wsLocation);
expect(wsDirs).toHaveLength(1);

await test.step('Open Manage Workspaces', async () => {
await page.locator('.workspace-name-container').click();
await page.locator('.dropdown-item').filter({ hasText: 'Manage workspaces' }).click();
await expect(page.getByText('Manage Workspace')).toBeVisible({ timeout: 5000 });
});

await test.step('Verify default workspace has no actions menu', async () => {
const defaultWorkspaceItem = page.locator('.workspace-item').filter({ hasText: 'My Workspace' });
await expect(defaultWorkspaceItem.locator('.more-actions-btn')).toHaveCount(0);
});
test.describe('Manage workspace actions-open in terminal', () => {
test('TC-3109: Verify that the workspace should open terminal from the workspace actions menu', { tag: '@sanity' }, async ({ page }) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Descriptions can be improved.

await openTerminalFromWorkspaceActions(page, 'Custom Workspace');
const locators = buildManageWorkspaceLocators(page);
await expect(locators.terminalSession()).toBeVisible();
await expect(locators.terminalSession()).toContainText('Custom Workspace');
});
});

await test.step('Open terminal from workspace actions', async () => {
const workspaceItem = page.locator('.workspace-item').filter({ hasText: 'Terminal Workspace' });
await expect(workspaceItem).toBeVisible({ timeout: 5000 });
await workspaceItem.locator('.more-actions-btn').click();
await page.locator('.dropdown-item').filter({ hasText: 'Open in Terminal' }).click();
});
test.describe('Manage workspace actions-rename workspace', () => {
test('TC-2612: Verify renaming a workspace from manage workspace section.', { tag: '@sanity' }, async ({ page }) => {
const locators = buildManageWorkspaceLocators(page);
await clickOnMoreActionsButton(page, 'Custom Workspace');
await renameOrRemoveWorkspaceOnPopup(page, 'Rename');
await enterNewWorkspaceFilename(page, 'New Workspace Name', 'Rename');
await expect(locators.renamedWorkspaceItem()).toHaveText('New Workspace Name');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This renamedWorkspaceItem does not make sence we can just name it activeWorkspaveName

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We can rename non-active workspaces too. Fix accordingly.

await expect(locators.workspaceItem().filter({ hasText: 'New Workspace Name' })).toBeVisible();
});
});

await test.step('Verify terminal session opens at the workspace folder', async () => {
const terminalSession = page.getByTestId('session-list-0');
await expect(terminalSession).toBeVisible({ timeout: 5000 });
await expect(terminalSession).toContainText(wsDirs[0]);
});
} finally {
await closeElectronApp(app);
}
test.describe('Manage Workspace Actions - Remove Workspace', () => {
test('TC-2611: Verify removing a Workspace from manage workspace section', { tag: '@sanity' }, async ({ page }) => {
const locators = buildManageWorkspaceLocators(page);
await clickOnMoreActionsButton(page, 'Custom Workspace');
await renameOrRemoveWorkspaceOnPopup(page, 'Remove');
await clickOnRemoveButtonOnPopup(page);
await expect(locators.workspaceItem().filter({ hasText: 'Custom Workspace' })).not.toBeVisible({ timeout: 10000 });
});
});
});
Loading