Skip to content

Commit

Permalink
[file_selector_web] Migrated to null-safety (flutter#3550)
Browse files Browse the repository at this point in the history
Co-authored-by: David Iglesias Teixeira <ditman@gmail.com>
  • Loading branch information
SirusCodes and ditman authored Feb 25, 2021
1 parent 499156e commit 96faf2c
Show file tree
Hide file tree
Showing 15 changed files with 199 additions and 87 deletions.
4 changes: 4 additions & 0 deletions packages/file_selector/file_selector_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
21 changes: 21 additions & 0 deletions packages/file_selector/file_selector_web/example/README.md
Original file line number Diff line number Diff line change
@@ -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:
* <https://chromedriver.chromium.org/downloads>

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`
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,26 @@
// 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: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<File> files) {
FileList? createFileList(List<File> 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<File> files) {
input.files = FileListItems(files);
input.files = createFileList(files);
input.dispatchEvent(Event('change'));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -15,30 +13,25 @@ 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'],
mimeTypes: ['image/png'],
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);
Expand All @@ -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);
Expand All @@ -81,7 +76,36 @@ void main() {
});
}

class MockDomHelper extends Mock implements DomHelper {}
class MockDomHelper implements DomHelper {
List<XFile> _files = <XFile>[];
String _expectedAccept = '';
bool _expectedMultiple = false;

@override
Future<List<XFile>> 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<XFile> 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);
Expand Down
25 changes: 25 additions & 0 deletions packages/file_selector/file_selector_web/example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -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<MyApp> {
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.ltr,
child: Text('Testing... Look at the console output for results!'),
);
}
}
21 changes: 21 additions & 0 deletions packages/file_selector/file_selector_web/example/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions packages/file_selector/file_selector_web/example/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<!-- Copyright 2014 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. -->
<html>
<head>
<title>Browser Tests</title>
</head>
<body>
<script src="main.dart.js"></script>
</body>
</html>
34 changes: 17 additions & 17 deletions packages/file_selector/file_selector_web/lib/file_selector_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -23,46 +23,46 @@ 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<XFile> openFile({
List<XTypeGroup> acceptedTypeGroups,
String initialDirectory,
String confirmButtonText,
List<XTypeGroup>? acceptedTypeGroups,
String? initialDirectory,
String? confirmButtonText,
}) async {
final files = await _openFiles(acceptedTypeGroups: acceptedTypeGroups);
return files.first;
}

@override
Future<List<XFile>> openFiles({
List<XTypeGroup> acceptedTypeGroups,
String initialDirectory,
String confirmButtonText,
List<XTypeGroup>? acceptedTypeGroups,
String? initialDirectory,
String? confirmButtonText,
}) async {
return _openFiles(acceptedTypeGroups: acceptedTypeGroups, multiple: true);
}

@override
Future<String> getSavePath({
List<XTypeGroup> acceptedTypeGroups,
String initialDirectory,
String suggestedName,
String confirmButtonText,
Future<String?> getSavePath({
List<XTypeGroup>? acceptedTypeGroups,
String? initialDirectory,
String? suggestedName,
String? confirmButtonText,
}) async =>
null;

@override
Future<String> getDirectoryPath({
String initialDirectory,
String confirmButtonText,
Future<String?> getDirectoryPath({
String? initialDirectory,
String? confirmButtonText,
}) async =>
null;

Future<List<XFile>> _openFiles({
List<XTypeGroup> acceptedTypeGroups,
List<XTypeGroup>? acceptedTypeGroups,
bool multiple = false,
}) async {
final accept = acceptedTypesToString(acceptedTypeGroups);
Expand Down
35 changes: 19 additions & 16 deletions packages/file_selector/file_selector_web/lib/src/dom_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,50 +14,53 @@ class DomHelper {

/// Default constructor, initializes the container DOM element.
DomHelper() {
final body = querySelector('body');
final body = querySelector('body')!;
body.children.add(_container);
}

/// Sets the <input /> attributes and waits for a file to be selected.
Future<List<XFile>> getFiles({
String accept = '',
bool multiple = false,
@visibleForTesting FileUploadInputElement input,
@visibleForTesting FileUploadInputElement? input,
}) {
final Completer<List<XFile>> _completer = Completer();
input = input ?? FileUploadInputElement();
final Completer<List<XFile>> completer = Completer();
final FileUploadInputElement inputElement =
input ?? FileUploadInputElement();

_container.children.add(
input
inputElement
..accept = accept
..multiple = multiple,
);

input.onChange.first.then((_) {
final List<XFile> files = input.files.map(_convertFileToXFile).toList();
input.remove();
_completer.complete(files);
inputElement.onChange.first.then((_) {
final List<XFile> 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),
);
}
Loading

0 comments on commit 96faf2c

Please sign in to comment.