blob: 334ba7cc868c2e23068ae39da47d66e1511332dc [file] [log] [blame]
// Copyright (c) 2019 The Chromium 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 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:test/test.dart' as test;
import 'package:cocoon_service/protos.dart'
show Commit, CommitStatus, Stage, Task;
import 'package:app_flutter/service/cocoon.dart';
import 'package:app_flutter/service/fake_cocoon.dart';
import 'package:app_flutter/state/flutter_build.dart';
import 'package:app_flutter/commit_box.dart';
import 'package:app_flutter/status_grid.dart';
import 'package:app_flutter/task_box.dart';
import 'package:app_flutter/task_icon.dart';
import 'package:app_flutter/task_matrix.dart' show TaskMatrix;
import 'utils/fake_flutter_build.dart';
void main() {
group('StatusGrid', () {
FakeCocoonService service;
List<CommitStatus> statuses;
TaskMatrix taskMatrix;
setUpAll(() async {
service = FakeCocoonService();
final CocoonResponse<List<CommitStatus>> response =
await service.fetchCommitStatuses();
statuses = response.data;
taskMatrix = TaskMatrix(statuses: statuses);
});
tearDown(() {
// Image.Network caches images which must be cleared.
PaintingBinding.instance.imageCache.clear();
});
testWidgets('shows loading indicator when statuses is empty',
(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Column(
children: <Widget>[
ChangeNotifierProvider<FlutterBuildState>(
create: (_) => FakeFlutterBuildState(),
child: const StatusGridContainer(),
),
],
),
),
);
expect(find.byType(CircularProgressIndicator), findsOneWidget);
expect(find.byType(GridView), findsNothing);
});
testWidgets('commits show in the same column', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Column(
children: <Widget>[
StatusGrid(
buildState: FakeFlutterBuildState(),
statuses: statuses,
taskMatrix: taskMatrix,
),
],
),
),
);
// TODO(chillers): Remove this web check once issue is resolved. https://github.com/flutter/flutter/issues/44370
if (!kIsWeb) {
expect(tester.takeException(),
const test.TypeMatcher<NetworkImageLoadException>());
}
final List<Element> commits = find.byType(CommitBox).evaluate().toList();
final double xPosition = commits.first.size.topLeft(Offset.zero).dx;
for (Element commit in commits) {
// All the x positions should match the first instance if they're all in the same column
expect(commit.size.topLeft(Offset.zero).dx, xPosition);
}
});
testWidgets('first task in grid is the first task given',
(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Column(
children: <Widget>[
StatusGrid(
buildState: FakeFlutterBuildState(),
statuses: statuses,
taskMatrix: taskMatrix,
),
],
),
),
);
// TODO(chillers): Remove this web check once issue is resolved. https://github.com/flutter/flutter/issues/44370
if (!kIsWeb) {
expect(tester.takeException(),
const test.TypeMatcher<NetworkImageLoadException>());
}
final TaskBox firstTask = find.byType(TaskBox).evaluate().first.widget;
expect(firstTask.task, taskMatrix.task(0, 0));
});
/// Matrix Diagram:
/// ✓☐☐
/// ☐✓☐
/// ☐☐✓
/// To construct the matrix from this diagram, each [CommitStatus] must have a unique [Task]
/// that does not share its name with any other [Task]. This will make that [CommitStatus] have
/// its task on its own unique row and column.
final List<CommitStatus> statusesWithSkips = <CommitStatus>[
CommitStatus()
..commit = (Commit()..author = 'Author')
..stages.add(Stage()
..name = 'A'
..tasks.addAll(<Task>[
Task()
..name = '1'
..status = TaskBox.statusSucceeded
])),
CommitStatus()
..commit = (Commit()..author = 'Author')
..stages.add(Stage()
..name = 'A'
..tasks.addAll(<Task>[
Task()
..name = '2'
..status = TaskBox.statusSucceeded
])),
CommitStatus()
..commit = (Commit()..author = 'Author')
..stages.add(Stage()
..name = 'A'
..tasks.addAll(<Task>[
Task()
..name = '3'
..status = TaskBox.statusSucceeded
]))
];
testWidgets('skipped tasks do not break the grid',
(WidgetTester tester) async {
final TaskMatrix taskMatrix = TaskMatrix(statuses: statusesWithSkips);
await tester.pumpWidget(
MaterialApp(
home: Column(
children: <Widget>[
StatusGrid(
buildState: FakeFlutterBuildState(),
statuses: statusesWithSkips,
taskMatrix: taskMatrix,
insertCellKeys: true,
),
],
),
),
);
// TODO(chillers): Remove this web check once issue is resolved. https://github.com/flutter/flutter/issues/44370
if (!kIsWeb) {
expect(tester.takeException(),
const test.TypeMatcher<NetworkImageLoadException>());
}
expect(find.byType(TaskBox), findsNWidgets(3));
// Row 1: ✓☐☐
final TaskBox firstTask =
find.byKey(const Key('cell-0-0')).evaluate().first.widget;
expect(firstTask.task, statusesWithSkips[0].stages[0].tasks[0]);
final SizedBox skippedTaskRow1Col2 =
find.byKey(const Key('cell-0-1')).evaluate().first.widget;
expect(skippedTaskRow1Col2, isNotNull);
final SizedBox skippedTaskRow1Col3 =
find.byKey(const Key('cell-0-2')).evaluate().first.widget;
expect(skippedTaskRow1Col3, isNotNull);
// Row 2: ☐✓☐
final SizedBox skippedTaskRow2Col1 =
find.byKey(const Key('cell-1-0')).evaluate().first.widget;
expect(skippedTaskRow2Col1, isNotNull);
final TaskBox secondTask =
find.byKey(const Key('cell-1-1')).evaluate().first.widget;
expect(secondTask.task, statusesWithSkips[1].stages[0].tasks[0]);
final SizedBox skippedTaskRow2Col3 =
find.byKey(const Key('cell-1-2')).evaluate().first.widget;
expect(skippedTaskRow2Col3, isNotNull);
// Row 3: ☐☐✓
final SizedBox skippedTaskRow3Col1 =
find.byKey(const Key('cell-2-0')).evaluate().first.widget;
expect(skippedTaskRow3Col1, isNotNull);
final SizedBox skippedTaskRow3Col2 =
find.byKey(const Key('cell-2-1')).evaluate().first.widget;
expect(skippedTaskRow3Col2, isNotNull);
final TaskBox lastTask =
find.byKey(const Key('cell-2-2')).evaluate().first.widget;
expect(lastTask.task, statusesWithSkips[2].stages[0].tasks[0]);
});
testWidgets(
'all cells in the grid have the same size even when grid has skipped tasks',
(WidgetTester tester) async {
final TaskMatrix taskMatrix = TaskMatrix(statuses: statusesWithSkips);
await tester.pumpWidget(
MaterialApp(
home: Column(
children: <Widget>[
StatusGrid(
buildState: FakeFlutterBuildState(),
statuses: statusesWithSkips,
taskMatrix: taskMatrix,
insertCellKeys: true,
),
],
),
),
);
// TODO(chillers): Remove this web check once issue is resolved. https://github.com/flutter/flutter/issues/44370
if (!kIsWeb) {
expect(tester.takeException(),
const test.TypeMatcher<NetworkImageLoadException>());
}
// Compare all the cells to the first cell to check they all have
// the same size
final Element taskBox =
find.byKey(const Key('cell-0-0')).evaluate().first;
for (int rowIndex = 0; rowIndex < taskMatrix.rows; rowIndex++) {
for (int colIndex = 0; colIndex < taskMatrix.columns; colIndex++) {
final Element cell =
find.byKey(Key('cell-$rowIndex-$colIndex')).evaluate().first;
expect(taskBox.size, cell.size);
}
}
});
testWidgets('task icon row is created', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Column(
children: <Widget>[
StatusGrid(
buildState: FakeFlutterBuildState(),
statuses: statuses,
taskMatrix: taskMatrix,
insertCellKeys: true,
),
],
),
),
);
// TODO(chillers): Remove this web check once issue is resolved. https://github.com/flutter/flutter/issues/44370
if (!kIsWeb) {
expect(tester.takeException(),
const test.TypeMatcher<NetworkImageLoadException>());
}
final List<Element> taskIcons = find.byType(TaskIcon).evaluate().toList();
final double yPosition = taskIcons.first.size.topLeft(Offset.zero).dy;
// Ensure all task icons are in the same row
for (Element taskIcon in taskIcons) {
// All y positions should match the first instance if in the same row
expect(taskIcon.size.topLeft(Offset.zero).dy, yPosition);
}
for (int taskIndex = 0; taskIndex < taskMatrix.columns; taskIndex++) {
// Task icon indexes are one off because of the gridIndex having to
// account for the first column of commit boxes.
expect(find.byKey(Key('taskicon-${taskIndex + 1}')), findsOneWidget);
}
});
testWidgets('loader row is created', (WidgetTester tester) async {
final CocoonResponse<List<CommitStatus>> response =
await service.fetchCommitStatuses();
final List<CommitStatus> smallRangeOfStatusesToShowLoader =
response.data.getRange(0, 2).toList();
final TaskMatrix smallTaskMatrix =
TaskMatrix(statuses: smallRangeOfStatusesToShowLoader);
await tester.pumpWidget(
MaterialApp(
home: Column(
children: <Widget>[
StatusGrid(
buildState: FakeFlutterBuildState(),
statuses: smallRangeOfStatusesToShowLoader,
taskMatrix: smallTaskMatrix,
insertCellKeys: true,
),
],
),
),
);
// TODO(chillers): Remove this web check once issue is resolved. https://github.com/flutter/flutter/issues/44370
if (!kIsWeb) {
expect(tester.takeException(),
const test.TypeMatcher<NetworkImageLoadException>());
}
/// Loader containers show 1 extra to account for the commit box
/// column on the left of the grid.
for (int index = 0; index < taskMatrix.columns + 1; index++) {
expect(find.byKey(Key('loader-$index')), findsOneWidget);
}
});
});
}