import 'package:flutter_test/flutter_test.dart'; import 'package:spotiflac_android/utils/synced_lyrics_scroll.dart'; void main() { test('symmetric padding lets the final lyric line reach center', () { const viewport = 500.0; const lineExtent = 64.0; const lineCount = 20; final padding = syncedLyricsCenterPadding( viewportDimension: viewport, estimatedLineExtent: lineExtent, ); final contentExtent = (padding * 2) + (lineCount * lineExtent); final maxScrollExtent = contentExtent - viewport; final lastLineOffset = syncedLyricsEstimatedOffset( index: lineCount - 1, estimatedLineExtent: lineExtent, ); expect(padding, 218); expect(lastLineOffset, maxScrollExtent); }); test('small viewports retain minimum breathing room', () { expect( syncedLyricsCenterPadding(viewportDimension: 80, estimatedLineExtent: 64), 24, ); }); test('advances at the exact next line boundary', () { const starts = [ Duration(seconds: 1), Duration(seconds: 3), Duration(seconds: 3), Duration(seconds: 6), ]; expect( syncedLyricsDueLineIndex( lineStarts: starts, currentIndex: 0, position: const Duration(milliseconds: 2999), ), 0, ); expect( syncedLyricsDueLineIndex( lineStarts: starts, currentIndex: 0, position: const Duration(seconds: 3), ), 2, ); }); group('smooth timed lyric highlight', () { double liftAt(int position, {int end = 1500}) => syncedLyricSegmentLift( position: Duration(milliseconds: position), start: const Duration(seconds: 1), end: Duration(milliseconds: end), ); test('short words anticipate gently and remain raised after singing', () { expect(liftAt(800), 0); expect(liftAt(950), inExclusiveRange(0, 1)); expect(liftAt(1000), greaterThan(liftAt(950))); expect(liftAt(1000), lessThan(0.2)); expect(liftAt(1200), inExclusiveRange(0.4, 0.7)); expect(liftAt(1500), lessThan(1)); expect(liftAt(1540), 1); expect(liftAt(3000), 1); }); test('held words rise further then settle to the normal highlight', () { final peak = liftAt(2300, end: 4000); expect(liftAt(1200, end: 4000), inExclusiveRange(0.4, 0.7)); expect(peak, greaterThan(1)); expect(liftAt(3900, end: 4000), inExclusiveRange(1, peak)); expect(liftAt(4000, end: 4000), 1); expect(liftAt(4200, end: 4000), 1); expect(liftAt(2300, end: 4000), peak); expect(liftAt(800, end: 4000), 0); }); test('short syllables keep the slow rise and trail their color sweep', () { expect(liftAt(1100, end: 1100), lessThan(0.4)); expect(liftAt(1200, end: 1100), lessThan(0.7)); expect(liftAt(1540, end: 1100), 1); for (var position = 820; position < 1540; position += 16) { final before = liftAt(position, end: 1100); final after = liftAt(position + 16, end: 1100); expect(after - before, inInclusiveRange(0, 0.034)); } }); test('invalid or zero-length timing does not move the text', () { expect(liftAt(1200, end: 1000), 0); expect(liftAt(1200, end: 500), 0); }); test('interpolates position only while playback is advancing', () { expect( interpolatedSyncedLyricsPosition( anchorPosition: const Duration(seconds: 10), elapsedSinceAnchor: const Duration(milliseconds: 250), isPlaying: true, ), const Duration(milliseconds: 10250), ); expect( interpolatedSyncedLyricsPosition( anchorPosition: const Duration(seconds: 10), elapsedSinceAnchor: const Duration(milliseconds: 250), isPlaying: false, ), const Duration(seconds: 10), ); }); test('blends small clock corrections and applies seeks immediately', () { expect( reconcileSyncedLyricsPosition( predictedPosition: const Duration(milliseconds: 1000), reportedPosition: const Duration(milliseconds: 1100), ), const Duration(milliseconds: 1025), ); expect( reconcileSyncedLyricsPosition( predictedPosition: const Duration(seconds: 1), reportedPosition: const Duration(seconds: 5), ), const Duration(seconds: 5), ); }); test('calculates continuous progress inside a timed segment', () { const start = Duration(seconds: 2); const end = Duration(seconds: 3); expect( syncedLyricSegmentProgress( position: const Duration(milliseconds: 1500), start: start, end: end, ), 0, ); expect( syncedLyricSegmentProgress( position: const Duration(milliseconds: 2250), start: start, end: end, ), 0.25, ); expect( syncedLyricSegmentProgress( position: const Duration(milliseconds: 3500), start: start, end: end, ), 1, ); }); test('moves the reveal boundary from left to right', () { expect( syncedLyricsLeftToRightBoundary(left: 10, right: 110, progress: 0), 10, ); expect( syncedLyricsLeftToRightBoundary(left: 10, right: 110, progress: 0.5), 60, ); expect( syncedLyricsLeftToRightBoundary(left: 10, right: 110, progress: 1), 110, ); }); }); }