Files
MyIP/tests/tool-availability.test.js
T
jason5ng32andClaude Fable 5.1 c55634c8e0 Feat(asn): frontend groundwork for the ASN Profile tool
- Registry gate `requiresConfig`: a tool is listed in the Advanced Tools
  grid and the nav only when its backend config is present; the listing
  filter is one pure function. ASN Profile requires the Cloudflare key.
- `useAsnInfo` owns the Radar ASN fetch and its session cache, shared by
  the IP cards and the query dialog.
- ASN Info's traffic-share and connection-quality blocks become small
  components over pure helpers, reusable by the profile page.
- ASN Profile lands as a mock-data page for now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 19:47:15 +08:00

76 lines
3.4 KiB
JavaScript

// Tests for frontend/utils/tool-availability.js — the listing gate the
// Advanced Tools card grid and nav filter through — plus the gate fields the
// registry in frontend/data/tools.js declares.
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { isToolAvailable } from '../frontend/utils/tool-availability.js';
import { ADVANCED_TOOLS, TOOL_BY_SLUG } from '../frontend/data/tools.js';
const PUBLIC = { slug: 'public' };
const ORIGINAL = { slug: 'original', requiresOriginalSite: true };
const RADAR = { slug: 'radar', requiresConfig: 'cloudFlare' };
const BOTH = { slug: 'both', requiresOriginalSite: true, requiresConfig: 'cloudFlare' };
describe('isToolAvailable', () => {
it('an ungated tool is always available', () => {
assert.equal(isToolAvailable(PUBLIC, {}), true);
assert.equal(isToolAvailable(PUBLIC, undefined), true);
assert.equal(isToolAvailable(PUBLIC, { originalSite: false, cloudFlare: false }), true);
});
it('a gated tool is unavailable until configs land', () => {
assert.equal(isToolAvailable(ORIGINAL, {}), false);
assert.equal(isToolAvailable(RADAR, {}), false);
assert.equal(isToolAvailable(RADAR, null), false);
});
it('requiresOriginalSite follows configs.originalSite', () => {
assert.equal(isToolAvailable(ORIGINAL, { originalSite: true }), true);
assert.equal(isToolAvailable(ORIGINAL, { originalSite: false }), false);
});
it('requiresConfig follows the named configs flag', () => {
assert.equal(isToolAvailable(RADAR, { cloudFlare: true }), true);
assert.equal(isToolAvailable(RADAR, { cloudFlare: false }), false);
assert.equal(isToolAvailable(RADAR, { map: true }), false);
});
it('both gates must pass', () => {
assert.equal(isToolAvailable(BOTH, { originalSite: true, cloudFlare: true }), true);
assert.equal(isToolAvailable(BOTH, { originalSite: true, cloudFlare: false }), false);
assert.equal(isToolAvailable(BOTH, { originalSite: false, cloudFlare: true }), false);
});
it('no tool is unavailable', () => {
assert.equal(isToolAvailable(null, { cloudFlare: true }), false);
assert.equal(isToolAvailable(undefined, {}), false);
});
it('filters a list in registry order', () => {
const tools = [PUBLIC, ORIGINAL, RADAR, BOTH];
const listed = (configs) => tools.filter((tool) => isToolAvailable(tool, configs)).map((tool) => tool.slug);
assert.deepEqual(listed({}), ['public']);
assert.deepEqual(listed({ originalSite: false, cloudFlare: true }), ['public', 'radar']);
assert.deepEqual(listed({ originalSite: true, cloudFlare: true }), ['public', 'original', 'radar', 'both']);
});
});
describe('registry gates (data/tools.js)', () => {
it('ASN Profile is gated on the Cloudflare Radar key only', () => {
const asn = TOOL_BY_SLUG.get('asn');
assert.ok(asn, 'asn tool is registered');
assert.equal(asn.requiresConfig, 'cloudFlare');
assert.equal(asn.requiresOriginalSite, undefined);
assert.equal(isToolAvailable(asn, { cloudFlare: false, originalSite: true }), false);
assert.equal(isToolAvailable(asn, { cloudFlare: true, originalSite: false }), true);
});
it('every requiresConfig is a non-empty string', () => {
for (const tool of ADVANCED_TOOLS.filter((entry) => 'requiresConfig' in entry)) {
assert.equal(typeof tool.requiresConfig, 'string', tool.slug);
assert.ok(tool.requiresConfig.length > 0, tool.slug);
}
});
});