mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-08 14:35:03 +00:00
fix(openapi):validate API spec imports (#8322)
* fix(openapi):validate API spec imports and resolve preview to error state instead of hanging * fix(openapi):allows opening of malformed files with correct extension * fix(openapi):reject non-OpenAPI content in spec preview before timeout * test(openapi): add unit + e2e coverage for spec import validation * refactor(openapi):centralise spec error messages and fix dialog restore * fix:removed nested try catch * test:added common locators * refactor:removed repeated lines * refactor:removed redundant file reads --------- Co-authored-by: Adwait Aayush <adwaitaayush@Adwaits-MacBook-Air.local>
This commit is contained in:
74
tests/import/openapi/api-spec-panel-validation.spec.ts
Normal file
74
tests/import/openapi/api-spec-panel-validation.spec.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import * as path from 'path';
|
||||
import { openApiSpecFromDialog, openApiSpecSidebarItem } from '../../utils/page/openapi/render-spec';
|
||||
import { SPEC_PREVIEW_ERRORS } from '../../../packages/bruno-app/src/components/ApiSpecPanel/constants';
|
||||
import { INVALID_EXTENSION_MESSAGE } from '../../../packages/bruno-electron/src/app/apiSpecs';
|
||||
|
||||
test.describe('API Spec Panel - open & preview validation', () => {
|
||||
test.beforeAll(async ({ electronApp }) => {
|
||||
await electronApp.evaluate(({ dialog }) => {
|
||||
(dialog as any).__savedShowOpenDialog = dialog.showOpenDialog;
|
||||
});
|
||||
});
|
||||
|
||||
test.afterAll(async ({ electronApp }) => {
|
||||
await electronApp.evaluate(({ dialog }) => {
|
||||
dialog.showOpenDialog = (dialog as any).__savedShowOpenDialog;
|
||||
delete (dialog as any).__savedShowOpenDialog;
|
||||
});
|
||||
});
|
||||
|
||||
test('Reject a file with an unsupported extension', async ({ page, electronApp }) => {
|
||||
const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-invalid-extension.txt');
|
||||
await openApiSpecFromDialog(page, electronApp, openApiFile);
|
||||
await expect(
|
||||
page.getByText(INVALID_EXTENSION_MESSAGE)
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Show the empty-spec message when opening an empty file', async ({ page, electronApp }) => {
|
||||
const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-empty.yaml');
|
||||
await openApiSpecFromDialog(page, electronApp, openApiFile);
|
||||
await openApiSpecSidebarItem(page, 'openapi-empty');
|
||||
await expect(page.getByText(SPEC_PREVIEW_ERRORS.EMPTY)).toBeVisible();
|
||||
});
|
||||
|
||||
test('Show a parse error when opening malformed YAML', async ({ page, electronApp }) => {
|
||||
const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-malformed.yaml');
|
||||
await openApiSpecFromDialog(page, electronApp, openApiFile);
|
||||
await openApiSpecSidebarItem(page, 'openapi-malformed');
|
||||
await expect(
|
||||
page.getByText(SPEC_PREVIEW_ERRORS.INVALID_YAML_JSON)
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Show a parse error when opening malformed JSON', async ({ page, electronApp }) => {
|
||||
const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-broken.json');
|
||||
await openApiSpecFromDialog(page, electronApp, openApiFile);
|
||||
await openApiSpecSidebarItem(page, 'openapi-broken');
|
||||
await expect(
|
||||
page.getByText(SPEC_PREVIEW_ERRORS.INVALID_YAML_JSON)
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Show a spec error when the file parses but is not a valid OpenAPI document', async ({ page, electronApp }) => {
|
||||
const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-missing-info.yaml');
|
||||
|
||||
await openApiSpecFromDialog(page, electronApp, openApiFile);
|
||||
await openApiSpecSidebarItem(page, 'openapi-missing-info');
|
||||
// Parses as an object but lacks the openapi/swagger + info contract, so it must fail
|
||||
// fast with a precise message rather than falling through to the preview timeout.
|
||||
await expect(
|
||||
page.getByText(SPEC_PREVIEW_ERRORS.INVALID_OPENAPI)
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Render a valid spec without any preview error', async ({ page, electronApp }) => {
|
||||
const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-simple.json');
|
||||
await openApiSpecFromDialog(page, electronApp, openApiFile);
|
||||
await openApiSpecSidebarItem(page, 'Simple Test API');
|
||||
// Panel opens for the valid spec (filename shown in the header) and no preview error appears
|
||||
await expect(page.getByText('openapi-simple.json')).toBeVisible();
|
||||
await expect(page.getByText(/Unable to render preview/i)).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
19
tests/import/openapi/fixtures/openapi-broken.json
Normal file
19
tests/import/openapi/fixtures/openapi-broken.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"title": "Malformed OpenAPI"
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"paths": {
|
||||
"/test": {
|
||||
"get": {
|
||||
"summary": "Test endpoint",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
tests/import/openapi/fixtures/openapi-empty.yaml
Normal file
0
tests/import/openapi/fixtures/openapi-empty.yaml
Normal file
@@ -0,0 +1 @@
|
||||
This is a plain text file, not an OpenAPI spec.
|
||||
@@ -1,7 +1,11 @@
|
||||
import { Page, Locator } from '../../../playwright';
|
||||
import { buildApiSpecPanelLocators } from './openapi/render-spec';
|
||||
|
||||
export const buildCommonLocators = (page: Page) => ({
|
||||
runner: () => page.getByTestId('run-button'),
|
||||
openApi: {
|
||||
render: buildApiSpecPanelLocators(page)
|
||||
},
|
||||
saveButton: () => page
|
||||
.locator('.infotip')
|
||||
.filter({ hasText: /^Save/ }),
|
||||
|
||||
30
tests/utils/page/openapi/render-spec.ts
Normal file
30
tests/utils/page/openapi/render-spec.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { test, Page, ElectronApplication } from '../../../../playwright';
|
||||
|
||||
export const buildApiSpecPanelLocators = (page: Page) => ({
|
||||
addMenuButton: () => page.getByTestId('api-specs-header-add-menu'),
|
||||
openApiSpecMenuItem: () => page.getByTestId('api-specs-header-add-menu-open-api-spec'),
|
||||
sidebarItem: (name: string) => page.locator('.api-spec-item').filter({ hasText: name })
|
||||
});
|
||||
|
||||
export const openApiSpecFromDialog = async (
|
||||
page: Page,
|
||||
electronApp: ElectronApplication,
|
||||
filePath: string
|
||||
): Promise<void> => {
|
||||
await test.step(`Open API spec from path: ${filePath}`, async () => {
|
||||
await electronApp.evaluate(({ dialog }, filePath) => {
|
||||
dialog.showOpenDialog = async () => ({ canceled: false, filePaths: [filePath] });
|
||||
}, filePath);
|
||||
|
||||
const { addMenuButton, openApiSpecMenuItem } = buildApiSpecPanelLocators(page);
|
||||
await addMenuButton().click();
|
||||
await openApiSpecMenuItem().click();
|
||||
});
|
||||
};
|
||||
|
||||
export const openApiSpecSidebarItem = async (page: Page, name: string): Promise<void> => {
|
||||
await test.step(`Open API spec sidebar item "${name}"`, async () => {
|
||||
const { sidebarItem } = buildApiSpecPanelLocators(page);
|
||||
await sidebarItem(name).click();
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user