diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index 619aa769d5f6..4caad4b975f9 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.8.0 + +- Migrated to null-safety + # 0.7.0+1 - Add dummy `ios` dir, so flutter sdk can be lower than 1.20 diff --git a/packages/file_selector/file_selector_web/example/README.md b/packages/file_selector/file_selector_web/example/README.md new file mode 100644 index 000000000000..6187e55841c9 --- /dev/null +++ b/packages/file_selector/file_selector_web/example/README.md @@ -0,0 +1,21 @@ +# Testing + +This package utilizes the `integration_test` package to run its tests in a web browser. + +See [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) for more info. + +## Running the tests + +Make sure you have updated to the latest Flutter master. + +1. Check what version of Chrome is running on the machine you're running tests on. + +2. Download and install driver for that version from here: + * + +3. Start the driver using `chromedriver --port=4444` + +4. Run tests: `flutter drive -d web-server --browser-name=chrome --driver=test_driver/integration_test.dart --target=integration_test/TEST_NAME.dart`, or (in Linux): + + * Single: `./run_test.sh integration_test/TEST_NAME.dart` + * All: `./run_test.sh` diff --git a/packages/file_selector/file_selector_web/integration_test/dom_helper_test.dart b/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart similarity index 91% rename from packages/file_selector/file_selector_web/integration_test/dom_helper_test.dart rename to packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart index a942c0db10bf..274aed93659e 100644 --- a/packages/file_selector/file_selector_web/integration_test/dom_helper_test.dart +++ b/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.9 - import 'dart:html'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -11,19 +9,19 @@ import 'package:file_selector_web/src/dom_helper.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; void main() { - group('FileSelectorWeb', () { + group('dom_helper', () { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - DomHelper domHelper; - FileUploadInputElement input; + late DomHelper domHelper; + late FileUploadInputElement input; - FileList FileListItems(List files) { + FileList? createFileList(List files) { final dataTransfer = DataTransfer(); - files.forEach(dataTransfer.items.add); - return dataTransfer.files; + files.forEach(dataTransfer.items!.add); + return dataTransfer.files as FileList?; } void setFilesAndTriggerChange(List files) { - input.files = FileListItems(files); + input.files = createFileList(files); input.dispatchEvent(Event('change')); } diff --git a/packages/file_selector/file_selector_web/integration_test/file_selector_web_test.dart b/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart similarity index 59% rename from packages/file_selector/file_selector_web/integration_test/file_selector_web_test.dart rename to packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart index abd31dd9fcc6..5442fedf5408 100644 --- a/packages/file_selector/file_selector_web/integration_test/file_selector_web_test.dart +++ b/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart @@ -2,11 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.9 - +import 'dart:html'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; import 'package:integration_test/integration_test.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_web/file_selector_web.dart'; @@ -15,18 +13,18 @@ import 'package:file_selector_web/src/dom_helper.dart'; void main() { group('FileSelectorWeb', () { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - MockDomHelper mockDomHelper; - FileSelectorWeb plugin; - - setUp(() { - mockDomHelper = MockDomHelper(); - plugin = FileSelectorWeb(domHelper: mockDomHelper); - }); group('openFile', () { - final mockFile = createXFile('1001', 'identity.png'); - testWidgets('works', (WidgetTester _) async { + final mockFile = createXFile('1001', 'identity.png'); + + final mockDomHelper = MockDomHelper() + ..setFiles([mockFile]) + ..expectAccept('.jpg,.jpeg,image/png,image/*') + ..expectMultiple(false); + + final plugin = FileSelectorWeb(domHelper: mockDomHelper); + final typeGroup = XTypeGroup( label: 'images', extensions: ['jpg', 'jpeg'], @@ -34,11 +32,6 @@ void main() { webWildCards: ['image/*'], ); - when(mockDomHelper.getFiles( - accept: '.jpg,.jpeg,image/png,image/*', - multiple: false, - )).thenAnswer((_) async => [mockFile]); - final file = await plugin.openFile(acceptedTypeGroups: [typeGroup]); expect(file.name, mockFile.name); @@ -49,20 +42,22 @@ void main() { }); group('openFiles', () { - final mockFile1 = createXFile('123456', 'file1.txt'); - final mockFile2 = createXFile('', 'file2.txt'); - testWidgets('works', (WidgetTester _) async { + final mockFile1 = createXFile('123456', 'file1.txt'); + final mockFile2 = createXFile('', 'file2.txt'); + + final mockDomHelper = MockDomHelper() + ..setFiles([mockFile1, mockFile2]) + ..expectAccept('.txt') + ..expectMultiple(true); + + final plugin = FileSelectorWeb(domHelper: mockDomHelper); + final typeGroup = XTypeGroup( label: 'files', extensions: ['.txt'], ); - when(mockDomHelper.getFiles( - accept: '.txt', - multiple: true, - )).thenAnswer((_) async => [mockFile1, mockFile2]); - final files = await plugin.openFiles(acceptedTypeGroups: [typeGroup]); expect(files.length, 2); @@ -81,7 +76,36 @@ void main() { }); } -class MockDomHelper extends Mock implements DomHelper {} +class MockDomHelper implements DomHelper { + List _files = []; + String _expectedAccept = ''; + bool _expectedMultiple = false; + + @override + Future> getFiles({ + String accept = '', + bool multiple = false, + FileUploadInputElement? input, + }) { + expect(accept, _expectedAccept, + reason: 'Expected "accept" value does not match.'); + expect(multiple, _expectedMultiple, + reason: 'Expected "multiple" value does not match.'); + return Future.value(_files); + } + + void setFiles(List files) { + _files = files; + } + + void expectAccept(String accept) { + _expectedAccept = accept; + } + + void expectMultiple(bool multiple) { + _expectedMultiple = multiple; + } +} XFile createXFile(String content, String name) { final data = Uint8List.fromList(content.codeUnits); diff --git a/packages/file_selector/file_selector_web/example/lib/main.dart b/packages/file_selector/file_selector_web/example/lib/main.dart new file mode 100644 index 000000000000..e1a38dcdcd46 --- /dev/null +++ b/packages/file_selector/file_selector_web/example/lib/main.dart @@ -0,0 +1,25 @@ +// Copyright 2013 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 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +/// App for testing +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => _MyAppState(); +} + +class _MyAppState extends State { + @override + Widget build(BuildContext context) { + return Directionality( + textDirection: TextDirection.ltr, + child: Text('Testing... Look at the console output for results!'), + ); + } +} diff --git a/packages/file_selector/file_selector_web/example/pubspec.yaml b/packages/file_selector/file_selector_web/example/pubspec.yaml new file mode 100644 index 000000000000..cae4b13a8207 --- /dev/null +++ b/packages/file_selector/file_selector_web/example/pubspec.yaml @@ -0,0 +1,21 @@ +name: file_selector_web_integration_tests +publish_to: none + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + build_runner: ^1.10.0 + file_selector_web: + path: ../ + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +environment: + sdk: ">=2.12.0-259.9.beta <3.0.0" + flutter: ">=1.27.0-0" # For integration_test from sdk diff --git a/packages/file_selector/file_selector_web/run_integration_test b/packages/file_selector/file_selector_web/example/run_test.sh similarity index 100% rename from packages/file_selector/file_selector_web/run_integration_test rename to packages/file_selector/file_selector_web/example/run_test.sh diff --git a/packages/file_selector/file_selector_web/test_driver/integration_test.dart b/packages/file_selector/file_selector_web/example/test_driver/integration_test.dart similarity index 100% rename from packages/file_selector/file_selector_web/test_driver/integration_test.dart rename to packages/file_selector/file_selector_web/example/test_driver/integration_test.dart diff --git a/packages/file_selector/file_selector_web/example/web/index.html b/packages/file_selector/file_selector_web/example/web/index.html new file mode 100644 index 000000000000..dc8d0cfe0428 --- /dev/null +++ b/packages/file_selector/file_selector_web/example/web/index.html @@ -0,0 +1,12 @@ + + + + + Browser Tests + + + + + diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 48f57ee880c8..1c411ca0a2f0 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -13,7 +13,7 @@ import 'package:file_selector_web/src/utils.dart'; /// /// This class implements the `package:file_selector` functionality for the web. class FileSelectorWeb extends FileSelectorPlatform { - final _domHelper; + final DomHelper _domHelper; /// Registers this class as the default instance of [FileSelectorPlatform]. static void registerWith(Registrar registrar) { @@ -23,14 +23,14 @@ class FileSelectorWeb extends FileSelectorPlatform { /// Default constructor, initializes _domHelper that we can use /// to interact with the DOM. /// overrides parameter allows for testing to override functions - FileSelectorWeb({@visibleForTesting DomHelper domHelper}) + FileSelectorWeb({@visibleForTesting DomHelper? domHelper}) : _domHelper = domHelper ?? DomHelper(); @override Future openFile({ - List acceptedTypeGroups, - String initialDirectory, - String confirmButtonText, + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, }) async { final files = await _openFiles(acceptedTypeGroups: acceptedTypeGroups); return files.first; @@ -38,31 +38,31 @@ class FileSelectorWeb extends FileSelectorPlatform { @override Future> openFiles({ - List acceptedTypeGroups, - String initialDirectory, - String confirmButtonText, + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, }) async { return _openFiles(acceptedTypeGroups: acceptedTypeGroups, multiple: true); } @override - Future getSavePath({ - List acceptedTypeGroups, - String initialDirectory, - String suggestedName, - String confirmButtonText, + Future getSavePath({ + List? acceptedTypeGroups, + String? initialDirectory, + String? suggestedName, + String? confirmButtonText, }) async => null; @override - Future getDirectoryPath({ - String initialDirectory, - String confirmButtonText, + Future getDirectoryPath({ + String? initialDirectory, + String? confirmButtonText, }) async => null; Future> _openFiles({ - List acceptedTypeGroups, + List? acceptedTypeGroups, bool multiple = false, }) async { final accept = acceptedTypesToString(acceptedTypeGroups); diff --git a/packages/file_selector/file_selector_web/lib/src/dom_helper.dart b/packages/file_selector/file_selector_web/lib/src/dom_helper.dart index a965cebe97f9..5c578b6f4639 100644 --- a/packages/file_selector/file_selector_web/lib/src/dom_helper.dart +++ b/packages/file_selector/file_selector_web/lib/src/dom_helper.dart @@ -14,7 +14,7 @@ class DomHelper { /// Default constructor, initializes the container DOM element. DomHelper() { - final body = querySelector('body'); + final body = querySelector('body')!; body.children.add(_container); } @@ -22,42 +22,45 @@ class DomHelper { Future> getFiles({ String accept = '', bool multiple = false, - @visibleForTesting FileUploadInputElement input, + @visibleForTesting FileUploadInputElement? input, }) { - final Completer> _completer = Completer(); - input = input ?? FileUploadInputElement(); + final Completer> completer = Completer(); + final FileUploadInputElement inputElement = + input ?? FileUploadInputElement(); _container.children.add( - input + inputElement ..accept = accept ..multiple = multiple, ); - input.onChange.first.then((_) { - final List files = input.files.map(_convertFileToXFile).toList(); - input.remove(); - _completer.complete(files); + inputElement.onChange.first.then((_) { + final List files = + inputElement.files!.map(_convertFileToXFile).toList(); + inputElement.remove(); + completer.complete(files); }); - input.onError.first.then((event) { - final ErrorEvent error = event; + inputElement.onError.first.then((event) { + final ErrorEvent error = event as ErrorEvent; final platformException = PlatformException( code: error.type, message: error.message, ); - input.remove(); - _completer.completeError(platformException); + inputElement.remove(); + completer.completeError(platformException); }); - input.click(); + inputElement.click(); - return _completer.future; + return completer.future; } XFile _convertFileToXFile(File file) => XFile( Url.createObjectUrl(file), name: file.name, length: file.size, - lastModified: DateTime.fromMillisecondsSinceEpoch(file.lastModified), + lastModified: DateTime.fromMillisecondsSinceEpoch( + file.lastModified ?? DateTime.now().millisecondsSinceEpoch), ); } diff --git a/packages/file_selector/file_selector_web/lib/src/utils.dart b/packages/file_selector/file_selector_web/lib/src/utils.dart index 4ddd7ddcbda5..6be58c2aa0ec 100644 --- a/packages/file_selector/file_selector_web/lib/src/utils.dart +++ b/packages/file_selector/file_selector_web/lib/src/utils.dart @@ -5,19 +5,19 @@ import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; /// Convert list of XTypeGroups to a comma-separated string -String acceptedTypesToString(List acceptedTypes) { +String acceptedTypesToString(List? acceptedTypes) { if (acceptedTypes == null) return ''; final List allTypes = []; for (final group in acceptedTypes) { _assertTypeGroupIsValid(group); if (group.extensions != null) { - allTypes.addAll(group.extensions.map(_normalizeExtension)); + allTypes.addAll(group.extensions!.map(_normalizeExtension)); } if (group.mimeTypes != null) { - allTypes.addAll(group.mimeTypes); + allTypes.addAll(group.mimeTypes!); } if (group.webWildCards != null) { - allTypes.addAll(group.webWildCards); + allTypes.addAll(group.webWildCards!); } } return allTypes.join(','); @@ -26,9 +26,9 @@ String acceptedTypesToString(List acceptedTypes) { /// Make sure that at least one of its fields is populated. void _assertTypeGroupIsValid(XTypeGroup group) { assert( - !((group.extensions == null || group.extensions.isEmpty) && - (group.mimeTypes == null || group.mimeTypes.isEmpty) && - (group.webWildCards == null || group.webWildCards.isEmpty)), + !((group.extensions == null || group.extensions!.isEmpty) && + (group.mimeTypes == null || group.mimeTypes!.isEmpty) && + (group.webWildCards == null || group.webWildCards!.isEmpty)), 'At least one of extensions / mimeTypes / webWildCards is required for web.'); } diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index a170d5f39607..55424a7a4c1c 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -1,7 +1,7 @@ name: file_selector_web description: Web platform implementation of file_selector homepage: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_web -version: 0.7.0+1 +version: 0.8.0 flutter: plugin: @@ -11,22 +11,18 @@ flutter: fileName: file_selector_web.dart dependencies: - file_selector_platform_interface: ^1.0.2 - platform_detect: ^1.4.0 + file_selector_platform_interface: ^2.0.0 flutter: sdk: flutter flutter_web_plugins: sdk: flutter - meta: ^1.1.7 + meta: ^1.3.0 dev_dependencies: flutter_test: sdk: flutter - mockito: ^4.1.1 - pedantic: ^1.8.0 - integration_test: - path: ../../integration_test + pedantic: ^1.10.0 environment: - sdk: ">=2.2.0 <3.0.0" - flutter: ">=1.10.0" + sdk: ">=2.12.0-259.9.beta <3.0.0" + flutter: ">=1.20.0" diff --git a/packages/file_selector/file_selector_web/test/more_tests_exist_elsewhere_test.dart b/packages/file_selector/file_selector_web/test/more_tests_exist_elsewhere_test.dart new file mode 100644 index 000000000000..e9faa3af4808 --- /dev/null +++ b/packages/file_selector/file_selector_web/test/more_tests_exist_elsewhere_test.dart @@ -0,0 +1,10 @@ +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('Tell the user where to find the real tests', () { + print('---'); + print('This package also uses integration_test to run additional tests.'); + print('See `example/README.md` for more info.'); + print('---'); + }); +} diff --git a/packages/file_selector/file_selector_web/test/utils_test.dart b/packages/file_selector/file_selector_web/test/utils_test.dart index 9fa187eede5b..e3e47c00f176 100644 --- a/packages/file_selector/file_selector_web/test/utils_test.dart +++ b/packages/file_selector/file_selector_web/test/utils_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.9 - import 'package:flutter_test/flutter_test.dart'; import 'package:file_selector_web/src/utils.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';