Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions dashboard/e2e/tree-compare.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ test('loads comparison data and opens a side-by-side details drawer', async ({
);

await page.goto(
`/tree/linux/master/compare?hashA=${HASH_A}&hashB=${HASH_B}&origin=maestro`,
`/tree/linux/master/compare?range=${HASH_A}..${HASH_B}&origin=maestro`,
);

await expect(page.getByText('Tree summary')).toBeVisible();
Expand Down Expand Up @@ -304,7 +304,7 @@ test('drawer next stays on searched rows', async ({ page }) => {
);

await page.goto(
`/tree/linux/master/compare?hashA=${HASH_A}&hashB=${HASH_B}&origin=maestro`,
`/tree/linux/master/compare?range=${HASH_A}..${HASH_B}&origin=maestro`,
);

await page.getByPlaceholder('Search').fill('keep-');
Expand Down
14 changes: 7 additions & 7 deletions dashboard/src/locales/messages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ export const messages = {
'tree.path': 'Trees',
'tree.searchPlaceholder': 'Search by tree, branch or tag with a regex',
'treeCompare.backToDetails': 'Back to tree details',
'treeCompare.base': 'Base',
'treeCompare.breadcrumb': 'Compare',
'treeCompare.breakdownTitle': 'Changed results',
'treeCompare.change.appeared': 'Appeared',
Expand All @@ -431,11 +432,12 @@ export const messages = {
'treeCompare.change.stillFailing': 'Still failing',
'treeCompare.change.unchanged': 'Unchanged',
'treeCompare.changed': 'Changed',
'treeCompare.compare': 'Compare',
'treeCompare.description':
'Compare pass/fail counts between two revisions on the same tree and branch.',
'treeCompare.detail.missingSide': 'No result on this side',
'Compare pass/fail counts of a revision against a base revision on the same tree and branch.',
'treeCompare.detail.missingSide': 'No result on this revision',
'treeCompare.drilldownHint':
'Individual builds, boots, and tests that changed between Side A and Side B.',
'Individual builds, boots, and tests that changed from the base revision to the compared revision.',
'treeCompare.failures.change': 'Change',
'treeCompare.failures.configArch': 'Config / Arch',
'treeCompare.failures.pathArch': 'Path / Arch',
Expand All @@ -454,22 +456,20 @@ export const messages = {
'Select two different revisions to compare. This tree needs at least two commits with results.',
'treeCompare.openCompare': 'Compare revisions',
'treeCompare.selectRevision': 'Select a revision',
'treeCompare.sideA': 'Side A',
'treeCompare.sideB': 'Side B',
'treeCompare.statusPairFilter.active': 'Active status pair filters',
'treeCompare.statusPairFilter.add': 'Add',
'treeCompare.statusPairFilter.from': 'From',
'treeCompare.statusPairFilter.glossary':
'PASS — completed successfully{br}' +
'FAIL — completed with a failure{br}' +
'INCONCLUSIVE — no definitive pass or fail result{br}' +
'— — absent on this side',
'— — absent on this revision',
'treeCompare.statusPairFilter.remove': 'Remove {from} to {to} filter',
'treeCompare.statusPairFilter.select': 'Select status',
'treeCompare.statusPairFilter.to': 'To',
'treeCompare.suggestion.branchHead': 'Branch head',
'treeCompare.suggestion.previous': 'Previous commit',
'treeCompare.suggestion.swap': 'Swap sides',
'treeCompare.suggestion.swap': 'Swap base and compare',
'treeCompare.suggestions': 'Suggestions',
'treeCompare.summaryTitle': 'Tree summary',
'treeDetails.bootsHistory': 'Boots History',
Expand Down
7 changes: 6 additions & 1 deletion dashboard/src/pages/TreeCompare/TreeCompareLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { FormattedMessage } from 'react-intl';

import { Button } from '@/components/ui/button';

import { formatCompareRange } from '@/utils/treeCompareDiff';

interface TreeCompareLinkProps {
treeName: string;
branch: string;
Expand All @@ -24,7 +26,10 @@ export function TreeCompareLink({
<Link
to="/tree/$treeName/$branch/compare"
params={{ treeName, branch }}
search={{ hashA: hash, hashB: '', origin }}
search={{
range: formatCompareRange({ base: '', compare: hash }),
origin,
}}
state={s => s}
>
<GitCompareArrows className="mr-2 h-4 w-4" />
Expand Down
83 changes: 43 additions & 40 deletions dashboard/src/pages/TreeCompare/TreeComparePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,14 @@ import {
} from '@/types/tree/TreeCompare';
import type { PossibleTabs } from '@/types/tree/TreeDetails';
import {
type CompareRange,
applyStatusPairFilter,
formatCompareRange,
mapBootOrTestDiffRows,
mapBuildDiffRows,
parseCompareRange,
readStoredStatusPairs,
resolveCompareRange,
resolveStatusPairs,
serializeStatusPairs,
writeStoredStatusPairs,
Expand All @@ -57,16 +61,20 @@ import {
} from './components/CompareFailuresTables';
import { CompareTestsGroupedTable } from './components/CompareTestsGroupedTable';
import { CompareSummary } from './components/CompareSummary';
import { RevisionSelectorBar } from './components/RevisionSelector';
import {
RevisionSelectorBar,
type RevisionSide,
} from './components/RevisionSelector';

const SHORT_HASH_LENGTH = 7;

const TreeComparePage = (): JSX.Element => {
const { formatMessage } = useIntl();
const { treeName, branch } = useParams({ from: compareRouteName });
const { hashA, hashB, origin, currentPageTab, statusPair } = useSearch({
const { range, origin, currentPageTab, statusPair } = useSearch({
from: compareRouteName,
});
const { base: baseHash, compare: compareHash } = parseCompareRange(range);
const navigate = useNavigate({ from: compareNavigateFrom });

const commitsQuery = useCommits({
Expand All @@ -88,19 +96,19 @@ const TreeComparePage = (): JSX.Element => {
[commitsQuery.data],
);

const resolvedHashA = hashA || revisions[0]?.hash || '';
const resolvedHashB =
hashB ||
revisions.find(revision => revision.hash !== resolvedHashA)?.hash ||
'';
const { base: resolvedBase, compare: resolvedCompare } = useMemo(
() =>
resolveCompareRange(revisions, { base: baseHash, compare: compareHash }),
[revisions, baseHash, compareHash],
);

const canCompare = Boolean(resolvedHashA && resolvedHashB);
const canCompare = Boolean(resolvedBase && resolvedCompare);

const compareParams = {
treeName,
branch,
hashA: resolvedHashA,
hashB: resolvedHashB,
hashA: resolvedBase,
hashB: resolvedCompare,
origin,
};

Expand All @@ -111,16 +119,17 @@ const TreeComparePage = (): JSX.Element => {

const updateSearch = useCallback(
(updates: {
hashA?: string;
hashB?: string;
range?: Partial<CompareRange>;
currentPageTab?: PossibleTabs;
statusPair?: string[];
}) => {
navigate({
search: previous => ({
...previous,
hashA: updates.hashA ?? previous.hashA,
hashB: updates.hashB ?? previous.hashB,
range: formatCompareRange({
...parseCompareRange(previous.range),
...updates.range,
}),
currentPageTab:
updates.currentPageTab ??
previous.currentPageTab ??
Expand All @@ -135,18 +144,18 @@ const TreeComparePage = (): JSX.Element => {
);

useEffect(() => {
if ((!hashA || !hashB) && resolvedHashA && resolvedHashB) {
updateSearch({ hashA: resolvedHashA, hashB: resolvedHashB });
if ((!baseHash || !compareHash) && resolvedBase && resolvedCompare) {
updateSearch({ range: { base: resolvedBase, compare: resolvedCompare } });
}
}, [hashA, hashB, resolvedHashA, resolvedHashB, updateSearch]);
}, [baseHash, compareHash, resolvedBase, resolvedCompare, updateSearch]);

const handleSwap = useCallback(() => {
updateSearch({ hashA: resolvedHashB, hashB: resolvedHashA });
}, [resolvedHashA, resolvedHashB, updateSearch]);
updateSearch({ range: { base: resolvedCompare, compare: resolvedBase } });
}, [resolvedBase, resolvedCompare, updateSearch]);

const handleSideAction = useCallback(
(side: 'A' | 'B', action: 'previous' | 'branchHead') => {
const currentHash = side === 'A' ? resolvedHashA : resolvedHashB;
(side: RevisionSide, action: 'previous' | 'branchHead') => {
const currentHash = side === 'base' ? resolvedBase : resolvedCompare;
const currentIndex = revisions.findIndex(r => r.hash === currentHash);

if (action === 'previous') {
Expand All @@ -155,22 +164,14 @@ const TreeComparePage = (): JSX.Element => {
Math.max(currentIndex, 0) + 1,
);
const nextHash = revisions[previousIndex]?.hash ?? currentHash;
if (side === 'A') {
updateSearch({ hashA: nextHash });
} else {
updateSearch({ hashB: nextHash });
}
updateSearch({ range: { [side]: nextHash } });
return;
}

const headHash = revisions[0]?.hash ?? currentHash;
if (side === 'A') {
updateSearch({ hashA: headHash });
} else {
updateSearch({ hashB: headHash });
}
updateSearch({ range: { [side]: headHash } });
},
[revisions, resolvedHashA, resolvedHashB, updateSearch],
[revisions, resolvedBase, resolvedCompare, updateSearch],
);

const buildRows = useMemo(
Expand Down Expand Up @@ -317,7 +318,7 @@ const TreeComparePage = (): JSX.Element => {
params={{
treeName,
branch,
hash: resolvedHashA,
hash: resolvedCompare,
}}
state={s => s}
>
Expand Down Expand Up @@ -345,8 +346,8 @@ const TreeComparePage = (): JSX.Element => {
origin,
gitUrl: compareQuery.data?.gitUrl,
branch,
hashA: resolvedHashA || undefined,
hashB: resolvedHashB || undefined,
hashA: resolvedBase || undefined,
hashB: resolvedCompare || undefined,
omittedFilters:
statusPairs.length > 0 ? ['status-pair filter'] : [],
})}
Expand All @@ -362,7 +363,7 @@ const TreeComparePage = (): JSX.Element => {
</div>
<Link
to="/tree/$treeName/$branch/$hash"
params={{ treeName, branch, hash: resolvedHashA }}
params={{ treeName, branch, hash: resolvedCompare }}
className="text-blue text-sm font-medium hover:underline"
state={s => s}
>
Expand All @@ -375,11 +376,13 @@ const TreeComparePage = (): JSX.Element => {
error={commitsQuery.error}
>
<RevisionSelectorBar
hashA={resolvedHashA}
hashB={resolvedHashB}
baseHash={resolvedBase}
compareHash={resolvedCompare}
revisions={revisions}
onHashAChange={value => updateSearch({ hashA: value })}
onHashBChange={value => updateSearch({ hashB: value })}
onBaseChange={value => updateSearch({ range: { base: value } })}
onCompareChange={value =>
updateSearch({ range: { compare: value } })
}
onSideAction={handleSideAction}
onSwap={handleSwap}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function SideColumn({
logData,
isLoading,
}: {
labelId: 'treeCompare.sideA' | 'treeCompare.sideB';
labelId: 'treeCompare.base' | 'treeCompare.compare';
status: CompareItemStatus;
id: string | null;
logType: LogType;
Expand Down Expand Up @@ -154,15 +154,15 @@ export function CompareDetailSheet({
</div>
<div className="grid gap-6 lg:grid-cols-2">
<SideColumn
labelId="treeCompare.sideA"
labelId="treeCompare.base"
status={item.sideA}
id={item.idA}
logType={logType}
logData={logA.data}
isLoading={logA.isLoading}
/>
<SideColumn
labelId="treeCompare.sideB"
labelId="treeCompare.compare"
status={item.sideB}
id={item.idB}
logType={logType}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,15 +268,15 @@ export function CompareBuildsFailuresTable({
/>
<SortableHead
className="text-center"
intlKey="treeCompare.sideA"
intlKey="treeCompare.base"
sortKey="sideA"
sort={sort}
onSort={key => setSort(current => cycleSort(current, key))}
/>
<TableHead className="bg-light-gray w-8" />
<SortableHead
className="text-center"
intlKey="treeCompare.sideB"
intlKey="treeCompare.compare"
sortKey="sideB"
sort={sort}
onSort={key => setSort(current => cycleSort(current, key))}
Expand Down Expand Up @@ -408,15 +408,15 @@ export function CompareBootsFailuresTable({
/>
<SortableHead
className="text-center"
intlKey="treeCompare.sideA"
intlKey="treeCompare.base"
sortKey="sideA"
sort={sort}
onSort={key => setSort(current => cycleSort(current, key))}
/>
<TableHead className="bg-light-gray w-8" />
<SortableHead
className="text-center"
intlKey="treeCompare.sideB"
intlKey="treeCompare.compare"
sortKey="sideB"
sort={sort}
onSort={key => setSort(current => cycleSort(current, key))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ function createCompareSideColumns(
sideStatus(row, 'sideA'),
header: ({ column }): JSX.Element => (
<div className="flex justify-center">
<SortableTableHeader column={column} intlKey="treeCompare.sideA" />
<SortableTableHeader column={column} intlKey="treeCompare.base" />
</div>
),
cell: ({ row }): JSX.Element | null => {
Expand Down Expand Up @@ -273,7 +273,7 @@ function createCompareSideColumns(
sideStatus(row, 'sideB'),
header: ({ column }): JSX.Element => (
<div className="flex justify-center">
<SortableTableHeader column={column} intlKey="treeCompare.sideB" />
<SortableTableHeader column={column} intlKey="treeCompare.compare" />
</div>
),
cell: ({ row }): JSX.Element | null => {
Expand Down
Loading
Loading