// Regression test for feedBack#800: tuner injectPlayerButton() must anchor the // injected button to a DIRECT-child button of #player-controls. The old // `controls.querySelector('button:last-child')` could resolve to a NESTED // button, and `controls.insertBefore(btn, nestedButton)` then throws // NotFoundError — which propagated out of the player-screen transition and // aborted its render. // // Same isolation strategy as the core tests/js suite: extract the real function // from source with extractFunction() and run it in a vm sandbox over a small // but faithful DOM model. The model's insertBefore() enforces the real DOM // invariant (reference node must be a direct child, else NotFoundError), and // querySelector() implements the exact semantics of both the old // (`button:last-child`) and new (`:scope > button:last-of-type`) selectors — so // reverting the fix makes this test throw. const { test } = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const vm = require('node:vm'); const { extractFunction } = require('../../../js/test_utils'); const UI_JS = path.join(__dirname, '..', '..', '..', '..', 'plugins', 'tuner', 'utils', 'ui.js'); const SRC = fs.readFileSync(UI_JS, 'utf8'); const FN_SRC = extractFunction(SRC, 'function injectPlayerButton('); // ── Minimal, faithful DOM model ────────────────────────────────────────────── class El { constructor(tag, id = '') { this.tagName = tag.toUpperCase(); this.id = id; this.children = []; this.parentNode = null; this.textContent = ''; this.title = ''; this.onclick = null; } appendChild(node) { node.parentNode = this; this.children.push(node); return node; } insertBefore(node, ref) { const idx = this.children.indexOf(ref); if (ref == null || idx === -1) { // Faithful to the browser: ref must be a direct child. const e = new Error( "Failed to execute 'insertBefore' on 'Node': The node before which the " + 'new node is to be inserted is not a child of this node.' ); e.name = 'NotFoundError'; throw e; } node.parentNode = this; this.children.splice(idx, 0, node); return node; } querySelector(sel) { if (sel === ':scope > button:last-of-type') { // Last direct-child