import { describe, expect, it } from 'vitest';
import type {
CatalogItem,
ComparisonCategory
} from '@tool.io/tools/compare';
import {
commonComparisonCategory,
comparisonHref,
comparisonItemsHref,
maximumComparisonItems,
parseComparisonSlugs,
qualifiedComparisonSlug
} from './routes';
const categories: ComparisonCategory[] = [
{
id: 'astronomy',
name: 'Astronomy',
parentId: null,
sectionOrder: [],
slug: 'astronomy'
},
{
id: 'star',
name: 'Stars',
parentId: 'astronomy',
sectionOrder: [],
slug: 'stars'
},
{
id: 'product',
name: 'Products',
parentId: null,
sectionOrder: [],
slug: 'products'
},
{
id: 'smartphone',
name: 'Smartphones',
parentId: 'product',
sectionOrder: [],
slug: 'smartphones'
}
];
const item = (
id: string,
name: string,
categoryIds: string[]
): CatalogItem => ({
attributes: {},
categoryIds,
id,
image: null,
name,
schemaVersion: 1,
slug: id,
sourceIds: ['fixture'],
summary: `${name} fixture`,
updatedAt: '2026-09-15T00:00:00Z'
});
const sirius = item('sirius', 'Sirius', ['astronomy', 'star']);
const sun = item('sun', 'Sun', ['astronomy', 'star']);
const phone = item(
'samsung-galaxy-s26-global-256gb',
'Samsung Galaxy S26 Global 256 GB',
['product', 'smartphone']
);
const otherPhone = item('apple-iphone-17', 'Apple iPhone 17', [
'product',
'smartphone'
]);
describe('comparison routes', () => {
it('parses two or more slugs separated by the reserved delimiter', () => {
expect(parseComparisonSlugs('sirius-vs-sun')).toEqual([
'sirius',
'sun'
]);
expect(parseComparisonSlugs('one-vs-two-vs-three')).toEqual(
['one', 'two', 'three']
);
});
it('accepts at most twelve items in a comparison URL', () => {
const twelve = Array.from(
{ length: maximumComparisonItems },
(_, index) => `item-${index + 1}`
).join('-vs-');
expect(parseComparisonSlugs(twelve)).toHaveLength(12);
expect(parseComparisonSlugs(`${twelve}-vs-item-13`)).toBeNull();
});
it('qualifies non-product slugs with their most specific category', () => {
expect(qualifiedComparisonSlug(sirius, categories)).toBe(
'star-sirius'
);
expect(qualifiedComparisonSlug(phone, categories)).toBe(
phone.slug
);
});
it('uses concise scoped paths for same-category comparisons', () => {
expect(
comparisonHref(sun, sirius, categories, categories[1])
).toBe('/compare/stars/sirius-vs-sun');
});
it('uses qualified slugs for unrelated items', () => {
expect(comparisonHref(sirius, phone, categories)).toBe(
'/compare/samsung-galaxy-s26-global-256gb-vs-star-sirius'
);
});
it('keeps every selected item in a multi-item URI', () => {
expect(
comparisonItemsHref([sun, phone, sirius], categories)
).toBe(
'/compare/samsung-galaxy-s26-global-256gb-vs-star-sirius-vs-star-sun'
);
});
it('keeps all twelve selected items in the URL', () => {
const stars = Array.from(
{ length: maximumComparisonItems },
(_, index) =>
item(`star-${index + 1}`, `Star ${index + 1}`, [
'astronomy',
'star'
])
);
const href = comparisonItemsHref(
stars,
categories,
categories[1]
);
expect(href.startsWith('/compare/stars/')).toBe(true);
expect(href.match(/-vs-/g)).toHaveLength(11);
});
it('automatically chooses the most specific shared category', () => {
expect(
commonComparisonCategory([phone, otherPhone], categories)
?.id
).toBe('smartphone');
expect(
comparisonItemsHref([phone, otherPhone], categories)
).toBe(
'/compare/smartphones/apple-iphone-17-vs-samsung-galaxy-s26-global-256gb'
);
});
});
Browse the implementation files for GPUs Comparison.