diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/CollectionVersionInfo/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/CollectionVersionInfo/index.js index 204d17ed85..eb0c37074b 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/CollectionVersionInfo/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/CollectionVersionInfo/index.js @@ -1,36 +1,27 @@ -import React, { memo } from 'react'; -import semver from 'semver'; +import React, { memo, Fragment } from 'react'; -const DEFAULT_VERSION = 'v1.0.0'; - -/** - * Normalise a raw collection version for display: coerce partials to a full - * major.minor.patch ("1" -> "v1.0.0", "2.1" -> "v2.1.0"), keep a single "v" - * prefix, preserve pre-releases ("1.0.0-beta" -> "v1.0.0-beta"), and fall back to - * the default when the version is unset or unparseable. - */ -const formatVersion = (version) => { - const coerced = semver.coerce(version, { includePrerelease: true }); - return coerced ? `v${coerced.version}` : DEFAULT_VERSION; -}; - -/** - * Read-only display of the collection's current version and a summary of its - * contents (folder + request counts). Presentational and prop-driven so it can be - * reused wherever the collection version needs to be shown. - */ -const CollectionVersionInfo = ({ version, folderCount = 0, requestCount = 0 }) => { +const CollectionVersionInfo = ({ name, version, folderCount = 0, requestCount = 0, environmentCount = 0 }) => { const folderLabel = folderCount === 1 ? 'Folder' : 'Folders'; const requestLabel = requestCount === 1 ? 'request' : 'requests'; return (
- Collection Version:{' '} - {formatVersion(version)} + {name} + {version ? ( + {`version : ${version}`} + ) : null}

- {`${folderCount} ${folderLabel} • ${requestCount} ${requestLabel}`} + {`${folderCount} ${folderLabel}`} +

); diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/CollectionVersionInfo/index.spec.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/CollectionVersionInfo/index.spec.js new file mode 100644 index 0000000000..5158aab6f1 --- /dev/null +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/CollectionVersionInfo/index.spec.js @@ -0,0 +1,39 @@ +import '@testing-library/jest-dom'; +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import CollectionVersionInfo from './index'; + +describe('CollectionVersionInfo', () => { + it('shows the raw collection version with a "version :" prefix and no "v"/semver formatting', () => { + render(); + expect(screen.getByTestId('version-value')).toHaveTextContent('version : 1'); + }); + + it('shows whatever version string the collection has, unchanged', () => { + render(); + expect(screen.getByTestId('version-value')).toHaveTextContent('version : 2.3-beta'); + }); + + it('omits the version when the collection has none', () => { + render(); + expect(screen.queryByTestId('version-value')).not.toBeInTheDocument(); + }); + + it('renders folder and request counts and pluralizes them', () => { + const { rerender } = render(); + expect(screen.getByTestId('version-summary')).toHaveTextContent('2 Folders'); + expect(screen.getByTestId('version-summary')).toHaveTextContent('5 requests'); + + rerender(); + expect(screen.getByTestId('version-summary')).toHaveTextContent('1 Folder'); + expect(screen.getByTestId('version-summary')).toHaveTextContent('1 request'); + }); + + it('shows "0 environments" only when there are no environments', () => { + const { rerender } = render(); + expect(screen.getByTestId('version-summary')).toHaveTextContent('0 environments'); + + rerender(); + expect(screen.getByTestId('version-summary')).not.toHaveTextContent('environments'); + }); +}); diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/EnvironmentSelectionList/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/EnvironmentSelectionList/index.js index 8cf1bbb6be..07e26ad3ed 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/EnvironmentSelectionList/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/EnvironmentSelectionList/index.js @@ -2,14 +2,14 @@ import React, { useCallback, useEffect, useMemo, useRef, memo } from 'react'; import { Virtuoso } from 'react-virtuoso'; import ColorBadge from 'components/ColorBadge'; -// Show at most 5 environments at a glance; the list virtualises and scrolls beyond +// Show at most 6 environments at a glance; the list virtualises and scrolls beyond // that, so it stays performant even for collections with hundreds of environments // (only the visible rows are ever in the DOM). -const MAX_VISIBLE_ROWS = 5; +const MAX_VISIBLE_ROWS = 6; // Fixed row height (px). MUST stay in sync with the `.env-row` height in StyledWrapper.js, // since it is passed to Virtuoso as `fixedItemHeight`. -const ENV_ROW_HEIGHT = 34; +const ENV_ROW_HEIGHT = 28; /** * A selectable, virtualised list of collection environments (checkbox + color dot + name) diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/StyledWrapper.js index dbe18a56e5..ccd67dbd00 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/StyledWrapper.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/StyledWrapper.js @@ -14,33 +14,57 @@ const StyledWrapper = styled.div` } .config-card { - border: 1px solid ${(props) => props.theme.border.border1}; + border: 1px solid ${(props) => props.theme.table.border}; border-radius: ${(props) => props.theme.border.radius.md}; overflow: hidden; + width: 100%; .version-info { padding: 0.75rem 1rem; background-color: ${(props) => props.theme.background.mantle}; .version-line { - font-size: ${(props) => props.theme.font.size.sm}; - color: ${(props) => props.theme.text}; + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; } - .version-label { + .collection-name { font-weight: 500; + font-size: ${(props) => props.theme.font.size.base}; + color: ${(props) => props.theme.text}; + min-width: 0; + } + + .version-value { + font-weight: 400; + font-size: ${(props) => props.theme.font.size.sm}; + color: ${(props) => props.theme.colors.text.subtext2}; + white-space: nowrap; } .version-summary { + display: flex; + align-items: center; + gap: 0.375rem; margin: 0.25rem 0 0; - font-size: ${(props) => props.theme.font.size.xs}; - color: ${(props) => props.theme.colors.text.muted}; + font-size: ${(props) => props.theme.font.size.sm}; + color: ${(props) => props.theme.colors.text.subtext2}; + } + + .version-dot { + width: 5px; + height: 5px; + border-radius: 50%; + background-color: ${(props) => props.theme.colors.text.subtext0}; + flex-shrink: 0; } } .card-divider { height: 1px; - background-color: ${(props) => props.theme.border.border1}; + background-color: ${(props) => props.theme.table.border}; } .env-section { @@ -59,7 +83,7 @@ const StyledWrapper = styled.div` align-items: center; justify-content: space-between; gap: 0.5rem; - margin-bottom: 0.75rem; + margin-bottom: 0.15rem; } .env-section-heading { @@ -70,18 +94,18 @@ const StyledWrapper = styled.div` } .env-section-count { - font-size: ${(props) => props.theme.font.size.xs}; - color: ${(props) => props.theme.colors.text.muted}; + font-size: ${(props) => props.theme.font.size.sm}; + color: ${(props) => props.theme.colors.text.subtext2}; white-space: nowrap; } .env-section-title { margin: 0; - font-size: ${(props) => props.theme.font.size.xs}; + font-size: ${(props) => props.theme.font.size.sm}; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; - color: ${(props) => props.theme.colors.text.muted}; + color: ${(props) => props.theme.colors.text.subtext2}; } .env-select-all { @@ -94,7 +118,7 @@ const StyledWrapper = styled.div` .env-select-all-label { font-size: ${(props) => props.theme.font.size.sm}; - color: ${(props) => props.theme.colors.text.muted}; + color: ${(props) => props.theme.colors.text.subtext2}; white-space: nowrap; } } @@ -105,12 +129,12 @@ const StyledWrapper = styled.div` gap: 0.5rem; /* Fixed row height — MUST match ENV_ROW_HEIGHT (Virtuoso fixedItemHeight) in EnvironmentSelectionList. The inter-row spacing is baked in here. */ - height: 34px; + height: 28px; cursor: pointer; margin: 0; .env-name { - font-size: ${(props) => props.theme.font.size.sm}; + font-size: ${(props) => props.theme.font.size.base}; color: ${(props) => props.theme.text}; min-width: 0; } diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/index.js index ab038a433f..bac6ff4992 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/GenerateDocumentation/index.js @@ -189,7 +189,7 @@ const GenerateDocumentation = ({ onClose, collectionUid }) => { handleCancel={onClose} confirmDisabled={isLoading} > - + {isLoading ? (
@@ -215,7 +215,7 @@ const GenerateDocumentation = ({ onClose, collectionUid }) => {
- + {environments.length > 0 && (
diff --git a/tests/collection/generate-docs/generate-docs.spec.ts b/tests/collection/generate-docs/generate-docs.spec.ts index dbc2c48200..edc8c8ece5 100644 --- a/tests/collection/generate-docs/generate-docs.spec.ts +++ b/tests/collection/generate-docs/generate-docs.spec.ts @@ -164,7 +164,7 @@ test.describe('Generate Documentation', () => { await expect(modal).toBeHidden(); }); - test('shows the current collection version formatted as a v-prefixed semver', async ({ + test('shows the collection name alongside the raw collection version', async ({ pageWithUserData: page }) => { const locators = buildCommonLocators(page); @@ -176,13 +176,18 @@ test.describe('Generate Documentation', () => { const modal = locators.generateDocs.modal(); await expect(modal).toBeVisible(); - // The fixture's bruno.json version ("1") is normalised for display to "v1.0.0". - await expect(locators.generateDocs.versionInfo()).toContainText('Collection Version:'); - await expect(locators.generateDocs.versionValue()).toHaveText('v1.0.0'); + await expect(locators.generateDocs.collectionName()).toHaveText(COLLECTION_NAME); + + // The version is shown exactly as the collection sets it. + await expect(locators.generateDocs.versionValue()).toHaveText('version : 1'); // The fixture has 2 folders (Zoo, Aviary) and 5 requests (Lion, Bear, Parrot, // ReqAlpha, ReqBeta), counted recursively across the whole tree. - await expect(locators.generateDocs.versionCounts()).toHaveText('2 Folders • 5 requests'); + await expect(locators.generateDocs.versionCounts()).toContainText('2 Folders'); + await expect(locators.generateDocs.versionCounts()).toContainText('5 requests'); + + // This collection has environments, so the "0 environments" hint is not shown. + await expect(locators.generateDocs.versionCounts()).not.toContainText('environments'); await locators.generateDocs.cancelButton().click(); await expect(modal).toBeHidden(); diff --git a/tests/utils/page/locators.ts b/tests/utils/page/locators.ts index c0b30d4d03..8ee6f3a30c 100644 --- a/tests/utils/page/locators.ts +++ b/tests/utils/page/locators.ts @@ -163,8 +163,9 @@ export const buildCommonLocators = (page: Page) => ({ heading: () => page.locator('.bruno-modal').getByText('Interactive API Documentation'), generateButton: () => page.locator('.bruno-modal').getByRole('button', { name: 'Generate', exact: true }), cancelButton: () => page.locator('.bruno-modal').getByRole('button', { name: 'Cancel', exact: true }), - // Collection version (read-only) display + // Collection name + version (read-only) display versionInfo: () => page.locator('.bruno-modal').getByTestId('version-info'), + collectionName: () => page.locator('.bruno-modal').getByTestId('collection-name'), versionValue: () => page.locator('.bruno-modal').getByTestId('version-value'), versionCounts: () => page.locator('.bruno-modal').getByTestId('version-summary'), // Environment selection list