From d93e35b991c1ec7d5a5357b20ca6fe8abc36aca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87A=C4=9EAN=20TURGUT?= Date: Tue, 20 Oct 2026 01:35:51 +0300 Subject: [PATCH] Self-test: scan a populated panel for translation and contrast (#777) The self-test's settings have no saved scenes, preview cells or sourceless slots, so the untranslated-text scan and the contrast audit never drew those screens (the selected scene row at 3.9:1 in Light was found by hand, #775). The panel now gets, in memory: four saved scenes (white, black, gradient backgrounds, and a no-op one applied as the selected row), clip slots referencing them, a white image, white and black colour slots and a slot without a source, written to the visible deck and grown to at least 3x4 (an earlier step leaves 2x2). Folds are opened in both scans; the theme step re-selects the scene after each theme pick (any change clears the highlight), counts what it scanned and fails if the selected row, four preview cells or the sourceless slot were missing. Content is restored afterwards. Verified: --smoke with an isolated profile PASS; ~3,040 texts per theme, none below threshold; the first run caught the 2x2 deck. Tests 3230. Co-authored-by: caganturgut <271973834+caganturgut@users.noreply.github.com> --- CHANGELOG.md | 14 +++++++ README.md | 4 +- README.tr.md | 4 +- ROADMAP.md | 2 +- src/main/main.js | 87 ++++++++++++++++++++++++++++++++++++++++- tests/ui-themes.test.js | 25 ++++++++++++ 6 files changed, 129 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97f9e91..1a92420 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,20 @@ updater sees v3.1.5 as the latest release and does not offer it to the beta as a - **The selected scene's info line** was 3.9:1 in Light and 4.1–4.4:1 in Midnight and CaYaDev. On the selected row's accent tint it now uses the secondary text colour, and the contrast rules cover body and secondary text on selected rows. +- **The self-test scans a populated panel [777].** + - The self-test's settings had no saved scenes, no clip cells with a preview and no slot without + a source. So the untranslated-text scan and the contrast audit never drew those screens. The + selected scene's 3.9:1 label above was found by hand. + - The self-test now adds representative content to the panel in memory: + - three saved scenes (white, black and gradient backgrounds) and a selected one that changes + nothing; + - clip slots that reference them; + - a white image, white and black colour slots, and a slot with no source. + - It also opens every fold, and puts the panel back afterwards. + - It counts what it actually scanned and fails if the selected row, four preview cells or the + sourceless slot were not drawn. On the first run they were not: an earlier step leaves the + deck at 2×2. + - Each theme now measures about 3,040 texts, with none below the threshold. - **Two labels were unreadable in every theme.** The empty side of the A/B crossfader and the "built-in" tag in Studio's list were dimmed with opacity to 2.9:1 and 3.7:1. They now use the secondary text colour. The decorative "·" separators in status lines are hidden from screen diff --git a/README.md b/README.md index b6ff7ed..832f0b2 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ layers and **40 GPU effects**, an **OBS overlay**, **Spout / Syphon**, **project [![Downloads](https://img.shields.io/github/downloads/CaYatur/SoundVisualizer/total?label=downloads)](https://github.com/CaYatur/SoundVisualizer/releases) [![License: MIT](https://img.shields.io/badge/License-MIT-e11d2a.svg)](LICENSE) [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-111997.svg)](#download) -[![Tests](https://img.shields.io/badge/tests-3229%20passing-2ea043.svg)](#tests) +[![Tests](https://img.shields.io/badge/tests-3230%20passing-2ea043.svg)](#tests) [![Electron](https://img.shields.io/badge/Electron-43-47848F.svg)](https://www.electronjs.org/) Download for Windows @@ -1746,7 +1746,7 @@ npm test npm start -- --smoke ``` -**3229 unit tests, all passing.** They are written to check answers, not to exercise lines: +**3230 unit tests, all passing.** They are written to check answers, not to exercise lines: - **Formulas** are checked against values derived by hand from their definitions — Viviani's curve staying on its sphere, the torus tube radius, Chladni's m↔n antisymmetry, every attractor staying diff --git a/README.tr.md b/README.tr.md index 7bcf4ef..b5ca035 100644 --- a/README.tr.md +++ b/README.tr.md @@ -18,7 +18,7 @@ destesi**, ve **MCP üzerinden yapay zekâ kontrolü**. [![İndirme](https://img.shields.io/github/downloads/CaYatur/SoundVisualizer/total?label=indirme)](https://github.com/CaYatur/SoundVisualizer/releases) [![Lisans: MIT](https://img.shields.io/badge/Lisans-MIT-e11d2a.svg)](LICENSE) [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-111997.svg)](#hızlı-başlangıç) -[![Test](https://img.shields.io/badge/test-3229%20geçiyor-2ea043.svg)](#testler) +[![Test](https://img.shields.io/badge/test-3230%20geçiyor-2ea043.svg)](#testler) [![Electron](https://img.shields.io/badge/Electron-43-47848F.svg)](https://www.electronjs.org/) Windows için indir @@ -1749,7 +1749,7 @@ npm test npm start -- --smoke ``` -**3229 birim testi, hepsi geçiyor.** Satır çalıştırmak için değil, cevap denetlemek için yazıldılar: +**3230 birim testi, hepsi geçiyor.** Satır çalıştırmak için değil, cevap denetlemek için yazıldılar: - **Formüller**, tanımlarından elle türetilmiş değerlerle sınanıyor — Viviani eğrisinin küre üzerinde kalması, simidin boru yarıçapı, Chladni'nin m↔n antisimetrisi, her çekicinin sınırlı kalması ve görüş diff --git a/ROADMAP.md b/ROADMAP.md index ce513ff..3960a35 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -133,7 +133,7 @@ npm test npm start -- --smoke ``` -- **3229 unit tests, all passing** on `main`. 703 of those shipped in v3.1.0; +- **3230 unit tests, all passing** on `main`. 703 of those shipped in v3.1.0; 105 came with v3.1.1; 163 came with v3.1.2; 157 came with v3.1.3 — 1128 at that tag — 469 more with v3.1.4, most of them from the MilkDrop work — 1597 at that tag — 1402 more with v3.1.5 — 2999 at that tag — and 27 diff --git a/src/main/main.js b/src/main/main.js index 0f8b6d3..bb3a5ff 100644 --- a/src/main/main.js +++ b/src/main/main.js @@ -7070,6 +7070,55 @@ async function runSmoke() { if (opened !== 'açıldı') errors.push('i18n scan could not open timeline/clipdeck: ' + opened); await wait(900); + /* DOLU PANEL (#777). Öz testin ayarlarında kayıtlı sahne, önizlemeli + klip hücresi ya da kaynağı seçilmemiş yuva yok; çeviri ve karşıtlık + taramaları o ekranları hiç görmüyordu (seçili sahnenin alt yazısı + Aydınlık'ta 3,9:1 kalmıştı, #775). Panelin kendi nesnesine temsilî + içerik eklenir, seçili sahne görüntüyü değiştirmeyen bir kopyadır; + tema adımından sonra eski hâline döner. Disk zaten korunuyor. */ + const populated = await awc3.executeJavaScript(`(function(){ + var P = window.SVPanel; if (!P) return { error: 'panel yok' }; + var c = P.cfg(); + // Önceki adımlar başka bir desteyi etkin bırakabiliyor: görünen deste + var decks = (c.clipdeck && c.clipdeck.decks) || []; + var deck = decks.filter(function (d) { return d && d.id === c.clipdeck.activeDeck; })[0] || decks[0]; + if (!deck) return { error: 'deste yok' }; + window.__smokePopulateDeck = deck.id; + window.__smokePopulateBackup = JSON.stringify({ scenes: c.scenes || [], slots: deck.slots || [], rows: deck.rows, cols: deck.cols, active: c._activeSceneId || null }); + // Önceki deste adımı ızgarayı 2x2 bırakıyor; yuvalar ızgaranın içinde kalmalı + deck.rows = Math.max(+deck.rows || 0, 3); + deck.cols = Math.max(+deck.cols || 0, 4); + var same = JSON.parse(JSON.stringify(c)); delete same.scenes; + var bgScene = function (id, name, bg) { + var d = JSON.parse(JSON.stringify(same)); + d.background = Object.assign({}, d.background, bg); + return { id: id, name: name, data: d }; + }; + c.scenes = (c.scenes || []).concat([ + { id: 'smoke-same', name: 'Smoke current', data: same }, + bgScene('smoke-white', 'Smoke white', { type: 'solid', solidColor: '#ffffff' }), + bgScene('smoke-black', 'Smoke black', { type: 'solid', solidColor: '#000000' }), + bgScene('smoke-grad', 'Smoke gradient', { type: 'gradient', gradient: { colors: ['#ffffff', '#ffe066', '#00ffff'] } }), + ]); + var img = 'data:image/svg+xml,' + encodeURIComponent(''); + deck.slots = [ + { row: 0, col: 0, name: 'White scene', type: 'scene', ref: 'smoke-white' }, + { row: 0, col: 1, name: 'Black scene', type: 'scene', ref: 'smoke-black' }, + { row: 0, col: 2, name: 'Gradient scene', type: 'scene', ref: 'smoke-grad' }, + { row: 0, col: 3, name: 'White image', type: 'image', ref: img }, + { row: 1, col: 0, name: 'White slot', type: 'palette', ref: 'x', color: '#ffffff' }, + { row: 1, col: 1, name: 'Black slot', type: 'palette', ref: 'x', color: '#000000' }, + { row: 1, col: 2, name: 'No source', type: 'video', ref: '' }, + ]; + P.applyScene('smoke-same'); + P.rerender(); + return { scenes: c.scenes.length, slots: deck.slots.length }; + })()`); + console.log('[SMOKE] dolu panel: ' + JSON.stringify(populated)); + if (!populated || populated.error) errors.push('populated panel: ' + ((populated && populated.error) || 'no result')); + await wait(700); + const openFolds = "(function(){var h=document.querySelectorAll('#sections .fold-head:not(.open)');for(var i=0;i b.dataset.cat); const out = []; + // Dolu panelin ekranları gerçekten tarandı mı (#777) + const seen = { active: false, thumbs: 0, noref: 0, folds: 0 }; for (const t of UT.THEMES) { click('[data-cat="settings"]'); await sleep(450); if (!click('.ui-theme-opt[data-theme="' + t.id + '"]')) { out.push({ theme: t.id, error: 'not in the picker' }); continue; } await sleep(250); + /* Her ayar değişikliği seçili sahne vurgusunu kaldırıyor (bilerek): + dolu panelin seçili satırı her temada yeniden seçilir. */ + if (window.__smokePopulateBackup && window.SVPanel) { window.SVPanel.applyScene('smoke-same'); await sleep(250); } let checked = 0, min = Infinity; const fails = []; for (const c of cats) { click('[data-cat="' + c + '"]'); await sleep(400); + // Katlanır bölümlerin içeriği kapalıyken DOM'da yok + const folds = document.querySelectorAll('#sections .fold-head:not(.open)'); + folds.forEach((h) => h.click()); + if (folds.length) { seen.folds += folds.length; await sleep(250); } + if (document.querySelector('#sceneList .scene-item.active')) seen.active = true; + seen.thumbs = Math.max(seen.thumbs, document.querySelectorAll('.cd-cell > .cd-thumb').length); + seen.noref = Math.max(seen.noref, document.querySelectorAll('.cd-cell.noref').length); const s = document.getElementById('sections'); - const steps = Math.min(8, Math.ceil(s.scrollHeight / Math.max(1, s.clientHeight))); + const steps = Math.min(14, Math.ceil(s.scrollHeight / Math.max(1, s.clientHeight))); for (let k = 0; k < steps; k++) { s.scrollTop = k * s.clientHeight; await sleep(100); const r = A.audit(document, window, { pageBg: getComputedStyle(document.body).backgroundColor, aaa: !!U.current().aaa }); @@ -7188,7 +7251,7 @@ async function runSmoke() { click('[data-cat="settings"]'); await sleep(350); click('.ui-theme-opt[data-theme="' + original + '"]'); await sleep(200); - return { themes: out, restored: U.choice() === original }; + return { themes: out, restored: U.choice() === original, seen }; })()`); if (!report || report.error) { errors.push('ui themes: ' + ((report && report.error) || 'no report')); @@ -7202,10 +7265,30 @@ async function runSmoke() { if (r.failCount) errors.push('ui theme ' + r.theme + ': ' + r.failCount + ' text element(s) below the contrast threshold'); }); if (!report.restored) errors.push('ui themes: the user\'s theme was not restored'); + console.log('[SMOKE] dolu panel tarandı: ' + JSON.stringify(report.seen)); + const sn = report.seen || {}; + if (!sn.active || sn.thumbs < 4 || sn.noref < 1) { + errors.push('ui themes: the populated panel was not scanned (' + JSON.stringify(sn) + ')'); + } } } catch (e) { errors.push('ui themes: ' + (e && e.message ? e.message : e)); } + // Dolu panel eski hâline (sahneler, yuvalar, seçili sahne) + try { + await awc4.executeJavaScript(`(function(){ + var raw = window.__smokePopulateBackup; if (!raw || !window.SVPanel) return false; + var b = JSON.parse(raw), c = window.SVPanel.cfg(); + c.scenes = b.scenes; + var d = ((c.clipdeck && c.clipdeck.decks) || []).filter(function (x) { return x && x.id === window.__smokePopulateDeck; })[0]; + if (d) { d.slots = b.slots; d.rows = b.rows; d.cols = b.cols; } + delete window.__smokePopulateDeck; + c._activeSceneId = b.active; + delete window.__smokePopulateBackup; + window.SVPanel.apply(); + return true; + })()`); + } catch (e) { /* panel kapanıyor */ } } /* --- Panel ekran görüntüleri (--smoke-shots) --- diff --git a/tests/ui-themes.test.js b/tests/ui-themes.test.js index 447dad1..faee184 100644 --- a/tests/ui-themes.test.js +++ b/tests/ui-themes.test.js @@ -560,6 +560,31 @@ test('öz test her temayı panelin seçicisinden tarıyor ve kullanıcının tem assert.match(step, /restored/); }); +/* Dolu panel (#777): öz testin ayarlarında kayıtlı sahne ve önizlemeli + hücre yoktu; tarama o ekranları hiç görmüyordu. İçerik ekleniyor, gerçekten + tarandığı sayılıyor ve sonra geri alınıyor. */ +test('öz test çeviri ve karşıtlık taramalarını dolu panelde yapıyor', () => { + const m = read('src/main/main.js'); + const p = m.indexOf('DOLU PANEL (#777)'); + assert.ok(p > 0, 'dolu panel adımı'); + const pop = m.slice(p, p + 4000); + ['smoke-same', 'smoke-white', 'smoke-black', 'smoke-grad'].forEach((id) => assert.ok(pop.includes(id), id)); + assert.match(pop, /type: 'image', ref: img/); + assert.match(pop, /ref: ''/, 'kaynağı seçilmemiş yuva'); + assert.match(pop, /P\.applyScene\('smoke-same'\)/, 'seçili sahne satırı'); + assert.match(pop, /c\.clipdeck\.activeDeck/, 'görünen desteye yazılıyor'); + assert.match(pop, /deck\.cols = Math\.max/, 'yuvalar ızgaranın içinde'); + // Çeviri taraması katlanır bölümleri açıyor + assert.match(m, /if \(await awc3\.executeJavaScript\(openFolds\)\)/); + // Karşıtlık taraması kapsamı sayıyor ve eksikse düşüyor + const t = m.indexOf('Arayüz temaları: ekrandaki yazıların karşıtlığı (#770)'); + const step = m.slice(t, t + 7000); + assert.match(step, /\.fold-head:not\(\.open\)/); + assert.match(step, /window\.SVPanel\.applyScene\('smoke-same'\)/); + assert.match(step, /!sn\.active \|\| sn\.thumbs < 4 \|\| sn\.noref < 1/); + assert.match(step, /__smokePopulateBackup/, 'içerik geri alınıyor'); +}); + test('süs ayraçları ekran okuyucudan ve ölçümden gizli', () => { const dir = path.join(root, 'src', 'admin'); fs.readdirSync(dir).filter((f) => f.endsWith('.js')).forEach((f) => {