| // Copyright 2019 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:async'; |
| |
| import 'package:cocoon_common/rpc_model.dart'; |
| import 'package:cocoon_common/task_status.dart'; |
| import 'package:flutter/material.dart'; |
| import 'package:flutter_app_icons/flutter_app_icons_platform_interface.dart'; |
| import 'package:flutter_dashboard/service/cocoon.dart'; |
| import 'package:flutter_dashboard/service/dev_cocoon.dart'; |
| import 'package:flutter_dashboard/service/firebase_auth.dart'; |
| import 'package:flutter_dashboard/state/build.dart'; |
| import 'package:flutter_dashboard/views/build_dashboard_page.dart'; |
| import 'package:flutter_dashboard/widgets/commit_box.dart'; |
| import 'package:flutter_dashboard/widgets/error_brook_watcher.dart'; |
| import 'package:flutter_dashboard/widgets/state_provider.dart'; |
| import 'package:flutter_dashboard/widgets/task_box.dart'; |
| import 'package:flutter_dashboard/widgets/user_sign_in.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| import 'package:mockito/mockito.dart'; |
| |
| import 'utils/fake_build.dart'; |
| import 'utils/fake_firebase_user.dart'; |
| import 'utils/fake_flutter_app_icons.dart'; |
| import 'utils/generate_commit_for_tests.dart'; |
| import 'utils/generate_task_for_tests.dart'; |
| import 'utils/golden.dart'; |
| import 'utils/mocks.dart'; |
| import 'utils/output.dart'; |
| import 'utils/task_icons.dart'; |
| |
| void main() { |
| late MockFirebaseAuthService fakeAuthService; |
| |
| final dropdownButtonType = |
| DropdownButton<String>( |
| onChanged: (_) {}, |
| items: const <DropdownMenuItem<String>>[], |
| ).runtimeType; |
| |
| void configureView(TestFlutterView view) { |
| // device pixel ratio of 1.0 works well on web app and emulator |
| // If not set, flutter test uses a Pixel 4 device pixel ratio of roughly 2.75, which doesn't quite work |
| // I am using the default settings of Pixel 4 in this test, as referenced in the link below |
| // https://android.googlesource.com/platform/external/qemu/+/b5b78438ae9ff3b90aafdab0f4f25585affc22fb/android/avd/hardware-properties.ini |
| view.devicePixelRatio = 1.0; |
| view.physicalSize = const Size(1080, 2280); |
| addTearDown(view.reset); |
| } |
| |
| setUp(() { |
| fakeAuthService = MockFirebaseAuthService(); |
| when(fakeAuthService.isAuthenticated).thenReturn(true); |
| when(fakeAuthService.user).thenReturn(FakeFirebaseUser()); |
| |
| FlutterAppIconsPlatform.instance = FakeFlutterAppIcons(); |
| }); |
| |
| testWidgets('shows sign in button', (WidgetTester tester) async { |
| configureView(tester.view); |
| final fakeCocoonService = MockCocoonService(); |
| throwOnMissingStub(fakeCocoonService); |
| when( |
| fakeCocoonService.fetchFlutterBranches(), |
| ).thenAnswer((_) => Completer<CocoonResponse<List<Branch>>>().future); |
| when( |
| fakeCocoonService.fetchRepos(), |
| ).thenAnswer((_) => Completer<CocoonResponse<List<String>>>().future); |
| when( |
| fakeCocoonService.fetchCommitStatuses( |
| branch: anyNamed('branch'), |
| repo: anyNamed('repo'), |
| ), |
| ).thenAnswer((_) => Completer<CocoonResponse<List<CommitStatus>>>().future); |
| when( |
| fakeCocoonService.fetchTreeBuildStatus( |
| branch: anyNamed('branch'), |
| repo: anyNamed('repo'), |
| ), |
| ).thenAnswer( |
| (_) => Completer<CocoonResponse<BuildStatusResponse>>().future, |
| ); |
| |
| final buildState = BuildState( |
| cocoonService: fakeCocoonService, |
| authService: fakeAuthService, |
| ); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: buildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: buildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| expect(find.byType(UserSignIn), findsOneWidget); |
| |
| await tester.pumpWidget(Container()); |
| buildState.dispose(); |
| }); |
| |
| testWidgets('shows settings button', (WidgetTester tester) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState()..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.byIcon(Icons.settings), findsOneWidget); |
| }); |
| |
| testWidgets('shows infra ticket queue button', (WidgetTester tester) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState()..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.byIcon(Icons.queue), findsOneWidget); |
| }); |
| |
| testWidgets('shows file a bug button', (WidgetTester tester) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState()..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.byIcon(Icons.bug_report), findsOneWidget); |
| }); |
| |
| testWidgets('shows key button & legend', (WidgetTester tester) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState()..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.byIcon(Icons.info_outline), findsOneWidget); |
| |
| await tester.tap(find.byIcon(Icons.info_outline)); |
| await tester.pump(); |
| |
| for (final status in TaskBox.statusColor.keys) { |
| expect(find.text(status.value), findsOneWidget); |
| } |
| expect(find.text('Flaky'), findsOneWidget); |
| expect(find.text('Ran more than once'), findsOneWidget); |
| }); |
| |
| testWidgets( |
| 'shows branch and repo dropdown button when screen is decently large', |
| (WidgetTester tester) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState()..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.byType(dropdownButtonType), findsNWidgets(2)); |
| |
| expect(find.text('repo: '), findsOneWidget); |
| expect( |
| (tester.widget(find.byKey(const Key('repo dropdown'))) |
| as DropdownButton) |
| .value, |
| equals('flutter'), |
| ); |
| |
| expect(find.text('branch: '), findsOneWidget); |
| expect( |
| (tester.widget(find.byKey(const Key('branch dropdown'))) |
| as DropdownButton) |
| .value, |
| equals('master'), |
| ); |
| }, |
| ); |
| |
| testWidgets( |
| 'shows enabled Refresh GitHub Commits button when isAuthenticated', |
| (WidgetTester tester) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState()..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| // Open settings overlay |
| await tester.tap(find.byIcon(Icons.settings)); |
| await tester.pump(); |
| await tester.pump( |
| const Duration(seconds: 1), |
| ); // Finish the menu animation. |
| |
| final labelFinder = find.text('Refresh GitHub Commits'); |
| |
| expect(labelFinder, findsOneWidget); |
| |
| final button = |
| tester |
| .element(labelFinder) |
| .findAncestorWidgetOfExactType<TextButton>()!; |
| |
| expect( |
| button.onPressed, |
| isNotNull, |
| reason: 'The button should have a non-null onPressed attribute.', |
| ); |
| }, |
| ); |
| |
| testWidgets( |
| 'shows disabled Refresh GitHub Commits button when !isAuthenticated', |
| (WidgetTester tester) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState()..authService = fakeAuthService; |
| when(fakeAuthService.isAuthenticated).thenReturn(false); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| // Open settings overlay |
| await tester.tap(find.byIcon(Icons.settings)); |
| await tester.pump(); |
| await tester.pump( |
| const Duration(seconds: 1), |
| ); // Finish the menu animation. |
| |
| final labelFinder = find.text('Refresh GitHub Commits'); |
| final button = |
| tester |
| .element(labelFinder) |
| .findAncestorWidgetOfExactType<TextButton>()!; |
| |
| expect( |
| button.onPressed, |
| isNull, |
| reason: 'The button should have a null onPressed attribute.', |
| ); |
| }, |
| ); |
| |
| testWidgets('shows loading when fetch tree status is null', ( |
| WidgetTester tester, |
| ) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState() |
| ..isTreeBuilding = null |
| ..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.text('Loading...'), findsOneWidget); |
| |
| final appbarWidget = find.byType(AppBar).evaluate().first.widget as AppBar; |
| expect(appbarWidget.backgroundColor, Colors.grey[850]); |
| expect(tester, meetsGuideline(textContrastGuideline)); |
| }); |
| |
| testWidgets('shows loading when fetch tree status is null, dark mode', ( |
| WidgetTester tester, |
| ) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState() |
| ..isTreeBuilding = null |
| ..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData.dark(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.text('Loading...'), findsOneWidget); |
| |
| final appbarWidget = find.byType(AppBar).evaluate().first.widget as AppBar; |
| expect(appbarWidget.backgroundColor, Colors.grey[850]); |
| expect(tester, meetsGuideline(textContrastGuideline)); |
| }); |
| |
| testWidgets('shows tree closed when fetch tree status is false', ( |
| WidgetTester tester, |
| ) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState() |
| ..isTreeBuilding = false |
| ..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| // Verify the "Tree is Closed" message is wrapped in a [Tooltip]. |
| final tooltipFinder = find.byWidgetPredicate((Widget widget) { |
| return widget is Tooltip && |
| (widget.message?.contains('Tree is Closed') ?? false); |
| }); |
| expect(tooltipFinder, findsOneWidget); |
| |
| final appbarWidget = find.byType(AppBar).evaluate().first.widget as AppBar; |
| expect(appbarWidget.backgroundColor, Colors.red); |
| expect(tester, meetsGuideline(textContrastGuideline)); |
| }); |
| |
| testWidgets('shows tree closed when fetch tree status is false, dark mode', ( |
| WidgetTester tester, |
| ) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState() |
| ..isTreeBuilding = false |
| ..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData.dark(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| // Verify the "Tree is Closed" message is wrapped in a [Tooltip]. |
| final tooltipFinder = find.byWidgetPredicate((Widget widget) { |
| return widget is Tooltip && |
| (widget.message?.contains('Tree is Closed') ?? false); |
| }); |
| expect(tooltipFinder, findsOneWidget); |
| |
| final appbarWidget = find.byType(AppBar).evaluate().first.widget as AppBar; |
| expect(appbarWidget.backgroundColor, Colors.red[800]); |
| expect(tester, meetsGuideline(textContrastGuideline)); |
| }); |
| |
| testWidgets('shows tree open when fetch tree status is true', ( |
| WidgetTester tester, |
| ) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState() |
| ..isTreeBuilding = true |
| ..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.text('Tree is Open'), findsOneWidget); |
| |
| final appbarWidget = find.byType(AppBar).evaluate().first.widget as AppBar; |
| expect(appbarWidget.backgroundColor, Colors.green); |
| expect(tester, meetsGuideline(textContrastGuideline)); |
| }); |
| |
| testWidgets('shows tree open when fetch tree status is true, dark mode', ( |
| WidgetTester tester, |
| ) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState() |
| ..isTreeBuilding = true |
| ..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData.dark(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.text('Tree is Open'), findsOneWidget); |
| |
| final appbarWidget = find.byType(AppBar).evaluate().first.widget as AppBar; |
| expect(appbarWidget.backgroundColor, Colors.green[800]); |
| expect(tester, meetsGuideline(textContrastGuideline)); |
| }); |
| |
| testWidgets('show error snackbar when error occurs', ( |
| WidgetTester tester, |
| ) async { |
| configureView(tester.view); |
| String? lastError; |
| final buildState = |
| FakeBuildState() |
| ..errors.addListener((String message) => lastError = message) |
| ..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: buildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: buildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(lastError, isNull); |
| |
| // propagate the error message |
| await checkOutput( |
| block: () async { |
| buildState.errors.send('ERROR'); |
| }, |
| output: <String>['ERROR'], |
| ); |
| await tester.pump(); |
| |
| await tester.pump( |
| const Duration(milliseconds: 750), |
| ); // open animation for snackbar |
| |
| expect(find.text(lastError!), findsOneWidget); |
| |
| // Snackbar message should go away after its duration |
| await tester.pump( |
| ErrorBrookWatcher.errorSnackbarDuration, |
| ); // wait the duration |
| await tester.pump(); // schedule animation |
| await tester.pump(const Duration(milliseconds: 1500)); // close animation |
| |
| expect(find.text(lastError!), findsNothing); |
| }); |
| |
| testWidgets('TaskGridContainer with default Settings property sheet', ( |
| WidgetTester tester, |
| ) async { |
| configureView(tester.view); |
| await precacheTaskIcons(tester); |
| final buildState = BuildState( |
| cocoonService: DevelopmentCocoonService(DateTime.utc(2020)), |
| authService: fakeAuthService, |
| ); |
| void listener1() {} |
| buildState.addListener(listener1); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: buildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: buildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| await tester.tap(find.byIcon(Icons.settings)); |
| await tester.pump(); |
| |
| await expectGoldenMatches( |
| find.byType(BuildDashboardPage), |
| 'build_dashboard.defaultPropertySheet.png', |
| ); |
| expect(tester, meetsGuideline(textContrastGuideline)); |
| |
| await tester.pumpWidget(Container()); |
| buildState.dispose(); |
| }); |
| |
| testWidgets( |
| 'TaskGridContainer with default Settings property sheet, dark mode', |
| (WidgetTester tester) async { |
| configureView(tester.view); |
| await precacheTaskIcons(tester); |
| final buildState = BuildState( |
| cocoonService: DevelopmentCocoonService(DateTime.utc(2020)), |
| authService: fakeAuthService, |
| ); |
| void listener1() {} |
| buildState.addListener(listener1); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData.dark(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: buildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: buildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| await tester.tap(find.byIcon(Icons.settings)); |
| await tester.pump(); |
| |
| await expectGoldenMatches( |
| find.byType(BuildDashboardPage), |
| 'build_dashboard.defaultPropertySheet.dark.png', |
| ); |
| expect(tester, meetsGuideline(textContrastGuideline)); |
| |
| await tester.pumpWidget(Container()); |
| buildState.dispose(); |
| }, |
| ); |
| |
| testWidgets('ensure smooth transition between invalid states', ( |
| WidgetTester tester, |
| ) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState()..authService = fakeAuthService; |
| var controlledBuildDashboardPage = const BuildDashboardPage( |
| queryParameters: {'repo': 'cocoon', 'branch': 'flutter-release'}, |
| ); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: controlledBuildDashboardPage, |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.byType(dropdownButtonType), findsNWidgets(2)); |
| // simulate a url request, which retriggers a rebuild of the widget |
| controlledBuildDashboardPage = const BuildDashboardPage( |
| queryParameters: {'repo': 'flutter'}, |
| ); |
| expect( |
| (tester.widget(find.byKey(const Key('branch dropdown'))) |
| as DropdownButton) |
| .value, |
| equals('flutter-release'), |
| ); //invalid state: engine + flutter-release |
| await tester |
| .pump(); //an invalid state will generate delayed network responses |
| |
| //if a delayed network request come in, from a previous invalid state: cocoon + engine - release, no exceptions should be raised |
| controlledBuildDashboardPage = const BuildDashboardPage( |
| queryParameters: {'repo': 'cocoon', 'branch': 'flutter-release'}, |
| ); |
| }); |
| |
| testWidgets( |
| 'shows branch and repo dropdown button in settings when screen is small', |
| (WidgetTester tester) async { |
| tester.view.physicalSize = const Size(500, 500); |
| tester.view.devicePixelRatio = 1.0; |
| addTearDown(tester.view.reset); |
| final BuildState fakeBuildState = |
| FakeBuildState()..authService = fakeAuthService; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.byType(dropdownButtonType), findsNothing); |
| |
| await tester.tap(find.byIcon(Icons.settings)); |
| await tester.pump(); |
| |
| expect(find.byType(dropdownButtonType), findsNWidgets(2)); |
| }, |
| ); |
| |
| testWidgets('Settings dialog default background color', ( |
| WidgetTester tester, |
| ) async { |
| configureView(tester.view); |
| final BuildState fakeBuildState = |
| FakeBuildState() |
| ..isTreeBuilding = true |
| ..authService = fakeAuthService; |
| const dialogText = 'Refresh GitHub Commits'; |
| ThemeData theme; |
| Widget buildDashboard({required Brightness brightness}) { |
| theme = ThemeData(useMaterial3: false, brightness: brightness); |
| return MaterialApp( |
| theme: theme, |
| home: ValueProvider<BuildState>( |
| value: fakeBuildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: fakeBuildState.authService, |
| child: const BuildDashboardPage(), |
| ), |
| ), |
| ); |
| } |
| |
| // Test dashboard in light mode. |
| await tester.pumpWidget(buildDashboard(brightness: Brightness.light)); |
| await tester.tap(find.byIcon(Icons.settings)); |
| await tester.pump(); |
| |
| var dialogContainer = find.ancestor( |
| of: find.text(dialogText), |
| matching: find.byType(Container), |
| ); |
| expect(dialogContainer, paints..rrect(color: Colors.white.withAlpha(0xe0))); |
| |
| // Test dashboard in dark mode. |
| await tester.pumpWidget(buildDashboard(brightness: Brightness.dark)); |
| await tester.tap(find.byIcon(Icons.settings)); |
| await tester.pump(const Duration(seconds: 1)); // Finish changing theme. |
| |
| dialogContainer = find.ancestor( |
| of: find.text(dialogText), |
| matching: find.byType(Container), |
| ); |
| expect( |
| dialogContainer, |
| paints..rrect(color: Colors.grey[800]!.withAlpha(0xe0)), |
| ); |
| }); |
| |
| group('schedulePostsubmitsForCommit', () { |
| final commit = generateCommitForTest( |
| author: 'foo@bar.com', |
| sha: 'abcdefghijkl1234567890', |
| ); |
| |
| late MockCocoonService cocoonService; |
| late FakeBuildState buildState; |
| late bool isAuthenticatd; |
| |
| setUp(() { |
| cocoonService = MockCocoonService(); |
| throwOnMissingStub(cocoonService); |
| |
| when(fakeAuthService.isAuthenticated).thenAnswer((_) => isAuthenticatd); |
| when(fakeAuthService.idToken).thenAnswer((_) async => '1234567890'); |
| |
| buildState = FakeBuildState( |
| authService: fakeAuthService, |
| cocoonService: cocoonService, |
| statuses: [ |
| CommitStatus( |
| commit: commit, |
| tasks: [ |
| generateTaskForTest( |
| status: TaskStatus.waitingForBackfill, |
| builderName: 'Builder', |
| ), |
| ], |
| ), |
| ], |
| ); |
| }); |
| |
| testWidgets('is disabled if signed out', (WidgetTester tester) async { |
| isAuthenticatd = false; |
| configureView(tester.view); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: buildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: buildState.authService, |
| child: const BuildDashboardPage( |
| queryParameters: { |
| 'repo': 'flutter', |
| 'branch': 'flutter-release', |
| }, |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| // Click a commit to open the commit box. |
| await tester.tap(find.byType(CommitBox)); |
| await tester.pump(); |
| |
| // Find the schedule button. |
| final tooltip = |
| tester.firstWidget(find.byKey(const ValueKey('schedulePostsubmit'))) |
| as Tooltip; |
| expect(tooltip.message, contains('Only enabled for release branches')); |
| }); |
| |
| testWidgets('is disabled on flutter/flutter master', ( |
| WidgetTester tester, |
| ) async { |
| isAuthenticatd = true; |
| configureView(tester.view); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: buildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: buildState.authService, |
| child: const BuildDashboardPage( |
| queryParameters: {'repo': 'flutter', 'branch': 'master'}, |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| // Click a commit to open the commit box. |
| await tester.tap(find.byType(CommitBox)); |
| await tester.pump(); |
| |
| // Find the schedule button. |
| final tooltip = |
| tester.firstWidget(find.byKey(const ValueKey('schedulePostsubmit'))) |
| as Tooltip; |
| expect(tooltip.message, contains('Only enabled for release branches')); |
| }); |
| |
| testWidgets('is disabled on flutter/cocoon non-master', ( |
| WidgetTester tester, |
| ) async { |
| isAuthenticatd = true; |
| configureView(tester.view); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: buildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: buildState.authService, |
| child: const BuildDashboardPage( |
| queryParameters: { |
| 'repo': 'cocoon', |
| 'branch': 'flutter-release', |
| }, |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| // Click a commit to open the commit box. |
| await tester.tap(find.byType(CommitBox)); |
| await tester.pump(); |
| |
| // Find the schedule button. |
| final tooltip = |
| tester.firstWidget(find.byKey(const ValueKey('schedulePostsubmit'))) |
| as Tooltip; |
| expect(tooltip.message, contains('Only enabled for release branches')); |
| }); |
| |
| testWidgets('is enabled if signed in, on flutter/flutter, on non-master', ( |
| WidgetTester tester, |
| ) async { |
| isAuthenticatd = true; |
| configureView(tester.view); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: ValueProvider<BuildState>( |
| value: buildState, |
| child: ValueProvider<FirebaseAuthService>( |
| value: buildState.authService, |
| child: const BuildDashboardPage( |
| queryParameters: { |
| 'repo': 'flutter', |
| 'branch': 'flutter-release', |
| }, |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| // Click a commit to open the commit box. |
| await tester.tap(find.byType(CommitBox)); |
| await tester.pump(); |
| |
| // Find the schedule button and press it. |
| when( |
| cocoonService.rerunCommit( |
| commitSha: commit.sha, |
| idToken: '1234567890', |
| branch: 'flutter-release', |
| repo: 'flutter', |
| include: {TaskStatus.skipped}, |
| ), |
| ).thenAnswer((_) async => const CocoonResponse.data(null)); |
| final tooltip = tester.firstWidget<Tooltip>( |
| find.byKey(const ValueKey('schedulePostsubmit')), |
| ); |
| await tester.tap(find.byWidget(tooltip.child!)); |
| }); |
| }); |
| } |