Files
MyIP/tests/composable-route-active.test.js
T
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

82 lines
3.1 KiB
JavaScript

// use-route-active.js: activation itself needs a mounted tree; here the
// outside-a-component fallback and useActiveValue with an injected `active`.
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { effectScope, nextTick, ref, watch } from 'vue';
import { useActiveValue, useRouteActive } from '../frontend/composables/use-route-active.js';
describe('useRouteActive()', () => {
it('is always active outside a component instance', () => {
const scope = effectScope();
const active = scope.run(() => useRouteActive());
assert.equal(active.value, true);
scope.stop();
});
});
describe('useActiveValue()', () => {
it('follows the getter while active', () => {
const source = ref('a');
const value = useActiveValue(() => source.value, { active: ref(true) });
assert.equal(value.value, 'a');
source.value = 'b';
assert.equal(value.value, 'b');
});
it('holds the last active value while inactive', () => {
const source = ref('a');
const active = ref(true);
const value = useActiveValue(() => source.value, { active });
assert.equal(value.value, 'a');
active.value = false;
source.value = 'b';
assert.equal(value.value, 'a');
active.value = true;
assert.equal(value.value, 'b', 'catches up on activation');
});
it('starts undefined when created inactive', () => {
const value = useActiveValue(() => 'a', { active: ref(false) });
assert.equal(value.value, undefined);
});
it('holds once the path leaves the one it was created on, even while still active', () => {
const route = ref({ path: '/tools/asn', q: 'AS1' });
const value = useActiveValue(() => route.value.q, { pathOf: () => route.value.path, active: ref(true) });
assert.equal(value.value, 'AS1');
route.value = { path: '/tools/ipcalculator', q: '10.0.0.0/22' };
assert.equal(value.value, 'AS1', 'the next page\'s query never leaks in');
route.value = { path: '/tools/asn', q: 'AS2' };
assert.equal(value.value, 'AS2');
});
it('a watcher fires on activation only if the value changed while hidden', async () => {
const source = ref('x');
const active = ref(true);
const seen = [];
const scope = effectScope();
scope.run(() => {
watch(useActiveValue(() => source.value, { active }), (v) => seen.push(v), { immediate: true });
});
assert.deepEqual(seen, ['x']);
active.value = false;
source.value = 'y';
await nextTick();
source.value = 'x';
active.value = true;
await nextTick();
assert.deepEqual(seen, ['x'], 'away and back to the same value: no re-run');
active.value = false;
source.value = 'z';
await nextTick();
assert.deepEqual(seen, ['x'], 'changes while hidden are not seen');
active.value = true;
await nextTick();
assert.deepEqual(seen, ['x', 'z']);
scope.stop();
});
});