import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pluriwave/servicios/servicio_audio.dart'; import 'package:pluriwave/widgets/visualizador_audio.dart'; /// Audit 1.7/2.5 (t4 lines 66-68, 120-122): the prototype draws 30 discrete /// bottom-anchored bars (`border-radius:2px`, a top-to-bottom gradient), but /// `_WaveFlowPainter` (`visualizador_audio.dart:206-275`) paints a single /// continuous oscilloscope stroke — the `barras` parameter was inert. These /// guards assert the actual bar COUNT and that the render is structurally /// discrete (no `CustomPaint` stroke), not just "looks bar-ish". void main() { group('VisualizadorAudio.barrasDiscretas', () { testWidgets( 'renders exactly `barras` discrete bars, not a single CustomPaint path', (tester) async { final controller = StreamController.broadcast(); addTearDown(controller.close); await tester.pumpWidget( MaterialApp( home: Scaffold( body: VisualizadorAudio( estadoStream: controller.stream, barras: 30, barrasDiscretas: true, ), ), ), ); await tester.pump(); for (var i = 0; i < 30; i++) { expect( find.byKey(Key('visualizador-barra-$i')), findsOneWidget, reason: 'bar $i of 30 must exist (t4 lines 66-68: 30 bars)', ); } expect( find.byKey(const Key('visualizador-barra-30')), findsNothing, reason: 'exactly 30 bars, not 31+', ); expect( find.descendant( of: find.byType(VisualizadorAudio), matching: find.byType(CustomPaint), ), findsNothing, reason: 'discrete mode must not fall back to the continuous ' '_WaveFlowPainter stroke', ); }, ); testWidgets('each bar has radius 2 and a top-to-bottom gradient ending ' 'at the given alpha (t4 line 67: linear-gradient(180deg,#7EE4C2,' 'rgba(126,228,194,.3)))', (tester) async { final controller = StreamController.broadcast(); addTearDown(controller.close); const color = Color(0xFF7EE4C2); await tester.pumpWidget( MaterialApp( home: Scaffold( body: VisualizadorAudio( estadoStream: controller.stream, barras: 8, barrasDiscretas: true, color: color, ), ), ), ); await tester.pump(); final barra = tester.widget( find.byKey(const Key('visualizador-barra-0')), ); final decoracion = barra.decoration as BoxDecoration; expect( decoracion.borderRadius, BorderRadius.circular(2), reason: 't4 line 67: border-radius:2px', ); final gradiente = decoracion.gradient as LinearGradient; expect(gradiente.begin, Alignment.topCenter); expect(gradiente.end, Alignment.bottomCenter); expect(gradiente.colors.first, color); expect(gradiente.colors.last, color.withValues(alpha: 0.3)); }); testWidgets( 'a custom gradienteFinAlpha changes only the gradient END colour ' '(t4 line 121: the player uses rgba(244,184,96,.45), not .3)', (tester) async { final controller = StreamController.broadcast(); addTearDown(controller.close); const color = Color(0xFFF4B860); await tester.pumpWidget( MaterialApp( home: Scaffold( body: VisualizadorAudio( estadoStream: controller.stream, barras: 8, barrasDiscretas: true, color: color, gradienteFinAlpha: 0.45, ), ), ), ); await tester.pump(); final barra = tester.widget( find.byKey(const Key('visualizador-barra-0')), ); final gradiente = (barra.decoration as BoxDecoration).gradient as LinearGradient; expect(gradiente.colors.last, color.withValues(alpha: 0.45)); }, ); testWidgets('barrasDiscretas defaults to false — existing callers keep the ' 'continuous CustomPaint stroke, no regression', (tester) async { final controller = StreamController.broadcast(); addTearDown(controller.close); await tester.pumpWidget( MaterialApp( home: Scaffold( body: VisualizadorAudio(estadoStream: controller.stream), ), ), ); await tester.pump(); expect( find.descendant( of: find.byType(VisualizadorAudio), matching: find.byType(CustomPaint), ), findsOneWidget, ); expect(find.byKey(const Key('visualizador-barra-0')), findsNothing); }); }); }