Files
Remote-Terminal-for-MeshCore/frontend/src/test/urlHash.test.ts

257 lines
6.7 KiB
TypeScript

/**
* Tests for URL hash utilities.
*
* These tests verify the URL hash parsing and generation
* for deep linking to conversations.
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { parseHashConversation, getConversationHash, getMapFocusHash } from '../utils/urlHash';
import type { Conversation } from '../types';
describe('parseHashConversation', () => {
let originalHash: string;
beforeEach(() => {
originalHash = window.location.hash;
});
afterEach(() => {
window.location.hash = originalHash;
});
it('returns null for empty hash', () => {
window.location.hash = '';
const result = parseHashConversation();
expect(result).toBeNull();
});
it('parses #raw as raw type', () => {
window.location.hash = '#raw';
const result = parseHashConversation();
expect(result).toEqual({ type: 'raw', name: 'raw' });
});
it('parses #map as map type', () => {
window.location.hash = '#map';
const result = parseHashConversation();
expect(result).toEqual({ type: 'map', name: 'map' });
});
it('parses #map/focus/PUBKEY with focus key', () => {
window.location.hash = '#map/focus/ABCD1234';
const result = parseHashConversation();
expect(result).toEqual({ type: 'map', name: 'map', mapFocusKey: 'ABCD1234' });
});
it('parses #map/focus/ with empty focus as plain map', () => {
window.location.hash = '#map/focus/';
const result = parseHashConversation();
expect(result).toEqual({ type: 'map', name: 'map' });
});
it('decodes URL-encoded map focus key', () => {
window.location.hash = '#map/focus/AB%20CD';
const result = parseHashConversation();
expect(result).toEqual({ type: 'map', name: 'map', mapFocusKey: 'AB CD' });
});
it('parses channel hash', () => {
window.location.hash = '#channel/Public';
const result = parseHashConversation();
expect(result).toEqual({ type: 'channel', name: 'Public' });
});
it('parses contact hash', () => {
window.location.hash = '#contact/Alice';
const result = parseHashConversation();
expect(result).toEqual({ type: 'contact', name: 'Alice' });
});
it('decodes URL-encoded names', () => {
window.location.hash = '#contact/John%20Doe';
const result = parseHashConversation();
expect(result).toEqual({ type: 'contact', name: 'John Doe' });
});
it('returns null for invalid type', () => {
window.location.hash = '#invalid/Test';
const result = parseHashConversation();
expect(result).toBeNull();
});
it('returns null for hash without slash', () => {
window.location.hash = '#channelPublic';
const result = parseHashConversation();
expect(result).toBeNull();
});
it('returns null for hash with empty name', () => {
window.location.hash = '#channel/';
const result = parseHashConversation();
expect(result).toBeNull();
});
it('handles channel names with special characters', () => {
window.location.hash = '#channel/Test%20Channel%21';
const result = parseHashConversation();
expect(result).toEqual({ type: 'channel', name: 'Test Channel!' });
});
});
describe('getConversationHash', () => {
it('returns empty string for null conversation', () => {
const result = getConversationHash(null);
expect(result).toBe('');
});
it('returns #raw for raw conversation', () => {
const conv: Conversation = { type: 'raw', id: 'raw', name: 'Raw Packet Feed' };
const result = getConversationHash(conv);
expect(result).toBe('#raw');
});
it('returns #map for map conversation', () => {
const conv: Conversation = { type: 'map', id: 'map', name: 'Node Map' };
const result = getConversationHash(conv);
expect(result).toBe('#map');
});
it('generates channel hash', () => {
const conv: Conversation = { type: 'channel', id: 'key123', name: 'Public' };
const result = getConversationHash(conv);
expect(result).toBe('#channel/Public');
});
it('generates contact hash', () => {
const conv: Conversation = { type: 'contact', id: 'pubkey123', name: 'Alice' };
const result = getConversationHash(conv);
expect(result).toBe('#contact/Alice');
});
it('strips leading # from channel names', () => {
const conv: Conversation = { type: 'channel', id: 'key123', name: '#TestChannel' };
const result = getConversationHash(conv);
expect(result).toBe('#channel/TestChannel');
});
it('encodes special characters in names', () => {
const conv: Conversation = { type: 'contact', id: 'key', name: 'John Doe' };
const result = getConversationHash(conv);
expect(result).toBe('#contact/John%20Doe');
});
it('does not strip # from contact names', () => {
const conv: Conversation = { type: 'contact', id: 'key', name: '#Hashtag' };
const result = getConversationHash(conv);
expect(result).toBe('#contact/%23Hashtag');
});
});
describe('parseHashConversation and getConversationHash roundtrip', () => {
let originalHash: string;
beforeEach(() => {
originalHash = window.location.hash;
});
afterEach(() => {
window.location.hash = originalHash;
});
it('channel roundtrip preserves data', () => {
const conv: Conversation = { type: 'channel', id: 'key123', name: 'Test Channel' };
const hash = getConversationHash(conv);
window.location.hash = hash;
const parsed = parseHashConversation();
expect(parsed).toEqual({ type: 'channel', name: 'Test Channel' });
});
it('contact roundtrip preserves data', () => {
const conv: Conversation = { type: 'contact', id: 'pubkey', name: 'Alice Bob' };
const hash = getConversationHash(conv);
window.location.hash = hash;
const parsed = parseHashConversation();
expect(parsed).toEqual({ type: 'contact', name: 'Alice Bob' });
});
it('raw roundtrip preserves type', () => {
const conv: Conversation = { type: 'raw', id: 'raw', name: 'Raw Packet Feed' };
const hash = getConversationHash(conv);
window.location.hash = hash;
const parsed = parseHashConversation();
expect(parsed).toEqual({ type: 'raw', name: 'raw' });
});
it('map roundtrip preserves type', () => {
const conv: Conversation = { type: 'map', id: 'map', name: 'Node Map' };
const hash = getConversationHash(conv);
window.location.hash = hash;
const parsed = parseHashConversation();
expect(parsed).toEqual({ type: 'map', name: 'map' });
});
});
describe('getMapFocusHash', () => {
it('generates hash with focus key', () => {
const result = getMapFocusHash('ABCD1234');
expect(result).toBe('#map/focus/ABCD1234');
});
it('encodes special characters in key', () => {
const result = getMapFocusHash('AB CD/12');
expect(result).toBe('#map/focus/AB%20CD%2F12');
});
});