> ## Documentation Index
> Fetch the complete documentation index at: https://www.testmuai.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Test on Real Android and iOS Devices with Kane CLI

> Run Kane CLI native app tests on real Android and iOS devices in the TestMu AI cloud from any machine. Pick a device, pass an .apk, .aab, or .ipa build, and run single tests or suites.

export const BrandName = ({type = 'default'}) => {
  const getBrandName = () => 'TestMu AI';
  const getBrandNameLowercase = () => 'testmu ai';
  if (type === 'lowercase') {
    return getBrandNameLowercase();
  }
  return getBrandName();
};

export const KaneCliCta = () => {
  const productType = 'Kane-CLI';
  const productList = [{
    value: 0,
    type: 'Online-Browser-Testing',
    label: 'Online Browser Testing'
  }, {
    value: 1,
    type: 'Selenium-Testing',
    label: 'Selenium Testing'
  }, {
    value: 2,
    type: 'Cypress-Testing',
    label: 'Cypress Testing'
  }, {
    value: 3,
    type: 'Hyperexecute',
    label: 'HyperExecute'
  }, {
    value: 4,
    type: 'Kane-AI',
    label: 'KaneAI'
  }, {
    value: 5,
    type: 'On-Premise-Selenium-Grid',
    label: 'On-premise Selenium Grid'
  }, {
    value: 6,
    type: 'Mobile-App-Testing',
    label: 'Native App Testing'
  }, {
    value: 7,
    type: 'Real-Device-Cloud',
    label: 'Real Device Cloud'
  }, {
    value: 8,
    type: 'Smart-Visual-Testing',
    label: 'Visual Regression Cloud'
  }, {
    value: 9,
    type: 'Appium',
    label: 'Appium'
  }, {
    value: 10,
    type: 'Enterprise',
    label: 'Enterprise'
  }, {
    value: 11,
    type: 'Test-Manager',
    label: 'Test Manager'
  }, {
    value: 12,
    type: 'Professional-Services',
    label: 'Professional Services'
  }, {
    value: 13,
    type: 'Accessibility-Testing',
    label: 'Accessibility Testing'
  }, {
    value: 14,
    type: 'Agent Testing',
    label: 'Agent Testing'
  }, {
    value: 15,
    type: 'Browser-Cloud',
    label: 'Browser Cloud'
  }, {
    value: 16,
    type: 'Kane-CLI',
    label: 'Kane CLI'
  }, {
    value: 'Other',
    type: 'Other',
    label: 'Other'
  }];
  const countryList = [{
    country_code: "US",
    value: "1",
    name: "USA (+1)"
  }, {
    country_code: "GB",
    value: "44",
    name: "UK (+44)"
  }, {
    country_code: "DZ",
    value: "213",
    name: "Algeria (+213)"
  }, {
    country_code: "AD",
    value: "376",
    name: "Andorra (+376)"
  }, {
    country_code: "AO",
    value: "244",
    name: "Angola (+244)"
  }, {
    country_code: "AI",
    value: "1264",
    name: "Anguilla (+1264)"
  }, {
    country_code: "AG",
    value: "1268",
    name: "Antigua &amp; Barbuda (+1268)"
  }, {
    country_code: "AR",
    value: "54",
    name: "Argentina (+54)"
  }, {
    country_code: "AM",
    value: "374",
    name: "Armenia (+374)"
  }, {
    country_code: "AW",
    value: "297",
    name: "Aruba (+297)"
  }, {
    country_code: "AU",
    value: "61",
    name: "Australia (+61)"
  }, {
    country_code: "AT",
    value: "43",
    name: "Austria (+43)"
  }, {
    country_code: "AZ",
    value: "994",
    name: "Azerbaijan (+994)"
  }, {
    country_code: "BS",
    value: "1242",
    name: "Bahamas (+1242)"
  }, {
    country_code: "BH",
    value: "973",
    name: "Bahrain (+973)"
  }, {
    country_code: "BD",
    value: "880",
    name: "Bangladesh (+880)"
  }, {
    country_code: "BB",
    value: "1246",
    name: "Barbados (+1246)"
  }, {
    country_code: "BY",
    value: "375",
    name: "Belarus (+375)"
  }, {
    country_code: "BE",
    value: "32",
    name: "Belgium (+32)"
  }, {
    country_code: "BZ",
    value: "501",
    name: "Belize (+501)"
  }, {
    country_code: "BJ",
    value: "229",
    name: "Benin (+229)"
  }, {
    country_code: "BM",
    value: "1441",
    name: "Bermuda (+1441)"
  }, {
    country_code: "BT",
    value: "975",
    name: "Bhutan (+975)"
  }, {
    country_code: "BO",
    value: "591",
    name: "Bolivia (+591)"
  }, {
    country_code: "BA",
    value: "387",
    name: "Bosnia Herzegovina (+387)"
  }, {
    country_code: "BW",
    value: "267",
    name: "Botswana (+267)"
  }, {
    country_code: "BR",
    value: "55",
    name: "Brazil (+55)"
  }, {
    country_code: "BN",
    value: "673",
    name: "Brunei (+673)"
  }, {
    country_code: "BG",
    value: "359",
    name: "Bulgaria (+359)"
  }, {
    country_code: "BF",
    value: "226",
    name: "Burkina Faso (+226)"
  }, {
    country_code: "BI",
    value: "257",
    name: "Burundi (+257)"
  }, {
    country_code: "KH",
    value: "855",
    name: "Cambodia (+855)"
  }, {
    country_code: "CM",
    value: "237",
    name: "Cameroon (+237)"
  }, {
    country_code: "CA",
    value: "1",
    name: "Canada (+1)"
  }, {
    country_code: "CV",
    value: "238",
    name: "Cape Verde Islands (+238)"
  }, {
    country_code: "KY",
    value: "1345",
    name: "Cayman Islands (+1345)"
  }, {
    country_code: "CF",
    value: "236",
    name: "Central African Republic (+236)"
  }, {
    country_code: "CL",
    value: "56",
    name: "Chile (+56)"
  }, {
    country_code: "CN",
    value: "86",
    name: "China (+86)"
  }, {
    country_code: "CO",
    value: "57",
    name: "Colombia (+57)"
  }, {
    country_code: "KM",
    value: "269",
    name: "Comoros (+269)"
  }, {
    country_code: "CG",
    value: "242",
    name: "Congo (+242)"
  }, {
    country_code: "CK",
    value: "682",
    name: "Cook Islands (+682)"
  }, {
    country_code: "CR",
    value: "506",
    name: "Costa Rica (+506)"
  }, {
    country_code: "HR",
    value: "385",
    name: "Croatia (+385)"
  }, {
    country_code: "CU",
    value: "53",
    name: "Cuba (+53)"
  }, {
    country_code: "CY",
    value: "90392",
    name: "Cyprus North (+90392)"
  }, {
    country_code: "CY",
    value: "357",
    name: "Cyprus South (+357)"
  }, {
    country_code: "CZ",
    value: "42",
    name: "Czech Republic (+42)"
  }, {
    country_code: "DK",
    value: "45",
    name: "Denmark (+45)"
  }, {
    country_code: "DJ",
    value: "253",
    name: "Djibouti (+253)"
  }, {
    country_code: "DM",
    value: "1809",
    name: "Dominica (+1809)"
  }, {
    country_code: "DO",
    value: "1809",
    name: "Dominican Republic (+1809)"
  }, {
    country_code: "EC",
    value: "593",
    name: "Ecuador (+593)"
  }, {
    country_code: "EG",
    value: "20",
    name: "Egypt (+20)"
  }, {
    country_code: "SV",
    value: "503",
    name: "El Salvador (+503)"
  }, {
    country_code: "GQ",
    value: "240",
    name: "Equatorial Guinea (+240)"
  }, {
    country_code: "ER",
    value: "291",
    name: "Eritrea (+291)"
  }, {
    country_code: "EE",
    value: "372",
    name: "Estonia (+372)"
  }, {
    country_code: "ET",
    value: "251",
    name: "Ethiopia (+251)"
  }, {
    country_code: "FK",
    value: "500",
    name: "Falkland Islands (+500)"
  }, {
    country_code: "FO",
    value: "298",
    name: "Faroe Islands (+298)"
  }, {
    country_code: "FJ",
    value: "679",
    name: "Fiji (+679)"
  }, {
    country_code: "FI",
    value: "358",
    name: "Finland (+358)"
  }, {
    country_code: "FR",
    value: "33",
    name: "France (+33)"
  }, {
    country_code: "GF",
    value: "594",
    name: "French Guiana (+594)"
  }, {
    country_code: "PF",
    value: "689",
    name: "French Polynesia (+689)"
  }, {
    country_code: "GA",
    value: "241",
    name: "Gabon (+241)"
  }, {
    country_code: "GM",
    value: "220",
    name: "Gambia (+220)"
  }, {
    country_code: "GE",
    value: "7880",
    name: "Georgia (+7880)"
  }, {
    country_code: "DE",
    value: "49",
    name: "Germany (+49)"
  }, {
    country_code: "GH",
    value: "233",
    name: "Ghana (+233)"
  }, {
    country_code: "GI",
    value: "350",
    name: "Gibraltar (+350)"
  }, {
    country_code: "GR",
    value: "30",
    name: "Greece (+30)"
  }, {
    country_code: "GL",
    value: "299",
    name: "Greenland (+299)"
  }, {
    country_code: "GD",
    value: "1473",
    name: "Grenada (+1473)"
  }, {
    country_code: "GP",
    value: "590",
    name: "Guadeloupe (+590)"
  }, {
    country_code: "GU",
    value: "671",
    name: "Guam (+671)"
  }, {
    country_code: "GT",
    value: "502",
    name: "Guatemala (+502)"
  }, {
    country_code: "GN",
    value: "224",
    name: "Guinea (+224)"
  }, {
    country_code: "GW",
    value: "245",
    name: "Guinea - Bissau (+245)"
  }, {
    country_code: "GY",
    value: "592",
    name: "Guyana (+592)"
  }, {
    country_code: "HT",
    value: "509",
    name: "Haiti (+509)"
  }, {
    country_code: "HN",
    value: "504",
    name: "Honduras (+504)"
  }, {
    country_code: "HK",
    value: "852",
    name: "Hong Kong (+852)"
  }, {
    country_code: "HU",
    value: "36",
    name: "Hungary (+36)"
  }, {
    country_code: "IS",
    value: "354",
    name: "Iceland (+354)"
  }, {
    country_code: "IN",
    value: "91",
    name: "India (+91)"
  }, {
    country_code: "ID",
    value: "62",
    name: "Indonesia (+62)"
  }, {
    country_code: "IR",
    value: "98",
    name: "Iran (+98)"
  }, {
    country_code: "IQ",
    value: "964",
    name: "Iraq (+964)"
  }, {
    country_code: "IE",
    value: "353",
    name: "Ireland (+353)"
  }, {
    country_code: "IL",
    value: "972",
    name: "Israel (+972)"
  }, {
    country_code: "IT",
    value: "39",
    name: "Italy (+39)"
  }, {
    country_code: "JM",
    value: "1876",
    name: "Jamaica (+1876)"
  }, {
    country_code: "JP",
    value: "81",
    name: "Japan (+81)"
  }, {
    country_code: "JO",
    value: "962",
    name: "Jordan (+962)"
  }, {
    country_code: "KZ",
    value: "7",
    name: "Kazakhstan (+7)"
  }, {
    country_code: "KE",
    value: "254",
    name: "Kenya (+254)"
  }, {
    country_code: "KI",
    value: "686",
    name: "Kiribati (+686)"
  }, {
    country_code: "KP",
    value: "850",
    name: "Korea North (+850)"
  }, {
    country_code: "KR",
    value: "82",
    name: "Korea South (+82)"
  }, {
    country_code: "KW",
    value: "965",
    name: "Kuwait (+965)"
  }, {
    country_code: "KG",
    value: "996",
    name: "Kyrgyzstan (+996)"
  }, {
    country_code: "LA",
    value: "856",
    name: "Laos (+856)"
  }, {
    country_code: "LV",
    value: "371",
    name: "Latvia (+371)"
  }, {
    country_code: "LB",
    value: "961",
    name: "Lebanon (+961)"
  }, {
    country_code: "LS",
    value: "266",
    name: "Lesotho (+266)"
  }, {
    country_code: "LR",
    value: "231",
    name: "Liberia (+231)"
  }, {
    country_code: "LY",
    value: "218",
    name: "Libya (+218)"
  }, {
    country_code: "LI",
    value: "417",
    name: "Liechtenstein (+417)"
  }, {
    country_code: "LT",
    value: "370",
    name: "Lithuania (+370)"
  }, {
    country_code: "LU",
    value: "352",
    name: "Luxembourg (+352)"
  }, {
    country_code: "MO",
    value: "853",
    name: "Macao (+853)"
  }, {
    country_code: "MK",
    value: "389",
    name: "Macedonia (+389)"
  }, {
    country_code: "MG",
    value: "261",
    name: "Madagascar (+261)"
  }, {
    country_code: "MW",
    value: "265",
    name: "Malawi (+265)"
  }, {
    country_code: "MY",
    value: "60",
    name: "Malaysia (+60)"
  }, {
    country_code: "MV",
    value: "960",
    name: "Maldives (+960)"
  }, {
    country_code: "ML",
    value: "223",
    name: "Mali (+223)"
  }, {
    country_code: "MT",
    value: "356",
    name: "Malta (+356)"
  }, {
    country_code: "MH",
    value: "692",
    name: "Marshall Islands (+692)"
  }, {
    country_code: "MQ",
    value: "596",
    name: "Martinique (+596)"
  }, {
    country_code: "MR",
    value: "222",
    name: "Mauritania (+222)"
  }, {
    country_code: "YT",
    value: "269",
    name: "Mayotte (+269)"
  }, {
    country_code: "MX",
    value: "52",
    name: "Mexico (+52)"
  }, {
    country_code: "FM",
    value: "691",
    name: "Micronesia (+691)"
  }, {
    country_code: "MD",
    value: "373",
    name: "Moldova (+373)"
  }, {
    country_code: "MC",
    value: "377",
    name: "Monaco (+377)"
  }, {
    country_code: "MN",
    value: "976",
    name: "Mongolia (+976)"
  }, {
    country_code: "MS",
    value: "1664",
    name: "Montserrat (+1664)"
  }, {
    country_code: "MA",
    value: "212",
    name: "Morocco (+212)"
  }, {
    country_code: "MZ",
    value: "258",
    name: "Mozambique (+258)"
  }, {
    country_code: "MN",
    value: "95",
    name: "Myanmar (+95)"
  }, {
    country_code: "NA",
    value: "264",
    name: "Namibia (+264)"
  }, {
    country_code: "NR",
    value: "674",
    name: "Nauru (+674)"
  }, {
    country_code: "NP",
    value: "977",
    name: "Nepal (+977)"
  }, {
    country_code: "NL",
    value: "31",
    name: "Netherlands (+31)"
  }, {
    country_code: "NC",
    value: "687",
    name: "New Caledonia (+687)"
  }, {
    country_code: "NZ",
    value: "64",
    name: "New Zealand (+64)"
  }, {
    country_code: "NI",
    value: "505",
    name: "Nicaragua (+505)"
  }, {
    country_code: "NE",
    value: "227",
    name: "Niger (+227)"
  }, {
    country_code: "NG",
    value: "234",
    name: "Nigeria (+234)"
  }, {
    country_code: "NU",
    value: "683",
    name: "Niue (+683)"
  }, {
    country_code: "NF",
    value: "672",
    name: "Norfolk Islands (+672)"
  }, {
    country_code: "NP",
    value: "670",
    name: "Northern Marianas (+670)"
  }, {
    country_code: "NO",
    value: "47",
    name: "Norway (+47)"
  }, {
    country_code: "OM",
    value: "968",
    name: "Oman (+968)"
  }, {
    country_code: "PW",
    value: "680",
    name: "Palau (+680)"
  }, {
    country_code: "PK",
    value: "+92",
    name: "Pakistan (+92)"
  }, {
    country_code: "PA",
    value: "507",
    name: "Panama (+507)"
  }, {
    country_code: "PG",
    value: "675",
    name: "Papua New Guinea (+675)"
  }, {
    country_code: "PY",
    value: "595",
    name: "Paraguay (+595)"
  }, {
    country_code: "PE",
    value: "51",
    name: "Peru (+51)"
  }, {
    country_code: "PH",
    value: "63",
    name: "Philippines (+63)"
  }, {
    country_code: "PL",
    value: "48",
    name: "Poland (+48)"
  }, {
    country_code: "PT",
    value: "351",
    name: "Portugal (+351)"
  }, {
    country_code: "PR",
    value: "1787",
    name: "Puerto Rico (+1787)"
  }, {
    country_code: "QA",
    value: "974",
    name: "Qatar (+974)"
  }, {
    country_code: "RE",
    value: "262",
    name: "Reunion (+262)"
  }, {
    country_code: "RO",
    value: "40",
    name: "Romania (+40)"
  }, {
    country_code: "RU",
    value: "7",
    name: "Russia (+7)"
  }, {
    country_code: "RW",
    value: "250",
    name: "Rwanda (+250)"
  }, {
    country_code: "SM",
    value: "378",
    name: "San Marino (+378)"
  }, {
    country_code: "ST",
    value: "239",
    name: "Sao Tome &amp; Principe (+239)"
  }, {
    country_code: "SA",
    value: "966",
    name: "Saudi Arabia (+966)"
  }, {
    country_code: "SN",
    value: "221",
    name: "Senegal (+221)"
  }, {
    country_code: "CS",
    value: "381",
    name: "Serbia (+381)"
  }, {
    country_code: "SC",
    value: "248",
    name: "Seychelles (+248)"
  }, {
    country_code: "SL",
    value: "232",
    name: "Sierra Leone (+232)"
  }, {
    country_code: "SG",
    value: "65",
    name: "Singapore (+65)"
  }, {
    country_code: "SK",
    value: "421",
    name: "Slovak Republic (+421)"
  }, {
    country_code: "SI",
    value: "386",
    name: "Slovenia (+386)"
  }, {
    country_code: "SB",
    value: "677",
    name: "Solomon Islands (+677)"
  }, {
    country_code: "SO",
    value: "252",
    name: "Somalia (+252)"
  }, {
    country_code: "ZA",
    value: "27",
    name: "South Africa (+27)"
  }, {
    country_code: "ES",
    value: "34",
    name: "Spain (+34)"
  }, {
    country_code: "LK",
    value: "94",
    name: "Sri Lanka (+94)"
  }, {
    country_code: "SH",
    value: "290",
    name: "St. Helena (+290)"
  }, {
    country_code: "KN",
    value: "1869",
    name: "St. Kitts (+1869)"
  }, {
    country_code: "SC",
    value: "1758",
    name: "St. Lucia (+1758)"
  }, {
    country_code: "SD",
    value: "249",
    name: "Sudan (+249)"
  }, {
    country_code: "SR",
    value: "597",
    name: "Suri (+597)"
  }, {
    country_code: "SZ",
    value: "268",
    name: "Swaziland (+268)"
  }, {
    country_code: "SE",
    value: "46",
    name: "Sweden (+46)"
  }, {
    country_code: "CH",
    value: "41",
    name: "Switzerland (+41)"
  }, {
    country_code: "SI",
    value: "963",
    name: "Syria (+963)"
  }, {
    country_code: "TW",
    value: "886",
    name: "Taiwan (+886)"
  }, {
    country_code: "TJ",
    value: "7",
    name: "Tajikstan (+7)"
  }, {
    country_code: "TH",
    value: "66",
    name: "Thailand (+66)"
  }, {
    country_code: "TG",
    value: "228",
    name: "Togo (+228)"
  }, {
    country_code: "TO",
    value: "676",
    name: "Tonga (+676)"
  }, {
    country_code: "TT",
    value: "1868",
    name: "Trinidad &amp; Tobago (+1868)"
  }, {
    country_code: "TN",
    value: "216",
    name: "Tunisia (+216)"
  }, {
    country_code: "TR",
    value: "90",
    name: "Turkey (+90)"
  }, {
    country_code: "TM",
    value: "7",
    name: "Turkmenistan (+7)"
  }, {
    country_code: "TM",
    value: "993",
    name: "Turkmenistan (+993)"
  }, {
    country_code: "TC",
    value: "1649",
    name: "Turks &amp; Caicos Islands (+1649)"
  }, {
    country_code: "TV",
    value: "688",
    name: "Tuvalu (+688)"
  }, {
    country_code: "UG",
    value: "256",
    name: "Uganda (+256)"
  }, {
    country_code: "UA",
    value: "380",
    name: "Ukraine (+380)"
  }, {
    country_code: "AE",
    value: "971",
    name: "United Arab Emirates (+971)"
  }, {
    country_code: "UY",
    value: "598",
    name: "Uruguay (+598)"
  }, {
    country_code: "UZ",
    value: "7",
    name: "Uzbekistan (+7)"
  }, {
    country_code: "VU",
    value: "678",
    name: "Vanuatu (+678)"
  }, {
    country_code: "VA",
    value: "379",
    name: "Vatican City (+379)"
  }, {
    country_code: "VE",
    value: "58",
    name: "Venezuela (+58)"
  }, {
    country_code: "VN",
    value: "84",
    name: "Vietnam (+84)"
  }, {
    country_code: "VG",
    value: "84",
    name: "Virgin Islands - British (+1284)"
  }, {
    country_code: "VI",
    value: "84",
    name: "Virgin Islands - US (+1340)"
  }, {
    country_code: "WF",
    value: "681",
    name: "Wallis &amp; Futuna (+681)"
  }, {
    country_code: "YE",
    value: "969",
    name: "Yemen (North)(+969)"
  }, {
    country_code: "YE",
    value: "967",
    name: "Yemen (South)(+967)"
  }, {
    country_code: "ZM",
    value: "260",
    name: "Zambia (+260)"
  }, {
    country_code: "ZW",
    value: "263",
    name: "Zimbabwe (+263)"
  }];
  const [showModal, setShowModal] = useState(false);
  const [isLoading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [country, setCountry] = useState({});
  const [selectedCountry, setSelectedCountry] = useState(null);
  const [isCountrySelectOpen, setIsCountrySelectOpen] = useState(false);
  const [selectedProduct, setSelectedProduct] = useState(productList.find(el => el.type === productType));
  const [isProductSelectOpen, setIsProductSelectOpen] = useState(false);
  const form = useRef(null);
  const countrySelectRef = useRef(null);
  const productSelectRef = useRef(null);
  const currentFormData = useRef({});
  const getCookie = name => {
    const result = new RegExp('(?:^|; )' + encodeURIComponent(name) + '=([^;]*)').exec(document.cookie);
    return result ? result[1] : null;
  };
  const toggleModal = () => {
    const next = !showModal;
    setShowModal(next);
    document.body.style.overflow = next ? 'hidden' : 'unset';
  };
  const listenToRevenueHeroWidget = event => {
    if (event?.data?.type === 'MEETING_BOOKED') {
      window.dataLayer?.push({
        event: 'user_demo_booked'
      });
      window.sendAnalytics?.('RevenueHero: Meeting Booked', {
        product_interested_in: currentFormData.current.product_interested_in
      });
    }
    if (event?.data?.type === 'CLOSE_DIALOG') {
      window.sendAnalytics?.('RevenueHero: Pop Up Closed', {
        product_interested_in: currentFormData.current.product_interested_in
      });
      window.removeEventListener('message', listenToRevenueHeroWidget);
    }
  };
  useEffect(() => {
    if (!document.querySelector('script[src="https://app.revenuehero.io/scheduler.min.js"]')) {
      const script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = 'https://app.revenuehero.io/scheduler.min.js';
      script.async = true;
      document.body.appendChild(script);
    }
    fetch('https://auth.lambdatest.com/api/country-details').then(res => res.json()).then(result => {
      setCountry(result || ({}));
      const found = countryList.find(e => e.country_code == result?.iso_code);
      if (found) {
        setSelectedCountry({
          name: found.name,
          value: result.calling_code,
          country_code: result.iso_code
        });
      }
    }, () => setError('Failed to load country data'));
    const handleClickOutside = event => {
      if (countrySelectRef.current && !countrySelectRef.current.contains(event.target)) {
        setIsCountrySelectOpen(false);
      }
      if (productSelectRef.current && !productSelectRef.current.contains(event.target)) {
        setIsProductSelectOpen(false);
      }
    };
    document.addEventListener('mousedown', handleClickOutside);
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
      document.body.style.overflow = 'unset';
    };
  }, []);
  const handleSubmit = async e => {
    e.preventDefault();
    setLoading(true);
    setError(null);
    const {email, mobile_no, first_name, last_name, message} = form.current.elements;
    const formdata = {
      email: email.value || '',
      mobile_no: mobile_no.value || '',
      first_name: first_name.value || '',
      product_interested_in: selectedProduct ? selectedProduct.type : productType,
      last_name: last_name.value || '',
      isd_code: selectedCountry ? selectedCountry.value : country.calling_code || '',
      message: message ? message.value || '' : '',
      source_page_url: window.location.href,
      source_page_title: document.title,
      query_type: 'Schedule a Demo',
      campaign_id: '701F9000001H1EdIAK',
      source: 'Web Submissions',
      utm: getCookie('utm'),
      gclid: getCookie('gclid')
    };
    currentFormData.current = formdata;
    try {
      const response = await fetch('https://forms.lambdatest.com/api/forms/contact', {
        method: 'POST',
        headers: {
          'Content-type': 'application/json'
        },
        body: JSON.stringify(formdata)
      });
      if (!response.ok) {
        const res = await response.json().catch(() => ({}));
        setError(res.message || 'Something went wrong. Please try again.');
        return;
      }
      form.current.reset();
      window.dataLayer?.push({
        event: 'book_a_demo_form_submitted'
      });
      if (formdata.email && formdata.email.toLowerCase().endsWith('gmail.com')) {
        window.sendAmplitudeEvents?.('Demo_Schedule_Gmail');
        window.location.href = 'https://www.testmuai.com/thank-you/';
        return;
      }
      window.sendAnalytics?.('book_a_demo_form_submitted', {
        event: 'book_a_demo_form_submitted',
        eventAction: 'success',
        eventLabel: window.location.href
      });
      window.sendAmplitudeEvents?.('Demo_Schedule');
      const hero = new window.RevenueHero({
        routerId: '630',
        showLoader: true
      });
      window.addEventListener('message', listenToRevenueHeroWidget);
      hero.submit({
        ...formdata,
        ...country
      }).then(data => {
        hero.dialog.open(data);
        setShowModal(false);
        document.body.style.overflow = 'unset';
      }).catch(err => {
        console.log(err);
        window.removeEventListener('message', listenToRevenueHeroWidget);
      });
    } catch (err) {
      setError('Something went wrong. Please try again.');
    } finally {
      setLoading(false);
    }
  };
  return <>
      <div className="kane_cli_cta not-prose">
        <div className="kane_cli_cta_box">
          <div className="kane_cli_cta_content">
            <h2>Terminal First Testing With Kane CLI</h2>
            <p>Natural language browser & mobile app tests right from terminal.</p>
            <button type="button" className="demoBtn" onClick={toggleModal}>Book a Demo</button>
          </div>
          <img loading="lazy" src="https://assets.testmu.ai/resources/images/Terminal-KaneCLIIcon.svg" alt="" aria-hidden="true" className="kane_cli_cta_icon no-zoom" />
          <img loading="lazy" src="https://assets.testmu.ai/resources/images/Terminal-KaneCLI.png" alt="Kane CLI terminal" title="Kane CLI" className="kane_cli_cta_terminal no-zoom" />
        </div>
      </div>
      <div className={'demo_popup_overlay demo_popup_header_demo not-prose ' + (showModal ? 'shown' : '')}>
        <div className="content text-left">
          <div className="demo_popup_box">
            <div className="demo_popup_form">
              <span onClick={toggleModal} className="demo_popup_close">×</span>
              <div className="demo_popup_form_title">Schedule Your Personal Demo</div>
              <form ref={form} onSubmit={handleSubmit}>
                <div className="demo_popup_grid">
                  <div>
                    <input type="text" name="first_name" placeholder="First Name" required />
                  </div>
                  <div>
                    <input type="text" name="last_name" placeholder="Last Name" required />
                  </div>
                </div>
                <div className="demo_popup_field">
                  <input type="email" name="email" placeholder="Work Email Address" pattern="^[^\s@]+@[^\s@]+\.[^\s@]+$" required />
                </div>
                <div className="demo_popup_field">
                  <div className="demo_popup_phone_row">
                    <div className="demo_popup_select_wrapper" ref={countrySelectRef}>
                      <div className={'demo_popup_select ' + (isCountrySelectOpen ? 'demo_popup_select_open' : '')} onClick={() => setIsCountrySelectOpen(!isCountrySelectOpen)}>
                        <span className="demo_popup_select_text">{selectedCountry ? selectedCountry.name : 'Select Country'}</span>
                        <span className="demo_popup_select_arrow">
                          <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M4 6L8 10L12 6" stroke="#4D4D4D" strokeWidth="1.25" strokeLinecap="square" />
              </svg>
                        </span>
                      </div>
                      {isCountrySelectOpen && <ul className="demo_popup_select_options">
                          {countryList.map((item, index) => <li key={index} className={'demo_popup_select_option ' + (selectedCountry && selectedCountry.country_code === item.country_code ? 'demo_popup_select_option_selected' : '')} onClick={() => {
    setSelectedCountry({
      name: item.name,
      value: item.value,
      country_code: item.country_code
    });
    setIsCountrySelectOpen(false);
  }}>
                              {item.name}
                            </li>)}
                        </ul>}
                    </div>
                    <input type="tel" name="mobile_no" placeholder="Phone Number" pattern="\d*" maxLength={15} required />
                  </div>
                </div>
                <div className="demo_popup_field">
                  <label>Select Product
                    <div className="demo_popup_select_wrapper" ref={productSelectRef}>
                      <div className={'demo_popup_select demo_popup_product_select ' + (isProductSelectOpen ? 'demo_popup_select_open' : '')} onClick={() => setIsProductSelectOpen(!isProductSelectOpen)}>
                        <span className="demo_popup_select_text">{selectedProduct ? selectedProduct.label : 'Select Product'}</span>
                        <span className="demo_popup_select_arrow">
                          <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M4 6L8 10L12 6" stroke="#4D4D4D" strokeWidth="1.25" strokeLinecap="square" />
              </svg>
                        </span>
                      </div>
                      {isProductSelectOpen && <ul className="demo_popup_select_options">
                          {productList.map((el, index) => <li key={index} className={'demo_popup_select_option ' + (selectedProduct && selectedProduct.type === el.type ? 'demo_popup_select_option_selected' : '')} onClick={() => {
    setSelectedProduct({
      label: el.label,
      type: el.type
    });
    setIsProductSelectOpen(false);
  }}>
                              {el.label}
                            </li>)}
                        </ul>}
                    </div>
                  </label>
                </div>
                <div className="demo_popup_field">
                  <textarea name="message" className="demo_popup_message" placeholder="Your message"></textarea>
                </div>
                {error && <div className="demo_popup_error">{error}</div>}
                <div className="demo_popup_action">
                  <button type="submit" className="demo_popup_submit" disabled={isLoading}>Schedule Demo</button>
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>
    </>;
};

Kane CLI runs your native app tests on **real Android and iOS devices in the <BrandName /> cloud**. Use a real device when you want to see how your app behaves on actual hardware. You write and run these tests the same way as an emulator or simulator test, from **any machine**: macOS (Intel or Apple Silicon), Linux, or Windows. You do not need Xcode, Android Studio, or `kane-cli doctor --install`.

Kane CLI asks the <BrandName /> device hub for a device, installs your app on it, and drives it. The **device hub** is the cloud service that hands out real devices to test sessions. A **session** is the period during which Kane CLI holds one device for you.

<Note>
  Requires Kane CLI 0.9.0 or later. Check your version with `kane-cli --version`.
</Note>

| Target | Drives | Builds it takes |
| - | - | - |
| `android` | A real Android device in the <BrandName /> cloud | `.apk`, `.aab`, or an `APP…` id |
| `ios` | A real iOS device in the <BrandName /> cloud | `.ipa`, or an `APP…` id |

`emulator` and `simulator` mean virtual devices on your Mac. See [Mobile Testing](/docs/docs/kane-cli-mobile/). `android` and `ios` always mean real devices.

## Plan Requirements

To run on real devices, your <BrandName /> account needs one of these plans. See [pricing](https://www.testmuai.com/pricing/).

| Plan | What you get |
| - | - |
| **KaneAI Max** | **One real device.** One real-device session runs at a time. |
| **KaneAI credits + Real Device Plus Automation Cloud** | Real devices from the automation plan, as many at once as its parallel tests allow. Credits are account-wide, so credits from any KaneAI plan qualify. |

When you start more real-device sessions than your plan allows, the extra session **queues for up to 15 minutes**. For example, `--parallel 2` on KaneAI Max starts one session too many. If a device frees up in that time, the session runs. Otherwise the run fails with `failed to open the mobile hub session: <reason>`.

If your account has neither plan, the run fails with the same message, and the hub's reason follows the colon.

## Quick Start

Sign in once, then pass a target and your app build:

```bash theme={null}
kane-cli login                                                    # once
kane-cli run "Sign in and open the account tab" --target android --app ./builds/app-release.apk
kane-cli run "Add the first item to the cart" --target ios --app ./builds/MyApp.ipa
```

Kane CLI prints the device it got before the first step:

```
Connecting to a real Android device…
Running on: Real device · Galaxy S24 · Android 14 · App APP1234567
```

## Choosing a Device

You do not have to choose. With no device named, the hub picks any free device for the platform.

To pick a specific device, list the devices the cloud offers. Listing needs **no login**:

```bash theme={null}
kane-cli devices list --target android --remote
kane-cli devices list --target ios --remote --os-version 17
```

Then name the device on the run, or save it as a default:

```bash theme={null}
kane-cli run "<objective>" --target android --device-name "Galaxy S24" --app ./app.apk
kane-cli run "<objective>" --target ios --os-version 17 --app ./MyApp.ipa      # any iPhone on iOS 17

kane-cli config set-target android
kane-cli config set-device-name "Pixel.*"     # a name or a pattern
kane-cli config set-os-version 15
```

* **Either one alone is enough.** `--device-name` alone means that device on any version. `--os-version` alone means any device on that version. Emulators and simulators need both.
* **A name can be a pattern.** A pattern is a name that matches several devices. If your name matches an entry in the list exactly, you get that device. Anything else is read as a case-insensitive regular expression, so `Pixel.*` means any Pixel.
* **Versions match by major.** `17.5` matches any iOS 17 device.
* **Order of precedence:** the flag first, then the saved config (or the test file's `device_name:` / `os_version:`), then the hub picks.

Kane CLI checks your choice against the device list before it asks for a session, so you find a typo before you wait for a device. If the list cannot be read, Kane CLI warns and runs anyway. The hub has the final say.

## The App Under Test

Every real-device run needs an app. You can provide it in two ways:

1. **A local build.** Android takes `.apk` or `.aab`. iOS takes `.ipa`. Kane CLI uploads the build to your account the first time and prints its `APP…` id. A per-machine cache skips a build the account already has.
2. **An uploaded app id**: `APP` followed by digits. List your real-device uploads with:

   ```bash theme={null}
   kane-cli apps list --target android
   kane-cli apps list --target ios
   ```

A simulator `.zip` does not run on a real iPhone. Build an `.ipa` instead. Every session starts with a **fresh install** of the app.

## In the TUI

In the interactive terminal UI (TUI), you can pick a device and app from menus and watch the test on the device screen.

* Run `/mobile` and choose **Android** or **iOS** (real device). `/mobile` works on any machine.
* Pick a device. "Any available Android device" is the top row. Then pick the app: a local build, one of your uploads, or an app id.
* A **live view** opens while the test runs. The live view is a picture of the device screen in its own Chrome window. It is view-only and shows a "View only — the test is driving this device" badge. Its address carries your account key, so Kane CLI never prints it.
* One device session lasts for the whole TUI session. `/new` and exit close it. If the hub ends an idle session, your next objective opens another one.
* To change the device or app mid-session, run `/new` first.

To turn the live view off for one run, pass `--no-stream`. To turn it off for every run:

```bash theme={null}
kane-cli config set-device-stream off
```

The live view needs Google Chrome (or `KANE_CLI_CHROME_PATH`). Without it, Kane CLI prints `note: no live view of the device — <reason>` and the test runs anyway. Agent and non-interactive runs never open a live view.

## Permissions and Alerts

By default, Kane CLI accepts app permission prompts and system alerts so they do not block your test. You can turn this off per platform:

| Setting | Applies to | Default |
| - | - | - |
| `kane-cli config set-auto-grant-permissions on\|off` | Android (`android`, and `emulator`) | on |
| `kane-cli config set-auto-accept-alerts on\|off` | iOS (`ios`, and `simulator`) | on |

Real devices and virtual devices keep separate values. Each command changes the value for your **saved** target. To change the real-device value, run `kane-cli config set-target android` (or `ios`) first.

Kane CLI also saves the setting on the mobile test it creates in Test Manager, so a cloud re-run behaves the same way.

## Saved Tests (`_test.md`)

Save a real-device test as a file so you can run it again or add it to a suite. Set the target and app in the frontmatter:

```yaml theme={null}
---
target: android            # android | ios
app: ./builds/app-release.apk
device_name: Pixel.*       # optional: a name or a pattern
os_version: "15"           # optional
---
```

* `app:` is required, or pass `--app`.
* `no_reset:` is refused, because a real-device session always starts with a fresh install.
* `kane-cli testmd run <path>` runs the file on the target its `target:` names. `--device-name`, `--os-version`, `--app`, and `--no-stream` work as on `run`.

For the full file format, see [Test.md](/docs/docs/kane-cli-testmd/).

## Suites (`testrun`)

You can run a suite of real-device tests from your machine or on the cloud grid. A **member** is one test file in the suite.

**Locally**, real-device members run from any machine. Each member opens its own device session, so `--parallel` works up to what your plan allows. Extra sessions queue, as described in [Plan Requirements](#plan-requirements).

```bash theme={null}
kane-cli testrun run --match '^tests/devices/' --parallel 2 < /dev/null
```

**On the cloud grid** with `--remote`, a real-device suite runs as a HyperExecute job on **Linux** runners. Kane CLI resolves each member's device on your machine (the flag, then the file, then the hub picks) and prints it before dispatch:

```
Devices:
  tests/devices/login_test.md  Real device · any Android device · Android 15  (test file)
```

* Android and iOS real-device members can share one job.
* A job cannot mix real-device members with web, emulator, or simulator members.
* `testrun` takes the app from each file's `app:`. There is no `--app` on `testrun`.
* `--device-slug` is refused for real devices.

See [Remote Runs on the Cloud Grid](/docs/docs/kane-cli-remote-execution/) and [Batch Runs](/docs/docs/kane-cli-testrun/).

## Limitations

* **No adaptive heal.** A replay step that fails is not repaired, and the test fails. Kane CLI prints `note: adaptive heal is off on real devices…` on a replay.
* **No `no_reset`.** Every session installs the app fresh.
* **Idle sessions end.** The hub ends a session left idle too long. In the TUI, your next objective opens another one.
* **The live view needs Chrome** and never opens for agent runs.

## Troubleshooting

Find the message Kane CLI printed in the left column, then apply the fix.

| You see | Fix |
| - | - |
| `failed to open the mobile hub session: <reason>` | Check that your account has KaneAI Max or KaneAI credits + Real Device Plus Automation Cloud, and that you are not over your plan's devices. The reason comes from the hub. |
| `a real-device run needs your TestMu AI username and access key, and none were found.` | Run `kane-cli login`, or pass `--username` and `--access-key`. |
| `"X" is not available as a real Android device.` | Pick a name from `kane-cli devices list --target android --remote`. |
| `no real Android device runs Android 9.` | Pick a version the list shows, or drop `--os-version`. |
| `no real Android devices are listed right now — try again later.` | The hub has no free devices for that platform. Try again later. |
| `… cannot run on a real iOS device — use an .ipa file, or an iOS app ID …` | Use the build type the platform takes. See the table at the top of this page. |
| `no app for this real-device test — add app: <path-to-build \| AppId> to the file, or pass --app.` | Add `app:` to the file, or pass `--app`. |
| `… has to be uploaded before it can run on a real device, but your account's org id was not found.` | Run `kane-cli login` again, or use an `APP…` id from `kane-cli apps list`. |
| `error: real devices are not listed from this machine — add --remote` | Add `--remote` to `devices list`. |

## Next Steps

* [Mobile Testing](/docs/docs/kane-cli-mobile/) to compare all mobile targets
* [Remote Runs on the Cloud Grid](/docs/docs/kane-cli-remote-execution/) to run suites on HyperExecute
* [Evidence](/docs/docs/kane-cli-evidence/) for what a real-device run records

<KaneCliCta />


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.