mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-29 12:52:13 +02:00
173 lines
5.9 KiB
Dart
173 lines
5.9 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:spotiflac_android/utils/lyrics_parser.dart';
|
|
import 'package:spotiflac_android/widgets/aligned_lyric_pronunciation.dart';
|
|
|
|
const _primaryStyle = TextStyle(fontSize: 34, height: 1.3);
|
|
const _pronunciationStyle = TextStyle(fontSize: 22, height: 1.35);
|
|
|
|
LyricWord _word(String text, int start, [int? end]) => LyricWord(
|
|
text: text,
|
|
time: Duration(milliseconds: start),
|
|
end: end == null ? null : Duration(milliseconds: end),
|
|
);
|
|
|
|
LyricLine _line({int pronunciationOffset = 0}) => LyricLine(
|
|
time: Duration.zero,
|
|
text: '今日も歌う',
|
|
words: [
|
|
_word('今', 0),
|
|
_word('日', 200),
|
|
_word('も', 500),
|
|
_word('歌う', 800, 1500),
|
|
],
|
|
romanization: 'kyou mo utau',
|
|
romanizationWords: [
|
|
_word('kyo', pronunciationOffset, 200),
|
|
_word('u ', 200, 400),
|
|
_word('mo ', 500, 750),
|
|
_word('utau', 800, 1500),
|
|
],
|
|
);
|
|
|
|
LyricPronunciationLayout? _layout(
|
|
LyricLine line, {
|
|
double width = 320,
|
|
double scale = 1,
|
|
}) => LyricPronunciationLayout.measure(
|
|
line: line,
|
|
primaryStyle: _primaryStyle,
|
|
pronunciationStyle: _pronunciationStyle,
|
|
maxWidth: width,
|
|
textScaler: TextScaler.linear(scale),
|
|
textDirection: TextDirection.ltr,
|
|
);
|
|
|
|
void main() {
|
|
testWidgets('right-aligned voices move original and pronunciation together', (
|
|
tester,
|
|
) async {
|
|
Future<void> pump(TextAlign alignment) => tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SizedBox(
|
|
width: 400,
|
|
child: AlignedLyricPronunciation(
|
|
layout: _layout(_line(), width: 400)!,
|
|
visibility: 1,
|
|
textAlign: alignment,
|
|
primaryStyle: _primaryStyle,
|
|
pronunciationStyle: _pronunciationStyle,
|
|
textBuilder: (text, words, style) => Text(text, style: style),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await pump(TextAlign.left);
|
|
final original = tester.getTopLeft(find.text('今日'));
|
|
final reading = tester.getTopLeft(find.text('kyou'));
|
|
await pump(TextAlign.right);
|
|
final shift = tester.getTopLeft(find.text('今日')) - original;
|
|
expect(shift.dx, greaterThan(0));
|
|
expect(shift.dy, 0);
|
|
expect(tester.getTopLeft(find.text('kyou')) - reading, shift);
|
|
expect(tester.takeException(), isNull);
|
|
});
|
|
|
|
test('does not guess alignment without matching word timing', () {
|
|
expect(_layout(_line(pronunciationOffset: 100)), isNull);
|
|
expect(
|
|
_layout(
|
|
const LyricLine(
|
|
time: Duration.zero,
|
|
text: 'Original',
|
|
romanization: 'Pronunciation',
|
|
),
|
|
),
|
|
isNull,
|
|
);
|
|
});
|
|
|
|
for (final width in [320.0, 170.0]) {
|
|
for (final scale in [1.0, 1.8]) {
|
|
testWidgets(
|
|
'phrases stay paired when wrapping and hiding ($width, $scale)',
|
|
(tester) async {
|
|
final layout = _layout(_line(), width: width, scale: scale)!;
|
|
final renderedWords = <String, List<LyricWord>>{};
|
|
Future<void> pump(double visibility) => tester.pumpWidget(
|
|
MaterialApp(
|
|
home: MediaQuery(
|
|
data: MediaQueryData(textScaler: TextScaler.linear(scale)),
|
|
child: Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SizedBox(
|
|
width: width,
|
|
child: AlignedLyricPronunciation(
|
|
layout: layout,
|
|
visibility: visibility,
|
|
primaryStyle: _primaryStyle,
|
|
pronunciationStyle: _pronunciationStyle,
|
|
textBuilder: (text, words, style) {
|
|
renderedWords[text] = words;
|
|
return Text(text, style: style);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await pump(1);
|
|
final block = find.byType(AlignedLyricPronunciation);
|
|
final bounds = tester.getRect(block);
|
|
expect(
|
|
bounds.height,
|
|
closeTo(layout.primaryHeight + layout.pronunciationHeight, 0.01),
|
|
);
|
|
final primaryPositions = <Offset>[];
|
|
for (final (original, pronunciation) in [
|
|
('今日', 'kyou'),
|
|
('も', 'mo'),
|
|
('歌う', 'utau'),
|
|
]) {
|
|
final primary = tester.getRect(find.text(original));
|
|
final secondary = tester.getRect(find.text(pronunciation));
|
|
primaryPositions.add(primary.topLeft);
|
|
expect(primary.left, closeTo(secondary.left, 0.01));
|
|
expect(secondary.top, greaterThanOrEqualTo(primary.bottom));
|
|
expect(secondary.right, lessThanOrEqualTo(bounds.right + 0.01));
|
|
}
|
|
// Syllables stay together, and the end of a sliced phrase still
|
|
// points to the next original word, not the end of the whole line.
|
|
expect(renderedWords['kyou']!.map((word) => word.text), ['kyo', 'u']);
|
|
expect(renderedWords['今日']!.last.end!.inMilliseconds, 500);
|
|
expect(renderedWords['kyou']!.last.end!.inMilliseconds, 400);
|
|
expect(renderedWords['utau']!.single.end!.inMilliseconds, 1500);
|
|
final firstPosition = primaryPositions.first;
|
|
for (final visibility in [0.5, 0.0]) {
|
|
await pump(visibility);
|
|
expect(
|
|
tester.getSize(block).height,
|
|
closeTo(
|
|
layout.primaryHeight + layout.pronunciationHeight * visibility,
|
|
0.01,
|
|
),
|
|
);
|
|
expect(tester.getTopLeft(find.text('今日')), firstPosition);
|
|
expect(
|
|
tester.getTopLeft(find.text('も')).dx,
|
|
primaryPositions[1].dx,
|
|
);
|
|
expect(tester.takeException(), isNull);
|
|
}
|
|
expect(find.text('kyou'), findsNothing);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|