From 3fc97b02ed7b3e7edd079904aa0ed2ab8337d056 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Thu, 17 Dec 2020 10:31:31 -0600 Subject: [PATCH 01/19] Add file_selector_web --- .../file_selector_web/CHANGELOG.md | 3 + .../file_selector/file_selector_web/LICENSE | 25 +++ .../file_selector/file_selector_web/README.md | 37 +++++ .../lib/file_selector_web.dart | 144 ++++++++++++++++++ .../file_selector_web/pubspec.yaml | 31 ++++ .../file_selector_web/test/lib/main.dart | 22 +++ .../file_selector_web/test/pubspec.yaml | 18 +++ .../file_selector_web/test/run_test | 17 +++ .../file_selector_web_integration.dart | 100 ++++++++++++ .../file_selector_web_integration_test.dart | 7 + .../file_selector_web/test/web/index.html | 12 ++ .../google_maps_flutter_web/test/pubspec.yaml | 1 - .../url_launcher_web_integration.dart | 1 - .../url_launcher_web/test/web/index.html | 14 +- 14 files changed, 423 insertions(+), 9 deletions(-) create mode 100644 packages/file_selector/file_selector_web/CHANGELOG.md create mode 100644 packages/file_selector/file_selector_web/LICENSE create mode 100644 packages/file_selector/file_selector_web/README.md create mode 100644 packages/file_selector/file_selector_web/lib/file_selector_web.dart create mode 100644 packages/file_selector/file_selector_web/pubspec.yaml create mode 100644 packages/file_selector/file_selector_web/test/lib/main.dart create mode 100644 packages/file_selector/file_selector_web/test/pubspec.yaml create mode 100755 packages/file_selector/file_selector_web/test/run_test create mode 100644 packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart create mode 100644 packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration_test.dart create mode 100644 packages/file_selector/file_selector_web/test/web/index.html diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md new file mode 100644 index 000000000000..cf87cfec36fd --- /dev/null +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.7.0 + +- Initial open-source release. diff --git a/packages/file_selector/file_selector_web/LICENSE b/packages/file_selector/file_selector_web/LICENSE new file mode 100644 index 000000000000..2c91f1438173 --- /dev/null +++ b/packages/file_selector/file_selector_web/LICENSE @@ -0,0 +1,25 @@ +Copyright 2020 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/file_selector/file_selector_web/README.md b/packages/file_selector/file_selector_web/README.md new file mode 100644 index 000000000000..e35424c8d320 --- /dev/null +++ b/packages/file_selector/file_selector_web/README.md @@ -0,0 +1,37 @@ +# file_picker_web + +The web implementation of [`file_picker`][1]. + +**Please set your constraint to `file_picker_web: '>=0.1.y+x <2.0.0'`** + +## Backward compatible 1.0.0 version is coming +The plugin has reached a stable API, we guarantee that version `1.0.0` will be backward compatible with `0.1.y+z`. +Please use `file_picker_web: '>=0.1.y+x <2.0.0'` as your dependency constraint to allow a smoother ecosystem migration. +For more details see: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 + +## Usage + +### Import the package +To use this plugin in your Flutter Web app, simply add it as a dependency in +your pubspec alongside the base `file_picker` plugin. + +_(This is only temporary: in the future we hope to make this package an +"endorsed" implementation of `file_picker`, so that it is automatically +included in your Flutter Web app when you depend on `package:file_picker`.)_ + +This is what the above means to your `pubspec.yaml`: + +```yaml +... +dependencies: + ... + file_picker: ^0.1.0 + file_picker_web: ^0.1.0 + ... +``` + +### Use the plugin +Once you have the `file_picker_web` dependency in your pubspec, you should +be able to use `package:file_picker` as normal. + +[1]: ../file_picker/file_picker 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 new file mode 100644 index 000000000000..65ba0b8262f1 --- /dev/null +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -0,0 +1,144 @@ +import 'dart:async'; +import 'dart:html'; +import 'package:meta/meta.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; + +/// The web implementation of [FileSelectorPlatform]. +/// +/// This class implements the `package:file_selector` functionality for the web. +class FileSelectorWeb extends FileSelectorPlatform { + final _container; + + /// Registers this class as the default instance of [FileSelectorPlatform]. + static void registerWith(Registrar registrar) { + FileSelectorPlatform.instance = FileSelectorWeb(); + } + + /// Default constructor, initializes _container to a DOM element that we can use + /// to host HTML elements. + /// overrides parameter allows for testing to override functions + FileSelectorWeb({@visibleForTesting Element container}) + : _container = container ?? Element.tag('file-selector') { + querySelector('body').children.add(_container); + } + + @override + Future openFile({ + List acceptedTypeGroups, + String initialDirectory, + String confirmButtonText, + }) async { + final files = await _openFiles(acceptedTypeGroups: acceptedTypeGroups); + return files.first; + } + + @override + Future> openFiles({ + List acceptedTypeGroups, + String initialDirectory, + String confirmButtonText, + }) async { + return _openFiles(acceptedTypeGroups: acceptedTypeGroups, multiple: true); + } + + @override + Future getSavePath({ + List acceptedTypeGroups, + String initialDirectory, + String suggestedName, + String confirmButtonText, + }) async => + null; + + @override + Future getDirectoryPath({ + String initialDirectory, + String confirmButtonText, + }) async => + null; + + Future> _openFiles({ + List acceptedTypeGroups, + bool multiple = false, + }) async { + final accept = _acceptedTypesToString(acceptedTypeGroups); + final input = _createInputElement() + ..accept = accept + ..multiple = multiple; + return _getFiles(input); + } + + /// Convert list of XTypeGroups to a comma-separated string + static 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)); + } + if (group.mimeTypes != null) { + allTypes.addAll(group.mimeTypes); + } + if (group.webWildCards != null) { + allTypes.addAll(group.webWildCards); + } + } + return allTypes.join(','); + } + + /// Creates a new input element and adds it to the cleared container. + FileUploadInputElement _createInputElement() { + final input = FileUploadInputElement()..id = 'file-selector-input'; + _container.children.clear(); + _container.children.add(input); + return input; + } + + /// For a given input, returns the files selected by an user. + static Future> _getFiles(FileUploadInputElement input) { + final Completer> _completer = Completer(); + + input.onChange.first.then((_) { + final List files = input.files.map(_convertFileToXFile).toList(); + _completer.complete(files); + }); + + input.onError.first.then((event) { + final ErrorEvent error = event; + final platformException = PlatformException( + code: error.type, + message: error.message, + ); + _completer.completeError(platformException); + }); + + input.click(); + + return _completer.future; + } + + /// Helper to convert an html.File to an XFile + static XFile _convertFileToXFile(File file) => XFile( + Url.createObjectUrl(file), + name: file.name, + length: file.size, + lastModified: DateTime.fromMillisecondsSinceEpoch(file.lastModified), + ); + + /// Make sure that at least one of its fields is populated. + static void _assertTypeGroupIsValid(XTypeGroup group) { + assert( + !((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.'); + } + + /// Append a dot at the beggining if it is not there png -> .png + static String _normalizeExtension(String ext) { + return ext.isNotEmpty && ext[0] != '.' ? '.' + ext : ext; + } +} diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml new file mode 100644 index 000000000000..eb046ef765f6 --- /dev/null +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -0,0 +1,31 @@ +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 + +flutter: + plugin: + platforms: + web: + pluginClass: FileSelectorWeb + fileName: file_selector_web.dart + +dependencies: + file_selector_platform_interface: ^1.0.0 + platform_detect: ^1.4.0 + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + meta: ^1.1.7 + +dev_dependencies: + flutter_test: + sdk: flutter + pedantic: ^1.8.0 + integration_test: + path: ../../integration_test + +environment: + sdk: ">=2.2.0 <3.0.0" + flutter: ">=1.10.0 <2.0.0" diff --git a/packages/file_selector/file_selector_web/test/lib/main.dart b/packages/file_selector/file_selector_web/test/lib/main.dart new file mode 100644 index 000000000000..10415204570c --- /dev/null +++ b/packages/file_selector/file_selector_web/test/lib/main.dart @@ -0,0 +1,22 @@ +// 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 Text('Testing... Look at the console output for results!'); + } +} diff --git a/packages/file_selector/file_selector_web/test/pubspec.yaml b/packages/file_selector/file_selector_web/test/pubspec.yaml new file mode 100644 index 000000000000..5543c0529040 --- /dev/null +++ b/packages/file_selector/file_selector_web/test/pubspec.yaml @@ -0,0 +1,18 @@ +name: file_selector_web_tests +publish_to: none + +environment: + sdk: ">=2.2.2 <3.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + mockito: ^4.1.1 + integration_test: + path: ../../../integration_test + file_selector_web: + path: ../ diff --git a/packages/file_selector/file_selector_web/test/run_test b/packages/file_selector/file_selector_web/test/run_test new file mode 100755 index 000000000000..74a8526a0fa3 --- /dev/null +++ b/packages/file_selector/file_selector_web/test/run_test @@ -0,0 +1,17 @@ +#!/usr/bin/bash +if pgrep -lf chromedriver > /dev/null; then + echo "chromedriver is running." + + if [ $# -eq 0 ]; then + echo "No target specified, running all tests..." + find test_driver/ -iname *_integration.dart | xargs -n1 -i -t flutter drive -d web-server --web-port=7357 --browser-name=chrome --target='{}' + else + echo "Running test target: $1..." + set -x + flutter drive -d web-server --web-port=7357 --browser-name=chrome --target=$1 + fi + + else + echo "chromedriver is not running." +fi + diff --git a/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart b/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart new file mode 100644 index 000000000000..ae9fffa1c8ee --- /dev/null +++ b/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart @@ -0,0 +1,100 @@ +// Copyright 2020 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. + +// @dart = 2.9 + +import 'dart:html'; +import 'package:flutter_test/flutter_test.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'; + +void main() { + group('FileSelectorWeb', () { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + FileSelectorWeb plugin; + Element container; + + setUp(() { + container = Element.div(); + plugin = FileSelectorWeb(container: container); + }); + + group('openFile', () { + final mockFile = File(['random content'], 'image.png'); + + testWidgets('works', (WidgetTester _) async { + final typeGroup = XTypeGroup( + label: 'images', + extensions: ['jpg', 'jpeg'], + mimeTypes: ['image/png'], + webWildCards: ['image/*'], + ); + + final futureFile = plugin.openFile(acceptedTypeGroups: [typeGroup]); + + setFilesAndTriggerChange(container, [mockFile]); + + final file = await futureFile; + final input = getInput(container); + + expect(input.accept, '.jpg,.jpeg,image/png,image/*'); + expect(input.multiple, false); + expect(file.name, mockFile.name); + expect(await file.length(), 14); + expect(await file.readAsString(), 'random content'); + expect(await file.lastModified(), isNotNull); + }); + }); + + group('openFiles', () { + final mockFile = File(['123456'], 'log.txt'); + + testWidgets('works', (WidgetTester _) async { + final txts = XTypeGroup( + label: 'txt', + mimeTypes: ['file/txt'], + ); + + final jsons = XTypeGroup( + label: 'JSON', + extensions: ['json'], + ); + + final futureFiles = plugin.openFiles(acceptedTypeGroups: [txts, jsons]); + + setFilesAndTriggerChange(container, [mockFile]); + + final files = await futureFiles; + final input = getInput(container); + + expect(input.accept, 'file/txt,.json'); + expect(input.multiple, true); + expect(files.length, 1); + expect(files[0].name, 'log.txt'); + expect(await files[0].length(), 6); + expect(await files[0].readAsString(), '123456'); + expect(await files[0].lastModified(), isNotNull); + }); + }); + }); +} + +void setFilesAndTriggerChange(Element container, List files) { + final input = getInput(container); + input.files = FileListItems(files); + input.dispatchEvent(Event('change')); +} + +FileUploadInputElement getInput(Element container) { + final input = container.children.first as FileUploadInputElement; + assert(input != null); + return input; +} + +FileList FileListItems(List files) { + final dt = DataTransfer(); + files.forEach((file) => dt.items.add(file)); + return dt.files; +} diff --git a/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration_test.dart b/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration_test.dart new file mode 100644 index 000000000000..1c5d1fcbcca7 --- /dev/null +++ b/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2020 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:integration_test/integration_test_driver.dart'; + +Future main() async => integrationDriver(); diff --git a/packages/file_selector/file_selector_web/test/web/index.html b/packages/file_selector/file_selector_web/test/web/index.html new file mode 100644 index 000000000000..402d9349ba8b --- /dev/null +++ b/packages/file_selector/file_selector_web/test/web/index.html @@ -0,0 +1,12 @@ + + + + + Browser Tests + + + + + diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml index 008cc0350430..a9350e109377 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml @@ -20,4 +20,3 @@ dev_dependencies: path: ../ integration_test: path: ../../../integration_test - diff --git a/packages/url_launcher/url_launcher_web/test/test_driver/url_launcher_web_integration.dart b/packages/url_launcher/url_launcher_web/test/test_driver/url_launcher_web_integration.dart index 4d103443deb9..f38aaaccd555 100644 --- a/packages/url_launcher/url_launcher_web/test/test_driver/url_launcher_web_integration.dart +++ b/packages/url_launcher/url_launcher_web/test/test_driver/url_launcher_web_integration.dart @@ -4,7 +4,6 @@ import 'dart:html' as html; import 'dart:js_util'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_web/url_launcher_web.dart'; diff --git a/packages/url_launcher/url_launcher_web/test/web/index.html b/packages/url_launcher/url_launcher_web/test/web/index.html index dc8d0cfe0428..402d9349ba8b 100644 --- a/packages/url_launcher/url_launcher_web/test/web/index.html +++ b/packages/url_launcher/url_launcher_web/test/web/index.html @@ -1,12 +1,12 @@ - + - - Browser Tests - - - - + + Browser Tests + + + + From 2a0b058ff0608cdeaf7fa0c6799a0b9ef6295ed6 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 22 Dec 2020 11:42:22 -0600 Subject: [PATCH 02/19] Refactor DomHelper --- .../lib/file_selector_web.dart | 72 ++++++------------- .../file_selector_web/lib/src/dom_helper.dart | 52 ++++++++++++++ .../test_driver/dom_helper_integration.dart | 50 +++++++++++++ .../dom_helper_integration_test.dart | 7 ++ .../file_selector_web_integration.dart | 69 +++--------------- 5 files changed, 142 insertions(+), 108 deletions(-) create mode 100644 packages/file_selector/file_selector_web/lib/src/dom_helper.dart create mode 100644 packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration.dart create mode 100644 packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration_test.dart 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 65ba0b8262f1..975b0c9cc498 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 @@ -1,28 +1,30 @@ +// Copyright 2020 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 'dart:html'; import 'package:meta/meta.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:file_selector_web/src/dom_helper.dart'; /// The web implementation of [FileSelectorPlatform]. /// /// This class implements the `package:file_selector` functionality for the web. class FileSelectorWeb extends FileSelectorPlatform { - final _container; + final _domHelper; /// Registers this class as the default instance of [FileSelectorPlatform]. static void registerWith(Registrar registrar) { FileSelectorPlatform.instance = FileSelectorWeb(); } - /// Default constructor, initializes _container to a DOM element that we can use - /// to host HTML elements. + /// Default constructor, initializes _domHelper that we can use + /// to interact with the DOM. /// overrides parameter allows for testing to override functions - FileSelectorWeb({@visibleForTesting Element container}) - : _container = container ?? Element.tag('file-selector') { - querySelector('body').children.add(_container); - } + FileSelectorWeb({@visibleForTesting DomHelper domHelper}) + : _domHelper = domHelper ?? DomHelper(); @override Future openFile({ @@ -64,10 +66,11 @@ class FileSelectorWeb extends FileSelectorPlatform { bool multiple = false, }) async { final accept = _acceptedTypesToString(acceptedTypeGroups); - final input = _createInputElement() - ..accept = accept - ..multiple = multiple; - return _getFiles(input); + final List files = await _domHelper.getFilesFromInput( + accept: accept, + multiple: multiple, + ); + return files.map(_convertFileToXFile).toList(); } /// Convert list of XTypeGroups to a comma-separated string @@ -89,35 +92,13 @@ class FileSelectorWeb extends FileSelectorPlatform { return allTypes.join(','); } - /// Creates a new input element and adds it to the cleared container. - FileUploadInputElement _createInputElement() { - final input = FileUploadInputElement()..id = 'file-selector-input'; - _container.children.clear(); - _container.children.add(input); - return input; - } - - /// For a given input, returns the files selected by an user. - static Future> _getFiles(FileUploadInputElement input) { - final Completer> _completer = Completer(); - - input.onChange.first.then((_) { - final List files = input.files.map(_convertFileToXFile).toList(); - _completer.complete(files); - }); - - input.onError.first.then((event) { - final ErrorEvent error = event; - final platformException = PlatformException( - code: error.type, - message: error.message, - ); - _completer.completeError(platformException); - }); - - input.click(); - - return _completer.future; + /// Make sure that at least one of its fields is populated. + static void _assertTypeGroupIsValid(XTypeGroup group) { + assert( + !((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.'); } /// Helper to convert an html.File to an XFile @@ -128,15 +109,6 @@ class FileSelectorWeb extends FileSelectorPlatform { lastModified: DateTime.fromMillisecondsSinceEpoch(file.lastModified), ); - /// Make sure that at least one of its fields is populated. - static void _assertTypeGroupIsValid(XTypeGroup group) { - assert( - !((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.'); - } - /// Append a dot at the beggining if it is not there png -> .png static String _normalizeExtension(String ext) { return ext.isNotEmpty && ext[0] != '.' ? '.' + ext : ext; 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 new file mode 100644 index 000000000000..c1ca389c1de8 --- /dev/null +++ b/packages/file_selector/file_selector_web/lib/src/dom_helper.dart @@ -0,0 +1,52 @@ +import 'dart:async'; +import 'dart:html'; +import 'package:meta/meta.dart'; +import 'package:flutter/services.dart'; + +/// Class to manipulate the DOM with the intention of reading files from it. +class DomHelper { + final FileUploadInputElement _input; + + /// Default constructor, initializes _input (an html element). + DomHelper({@visibleForTesting FileUploadInputElement input}) + : _input = input ?? FileUploadInputElement() { + final body = querySelector('body'); + final container = Element.tag('file-selector'); + body.children.add(container); + container.children.add(_input); + } + + /// Sets the attributes and waits for a file to be selected. + Future> getFilesFromInput({ + String accept = '', + bool multiple = false, + }) { + final Completer> _completer = Completer(); + StreamSubscription onChangeSubscription; + StreamSubscription onErrorSubscription; + + _input + ..accept = accept + ..multiple = multiple; + + onChangeSubscription = _input.onChange.listen((_) { + final List files = _input.files; + onChangeSubscription.cancel(); + _completer.complete(files); + }); + + onErrorSubscription = _input.onError.listen((event) { + final ErrorEvent error = event; + final platformException = PlatformException( + code: error.type, + message: error.message, + ); + onErrorSubscription.cancel(); + _completer.completeError(platformException); + }); + + _input.click(); + + return _completer.future; + } +} diff --git a/packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration.dart b/packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration.dart new file mode 100644 index 000000000000..2d5288a8d107 --- /dev/null +++ b/packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration.dart @@ -0,0 +1,50 @@ +// Copyright 2020 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. + +// @dart = 2.9 + +import 'dart:html'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:file_selector_web/src/dom_helper.dart'; +import 'package:pedantic/pedantic.dart'; +import 'package:mockito/mockito.dart'; + +void main() { + group('FileSelectorWeb', () { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + DomHelper domHelper; + MockInput mockInput; + + setUp(() { + mockInput = MockInput(); + domHelper = DomHelper(input: mockInput); + }); + + testWidgets('creates a container and an element', (_) async { + domHelper = DomHelper(); + final container = querySelector('file-selector'); + final input = querySelector('file-selector input'); + + expect(container, isNotNull); + expect(input, isNotNull); + }); + + group('getFilesFromInput', () { + testWidgets('sets the attributes and click it', (_) async { + final accept = '.png,.jpg,.txt,.json'; + final multiple = true; + + unawaited( + domHelper.getFilesFromInput(accept: accept, multiple: multiple)); + + expect(mockInput.accept, accept); + expect(mockInput.multiple, multiple); + verify(mockInput.click()); + }); + }); + }); +} + +class MockInput extends Mock implements FileUploadInputElement {} diff --git a/packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration_test.dart b/packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration_test.dart new file mode 100644 index 000000000000..1c5d1fcbcca7 --- /dev/null +++ b/packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2020 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:integration_test/integration_test_driver.dart'; + +Future main() async => integrationDriver(); diff --git a/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart b/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart index ae9fffa1c8ee..dd0760b893da 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart @@ -6,19 +6,21 @@ import 'dart:html'; 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'; +import 'package:file_selector_web/src/dom_helper.dart'; void main() { group('FileSelectorWeb', () { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + MockDomHelper mockDomHelper; FileSelectorWeb plugin; - Element container; setUp(() { - container = Element.div(); - plugin = FileSelectorWeb(container: container); + mockDomHelper = MockDomHelper(); + plugin = FileSelectorWeb(domHelper: mockDomHelper); }); group('openFile', () { @@ -32,69 +34,20 @@ void main() { webWildCards: ['image/*'], ); - final futureFile = plugin.openFile(acceptedTypeGroups: [typeGroup]); + when(mockDomHelper.getFilesFromInput( + accept: '.jpg,.jpeg,image/png,image/*', + multiple: false, + )).thenAnswer((_) async => [mockFile]); - setFilesAndTriggerChange(container, [mockFile]); + final file = await plugin.openFile(acceptedTypeGroups: [typeGroup]); - final file = await futureFile; - final input = getInput(container); - - expect(input.accept, '.jpg,.jpeg,image/png,image/*'); - expect(input.multiple, false); expect(file.name, mockFile.name); expect(await file.length(), 14); expect(await file.readAsString(), 'random content'); expect(await file.lastModified(), isNotNull); }); }); - - group('openFiles', () { - final mockFile = File(['123456'], 'log.txt'); - - testWidgets('works', (WidgetTester _) async { - final txts = XTypeGroup( - label: 'txt', - mimeTypes: ['file/txt'], - ); - - final jsons = XTypeGroup( - label: 'JSON', - extensions: ['json'], - ); - - final futureFiles = plugin.openFiles(acceptedTypeGroups: [txts, jsons]); - - setFilesAndTriggerChange(container, [mockFile]); - - final files = await futureFiles; - final input = getInput(container); - - expect(input.accept, 'file/txt,.json'); - expect(input.multiple, true); - expect(files.length, 1); - expect(files[0].name, 'log.txt'); - expect(await files[0].length(), 6); - expect(await files[0].readAsString(), '123456'); - expect(await files[0].lastModified(), isNotNull); - }); - }); }); } -void setFilesAndTriggerChange(Element container, List files) { - final input = getInput(container); - input.files = FileListItems(files); - input.dispatchEvent(Event('change')); -} - -FileUploadInputElement getInput(Element container) { - final input = container.children.first as FileUploadInputElement; - assert(input != null); - return input; -} - -FileList FileListItems(List files) { - final dt = DataTransfer(); - files.forEach((file) => dt.items.add(file)); - return dt.files; -} +class MockDomHelper extends Mock implements DomHelper {} From 3b5e744114d7afca312e40defeae955ae5f5ecb7 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 22 Dec 2020 16:45:32 -0600 Subject: [PATCH 03/19] Refactor utils --- .../lib/file_selector_web.dart | 46 +----------- .../file_selector_web/lib/src/utils.dart | 43 +++++++++++ .../test/test_driver/utils_integration.dart | 74 +++++++++++++++++++ .../test_driver/utils_integration_test.dart | 7 ++ 4 files changed, 127 insertions(+), 43 deletions(-) create mode 100644 packages/file_selector/file_selector_web/lib/src/utils.dart create mode 100644 packages/file_selector/file_selector_web/test/test_driver/utils_integration.dart create mode 100644 packages/file_selector/file_selector_web/test/test_driver/utils_integration_test.dart 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 975b0c9cc498..fc3819b74fb9 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 @@ -8,6 +8,7 @@ import 'package:meta/meta.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_web/src/dom_helper.dart'; +import 'package:file_selector_web/src/utils.dart'; /// The web implementation of [FileSelectorPlatform]. /// @@ -65,52 +66,11 @@ class FileSelectorWeb extends FileSelectorPlatform { List acceptedTypeGroups, bool multiple = false, }) async { - final accept = _acceptedTypesToString(acceptedTypeGroups); + final accept = acceptedTypesToString(acceptedTypeGroups); final List files = await _domHelper.getFilesFromInput( accept: accept, multiple: multiple, ); - return files.map(_convertFileToXFile).toList(); - } - - /// Convert list of XTypeGroups to a comma-separated string - static 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)); - } - if (group.mimeTypes != null) { - allTypes.addAll(group.mimeTypes); - } - if (group.webWildCards != null) { - allTypes.addAll(group.webWildCards); - } - } - return allTypes.join(','); - } - - /// Make sure that at least one of its fields is populated. - static void _assertTypeGroupIsValid(XTypeGroup group) { - assert( - !((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.'); - } - - /// Helper to convert an html.File to an XFile - static XFile _convertFileToXFile(File file) => XFile( - Url.createObjectUrl(file), - name: file.name, - length: file.size, - lastModified: DateTime.fromMillisecondsSinceEpoch(file.lastModified), - ); - - /// Append a dot at the beggining if it is not there png -> .png - static String _normalizeExtension(String ext) { - return ext.isNotEmpty && ext[0] != '.' ? '.' + ext : ext; + return files.map(convertFileToXFile).toList(); } } diff --git a/packages/file_selector/file_selector_web/lib/src/utils.dart b/packages/file_selector/file_selector_web/lib/src/utils.dart new file mode 100644 index 000000000000..3d6f034ea5cb --- /dev/null +++ b/packages/file_selector/file_selector_web/lib/src/utils.dart @@ -0,0 +1,43 @@ +import 'dart:html'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; + +/// Convert list of XTypeGroups to a comma-separated string +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)); + } + if (group.mimeTypes != null) { + allTypes.addAll(group.mimeTypes); + } + if (group.webWildCards != null) { + allTypes.addAll(group.webWildCards); + } + } + return allTypes.join(','); +} + +/// Helper to convert an html.File to an XFile +XFile convertFileToXFile(File file) => XFile( + Url.createObjectUrl(file), + name: file.name, + length: file.size, + lastModified: DateTime.fromMillisecondsSinceEpoch(file.lastModified), + ); + +/// 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)), + 'At least one of extensions / mimeTypes / webWildCards is required for web.'); +} + +/// Append a dot at the beggining if it is not there png -> .png +String _normalizeExtension(String ext) { + return ext.isNotEmpty && ext[0] != '.' ? '.' + ext : ext; +} diff --git a/packages/file_selector/file_selector_web/test/test_driver/utils_integration.dart b/packages/file_selector/file_selector_web/test/test_driver/utils_integration.dart new file mode 100644 index 000000000000..3dfefba63cd4 --- /dev/null +++ b/packages/file_selector/file_selector_web/test/test_driver/utils_integration.dart @@ -0,0 +1,74 @@ +// Copyright 2020 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. + +// @dart = 2.9 + +import 'dart:html'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:file_selector_web/src/utils.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; + +void main() { + group('FileSelectorWeb utils', () { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('acceptedTypesToString', () { + testWidgets('works', (_) async { + final List acceptedTypes = [ + XTypeGroup(label: 'images', webWildCards: ['images/*']), + XTypeGroup(label: 'jpgs', extensions: ['jpg', 'jpeg']), + XTypeGroup(label: 'pngs', mimeTypes: ['image/png']), + ]; + final accepts = acceptedTypesToString(acceptedTypes); + expect(accepts, 'images/*,.jpg,.jpeg,image/png'); + }); + + testWidgets('works with an empty list', (_) async { + final List acceptedTypes = []; + final accepts = acceptedTypesToString(acceptedTypes); + expect(accepts, ''); + }); + + testWidgets('works with extensions', (_) async { + final List acceptedTypes = [ + XTypeGroup(label: 'jpgs', extensions: ['jpeg', 'jpg']), + XTypeGroup(label: 'pngs', extensions: ['png']), + ]; + final accepts = acceptedTypesToString(acceptedTypes); + expect(accepts, '.jpeg,.jpg,.png'); + }); + + testWidgets('works with mime types', (_) async { + final List acceptedTypes = [ + XTypeGroup(label: 'jpgs', mimeTypes: ['image/jpeg', 'image/jpg']), + XTypeGroup(label: 'pngs', mimeTypes: ['image/png']), + ]; + final accepts = acceptedTypesToString(acceptedTypes); + expect(accepts, 'image/jpeg,image/jpg,image/png'); + }); + + testWidgets('works with web wild cards', (_) async { + final List acceptedTypes = [ + XTypeGroup(label: 'images', webWildCards: ['image/*']), + XTypeGroup(label: 'audios', webWildCards: ['audio/*']), + XTypeGroup(label: 'videos', webWildCards: ['video/*']), + ]; + final accepts = acceptedTypesToString(acceptedTypes); + expect(accepts, 'image/*,audio/*,video/*'); + }); + }); + + group('convertFileToXFile', () { + testWidgets('works', (_) async { + final file = convertFileToXFile(File(['123456'], 'numbers.txt')); + + expect(file.name, 'numbers.txt'); + expect(await file.length(), 6); + expect(await file.readAsString(), '123456'); + expect(await file.lastModified(), isNotNull); + }); + }); + }); +} diff --git a/packages/file_selector/file_selector_web/test/test_driver/utils_integration_test.dart b/packages/file_selector/file_selector_web/test/test_driver/utils_integration_test.dart new file mode 100644 index 000000000000..1c5d1fcbcca7 --- /dev/null +++ b/packages/file_selector/file_selector_web/test/test_driver/utils_integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2020 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:integration_test/integration_test_driver.dart'; + +Future main() async => integrationDriver(); From 8a1f7b808dff9fff27ccc74de2c9943e43a50a10 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 22 Dec 2020 16:47:23 -0600 Subject: [PATCH 04/19] Fix dependency --- packages/file_selector/file_selector_web/pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index eb046ef765f6..674dae04d457 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + mockito: ^4.1.1 pedantic: ^1.8.0 integration_test: path: ../../integration_test From 0240e2dfa4fd9ca3f7b1ea715cb9d471d4e11d2d Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 22 Dec 2020 16:49:12 -0600 Subject: [PATCH 05/19] Fix the README --- packages/file_selector/file_selector_web/README.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/file_selector/file_selector_web/README.md b/packages/file_selector/file_selector_web/README.md index e35424c8d320..36e0b446ffe8 100644 --- a/packages/file_selector/file_selector_web/README.md +++ b/packages/file_selector/file_selector_web/README.md @@ -2,13 +2,6 @@ The web implementation of [`file_picker`][1]. -**Please set your constraint to `file_picker_web: '>=0.1.y+x <2.0.0'`** - -## Backward compatible 1.0.0 version is coming -The plugin has reached a stable API, we guarantee that version `1.0.0` will be backward compatible with `0.1.y+z`. -Please use `file_picker_web: '>=0.1.y+x <2.0.0'` as your dependency constraint to allow a smoother ecosystem migration. -For more details see: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 - ## Usage ### Import the package @@ -25,8 +18,8 @@ This is what the above means to your `pubspec.yaml`: ... dependencies: ... - file_picker: ^0.1.0 - file_picker_web: ^0.1.0 + file_picker: ^0.7.0 + file_picker_web: ^0.7.0 ... ``` From 8f7b4661016764c689f7558328a6359abe2a736c Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Wed, 23 Dec 2020 10:40:28 -0600 Subject: [PATCH 06/19] Add a test for openFiles --- .../file_selector_web_integration.dart | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart b/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart index dd0760b893da..107b57d9cf72 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart @@ -24,7 +24,7 @@ void main() { }); group('openFile', () { - final mockFile = File(['random content'], 'image.png'); + final mockFile = File(['1001'], 'identity.png'); testWidgets('works', (WidgetTester _) async { final typeGroup = XTypeGroup( @@ -42,11 +42,42 @@ void main() { final file = await plugin.openFile(acceptedTypeGroups: [typeGroup]); expect(file.name, mockFile.name); - expect(await file.length(), 14); + expect(await file.length(), 4); expect(await file.readAsString(), 'random content'); expect(await file.lastModified(), isNotNull); }); }); + + group('openFiles', () { + final mockFile1 = File(['123456'], 'file1.txt'); + final mockFile2 = File([''], 'file2.txt'); + + testWidgets('works', (WidgetTester _) async { + final typeGroup = XTypeGroup( + label: 'files', + extensions: ['.txt'], + ); + + when(mockDomHelper.getFilesFromInput( + accept: '.txt', + multiple: true, + )).thenAnswer((_) async => [mockFile1, mockFile2]); + + final files = await plugin.openFiles(acceptedTypeGroups: [typeGroup]); + + expect(files.length, 2); + + expect(files[0].name, mockFile1.name); + expect(await files[0].length(), 6); + expect(await files[0].readAsString(), '123456'); + expect(await files[0].lastModified(), isNotNull); + + expect(files[1].name, mockFile2.name); + expect(await files[1].length(), 0); + expect(await files[1].readAsString(), ''); + expect(await files[1].lastModified(), isNotNull); + }); + }); }); } From dc715250798198de43d1a0d5b58b25f34496c34c Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Wed, 23 Dec 2020 14:55:08 -0600 Subject: [PATCH 07/19] Improve DomHelper tests. --- .../lib/file_selector_web.dart | 2 +- .../file_selector_web/lib/src/dom_helper.dart | 35 ++++--- .../test_driver/dom_helper_integration.dart | 93 ++++++++++++++----- .../file_selector_web_integration.dart | 6 +- 4 files changed, 92 insertions(+), 44 deletions(-) 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 fc3819b74fb9..362168ba1cb2 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 @@ -67,7 +67,7 @@ class FileSelectorWeb extends FileSelectorPlatform { bool multiple = false, }) async { final accept = acceptedTypesToString(acceptedTypeGroups); - final List files = await _domHelper.getFilesFromInput( + final List files = await _domHelper.getFiles( accept: accept, multiple: multiple, ); 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 c1ca389c1de8..b69b0392d44c 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 @@ -5,47 +5,44 @@ import 'package:flutter/services.dart'; /// Class to manipulate the DOM with the intention of reading files from it. class DomHelper { - final FileUploadInputElement _input; + final _container = Element.tag('file-selector'); - /// Default constructor, initializes _input (an html element). - DomHelper({@visibleForTesting FileUploadInputElement input}) - : _input = input ?? FileUploadInputElement() { + DomHelper() { final body = querySelector('body'); - final container = Element.tag('file-selector'); - body.children.add(container); - container.children.add(_input); + body.children.add(_container); } /// Sets the attributes and waits for a file to be selected. - Future> getFilesFromInput({ + Future> getFiles({ String accept = '', bool multiple = false, + @visibleForTesting FileUploadInputElement input, }) { final Completer> _completer = Completer(); - StreamSubscription onChangeSubscription; - StreamSubscription onErrorSubscription; - _input - ..accept = accept - ..multiple = multiple; + _container.children.add( + (input ?? FileUploadInputElement()) + ..accept = accept + ..multiple = multiple, + ); - onChangeSubscription = _input.onChange.listen((_) { - final List files = _input.files; - onChangeSubscription.cancel(); + input.onChange.first.then((_) { + final List files = input.files; + input.remove(); _completer.complete(files); }); - onErrorSubscription = _input.onError.listen((event) { + input.onError.first.then((event) { final ErrorEvent error = event; final platformException = PlatformException( code: error.type, message: error.message, ); - onErrorSubscription.cancel(); + input.remove(); _completer.completeError(platformException); }); - _input.click(); + input.click(); return _completer.future; } diff --git a/packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration.dart b/packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration.dart index 2d5288a8d107..3ee0b7ada87e 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration.dart @@ -9,42 +9,93 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:file_selector_web/src/dom_helper.dart'; import 'package:pedantic/pedantic.dart'; -import 'package:mockito/mockito.dart'; void main() { group('FileSelectorWeb', () { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); DomHelper domHelper; - MockInput mockInput; + FileUploadInputElement input; + + FileList FileListItems(List files) { + final dataTransfer = DataTransfer(); + files.forEach(dataTransfer.items.add); + return dataTransfer.files; + } + + void setFilesAndTriggerChange(List files) { + input.files = FileListItems(files); + input.dispatchEvent(Event('change')); + } setUp(() { - mockInput = MockInput(); - domHelper = DomHelper(input: mockInput); + domHelper = DomHelper(); + input = FileUploadInputElement(); }); - testWidgets('creates a container and an element', (_) async { - domHelper = DomHelper(); - final container = querySelector('file-selector'); - final input = querySelector('file-selector input'); + group('getFiles', () { + final mockFile1 = File([], 'file1.txt'); + final mockFile2 = File([], 'file2.txt'); - expect(container, isNotNull); - expect(input, isNotNull); - }); + testWidgets('works', (_) async { + final futureFile = domHelper.getFiles(input: input); + + setFilesAndTriggerChange([mockFile1, mockFile2]); + + final files = await futureFile; + + expect(files.length, 2); + + expect(files[0], mockFile1); + expect(files[1], mockFile2); + }); + + testWidgets('works multiple times', (_) async { + Future> futureFiles; + List files; - group('getFilesFromInput', () { - testWidgets('sets the attributes and click it', (_) async { - final accept = '.png,.jpg,.txt,.json'; + // It should work the first time + futureFiles = domHelper.getFiles(input: input); + setFilesAndTriggerChange([mockFile1]); + + files = await futureFiles; + + expect(files.length, 1); + expect(files.first, mockFile1); + + // The same input should work more than once + futureFiles = domHelper.getFiles(input: input); + setFilesAndTriggerChange([mockFile2]); + + files = await futureFiles; + + expect(files.length, 1); + expect(files.first, mockFile2); + }); + + testWidgets('sets the attributes and clicks it', (_) async { + final accept = '.jpg,.png'; final multiple = true; + bool wasClicked = false; + + unawaited(input.onClick.first.then((_) => wasClicked = true)); + + final futureFile = domHelper.getFiles( + accept: accept, + multiple: multiple, + input: input, + ); - unawaited( - domHelper.getFilesFromInput(accept: accept, multiple: multiple)); + expect(input.matchesWithAncestors('body'), true); + expect(input.accept, accept); + expect(input.multiple, multiple); + expect(wasClicked, true); - expect(mockInput.accept, accept); - expect(mockInput.multiple, multiple); - verify(mockInput.click()); + setFilesAndTriggerChange([]); + await futureFile; + + // It should be already removed from the DOM after the file is resolved. + expect(input.parent, isNull); }); }); }); } - -class MockInput extends Mock implements FileUploadInputElement {} diff --git a/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart b/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart index 107b57d9cf72..7960a67ae20f 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart @@ -34,7 +34,7 @@ void main() { webWildCards: ['image/*'], ); - when(mockDomHelper.getFilesFromInput( + when(mockDomHelper.getFiles( accept: '.jpg,.jpeg,image/png,image/*', multiple: false, )).thenAnswer((_) async => [mockFile]); @@ -43,7 +43,7 @@ void main() { expect(file.name, mockFile.name); expect(await file.length(), 4); - expect(await file.readAsString(), 'random content'); + expect(await file.readAsString(), '1001'); expect(await file.lastModified(), isNotNull); }); }); @@ -58,7 +58,7 @@ void main() { extensions: ['.txt'], ); - when(mockDomHelper.getFilesFromInput( + when(mockDomHelper.getFiles( accept: '.txt', multiple: true, )).thenAnswer((_) async => [mockFile1, mockFile2]); From 60cd1def66edd90a118ccc35eb642aed4201913c Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Wed, 23 Dec 2020 16:14:07 -0600 Subject: [PATCH 08/19] Fix reference issue --- .../file_selector/file_selector_web/lib/src/dom_helper.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 b69b0392d44c..8f87090e367d 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 @@ -7,6 +7,7 @@ import 'package:flutter/services.dart'; class DomHelper { final _container = Element.tag('file-selector'); + /// DomHelper() { final body = querySelector('body'); body.children.add(_container); @@ -19,9 +20,10 @@ class DomHelper { @visibleForTesting FileUploadInputElement input, }) { final Completer> _completer = Completer(); + input = input ?? FileUploadInputElement(); _container.children.add( - (input ?? FileUploadInputElement()) + input ..accept = accept ..multiple = multiple, ); From c54bf585625f1f7b993dc3a8dd0de74ad528d225 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Mon, 28 Dec 2020 11:20:41 -0600 Subject: [PATCH 09/19] Add comment doc --- .../file_selector/file_selector_web/lib/src/dom_helper.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8f87090e367d..872892f4c48b 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 @@ -7,7 +7,7 @@ import 'package:flutter/services.dart'; class DomHelper { final _container = Element.tag('file-selector'); - /// + /// Default constructor, initializes the container DOM element. DomHelper() { final body = querySelector('body'); body.children.add(_container); From f3a0c04d3d6dc51a18fe4fb33ddeaac0adaccf50 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 5 Jan 2021 09:26:12 -0600 Subject: [PATCH 10/19] Revert other packages changes --- .../google_maps_flutter_web/test/pubspec.yaml | 1 + .../test_driver/url_launcher_web_integration.dart | 1 + .../url_launcher_web/test/web/index.html | 14 +++++++------- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml index a9350e109377..008cc0350430 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml @@ -20,3 +20,4 @@ dev_dependencies: path: ../ integration_test: path: ../../../integration_test + diff --git a/packages/url_launcher/url_launcher_web/test/test_driver/url_launcher_web_integration.dart b/packages/url_launcher/url_launcher_web/test/test_driver/url_launcher_web_integration.dart index f38aaaccd555..4d103443deb9 100644 --- a/packages/url_launcher/url_launcher_web/test/test_driver/url_launcher_web_integration.dart +++ b/packages/url_launcher/url_launcher_web/test/test_driver/url_launcher_web_integration.dart @@ -4,6 +4,7 @@ import 'dart:html' as html; import 'dart:js_util'; +import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_web/url_launcher_web.dart'; diff --git a/packages/url_launcher/url_launcher_web/test/web/index.html b/packages/url_launcher/url_launcher_web/test/web/index.html index 402d9349ba8b..dc8d0cfe0428 100644 --- a/packages/url_launcher/url_launcher_web/test/web/index.html +++ b/packages/url_launcher/url_launcher_web/test/web/index.html @@ -1,12 +1,12 @@ - + - - Browser Tests - - - - + + Browser Tests + + + + From 50e9dd2a99308c43fa50a62416829a4e7e2876b1 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Wed, 6 Jan 2021 15:51:51 -0600 Subject: [PATCH 11/19] Bump the file_selector_platform_interface version --- packages/file_selector/file_selector_web/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 674dae04d457..a293c17066e4 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -11,7 +11,7 @@ flutter: fileName: file_selector_web.dart dependencies: - file_selector_platform_interface: ^1.0.0 + file_selector_platform_interface: ^1.0.2 platform_detect: ^1.4.0 flutter: sdk: flutter From 94293f5ad4763ec0245d7af0296dba777678059e Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 8 Jan 2021 15:55:10 -0600 Subject: [PATCH 12/19] Refactor tests --- .../{test => integration_test}/lib/main.dart | 0 .../{test => integration_test}/pubspec.yaml | 0 .../{test => integration_test}/run_test | 0 .../test_driver/dom_helper_integration.dart | 28 +++++++++++++------ .../dom_helper_integration_test.dart | 0 .../file_selector_web_integration.dart | 13 ++++++--- .../file_selector_web_integration_test.dart | 0 .../{test => integration_test}/web/index.html | 0 .../lib/file_selector_web.dart | 4 +-- .../file_selector_web/lib/src/dom_helper.dart | 14 ++++++++-- .../file_selector_web/lib/src/utils.dart | 9 ------ .../test_driver/utils_integration_test.dart | 7 ----- ...utils_integration.dart => utils_test.dart} | 25 ++++------------- 13 files changed, 45 insertions(+), 55 deletions(-) rename packages/file_selector/file_selector_web/{test => integration_test}/lib/main.dart (100%) rename packages/file_selector/file_selector_web/{test => integration_test}/pubspec.yaml (100%) rename packages/file_selector/file_selector_web/{test => integration_test}/run_test (100%) rename packages/file_selector/file_selector_web/{test => integration_test}/test_driver/dom_helper_integration.dart (74%) rename packages/file_selector/file_selector_web/{test => integration_test}/test_driver/dom_helper_integration_test.dart (100%) rename packages/file_selector/file_selector_web/{test => integration_test}/test_driver/file_selector_web_integration.dart (86%) rename packages/file_selector/file_selector_web/{test => integration_test}/test_driver/file_selector_web_integration_test.dart (100%) rename packages/file_selector/file_selector_web/{test => integration_test}/web/index.html (100%) delete mode 100644 packages/file_selector/file_selector_web/test/test_driver/utils_integration_test.dart rename packages/file_selector/file_selector_web/test/{test_driver/utils_integration.dart => utils_test.dart} (72%) diff --git a/packages/file_selector/file_selector_web/test/lib/main.dart b/packages/file_selector/file_selector_web/integration_test/lib/main.dart similarity index 100% rename from packages/file_selector/file_selector_web/test/lib/main.dart rename to packages/file_selector/file_selector_web/integration_test/lib/main.dart diff --git a/packages/file_selector/file_selector_web/test/pubspec.yaml b/packages/file_selector/file_selector_web/integration_test/pubspec.yaml similarity index 100% rename from packages/file_selector/file_selector_web/test/pubspec.yaml rename to packages/file_selector/file_selector_web/integration_test/pubspec.yaml diff --git a/packages/file_selector/file_selector_web/test/run_test b/packages/file_selector/file_selector_web/integration_test/run_test similarity index 100% rename from packages/file_selector/file_selector_web/test/run_test rename to packages/file_selector/file_selector_web/integration_test/run_test diff --git a/packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration.dart b/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration.dart similarity index 74% rename from packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration.dart rename to packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration.dart index 3ee0b7ada87e..4d8d5100a6a1 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration.dart +++ b/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration.dart @@ -9,6 +9,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:file_selector_web/src/dom_helper.dart'; import 'package:pedantic/pedantic.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; void main() { group('FileSelectorWeb', () { @@ -33,25 +34,34 @@ void main() { }); group('getFiles', () { - final mockFile1 = File([], 'file1.txt'); + final mockFile1 = File(['123456'], 'file1.txt'); final mockFile2 = File([], 'file2.txt'); testWidgets('works', (_) async { - final futureFile = domHelper.getFiles(input: input); + final Future> futureFiles = domHelper.getFiles( + input: input, + ); setFilesAndTriggerChange([mockFile1, mockFile2]); - final files = await futureFile; + final List files = await futureFiles; expect(files.length, 2); - expect(files[0], mockFile1); - expect(files[1], mockFile2); + expect(files[0].name, 'file1.txt'); + expect(await files[0].length(), 6); + expect(await files[0].readAsString(), '123456'); + expect(await files[0].lastModified(), isNotNull); + + expect(files[1].name, 'file2.txt'); + expect(await files[1].length(), 0); + expect(await files[1].readAsString(), ''); + expect(await files[1].lastModified(), isNotNull); }); testWidgets('works multiple times', (_) async { - Future> futureFiles; - List files; + Future> futureFiles; + List files; // It should work the first time futureFiles = domHelper.getFiles(input: input); @@ -60,7 +70,7 @@ void main() { files = await futureFiles; expect(files.length, 1); - expect(files.first, mockFile1); + expect(files.first.name, mockFile1.name); // The same input should work more than once futureFiles = domHelper.getFiles(input: input); @@ -69,7 +79,7 @@ void main() { files = await futureFiles; expect(files.length, 1); - expect(files.first, mockFile2); + expect(files.first.name, mockFile2.name); }); testWidgets('sets the attributes and clicks it', (_) async { diff --git a/packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration_test.dart b/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration_test.dart similarity index 100% rename from packages/file_selector/file_selector_web/test/test_driver/dom_helper_integration_test.dart rename to packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration_test.dart diff --git a/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart b/packages/file_selector/file_selector_web/integration_test/test_driver/file_selector_web_integration.dart similarity index 86% rename from packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart rename to packages/file_selector/file_selector_web/integration_test/test_driver/file_selector_web_integration.dart index 7960a67ae20f..abd31dd9fcc6 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration.dart +++ b/packages/file_selector/file_selector_web/integration_test/test_driver/file_selector_web_integration.dart @@ -4,7 +4,7 @@ // @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'; @@ -24,7 +24,7 @@ void main() { }); group('openFile', () { - final mockFile = File(['1001'], 'identity.png'); + final mockFile = createXFile('1001', 'identity.png'); testWidgets('works', (WidgetTester _) async { final typeGroup = XTypeGroup( @@ -49,8 +49,8 @@ void main() { }); group('openFiles', () { - final mockFile1 = File(['123456'], 'file1.txt'); - final mockFile2 = File([''], 'file2.txt'); + final mockFile1 = createXFile('123456', 'file1.txt'); + final mockFile2 = createXFile('', 'file2.txt'); testWidgets('works', (WidgetTester _) async { final typeGroup = XTypeGroup( @@ -82,3 +82,8 @@ void main() { } class MockDomHelper extends Mock implements DomHelper {} + +XFile createXFile(String content, String name) { + final data = Uint8List.fromList(content.codeUnits); + return XFile.fromData(data, name: name, lastModified: DateTime.now()); +} diff --git a/packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration_test.dart b/packages/file_selector/file_selector_web/integration_test/test_driver/file_selector_web_integration_test.dart similarity index 100% rename from packages/file_selector/file_selector_web/test/test_driver/file_selector_web_integration_test.dart rename to packages/file_selector/file_selector_web/integration_test/test_driver/file_selector_web_integration_test.dart diff --git a/packages/file_selector/file_selector_web/test/web/index.html b/packages/file_selector/file_selector_web/integration_test/web/index.html similarity index 100% rename from packages/file_selector/file_selector_web/test/web/index.html rename to packages/file_selector/file_selector_web/integration_test/web/index.html 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 362168ba1cb2..48f57ee880c8 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 @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html'; import 'package:meta/meta.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; @@ -67,10 +66,9 @@ class FileSelectorWeb extends FileSelectorPlatform { bool multiple = false, }) async { final accept = acceptedTypesToString(acceptedTypeGroups); - final List files = await _domHelper.getFiles( + return _domHelper.getFiles( accept: accept, multiple: multiple, ); - return files.map(convertFileToXFile).toList(); } } 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 872892f4c48b..4d843cbc92e8 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 @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:html'; import 'package:meta/meta.dart'; import 'package:flutter/services.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; /// Class to manipulate the DOM with the intention of reading files from it. class DomHelper { @@ -14,12 +15,12 @@ class DomHelper { } /// Sets the attributes and waits for a file to be selected. - Future> getFiles({ + Future> getFiles({ String accept = '', bool multiple = false, @visibleForTesting FileUploadInputElement input, }) { - final Completer> _completer = Completer(); + final Completer> _completer = Completer(); input = input ?? FileUploadInputElement(); _container.children.add( @@ -29,7 +30,7 @@ class DomHelper { ); input.onChange.first.then((_) { - final List files = input.files; + final List files = input.files.map(_convertFileToXFile).toList(); input.remove(); _completer.complete(files); }); @@ -48,4 +49,11 @@ class DomHelper { return _completer.future; } + + XFile _convertFileToXFile(File file) => XFile( + Url.createObjectUrl(file), + name: file.name, + length: file.size, + lastModified: DateTime.fromMillisecondsSinceEpoch(file.lastModified), + ); } 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 3d6f034ea5cb..4fc4938fe49a 100644 --- a/packages/file_selector/file_selector_web/lib/src/utils.dart +++ b/packages/file_selector/file_selector_web/lib/src/utils.dart @@ -1,4 +1,3 @@ -import 'dart:html'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; /// Convert list of XTypeGroups to a comma-separated string @@ -20,14 +19,6 @@ String acceptedTypesToString(List acceptedTypes) { return allTypes.join(','); } -/// Helper to convert an html.File to an XFile -XFile convertFileToXFile(File file) => XFile( - Url.createObjectUrl(file), - name: file.name, - length: file.size, - lastModified: DateTime.fromMillisecondsSinceEpoch(file.lastModified), - ); - /// Make sure that at least one of its fields is populated. void _assertTypeGroupIsValid(XTypeGroup group) { assert( diff --git a/packages/file_selector/file_selector_web/test/test_driver/utils_integration_test.dart b/packages/file_selector/file_selector_web/test/test_driver/utils_integration_test.dart deleted file mode 100644 index 1c5d1fcbcca7..000000000000 --- a/packages/file_selector/file_selector_web/test/test_driver/utils_integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2020 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:integration_test/integration_test_driver.dart'; - -Future main() async => integrationDriver(); diff --git a/packages/file_selector/file_selector_web/test/test_driver/utils_integration.dart b/packages/file_selector/file_selector_web/test/utils_test.dart similarity index 72% rename from packages/file_selector/file_selector_web/test/test_driver/utils_integration.dart rename to packages/file_selector/file_selector_web/test/utils_test.dart index 3dfefba63cd4..9fa187eede5b 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/utils_integration.dart +++ b/packages/file_selector/file_selector_web/test/utils_test.dart @@ -4,18 +4,14 @@ // @dart = 2.9 -import 'dart:html'; import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; import 'package:file_selector_web/src/utils.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; void main() { group('FileSelectorWeb utils', () { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - group('acceptedTypesToString', () { - testWidgets('works', (_) async { + test('works', () { final List acceptedTypes = [ XTypeGroup(label: 'images', webWildCards: ['images/*']), XTypeGroup(label: 'jpgs', extensions: ['jpg', 'jpeg']), @@ -25,13 +21,13 @@ void main() { expect(accepts, 'images/*,.jpg,.jpeg,image/png'); }); - testWidgets('works with an empty list', (_) async { + test('works with an empty list', () { final List acceptedTypes = []; final accepts = acceptedTypesToString(acceptedTypes); expect(accepts, ''); }); - testWidgets('works with extensions', (_) async { + test('works with extensions', () { final List acceptedTypes = [ XTypeGroup(label: 'jpgs', extensions: ['jpeg', 'jpg']), XTypeGroup(label: 'pngs', extensions: ['png']), @@ -40,7 +36,7 @@ void main() { expect(accepts, '.jpeg,.jpg,.png'); }); - testWidgets('works with mime types', (_) async { + test('works with mime types', () { final List acceptedTypes = [ XTypeGroup(label: 'jpgs', mimeTypes: ['image/jpeg', 'image/jpg']), XTypeGroup(label: 'pngs', mimeTypes: ['image/png']), @@ -49,7 +45,7 @@ void main() { expect(accepts, 'image/jpeg,image/jpg,image/png'); }); - testWidgets('works with web wild cards', (_) async { + test('works with web wild cards', () { final List acceptedTypes = [ XTypeGroup(label: 'images', webWildCards: ['image/*']), XTypeGroup(label: 'audios', webWildCards: ['audio/*']), @@ -59,16 +55,5 @@ void main() { expect(accepts, 'image/*,audio/*,video/*'); }); }); - - group('convertFileToXFile', () { - testWidgets('works', (_) async { - final file = convertFileToXFile(File(['123456'], 'numbers.txt')); - - expect(file.name, 'numbers.txt'); - expect(await file.length(), 6); - expect(await file.readAsString(), '123456'); - expect(await file.lastModified(), isNotNull); - }); - }); }); } From fd52b914cf483df28ac76451a55c2f0f44d11f79 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 12 Jan 2021 11:53:20 -0600 Subject: [PATCH 13/19] Remove unnecessary pedantic usage --- .../integration_test/test_driver/dom_helper_integration.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration.dart b/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration.dart index 4d8d5100a6a1..d29cfc468638 100644 --- a/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration.dart +++ b/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration.dart @@ -8,7 +8,6 @@ import 'dart:html'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:file_selector_web/src/dom_helper.dart'; -import 'package:pedantic/pedantic.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; void main() { @@ -87,7 +86,8 @@ void main() { final multiple = true; bool wasClicked = false; - unawaited(input.onClick.first.then((_) => wasClicked = true)); + //ignore: unawaited_futures + input.onClick.first.then((_) => wasClicked = true); final futureFile = domHelper.getFiles( accept: accept, From 06507d7ba1020f0817ffd03b041ae9d5c234d4f6 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 12 Jan 2021 11:56:19 -0600 Subject: [PATCH 14/19] Add a valid reason to expect --- .../test_driver/dom_helper_integration.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration.dart b/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration.dart index d29cfc468638..a942c0db10bf 100644 --- a/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration.dart +++ b/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration.dart @@ -98,7 +98,12 @@ void main() { expect(input.matchesWithAncestors('body'), true); expect(input.accept, accept); expect(input.multiple, multiple); - expect(wasClicked, true); + expect( + wasClicked, + true, + reason: + 'The should be clicked otherwise no dialog will be shown', + ); setFilesAndTriggerChange([]); await futureFile; From 7bd86ad3e5000b2501dcdecef68321f30c9660e7 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 12 Jan 2021 11:58:19 -0600 Subject: [PATCH 15/19] Remove pubspec upper bound constraint --- .../file_selector_web/integration_test/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/file_selector/file_selector_web/integration_test/pubspec.yaml b/packages/file_selector/file_selector_web/integration_test/pubspec.yaml index 5543c0529040..f067c81a9949 100644 --- a/packages/file_selector/file_selector_web/integration_test/pubspec.yaml +++ b/packages/file_selector/file_selector_web/integration_test/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_web_tests publish_to: none environment: - sdk: ">=2.2.2 <3.0.0" + sdk: ">=2.2.2" dependencies: flutter: From acff5e57a6f86be3596d8fec93a12ec5ce943664 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 12 Jan 2021 12:13:34 -0600 Subject: [PATCH 16/19] Change the integration_test structure to be compliant with the official guidelines --- ..._integration.dart => dom_helper_test.dart} | 0 ...ation.dart => file_selector_web_test.dart} | 0 .../integration_test/lib/main.dart | 22 ------------------- .../integration_test/pubspec.yaml | 18 --------------- .../dom_helper_integration_test.dart | 7 ------ .../integration_test/web/index.html | 12 ---------- .../run_test => run_integration_test} | 3 ++- .../integration_test.dart} | 3 +-- 8 files changed, 3 insertions(+), 62 deletions(-) rename packages/file_selector/file_selector_web/integration_test/{test_driver/dom_helper_integration.dart => dom_helper_test.dart} (100%) rename packages/file_selector/file_selector_web/integration_test/{test_driver/file_selector_web_integration.dart => file_selector_web_test.dart} (100%) delete mode 100644 packages/file_selector/file_selector_web/integration_test/lib/main.dart delete mode 100644 packages/file_selector/file_selector_web/integration_test/pubspec.yaml delete mode 100644 packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration_test.dart delete mode 100644 packages/file_selector/file_selector_web/integration_test/web/index.html rename packages/file_selector/file_selector_web/{integration_test/run_test => run_integration_test} (66%) rename packages/file_selector/file_selector_web/{integration_test/test_driver/file_selector_web_integration_test.dart => test_driver/integration_test.dart} (81%) diff --git a/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration.dart b/packages/file_selector/file_selector_web/integration_test/dom_helper_test.dart similarity index 100% rename from packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration.dart rename to packages/file_selector/file_selector_web/integration_test/dom_helper_test.dart diff --git a/packages/file_selector/file_selector_web/integration_test/test_driver/file_selector_web_integration.dart b/packages/file_selector/file_selector_web/integration_test/file_selector_web_test.dart similarity index 100% rename from packages/file_selector/file_selector_web/integration_test/test_driver/file_selector_web_integration.dart rename to packages/file_selector/file_selector_web/integration_test/file_selector_web_test.dart diff --git a/packages/file_selector/file_selector_web/integration_test/lib/main.dart b/packages/file_selector/file_selector_web/integration_test/lib/main.dart deleted file mode 100644 index 10415204570c..000000000000 --- a/packages/file_selector/file_selector_web/integration_test/lib/main.dart +++ /dev/null @@ -1,22 +0,0 @@ -// 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 Text('Testing... Look at the console output for results!'); - } -} diff --git a/packages/file_selector/file_selector_web/integration_test/pubspec.yaml b/packages/file_selector/file_selector_web/integration_test/pubspec.yaml deleted file mode 100644 index f067c81a9949..000000000000 --- a/packages/file_selector/file_selector_web/integration_test/pubspec.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: file_selector_web_tests -publish_to: none - -environment: - sdk: ">=2.2.2" - -dependencies: - flutter: - sdk: flutter - flutter_driver: - sdk: flutter - flutter_test: - sdk: flutter - mockito: ^4.1.1 - integration_test: - path: ../../../integration_test - file_selector_web: - path: ../ diff --git a/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration_test.dart b/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration_test.dart deleted file mode 100644 index 1c5d1fcbcca7..000000000000 --- a/packages/file_selector/file_selector_web/integration_test/test_driver/dom_helper_integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2020 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:integration_test/integration_test_driver.dart'; - -Future main() async => integrationDriver(); diff --git a/packages/file_selector/file_selector_web/integration_test/web/index.html b/packages/file_selector/file_selector_web/integration_test/web/index.html deleted file mode 100644 index 402d9349ba8b..000000000000 --- a/packages/file_selector/file_selector_web/integration_test/web/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - Browser Tests - - - - - diff --git a/packages/file_selector/file_selector_web/integration_test/run_test b/packages/file_selector/file_selector_web/run_integration_test similarity index 66% rename from packages/file_selector/file_selector_web/integration_test/run_test rename to packages/file_selector/file_selector_web/run_integration_test index 74a8526a0fa3..f54a4b32a6b1 100755 --- a/packages/file_selector/file_selector_web/integration_test/run_test +++ b/packages/file_selector/file_selector_web/run_integration_test @@ -1,10 +1,11 @@ #!/usr/bin/bash + if pgrep -lf chromedriver > /dev/null; then echo "chromedriver is running." if [ $# -eq 0 ]; then echo "No target specified, running all tests..." - find test_driver/ -iname *_integration.dart | xargs -n1 -i -t flutter drive -d web-server --web-port=7357 --browser-name=chrome --target='{}' + find integration_test/ -iname *_test.dart | xargs -n1 -i -t flutter drive -d web-server --web-port=7357 --browser-name=chrome --driver=test_driver/integration_test.dart --target='{}' else echo "Running test target: $1..." set -x diff --git a/packages/file_selector/file_selector_web/integration_test/test_driver/file_selector_web_integration_test.dart b/packages/file_selector/file_selector_web/test_driver/integration_test.dart similarity index 81% rename from packages/file_selector/file_selector_web/integration_test/test_driver/file_selector_web_integration_test.dart rename to packages/file_selector/file_selector_web/test_driver/integration_test.dart index 1c5d1fcbcca7..bed2d7246909 100644 --- a/packages/file_selector/file_selector_web/integration_test/test_driver/file_selector_web_integration_test.dart +++ b/packages/file_selector/file_selector_web/test_driver/integration_test.dart @@ -1,7 +1,6 @@ // Copyright 2020 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:integration_test/integration_test_driver.dart'; -Future main() async => integrationDriver(); +Future main() => integrationDriver(); From af48e8e910dff0cb252117b2f1780b2a6ab70bdf Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 12 Jan 2021 12:20:09 -0600 Subject: [PATCH 17/19] Remove pubspec upper bound constraint --- packages/file_selector/file_selector_web/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index a293c17066e4..c8e0eef56276 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -29,4 +29,4 @@ dev_dependencies: environment: sdk: ">=2.2.0 <3.0.0" - flutter: ">=1.10.0 <2.0.0" + flutter: ">=1.10.0" From 7206b0eb497c30a2afd11ea14bf1dc25cfc17a41 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 12 Jan 2021 12:37:13 -0600 Subject: [PATCH 18/19] Add missing license comment to files --- .../file_selector/file_selector_web/lib/src/dom_helper.dart | 4 ++++ packages/file_selector/file_selector_web/lib/src/utils.dart | 4 ++++ .../file_selector_web/test_driver/integration_test.dart | 1 + 3 files changed, 9 insertions(+) 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 4d843cbc92e8..a965cebe97f9 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 @@ -1,3 +1,7 @@ +// Copyright 2020 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 'dart:html'; import 'package:meta/meta.dart'; 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 4fc4938fe49a..4ddd7ddcbda5 100644 --- a/packages/file_selector/file_selector_web/lib/src/utils.dart +++ b/packages/file_selector/file_selector_web/lib/src/utils.dart @@ -1,3 +1,7 @@ +// Copyright 2020 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:file_selector_platform_interface/file_selector_platform_interface.dart'; /// Convert list of XTypeGroups to a comma-separated string diff --git a/packages/file_selector/file_selector_web/test_driver/integration_test.dart b/packages/file_selector/file_selector_web/test_driver/integration_test.dart index bed2d7246909..44d6ed9c64bc 100644 --- a/packages/file_selector/file_selector_web/test_driver/integration_test.dart +++ b/packages/file_selector/file_selector_web/test_driver/integration_test.dart @@ -1,6 +1,7 @@ // Copyright 2020 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:integration_test/integration_test_driver.dart'; Future main() => integrationDriver(); From 3d5036387c271d40d8f7cee62dfa9d699f076593 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 12 Jan 2021 15:20:13 -0600 Subject: [PATCH 19/19] Update packages/file_selector/file_selector_web/run_integration_test Co-authored-by: David Iglesias --- packages/file_selector/file_selector_web/run_integration_test | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/file_selector/file_selector_web/run_integration_test b/packages/file_selector/file_selector_web/run_integration_test index f54a4b32a6b1..c9f547a4f7d7 100755 --- a/packages/file_selector/file_selector_web/run_integration_test +++ b/packages/file_selector/file_selector_web/run_integration_test @@ -9,10 +9,9 @@ if pgrep -lf chromedriver > /dev/null; then else echo "Running test target: $1..." set -x - flutter drive -d web-server --web-port=7357 --browser-name=chrome --target=$1 + flutter drive -d web-server --web-port=7357 --browser-name=chrome --driver=test_driver/integration_test.dart --target=$1 fi else echo "chromedriver is not running." fi -