mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
257 lines
6.7 KiB
TypeScript
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');
|
|
});
|
|
});
|