AppiumTestDistribution / appium-flutter-integration-driver

MIT License
32 stars 12 forks source link

Unable to set text in textfield #96

Open rohitbhoite opened 2 months ago

rohitbhoite commented 2 months ago

I am having below widget in one form and using flutter-integration@1.1.3 driver

TextField(
                  key: Key("editText"),
                  decoration: InputDecoration(
                    border: OutlineInputBorder(),
                    hintText: 'Enter a search term',
                  ),
                ),

I am trying to set the text using below code but its not working. I also tried using sendKeys method but its not working as well.

await browser.flutterByValueKey$("editText").addValue("a");

Got below error:

image

Have checked below code as well but somehow its not working https://github.com/AppiumTestDistribution/appium-flutter-integration-driver/blob/main/test/specs/test.e2e.js

Logs

[0-0] 2024-09-16T12:55:22.079Z INFO webdriver: COMMAND elementClick("00000000-0000-0035-0000-006500000003")
[0-0] 2024-09-16T12:55:22.079Z INFO webdriver: [POST] http://127.0.0.1:4723/session/a8061f2f-407f-49e2-9d29-1d77ae7d499c/element/00000000-0000-0035-0000-006500000003/click
[0-0] 2024-09-16T12:55:22.153Z INFO webdriver: RESULT null
[0-0] 2024-09-16T12:55:22.153Z INFO webdriver: COMMAND flutterByValueKey$("<Screenshot[base64]>")
[0-0] 2024-09-16T12:55:22.153Z INFO webdriver: COMMAND flutterByValueKey("key", "<Screenshot[base64]>")
[0-0] 2024-09-16T12:55:22.153Z INFO webdriver: [POST] http://127.0.0.1:4723/session/a8061f2f-407f-49e2-9d29-1d77ae7d499c/element
[0-0] 2024-09-16T12:55:22.153Z INFO webdriver: DATA { using: 'key', value: 'editText' }
[0-0] 2024-09-16T12:55:22.471Z INFO webdriver: RESULT {
[0-0]   'element-6066-11e4-a52e-4f735466cecf': '3ad998dc-3ad9-98dc-3ad9-98dc3ad998dc',
[0-0]   ELEMENT: '3ad998dc-3ad9-98dc-3ad9-98dc3ad998dc'
[0-0] }
[0-0] 2024-09-16T12:55:22.472Z INFO webdriver: RESULT Element {
[0-0]   sessionId: 'a8061f2f-407f-49e2-9d29-1d77ae7d499c',
[0-0]   elementId: '3ad998dc-3ad9-98dc-3ad9-98dc3ad998dc',
[0-0]   ELEMENT: '3ad998dc-3ad9-98dc-3ad9-98dc3ad998dc',
[0-0]   selector: '',
[0-0]   parent: Browser {
[0-0]     sessionId: 'a8061f2f-407f-49e2-9d29-1d77ae7d499c',
[0-0]     capabilities: {
[0-0]       subcommand: 'server',
[0-0]       address: '0.0.0.0',
[0-0]       basePath: '/',
[0-0]       port: 4723,
[0-0]       extraArgs: [],
[0-0]       allowCors: false,
[0-0]       allowInsecure: [],
[0-0]       callbackPort: 4723,
[0-0]       debugLogSpacing: false,
[0-0]       denyInsecure: [],
[0-0]       keepAliveTimeout: 600,
[0-0]       localTimezone: false,
[0-0]       loglevel: 'debug',
[0-0]       logFormat: 'text',
[0-0]       logNoColors: false,
[0-0]       logTimestamp: false,
[0-0]       pluginsImportChunkSize: 7,
[0-0]       driversImportChunkSize: 3,
[0-0]       longStacktrace: false,
[0-0]       noPermsCheck: false,
[0-0]       relaxedSecurityEnabled: false,
[0-0]       sessionOverride: false,
[0-0]       strictCaps: false,
[0-0]       useDrivers: [],
[0-0]       usePlugins: [],
[0-0]       tmpDir: '/var/folders/sf/3f3dyp3n3fg0rbmxm_m51rvr0000gn/T',
[0-0]       browserName: '',
[0-0]       platformName: 'Android',
[0-0]       webSocketUrl: true,
[0-0]       app: '/Users/rohit_bhoite/mobile_ui/build/app/outputs/flutter-apk/app-dev-debug.apk',
[0-0]       deviceName: 'emulator-5554',
[0-0]       automationName: 'FlutterIntegration',
[0-0]       commandTimeout: 240,
[0-0]       newCommandTimeout: 240,
[0-0]       fastReset: true,
[0-0]       skipUninstall: true
[0-0]     },
[0-0]     addCommand: [Function (anonymous)],
[0-0]     overwriteCommand: [Function (anonymous)],
[0-0]     addLocatorStrategy: [Function (anonymous)]
[0-0]   },
[0-0]   emit: [Function: bound ],
[0-0]   isReactElement: false,
[0-0]   isShadowElement: false,
[0-0]   addCommand: [Function (anonymous)],
[0-0]   overwriteCommand: [Function (anonymous)]
[0-0] }
[0-0] Error in "1st AppiumFlutterIntegrationDriver test using appium latest.Flutter counter demo app"
Error: Malformed type for "text" parameter of command elementSendKeys
Expected: string
Actual: object

For more info see https://w3c.github.io/webdriver/#dfn-element-send-keys

    at Context.<anonymous> (/Users/rohit_bhoite/demo-js/webdriverio/appium-app/examples/appium-flutter-integration/test/specs/flutter.spec.ts:41:10)
[0-0] 2024-09-16T12:55:22.489Z INFO webdriver: COMMAND deleteSession()
[0-0] 2024-09-16T12:55:22.489Z INFO webdriver: [DELETE] http://127.0.0.1:4723/session/a8061f2f-407f-49e2-9d29-1d77ae7d499c
[0-0] 2024-09-16T12:55:22.957Z INFO webdriver: RESULT null
[0-0] FAILED in Android - file:///test/specs/flutter.spec.ts

Code: appium_test.dart file

import 'package:appium_flutter_server/appium_flutter_server.dart';
import 'package:testint/main.dart';

void main() {
  initializeTest(app: const MyApp());
}

main.dart file

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // TRY THIS: Try running your application with "flutter run". You'll see
        // the application has a purple toolbar. Then, without quitting the app,
        // try changing the seedColor in the colorScheme below to Colors.green
        // and then invoke "hot reload" (save your changes or press the "hot
        // reload" button in a Flutter-supported IDE, or press "r" if you used
        // the command line to start the app).
        //
        // Notice that the counter didn't reset back to zero; the application
        // state is not lost during the reload. To reset the state, use hot
        // restart instead.
        //
        // This works for code too, not just values: Most code changes can be
        // tested with just a hot reload.
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  bool _showProgress = false;
  void _incrementCounter() async {
    setState(() {
      _showProgress = true;
    });
    await Future.delayed(Duration(seconds: 2));
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
      _showProgress = false;
    });
  }

  void _decrementCounter() async {
    setState(() {
      _showProgress = true;
    });
    await Future.delayed(Duration(seconds: 2));
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter--;
      _showProgress = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // TRY THIS: Try changing the color here to a specific color (to
        // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
        // change color while the other colors stay the same.
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          //
          // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
          // action in the IDE, or press "p" in the console), to see the
          // wireframe for each widget.
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            if (_showProgress) CircularProgressIndicator(),
            const Text(
              'You have pushed the button this many times:',
            ),
            Semantics(
              value: 'counter_value',
              child: Text(
                '$_counter',
                style: Theme.of(context).textTheme.headlineMedium,
              ),
            ),
            TextField(
              key: Key("editText"),
              decoration: InputDecoration(
                border: OutlineInputBorder(),
                hintText: 'Enter a search term',
              ),
            ),
            ElevatedButton(
                key: const Key("decrement"),
                onPressed: () {},
                child: IconButton(
                  onPressed: () {
                    _decrementCounter();
                  },
                  icon: Icon(Icons.remove),
                )),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        key: const Key('increment'),
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
saikrishna321 commented 2 months ago

Please share full server logs pls

rohitbhoite commented 2 months ago

One more observation is If I generate a build for testing using command flutter run apk --debug --flavor dev -t integration_test/appium_test.dart and If I try to add any text manually in textfield, it does not allow me to type any text. Is this expected?

rbhoite-clgx commented 2 months ago
image

Also tried setting text using appium inspector send Keys function and it did not work either.

rohitbhoite commented 2 months ago

@saikrishna321 Is there any other way to implement this?

saikrishna321 commented 2 months ago

@rohitbhoite can you share sample apk?

saikrishna321 commented 2 months ago

@rohitbhoite Can you push the sample code to github repo. Can't download from unknown source.

rohitbhoite commented 2 months ago

Can you try with above main.dart file .Its just a single file. Have added code in the description section I am using ./gradlew app:assembleDebug -Ptarget=pwd/../integration_test/appium_test.dart to generate the build

rohitbhoite commented 2 months ago

Thank you for your prompt response :)

rohitbhoite commented 2 months ago

@saikrishna321 did you get a chance to check it

saikrishna321 commented 1 month ago

@rohitbhoite I'm unable to reproduce this issue. Everything works fine

   it.only('Issue test', async () => {
      await performLogin();
      await openScreen('Issue');
      await browser
         .flutterByValueKey$('editText').addValue("I'm typing in the text field");
   })

Logs: https://gist.github.com/saikrishna321/e2ff4f339a96a42676fbacab4c617864#file-flutter-issue-L354-L363

lmendes21 commented 1 week ago

@saikrishna321 I'm experiencing the exact same issue. My app initialized from the appium.dart is not allowing me to send keys. It is correctly identifying the element, it can even clear the element but not send keys.

Like mentioned above, I cannot add keys manually either, the keyboard is not displayed and a dart like pointer is instead.

This must be related how the app is being initialized, am I missing something? Some configuration, pluggin? My app is from a multiprovider, however I tried a very simple code and the exact same thing happened?

void main() {
  initializeTest(app: const TextFieldExampleApp());
}

class TextFieldExampleApp extends StatelessWidget {
  const TextFieldExampleApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Obscured Textfield')),
        body: const Center(
          child: ObscuredTextFieldSample(),
        ),
      ),
    );
  }
}

class ObscuredTextFieldSample extends StatelessWidget {
  const ObscuredTextFieldSample({super.key});
  @override
  Widget build(BuildContext context) {
    return const SizedBox(
      width: 250,
      child: TextField(
        key: ValueKey("email_input"),
        obscureText: true,
        decoration: InputDecoration(
          border: OutlineInputBorder(),
          labelText: 'Password',
        ),
      ),
    );
  }
}
saikrishna321 commented 1 week ago

Thanks for sharing a sample code snippet. Will get back.

lmendes21 commented 3 days ago

@saikrishna321 any updates on this?

saikrishna321 commented 3 days ago

@lmendes21 Sorry was snowed with work. Will see this soon. Thanks.