Skip to content

Commit 879722a

Browse files
feat(release-browser): add compare mode to diff two releases
Adds a View/Compare toggle to the right pane. In Compare mode the user picks a reference release from a grouped Autocomplete (current-in-env bindings first, then any other release) and the right pane renders a side-by-side YamlDiffViewer of that reference vs the highlighted release. When opened with an environmentName, pre-fills the comparison target with that env's current release so the common "is this release different from what's deployed?" question resolves with zero clicks. Manifests are fetched on demand and cached, so toggling and switching targets are free. Existing view-mode behavior, search, and selection flow are unchanged. Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
1 parent 52b2379 commit 879722a

2 files changed

Lines changed: 403 additions & 40 deletions

File tree

plugins/openchoreo/src/components/Environments/components/ReleaseBrowserDialog.test.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,30 @@ jest.mock('@openchoreo/backstage-design-system', () => ({
2222
),
2323
}));
2424

25+
// The plugin-react entry pulls Backstage's TabbedLayout transitively which
26+
// blows up under jest's isolated module env. Only YamlDiffViewer is used
27+
// here; mock the package surface to that one component.
28+
jest.mock('@openchoreo/backstage-plugin-react', () => ({
29+
YamlDiffViewer: ({
30+
original,
31+
modified,
32+
originalLabel,
33+
modifiedLabel,
34+
}: {
35+
original: string;
36+
modified: string;
37+
originalLabel?: string;
38+
modifiedLabel?: string;
39+
}) => (
40+
<div data-testid="yaml-diff-viewer">
41+
<div data-testid="diff-original-label">{originalLabel}</div>
42+
<div data-testid="diff-modified-label">{modifiedLabel}</div>
43+
<pre data-testid="diff-original">{original}</pre>
44+
<pre data-testid="diff-modified">{modified}</pre>
45+
</div>
46+
),
47+
}));
48+
2549
jest.mock('yaml', () => ({
2650
stringify: (obj: unknown) =>
2751
`name: ${
@@ -138,4 +162,63 @@ describe('ReleaseBrowserDialog', () => {
138162
expect(within(error).getByText(/boom/i)).toBeInTheDocument();
139163
expect(screen.queryByTestId('yaml-viewer')).toBeNull();
140164
});
165+
166+
it('renders the mode toggle, defaulting to View', async () => {
167+
renderDialog();
168+
const viewBtn = await screen.findByRole('button', {
169+
name: /view manifest/i,
170+
});
171+
const compareBtn = screen.getByRole('button', {
172+
name: /compare with another release/i,
173+
});
174+
expect(viewBtn).toHaveAttribute('aria-pressed', 'true');
175+
expect(compareBtn).toHaveAttribute('aria-pressed', 'false');
176+
// YamlViewer renders in view mode; YamlDiffViewer does not.
177+
expect(await screen.findByTestId('yaml-viewer')).toBeInTheDocument();
178+
expect(screen.queryByTestId('yaml-diff-viewer')).toBeNull();
179+
});
180+
181+
it('switching to Compare pre-fills the env-current release as compare target', async () => {
182+
// `rel-middle` is highlighted (selectedReleaseName) and current in
183+
// development. To make pre-select meaningful, highlight a *different*
184+
// release so the env-current target isn't a self-diff.
185+
const user = userEvent.setup();
186+
renderDialog({ selectedReleaseName: 'rel-newest' });
187+
188+
await user.click(
189+
screen.getByRole('button', { name: /compare with another release/i }),
190+
);
191+
192+
// The compare-with selector should now show the env-current release.
193+
const selector = await screen.findByPlaceholderText(
194+
/pick a release or environment/i,
195+
);
196+
expect(selector).toHaveValue('Current in development');
197+
198+
// Both manifests resolve → diff viewer renders.
199+
expect(await screen.findByTestId('yaml-diff-viewer')).toBeInTheDocument();
200+
expect(screen.getByTestId('diff-original-label')).toHaveTextContent(
201+
/current in development \(rel-middle\)/i,
202+
);
203+
expect(screen.getByTestId('diff-modified-label')).toHaveTextContent(
204+
/rel-newest/,
205+
);
206+
});
207+
208+
it('Compare with no pre-selectable target shows the empty hint', async () => {
209+
const user = userEvent.setup();
210+
// No deployments → no env-current pre-select candidate.
211+
renderDialog({ deployments: {} });
212+
213+
await user.click(
214+
screen.getByRole('button', { name: /compare with another release/i }),
215+
);
216+
217+
expect(
218+
await screen.findByText(
219+
/pick a release or environment to compare against/i,
220+
),
221+
).toBeInTheDocument();
222+
expect(screen.queryByTestId('yaml-diff-viewer')).toBeNull();
223+
});
141224
});

0 commit comments

Comments
 (0)