import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/theme/mornye_theme.dart';
import 'package:spotiflac_android/utils/editorial_notes.dart';
import 'package:spotiflac_android/widgets/album_description.dart';
void main() {
test(
'editorial notes prefer full text and fall back to a usable summary',
() {
expect(
albumDescriptionFromMetadata({
'editorial_notes': {
'standard': '
Full notes.
',
'short': 'Short notes.',
},
}),
'Full notes.
',
);
expect(
albumDescriptionFromMetadata({
'editorial_notes': {
'standard': '
',
'short': 'Short notes.',
},
}),
'Short notes.',
);
expect(
albumDescriptionFromMetadata({'description': 'Plain notes.'}),
'Plain notes.',
);
expect(albumDescriptionFromMetadata({'editorial_notes': null}), isNull);
expect(albumDescriptionFromMetadata({'editorial_notes': 42}), isNull);
expect(
albumDescriptionFromMetadata({
'editorial_notes': {'standard': ''},
}),
isNull,
);
},
);
for (final style in ['mornye-light', 'mornye-dark', 'material']) {
testWidgets('short album notes have no More action ($style)', (
tester,
) async {
final mornye = style.startsWith('mornye');
tester.view.physicalSize = const Size(393, 700);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
for (final description in [
'A short description.',
'A bold sound.
\n\n',
'',
]) {
await tester.pumpWidget(
MaterialApp(
theme: mornye
? MornyeTheme.build(
style.endsWith('light')
? Brightness.light
: Brightness.dark,
)
: ThemeData(),
home: Scaffold(
body: AlbumDescription(title: 'Album', description: description),
),
),
);
await tester.pumpAndSettle();
expect(find.text(mornye ? 'MORE' : 'More'), findsNothing);
final tap = tester.widget(find.byType(InkWell));
expect(tap.onTap, isNull);
expect(tester.takeException(), isNull);
}
});
testWidgets('More responds to album preview width and text size ($style)', (
tester,
) async {
final mornye = style.startsWith('mornye');
final scale = ValueNotifier(1.0);
addTearDown(scale.dispose);
tester.view.physicalSize = const Size(393, 700);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
await tester.pumpWidget(
MaterialApp(
theme: mornye
? MornyeTheme.build(
style.endsWith('light') ? Brightness.light : Brightness.dark,
)
: ThemeData(),
home: ValueListenableBuilder(
valueListenable: scale,
builder: (context, value, child) => MediaQuery(
data: MediaQuery.of(
context,
).copyWith(textScaler: TextScaler.linear(value)),
child: child!,
),
child: const Scaffold(
body: AlbumDescription(
title: 'Album',
description: 'New sounds and new stories.',
),
),
),
),
);
await tester.pumpAndSettle();
final more = find.text(mornye ? 'MORE' : 'More');
expect(more, findsNothing);
tester.view.physicalSize = const Size(180, 700);
await tester.pumpAndSettle();
expect(more, findsOneWidget);
tester.view.physicalSize = const Size(393, 700);
await tester.pumpAndSettle();
expect(more, findsNothing);
scale.value = 2.5;
await tester.pumpAndSettle();
expect(more, findsOneWidget);
expect(tester.takeException(), isNull);
});
testWidgets('album description opens a formatted reading sheet ($style)', (
tester,
) async {
await tester.binding.setSurfaceSize(const Size(393, 700));
addTearDown(() => tester.binding.setSurfaceSize(null));
final mornye = style.startsWith('mornye');
await tester.pumpWidget(
ProviderScope(
child: MaterialApp(
theme: mornye
? MornyeTheme.build(
style.endsWith('light')
? Brightness.light
: Brightness.dark,
)
: ThemeData(),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Navigator(
onGenerateRoute: (_) => MaterialPageRoute(
builder: (_) => Scaffold(
body: AlbumDescription(
title: 'Example Album',
description:
'A new direction & a bold sound.\n\n'
'A second paragraph.\nOne line break remains.\n\n'
'${List.filled(12, 'The artists explore melodies and new ideas in these songs.
').join()}'
'Final paragraph.
',
),
),
),
),
),
),
);
await tester.pumpAndSettle();
final preview = tester.widget(
find.byWidgetPredicate(
(widget) => widget is Text && widget.textSpan != null,
),
);
expect(preview.maxLines, 2);
expect(
preview.textSpan!.toPlainText(),
contains('new direction & a bold sound.\n\n'),
);
expect(preview.textSpan!.toPlainText(), isNot(contains('hidden script')));
expect(
preview.textSpan!.toPlainText(),
contains('sound.\n\nA second paragraph.\nOne line break remains.\n\n'),
);
Iterable spans(InlineSpan span) sync* {
if (span is TextSpan) {
yield span;
for (final child in span.children ?? []) {
yield* spans(child);
}
}
}
expect(
spans(preview.textSpan!).any(
(span) =>
span.style?.fontStyle == FontStyle.italic &&
span.toPlainText() == 'new direction',
),
isTrue,
);
await tester.tap(find.text(mornye ? 'MORE' : 'More'));
await tester.pumpAndSettle();
expect(find.text('Example Album'), findsOneWidget);
expect(find.byType(SelectionArea), findsOneWidget);
expect(
ModalRoute.of(tester.element(find.byType(SelectionArea)))!.navigator,
tester.state(find.byType(Navigator).first),
);
expect(find.byTooltip('Close').hitTestable(), findsOneWidget);
await tester.drag(
find.byType(SingleChildScrollView),
const Offset(0, -600),
);
await tester.pumpAndSettle();
final scroll = tester.state(
find.descendant(
of: find.byType(SingleChildScrollView),
matching: find.byType(Scrollable),
),
);
expect(scroll.position.pixels, greaterThan(0));
expect(find.byTooltip('Close').hitTestable(), findsOneWidget);
await tester.tap(find.byTooltip('Close'));
await tester.pumpAndSettle();
expect(find.byType(SelectionArea), findsNothing);
expect(tester.takeException(), isNull);
});
}
}