Skip to content

Commit 8e405f3

Browse files
authored
Fix deep links page displaying incorrect Windows path formats + a crash if no iOS config (#9027)
This fixes some of the issues noted in #7747, but not all of them (see my comment at #7747 (comment)).
1 parent 88fe865 commit 8e405f3

4 files changed

Lines changed: 69 additions & 10 deletions

File tree

packages/devtools_app/lib/src/screens/deep_link_validation/project_root_selection/root_selector.dart

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,27 @@ class _ProjectRootsDropdownState extends State<ProjectRootsDropdown> {
9393
selectedUri = widget.projectRoots.safeFirst;
9494
}
9595

96+
/// A regex that can be matched against the `path` of a URI to check whether
97+
/// it is a Windows file URI.
98+
final _fileUriWindowsPath = RegExp(r'^/[a-zA-Z](?::|%3A|%3a)');
99+
100+
/// Gets the file path from a file:/// URI taking into account the platform
101+
/// for the path.
102+
String toPath(Uri uri) {
103+
assert(uri.isScheme('file'));
104+
105+
// .toFilePath() on web always assumes non-Windows even if the file:/// URI
106+
// is Windows, so we need to check whether this is a Windows file path in
107+
// the URI first.
108+
final isWindows = _fileUriWindowsPath.hasMatch(uri.path);
109+
return uri.toFilePath(windows: isWindows);
110+
}
111+
96112
@override
97113
Widget build(BuildContext context) {
114+
final selectedUri = this.selectedUri;
98115
return _FlexibleProjectSelectionView(
99-
selectedProjectRoot: selectedUri?.path.trim(),
116+
selectedProjectRoot: selectedUri != null ? toPath(selectedUri) : null,
100117
onValidatePressed: widget.onValidatePressed,
101118
child: RoundedDropDownButton<Uri>(
102119
isDense: true,
@@ -105,7 +122,7 @@ class _ProjectRootsDropdownState extends State<ProjectRootsDropdown> {
105122
items: [for (final uri in widget.projectRoots) _buildMenuItem(uri)],
106123
onChanged:
107124
(uri) => setState(() {
108-
selectedUri = uri;
125+
this.selectedUri = uri;
109126
}),
110127
),
111128
);
@@ -115,9 +132,9 @@ class _ProjectRootsDropdownState extends State<ProjectRootsDropdown> {
115132
return DropdownMenuItem<Uri>(
116133
value: uri,
117134
child: DevToolsTooltip(
118-
message: uri.path,
135+
message: toPath(uri),
119136
waitDuration: tooltipWaitExtraLong,
120-
child: Text(uri.path, overflow: TextOverflow.ellipsis),
137+
child: Text(toPath(uri), overflow: TextOverflow.ellipsis),
121138
),
122139
);
123140
}

packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ TODO: Remove this section if there are not any general updates.
7777

7878
## Deep links tool updates
7979

80-
TODO: Remove this section if there are not any general updates.
80+
* Fixed an issue with Windows file paths showing incorrectly in the Deep Links
81+
page [#9027](https://github.com/flutter/devtools/pull/9027).
82+
* Fixed an issue with the Deep Links page crashing when no iOS configuration is
83+
present [#9027](https://github.com/flutter/devtools/pull/9027).
8184

8285
## VS Code Sidebar updates
8386

packages/devtools_app/test/screens/deep_link_validation/deep_links_screen_test.dart

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
44

5+
import 'dart:io';
6+
57
import 'package:devtools_app/devtools_app.dart';
68
import 'package:devtools_app/src/screens/deep_link_validation/deep_link_list_view.dart';
79
import 'package:devtools_app/src/screens/deep_link_validation/deep_links_model.dart';
@@ -27,6 +29,13 @@ final xcodeBuildOptions = XcodeBuildOptions.fromJson(
2729
'''{"configurations":["debug", "release"],"targets":["runner","runnerTests"]}''',
2830
);
2931

32+
final mockProjectRootUris =
33+
(Platform.isWindows
34+
? [r'C:\Users\me\package_root_1', r'C:\Users\me\package_root_2']
35+
: ['/Users/me/package_root_1', '/Users/me/package_root_2'])
36+
.map(Uri.file)
37+
.toList();
38+
3039
void main() {
3140
// ignore: avoid-redundant-async, false positive.
3241
setUp(() async {
@@ -40,10 +49,8 @@ void main() {
4049
setGlobal(NotificationService, NotificationService());
4150

4251
final mockDtdManager = MockDTDManager();
43-
final rootUri1 = Uri.parse('file:///Users/me/package_root_1');
44-
final rootUri2 = Uri.parse('file:///Users/me/package_root_2');
4552
when(mockDtdManager.projectRoots()).thenAnswer((_) async {
46-
return UriList(uris: [rootUri1, rootUri2]);
53+
return UriList(uris: mockProjectRootUris);
4754
});
4855
setGlobal(DTDManager, mockDtdManager);
4956
FeatureFlags.deepLinkIosCheck = true;
@@ -91,6 +98,37 @@ void main() {
9198
expect(find.byType(ProjectRootTextField), findsOneWidget);
9299
});
93100

101+
testWidgetsWithWindowSize(
102+
'populates project drop-down with the correct paths',
103+
windowSize,
104+
(WidgetTester tester) async {
105+
await pumpDeepLinkScreen(tester, controller: deepLinksController);
106+
107+
expect(find.byType(SelectProjectView), findsOneWidget);
108+
expect(find.byType(ProjectRootsDropdown), findsOneWidget);
109+
expect(find.byType(ProjectRootTextField), findsOneWidget);
110+
111+
// Verify the project roots dropdown contains paths in the correct format.
112+
final firstDropdownItemFinder =
113+
find.byType(DropdownMenuItem<Uri>).first;
114+
expect(firstDropdownItemFinder, findsWidgets);
115+
final firstDropdownItem = tester.widget<DropdownMenuItem<Uri>>(
116+
firstDropdownItemFinder,
117+
);
118+
// The value should be the URI.
119+
expect(firstDropdownItem.value, mockProjectRootUris.first);
120+
// The text should be the path. We can use toFilePath() here because this
121+
// code runs on the native platform (eg. Windows) and not inside the app.
122+
expect(
123+
find.descendant(
124+
of: firstDropdownItemFinder,
125+
matching: find.text(mockProjectRootUris.first.toFilePath()),
126+
),
127+
findsWidgets,
128+
);
129+
},
130+
);
131+
94132
testWidgetsWithWindowSize(
95133
'builds deeplink list page with no links',
96134
windowSize,

packages/devtools_shared/lib/src/deeplink/xcode_build_options.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ extension type const XcodeBuildOptions._(Map<String, Object?> _json) {
1818

1919
/// The available configurations for iOS build of this Flutter project.
2020
List<String> get configurations =>
21-
(_json[_kConfigurationsKey] as List).cast<String>();
21+
(_json[_kConfigurationsKey] as List?)?.cast<String>() ?? [];
2222

2323
/// The available targets for iOS build of this Flutter project.
24-
List<String> get targets => (_json[_kTargetsKey] as List).cast<String>();
24+
List<String> get targets =>
25+
(_json[_kTargetsKey] as List?)?.cast<String>() ?? [];
2526
}

0 commit comments

Comments
 (0)