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) => {