Unverified Commit a6f375ab authored by Alex's avatar Alex Committed by GitHub

Release Desktop UI Repo Info Widget (#91769)

parent ae19c8f6
......@@ -55,7 +55,7 @@ class MyApp extends StatelessWidget {
const SelectableText(
'Desktop app for managing a release of the Flutter SDK, currently in development',
),
const SizedBox(height: 20.0),
const SizedBox(height: 10.0),
MainProgression(
releaseState: state,
stateFilePath: _stateFilePath,
......
......@@ -28,6 +28,22 @@ class ConductorStatus extends StatefulWidget {
'Release Updated at',
'Dart SDK Revision',
];
static final List<String> engineRepoElements = <String>[
'Engine Candidate Branch',
'Engine Starting Git HEAD',
'Engine Current Git HEAD',
'Engine Path to Checkout',
'Engine LUCI Dashboard',
];
static final List<String> frameworkRepoElements = <String>[
'Framework Candidate Branch',
'Framework Starting Git HEAD',
'Framework Current Git HEAD',
'Framework Path to Checkout',
'Framework LUCI Dashboard',
];
}
class ConductorStatusState extends State<ConductorStatus> {
......@@ -86,7 +102,6 @@ class ConductorStatusState extends State<ConductorStatus> {
Table(
columnWidths: const <int, TableColumnWidth>{
0: FixedColumnWidth(200.0),
1: FixedColumnWidth(400.0),
},
children: <TableRow>[
for (String headerElement in ConductorStatus.headerElements)
......@@ -105,19 +120,23 @@ class ConductorStatusState extends State<ConductorStatus> {
children: <Widget>[
Column(
children: <Widget>[
RepoInfoExpansion(engineOrFramework: 'engine', currentStatus: currentStatus),
const SizedBox(height: 10.0),
CherrypickTable(engineOrFramework: 'engine', currentStatus: currentStatus),
],
),
const SizedBox(width: 20.0),
Column(
children: <Widget>[
RepoInfoExpansion(engineOrFramework: 'framework', currentStatus: currentStatus),
const SizedBox(height: 10.0),
CherrypickTable(engineOrFramework: 'framework', currentStatus: currentStatus),
],
),
],
)
],
)
),
],
);
}
......@@ -208,3 +227,80 @@ class CherrypickTableState extends State<CherrypickTable> {
);
}
}
/// Widget to display repo info related to the engine and framework.
///
/// Click to show/hide the repo info in a dropdown fashion. By default the section is hidden.
class RepoInfoExpansion extends StatefulWidget {
const RepoInfoExpansion({
Key? key,
required this.engineOrFramework,
required this.currentStatus,
}) : super(key: key);
final String engineOrFramework;
final Map<String, Object> currentStatus;
@override
RepoInfoExpansionState createState() => RepoInfoExpansionState();
}
class RepoInfoExpansionState extends State<RepoInfoExpansion> {
bool _isExpanded = false;
/// Show/hide [ExpansionPanel].
void showHide() {
setState(() {
_isExpanded = !_isExpanded;
});
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: 500.0,
child: ExpansionPanelList(
expandedHeaderPadding: EdgeInsets.zero,
expansionCallback: (int index, bool isExpanded) {
showHide();
},
children: <ExpansionPanel>[
ExpansionPanel(
isExpanded: _isExpanded,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
key: Key('${widget.engineOrFramework}RepoInfoDropdown'),
title: Text('${widget.engineOrFramework == 'engine' ? 'Engine' : 'Framework'} Repo Info'),
onTap: () {
showHide();
});
},
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Table(
columnWidths: const <int, TableColumnWidth>{
0: FixedColumnWidth(240.0),
},
children: <TableRow>[
for (String repoElement in widget.engineOrFramework == 'engine'
? ConductorStatus.engineRepoElements
: ConductorStatus.frameworkRepoElements)
TableRow(
decoration: const BoxDecoration(border: Border(top: BorderSide(color: Colors.grey))),
children: <Widget>[
Text('$repoElement:'),
SelectableText(
(widget.currentStatus[repoElement] == null || widget.currentStatus[repoElement] == '')
? 'Unknown'
: widget.currentStatus[repoElement]! as String),
],
),
],
),
),
),
],
),
);
}
}
......@@ -37,7 +37,7 @@ class MainProgression extends StatefulWidget {
class MainProgressionState extends State<MainProgression> {
int _completedStep = 0;
// Move forward the stepper to the next step of the release.
/// Move forward the stepper to the next step of the release.
void nextStep() {
if (_completedStep < MainProgression._stepTitles.length - 1) {
setState(() {
......@@ -74,6 +74,7 @@ class MainProgressionState extends State<MainProgression> {
releaseState: widget.releaseState,
stateFilePath: widget.stateFilePath,
),
const SizedBox(height: 20.0),
Stepper(
controlsBuilder: (BuildContext context, ControlsDetails details) => Row(),
physics: const ScrollPhysics(),
......
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:conductor_core/conductor_core.dart';
import 'package:conductor_core/proto.dart' as pb;
import 'package:conductor_ui/widgets/conductor_status.dart';
import 'package:flutter/gestures.dart';
......@@ -10,51 +11,32 @@ import 'package:flutter_test/flutter_test.dart';
void main() {
group('conductor_status', () {
testWidgets('Conductor_status displays nothing found when there is no state file', (WidgetTester tester) async {
const String testPath = './testPath';
await tester.pumpWidget(
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return MaterialApp(
home: Material(
child: Column(
children: const <Widget>[
ConductorStatus(
stateFilePath: testPath,
),
],
),
),
);
},
),
);
late pb.ConductorState state;
expect(find.text('No persistent state file found at $testPath'), findsOneWidget);
expect(find.text('Conductor version:'), findsNothing);
});
const String testPath = './testPath';
const String conductorVersion = 'v1.0';
const String releaseChannel = 'beta';
const String releaseVersion = '1.2.0-3.4.pre';
const String engineCandidateBranch = 'flutter-1.2-candidate.3';
const String frameworkCandidateBranch = 'flutter-1.2-candidate.4';
const String workingBranch = 'cherrypicks-$engineCandidateBranch';
const String dartRevision = 'fe9708ab688dcda9923f584ba370a66fcbc3811f';
const String engineCherrypick1 = 'a5a25cd702b062c24b2c67b8d30b5cb33e0ef6f0';
const String engineCherrypick2 = '94d06a2e1d01a3b0c693b94d70c5e1df9d78d249';
const String frameworkCherrypick = '768cd702b691584b2c67b8d30b5cb33e0ef6f0';
const String engineStartingGitHead = '083049e6cae311910c6a6619a6681b7eba4035b4';
const String engineCurrentGitHead = '23otn2o3itn2o3int2oi3tno23itno2i3tn';
const String engineCheckoutPath = '/Users/alexchen/Desktop/flutter_conductor_checkouts/engine';
const String frameworkStartingGitHead = 'df6981e98rh49er8h149er8h19er8h1';
const String frameworkCurrentGitHead = '239tnint023t09j2039tj0239tn';
const String frameworkCheckoutPath = '/Users/alexchen/Desktop/flutter_conductor_checkouts/framework';
final String engineLUCIDashboard = luciConsoleLink(releaseChannel, 'engine');
final String frameworkLUCIDashboard = luciConsoleLink(releaseChannel, 'flutter');
testWidgets('Conductor_status displays correct status with a state file', (WidgetTester tester) async {
const String testPath = './testPath';
const String conductorVersion = 'v1.0';
const String releaseChannel = 'beta';
const String releaseVersion = '1.2.0-3.4.pre';
const String candidateBranch = 'flutter-1.2-candidate.3';
const String workingBranch = 'cherrypicks-$candidateBranch';
const String dartRevision = 'fe9708ab688dcda9923f584ba370a66fcbc3811f';
const String engineCherrypick1 = 'a5a25cd702b062c24b2c67b8d30b5cb33e0ef6f0';
const String engineCherrypick2 = '94d06a2e1d01a3b0c693b94d70c5e1df9d78d249';
const String frameworkCherrypick = '768cd702b691584b2c67b8d30b5cb33e0ef6f0';
const String engineStartingGitHead = '083049e6cae311910c6a6619a6681b7eba4035b4';
const String engineCurrentGitHead = '083049e6cae311910c6a6619a6681b7eba4035b4';
const String engineCheckoutPath = '/Users/alexchen/Desktop/flutter_conductor_checkouts/engine';
const String frameworkStartingGitHead = '083049e6cae311910c6a6619a6681b7eba4035b4';
const String frameworkCurrentGitHead = '083049e6cae311910c6a6619a6681b7eba4035b4';
const String frameworkCheckoutPath = '/Users/alexchen/Desktop/flutter_conductor_checkouts/framework';
final pb.ConductorState state = pb.ConductorState(
setUp(() {
state = pb.ConductorState(
engine: pb.Repository(
candidateBranch: candidateBranch,
candidateBranch: engineCandidateBranch,
cherrypicks: <pb.Cherrypick>[
pb.Cherrypick(trunkRevision: engineCherrypick1),
pb.Cherrypick(trunkRevision: engineCherrypick2),
......@@ -66,7 +48,7 @@ void main() {
checkoutPath: engineCheckoutPath,
),
framework: pb.Repository(
candidateBranch: candidateBranch,
candidateBranch: frameworkCandidateBranch,
cherrypicks: <pb.Cherrypick>[
pb.Cherrypick(trunkRevision: frameworkCherrypick),
],
......@@ -79,13 +61,38 @@ void main() {
releaseChannel: releaseChannel,
releaseVersion: releaseVersion,
);
});
testWidgets('Conductor_status displays nothing found when there is no state file', (WidgetTester tester) async {
const String testPath = './testPath';
await tester.pumpWidget(
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return MaterialApp(
home: Material(
child: ListView(
children: const <Widget>[
ConductorStatus(
stateFilePath: testPath,
),
],
),
),
);
},
),
);
expect(find.text('No persistent state file found at $testPath'), findsOneWidget);
expect(find.text('Conductor version:'), findsNothing);
});
testWidgets('Conductor_status displays correct status with a state file', (WidgetTester tester) async {
await tester.pumpWidget(
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return MaterialApp(
home: Material(
child: Column(
child: ListView(
children: <Widget>[
ConductorStatus(
releaseState: state,
......@@ -130,9 +137,7 @@ void main() {
testWidgets('Conductor_status displays correct status with a null state file except a releaseChannel',
(WidgetTester tester) async {
const String testPath = './testPath';
const String releaseChannel = 'beta';
final pb.ConductorState state = pb.ConductorState(
final pb.ConductorState stateIncomplete = pb.ConductorState(
releaseChannel: releaseChannel,
);
......@@ -141,10 +146,10 @@ void main() {
builder: (BuildContext context, StateSetter setState) {
return MaterialApp(
home: Material(
child: Column(
child: ListView(
children: <Widget>[
ConductorStatus(
releaseState: state,
releaseState: stateIncomplete,
stateFilePath: testPath,
),
],
......@@ -160,7 +165,7 @@ void main() {
expect(find.text('$headerElement:'), findsOneWidget);
}
expect(find.text(releaseChannel), findsOneWidget);
expect(find.text('Unknown'), findsNWidgets(3));
expect(find.text('Unknown'), findsNWidgets(11));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
addTearDown(gesture.removePointer);
......@@ -176,5 +181,52 @@ void main() {
await tester.pumpAndSettle();
expect(find.textContaining('PENDING: The cherrypick has not yet been applied.'), findsOneWidget);
});
testWidgets('Repo Info section displays corresponding info in a dropdown fashion', (WidgetTester tester) async {
await tester.pumpWidget(
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return MaterialApp(
home: Material(
child: ListView(
children: <Widget>[
ConductorStatus(
releaseState: state,
stateFilePath: testPath,
),
],
),
),
);
},
),
);
expect(find.text('No persistent state file found at $testPath'), findsNothing);
for (final String repoElement in ConductorStatus.engineRepoElements) {
expect(find.text('$repoElement:'), findsOneWidget);
}
for (final String repoElement in ConductorStatus.frameworkRepoElements) {
expect(find.text('$repoElement:'), findsOneWidget);
}
expect(find.text(engineCandidateBranch), findsOneWidget);
expect(find.text(engineStartingGitHead), findsOneWidget);
expect(find.text(engineCurrentGitHead), findsOneWidget);
expect(find.text(engineCheckoutPath), findsOneWidget);
expect(find.text(engineLUCIDashboard), findsOneWidget);
expect(find.text(frameworkCandidateBranch), findsOneWidget);
expect(find.text(frameworkStartingGitHead), findsOneWidget);
expect(find.text(frameworkCurrentGitHead), findsOneWidget);
expect(find.text(frameworkCheckoutPath), findsOneWidget);
expect(find.text(frameworkLUCIDashboard), findsOneWidget);
expect(tester.widget<ExpansionPanelList>(find.byType(ExpansionPanelList).first).children[0].isExpanded,
equals(false));
await tester.tap(find.byKey(const Key('engineRepoInfoDropdown')));
await tester.pumpAndSettle();
expect(tester.widget<ExpansionPanelList>(find.byType(ExpansionPanelList).first).children[0].isExpanded,
equals(true));
});
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment