Files
wehub-resource-sync 070959e133
landing-page-staging / Deploy landing page to staging (push) Has been skipped
landing-page-ci / Validate landing page (push) Failing after 4s
visual-baseline / Capture visual baselines (push) Has been cancelled
bake-plugin-previews / Bake plugin previews (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:00:47 +08:00

115 lines
3.7 KiB
TypeScript

/**
* URL router round-trip tests (issue #1505).
*
* Pins the deep-link shape for the project route:
*
* / home
* /projects/:id project root
* /projects/:id/files/:path file view
* /projects/:id/conversations/:cid specific conversation
* /projects/:id/conversations/:cid/files/:path conversation + file
*
* The conversation segment was added to unblock the Routines history
* row: clicking "Open project" on a parallel run's row needs to land
* the user on that run's own conversation, not on whatever the
* project happens to default to.
*/
import { describe, expect, it } from 'vitest';
import { buildPath, parseRoute, type Route } from '../src/router';
function roundTrip(route: Route): Route {
return parseRoute(buildPath(route));
}
describe('parseRoute / buildPath (issue #1505)', () => {
it('parses the home route', () => {
expect(parseRoute('/')).toEqual({ kind: 'home', view: 'home' });
expect(parseRoute('')).toEqual({ kind: 'home', view: 'home' });
});
it('round-trips a bare project route', () => {
const route: Route = {
kind: 'project',
projectId: 'p-1',
conversationId: null,
fileName: null,
};
expect(roundTrip(route)).toEqual(route);
expect(buildPath(route)).toBe('/projects/p-1');
});
it('round-trips a project + file route (no conversation)', () => {
const route: Route = {
kind: 'project',
projectId: 'p-1',
conversationId: null,
fileName: 'src/index.tsx',
};
expect(roundTrip(route)).toEqual(route);
expect(buildPath(route)).toBe('/projects/p-1/files/src/index.tsx');
});
it('round-trips a project + conversation route', () => {
const route: Route = {
kind: 'project',
projectId: 'p-1',
conversationId: 'conv-abc',
fileName: null,
};
expect(roundTrip(route)).toEqual(route);
expect(buildPath(route)).toBe('/projects/p-1/conversations/conv-abc');
});
it('round-trips a project + conversation + file route', () => {
const route: Route = {
kind: 'project',
projectId: 'p-1',
conversationId: 'conv-abc',
fileName: 'index.html',
};
expect(roundTrip(route)).toEqual(route);
expect(buildPath(route)).toBe('/projects/p-1/conversations/conv-abc/files/index.html');
});
it('percent-encodes ids and file names with reserved characters', () => {
const route: Route = {
kind: 'project',
projectId: 'p/1 with space',
conversationId: 'conv/abc with space',
fileName: 'dir/file name.tsx',
};
const built = buildPath(route);
expect(built).toContain('p%2F1%20with%20space');
expect(built).toContain('conv%2Fabc%20with%20space');
// File path components are percent-encoded individually so the
// slash between segments survives.
expect(built.endsWith('/dir/file%20name.tsx')).toBe(true);
expect(roundTrip(route)).toEqual(route);
});
it('parses a legacy project + file URL with no conversation segment', () => {
expect(parseRoute('/projects/p-1/files/README.md')).toEqual({
kind: 'project',
projectId: 'p-1',
conversationId: null,
fileName: 'README.md',
});
});
it('parses a project + conversation URL with no file segment', () => {
expect(parseRoute('/projects/p-1/conversations/c-2')).toEqual({
kind: 'project',
projectId: 'p-1',
conversationId: 'c-2',
fileName: null,
});
});
it('falls back to home when the URL is unrecognized', () => {
expect(parseRoute('/something/else')).toEqual({ kind: 'home', view: 'home' });
expect(parseRoute('/projects')).toEqual({ kind: 'home', view: 'projects' });
});
});