mirror of
https://github.com/jason5ng32/MyIP.git
synced 2026-11-04 08:55:47 +00:00
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
99 lines
4.5 KiB
JavaScript
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');
|
|
});
|