Files
explore/configs/app/primaryChains.spec.ts
zeekay eac4772aab feat(explore/primary-vms): handle all 10 Lux-primary VM types + route D-Chain to the DEX UI
The Lux primary network is a family of VM-specialized chains (C=EVM, X=UTXO,
D=DEX, P=platform, + A/B/Q/T/Z/G/K/O/R/I/M), mirrored from node/node/vms.go.

- configs/app/primaryChains.ts: single source of truth (PRIMARY_VMS, getPrimaryVm,
  per-VM `view`, hasBespokeView, chainsAwaitingBespokeView). Decomplects the two
  lists that duplicated this — ChainsPage + ChainDetailPage now consume it
  (ChainDetailPage drops ~110 lines of copy).
- D-Chain (DexVM) renders the DEX order-book UI (ui/dex/DexPage) via view:'dex',
  not a generic block list. C-Chain=EVM core, P-Chain=platform/validators,
  X-Chain + the custom VMs render the generic detail fallback (info + indexer DAG
  stats + validators) — graceful, never blank. chainsAwaitingBespokeView() FLAGS
  the backlog: X-Chain (UTXO) + the 11 custom VMs still need a bespoke view.
- These VMs are Lux-primary, so the primary-VM surfaces (Chains/DEX/Bridge/AI
  Compute nav + /chains, /chains/[slug], /dex pages) gate on
  isPrimaryNetworkExplorer(). ui/shared/PrimaryNetworkGuard backstops direct-URL
  access so the VMs never leak onto a brand explorer (Hanzo/Zoo/Pars).
- Tests: configs/app/primaryChains.spec.ts (26 cases) — registry, per-VM view,
  bespoke-view flag, and the lux-only visibility keystone. 38/38 green with the
  existing chainRegistry.spec.ts.
2026-06-28 15:08:34 -07:00

114 lines
4.3 KiB
TypeScript

import { describe, expect, it, beforeEach } from 'vitest';
import { isPrimaryNetworkExplorer } from './chainRegistry';
import {
PRIMARY_VMS,
getPrimaryVm,
hasBespokeView,
chainsAwaitingBespokeView,
} from './primaryChains';
// Drives the registry off a simulated request host, exactly like the browser
// (getHostname() reads window.location.hostname). Mirrors chainRegistry.spec.ts.
function atHost(hostname: string) {
(globalThis as unknown as { window: unknown }).window = { location: { hostname }, __envs: {} };
}
describe('primary-network VM registry', () => {
// The 10 VM chains the Lux primary network must expose + route (the build
// directive). The registry is a superset (P/M/O/R/I as well), so we assert
// the mandated 10 are all present with the right VM type.
const REQUIRED = [
{ slug: 'c-chain', vm: 'EVM' },
{ slug: 'x-chain', vm: 'XVM' },
{ slug: 'd-chain', vm: 'DexVM' },
{ slug: 'a-chain', vm: 'AIVM' },
{ slug: 'z-chain', vm: 'ZKVM' },
{ slug: 'q-chain', vm: 'QuantumVM' },
{ slug: 'b-chain', vm: 'BridgeVM' },
{ slug: 'k-chain', vm: 'KeyVM' },
{ slug: 'g-chain', vm: 'GraphVM' },
{ slug: 't-chain', vm: 'ThresholdVM' },
];
it.each(REQUIRED)('registers $slug as $vm', ({ slug, vm }) => {
const entry = getPrimaryVm(slug);
expect(entry).toBeDefined();
expect(entry?.vm).toBe(vm);
});
it('exposes every VM at a unique slug routed under /chains/<slug>', () => {
const slugs = PRIMARY_VMS.map((c) => c.slug);
expect(new Set(slugs).size).toBe(slugs.length);
expect(PRIMARY_VMS.length).toBeGreaterThanOrEqual(REQUIRED.length);
});
it('resolves slugs case-insensitively', () => {
expect(getPrimaryVm('D-Chain')?.slug).toBe('d-chain');
expect(getPrimaryVm('C-CHAIN')?.slug).toBe('c-chain');
});
describe('each VM type maps to the correct render view', () => {
it('D-Chain renders the DEX order-book UI (not an EVM block list)', () => {
expect(getPrimaryVm('d-chain')?.view).toBe('dex');
});
it('C-Chain renders the EVM model', () => {
expect(getPrimaryVm('c-chain')?.view).toBe('evm');
});
it('X-Chain renders the UTXO model', () => {
expect(getPrimaryVm('x-chain')?.view).toBe('utxo');
});
it('P-Chain renders the platform/validators model', () => {
expect(getPrimaryVm('p-chain')?.view).toBe('platform');
});
it('custom VMs (A/Z/Q/B/K/G/T) fall back to the generic view', () => {
for (const slug of [ 'a-chain', 'z-chain', 'q-chain', 'b-chain', 'k-chain', 'g-chain', 't-chain' ]) {
expect(getPrimaryVm(slug)?.view).toBe('generic');
}
});
});
describe('bespoke-view flag — what still needs a native explorer', () => {
it('EVM / platform / DEX have a bespoke view today', () => {
expect(hasBespokeView('evm')).toBe(true);
expect(hasBespokeView('platform')).toBe(true);
expect(hasBespokeView('dex')).toBe(true);
});
it('UTXO + generic still fall back (flagged backlog)', () => {
expect(hasBespokeView('utxo')).toBe(false);
expect(hasBespokeView('generic')).toBe(false);
});
it('flags X-Chain and the custom VMs, never C/P/D', () => {
const flagged = chainsAwaitingBespokeView().map((c) => c.slug);
expect(flagged).toEqual(expect.arrayContaining([ 'x-chain', 'a-chain', 'z-chain', 'q-chain', 'b-chain', 'k-chain', 'g-chain', 't-chain' ]));
expect(flagged).not.toContain('c-chain');
expect(flagged).not.toContain('p-chain');
expect(flagged).not.toContain('d-chain');
});
});
});
// The primary-network VM surfaces (Chains / Chain detail / DEX nav + pages) are
// gated on isPrimaryNetworkExplorer(). This keystone proves the 10 VMs surface
// ONLY on the Lux explorer and never leak onto a brand explorer — the same
// predicate the nav and PrimaryNetworkGuard use.
describe('primary VMs surface only on the Lux primary explorer', () => {
beforeEach(() => atHost('localhost'));
it('shows the VM surfaces on the Lux explorer', () => {
atHost('explore.lux.network');
expect(isPrimaryNetworkExplorer()).toBe(true);
});
it.each([
'explore.hanzo.network',
'explore.zoo.network',
'explore.pars.network',
'explore.osage.network',
'explore.unknown.example',
])('hides the VM surfaces on %s (no leak)', (host) => {
atHost(host);
expect(isPrimaryNetworkExplorer()).toBe(false);
});
});