import 'package:flutter_test/flutter_test.dart'; import 'package:pluriwave/modelos/emisora.dart'; import 'package:pluriwave/modelos/grupo_favoritos.dart'; import 'package:pluriwave/servicios/contexto_reproduccion.dart'; import 'package:pluriwave/servicios/navegacion_auto.dart'; /// Requested: the Android Auto playback screen must offer previous/next for /// stations too, not only for a local-music queue. Those buttons appear /// because `skipToPrevious`/`skipToNext` are advertised in `systemActions` — /// so they have to actually move, or the car shows two dead buttons. void main() { Emisora emisora(String uuid) => Emisora(uuid: uuid, nombre: uuid, url: 'https://example.com/$uuid'); final a = emisora('a'); final b = emisora('b'); final c = emisora('c'); group('emisoraVecina', () { test('avanza y retrocede dentro de la lista', () { expect(emisoraVecina(a, [a, b, c], haciaAtras: false), b); expect(emisoraVecina(b, [a, b, c], haciaAtras: true), a); }); test('da la vuelta en los dos extremos', () { // A dead button at the end of a list reads as a broken app on a car // screen, where there is no visible list position to explain it. expect(emisoraVecina(c, [a, b, c], haciaAtras: false), a); expect(emisoraVecina(a, [a, b, c], haciaAtras: true), c); }); test('identifica por uuid, no por instancia: un snapshot refrescado trae ' 'objetos distintos', () { final copiaDeB = Emisora( uuid: 'b', nombre: 'otro nombre', url: 'https://example.com/cambiada', ); expect(emisoraVecina(copiaDeB, [a, b, c], haciaAtras: false), c); }); test('no hace nada sin contexto suficiente', () { expect(emisoraVecina(null, [a, b], haciaAtras: false), isNull); expect(emisoraVecina(a, [a], haciaAtras: false), isNull); expect(emisoraVecina(a, const [], haciaAtras: false), isNull); expect( emisoraVecina(emisora('fuera'), [a, b], haciaAtras: false), isNull, reason: 'una emisora que no está en la lista no debe saltar a ciegas', ); }); }); group('listaParaSaltoEmisora', () { test('favoritos gana: "siguiente" desde un favorito va al siguiente ' 'favorito, no a la entrada 4318 del catálogo', () { expect( listaParaSaltoEmisora( actual: a, favoritos: [a, b], misEmisoras: [a, c], todas: [a, b, c], ), [a, b], ); }); test('mis emisoras cuando no es favorita', () { expect( listaParaSaltoEmisora( actual: c, favoritos: [a, b], misEmisoras: [c, a], todas: [a, b, c], ), [c, a], ); }); test('cae al catálogo completo para una emisora llegada por búsqueda', () { expect( listaParaSaltoEmisora( actual: c, favoritos: [a], misEmisoras: [b], todas: [a, b, c], ), [a, b, c], ); }); test('vacía si no está en ninguna: el salto queda en no-op', () { expect( listaParaSaltoEmisora( actual: emisora('huerfana'), favoritos: [a], misEmisoras: [b], todas: [a, b], ), isEmpty, ); }); }); group('navegación por GRUPO de favoritos (pedido por el dueño)', () { Emisora favorita(String uuid, String grupo) => Emisora( uuid: uuid, nombre: uuid, url: 'https://example.com/$uuid', grupoFavoritosId: grupo, ); final rock1 = favorita('rock1', 'g-rock'); final rock2 = favorita('rock2', 'g-rock'); final jazz1 = favorita('jazz1', 'g-jazz'); final suelta = favorita('suelta', GrupoFavoritos.sinAsignarId); test('sonando una favorita de un grupo, se recorre SOLO ese grupo', () { expect( listaParaSaltoEmisora( actual: rock1, favoritos: [rock1, jazz1, rock2, suelta], misEmisoras: const [], todas: [rock1, jazz1, rock2, suelta], ), [rock1, rock2], ); }); test('el grupo se lee del registro de FAVORITOS, no de lo que suena: la ' 'emisora reconstruida desde el MediaItem no lleva grupo', () { // emisoraDesdeMediaItem no puede saber el grupo -> llega "sin asignar". final reconstruida = Emisora( uuid: 'rock1', nombre: 'Rock 1', url: 'https://example.com/rock1', ); expect(reconstruida.grupoFavoritosId, GrupoFavoritos.sinAsignarId); expect( listaParaSaltoEmisora( actual: reconstruida, favoritos: [rock1, jazz1, rock2], misEmisoras: const [], todas: const [], ), [rock1, rock2], reason: 'si se leyera de `actual` caeríamos a todos los favoritos', ); }); test('"sin asignar" NO es un grupo: recorre todos los favoritos', () { expect( listaParaSaltoEmisora( actual: suelta, favoritos: [rock1, suelta, jazz1], misEmisoras: const [], todas: const [], ), [rock1, suelta, jazz1], ); }); test('un grupo de UNA sola emisora cae a todos los favoritos, para no ' 'dejar los dos botones muertos', () { expect( listaParaSaltoEmisora( actual: jazz1, favoritos: [rock1, rock2, jazz1], misEmisoras: const [], todas: const [], ), [rock1, rock2, jazz1], ); }); }); group('contextoParaSaltoEmisora — la MISMA decisión, nombrada para poder ' 'recordarla entre procesos', () { Emisora favorita(String uuid, String grupo) => Emisora( uuid: uuid, nombre: uuid, url: 'https://example.com/$uuid', grupoFavoritosId: grupo, ); final rock1 = favorita('rock1', 'g-rock'); final rock2 = favorita('rock2', 'g-rock'); final jazz1 = favorita('jazz1', 'g-jazz'); test('nombra el grupo cuando el salto se queda dentro del grupo', () { expect( contextoParaSaltoEmisora( actual: rock1, favoritos: [rock1, jazz1, rock2], misEmisoras: const [], todas: const [], ), const ContextoSalto.grupo('g-rock'), ); }); test('nombra la lista de cada uno de los otros tres casos', () { expect( contextoParaSaltoEmisora( actual: jazz1, favoritos: [rock1, jazz1, rock2], misEmisoras: const [], todas: const [], ), const ContextoSalto.favoritos(), reason: 'un grupo de un solo miembro cae a todos los favoritos', ); expect( contextoParaSaltoEmisora( actual: c, favoritos: [a, b], misEmisoras: [c, a], todas: [a, b, c], ), const ContextoSalto.misEmisoras(), ); expect( contextoParaSaltoEmisora( actual: c, favoritos: [a], misEmisoras: [b], todas: [a, b, c], ), const ContextoSalto.todas(), ); }); test('null cuando la emisora no está en ninguna lista', () { expect( contextoParaSaltoEmisora( actual: emisora('huerfana'), favoritos: [a], misEmisoras: [b], todas: [a, b], ), isNull, ); }); test('CONCUERDA con listaParaSaltoEmisora en todos los casos: son la ' 'misma decisión y no pueden divergir', () { final escenarios = >>[ [ [rock1], [rock1, jazz1, rock2], const [], const [], ], [ [jazz1], [rock1, jazz1, rock2], const [], const [], ], [ [c], [a, b], [c, a], [a, b, c], ], [ [c], [a], [b], [a, b, c], ], [ [emisora('huerfana')], [a], [b], [a, b], ], ]; for (final escenario in escenarios) { final actual = escenario[0].single; final favoritos = escenario[1]; final misEmisoras = escenario[2]; final todas = escenario[3]; final contexto = contextoParaSaltoEmisora( actual: actual, favoritos: favoritos, misEmisoras: misEmisoras, todas: todas, ); final porContexto = contexto == null ? const [] : resolverListaContexto( contexto: contexto, actual: actual, favoritos: favoritos, misEmisoras: misEmisoras, todas: todas, destacadas: const [], grupos: const [ GrupoFavoritos(id: 'g-rock', nombre: 'Rock', orden: 1), GrupoFavoritos(id: 'g-jazz', nombre: 'Jazz', orden: 2), ], ); expect( porContexto, listaParaSaltoEmisora( actual: actual, favoritos: favoritos, misEmisoras: misEmisoras, todas: todas, ), ); } }); }); }