Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 4 additions & 4 deletions src/containers/Operations/Operations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@ export function Operations({database, scrollContainerRef}: OperationsProps) {
scrollContainerRef,
});

if (isAccessError(error)) {
return <AccessDenied position="left" />;
}

const settings = React.useMemo(() => {
return {
...DEFAULT_TABLE_SETTINGS,
sortable: false,
};
}, []);

if (isAccessError(error)) {
return <AccessDenied position="left" />;
}

return (
<TableWithControlsLayout>
<TableWithControlsLayout.Controls>
Expand Down
18 changes: 18 additions & 0 deletions tests/suites/tenant/diagnostics/tabs/OperationsModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export class OperationsTable extends BaseModel {
private emptyState: Locator;
private loadingMore: Locator;
private scrollContainer: Locator;
private accessDeniedState: Locator;
private accessDeniedTitle: Locator;

constructor(page: Page) {
super(page, page.locator('.kv-tenant-diagnostics'));
Expand All @@ -22,6 +24,9 @@ export class OperationsTable extends BaseModel {
this.emptyState = page.locator('.operations__table:has-text("No operations data")');
this.loadingMore = page.locator('.operations__loading-more');
this.scrollContainer = page.locator('.kv-tenant-diagnostics__page-wrapper');
// AccessDenied component is rendered at the root level of Operations component
this.accessDeniedState = page.locator('.kv-tenant-diagnostics .empty-state');
this.accessDeniedTitle = this.accessDeniedState.locator('.empty-state__title');
}

async waitForTableVisible() {
Expand Down Expand Up @@ -124,4 +129,17 @@ export class OperationsTable extends BaseModel {

return false;
}

async isAccessDeniedVisible(): Promise<boolean> {
try {
await this.accessDeniedState.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
return true;
} catch {
return false;
}
}

async getAccessDeniedTitle(): Promise<string> {
return await this.accessDeniedTitle.innerText();
}
}
33 changes: 32 additions & 1 deletion tests/suites/tenant/diagnostics/tabs/operations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import {tenantName} from '../../../../utils/constants';
import {TenantPage} from '../../TenantPage';
import {Diagnostics, DiagnosticsTab} from '../Diagnostics';

import {setupEmptyOperationsMock, setupOperationsMock} from './operationsMocks';
import {
setupEmptyOperationsMock,
setupOperation403Mock,
setupOperationsMock,
} from './operationsMocks';

test.describe('Operations Tab - Infinite Query', () => {
test('loads initial page of operations on tab click', async ({page}) => {
Expand Down Expand Up @@ -119,4 +123,31 @@ test.describe('Operations Tab - Infinite Query', () => {
const rowCount = await diagnostics.operations.getRowCount();
expect(rowCount).toBeLessThanOrEqual(1);
});

test('shows access denied when operations request returns 403', async ({page}) => {
// Setup 403 error mock
await setupOperation403Mock(page);

const pageQueryParams = {
schema: tenantName,
database: tenantName,
tenantPage: 'diagnostics',
};

const tenantPageInstance = new TenantPage(page);
await tenantPageInstance.goto(pageQueryParams);

const diagnostics = new Diagnostics(page);
await diagnostics.clickTab(DiagnosticsTab.Operations);
// Wait a bit for potential loading
await page.waitForTimeout(2000);

// Wait for access denied state to be visible
const isAccessDeniedVisible = await diagnostics.operations.isAccessDeniedVisible();
expect(isAccessDeniedVisible).toBe(true);

// Verify the access denied message
const accessDeniedTitle = await diagnostics.operations.getAccessDeniedTitle();
expect(accessDeniedTitle).toBe('Access denied');
});
});
14 changes: 14 additions & 0 deletions tests/suites/tenant/diagnostics/tabs/operationsMocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,20 @@ export const setupOperationErrorMock = async (page: Page) => {
});
};

export const setupOperation403Mock = async (page: Page) => {
await page.route(`${backend}/operation/list*`, async (route) => {
await new Promise((resolve) => setTimeout(resolve, MOCK_DELAY));

await route.fulfill({
status: 403,
contentType: 'application/json',
body: JSON.stringify({
error: 'Forbidden',
}),
});
});
};

// Helper to setup all required mocks for operations
export const setupAllOperationMocks = async (page: Page, options?: {totalOperations?: number}) => {
await setupOperationsMock(page, options);
Expand Down
Loading