Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add new command to get text #1

Merged
merged 2 commits into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 212 additions & 0 deletions lib/get_text_command.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_driver/driver_extension.dart';
import 'package:flutter_driver/src/common/find.dart';
import 'package:flutter_driver/src/common/message.dart';
import 'package:flutter_test/flutter_test.dart';

class Base64URL {
static String encode(String str) {
String base64 = base64Encode(utf8.encode(str));
return base64.replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');
}

static String decode(String str) {
String base64 = str.replaceAll('-', '+').replaceAll('_', '/');

// Add padding if needed
switch (base64.length % 4) {
case 2:
base64 += '==';
break;
case 3:
base64 += '=';
break;
}

return utf8.decode(base64Decode(base64));
}
}

class FinderHelper {
static SerializableFinder deserializeBase64(String base64Str) {
try {
// Decode base64 to JSON string
final jsonStr = Base64URL.decode(base64Str);
if (kDebugMode) {
print('decoded string: $jsonStr');
}

// Parse JSON
final dynamic finderData = json.decode(jsonStr);

if (finderData is! Map<String, dynamic>) {
throw Exception('finder is not valid');
}

if (!finderData.containsKey('finderType')) {
throw Exception('Invalid finder format: missing finderType');
}

final String finderType = finderData['finderType'] as String;

switch (finderType) {
case 'ByText':
return ByText(finderData['text'] as String);

case 'ByType':
return ByType(finderData['type'] as String);

case 'ByValueKey':
final keyType = finderData['keyValueType'] as String?;
final keyValue = finderData['keyValueString'] as String;

if (keyType == 'int') {
return ByValueKey(int.parse(keyValue));
}
return ByValueKey(keyValue);

case 'Ancestor':
// Parse of and matching which are JSON strings
final ofJson = json.decode(finderData['of'] as String);
final matchingJson = json.decode(finderData['matching'] as String);

return Ancestor(
of: deserializeBase64(Base64URL.encode(json.encode(ofJson))),
matching:
deserializeBase64(Base64URL.encode(json.encode(matchingJson))),
matchRoot: finderData['matchRoot'] == 'true',
firstMatchOnly: finderData['firstMatchOnly'] == 'true',
);

case 'Descendant':
final ofJson = json.decode(finderData['of'] as String);
final matchingJson = json.decode(finderData['matching'] as String);

return Descendant(
of: deserializeBase64(Base64URL.encode(json.encode(ofJson))),
matching:
deserializeBase64(Base64URL.encode(json.encode(matchingJson))),
matchRoot: finderData['matchRoot'] == 'true',
firstMatchOnly: finderData['firstMatchOnly'] == 'true',
);

default:
throw Exception('Unsupported finder type: $finderType');
}
} catch (e) {
throw Exception('Error deserializing finder: $e');
}
}
}

class GetTextCommandExtension extends CommandExtension {
String getTextFromWidget(Text widget) {
// If direct text data is available, return it
if (widget.data != null) {
return widget.data!;
}
// Otherwise get text from textSpan
if (widget.textSpan != null) {
return getTextFromTextSpan(widget.textSpan!);
}
return 'TEXT_NOT_FOUND';
}

String getTextFromTextSpan(InlineSpan span) {
if (span is TextSpan) {
final List<String> textParts = <String>[];

// Add current text if not null
if (span.text != null) {
textParts.add(span.text!);
}

// Add children's text recursively
if (span.children != null) {
for (final InlineSpan child in span.children!) {
textParts.add(getTextFromTextSpan(child));
}
}

return textParts.join('');
}
return 'WIDGET_IS_NOT_TEXT_SPAN';
}

@override
Future<Result> call(
Command command,
WidgetController prober,
CreateFinderFactory finderFactory,
CommandHandlerFactory handlerFactory) async {
final GetTextCommand dragCommand = command as GetTextCommand;

// Create finder for Text widget
final type = dragCommand.base64Element;
// decodeBase64 to json
SerializableFinder serializableFinder =
FinderHelper.deserializeBase64(type);

final Finder finder = finderFactory.createFinder(serializableFinder);

// Get the widget element
final Element element = prober.element(finder);

// Get text data from Text widget
String textData = '';

if (element.widget is Text) {
textData = getTextFromWidget(element.widget as Text);
} else {
textData = 'Found element is not a Text widget';
Alpaca00 marked this conversation as resolved.
Show resolved Hide resolved
}

return GetTextResult(true, data: {'text': textData});
}

@override
String get commandKind => 'getTextWithCommandExtension';

@override
Command deserialize(
Map<String, String> params,
DeserializeFinderFactory finderFactory,
DeserializeCommandFactory commandFactory) {
if (kDebugMode) {
print('Available keys: ${params.keys.toList()}');
print('Params content: $params');
}
return GetTextCommand.deserialize(params);
}
}

class GetTextCommand extends Command {
final String base64Element;

GetTextCommand(this.base64Element);

@override
String get kind => 'getTextWithCommandExtension';

GetTextCommand.deserialize(Map<String, String> params)
: base64Element = params['findBy']!;
}

class GetTextResult extends Result {
final bool success;
final Map<String, dynamic>? data;

const GetTextResult(this.success, {this.data});

@override
Map<String, dynamic> toJson() {
return <String, dynamic>{
'success': success,
if (data != null) ...data!,
};
}
}
78 changes: 59 additions & 19 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import 'package:demo/get_text_command.dart';
import 'package:flutter/material.dart';
import 'package:flutter_driver/driver_extension.dart';
import 'package:flutter_driver/src/extension/extension.dart';

import 'extended_commands.dart';


void main() {
enableFlutterDriverExtension(
commands: [DragCommandExtension()]);
enableFlutterDriverExtension(commands: [DragCommandExtension(), GetTextCommandExtension()]);
runApp(const MyApp());
}

Expand All @@ -29,6 +28,7 @@ class MyApp extends StatelessWidget {

class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});

final String title;

@override
Expand All @@ -45,22 +45,62 @@ class _MyHomePageState extends State<MyHomePage> {
title: Text(widget.title),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ReorderableListView(
padding: const EdgeInsets.all(8),
children: items.map((item) {
return ListTile(
key: ValueKey(item),
title: Text(item),
tileColor: Colors.grey.shade200,
);
}).toList(),
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) newIndex -= 1;
final item = items.removeAt(oldIndex);
items.insert(newIndex, item);
});
},
body: Column(
children: [
// Original ReorderableListView
Expanded(
child: ReorderableListView(
padding: const EdgeInsets.all(8),
children: items.map((item) {
return ListTile(
key: ValueKey(item),
title: Text(item),
tileColor: Colors.grey.shade200,
);
}).toList(),
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) newIndex -= 1;
final item = items.removeAt(oldIndex);
items.insert(newIndex, item);
});
},
),
),
// TextSpan example
const Padding(
padding: EdgeInsets.all(16.0),
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: 'Amount: ',
style: TextStyle(
fontSize: 18,
color: Colors.grey,
),
),
TextSpan(
text: '100',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
TextSpan(
text: ' USD',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
),
],
),
key: ValueKey('amount'),
),
),
],
),
);
}
Expand Down