132 lines
3.6 KiB
Dart
132 lines
3.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:appsok/src/features/builds/builds_page.dart';
|
|
import 'package:appsok/src/models/jenkins_build.dart';
|
|
import 'package:appsok/src/services/jenkins_client.dart';
|
|
import 'package:appsok/src/theme/app_theme.dart';
|
|
|
|
Widget _wrap(Widget child) => MaterialApp(
|
|
theme: AppTheme.light(),
|
|
home: Scaffold(body: child),
|
|
);
|
|
|
|
List<JenkinsJob> _sampleJobs() => [
|
|
JenkinsJob(
|
|
name: 'android-app',
|
|
fullName: 'team/android-app',
|
|
url: Uri.parse('https://jenkins.example/job/android-app/'),
|
|
),
|
|
JenkinsJob(
|
|
name: 'ios-build',
|
|
url: Uri.parse('https://jenkins.example/job/ios-build/'),
|
|
),
|
|
];
|
|
|
|
void main() {
|
|
testWidgets('renders login required state without job loader', (
|
|
tester,
|
|
) async {
|
|
await tester.pumpWidget(_wrap(const BuildsPage()));
|
|
|
|
expect(find.text('Jenkins 연결 필요'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('renders searchable Jenkins jobs', (tester) async {
|
|
await tester.pumpWidget(
|
|
_wrap(BuildsPage(jobLoader: () async => _sampleJobs())),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('android-app'), findsOneWidget);
|
|
expect(find.text('ios-build'), findsOneWidget);
|
|
expect(find.byKey(const ValueKey('job-search-field')), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('filters jobs by name full name and url', (tester) async {
|
|
await tester.pumpWidget(
|
|
_wrap(BuildsPage(jobLoader: () async => _sampleJobs())),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
await tester.enterText(
|
|
find.byKey(const ValueKey('job-search-field')),
|
|
'android',
|
|
);
|
|
await tester.pump();
|
|
|
|
expect(find.text('android-app'), findsOneWidget);
|
|
expect(find.text('ios-build'), findsNothing);
|
|
});
|
|
|
|
testWidgets('selects a job and shows build list placeholder', (tester) async {
|
|
await tester.pumpWidget(
|
|
_wrap(BuildsPage(jobLoader: () async => _sampleJobs())),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
await tester.tap(find.text('android-app'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
find.byKey(const ValueKey('build-list-placeholder')),
|
|
findsOneWidget,
|
|
);
|
|
});
|
|
|
|
testWidgets('shows empty state when no jobs are returned', (tester) async {
|
|
await tester.pumpWidget(_wrap(BuildsPage(jobLoader: () async => [])));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('접근 가능한 job 없음'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('shows permission state for forbidden Jenkins response', (
|
|
tester,
|
|
) async {
|
|
await tester.pumpWidget(
|
|
_wrap(
|
|
BuildsPage(
|
|
jobLoader: () async => throw const JenkinsClientException(
|
|
statusCode: 403,
|
|
message: 'Forbidden',
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('권한 없음'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('shows failure state for network error', (tester) async {
|
|
await tester.pumpWidget(
|
|
_wrap(
|
|
BuildsPage(
|
|
jobLoader: () async => throw const JenkinsClientException(
|
|
statusCode: 500,
|
|
message: 'Error',
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('Jenkins 조회 실패'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('renders job selector without overflow at compact viewport', (
|
|
tester,
|
|
) async {
|
|
tester.view.physicalSize = const Size(600, 400);
|
|
tester.view.devicePixelRatio = 1.0;
|
|
addTearDown(tester.view.reset);
|
|
|
|
await tester.pumpWidget(
|
|
_wrap(BuildsPage(jobLoader: () async => _sampleJobs())),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(tester.takeException(), isNull);
|
|
});
|
|
}
|