Files
MyIP/tests/ip-calculator-query.test.js
Claude 96525d2fc7 Chore: trim the branch — leftovers, redundancy, comments
A pass over every file the branch touched, after the iOS animation hunt
left tries and reverts behind it. Removed what nothing reaches: the
view-transition rule for a transition no code starts, an unreachable
guard in the transition hook, a no-op KeepAlive key branch, a sign-in
guard the menu already enforces, usage-dialog wrappers that only
forwarded to the store, a duplicated modifier-click check and card
filter (isPlainClick / listedTools), a test that hand-rolled
listedTools, a drawer-era fixture parameter. auth-flow.js folded into
auth-redirect.js. Comments cut to what the code cannot say itself —
file headers of a few lines, the measured iOS reasons, non-obvious
orderings — and stale ones fixed (Nav under KeepAlive, Home.vue
reusing DEFAULT_META, "from Nav", enabledCards). AGENTS.md sections
tightened to the code as it is now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
2026-10-05 16:50:16 +00:00

99 lines
4.5 KiB
JavaScript

// Exercise calculator prefix commits and shareable query state without a DOM.
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { test } from 'node:test';
import { computed, nextTick, reactive, ref, watch } from 'vue';
import { parse } from 'vue/compiler-sfc';
import { analyzeCidr, calculate } from '../frontend/utils/features/ip-calc.js';
import { useActiveValue } from '../frontend/composables/use-route-active.js';
const source = readFileSync(new URL('../frontend/components/advanced-tools/IpCalculator.vue', import.meta.url), 'utf8');
const script = parse(source).descriptor.scriptSetup.content.replace(/^import[\s\S]*?;$/gm, '');
// `active` stands in for the page's KeepAlive state (use-route-active.js).
const fixture = (q, active = ref(true)) => {
const route = reactive({ path: '/tools/ipcalculator', query: { q, hl: 'en' } });
const replacements = [];
const bindings = {
computed, ref, watch, analyzeCidr, calculate,
useActiveValue: (getter, options) => useActiveValue(getter, { ...options, active }),
useRoute: () => route,
useRouter: () => ({ replace: value => { replacements.push(value); route.query = value.query; } }),
useI18n: () => ({ t: key => key }),
useMainStore: () => ({ allIPs: [] }),
};
const setup = new Function(...Object.keys(bindings), `${script}\nreturn { query, result, picked, onPrefix, onPrefixCommit };`);
const component = setup(...Object.values(bindings));
return { ...component, route, replacements };
};
for (const [input, prefix, expected] of [
['192.168.1.130/26', 24, '192.168.1.130/24'],
['2001:0db8:0000::1234/48', 64, '2001:db8::1234/64'],
['0x7f000001', 8, '127.0.0.1/8'],
['192.0.2.1', 0, '192.0.2.1/0'],
['192.0.2.1/24', 32, '192.0.2.1/32'],
['2001:db8::1/64', 128, '2001:db8::1/128'],
]) {
test(`prefix commit preserves the host in the share URL: ${input}`, () => {
const run = fixture(input);
const split = run.result.value.analysis.split;
run.picked.value = input;
run.onPrefix(prefix);
assert.equal(run.query.value, input, 'dragging leaves the input and URL untouched');
assert.equal(run.replacements.length, 0);
assert.equal(run.result.value.analysis.split, split, 'dragging preserves the subnet list');
run.onPrefixCommit(prefix);
assert.equal(run.query.value, expected);
assert.deepEqual(run.route.query, { q: expected, hl: 'en' });
assert.equal(run.replacements.length, 1);
assert.equal(run.picked.value, '');
assert.deepEqual(calculate(run.route.query.q).analysis.cidr, run.result.value.analysis.cidr);
assert.equal(run.result.value.analysis.split, run.result.value.analysis.cidr);
run.onPrefixCommit(prefix);
assert.equal(run.replacements.length, 1, 'an unchanged prefix does not replace the URL again');
});
}
test('a prefix commit without an address result leaves the URL alone', () => {
for (const input of ['invalid', '10.0.0.1-10.0.0.10']) {
const run = fixture(input);
run.onPrefixCommit(24);
assert.equal(run.query.value, input);
assert.equal(run.replacements.length, 0);
}
});
test('?q= runs on mount, follows the route while active, and is ignored while cached away', async () => {
const active = ref(true);
const run = fixture('10.0.0.0/22', active);
assert.equal(run.query.value, '10.0.0.0/22');
assert.equal(run.result.value.analysis.cidr.prefix, 22);
// The route changes before this page is deactivated; the path tells them apart.
run.route.path = '/tools/asn';
run.route.query = { q: 'AS13335' };
await nextTick();
assert.equal(run.query.value, '10.0.0.0/22', 'another path\'s ?q= is ignored');
active.value = false;
run.route.query = { q: 'AS15169' };
await nextTick();
assert.equal(run.query.value, '10.0.0.0/22', 'a hidden tool ignores the route');
assert.equal(run.result.value.analysis.cidr.prefix, 22);
run.route.path = '/tools/ipcalculator';
run.route.query = { q: '10.0.0.0/22' };
run.query.value = '192.0.2.';
active.value = true;
await nextTick();
assert.equal(run.query.value, '192.0.2.', 'an unchanged ?q= does not overwrite the input');
active.value = false;
run.route.query = { q: '192.0.2.0/24' };
active.value = true;
await nextTick();
assert.equal(run.query.value, '192.0.2.0/24');
assert.equal(run.result.value.analysis.cidr.prefix, 24);
assert.equal(run.replacements.length, 0, 'reading ?q= never writes it back');
});