Skip to content

Commit 731b93d

Browse files
BeMacizedditman
andauthored
[image_picker_for_web] Migrate image_picker to package:cross_file (flutter#4083)
* Migrate image picker platform interface to cross_file * Port tests from --platform=chrome to integration_test Co-authored-by: David Iglesias Teixeira <[email protected]>
1 parent 8a966eb commit 731b93d

File tree

13 files changed

+234
-9
lines changed

13 files changed

+234
-9
lines changed

.cirrus.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ task:
168168
env:
169169
# Currently missing; see https://github.com/flutter/flutter/issues/81982
170170
# and https://github.com/flutter/flutter/issues/82211
171-
PLUGINS_TO_EXCLUDE_INTEGRATION_TESTS: "file_selector,image_picker_for_web,shared_preferences_web"
171+
PLUGINS_TO_EXCLUDE_INTEGRATION_TESTS: "file_selector,shared_preferences_web"
172172
matrix:
173173
CHANNEL: "master"
174174
CHANNEL: "stable"

packages/image_picker/image_picker_for_web/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# 2.1.0
2+
3+
* Implemented `getImage`, `getVideo` and `getFile` methods that return `XFile` instances.
4+
* Move tests to `example` directory, so they run as integration_tests with `flutter drive`.
5+
16
# 2.0.0
27

38
* Migrate to null safety.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Testing
2+
3+
This package uses `package:integration_test` to run its tests in a web browser.
4+
5+
See [Plugin Tests > Web Tests](https://github.com/flutter/flutter/wiki/Plugin-Tests#web-tests)
6+
in the Flutter wiki for instructions to setup and run the tests in this package.
7+
8+
Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests)
9+
for more info.

packages/image_picker/image_picker_for_web/test/image_picker_for_web_test.dart renamed to packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,30 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
@TestOn('chrome') // Uses dart:html
6-
75
import 'dart:convert';
86
import 'dart:html' as html;
97
import 'dart:typed_data';
108

119
import 'package:flutter_test/flutter_test.dart';
1210
import 'package:image_picker_for_web/image_picker_for_web.dart';
1311
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
12+
import 'package:integration_test/integration_test.dart';
1413

1514
final String expectedStringContents = "Hello, world!";
1615
final Uint8List bytes = utf8.encode(expectedStringContents) as Uint8List;
1716
final html.File textFile = html.File([bytes], "hello.txt");
1817

1918
void main() {
19+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
20+
2021
// Under test...
2122
late ImagePickerPlugin plugin;
2223

2324
setUp(() {
2425
plugin = ImagePickerPlugin();
2526
});
2627

27-
test('Can select a file', () async {
28+
testWidgets('Can select a file (Deprecated)', (WidgetTester tester) async {
2829
final mockInput = html.FileUploadInputElement();
2930

3031
final overrides = ImagePickerPluginTestOverrides()
@@ -45,9 +46,30 @@ void main() {
4546
expect((await file).readAsBytes(), completion(isNotEmpty));
4647
});
4748

49+
testWidgets('Can select a file', (WidgetTester tester) async {
50+
final mockInput = html.FileUploadInputElement();
51+
52+
final overrides = ImagePickerPluginTestOverrides()
53+
..createInputElement = ((_, __) => mockInput)
54+
..getFileFromInput = ((_) => textFile);
55+
56+
final plugin = ImagePickerPlugin(overrides: overrides);
57+
58+
// Init the pick file dialog...
59+
final file = plugin.getFile();
60+
61+
// Mock the browser behavior of selecting a file...
62+
mockInput.dispatchEvent(html.Event('change'));
63+
64+
// Now the file should be available
65+
expect(file, completes);
66+
// And readable
67+
expect((await file).readAsBytes(), completion(isNotEmpty));
68+
});
69+
4870
// There's no good way of detecting when the user has "aborted" the selection.
4971

50-
test('computeCaptureAttribute', () {
72+
testWidgets('computeCaptureAttribute', (WidgetTester tester) async {
5173
expect(
5274
plugin.computeCaptureAttribute(ImageSource.gallery, CameraDevice.front),
5375
isNull,
@@ -67,14 +89,14 @@ void main() {
6789
});
6890

6991
group('createInputElement', () {
70-
test('accept: any, capture: null', () {
92+
testWidgets('accept: any, capture: null', (WidgetTester tester) async {
7193
html.Element input = plugin.createInputElement('any', null);
7294

7395
expect(input.attributes, containsPair('accept', 'any'));
7496
expect(input.attributes, isNot(contains('capture')));
7597
});
7698

77-
test('accept: any, capture: something', () {
99+
testWidgets('accept: any, capture: something', (WidgetTester tester) async {
78100
html.Element input = plugin.createInputElement('any', 'something');
79101

80102
expect(input.attributes, containsPair('accept', 'any'));
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
7+
void main() {
8+
runApp(MyApp());
9+
}
10+
11+
/// App for testing
12+
class MyApp extends StatefulWidget {
13+
@override
14+
_MyAppState createState() => _MyAppState();
15+
}
16+
17+
class _MyAppState extends State<MyApp> {
18+
@override
19+
Widget build(BuildContext context) {
20+
return Directionality(
21+
textDirection: TextDirection.ltr,
22+
child: Text('Testing... Look at the console output for results!'),
23+
);
24+
}
25+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: connectivity_for_web_integration_tests
2+
publish_to: none
3+
4+
environment:
5+
sdk: ">=2.12.0 <3.0.0"
6+
flutter: ">=2.2.0"
7+
8+
dependencies:
9+
image_picker_for_web:
10+
path: ../
11+
flutter:
12+
sdk: flutter
13+
14+
dev_dependencies:
15+
js: ^0.6.3
16+
flutter_test:
17+
sdk: flutter
18+
flutter_driver:
19+
sdk: flutter
20+
integration_test:
21+
sdk: flutter
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/bash
2+
# Copyright 2013 The Flutter Authors. All rights reserved.
3+
# Use of this source code is governed by a BSD-style license that can be
4+
# found in the LICENSE file.
5+
6+
if pgrep -lf chromedriver > /dev/null; then
7+
echo "chromedriver is running."
8+
9+
if [ $# -eq 0 ]; then
10+
echo "No target specified, running all tests..."
11+
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='{}'
12+
else
13+
echo "Running test target: $1..."
14+
set -x
15+
flutter drive -d web-server --web-port=7357 --browser-name=chrome --driver=test_driver/integration_test.dart --target=$1
16+
fi
17+
18+
else
19+
echo "chromedriver is not running."
20+
echo "Please, check the README.md for instructions on how to use run_test.sh"
21+
fi
22+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:integration_test/integration_test_driver.dart';
6+
7+
Future<void> main() => integrationDriver();
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<!-- Copyright 2013 The Flutter Authors. All rights reserved.
3+
Use of this source code is governed by a BSD-style license that can be
4+
found in the LICENSE file. -->
5+
<html>
6+
<head>
7+
<meta charset="UTF-8">
8+
<title>example</title>
9+
</head>
10+
<body>
11+
<script src="main.dart.js" type="application/javascript"></script>
12+
</body>
13+
</html>

packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,68 @@ class ImagePickerPlugin extends ImagePickerPlatform {
9696
return _getSelectedFile(input);
9797
}
9898

99+
/// Returns an [XFile] with the image that was picked.
100+
///
101+
/// The `source` argument controls where the image comes from. This can
102+
/// be either [ImageSource.camera] or [ImageSource.gallery].
103+
///
104+
/// Note that the `maxWidth`, `maxHeight` and `imageQuality` arguments are not supported on the web. If any of these arguments is supplied, it'll be silently ignored by the web version of the plugin.
105+
///
106+
/// Use `preferredCameraDevice` to specify the camera to use when the `source` is [ImageSource.camera].
107+
/// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. It is also ignored if the chosen camera is not supported on the device.
108+
/// Defaults to [CameraDevice.rear].
109+
///
110+
/// If no images were picked, the return value is null.
111+
@override
112+
Future<XFile> getImage({
113+
required ImageSource source,
114+
double? maxWidth,
115+
double? maxHeight,
116+
int? imageQuality,
117+
CameraDevice preferredCameraDevice = CameraDevice.rear,
118+
}) {
119+
String? capture = computeCaptureAttribute(source, preferredCameraDevice);
120+
return getFile(accept: _kAcceptImageMimeType, capture: capture);
121+
}
122+
123+
/// Returns an [XFile] containing the video that was picked.
124+
///
125+
/// The [source] argument controls where the video comes from. This can
126+
/// be either [ImageSource.camera] or [ImageSource.gallery].
127+
///
128+
/// Note that the `maxDuration` argument is not supported on the web. If the argument is supplied, it'll be silently ignored by the web version of the plugin.
129+
///
130+
/// Use `preferredCameraDevice` to specify the camera to use when the `source` is [ImageSource.camera].
131+
/// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. It is also ignored if the chosen camera is not supported on the device.
132+
/// Defaults to [CameraDevice.rear].
133+
///
134+
/// If no images were picked, the return value is null.
135+
@override
136+
Future<XFile> getVideo({
137+
required ImageSource source,
138+
CameraDevice preferredCameraDevice = CameraDevice.rear,
139+
Duration? maxDuration,
140+
}) {
141+
String? capture = computeCaptureAttribute(source, preferredCameraDevice);
142+
return getFile(accept: _kAcceptVideoMimeType, capture: capture);
143+
}
144+
145+
/// Injects a file input with the specified accept+capture attributes, and
146+
/// returns the PickedFile that the user selected locally.
147+
///
148+
/// `capture` is only supported in mobile browsers.
149+
/// See https://caniuse.com/#feat=html-media-capture
150+
@visibleForTesting
151+
Future<XFile> getFile({
152+
String? accept,
153+
String? capture,
154+
}) {
155+
html.FileUploadInputElement input =
156+
createInputElement(accept, capture) as html.FileUploadInputElement;
157+
_injectAndActivate(input);
158+
return _getSelectedXFile(input);
159+
}
160+
99161
// DOM methods
100162

101163
/// Converts plugin configuration into a proper value for the `capture` attribute.
@@ -150,6 +212,26 @@ class ImagePickerPlugin extends ImagePickerPlatform {
150212
return _completer.future;
151213
}
152214

215+
Future<XFile> _getSelectedXFile(html.FileUploadInputElement input) {
216+
final Completer<XFile> _completer = Completer<XFile>();
217+
// Observe the input until we can return something
218+
input.onChange.first.then((event) {
219+
final objectUrl = _handleOnChangeEvent(event);
220+
if (!_completer.isCompleted && objectUrl != null) {
221+
_completer.complete(XFile(objectUrl));
222+
}
223+
});
224+
input.onError.first.then((event) {
225+
if (!_completer.isCompleted) {
226+
_completer.completeError(event);
227+
}
228+
});
229+
// Note that we don't bother detaching from these streams, since the
230+
// "input" gets re-created in the DOM every time the user needs to
231+
// pick a file.
232+
return _completer.future;
233+
}
234+
153235
/// Initializes a DOM container where we can host input elements.
154236
html.Element _ensureInitialized(String id) {
155237
var target = html.querySelector('#${id}');

packages/image_picker/image_picker_for_web/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: image_picker_for_web
22
description: Web platform implementation of image_picker
33
repository: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker_for_web
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22
5-
version: 2.0.0
5+
version: 2.1.0
66

77
environment:
88
sdk: ">=2.12.0 <3.0.0"
@@ -20,7 +20,7 @@ dependencies:
2020
sdk: flutter
2121
flutter_web_plugins:
2222
sdk: flutter
23-
image_picker_platform_interface: ^2.0.0
23+
image_picker_platform_interface: ^2.2.0
2424
meta: ^1.3.0
2525

2626
dev_dependencies:
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## test
2+
3+
This package uses integration tests for testing.
4+
5+
See `example/README.md` for more info.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter_test/flutter_test.dart';
6+
7+
void main() {
8+
test('Tell the user where to find the real tests', () {
9+
print('---');
10+
print('This package uses integration_test for its tests.');
11+
print('See `example/README.md` for more info.');
12+
print('---');
13+
});
14+
}

0 commit comments

Comments
 (0)