 Hi, guys. Good morning, everyone. It's nice to have you all here. Welcome you for the session. Hope you're having great time. Today, I'm going to deliver the talk on case study, focusing mainly on APM in an enterprise banking organization. So here is our agenda. So first, I'll start with intro into mobile banking. Then we'll go through the challenges, what we have faced in an enterprise banking automation, and how we have overcome those challenges, the solutions, and then our continuous integration journey with mobile automation. So before actually getting into the topic, may know like who are actually involved in the mobile automation right now, or any mobile testing. Okay. It's just very few. But still, I think most of you would have worked on Selenium so you can easily correlate because APM as Dan mentioned, it's all similar to Selenium. So let's jump into the topic. So what's mobile banking? So if you think of banking, there are n number of services provided by the bank, such as some or like some of the online services, like you can check your transactions, you can pay to the third party billers, you can do the international money transfers, and some things into stock exchange, investment, and customer support, content services, so many things. So previously all these services were actually managed individually, somewhere like managed in the online portal, like a web portal, and for some of the services, like the customer act to visit the branch in person and get it done. But now in this digital world, like everything has to be digitalized, and everything is in our hand in one single application. So automating the UI automation, mainly for such complex application is not that easy, especially when we have to test it against both iOS and Android, Appium is the best choice open source tool for automating such application. So here is just the gist of our framework tool set, what we have used. It's mainly Appium based on Java Client, and TestNG as our test framework, and then Apache Maven is the build tool. So these are some of the top challenges which we have faced in the banking automation. I'll be going through one by one and giving the solutions for all this, and then we'll jump on to the CI stuff. So the first one is maintaining the app state during the failures. So let me just give you an example from the banking perspective. Say if you are testing the payment transaction in your test environment, and the payment transaction has been failed because of N number of reasons. It could be due to backend, or it could be some network issue or whatever. As you all know, like if some of the test case fails, it is actually very hard to continue with the next test smoothly, right? Because it won't be in the same page as expected, right? So Appium, so in case of browser, as you know, you can just refresh the browser page, or you can actually provide the URL value in the URL field, so it can actually navigate to the specific page. But in case of native application, you can't actually refresh the page, right? You have to actually restart the app. So restart in the sense it's like killing the application and starting the application once again. So with Appium, we have like a basic, some of the commands to restart the application, which is driver.reset. But the problem here is mainly when we are running these tests in the CI infrastructure, especially where we are running on things like a mobile device cloud or no, which is available in the lab, all these Appium nodes are actually set with the full reset flag. So why we have full reset flag? The appium node is mainly to ensure the clean state of the app, right? In browser, you can just hit the URL, you will always get the latest code base. But for native, we actually have to, you know, uninstall the existing old version and download the latest version and no proceed with the testing. So that is the main reason we have the full reset flag in the appium node. So in that case, if we use like a driver reset, what happens is it actually, you know, uninstalls the app and then reinstalls the app. So it basically starts from the starting, especially, you know, in the financial application and all, if I need to go through the payment scenario, I can't just directly get in because first I have to go through the user registration. So it is by providing user ID password. And then it comes up with like a OTP verification to a mobile. And then you have to set in some of the authentication process, accept the terms and conditions. So there are n number of screens, you know, you have to go through. So in that case, it is like a long time consuming process if the test case fails, right? So what we can actually do here is, so we can write a small set of method like a handle failure. This basically gets executed after every test. So it will first check the status of the test. If the status of the test is failure, then it actually quits the driver, which basically, you know, kills the existing driver session. And then it calls launch app session. So launch app session is nothing to create the new driver instance, right? So first time when we are starting the test, when we launch the session, we can just pass app restart as false, but especially during failure, we can pass app restart as true. So when we pass this app restart value as true, what it does is it sets the capability of no reset flag to true. So now it is actually, you know, even though in the Appian node, it is full reset, especially during the failure, we set the no reset to true and all the remaining other capabilities, you know, remind intact and then we launch the driver session. So in this case, it will just launch the app without uninstalling the app and it can just go into the main screen like a login page. We can just log in and continue with our test case. So it actually saves a lot of time doing this. So we have used the solution mainly for, only for iOS, we can actually use the same thing for Android, but in Android, there is even more easier solution. So in Android, each of the screen is actually associated with something called activity name, but in iOS, we don't have such things. So if we specify the activity, like basically the app package and the activity name, and then if we start the specific activity, it goes to that screen. For example, in our case, welcome page has something called, you know, welcome activity and login has something called login activity, registration has something called registration activity. So what we can do here is in case of failure, just start only the login activity. So it goes to the main login page and we can just log in and proceed with the next test. So the next challenge is the complex app flows. Again, in banking, there are n number of complex app flows, but I've just taken couple of examples here. The first one is the SSO jump. So SSO is basically, you know, the single sign-on, which actually deep links from the native application to the browser. For example, in my application, we have something called, you know, MyContacts. And if I click on that link, it takes to the browser and we can do all the verifications. And we have to actually come back to the native application to proceed with the, you know, next test. So there is no specific steps in Appium to navigate from native to browser because just by tapping on it, it automatically navigates to the, you know, browser. And with the usual commands, you can actually, you know, verify all the stuff in the browser. But the challenge is mainly, how do we come back to the native? That's the main challenge. It is pretty simple in Android because in Android, we have a back button and we can just invoke this, you know, key code. Four is the equivalent key code for the back button in Android. So if you just press that, it comes back to the native in Android. But in case of iOS, there is no such things. So how did we solve this in iOS? So in iOS, each of the native application has got something called scheme name, okay? For example, if you are an iPhone user, I mean, go to your Safari browser and if you just type in SMS, colon, double slash, it actually opens up the alert saying, do you want to navigate to the messaging? So each of the iOS native application has something called scheme name. Mostly you would get that scheme name from the iOS native code base. So get that scheme name and once you complete your verifications in the browser, you can just provide that scheme name in the browser URL and it just navigates back to the native. So the next one in the complex app flow is like switching between, you know, natives and like a couple of native applications, right? So especially here, I've taken an example of switching between native and the settings app. So why do we need to first of all, switch between the settings and our native? Before launching any application in your test environment, you have a requirement, you know, you have to configure some of the things, right? First you install the app, you go to the phone settings and configure something like selecting the environment or providing some values and then launch the application, right? So once again, if we run this in the CI infrastructure with, you know, the full reset flag, what happens is first time I install the app and then second driver session is the settings, go and change the configuration and once again when they create the native driver session, it is again uninstalling and installing that by losing the context of the configuration in the settings, right? So here actually for specifically for iOS, Appium has a nice capability called process argument capability. So process argument capability actually allows to pass in all the command line arguments and the environment variables to the application. So what we have done here is like, we created the array list of process arguments. So all the native application, they will have certain key value name pairing in the code base for all these configurations. So get those key value names from the code base or from your developer. For example, in our case, the environment, key name is environment.selector and the value should be container. You know, some of the test environment value, right? And also some, we can even check, I mean, enable the checkbox by providing the key value is equal to X. Yes, if we provide that, it actually enables the checkbox. So with this, we can actually set all the configurations in the settings. And once we provide this array list, we can just set this as a capability to the iOS mobile capability. And then start the session. So by default, the app launches with all these configurations. If there are different configurations in the default, it overwrites all those configuration. So you don't have the need to go and, you know, switch between the settings and the native application. So this solution is specifically for iOS. This is because the process argument capability is only available in iOS Appium. We don't have this for Android. So for Android, what we have done is actually, we can once again follow the same solution as passing the no reset. But what we have done is we actually, you know, communicated with developer and we have asked them to provide one top layer of the screen on top of the, you know, application, having all those configuration settings. So when I launched the test app, the first page actually shows the configuration value. I can just directly select all that and then it navigates to the actual app. So the next comes the multiple application state. How do we handle the multiple application state? So basically the behavior of the application actually changes, you know, based on the input or based on the flows you follow, right? Especially in a banking being a secure environment, during the registration process, I have to go through the OTP process. So OTP is mainly like, you know, your mobile number has to be verified. If I don't go through the OTP process, what happens is when I carry out the payment transaction, it won't allow me to execute that. It once again asked me the OTP verification, right? So in the test environments, ideally there won't be any fake test mobile numbers to get all these verification, right? So there is a web service portal where we can actually provide, you know, the other user ID, basically the login ID and it returns back the OTP verification. So in this case, in between the test, we actually have to talk to the web service API call. The next example is, no, I just want to explain with one example, like if I run the same test again and again, it changes the behavior of the application. Example, say if you are, you know, scheduling the recurring payment for some particular biller, say you're paying some electricity bill and you are actually, you know, scheduled a recurring payment transfer for the electricity on a month-on-month basis. So today I've written the script, I've run it, it works fine, but next day it would fail. The reason behind that, it has already been scheduled, right? So I can't reuse the same data again. So one way is like you can actually execute some of the prerequisite or the post-requisite to go and delete those recurring payments. But the problem is, you know, all these are again like time consuming because the main intention of my test is only to do the recurring payment and not the delete recurring payment, right? So once again, if there are like some of the web services calls available to, for you to execute those pre and post-requisites, you can actually call those web services. So to execute those web services call, we have a rest client library in our framework, which opens up the HTTP connection through the Apache HTTP connection. And it has got set of libraries like, you know, set header, request body, get put, all the normal web services call, gets the response, parses it, and then it gives us the input. So the next is the multiple test environment. So you all know like there are different test environments, like basically dev environment, test environment, you know, staging and all that. So especially for banking, we have like multiple test environments. So for example, in one environment, say test one, you will have a production backend code base. And in another environment, say test two, we have an ongoing development code base, right? So that testers can actually, you know, switch between different environments. So each of these environments, you have to have different data conditions. You can't use the same data. That's like a common for every application. And even like on a single environment, we have to verify multiple data. For example, if I just need to verify a card, or if I just need to verify the account, it's not one single card or one single account. We have multiple data like credit card, visa card, you know, accounts like home loan account, smart account, so those things. So for all these test data handling, especially for the static test data, we use the test entry. So if it is like a normal, you know, one parameter, just one data is enough for the test case, we can directly get it through the at parameter annotation. But if it is like a complex test data, we actually get it from the XLS sheet. You can even take it from the, you know, CSV or the XML format, and we use data provider annotation for it. Suppose if there is a dynamic data, so all these are with like a static data, right? So dynamic data, like it can actually keeps varying time to time. So in that case, we once again use a rest client library at the time of, you know, execution, it gets the dynamic data, and then it compares with the UI. And then comes the complex backend. So instead of actually explaining the whole backend, you know, complex thing, I just take a simple example. Say if you have ordered a credit card with your bank, right? So first you get the card. The first step is to activate your card, and then you keep using it for your remaining transactions, right? So first activating the credit card is just like a one-time activity, right? So especially in test environments, you can't use the same card data again and again to activate the same test case. So we call this test data as burnable test data. So you can say, I suppose requisite, okay, I can just go to my database and update the card status. But the problem in banking is there are a lot of legacy systems available. It is not just one database which interacts with your native. There are n number of system. So there are like a lot of batch jobs scheduled on a nightly basis or on weekly basis, which goes and updates the card status or whatever it is in a different system. So if I just go and update in one single database, it might work for like a couple of times. But later after three, four runs, it actually corrects the test data. And we as a tester in a specific project, we won't be actually knowing where all downstream channels it is connecting to. It's like a big nightmare actually. So in this case, what we can do is we use an API mock server, which is based on the Node.js server. Basically we can track all the request and response from the Fiddler or from the Charles proxy. And then we can stop those request and response in the mock server. So the advantage of the mock server is with the same data actually, you can create n number of rules. For example, with one single user ID, if I need to have multiple card status, I can create rule like if I log in with pin number, say one, two, three, four, give me the card status back, say as in active. If I log in with three, four, five, six, the card status comes with active. So different kind of rules you can actually create with this mock data. And it helps us a lot to avoid the data corruption, especially for the dev specific bills. So the next comes our favorite challenge, which is the device multitude. So in market right now, we have like n number of devices available with iPhone, iPads and it's worth to mention, like we don't just test only in iPhone, we have to test even in the iPads and even in the Android tablets. So we have different models of Android like Samsung, LG and Samsung itself have different models, right? So how do we achieve running all these devices in the CI environment? So I'll just start with a simple CI goal, which everyone of us is aware. So the CI goal is the developer checks out the code and then it initiates the dev bill pipeline. Once the app has been generated, then it initiates the test bill pipeline. So the main challenge is like executing the test, which is what we are going to discuss now. And after the execution, it generates the report and sends back the result to all these stakeholders. This is a normal, simple CI goal, right? So how did we achieve this in the CI environment? So for us, before even started with the, when we started with the automation, like we thought, okay, we can achieve it through Selenium grid, I mean connecting to all the emulators and simulators. So we had different APM nodes configured on all the missions. We can even configure more than two, three APM nodes on the same mission, running on the different port connected to Android emulators, to three Android emulators and simulators. But we had a major challenge in executing this. The major challenges were, especially when the test is actually getting launched, the emulator also gets reset and it will launch. We can avoid that, but it's especially ideal to reset the emulator because it would actually clear off all the old issues and all. So when it is actually resetting the emulator and it starts take a lot of time to restart the emulator. And also like if we have like more than three, four, you know, APM nodes running in parallel. So when it is starting the emulator, it takes longer time, right? So it may just start like two emulators at a time. And the third one, it just gets stuck and it hangs on. So we can't actually continue with the execution. And with iOS now with the recent version, we have the capability of running multiple simulators in parallel, but the older versions, it was not even supporting the, you know, parallel execution of simulators on a single machine. And another main challenge is like the advance interactions, like, you know, the touch action, flicks, floor, all those things, it works sometimes, you know, in simulator it doesn't work sometimes and it works fine in the physical device and vice versa. And we also have seen some of the app crashes, which happens in the physical device, doesn't happen in simulator and even vice versa. So the main intention of us here is to test the physical device because the end user is actually using the device, not the emulator. So we don't have to worry about all this, right? So we wanted to migrate to, you know, integrate with the physical device testing. So what we actually did is even before starting the automation, we had the mobile labs in place for us for the manual test execution as well. So in fact, mobile labs is one of the sponsor for the Selenium conference. They had a stall in Kalinga, so we have been using this mobile labs for quite a long time. We started with the manual testing actually. So it has something called, you know, in house, basically it is like an in house private mobile device cloud and it has a manual cart like this where you can actually configure all the devices into the particular server. And it has got a device portal where each one of us can create, you know, the login ID credentials and login to the system. There are different access level provided for the team. So once you log into the system, it displays what are the devices actually connected to this, you know, cart. So you can connect both Android, iOS or any device. So once it displays, you can actually physically grab that device. So instead of having it in your hand and testing the manual testing, you can actually know the device gets displayed on your laptop or the mission and you can actually do all the normal infractions what you do, you know, on your hand. So how did we achieve automation from here? So mobile labs is giving a flexibility like they have provided a software application called device bridge, which is like a EXE for Windows and it has a DMG for Mac. You can install the device bridge software on your laptop. And once you install it and log in with the, you know, the IP address, basically your credentials and providing the MDC, MDC is a mobile device cloud, providing the IP address, it once again lists all the devices. And if you click on connect, what happens is it actually remotely gets connected to your laptop, which is like, you know, physically connecting the device through USB. So if you connect through portal, the remote connection doesn't happen. You can just visually see and interact. But if you connect through device bridge, it actually gets connected, you know, remotely to your laptop. So if you do, if you go to terminal and type in ADB devices, it would list all the Android devices. And similarly, if you just type in instruments, minus S devices, it would list all the iOS devices. So Appium can actually identify those connected devices. So it can continue with the automation. So this is how our CI infrastructure looks like. We have a TeamCity CI integration, which is similar to Jenkins. So it is the enterprise CI tool. And from there, it can talk to Selenium Grid. So in Selenium Grid, we actually have configured different missions, like different virtual missions and also the Mac minis. And each of these missions have a device bridge software installed on it. So through device bridge, it can connect to the mobile labs and get all the physical device connections. And each of the node, I mean, each of the missions, we also have configured the Appium nodes. And through the node config, and these Appium nodes in turn actually points to physical devices. So in emulator, we can have maximum of, you know, three to four connections. And once again, it is very difficult to, when you're running in parallel, it doesn't launch properly. But here, for the normal system configuration, you can have up to eight to 10 devices connected to the mission. And if you have an iron configuration, you can even go up to like 13 to 15 devices on per single mission. So you are actually increasing, you know, the parallel runs. So that's the main advantage here. So combining the framework and the CI, this is how the infrastructure looks like, architecture looks like. So based on Apache Maven is our build tool, we use Appium Java. And I haven't talked about the UI object repo because most of you are aware of it. So we all place all the UI elements in the object repository for both Android and iOS. And TestNG is our, you know, for the static data and REST API is for getting the dynamic data. And once again here, Team3 talks to Selenium Grid, in turn talks to Appium nodes, and then it connects to the mobile labs. So the main benefits of this, what we are getting is like a wider device coverage. Say suppose if we have to maintain all the devices even for manual testing, you know, covering all these devices is like a nightmare. So we have a specific infrastructure team who actually takes care of the Selenium Grid and also, you know, the mobile labs. So, and also you don't have to have that maintenance headache. And the executions are very faster. We can achieve it in parallel. And you get an early feedback because of this, that's reducing the release time to market. So in a nutshell, basically, though Appium is like a very complex, you know, enterprise application, I mean, banking is an enterprise complex application, we can still achieve UI automation with, you know, the Appium as an open source by integrating with a lot of other open source and some of the, you know, solutions like source labs or mobile labs. So thumbs up for Appium. So before taking up the questions, I would like to thank you all for your, you know, presence here. Thank you so much for the Selenium community members for giving me this opportunity. Yeah, so here is my contact. You guys can take a note of it and you can contact me anytime. I'm happy to take any questions right now. Thanks Parvita. Real work problems and real solutions which we can take away. Thanks a lot for this. So we can have questions. You just raise hand and I bring mic to you. It was a wonderful slides and presentation actually. I just go through, go back to the previous slides before that labs, before that, before that, before that. Okay, here you have mentioned one thing. I remember like, you said like, while launching three or four simulators from your mission will face a hang up of third simulators but how the mobile labs is handling that? Say for example, even I have tried to launch three more simulators in the same VM or in Mac mini. How it is handling? So basically in mobile labs, we are not handling with simulators or emulators, right? It is basically a physical device. So the device has already been connected. You are actually not restarting the device or you are not actually connecting to the device at the time of execution. It is already in place. So that's what I've shown here. Once again, I'll just, yeah. So this setup is already existing, right? So you have like different virtual missions and different Mac minis. Each of these missions are actually having a device bridge software, which is connected to the mobile labs. So if you open this software and you know, connected to, you can actually connect to all the devices. So this is like a one-time setup, similar to, you know, setting up the browser nodes in Selenium Grid. We are already having like set up the Appium nodes here. So apart from the Appium node, the first step is first we have to set up the device bridge, connect to the devices. So the devices could be just not two or three. You can connect up to like eight to 10 devices. So if you like list ADB devices and if it is connected to 10, it would list all the 10 Android devices. So it's already been connected. You don't have to know specifically connected at the time of execution. And from the Appium node, like based on different, it is all running on different port. These Appium nodes are actually connected to each of the device based on the capabilities, right? So we provide the device capabilities in the node config.json. So we provide like one of the node, connect to Samsung S8 and version is 8.0. And the second node connect to, you know, iPhone 7 and version is 11.0. So like that, it is already all set up. So it doesn't have to launch at the time of execution. You just have to launch only the test. So we don't face that, you know, performance issues. The one more thing I just want to play up my clear. Maybe I'm not understanding the event. If you're trying to launch more than 50 MB of application, say for example, my application is more than 50 MB. Say for example, I'm trying to install every time and I'm trying to get the user feedbacks every time. Consider kind of gaming applications, okay? So at that time, even though I just tried to connect this kind of source labs or whatever the test labs, will it handle automatically the launch progress of your particular connected device, even at different nodes? That's what my question is. So you are not asking about the device launch here. You are asking about the app launch. App which in turn handling the device memory, correct? Yes. So basically here it's all about like how much MB your app is, right? So if suppose the app is like having huge amount of MB, then of course it takes a lot of time. But if you compare to the emulator in physical devices, it is much faster. And also you can have like kind of set the time out in your framework as well as in the Selenium node config. You can just say this is the time out. So it actually, say suppose if it is taking a longer time, you can increase that time. Otherwise you can have the default time. But yeah, I don't have a specific answer for this. But what we have seen is like in such cases, we usually inform the developers and ask them like why it is taking a lot of time to launch? Because these scenarios doesn't happen only in the source labs or mobile labs, right? Even in your local execution, it could even happen because it is all about the app, app and these. So in that case, you have to speak with your developer to see like what is actually causing that delay and get that sorted out. To add to this question basically like just what I said, yeah, first thing I would do is why the hell it is taking so much time on launch. So I'll be actually filing a bug against it and I want it to be fixed before actually I go and automate. And second thing, if I have to bear with that. So the like in last slide we just said, we can go for no reset in the next runs, basically. Then we can save time. Especially on your local runs, you can have the no reset, but I don't suggest on the CI runs, especially when we are starting the app because every time the main purpose of testing is to get the new version of the build and then install. So we do the no reset only when we are restarting the driver in case of failure. Otherwise it's not advisable. Yeah. Can you explain more about the UI object that was you were talking about? Yeah, yeah. So basically, I know many of us here actually follow the page object model. This is something similar to it, but we don't have a specific page object model as such because what a disadvantage I have seen here is basically say suppose there is a common close button or a submit button, right? And say if the ID or the accessibility ID is a close and it can be present in different screens, right? So if you go with the page object model, you have to define this close button for each and every screen, right? So instead what we do is like, we created a properties under like a UI element package separately for Android and separately for iOS. And so these properties basically contains the locator name and the values. For example, in this case it has a locator name called close underscore button and the value is ID comma the unique ID or X path comma whatever it is. And in our framework, what we do is using the Java reflection concept. At the runtime, we specifically go and grab that property from the memory. So based on the locator name, we map it with the value and then we go and identify it. So here actually it is helping us to maintain all the common property. So if the close button ID has been changed, it will be changed across different screens, right? So in page object model, you have to go and once again change it everywhere. But in this case, if I just change in one property file, it gets reflected everywhere. It is actually internally used by the, you know, this case. And also another thing is especially in the banking, there are a lot of elements like reusable elements. For example, you know, in the accounts screen, you will not have a specific ID values, right? Say if the account is like a home loan account and a smart access account. So we usually identify through the X path, but it is not ideal to go and define this X path for each of the accounts. It's like again, you know, too much of maintenance, right? So in case of Android, we say like Android widget text view and we give something like a dollar value in the property file. So what we do is from the data sheet, we actually provide the actual account number or the name. And during the execution, it replaces the dollar value from the data sheet, the value what we have taken, and it replaces and makes the X path as like Android widget text view, smart access, and then it finds it. So it allows us to maintain also know the UI elements very easily. Yeah. Is it possible to clear the cache through Appian? So through Appian, I'm not sure we have a specific command, but we can actually go to the... So if you have the full reset flag, it basically clears the cache, clears all the data before actually launching the app. It is used. Okay. So I installed one time. Okay. I'm using the same app without installing. Okay. Just I'm re-invoking and testing the applications. Okay. So that time I have to change the environment where it goes, means we have to clear that cache. In iOS, yeah, reset app through Safari is possible, but instead of that going manually, we have to do that. Okay. So in this case, that is what I have shown here. Like for iOS, you have a process argument capability and definitely the clear cache would be there in your settings, right? So if you set that capability, especially when you are handling the failure, you can even set that process argument capability with a specific configuration and then start the driver session with no reset. So it would actually override the existing configuration with the configuration what you have provided and then it starts the app. Is there any way to view the live experience? Sorry? Is there any way to view the live experience? Oh, I don't have a live demo actually here, but I can explain. So do you want me to explain the CI execution once again? Yeah, sure. Yeah, sure. So what we do here is, as I mentioned, like Team City is our enterprise CI, right? So what we have done is we have a standard dev jobs in the CI pipeline. So for our test build pipeline, we actually create a dependency with the dev build job, right? So after the every run of dev build job, it can actually trigger the test build pipeline, but in terms of UI automation, no, you don't have the need to test for each and every build because we almost get like eight to 10 bills per day and it doesn't make sense to test it that many times. So what we have done is we have scheduled in the scheduler. So it runs on a nightly basis and it takes the latest build from the dev build pipeline. So how it takes the latest build is basically based on the dependency what we have created, right? So if you provide dependency.devbuild number, it actually returns the latest build number and what our dev pipeline does is once it completes, it generates the APK, it uploads into the storage or report something called Artifactory. So every time when the dev build job runs, it generates the APK and it creates like a specific URL path with the dev build number and it uploads into the Artifactory. So our test build pipeline, so we know the dev build number here based on the dependency and then it goes to the artifact path, just downloads that latest version and then it starts the execution. So in our framework, what we have, there is a flexibility like you can either use the grid run or you can just run it even in local. So we have a property called usegrid. So if the usegrid property is set to true, it runs against the grid. So what we have done is in the team city CI parameter, we actually provided usegrid as true. So it goes and picks up the grid IP address and it hits the particular grid and all the nodes are under the grid and based on our test, for example, since we are using Maven, we maintain all the profiles in the maven.pom.xml, right? So for example, I'm running say some account test cases, we create a profile in the Maven profile called account information underscore, maybe the test environment value, say account information underscore defy or whatever. Again in the CI, there is a build step actually, there is a configuration build step. So in the build step, you can create the, you know, give the Maven goal as clean install and the profile and the profile ID, right? So it goes to the pom.xml file, checks for the mapping profile and it goes under the profile. We also create the test and G, we map it to the relevant test and G file. So it goes and picks up the relevant test and G file and in the test and G file, we actually pass in all the device capabilities as a parameter. So we give like Samsung and the version and all that. So based on that, it detects which APM node it has to run and it goes and hits that particular node. And obviously that node in turn is connected to the physical device in the mobile labs and it starts the execution. Yeah. Is that answers your question? Cool. Oh, my guru is here. Hey Pavitran. Just to mention, like I worked with Krishnan like quite a long time before back in 2010, 11, I was new to automation. I used to bug him a lot to ask a lot of questions. Thanks a lot for that. Yeah. So I had a specific question about on the mobile labs that you have been using, right? So you mentioned that, you know, I mean you'd probably kick off the device bridge binary on each of the Mac minis or whatever. And that basically establishes a kind of like, does like a remote USB port forwarding kind of a thing wherein it lets me access a device that is sitting on a remote machine which is basically the mobile lab strolling. Yeah. And normally I get to experience as if it is basically connected to my local machine. Right? Yes, yes, yes. So when you are spinning off your APM, right? So how does, you know, I mean, how do you basically map when I mean which APM server is mapped to which of your device or do you basically mean put in the device ID or whatever inside your APM configuration or how does it happen? Yeah. So the first thing is like, we don't actually spin off the APM node during the execution time. It's already there in the Selenium grid. So how that configuration works is say in the mobile labs device portal, there is some of some of some configuration says like, you know, the device name, version, the UDID of the device and all that. So based on the UDID, we actually map to the specific device in the device lab. And from the APM node perspective, what we do is like, so this mobile lab devices, the names are like so unique. We maintain it as a unique and also the UDID is unique. So based on the unique names in the APM node actually in the node config.json, we provide the desired capability as device name as, you know, s8 underscore ash five. So there could be more of s8, but we just name it like, you know, s8 one, s8 two, something like that. And we just map it with the particular unique device name and also the version. Yeah. Did I finish too early? Okay. Yeah. So in the test engine, what we can do is we can actually create some set of suits, right? So we create like more than two to three tests. So the first test actually would be pointing to, you know, s7, eight dot zero. The second test would be pointing to s8. Third test would be pointing to, you know, some LG or something. And in the test engine, you can actually mention parallel equal to test and you can give the threat count. So if you have the three test, you can give threat count as three. If you have four, you can give the threat count as four, right? So once the test execution starts, based on the threat counts, it initiates all the, you know, test. And just based on the device mapping, it goes and hits that note. Do you want me to show the example of the test engine or we are running out of time? Okay. Yeah, we can discuss outside. So this was a lot of real-time insights we all face. So thanks a lot coming along and sharing with us. So up next, we have... Thanks, Anthony. Yeah.