diff --git a/internal/check-view-address.mjs b/internal/check-view-address.mjs index 65a4b994b..029984700 100644 --- a/internal/check-view-address.mjs +++ b/internal/check-view-address.mjs @@ -300,6 +300,15 @@ async function arms({ va, sv, counts, pw, src }, { skipF = false } = {}) { if (b.at() !== '/?tab=posterverse__posterverse' || b.writes.push !== pushes + 1) fails.push(`H: the owner's tab change did not drop every subtab segment: ${b.at()}`) off() } + { + // a tab claim that is refused (a hole) must not take the rest of its announcement with it + const b = browser('https://x.test/?tab=doxa-maps-spa__doxa-maps-spa') + const el = new EventTarget() + const off = va.ownAddress(el, () => 'app') + el.dispatchEvent(new CustomEvent(va.VIEW_EVENT, { bubbles: true, composed: true, detail: { changes: { tab: 'x', place: 'benin' }, push: false, depth: 3 } })) + if (b.q().get('place') !== 'benin' || b.q().get('subtab')) fails.push(`H: a refused tab claim dropped the pane/place announced with it: ${b.at()}`) + off() + } // ── F. pane-wrapper's address, on a simulated column (the slow arm: it waits out the scroll debounce) ── globalThis.document = { createElement: () => new FakeEl() } @@ -527,8 +536,9 @@ if (process.argv.includes('--self-test')) { // P2b: nested shells at any depth ['a nested shell\'s tab dropped (the P2 behaviour)', { ...real, va: await vaVariant(' if (tab) {', ' if (false) {') }, /^H: a depth-1 first-paint claim/], ['a depth-2 claim that ignores its depth (overwrites segment 1)', { ...real, va: await vaVariant('path.slice(0, depth - 1).concat(tab)', '[tab]') }, /^H: depth 2 is not the second segment|^H: a depth-2 claim with no depth-1/], - ['a hole that is not refused', { ...real, va: await vaVariant('if (path.length < depth - 1 || path[depth - 1] === tab) return', 'if (path[depth - 1] === tab) return') }, /^H: a depth-2 claim with no depth-1 segment/], - ['the same tab claimed again is not a no-op', { ...real, va: await vaVariant('if (path.length < depth - 1 || path[depth - 1] === tab) return', 'if (path.length < depth - 1) return') }, /^H: the same tab claimed again dropped deeper/], + ['a hole that is not refused', { ...real, va: await vaVariant('if (path.length >= depth - 1 && path[depth - 1] !== tab)', 'if (path[depth - 1] !== tab)') }, /^H: a depth-2 claim with no depth-1 segment/], + ['a refused tab claim that drops the rest of its announcement', { ...real, va: await vaVariant('if (path.length >= depth - 1 && path[depth - 1] !== tab) changes.subtab', 'if (path.length < depth - 1 || path[depth - 1] === tab) return; changes.subtab') }, /^H: a refused tab claim dropped/], + ['the same tab claimed again is not a no-op', { ...real, va: await vaVariant('if (path.length >= depth - 1 && path[depth - 1] !== tab)', 'if (path.length >= depth - 1)') }, /^H: the same tab claimed again dropped deeper/], ['a subtab change that keeps pane and place', { ...real, va: await vaVariant('const LEVEL = { tab: 0, subtab: 1, pane: 2, place: 2 }', 'const LEVEL = { tab: 0, subtab: 2, pane: 2, place: 2 }') }, /^A: a subtab change drops pane and place|^H: depth 2 is not the second segment, or the depth-1 change kept/], ["an owner's tab change that keeps subtab", { ...real, va: await vaVariant('const LEVEL = { tab: 0, subtab: 1, pane: 2, place: 2 }', 'const LEVEL = { tab: 0, subtab: 0, pane: 2, place: 2 }') }, /^A: a tab change drops subtab|^H: the owner's tab change did not drop/], ['a subtab path longer than 6', { ...real, va: await vaVariant('return p.length <= MAX_DEPTH && p.every', 'return p.length <= 99 && p.every') }, /^A: a subtab path reads segment by segment/], diff --git a/library/composables/useViewAddress.js b/library/composables/useViewAddress.js index cc266c1ae..949f39f92 100644 --- a/library/composables/useViewAddress.js +++ b/library/composables/useViewAddress.js @@ -151,8 +151,7 @@ export function ownAddress(el, getOwner) { if (tab) { const depth = Math.min(MAX_DEPTH, Math.max(1, Math.floor(e.detail.depth) || 1)) const path = readViewKeys(location.search).subtab.split('.').filter(Boolean) - if (path.length < depth - 1 || path[depth - 1] === tab) return - changes.subtab = path.slice(0, depth - 1).concat(tab).join('.') + if (path.length >= depth - 1 && path[depth - 1] !== tab) changes.subtab = path.slice(0, depth - 1).concat(tab).join('.') } if (Object.keys(changes).length) writeViewKeys(changes, { owner, el, push: e.detail.push !== false }) }