Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
f073f6b5
Commit
f073f6b5
authored
Feb 18, 2016
by
Yegor
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1842 from yjbanov/flutter-driver
flutter_driver package
parents
d93a87ee
439aadf4
Changes
12
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
920 additions
and
0 deletions
+920
-0
driver_extension.dart
packages/flutter_driver/lib/driver_extension.dart
+27
-0
flutter_driver.dart
packages/flutter_driver/lib/flutter_driver.dart
+38
-0
driver.dart
packages/flutter_driver/lib/src/driver.dart
+176
-0
error.dart
packages/flutter_driver/lib/src/error.dart
+81
-0
extension.dart
packages/flutter_driver/lib/src/extension.dart
+137
-0
find.dart
packages/flutter_driver/lib/src/find.dart
+82
-0
gesture.dart
packages/flutter_driver/lib/src/gesture.dart
+25
-0
health.dart
packages/flutter_driver/lib/src/health.dart
+54
-0
message.dart
packages/flutter_driver/lib/src/message.dart
+75
-0
pubspec.yaml
packages/flutter_driver/pubspec.yaml
+21
-0
flutter_driver_test.dart
packages/flutter_driver/test/flutter_driver_test.dart
+203
-0
test.sh
travis/test.sh
+1
-0
No files found.
packages/flutter_driver/lib/driver_extension.dart
0 → 100644
View file @
f073f6b5
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// This library provides a Dart VM service extension that is required for
/// tests that use `package:flutter_driver` to drive applications from a
/// separate process, similar to Selenium (web), Espresso (Android) and UI
/// Automation (iOS).
///
/// The extension must be installed in the same process (isolate) with your
/// application.
///
/// To enable the extension call [enableFlutterDriverExtension] early in your
/// program, prior to running your application, e.g. before you call `runApp`.
///
/// Example:
///
/// import 'package:flutter/material.dart';
/// import 'package:flutter_driver/driver_extension.dart';
///
/// main() {
/// enableFlutterDriverExtension();
/// runApp(new ExampleApp());
/// }
library
flutter_driver_extension
;
export
'src/extension.dart'
show
enableFlutterDriverExtension
;
packages/flutter_driver/lib/flutter_driver.dart
0 → 100644
View file @
f073f6b5
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// This library provides API to test Flutter applications that run on real
/// devices and emulators.
///
/// The application run in a separate process from the test itself. If you are
/// familiar with Selenium (web), Espresso (Android) or UI Automation (iOS),
/// this is Flutter's version of that.
///
/// This is Flutter's version of Selenium WebDriver (generic web),
/// Protractor (Angular), Espresso (Android) or Earl Gray (iOS).
library
flutter_driver
;
export
'src/driver.dart'
show
FlutterDriver
;
export
'src/error.dart'
show
DriverError
,
LogLevel
,
LogRecord
,
flutterDriverLog
;
export
'src/find.dart'
show
ObjectRef
,
GetTextResult
;
export
'src/health.dart'
show
Health
,
HealthStatus
;
export
'src/message.dart'
show
Message
,
Command
,
ObjectRef
,
CommandWithTarget
,
Result
;
packages/flutter_driver/lib/src/driver.dart
0 → 100644
View file @
f073f6b5
// Copyright 2016 The Chromium 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
'package:vm_service_client/vm_service_client.dart'
;
import
'package:json_rpc_2/json_rpc_2.dart'
as
rpc
;
import
'error.dart'
;
import
'find.dart'
;
import
'gesture.dart'
;
import
'health.dart'
;
import
'message.dart'
;
/// A function that connects to a Dart VM service given the [url].
typedef
Future
<
VMServiceClient
>
VMServiceConnectFunction
(
String
url
);
/// Connects to a real Dart VM service using the [VMServiceClient].
final
VMServiceConnectFunction
vmServiceClientConnectFunction
=
VMServiceClient
.
connect
;
/// The connection function used by [FlutterDriver.connect].
///
/// Overwrite this function if you require a different method for connecting to
/// the VM service.
VMServiceConnectFunction
vmServiceConnectFunction
=
vmServiceClientConnectFunction
;
/// Drives a Flutter Application running in another process.
class
FlutterDriver
{
static
const
String
_flutterExtensionMethod
=
'ext.flutter_driver'
;
static
final
Logger
_log
=
new
Logger
(
'FlutterDriver'
);
/// Connects to a Flutter application.
///
/// Resumes the application if it is currently paused (e.g. at a breakpoint).
///
/// [dartVmServiceUrl] is the URL to Dart observatory (a.k.a. VM service). By
/// default it connects to `http://localhost:8181`.
static
Future
<
FlutterDriver
>
connect
({
String
dartVmServiceUrl:
'http://localhost:8181'
})
async
{
// Connect to Dart VM servcies
_log
.
info
(
'Connecting to Flutter application at
$dartVmServiceUrl
'
);
VMServiceClient
client
=
await
vmServiceConnectFunction
(
dartVmServiceUrl
);
VM
vm
=
await
client
.
getVM
();
_log
.
trace
(
'Looking for the isolate'
);
VMIsolate
isolate
=
await
vm
.
isolates
.
first
.
load
();
FlutterDriver
driver
=
new
FlutterDriver
.
connectedTo
(
client
,
isolate
);
// Attempts to resume the isolate, but does not crash if it fails because
// the isolate is already resumed. There could be a race with other tools,
// such as a debugger, any of which could have resumed the isolate.
Future
resumeLeniently
()
{
_log
.
trace
(
'Attempting to resume isolate'
);
return
isolate
.
resume
().
catchError
((
e
)
{
const
vmMustBePausedCode
=
101
;
if
(
e
is
rpc
.
RpcException
&&
e
.
code
==
vmMustBePausedCode
)
{
// No biggie; something else must have resumed the isolate
_log
.
warning
(
'Attempted to resume an already resumed isolate. This may happen '
'when we lose a race with another tool (usually a debugger) that '
'is connected to the same isolate.'
);
}
else
{
// Failed to resume due to another reason. Fail hard.
throw
e
;
}
});
}
// Attempt to resume isolate if it was paused
if
(
isolate
.
pauseEvent
is
VMPauseStartEvent
)
{
_log
.
trace
(
'Isolate is paused at start.'
);
// Waits for a signal from the VM service that the extension is registered
Future
waitForServiceExtension
()
{
return
isolate
.
onExtensionAdded
.
firstWhere
((
String
extension
)
{
return
extension
==
_flutterExtensionMethod
;
});
}
// If the isolate is paused at the start, e.g. via the --start-paused
// option, then the VM service extension is not registered yet. Wait for
// it to be registered.
Future
whenResumed
=
resumeLeniently
();
Future
whenServiceExtensionReady
=
Future
.
any
(<
Future
>[
waitForServiceExtension
(),
// We will never receive the extension event if the user does not
// register it. If that happens time out.
new
Future
<
String
>.
delayed
(
const
Duration
(
seconds:
10
),
()
=>
'timeout'
)
]);
await
whenResumed
;
_log
.
trace
(
'Waiting for service extension'
);
dynamic
signal
=
await
whenServiceExtensionReady
;
if
(
signal
==
'timeout'
)
{
throw
new
DriverError
(
'Timed out waiting for Flutter Driver extension to become available. '
'To enable the driver extension call registerFlutterDriverExtension '
'first thing in the main method of your application.'
);
}
}
else
if
(
isolate
.
pauseEvent
is
VMPauseExitEvent
||
isolate
.
pauseEvent
is
VMPauseBreakpointEvent
||
isolate
.
pauseEvent
is
VMPauseExceptionEvent
||
isolate
.
pauseEvent
is
VMPauseInterruptedEvent
)
{
// If the isolate is paused for any other reason, assume the extension is
// already there.
_log
.
trace
(
'Isolate is paused mid-flight.'
);
await
resumeLeniently
();
}
else
if
(
isolate
.
pauseEvent
is
VMResumeEvent
)
{
_log
.
trace
(
'Isolate is not paused. Assuming application is ready.'
);
}
else
{
_log
.
warning
(
'Unknown pause event type
${isolate.pauseEvent.runtimeType}
. '
'Assuming application is ready.'
);
}
// At this point the service extension must be installed. Verify it.
Health
health
=
await
driver
.
checkHealth
();
if
(
health
.
status
!=
HealthStatus
.
ok
)
{
client
.
close
();
throw
new
DriverError
(
'Flutter application health check failed.'
);
}
_log
.
info
(
'Connected to Flutter application.'
);
return
driver
;
}
FlutterDriver
.
connectedTo
(
this
.
_serviceClient
,
this
.
_appIsolate
);
/// Client connected to the Dart VM running the Flutter application
final
VMServiceClient
_serviceClient
;
/// The main isolate hosting the Flutter application
final
VMIsolateRef
_appIsolate
;
Future
<
Map
<
String
,
dynamic
>>
_sendCommand
(
Command
command
)
async
{
Map
<
String
,
dynamic
>
json
=
<
String
,
dynamic
>{
'kind'
:
command
.
kind
}
..
addAll
(
command
.
toJson
());
return
_appIsolate
.
invokeExtension
(
_flutterExtensionMethod
,
json
)
.
then
((
Map
<
String
,
dynamic
>
result
)
=>
result
,
onError:
(
error
,
stackTrace
)
{
throw
new
DriverError
(
'Failed to fulfill
${command.runtimeType}
due to remote error'
,
error
,
stackTrace
);
});
}
/// Checks the status of the Flutter Driver extension.
Future
<
Health
>
checkHealth
()
async
{
return
Health
.
fromJson
(
await
_sendCommand
(
new
GetHealth
()));
}
Future
<
ObjectRef
>
findByValueKey
(
dynamic
key
)
async
{
return
ObjectRef
.
fromJson
(
await
_sendCommand
(
new
FindByValueKey
(
key
)));
}
Future
<
Null
>
tap
(
ObjectRef
ref
)
async
{
return
await
_sendCommand
(
new
Tap
(
ref
)).
then
((
_
)
=>
null
);
}
Future
<
String
>
getText
(
ObjectRef
ref
)
async
{
GetTextResult
result
=
GetTextResult
.
fromJson
(
await
_sendCommand
(
new
GetText
(
ref
)));
return
result
.
text
;
}
/// Closes the underlying connection to the VM service.
///
/// Returns a [Future] that fires once the connection has been closed.
// TODO(yjbanov): cleanup object references
Future
close
()
=>
_serviceClient
.
close
().
then
((
_
)
{
// Don't leak vm_service_client-specific objects, if any
return
null
;
});
}
packages/flutter_driver/lib/src/error.dart
0 → 100644
View file @
f073f6b5
// Copyright 2016 The Chromium 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:io'
show
stderr
;
/// Standard error thrown by Flutter Driver API.
class
DriverError
extends
Error
{
DriverError
(
this
.
message
,
[
this
.
originalError
,
this
.
originalStackTrace
]);
/// Human-readable error message.
final
String
message
;
final
dynamic
originalError
;
final
dynamic
originalStackTrace
;
String
toString
()
=>
'DriverError:
$message
'
;
}
// Whether someone redirected the log messages somewhere.
bool
_noLogSubscribers
=
true
;
final
StreamController
<
LogRecord
>
_logger
=
new
StreamController
<
LogRecord
>.
broadcast
(
sync:
true
,
onListen:
()
{
_noLogSubscribers
=
false
;
});
void
_log
(
LogLevel
level
,
String
loggerName
,
Object
message
)
{
LogRecord
record
=
new
LogRecord
.
_
(
level
,
loggerName
,
'
$message
'
);
// If nobody expressed interest in rerouting log messages somewhere specific,
// print them to stderr.
if
(
_noLogSubscribers
)
stderr
.
writeln
(
record
);
else
_logger
.
add
(
record
);
}
/// Emits log records from Flutter Driver.
final
Stream
<
LogRecord
>
flutterDriverLog
=
_logger
.
stream
;
/// Severity of a log entry.
enum
LogLevel
{
trace
,
info
,
warning
,
error
,
critical
}
/// A log entry.
class
LogRecord
{
const
LogRecord
.
_
(
this
.
level
,
this
.
loggerName
,
this
.
message
);
final
LogLevel
level
;
final
String
loggerName
;
final
String
message
;
String
toString
()
=>
'[
${"$level".split(".").last}
]
$loggerName
:
$message
'
;
}
/// Package-private; users should use other public logging libraries.
class
Logger
{
Logger
(
this
.
name
);
final
String
name
;
void
trace
(
Object
message
)
{
_log
(
LogLevel
.
trace
,
name
,
message
);
}
void
info
(
Object
message
)
{
_log
(
LogLevel
.
info
,
name
,
message
);
}
void
warning
(
Object
message
)
{
_log
(
LogLevel
.
warning
,
name
,
message
);
}
void
error
(
Object
message
)
{
_log
(
LogLevel
.
error
,
name
,
message
);
}
void
critical
(
Object
message
)
{
_log
(
LogLevel
.
critical
,
name
,
message
);
}
}
packages/flutter_driver/lib/src/extension.dart
0 → 100644
View file @
f073f6b5
// Copyright 2016 The Chromium 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:convert'
;
import
'dart:developer'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/src/instrumentation.dart'
;
import
'error.dart'
;
import
'find.dart'
;
import
'gesture.dart'
;
import
'health.dart'
;
import
'message.dart'
;
const
String
_extensionMethod
=
'ext.flutter_driver'
;
bool
_flutterDriverExtensionEnabled
=
false
;
/// Enables Flutter Driver VM service extension.
///
/// This extension is required for tests that use `package:flutter_driver` to
/// drive applications from a separate process.
///
/// Call this function prior to running your application, e.g. before you call
/// `runApp`.
void
enableFlutterDriverExtension
(
)
{
if
(
_flutterDriverExtensionEnabled
)
return
;
FlutterDriverExtension
extension
=
new
FlutterDriverExtension
();
registerExtension
(
_extensionMethod
,
(
String
methodName
,
Map
<
String
,
String
>
params
)
{
return
extension
.
call
(
params
);
});
_flutterDriverExtensionEnabled
=
true
;
}
/// Handles a command and returns a result.
typedef
Future
<
R
>
CommandHandlerCallback
<
R
extends
Result
>(
Command
c
);
/// Deserializes JSON map to a command object.
typedef
Command
CommandDeserializerCallback
(
Map
<
String
,
String
>
params
);
class
FlutterDriverExtension
{
static
final
Logger
_log
=
new
Logger
(
'FlutterDriverExtension'
);
FlutterDriverExtension
()
{
_commandHandlers
=
{
'get_health'
:
getHealth
,
'find_by_value_key'
:
findByValueKey
,
'tap'
:
tap
,
'get_text'
:
getText
,
};
_commandDeserializers
=
{
'get_health'
:
GetHealth
.
fromJson
,
'find_by_value_key'
:
FindByValueKey
.
fromJson
,
'tap'
:
Tap
.
fromJson
,
'get_text'
:
GetText
.
fromJson
,
};
}
final
Instrumentation
prober
=
new
Instrumentation
();
Map
<
String
,
CommandHandlerCallback
>
_commandHandlers
=
<
String
,
CommandHandlerCallback
>{};
Map
<
String
,
CommandDeserializerCallback
>
_commandDeserializers
=
<
String
,
CommandDeserializerCallback
>{};
Future
<
ServiceExtensionResponse
>
call
(
Map
<
String
,
String
>
params
)
async
{
String
commandKind
=
params
[
'kind'
];
CommandHandlerCallback
commandHandler
=
_commandHandlers
[
commandKind
];
CommandDeserializerCallback
commandDeserializer
=
_commandDeserializers
[
commandKind
];
if
(
commandHandler
==
null
||
commandDeserializer
==
null
)
{
return
new
ServiceExtensionResponse
.
error
(
ServiceExtensionResponse
.
kInvalidParams
,
'Extension
$_extensionMethod
does not support command
$commandKind
'
);
}
Command
command
=
commandDeserializer
(
params
);
return
commandHandler
(
command
).
then
((
Result
result
)
{
return
new
ServiceExtensionResponse
.
result
(
JSON
.
encode
(
result
.
toJson
()));
},
onError:
(
e
,
s
)
{
_log
.
warning
(
'
$e
:
\n
$s
'
);
return
new
ServiceExtensionResponse
.
error
(
ServiceExtensionResponse
.
kExtensionError
,
'
$e
'
);
});
}
Future
<
Health
>
getHealth
(
GetHealth
command
)
async
=>
new
Health
(
HealthStatus
.
ok
);
Future
<
ObjectRef
>
findByValueKey
(
FindByValueKey
command
)
{
Element
elem
=
prober
.
findElementByKey
(
new
ValueKey
<
dynamic
>(
command
.
keyValue
));
ObjectRef
elemRef
=
elem
!=
null
?
new
ObjectRef
(
_registerObject
(
elem
))
:
new
ObjectRef
.
notFound
();
return
new
Future
.
value
(
elemRef
);
}
Future
<
TapResult
>
tap
(
Tap
command
)
async
{
Element
target
=
await
_dereferenceOrDie
(
command
.
targetRef
);
prober
.
tap
(
target
);
return
new
TapResult
();
}
Future
<
GetTextResult
>
getText
(
GetText
command
)
async
{
Element
target
=
await
_dereferenceOrDie
(
command
.
targetRef
);
// TODO(yjbanov): support more ways to read text
Text
text
=
target
.
widget
;
return
new
GetTextResult
(
text
.
data
);
}
int
_refCounter
=
1
;
final
Map
<
String
,
Object
>
_objectRefs
=
<
String
,
Object
>{};
String
_registerObject
(
Object
obj
)
{
if
(
obj
==
null
)
throw
new
ArgumentError
(
'Cannot register null object'
);
String
refKey
=
'
${_refCounter++}
'
;
_objectRefs
[
refKey
]
=
obj
;
return
refKey
;
}
dynamic
_dereference
(
String
reference
)
=>
_objectRefs
[
reference
];
Future
<
dynamic
>
_dereferenceOrDie
(
String
reference
)
{
Element
object
=
_dereference
(
reference
);
if
(
object
==
null
)
return
new
Future
.
error
(
'Object reference not found (
$reference
).'
);
return
new
Future
.
value
(
object
);
}
}
packages/flutter_driver/lib/src/find.dart
0 → 100644
View file @
f073f6b5
// Copyright 2016 The Chromium 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
'error.dart'
;
import
'message.dart'
;
const
List
<
Type
>
_supportedKeyValueTypes
=
const
<
Type
>[
String
,
int
];
/// Command to find an element by a value key.
class
FindByValueKey
extends
Command
{
final
String
kind
=
'find_by_value_key'
;
FindByValueKey
(
dynamic
keyValue
)
:
this
.
keyValue
=
keyValue
,
this
.
keyValueString
=
'
$keyValue
'
,
this
.
keyValueType
=
'
${keyValue.runtimeType}
'
{
if
(!
_supportedKeyValueTypes
.
contains
(
keyValue
.
runtimeType
))
_throwInvalidKeyValueType
(
'
$keyValue
.runtimeType'
);
}
/// The true value of the key.
final
dynamic
keyValue
;
/// Stringified value of the key (we can only send strings to the VM service)
final
String
keyValueString
;
/// The type name of the key.
///
/// May be one of "String", "int". The list of supported types may change.
final
String
keyValueType
;
Map
<
String
,
dynamic
>
toJson
()
=>
{
'keyValueString'
:
keyValueString
,
'keyValueType'
:
keyValueType
,
};
static
FindByValueKey
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
String
keyValueString
=
json
[
'keyValueString'
];
String
keyValueType
=
json
[
'keyValueType'
];
switch
(
keyValueType
)
{
case
'int'
:
return
new
FindByValueKey
(
int
.
parse
(
keyValueString
));
case
'String'
:
return
new
FindByValueKey
(
keyValueString
);
default
:
return
_throwInvalidKeyValueType
(
keyValueType
);
}
}
static
_throwInvalidKeyValueType
(
String
invalidType
)
{
throw
new
DriverError
(
'Unsupported key value type
$invalidType
. Flutter Driver only supports
${_supportedKeyValueTypes.join(", ")}
'
);
}
}
/// Command to read the text from a given element.
class
GetText
extends
CommandWithTarget
{
final
String
kind
=
'get_text'
;
static
GetText
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
new
GetText
(
new
ObjectRef
(
json
[
'targetRef'
]));
}
/// [targetRef] identifies an element that contains a piece of text.
GetText
(
ObjectRef
targetRef
)
:
super
(
targetRef
);
Map
<
String
,
dynamic
>
toJson
()
=>
super
.
toJson
();
}
class
GetTextResult
extends
Result
{
static
GetTextResult
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
new
GetTextResult
(
json
[
'text'
]);
}
GetTextResult
(
this
.
text
);
final
String
text
;
Map
<
String
,
dynamic
>
toJson
()
=>
{
'text'
:
text
,
};
}
packages/flutter_driver/lib/src/gesture.dart
0 → 100644
View file @
f073f6b5
// Copyright 2016 The Chromium 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
'message.dart'
;
class
Tap
extends
CommandWithTarget
{
final
String
kind
=
'tap'
;
Tap
(
ObjectRef
targetRef
)
:
super
(
targetRef
);
static
Tap
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
new
Tap
(
new
ObjectRef
(
json
[
'targetRef'
]));
}
Map
<
String
,
dynamic
>
toJson
()
=>
super
.
toJson
();
}
class
TapResult
extends
Result
{
static
TapResult
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
new
TapResult
();
}
Map
<
String
,
dynamic
>
toJson
()
=>
{};
}
packages/flutter_driver/lib/src/health.dart
0 → 100644
View file @
f073f6b5
// Copyright 2016 The Chromium 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
'message.dart'
;
/// Requests an application health check.
class
GetHealth
implements
Command
{
final
String
kind
=
'get_health'
;
static
fromJson
(
Map
<
String
,
dynamic
>
json
)
=>
new
GetHealth
();
Map
<
String
,
dynamic
>
toJson
()
=>
const
{};
}
/// Application health status.
enum
HealthStatus
{
/// Application is known to be in a good shape and should be able to respond.
ok
,
/// Application is not known to be in a good shape and may be unresponsive.
bad
,
}
/// Application health status.
class
Health
extends
Result
{
Health
(
this
.
status
)
{
assert
(
status
!=
null
);
}
static
Health
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
new
Health
(
_statusFromId
(
json
[
'status'
]));
}
/// Health status
final
HealthStatus
status
;
Map
<
String
,
dynamic
>
toJson
()
=>
{
'status'
:
_getStatusId
(
status
)
};
}
String
_getStatusId
(
HealthStatus
status
)
=>
status
.
toString
().
split
(
'.'
).
last
;
final
Map
<
String
,
HealthStatus
>
_idToStatus
=
new
Map
<
String
,
HealthStatus
>.
fromIterable
(
HealthStatus
.
values
,
key:
_getStatusId
);
HealthStatus
_statusFromId
(
String
id
)
{
return
_idToStatus
.
containsKey
(
id
)
?
_idToStatus
[
id
]
:
throw
new
ArgumentError
.
value
(
id
,
'id'
,
'unknown'
);
}
packages/flutter_driver/lib/src/message.dart
0 → 100644
View file @
f073f6b5
// Copyright 2016 The Chromium 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
'error.dart'
;
/// A piece of data travelling between Flutter Driver and a Flutter application.
abstract
class
Message
{
/// Serializes this message to a JSON map.
Map
<
String
,
dynamic
>
toJson
();
}
/// A message that travels from the Flutter Driver to a Flutter application to
/// instruct the application to perform a task.
abstract
class
Command
extends
Message
{
/// Identifies the type of the command object and of the handler.
String
get
kind
;
}
/// A message sent from a Flutter application back to the Flutter Driver in
/// response to a command.
abstract
class
Result
extends
Message
{
}
/// A serializable reference to an object that lives in the application isolate.
class
ObjectRef
extends
Result
{
ObjectRef
(
this
.
objectReferenceKey
);
ObjectRef
.
notFound
()
:
this
(
null
);
static
ObjectRef
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
json
[
'objectReferenceKey'
]
!=
null
?
new
ObjectRef
(
json
[
'objectReferenceKey'
])
:
null
;
}
/// Identifier used to dereference an object.
///
/// This value is generated by the application-side isolate. Flutter driver
/// tests should not generate these keys.
final
String
objectReferenceKey
;
Map
<
String
,
dynamic
>
toJson
()
=>
{
'objectReferenceKey'
:
objectReferenceKey
,
};
}
/// A command aimed at an object represented by [targetRef].
///
/// Implementations must provide a concrete [kind]. If additional data is
/// required beyond the [targetRef] the implementation may override [toJson]
/// and add more keys to the returned map.
abstract
class
CommandWithTarget
extends
Command
{
CommandWithTarget
(
ObjectRef
ref
)
:
this
.
targetRef
=
ref
?.
objectReferenceKey
{
if
(
ref
==
null
)
throw
new
DriverError
(
'
${this.runtimeType}
target cannot be null'
);
if
(
ref
.
objectReferenceKey
==
null
)
throw
new
DriverError
(
'
${this.runtimeType}
target reference cannot be null'
);
}
/// Refers to the object targeted by this command.
final
String
targetRef
;
/// This method is meant to be overridden if data in addition to [targetRef]
/// is serialized to JSON.
///
/// Example:
///
/// Map<String, dynamic> toJson() => super.toJson()..addAll({
/// 'foo': this.foo,
/// });
Map
<
String
,
dynamic
>
toJson
()
=>
{
'targetRef'
:
targetRef
,
};
}
packages/flutter_driver/pubspec.yaml
0 → 100644
View file @
f073f6b5
name
:
flutter_driver
version
:
0.0.1
description
:
Integration and performance test API for Flutter applications
homepage
:
http://flutter.io
author
:
Flutter Authors <flutter-dev@googlegroups.com>
environment
:
sdk
:
'
>=1.12.0
<2.0.0'
dependencies
:
vm_service_client
:
'
>=0.1.2
<1.0.0'
json_rpc_2
:
any
logging
:
'
>=0.11.0
<1.0.0'
flutter
:
path
:
'
../flutter'
flutter_test
:
path
:
'
../flutter_test'
dev_dependencies
:
test
:
'
>=0.12.6
<1.0.0'
mockito
:
^0.10.1
packages/flutter_driver/test/flutter_driver_test.dart
0 → 100644
View file @
f073f6b5
// Copyright 2016 The Chromium 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
'package:test/test.dart'
;
import
'package:flutter_driver/src/driver.dart'
;
import
'package:flutter_driver/src/error.dart'
;
import
'package:flutter_driver/src/health.dart'
;
import
'package:flutter_driver/src/message.dart'
;
import
'package:json_rpc_2/json_rpc_2.dart'
as
rpc
;
import
'package:mockito/mockito.dart'
;
import
'package:vm_service_client/vm_service_client.dart'
;
main
()
{
group
(
'FlutterDriver.connect'
,
()
{
List
<
LogRecord
>
log
;
StreamSubscription
logSub
;
MockVMServiceClient
mockClient
;
MockVM
mockVM
;
MockIsolate
mockIsolate
;
expectLogContains
(
String
message
)
{
expect
(
log
.
map
((
r
)
=>
'
$r
'
),
anyElement
(
contains
(
message
)));
}
setUp
(()
{
log
=
<
LogRecord
>[];
logSub
=
flutterDriverLog
.
listen
(
log
.
add
);
mockClient
=
new
MockVMServiceClient
();
mockVM
=
new
MockVM
();
mockIsolate
=
new
MockIsolate
();
when
(
mockClient
.
getVM
()).
thenReturn
(
mockVM
);
when
(
mockVM
.
isolates
).
thenReturn
([
mockIsolate
]);
when
(
mockIsolate
.
load
()).
thenReturn
(
mockIsolate
);
when
(
mockIsolate
.
invokeExtension
(
any
,
any
))
.
thenReturn
(
new
Future
.
value
({
'status'
:
'ok'
}));
vmServiceConnectFunction
=
(
_
)
=>
new
Future
.
value
(
mockClient
);
});
tearDown
(()
async
{
await
logSub
.
cancel
();
vmServiceConnectFunction
=
vmServiceClientConnectFunction
;
});
test
(
'connects to isolate paused at start'
,
()
async
{
when
(
mockIsolate
.
pauseEvent
).
thenReturn
(
new
MockVMPauseStartEvent
());
when
(
mockIsolate
.
resume
()).
thenReturn
(
new
Future
.
value
());
when
(
mockIsolate
.
onExtensionAdded
)
.
thenReturn
(
new
Stream
.
fromIterable
([
'ext.flutter_driver'
]));
FlutterDriver
driver
=
await
FlutterDriver
.
connect
();
expect
(
driver
,
isNotNull
);
expectLogContains
(
'Isolate is paused at start'
);
});
test
(
'connects to isolate paused mid-flight'
,
()
async
{
when
(
mockIsolate
.
pauseEvent
).
thenReturn
(
new
MockVMPauseBreakpointEvent
());
when
(
mockIsolate
.
resume
()).
thenReturn
(
new
Future
.
value
());
FlutterDriver
driver
=
await
FlutterDriver
.
connect
();
expect
(
driver
,
isNotNull
);
expectLogContains
(
'Isolate is paused mid-flight'
);
});
// This test simulates a situation when we believe that the isolate is
// currently paused, but something else (e.g. a debugger) resumes it before
// we do. There's no need to fail as we should be able to drive the app
// just fine.
test
(
'connects despite losing the race to resume isolate'
,
()
async
{
when
(
mockIsolate
.
pauseEvent
).
thenReturn
(
new
MockVMPauseBreakpointEvent
());
when
(
mockIsolate
.
resume
()).
thenAnswer
((
_
)
{
// This needs to be wrapped in a closure to not be considered uncaught
// by package:test
return
new
Future
.
error
(
new
rpc
.
RpcException
(
101
,
''
));
});
FlutterDriver
driver
=
await
FlutterDriver
.
connect
();
expect
(
driver
,
isNotNull
);
expectLogContains
(
'Attempted to resume an already resumed isolate'
);
});
test
(
'connects to unpaused isolate'
,
()
async
{
when
(
mockIsolate
.
pauseEvent
).
thenReturn
(
new
MockVMResumeEvent
());
FlutterDriver
driver
=
await
FlutterDriver
.
connect
();
expect
(
driver
,
isNotNull
);
expectLogContains
(
'Isolate is not paused. Assuming application is ready.'
);
});
});
group
(
'FlutterDriver'
,
()
{
MockVMServiceClient
mockClient
;
MockIsolate
mockIsolate
;
FlutterDriver
driver
;
setUp
(()
{
mockClient
=
new
MockVMServiceClient
();
mockIsolate
=
new
MockIsolate
();
driver
=
new
FlutterDriver
.
connectedTo
(
mockClient
,
mockIsolate
);
});
test
(
'checks the health of the driver extension'
,
()
async
{
when
(
mockIsolate
.
invokeExtension
(
any
,
any
)).
thenReturn
(
new
Future
.
value
({
'status'
:
'ok'
,
}));
Health
result
=
await
driver
.
checkHealth
();
expect
(
result
.
status
,
HealthStatus
.
ok
);
});
test
(
'closes connection'
,
()
async
{
when
(
mockClient
.
close
()).
thenReturn
(
new
Future
.
value
());
await
driver
.
close
();
});
group
(
'findByValueKey'
,
()
{
test
(
'restricts value types'
,
()
async
{
expect
(
driver
.
findByValueKey
(
null
),
throwsA
(
new
isInstanceOf
<
DriverError
>()));
});
test
(
'finds by ValueKey'
,
()
async
{
when
(
mockIsolate
.
invokeExtension
(
any
,
any
)).
thenAnswer
((
Invocation
i
)
{
expect
(
i
.
positionalArguments
[
1
],
{
'kind'
:
'find_by_value_key'
,
'keyValueString'
:
'foo'
,
'keyValueType'
:
'String'
,
});
return
new
Future
.
value
({
'objectReferenceKey'
:
'123'
,
});
});
ObjectRef
result
=
await
driver
.
findByValueKey
(
'foo'
);
expect
(
result
,
isNotNull
);
expect
(
result
.
objectReferenceKey
,
'123'
);
});
});
group
(
'tap'
,
()
{
test
(
'requires a target reference'
,
()
async
{
expect
(
driver
.
tap
(
null
),
throwsA
(
new
isInstanceOf
<
DriverError
>()));
});
test
(
'requires a valid target reference'
,
()
async
{
expect
(
driver
.
tap
(
new
ObjectRef
.
notFound
()),
throwsA
(
new
isInstanceOf
<
DriverError
>()));
});
test
(
'sends the tap command'
,
()
async
{
when
(
mockIsolate
.
invokeExtension
(
any
,
any
)).
thenAnswer
((
Invocation
i
)
{
expect
(
i
.
positionalArguments
[
1
],
{
'kind'
:
'tap'
,
'targetRef'
:
'123'
});
return
new
Future
.
value
();
});
await
driver
.
tap
(
new
ObjectRef
(
'123'
));
});
});
group
(
'getText'
,
()
{
test
(
'requires a target reference'
,
()
async
{
expect
(
driver
.
getText
(
null
),
throwsA
(
new
isInstanceOf
<
DriverError
>()));
});
test
(
'requires a valid target reference'
,
()
async
{
expect
(
driver
.
getText
(
new
ObjectRef
.
notFound
()),
throwsA
(
new
isInstanceOf
<
DriverError
>()));
});
test
(
'sends the getText command'
,
()
async
{
when
(
mockIsolate
.
invokeExtension
(
any
,
any
)).
thenAnswer
((
Invocation
i
)
{
expect
(
i
.
positionalArguments
[
1
],
{
'kind'
:
'get_text'
,
'targetRef'
:
'123'
});
return
new
Future
.
value
({
'text'
:
'hello'
});
});
String
result
=
await
driver
.
getText
(
new
ObjectRef
(
'123'
));
expect
(
result
,
'hello'
);
});
});
});
}
@proxy
class
MockVMServiceClient
extends
Mock
implements
VMServiceClient
{
}
@proxy
class
MockVM
extends
Mock
implements
VM
{
}
@proxy
class
MockIsolate
extends
Mock
implements
VMRunnableIsolate
{
}
@proxy
class
MockVMPauseStartEvent
extends
Mock
implements
VMPauseStartEvent
{
}
@proxy
class
MockVMPauseBreakpointEvent
extends
Mock
implements
VMPauseBreakpointEvent
{
}
@proxy
class
MockVMResumeEvent
extends
Mock
implements
VMResumeEvent
{
}
travis/test.sh
View file @
f073f6b5
...
...
@@ -15,5 +15,6 @@ flutter analyze --flutter-repo --no-current-directory --no-current-package --con
(
cd
packages/newton
;
pub run
test
-j1
)
# (cd packages/playfair; ) # No tests to run.
# (cd packages/updater; ) # No tests to run.
(
cd
packages/flutter_driver
;
pub run
test
-j1
)
(
cd
examples/stocks
;
flutter
test
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment