192 lines
6.5 KiB
Dart
192 lines
6.5 KiB
Dart
|
|
import 'package:flutter_test/flutter_test.dart';
|
||
|
|
import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart';
|
||
|
|
import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart';
|
||
|
|
import 'package:halkyon_learn_flutter/src/feature/jellyfin/widget/watch_screen.dart';
|
||
|
|
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_payload.dart';
|
||
|
|
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.dart';
|
||
|
|
import 'package:material_ui/material_ui.dart';
|
||
|
|
|
||
|
|
import '../../support/fake_jellyfin.dart';
|
||
|
|
import '../../support/fake_studyset_gateway.dart';
|
||
|
|
import '../../support/pump_app.dart';
|
||
|
|
import '../../support/sample_episode.dart';
|
||
|
|
import '../../support/test_dependencies.dart';
|
||
|
|
|
||
|
|
void main() {
|
||
|
|
late FakeJellyfinGateway jellyfin;
|
||
|
|
late MemoryJellyfinStore store;
|
||
|
|
|
||
|
|
setUp(() {
|
||
|
|
jellyfin = sampleJellyfin();
|
||
|
|
store = MemoryJellyfinStore();
|
||
|
|
});
|
||
|
|
|
||
|
|
Future<void> show(WidgetTester tester, String location) async {
|
||
|
|
tester.view.physicalSize = const Size(1200, 1400);
|
||
|
|
tester.view.devicePixelRatio = 1;
|
||
|
|
addTearDown(tester.view.reset);
|
||
|
|
final studysets = FakeStudysetGateway()
|
||
|
|
..receiveRemoteChange(
|
||
|
|
'japanese',
|
||
|
|
encodeStudysetPayload(
|
||
|
|
StudysetContent(
|
||
|
|
title: 'Japanese',
|
||
|
|
description: RichContent.empty(),
|
||
|
|
frontLanguage: 'ja',
|
||
|
|
backLanguage: 'en',
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
await pumpApp(
|
||
|
|
tester,
|
||
|
|
testDependencies(
|
||
|
|
studysetGateway: studysets,
|
||
|
|
dictionaryGateway: sampleDictionary(),
|
||
|
|
jellyfinGateway: jellyfin,
|
||
|
|
jellyfinStore: store,
|
||
|
|
),
|
||
|
|
location: location,
|
||
|
|
);
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Finds the server, from Settings or the studyset.
|
||
|
|
Future<void> findServer(WidgetTester tester) async {
|
||
|
|
await tester.enterText(
|
||
|
|
find.widgetWithText(TextField, 'Server address'),
|
||
|
|
'jellyfin.local:8920',
|
||
|
|
);
|
||
|
|
await tester.tap(find.text('Continue'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
}
|
||
|
|
|
||
|
|
testWidgets('Settings connects a server over HTTPS with a name and '
|
||
|
|
'password, kept on the device, and signs out of it', (tester) async {
|
||
|
|
await show(tester, RoutePaths.settings);
|
||
|
|
await tester.scrollUntilVisible(
|
||
|
|
find.widgetWithText(TextField, 'Server address'),
|
||
|
|
200,
|
||
|
|
scrollable: find.byType(Scrollable).first,
|
||
|
|
);
|
||
|
|
|
||
|
|
await tester.enterText(
|
||
|
|
find.widgetWithText(TextField, 'Server address'),
|
||
|
|
'nothing.local',
|
||
|
|
);
|
||
|
|
await tester.tap(find.text('Continue'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
expect(
|
||
|
|
find.text('No server answered at that address over HTTPS.'),
|
||
|
|
findsOneWidget,
|
||
|
|
);
|
||
|
|
await tester.enterText(
|
||
|
|
find.widgetWithText(TextField, 'Server address'),
|
||
|
|
'http://jellyfin.local:8920',
|
||
|
|
);
|
||
|
|
await tester.tap(find.text('Continue'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
expect(
|
||
|
|
find.text(
|
||
|
|
"That address isn't encrypted, and your password would travel in "
|
||
|
|
"the clear. Use the server's HTTPS address.",
|
||
|
|
),
|
||
|
|
findsOneWidget,
|
||
|
|
);
|
||
|
|
|
||
|
|
await findServer(tester);
|
||
|
|
expect(find.text('Living room'), findsOneWidget);
|
||
|
|
await tester.enterText(find.widgetWithText(TextField, 'User name'), 'mika');
|
||
|
|
await tester.enterText(find.widgetWithText(TextField, 'Password'), 'wrong');
|
||
|
|
await tester.tap(find.text('Sign in'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
expect(find.text('The user name or password is wrong.'), findsOneWidget);
|
||
|
|
|
||
|
|
await tester.enterText(
|
||
|
|
find.widgetWithText(TextField, 'Password'),
|
||
|
|
'secret',
|
||
|
|
);
|
||
|
|
await tester.tap(find.text('Sign in'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
|
||
|
|
expect(find.text('mika on Living room'), findsOneWidget);
|
||
|
|
expect(store.kept?.account?.token, 'token-1');
|
||
|
|
await tester.tap(find.byTooltip('Sign out of Jellyfin'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
expect(find.widgetWithText(TextField, 'Server address'), findsOneWidget);
|
||
|
|
expect(jellyfin.signedOut, ['token-1']);
|
||
|
|
});
|
||
|
|
|
||
|
|
testWidgets('a code approved in another Jellyfin app signs in', (
|
||
|
|
tester,
|
||
|
|
) async {
|
||
|
|
await show(tester, RoutePaths.jellyfin('japanese'));
|
||
|
|
await findServer(tester);
|
||
|
|
|
||
|
|
await tester.tap(find.text('Sign in with a code'));
|
||
|
|
// It waits, spinning, until the code is approved.
|
||
|
|
await tester.pump();
|
||
|
|
await tester.pump();
|
||
|
|
expect(find.text('123456'), findsOneWidget);
|
||
|
|
await tester.pump(const Duration(seconds: 3));
|
||
|
|
expect(find.text('123456'), findsOneWidget);
|
||
|
|
jellyfin.approved = true;
|
||
|
|
await tester.pump(const Duration(seconds: 3));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
|
||
|
|
expect(find.text('Anime'), findsOneWidget);
|
||
|
|
expect(store.kept?.account?.token, 'token-2');
|
||
|
|
});
|
||
|
|
|
||
|
|
testWidgets('a show tied to the studyset comes first next time; an '
|
||
|
|
'episode opens to be watched', (tester) async {
|
||
|
|
store = signedInJellyfin();
|
||
|
|
await show(tester, RoutePaths.jellyfin('japanese'));
|
||
|
|
expect(find.text('Tied to this studyset'), findsNothing);
|
||
|
|
|
||
|
|
await tester.tap(find.text('Anime'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
await tester.tap(find.byTooltip('Tie to this studyset'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
expect(find.byTooltip('Untie from this studyset'), findsOneWidget);
|
||
|
|
await tester.pageBack();
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
|
||
|
|
expect(find.text('Tied to this studyset'), findsOneWidget);
|
||
|
|
await tester.tap(find.text('Sample Journey'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
await tester.tap(find.text('Season 1'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
expect(find.text('S1 E1 · 24 min'), findsOneWidget);
|
||
|
|
await tester.tap(find.text('The Station'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
|
||
|
|
expect(find.byType(WatchScreen), findsOneWidget);
|
||
|
|
});
|
||
|
|
|
||
|
|
testWidgets('a search finds shows and episodes by their name', (
|
||
|
|
tester,
|
||
|
|
) async {
|
||
|
|
store = signedInJellyfin();
|
||
|
|
await show(tester, RoutePaths.jellyfin('japanese'));
|
||
|
|
|
||
|
|
await tester.enterText(find.byType(TextField), 'station');
|
||
|
|
await tester.pump(const Duration(milliseconds: 300));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
|
||
|
|
expect(find.text('The Station'), findsOneWidget);
|
||
|
|
expect(find.text('Libraries'), findsNothing);
|
||
|
|
});
|
||
|
|
|
||
|
|
testWidgets("the studyset's Add material offers watching from Jellyfin", (
|
||
|
|
tester,
|
||
|
|
) async {
|
||
|
|
await show(tester, RoutePaths.studyset('japanese'));
|
||
|
|
await tester.tap(find.text('Add material'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
await tester.tap(find.text('Watch from Jellyfin'));
|
||
|
|
await tester.pump();
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
|
||
|
|
expect(find.widgetWithText(TextField, 'Server address'), findsOneWidget);
|
||
|
|
});
|
||
|
|
}
|