mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 11:19:24 +00:00
* Update GitHub repo references from feedback* to feedBack* * rename: slopsmith -> feedBack, byron -> got-feedBack Renames across the entire codebase: - slopsmith/Slopsmith/SLOPSMITH/SlopSmith -> feedBack/FeedBack/FEEDBACK/FeedBack - byron/Byron/Byrongamatos -> got-feedBack/got-feedBack/got-feedBack - /home/byron/ -> /opt/got-feedBack/ - byron@ougsoft.com -> hi@got-feedBack.org - github.com/byrongamatos/ -> github.com/got-feedback/ - com.byron. -> com.got-feedback. - SLOPSMITH_ env vars -> FEEDBACK_ with backward-compat fallback - Protocol/storage strings migrated with read-old/write-new pattern - window.slopsmith JS API -> window.feedBack (canonical) + backward-compat alias Refs: #rename-slopsmith * rename: complete regen against current main + fix backward-compat alias Regenerated the slopsmith->feedBack / byron->got-feedBack rename on top of current main (3 commits had landed since the branch: #572/#554/#574), resolving the four content conflicts in favour of main's newer content (autoplay/auto-exit, accuracy-badge, Virtuoso re-home, feedpak badge). Completion fixes on top of the mechanical rename: - Re-apply rename to post-branch content the original rename never saw: window.slopsmith(.Tour) consumers in lessons.js / notifications.js / onboarding-tour.js, and the matching JS + python tests (autoplay_exit, progression_*, test_feedpak_extension FEEDBACK_* env vars). The test env vars now match server.py (which reads FEEDBACK_SYNC_STARTUP / FEEDBACK_SKIP_STARTUP_TASKS), so the sync-startup test exercises the real path again. - Restore the window.slopsmith backward-compat alias dropped during conflict resolution, and move the bus aliases to AFTER the _feedBackExisting merge block so they reference the fully-assembled object (also fixes the loop_api.test.js API-surface regex, which the original PR latently broke). - Drop the stray empty data/web_library.db (runtime DB lives in CONFIG_DIR) and gitignore it. - Fix stale tone-source test: feed[dB]ack -> fee[dB]ack to match shipped source labels. Verified locally (org CI billing-blocked): JS 819/819 pass; pytest 1669 passed / 1683 collected with 0 import errors; zero residual slopsmith/byron except the two intentional window.slopsmith aliases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * rename: implement advertised backward-compat + prune dead community plugins Address gaps where PR #537's "Backward compatibility" section was advertised but not implemented, and clean up the community plugin list. Env vars (FEEDBACK_* canonical, legacy SLOPSMITH_* honoured): - New lib/env_compat.py (getenv_compat / env_flag_compat) + tests. server.py (_env_flag + all FEEDBACK_* reads), diagnostics_hardware, gp2midi and tailwind_rebuild now resolve the legacy alias, so existing SLOPSMITH_UI / SLOPSMITH_PLUGINS_DIR / etc. deployments keep working. - Fix the rename collapsing plugins/__init__.py and minigames/routes.py from `FEEDBACK_PLUGINS_DIR or SLOPSMITH_PLUGINS_DIR` into a redundant `FEEDBACK_ or FEEDBACK_` (the fallback was silently lost). Storage (app.js update-channel): - Read feedBack-update-channel, fall back to legacy slopsmith-update-channel, and clear the legacy key on write — so a user's update-channel preference survives the rename instead of resetting to "stable". Community plugin list (README): the rename rewrote third-party repo URLs we don't own. Probed every one; their owners never renamed, so: - Restore the 13 live community plugins to their real slopsmith-* names. - Prune 6 that are 404 to the public (topkoa splitscreen/stems, OmikronApex tuner, Jafz2001 nam-rig-builder, DeathlySin song-preview, Erikcb91 shuffle). - Fix a pre-existing Guitar Theory clone-command typo (nam-tone -> guitar-theory). Verified: env_compat 7/7, JS 819/819, pytest 1690 collected / 0 import errors, rename-sensitive + startup suites green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: byrongamatos <xasiklas@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
387 lines
17 KiB
JavaScript
387 lines
17 KiB
JavaScript
'use strict';
|
|
|
|
const { test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
|
|
const venueScene = require('../../static/v3/venue-scene-3d.js');
|
|
const venueViz = require('../../static/v3/venue-viz.js');
|
|
const pov = require('../../static/v3/venue-instrument-pov.js');
|
|
const APP_JS = path.join(__dirname, '..', '..', 'static', 'app.js');
|
|
const H3D_JS = path.join(__dirname, '..', '..', 'plugins', 'highway_3d', 'screen.js');
|
|
const INDEX_HTML = path.join(__dirname, '..', '..', 'static', 'v3', 'index.html');
|
|
const ASSET_DIR = path.join(__dirname, '..', '..', 'static', 'assets', 'venue', 'themes', 'small-club');
|
|
|
|
test('small-club venue scene asset files exist', () => {
|
|
assert.ok(fs.existsSync(path.join(ASSET_DIR, 'manifest.json')));
|
|
assert.ok(fs.existsSync(path.join(ASSET_DIR, 'bg-plate.png')));
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(ASSET_DIR, 'manifest.json'), 'utf8'));
|
|
assert.equal(manifest.id, 'small-club');
|
|
assert.equal(manifest.name, 'Small Club');
|
|
assert.equal(manifest.type, 'generated-original');
|
|
assert.equal(manifest.version, 10);
|
|
assert.equal(manifest.assets.bgPlate, 'bg-plate.webp');
|
|
assert.equal(manifest.assets.fallbackBgPlate, 'bg-plate.png');
|
|
assert.equal(manifest.instrumentPlates.guitar.png, 'guitar-pov-bg.png');
|
|
assert.equal(manifest.instrumentPlates.bass.webp, 'bass-pov-bg.webp');
|
|
assert.equal(manifest.instrumentPlates.drums.png, 'drums-pov-bg.png');
|
|
assert.equal(manifest.instrumentPlates.piano.png, 'piano-pov-bg.png');
|
|
assert.equal(manifest.instrumentPlates.vocals.png, 'vocals-pov-bg.png');
|
|
assert.equal(manifest.instrumentPlates.vocals.webp, 'vocals-pov-bg.webp');
|
|
for (const optionalVocals of ['vocals-pov-bg.png', 'vocals-pov-bg.webp']) {
|
|
const vocalsPath = path.join(ASSET_DIR, optionalVocals);
|
|
if (fs.existsSync(vocalsPath)) {
|
|
const st = fs.statSync(vocalsPath);
|
|
assert.ok(st.isFile() && st.size > 0, `${optionalVocals} must be a non-empty file when installed`);
|
|
}
|
|
}
|
|
});
|
|
|
|
test('crowd SVG uses connected head-and-shoulder silhouettes', () => {
|
|
const svg = fs.readFileSync(path.join(ASSET_DIR, 'crowd-silhouette.svg'), 'utf8');
|
|
assert.match(svg, /crowd-silhouette/);
|
|
assert.match(svg, /id="crowd-front-row"/);
|
|
assert.match(svg, /id="crowd-rear-row"/);
|
|
assert.match(svg, /class="crowd-hand"/);
|
|
assert.doesNotMatch(svg, /<ellipse[^>]*rx=/);
|
|
assert.doesNotMatch(svg, /class="crowd-arm"/);
|
|
assert.doesNotMatch(svg, /class="crowd-person"/);
|
|
});
|
|
|
|
test('backdrop SVG includes club stage elements', () => {
|
|
const svg = fs.readFileSync(path.join(ASSET_DIR, 'venue-backdrop.svg'), 'utf8');
|
|
assert.match(svg, /id="stage-curtain"/);
|
|
assert.match(svg, /class="speaker-stack"/);
|
|
assert.match(svg, /id="stage-platform"/);
|
|
assert.match(svg, /id="lighting-truss"/);
|
|
assert.match(svg, /id="side-wall-left"/);
|
|
assert.match(svg, /id="side-wall-right"/);
|
|
});
|
|
|
|
test('stage lights SVG uses beam shapes not soft circles', () => {
|
|
const svg = fs.readFileSync(path.join(ASSET_DIR, 'stage-lights.svg'), 'utf8');
|
|
assert.match(svg, /spot-beam/);
|
|
assert.match(svg, /id="spot-beam-center"/);
|
|
assert.doesNotMatch(svg, /<ellipse[^>]*rx=/);
|
|
});
|
|
|
|
test('highway_3d venue plate chain falls back to generic bg-plate', () => {
|
|
const src = fs.readFileSync(H3D_JS, 'utf8');
|
|
const chainFn = src.match(/function _venuePlateUrlChain\(pov\)\s*\{[\s\S]*?\n\s*\}/);
|
|
assert.ok(chainFn, '_venuePlateUrlChain missing');
|
|
assert.match(chainFn[0], /plate\.webp/);
|
|
assert.match(chainFn[0], /plate\.png/);
|
|
assert.match(chainFn[0], /VENUE_BG_PLATE_WEBP/);
|
|
assert.match(chainFn[0], /VENUE_BG_PLATE_PNG/);
|
|
});
|
|
|
|
test('highway_3d venue style loads instrument POV plates with fallback', () => {
|
|
const src = fs.readFileSync(H3D_JS, 'utf8');
|
|
assert.match(src, /VENUE_SCENE_ASSET_BASE\s*=\s*'\/static\/assets\/venue\/themes\/small-club\/'/);
|
|
assert.match(src, /VENUE_INSTRUMENT_PLATES/);
|
|
assert.match(src, /guitar-pov-bg\.png/);
|
|
assert.match(src, /bass-pov-bg\.png/);
|
|
assert.match(src, /drums-pov-bg\.png/);
|
|
assert.match(src, /piano-pov-bg\.png/);
|
|
assert.match(src, /vocals-pov-bg\.png/);
|
|
assert.match(src, /karaoke|vocal|vocals/);
|
|
assert.match(src, /VENUE_BG_PLATE_PNG\s*=\s*'bg-plate\.png'/);
|
|
assert.match(src, /VENUE_BG_PLATE_WEBP\s*=\s*'bg-plate\.webp'/);
|
|
assert.match(src, /_venuePlateUrlChain/);
|
|
assert.match(src, /_venueLoadPlateForPov/);
|
|
assert.match(src, /_venueTextureCache/);
|
|
assert.match(src, /h3dVenueSceneSetInstrumentPov/);
|
|
assert.match(src, /venue:\s*\{/);
|
|
assert.match(src, /h3dVenueSceneSetActive/);
|
|
assert.match(src, /h3dVenueSceneSetMood/);
|
|
assert.match(src, /_bgEffectiveStyleId/);
|
|
const venueBlock = src.match(/venue:\s*\{[\s\S]*?teardown\(s\)\s*\{[\s\S]*?\},\s*\n\s*\},/);
|
|
assert.ok(venueBlock, 'venue style block missing');
|
|
assert.match(venueBlock[0], /_venueLoadPlateForPov/);
|
|
assert.doesNotMatch(venueBlock[0], /venue-backdrop\.svg/);
|
|
assert.match(src, /VENUE_HAZE_STEADY\s*=\s*0\.008/);
|
|
});
|
|
|
|
test('plain 3D image style does not load venue POV plate', () => {
|
|
const src = fs.readFileSync(H3D_JS, 'utf8');
|
|
const imageBlock = src.match(/image:\s*\{[\s\S]*?teardown\(s\)/);
|
|
assert.ok(imageBlock, 'image style block missing');
|
|
assert.doesNotMatch(imageBlock[0], /guitar-pov-bg/);
|
|
assert.doesNotMatch(imageBlock[0], /_venueLoadPlateForPov/);
|
|
});
|
|
|
|
test('venue-scene-3d syncs instrument POV from arrangement signal', () => {
|
|
global.h3dVenueSceneSetActive = () => {};
|
|
global.h3dVenueSceneSetMood = () => {};
|
|
global.h3dVenueSceneSetInstrumentPov = (input) => { global._venuePovInput = input; };
|
|
global.h3dVenueSceneGetState = () => ({});
|
|
global.highway = { getSongInfo: () => ({ arrangement: 'Bass' }) };
|
|
global.v3VenueViz = venueViz;
|
|
global.v3VenueInstrumentPov = pov;
|
|
global.feedBack = { on() {} };
|
|
try {
|
|
venueScene.activate();
|
|
assert.equal(global._venuePovInput, 'Bass');
|
|
assert.equal(venueScene.getState().instrumentPov, 'bass');
|
|
global.highway.getSongInfo = () => ({ arrangement: 'Drums' });
|
|
venueScene.syncInstrumentPov();
|
|
assert.equal(global._venuePovInput, 'Drums');
|
|
global.highway.getSongInfo = () => ({ arrangement: 'Vocals' });
|
|
venueScene.syncInstrumentPov();
|
|
assert.equal(global._venuePovInput, 'Vocals');
|
|
assert.equal(venueScene.getState().instrumentPov, 'vocals');
|
|
} finally {
|
|
venueScene.deactivate();
|
|
delete global.h3dVenueSceneSetActive;
|
|
delete global.h3dVenueSceneSetMood;
|
|
delete global.h3dVenueSceneSetInstrumentPov;
|
|
delete global.h3dVenueSceneGetState;
|
|
delete global.highway;
|
|
delete global.v3VenueViz;
|
|
delete global.v3VenueInstrumentPov;
|
|
delete global.feedBack;
|
|
delete global._venuePovInput;
|
|
}
|
|
});
|
|
|
|
test('lyrics visibility during guitar practice does not force vocals POV', () => {
|
|
global.h3dVenueSceneSetActive = () => {};
|
|
global.h3dVenueSceneSetMood = () => {};
|
|
global.h3dVenueSceneSetInstrumentPov = (input) => { global._venuePovInput = input; };
|
|
global.h3dVenueSceneGetState = () => ({});
|
|
global.highway = {
|
|
getSongInfo: () => ({ arrangement: 'Lead' }),
|
|
getLyricsVisible: () => true,
|
|
};
|
|
global.v3VenueViz = venueViz;
|
|
global.v3VenueInstrumentPov = pov;
|
|
global.feedBack = { on() {} };
|
|
try {
|
|
venueScene.activate();
|
|
assert.equal(global._venuePovInput, 'Lead');
|
|
assert.equal(venueScene.getState().instrumentPov, 'guitar');
|
|
assert.equal(pov.resolveVenueInstrumentPov(global._venuePovInput), 'guitar');
|
|
const src = fs.readFileSync(path.join(__dirname, '..', '..', 'static', 'v3', 'venue-scene-3d.js'), 'utf8');
|
|
const codeOnly = src.replace(/\/\/[^\n]*/g, '');
|
|
assert.doesNotMatch(codeOnly, /\.getLyricsVisible\s*\(/);
|
|
} finally {
|
|
venueScene.deactivate();
|
|
delete global.h3dVenueSceneSetActive;
|
|
delete global.h3dVenueSceneSetMood;
|
|
delete global.h3dVenueSceneSetInstrumentPov;
|
|
delete global.h3dVenueSceneGetState;
|
|
delete global.highway;
|
|
delete global.v3VenueViz;
|
|
delete global.v3VenueInstrumentPov;
|
|
delete global.feedBack;
|
|
delete global._venuePovInput;
|
|
}
|
|
});
|
|
|
|
test('venue-scene-3d exports bg plate asset ids', () => {
|
|
assert.equal(venueScene.BG_PLATE, 'bg-plate.png');
|
|
assert.equal(venueScene.BG_PLATE_WEBP, 'bg-plate.webp');
|
|
assert.equal(venueScene.ASSET_BASE, '/static/assets/venue/themes/small-club/');
|
|
});
|
|
|
|
test('app.js syncs venue 3D scene on viz changes', () => {
|
|
const src = fs.readFileSync(APP_JS, 'utf8');
|
|
assert.match(src, /v3VenueScene3d\.syncViz\('venue'\)/);
|
|
assert.match(src, /v3VenueScene3d\.syncViz\(id\)/);
|
|
});
|
|
|
|
test('index.html loads venue deps before venue-scene-3d', () => {
|
|
const html = fs.readFileSync(INDEX_HTML, 'utf8');
|
|
const povIdx = html.indexOf('venue-instrument-pov.js');
|
|
const vizIdx = html.indexOf('venue-viz.js');
|
|
const sceneIdx = html.indexOf('venue-scene-3d.js');
|
|
const moodIdx = html.indexOf('venue-mood-fx.js');
|
|
assert.ok(povIdx !== -1 && sceneIdx !== -1);
|
|
assert.ok(povIdx < sceneIdx);
|
|
// venue-scene-3d's boot reads window.v3VenueMoodFx.getMotion() synchronously,
|
|
// so venue-viz and venue-mood-fx must both load before it; otherwise first
|
|
// paint falls back to 'subtle' and ignores a saved 'off'/'full' motion pref.
|
|
assert.ok(vizIdx < moodIdx && moodIdx < sceneIdx);
|
|
});
|
|
|
|
test('syncViz activates only for venue visualization id', () => {
|
|
global.h3dVenueSceneSetActive = (on) => { global._h3dActive = on; };
|
|
global.h3dVenueSceneSetMood = (s) => { global._h3dMood = s; };
|
|
global.h3dVenueSceneSetInstrumentPov = () => {};
|
|
global.h3dVenueSceneGetState = () => ({ active: !!global._h3dActive, assetsLoaded: false, loadFailed: false });
|
|
global.v3VenueViz = venueViz;
|
|
global.v3VenueInstrumentPov = pov;
|
|
global.feedBack = { on() {} };
|
|
try {
|
|
venueScene.deactivate();
|
|
venueScene.syncViz('highway_3d');
|
|
assert.equal(global._h3dActive, false);
|
|
venueScene.syncViz('venue');
|
|
assert.equal(global._h3dActive, true);
|
|
assert.equal(venueScene.getState().active, true);
|
|
assert.equal(venueScene.getState().themeId, 'small-club');
|
|
} finally {
|
|
venueScene.deactivate();
|
|
delete global.h3dVenueSceneSetActive;
|
|
delete global.h3dVenueSceneSetMood;
|
|
delete global.h3dVenueSceneSetInstrumentPov;
|
|
delete global.h3dVenueSceneGetState;
|
|
delete global.v3VenueViz;
|
|
delete global.v3VenueInstrumentPov;
|
|
delete global.feedBack;
|
|
delete global._h3dActive;
|
|
delete global._h3dMood;
|
|
}
|
|
});
|
|
|
|
test('shouldShowDomPlaceholder stays false so badge is not shown', () => {
|
|
global.v3VenueViz = {
|
|
getSelectedVizId: () => 'venue',
|
|
isVenueVisualization: (v) => v === 'venue',
|
|
readVizSelection: () => 'venue',
|
|
};
|
|
try {
|
|
venueScene.syncViz('venue');
|
|
assert.equal(venueScene.shouldShowDomPlaceholder(), false);
|
|
venueScene.onAssetsLoaded();
|
|
assert.equal(venueScene.shouldShowDomPlaceholder(), false);
|
|
} finally {
|
|
venueScene.deactivate();
|
|
delete global.v3VenueViz;
|
|
}
|
|
});
|
|
|
|
test('live performance state forwards mood to highway venue scene API', () => {
|
|
global.h3dVenueSceneSetActive = () => {};
|
|
global.h3dVenueSceneSetMood = (s) => { global._mood = s; };
|
|
global.h3dVenueSceneSetInstrumentPov = () => {};
|
|
global.h3dVenueSceneGetState = () => ({});
|
|
global.v3VenueViz = {
|
|
getSelectedVizId: () => 'venue',
|
|
isVenueVisualization: () => true,
|
|
readVizSelection: () => 'venue',
|
|
};
|
|
global.feedBack = { on() {} };
|
|
try {
|
|
venueScene.activate();
|
|
venueScene.onPerformanceState({ detail: { state: 'fire' } });
|
|
assert.equal(global._mood, 'fire');
|
|
venueScene.onPerformanceState({ detail: { state: 'smoke' } });
|
|
assert.equal(global._mood, 'smoke');
|
|
} finally {
|
|
venueScene.deactivate();
|
|
delete global.h3dVenueSceneSetActive;
|
|
delete global.h3dVenueSceneSetMood;
|
|
delete global.h3dVenueSceneSetInstrumentPov;
|
|
delete global.h3dVenueSceneGetState;
|
|
delete global.v3VenueViz;
|
|
delete global.feedBack;
|
|
delete global._mood;
|
|
}
|
|
});
|
|
|
|
test('venue viz still maps renderer to highway_3d', () => {
|
|
assert.equal(venueViz.resolveRendererVizId('venue'), 'highway_3d');
|
|
assert.equal(venueViz.isVenueVisualization('highway_3d'), false);
|
|
});
|
|
|
|
test('STRIP_OVERLAY_ENABLED remains false in venue mood fx', () => {
|
|
const venue = require('../../static/v3/venue-mood-fx.js');
|
|
assert.equal(venue.STRIP_OVERLAY_ENABLED, false);
|
|
});
|
|
|
|
test('highway_3d venue style exposes motion APIs and background-only motion', () => {
|
|
const src = fs.readFileSync(H3D_JS, 'utf8');
|
|
assert.match(src, /h3dVenueSceneSetMotionMode/);
|
|
assert.match(src, /h3dVenueSceneGetState/);
|
|
assert.match(src, /motionMode/);
|
|
assert.match(src, /motionEffective/);
|
|
assert.match(src, /motionEnabled/);
|
|
assert.match(src, /motionIntensity/);
|
|
assert.match(src, /_venueApplyFakeDepthMotion/);
|
|
assert.match(src, /_venueEffectiveMotionMode/);
|
|
assert.match(src, /_venuePrefersReducedMotion/);
|
|
assert.match(src, /function _venueApplyFakeDepthMotion[\s\S]*?return motion;\s*\n\s*\}/);
|
|
assert.match(src, /function _venueApplyFakeDepthMotion[\s\S]*?s\.backdrop[\s\S]*?s\.haze/);
|
|
const venueUpdate = src.match(/venue:\s*\{[\s\S]*?update\(s, bands, dt, t\)\s*\{[\s\S]*?\},\s*\n\s*teardown/);
|
|
assert.ok(venueUpdate, 'venue update block missing');
|
|
assert.match(venueUpdate[0], /_venueApplyFakeDepthMotion/);
|
|
assert.doesNotMatch(venueUpdate[0], /STRIP_OVERLAY/);
|
|
});
|
|
|
|
test('off motion mode zeros profile and venue inactive forces effective off', () => {
|
|
const src = fs.readFileSync(H3D_JS, 'utf8');
|
|
assert.match(src, /function _venueMotionProfile[\s\S]*breathe:\s*0,\s*parallax:\s*0/);
|
|
assert.match(src, /function _venueEffectiveMotionMode\(\)[\s\S]*!_venueSceneOverride[\s\S]*return 'off'/);
|
|
});
|
|
|
|
test('venue-scene-3d syncs motion on activate', () => {
|
|
global.h3dVenueSceneSetActive = () => {};
|
|
global.h3dVenueSceneSetMood = () => {};
|
|
global.h3dVenueSceneSetInstrumentPov = () => {};
|
|
global.h3dVenueSceneSetMotionMode = (mode) => { global._venueMotion = mode; };
|
|
global.h3dVenueSceneGetState = () => ({});
|
|
global.v3VenueMoodFx = { getMotion: () => 'full' };
|
|
global.v3VenueViz = venueViz;
|
|
global.v3VenueInstrumentPov = pov;
|
|
global.feedBack = { on() {} };
|
|
try {
|
|
venueScene.activate();
|
|
assert.equal(global._venueMotion, 'full');
|
|
global.v3VenueMoodFx.getMotion = () => 'off';
|
|
venueScene.syncVenueMotion();
|
|
assert.equal(global._venueMotion, 'off');
|
|
} finally {
|
|
venueScene.deactivate();
|
|
delete global.h3dVenueSceneSetActive;
|
|
delete global.h3dVenueSceneSetMood;
|
|
delete global.h3dVenueSceneSetInstrumentPov;
|
|
delete global.h3dVenueSceneSetMotionMode;
|
|
delete global.h3dVenueSceneGetState;
|
|
delete global.v3VenueMoodFx;
|
|
delete global.v3VenueViz;
|
|
delete global.v3VenueInstrumentPov;
|
|
delete global.feedBack;
|
|
delete global._venueMotion;
|
|
}
|
|
});
|
|
|
|
test('full motion intensity is stronger than subtle in profile table', () => {
|
|
const venueMood = require('../../static/v3/venue-mood-fx.js');
|
|
assert.ok(venueMood.venueMotionIntensity('full') > venueMood.venueMotionIntensity('subtle'));
|
|
assert.equal(venueMood.venueMotionIntensity('off'), 0);
|
|
});
|
|
|
|
test('runtime safety: motion pass does not touch scoring detection timing or audio', () => {
|
|
const venueMood = require('../../static/v3/venue-mood-fx.js');
|
|
const forbidden = [
|
|
'plugins/note_detection',
|
|
'plugins/scoring',
|
|
'static/audio',
|
|
'AudioEngine',
|
|
'feedBack-desktop',
|
|
];
|
|
const allowedTouched = [
|
|
'plugins/highway_3d/screen.js',
|
|
'static/v3/venue-mood-fx.js',
|
|
'static/v3/venue-scene-3d.js',
|
|
'static/v3/index.html',
|
|
];
|
|
for (const rel of allowedTouched) {
|
|
assert.ok(fs.existsSync(path.join(__dirname, '..', '..', rel)), rel);
|
|
}
|
|
const h3d = fs.readFileSync(H3D_JS, 'utf8');
|
|
assert.doesNotMatch(h3d, /v3-venue-mode-badge.*remove\('hidden'\)/);
|
|
assert.equal(venueMood.STRIP_OVERLAY_ENABLED, false);
|
|
for (const token of forbidden) {
|
|
assert.doesNotMatch(h3d, new RegExp(token.replace(/\//g, '\\/'), 'i'));
|
|
}
|
|
});
|
|
|
|
test('controls z-index remains above venue scene wash in CSS', () => {
|
|
const css = fs.readFileSync(path.join(__dirname, '..', '..', 'static', 'v3', 'v3.css'), 'utf8');
|
|
assert.match(css, /\.v3-venue-scene-wash[\s\S]*z-index:\s*3/);
|
|
assert.match(css, /#player \.v3-transport[\s\S]*z-index:\s*20/);
|
|
});
|