Guida di riferimento agli script Robo

Questo documento fornisce informazioni di riferimento sugli script Robo tra cui struttura, funzionalità, utilizzo, registrazione e azioni. Gli script Robo sono test che automatizzano le attività manuali di controllo qualità (QA) per le app mobili e abilitano l'integrazione continua (CI) e strategie di test pre-lancio. Uno script Robo è un file JSON che descrive una sequenza di interfaccia utente (UI) e altre azioni.

Puoi creare uno script Robo nei seguenti modi:

  • Utilizza la funzione di registrazione dello script Robo. (Solo Android)

  • Crea manualmente lo script Robo. (Android e iOS+)

  • Registra lo script Robo e poi modificalo manualmente. (Solo Android)

Per ulteriori informazioni sull'utilizzo degli script Robo, consulta Eseguire uno script Robo .

introduzione

Lo script Robo viene fornito al test Robo insieme ad altri input come il pacchetto di applicazioni Android (APK) dell'app sotto test.

Di seguito è riportato un esempio di uno script Robo che fa accedere un utente a un'app, che viene attivato quando viene avviata l'app in fase di test:

[
  {
    "crawlStage": "crawl",
    "contextDescriptor": {
      "condition": "app_under_test_shown"
    },
    "actions": [
      {
        "eventType": "VIEW_TEXT_CHANGED",
        "replacementText": "user123",
        "elementDescriptors": [
          {
            "resourceId": "my.app.package:id/username"
          }
        ]
      },
      {
        "eventType": "VIEW_TEXT_CHANGED",
        "replacementText": "12345",
        "elementDescriptors": [
          {
            "resourceId": "my.app.package:id/password"
          }
        ]
      },
      {
        "eventType": "VIEW_CLICKED",
        "elementDescriptors": [
          {
            "resourceId": "my.app.package:id/login"
          }
        ]
      }
    ]
  }
]

Se è presente un singolo script Robo in un file e ha la condizione di attivazione predefinita app_under_test_shown , come nell'esempio sopra, puoi specificare lo script Robo in un file utilizzando un formato più semplice, proprio come una sequenza delle sue azioni:

[
  {
    "eventType": "VIEW_TEXT_CHANGED",
    "replacementText": "user123",
    "elementDescriptors": [
      {
        "resourceId": "my.app.package:id/username"
      }
    ]
  },
  {
    "eventType": "VIEW_TEXT_CHANGED",
    "replacementText": "12345",
    "elementDescriptors": [
      {
        "resourceId": "my.app.package:id/password"
      }
    ]
  },
  {
    "eventType": "VIEW_CLICKED",
    "elementDescriptors": [
      {
        "resourceId": "my.app.package:id/login"
      }
    ]
  }
]

Supporto iOS+ per gli script Robo

Robo per iOS+ (Beta) ha un supporto limitato per gli script Robo. La sintassi dello script Robo per iOS+ è identica alla sintassi Android e le funzionalità iOS+ supportate si comportano in modo simile alle loro controparti Android.

Le seguenti azioni sono supportate in iOS+:

  • Asserzione
  • Clic
  • Clic lungo
  • Scorri
  • Ignora tutti gli elementi
  • Aspettare
  • Fai uno screenshot
  • Termina la scansione

I seguenti attributi identificativi nei descrittori di elemento sono supportati in iOS+:

  • Nome della classe
  • Nome della classe antenato
  • Descrizione del contenuto (e regex)
  • Testo (e espressioni regolari)

Le seguenti condizioni di attivazione nei descrittori di contesto sono supportate in iOS+:

  • App in prova mostrata
  • Elemento presente
  • Azione script non Robo eseguita

Struttura

Uno script Robo ha diversi attributi che descrivono come Robo lo esegue. La maggior parte di questi attributi sono facoltativi con valori predefiniti predefiniti:

Attributo Descrizione
id Un numero intero che aiuta a tenere traccia di questo script Robo negli output della scansione. Robo ha script Robo integrati con i propri id . Sebbene lo stesso id nei diversi script Robo non influenzi il loro comportamento, distinguere le azioni da questi script Robo negli output della scansione può essere difficile. Ti consigliamo di assegnare un id univoco pari o superiore a 1000 per i tuoi script Robo per evitare eventuali conflitti.
description Simile a id ma più descrittivo.
crawlStage La fase di scansione in cui Robo applica questo script Robo. Per impostazione predefinita, è la fase di scansione principale.
priority La priorità di questo script Robo rispetto ad altri script Robo. Per impostazione predefinita, tutti gli script Robo hanno una priorità pari a 1 .
maxNumberOfRuns Specifica quante volte durante una scansione Robo può eseguire questo script Robo. Per impostazione predefinita, Robo può eseguire uno script Robo una volta.
contextDescriptor Descrive il contesto o la condizione che attiva questo script Robo. Se omessa, la condizione di attivazione di questo script Robo viene considerata sempre soddisfatta; in altre parole, lo script Robo è incondizionato.
actions Tutte le azioni di questo script Robo.

Un singolo file contiene una raccolta di uno o più script Robo.

Quello che segue è un esempio di un file con due script Robo incondizionati, ciascuno con una singola azione che viene eseguita una volta all'inizio di una scansione:

[
  {
    "id": 1000,
    "description": "My first Robo script",
    "actions": [
      {
        "eventType": "DISABLE_KEYBOARD"
      }
    ]
  },
  {
    "id": 1001,
    "description": "My second Robo script",
    "actions": [
      {
        "eventType": "PRESSED_BACK"
      }
    ]
  }
]

Descrittore del contesto

Un descrittore di contesto definisce il contesto o la condizione che attiva uno script Robo utilizzando uno o una combinazione di diversi attributi:

Attributo Descrizione
"condition": "always" Attiva sempre uno script Robo.
"condition": "element_present" Verifica che sullo schermo sia presente un widget dell'interfaccia utente che corrisponde elementDescriptors o al testo specificato da visionText .
"condition": "element_disabled" Verifica che un widget dell'interfaccia utente che corrisponde elementDescriptors sia presente sullo schermo e non sia possibile interagire con esso.
"condition": "element_checked" Verifica che un widget dell'interfaccia utente che corrisponde elementDescriptors sia presente sullo schermo e sia selezionato.
"condition": "app_under_test_shown" Verifica che l'app in fase di test sia in esecuzione in primo piano.
"condition": "default_launcher_shown" Verifica che venga visualizzata la schermata iniziale di un dispositivo, il che significa che nessuna app è in esecuzione in primo piano.
"condition": "non_roboscript_action_performed" Verifica che le ultime azioni consecutive diverse nonRoboscriptActionCount eseguite dal test Robo non siano azioni di script Robo.
negateCondition Se impostato su true , nega la condition . Ad esempio, puoi utilizzare questo attributo per verificare se un widget dell'interfaccia utente NON è presente sullo schermo o che l'app in fase di test NON è in esecuzione in primo piano.
elementDescriptors Uno o più descrittori di elementi che identificano un widget dell'interfaccia utente sullo schermo. Viene utilizzato in combinazione con le condizioni element_present , element_disabled e element_checked . Si escludono a vicenda con visionText . Per ulteriori informazioni, vedere Descrittori di elementi .
visionText Il testo sullo schermo viene rilevato utilizzando l'API di riconoscimento ottico dei caratteri (OCR). visionText viene utilizzato in combinazione con la condizione element_present . Si escludono a vicenda con elementDescriptors .
nonRoboscriptActionCount Il numero di azioni consecutive di script non Robo eseguite in precedenza. Viene utilizzato in combinazione con la condizione non_roboscript_action_performed per attivare uno script Robo dopo ogni azione nonRoboscriptActionCount Robo. Per impostazione predefinita, è 1 .

Di seguito è riportato un esempio di uno script Robo attivato da un widget dell'interfaccia utente con un ID risorsa "my.app.package:id/page_header" presente sullo schermo:

{
  "id": 1000,
  "contextDescriptor": {
    "condition": "element_present",
    "elementDescriptors": [
      {
        "resourceId": "my.app.package:id/page_header"
      }
    ]
  },
  "actions": [
    {
      "eventType": "VIEW_CLICKED",
      "elementDescriptors": [
        {
          "text": "Settings"
        }
      ]
    }
  ]
}

Di seguito è riportato un esempio di uno script Robo attivato dalla "Privacy Policy" rilevata dal riconoscimento ottico dei caratteri (OCR):

{
  "id": 1000,
  "description": "Vision text Robo script",
  "contextDescriptor": {
    "condition": "element_present",
    "visionText": "Privacy Policy"
  },
  "actions": [
    {
      "eventType": "VIEW_CLICKED",
      "visionText": "Privacy Policy"
    }
  ]
}

Quello che segue è un esempio di uno script Robo che attende 5 secondi dopo ogni azione Robo non basata su script:

{
  "contextDescriptor": {
    "condition": "non_roboscript_action_performed"
  },
  "maxNumberOfRuns" : 1000,
  "actions" : [
    {
      "eventType" : "DELAYED_MESSAGE_POSTED",
      "delayTime" : 5000
    }]
}

Azioni

Ogni azione in uno script Robo è rappresentata come un insieme di una o più coppie attributo-valore, descritte nella tabella seguente:

Attributo Descrizione
eventType Specifica il tipo di azione, ad esempio clic, modifica del testo e così via. Obbligatorio per ogni azione.
elementDescriptors Descrittori che identificano un widget dell'interfaccia utente. Necessario per tutte le azioni che hanno un widget dell'interfaccia utente di destinazione, come fare clic su un pulsante particolare.
optional Se impostato su true , questa azione viene saltata quando non può essere eseguita. Ad esempio, questa azione viene saltata quando non riesce a trovare il widget dell'interfaccia utente di destinazione su uno schermo, senza fallire nello script Robo che lo contiene. Per impostazione predefinita, il valore è false .
replacementText Il testo da inserire nel widget dell'interfaccia utente di destinazione. Necessario per le azioni di modifica del testo.
swipeDirection Specifica la direzione dello scorrimento. Necessario per le azioni di scorrimento.
delayTime Specifica il tempo di attesa, in millisecondi. Necessario per le azioni di attesa.
pointTapXCoordinate e pointTapYCoordinate Le coordinate X e Y dei pixel del punto toccato. Si escludono a vicenda con pointTapXPercent e pointTapYPercent . Necessario per le azioni di tocco del punto.
pointTapXPercent e pointTapYPercent Le coordinate percentuali X e Y del punto toccato. Si escludono a vicenda con pointTapXCoordinate e pointTapYCoordinate . Necessario per le azioni di tocco del punto.

Quello che segue è un esempio di uno script Robo con due azioni senza widget dell'interfaccia utente di destinazione, il che significa che queste azioni non funzionano su un widget dell'interfaccia utente specifico:

[
  {
    "eventType": "DELAYED_MESSAGE_POSTED",
    "delayTime": 3000
  },
  {
    "eventType": "PRESSED_BACK"
  }
]

Descrittori di elementi

Un descrittore di elemento identifica un widget dell'interfaccia utente utilizzando uno o più dei seguenti attributi identificativi:

Attributo Descrizione
className
ancestorClassName Nome della classe dell'antenato della gerarchia dell'interfaccia utente dell'elemento. Un antenato è uno qualsiasi dei nodi principali nella gerarchia dell'interfaccia utente dell'elemento, incluso l'elemento stesso.
resourceId
resourceIdRegex Espressione regolare Java per far corrispondere resourceId .
contentDescription
contentDescriptionRegex Espressione regolare Java per corrispondere contentDescription .
text (che appare sullo schermo)
textRegex Espressione regolare Java per la corrispondenza text .
groupViewChildPosition , recyclerViewChildPosition o adapterViewChildPosition Rappresenta la posizione secondaria di un widget dell'interfaccia utente in base al tipo di widget principale.

Spesso questi attributi non sono definiti, ad esempio un pulsante potrebbe non avere testo e descrizione del contenuto. Anche se sono presenti alcuni valori di attributo, potrebbero non essere univoci nella schermata di una determinata app (incluso resourceId ).

Ad esempio, la differenziazione tra gli elementi di un elenco è comunemente possibile solo utilizzando le diverse posizioni secondarie all'interno del widget principale. Ciò significa che l'utilizzo di un solo descrittore di elemento per identificare un widget dell'interfaccia utente è solitamente insufficiente. Pertanto, l'attributo elementDescriptors di un'azione contiene una sequenza di descrittori di elementi ordinati in modo tale che il primo corrisponda al widget dell'interfaccia utente di destinazione, il secondo corrisponda al widget principale del widget dell'interfaccia utente di destinazione e così via. Il widget dell'interfaccia utente di destinazione di un'azione viene abbinato quando tutti i suoi descrittori di elemento corrispondono alla sottogerarchia del widget dell'interfaccia utente corrispondente.

Quello che segue è un esempio di uno script Robo con una modifica del testo e azioni di clic, che richiedono entrambi di identificare il widget dell'interfaccia utente di destinazione utilizzando i descrittori di elemento forniti:

[
  {
    "eventType": "VIEW_TEXT_CHANGED",
    "replacementText": "John",
    "elementDescriptors": [
      {
        "className": "android.support.v7.widget.AppCompatEditText",
        "groupViewChildPosition": 0,
        "resourceId": "com.google.samples.apps.topeka:id/first_name"
      },
      {
        "className": "android.widget.FrameLayout",
        "groupViewChildPosition": 0
      },
      {
        "className": "android.support.design.widget.TextInputLayout",
        "groupViewChildPosition": 1
      }
    ]
  },
  {
    "eventType": "VIEW_CLICKED",
    "elementDescriptors": [
      {
        "className": "android.support.design.widget.FloatingActionButton",
        "groupViewChildPosition": 1,
        "resourceId": "com.google.samples.apps.topeka:id/done"
      },
      {
        "className": "android.widget.FrameLayout",
        "groupViewChildPosition": 1,
        "resourceId": "com.google.samples.apps.topeka:id/content"
      },
      {
        "className": "android.widget.FrameLayout",
        "groupViewChildPosition": 0,
        "resourceId": "com.google.samples.apps.topeka:id/sign_in_content"
      }
    ]
  }
]

Opzioni di esecuzione

Facoltativamente puoi prefissare l'elenco di azioni in uno script Robo con un oggetto JSON che specifica le opzioni di esecuzione per quello script Robo. Questa intestazione di configurazione inizia con la parola chiave roboscript seguita da una rappresentazione JSON delle opzioni di esecuzione desiderate.

Gli script Robo supportano le seguenti opzioni di esecuzione:

  • executionMode : opzioni di esecuzione applicate quando è in esecuzione uno script Robo:
    • strict : se impostato su true , lo script Robo non utilizza la corrispondenza parziale, il salto dell'azione corrente e la sospensione . Cioè, lo script Robo viene eseguito come un normale test della strumentazione e fallisce non appena una delle sue azioni non può essere eseguita. Per impostazione predefinita, è false .
    • notify - se impostato su false , lo script Robo non mostra notifiche sullo schermo all'inizio e alla fine della sua esecuzione. Per impostazione predefinita, è true .
  • postscript : opzioni di esecuzione applicate dopo il completamento di uno script Robo:
    • terminate : se impostato su true , il test Robo interrompe la scansione una volta completato lo script Robo. Per impostazione predefinita, è false .

Quello che segue è un esempio di uno script Robo eseguito in modalità strict senza notifiche sullo schermo che dorme per tre secondi, dopodiché la scansione si interrompe:

"roboscript": {
  "executionMode": {
    "strict": true,
    "notify": false
  },
  "postscript": {
    "terminate": true
  }
}
[
  {
    "eventType": "DELAYED_MESSAGE_POSTED",
    "delayTime": 3000
  }
]

Parametri del modello

Un parametro del modello è un segnaposto in uno script Robo che viene sostituito con il valore effettivo quando il test Robo carica lo script Robo per l'esecuzione. I parametri del modello sono preceduti da un doppio carattere di sottolineatura seguito da un segno di percentuale e sono preceduti da un segno di percentuale seguito da un doppio carattere di sottolineatura.

Gli script Robo supportano il seguente parametro di modello:

  • __%APP_PACKAGE_NAME%__ : il nome del pacchetto dell'app in fase di test.

Di seguito è riportato un esempio di uno script Robo che interrompe il processo dell'app in fase di test:

[
  {
    "eventType": "ADB_SHELL_COMMAND",
    "command": "am force-stop __%APP_PACKAGE_NAME%__"
  }
]

Commenti

Uno script Robo può contenere righe di commento, ovvero righe che iniziano con # o // .

Quello che segue è un esempio di uno script Robo con un paio di commenti:

# Confirm a user account.
[
  {
    // Click the DONE button.
    "eventType": "VIEW_CLICKED",
    "elementDescriptors": [
      {
        "resourceId": "com.google.samples.apps.topeka:id/done"
      }
    ]
  }
]

Capacità

Per impostazione predefinita, finché tutte le azioni di uno script Robo non vengono completate (o almeno tentate), lo script Robo rimane attivo. Il test Robo continua a cercare di abbinare un'azione dello script Robo ogni volta che sceglie un'azione da eseguire. Lo script Robo utilizza le seguenti tecniche per aumentare la robustezza:

Tecnica Descrizione
Corrispondenza parziale Se non è possibile far corrispondere completamente l'azione dello script Robo corrente, i criteri di corrispondenza vengono attenuati e la corrispondenza viene ritentata. La corrispondenza parziale non considera il descrittore dell'elemento più esterno mentre corrisponde al widget dell'interfaccia utente di destinazione di un'azione dello script Robo.

Se la corrispondenza parziale ha esito positivo, l'azione dello script Robo corrispondente viene eseguita normalmente. Questa tecnica supporta scenari in cui la struttura dell'app cambia, ad esempio tra le versioni dell'app, quando gli elementi dello schermo vengono riorganizzati.

Salta l'azione corrente Se l'azione dello script Robo corrente non può essere abbinata completamente o parzialmente, Robo tenta di abbinare l'azione dello script Robo successiva. Se l'azione successiva corrisponde completamente o parzialmente, Robo test salta (e non torna mai a) l'azione corrente dello script Robo ed esegue quella successiva.

Questa tecnica supporta scenari in cui il comportamento dell'app cambia tra le versioni o è instabile, ad esempio quando una finestra di dialogo intermittente potrebbe apparire su schermi diversi durante la registrazione rispetto alla riproduzione di uno script Robo.

Sospendere Se né le azioni attuali né quelle successive dello script Robo possono essere abbinate completamente o parzialmente, lo script Robo viene temporaneamente sospeso e il test Robo sceglie un'azione da eseguire utilizzando le sue altre strategie. Una volta completata questa azione, Robo test riprende l'esecuzione dello script Robo.

Finché non è possibile abbinare le azioni correnti o successive dello script Robo, lo script Robo rimane sospeso per un numero qualsiasi di azioni. Pertanto, gli script Robo non devono necessariamente essere un prologo per un test Robo ed è possibile alternare azioni di script Robo con azioni di test Robo standard. Questa tecnica supporta scenari in cui il comportamento dell'app è instabile o quando le modifiche tra le versioni dell'app sono abbastanza grandi da richiedere al test Robo di "colmare le lacune" con le sue azioni standard.

Priorità

Se uno script Robo raggiunge il suo maxNumberOfRuns , non può più essere attivato in una determinata scansione. Se più di uno script Robo può essere attivato dal contesto corrente, la priorità viene data scegliendo, nel seguente ordine, lo script Robo che:

  1. Ha un attributo contextDescriptor .
  2. Ha la priority più alta (per impostazione predefinita, tutti gli script Robo hanno la stessa priority di esecuzione pari a 1 ).
  3. Appare per primo nell'elenco degli script Robo, se le priorità degli script Robo sono le stesse.

Quello che segue è un esempio di un file con tre script Robo che eseguono la stessa azione e vengono attivati ​​dalla stessa condizione: l'app sotto test è in primo piano:

[
  {
    "id": 1000,
    "description": "Robo script 1",
    "contextDescriptor": {
      "condition": "app_under_test_shown"
    },
    "actions": [
      {
        "eventType": "DELAYED_MESSAGE_POSTED",
        "delayTime": 3000
      }
    ]
  },
  {
    "id": 1001,
    "description": "Robo script 2",
    "priority": "2",
    "contextDescriptor": {
      "condition": "app_under_test_shown"
    },
    "actions": [
      {
        "eventType": "DELAYED_MESSAGE_POSTED",
        "delayTime": 3000
      }
    ]
  },
  {
    "id": 1002,
    "description": "Robo script 3",
    "contextDescriptor": {
      "condition": "app_under_test_shown"
    },
    "actions": [
      {
        "eventType": "DELAYED_MESSAGE_POSTED",
        "delayTime": 3000
      }
    ]
  }
]

Quando l'app in fase di test è in primo piano, Robo attiva quanto segue, in ordine:

  1. "Robo script 2" perché ha la massima priorità.
  2. "Robo script 1" perché appare prima tra i restanti script Robo applicabili con la stessa priorità.
  3. "Robo script 3" come ultimo script Robo applicabile.

Corse ripetute

Per impostazione predefinita, Robo attiva uno script Robo al massimo una volta durante una scansione. Questo può essere regolato tramite l'attributo maxNumberOfRuns .

Quello che segue è un esempio di uno script Robo che porta l'app in fase di test in background fino a 10 volte:

{
  "id": 1000,
  "maxNumberOfRuns": 10,
  "contextDescriptor": {
    "condition": "app_under_test_shown"
  },
  "actions": [
    {
      "eventType": "GO_HOME"
    }
  ]
}

Fase di scansione

Gli script Robo sono applicabili in diverse fasi di una determinata scansione Robo:

Fase di scansione Descrizione
pre_crawl Prima che Robo venga avviato e inizi a eseguire la scansione dell'app in fase di test.
post_crawl Dopo che Robo ha terminato la scansione dell'app in fase di test.
crawl La fase di scansione principale, quando Robo esegue la scansione dell'app sotto test.
close_screen Quando Robo tenta di tornare indietro (backtrack) da una determinata schermata, quando vengono esplorate tutte le possibili azioni su questa schermata. Per impostazione predefinita, Robo reagisce, il che in alcuni scenari non è auspicabile.

Se l'attributo crawlStage di uno script Robo non è specificato, è implicito che sia crawl .

Quello che segue è un esempio di uno script Robo che cancella i dati utente dell'app in fase di test prima che Robo inizi a scansionarla:

{
  "id": 1000,
  "crawlStage": "pre_crawl",
  "actions": [
    {
      "eventType": "ADB_SHELL_COMMAND",
      "command": "pm clear __%APP_PACKAGE_NAME%__"
    }
  ]
}

Quello che segue è un esempio di uno script Robo che indica a Robo di fare clic su "Cancel" ogni volta che tenta di tornare indietro (backtrack) da una finestra di dialogo di conferma:

{
  "id": 1000,
  "crawlStage": "close_screen",
  "maxNumberOfRuns": 999,
  "contextDescriptor": {
    "condition": "element_present",
    "elementDescriptors": [
      {
        "resourceId": "my.app.package:id/confirmation_dialog"
      }
    ]
  },
  "actions": [
    {
      "eventType": "VIEW_CLICKED",
      "elementDescriptors": [
        {
          "text": "Cancel"
        }
      ]
    }
  ]
}

Azioni condizionali

Uno script Robo può contenere azioni condizionali. Le azioni condizionali hanno tre attributi aggiuntivi che descrivono come Robo le esegue:

Attributo Descrizione
priority La priorità di questa azione condizionale rispetto ad altre azioni condizionali all'interno dello script Robo che la contiene. Per impostazione predefinita, tutte le azioni condizionali hanno una priorità pari a 1 .
maxNumberOfRuns Quante volte questa azione condizionale può essere eseguita durante un'esecuzione dello script Robo che la contiene. Per impostazione predefinita, tutte le azioni condizionali possono essere eseguite al massimo una volta in un'unica esecuzione dello script Robo che le contiene.
contextDescriptor Il contesto/condizione che attiva questa azione condizionale. Ha la stessa struttura e offre funzionalità simili al [contextDescriptor dello script Robo](#context-descriptor).

Quando viene attivato, uno script Robo esegue le sue azioni non condizionali una per una in ordine di apparizione. Se uno script Robo contiene azioni condizionali, queste vengono prese in considerazione ogni volta prima di scegliere un'azione non condizionale da eseguire. Se un'azione condizionale viene attivata e scelta in base alla sua priorità e al numero rimanente di esecuzioni, lo script Robo esegue questa azione condizionale. Altrimenti, lo script Robo esegue la seguente azione non condizionale. Per essere valido, uno script Robo deve contenere almeno un'azione non condizionale.

Quello che segue è un esempio di uno script Robo incondizionato con un'azione condizionale che chiude le finestre di dialogo popup se vengono visualizzate in qualsiasi momento durante l'esecuzione dello script Robo:

{
  "id": 1000,
  "actions": [
    {
      "description": "Dismiss popup",
      "maxNumberOfRuns": 100,
      "contextDescriptor": {
        "condition": "default_launcher_shown",
        "negateCondition": true
      },
      "eventType": "GO_HOME"
    },
    {
      "description": "Screen off",
      "eventType": "ADB_SHELL_COMMAND",
      "command": "input keyevent 26"
    },
    {
      "description": "Wait for 10 seconds",
      "eventType": "DELAYED_MESSAGE_POSTED",
      "delayTime": 10000
    },
    {
      "description": "Screen on",
      "eventType": "ADB_SHELL_COMMAND",
      "command": "input keyevent 82"
    },
    {
      "description": "Wait for 10 seconds",
      "eventType": "DELAYED_MESSAGE_POSTED",
      "delayTime": 10000
    }
}

Ignorare le azioni

Uno script Robo può contenere istruzioni affinché Robo ignori widget specifici dell'interfaccia utente o tutti i widget dell'interfaccia utente su una schermata particolare. Queste istruzioni sono rappresentate ignorando le "azioni" con eventType ELEMENT_IGNORED e ALL_ELEMENTS_IGNORED corrispondentemente.

Ogni volta che l'attributo contextDescriptor di uno script Robo contenente azioni da ignorare corrisponde a una determinata schermata, Robo non interagisce con alcun widget dell'interfaccia utente preso di mira dalle sue azioni da ignorare (a meno che qualche altra azione dello script Robo non faccia eseguire a Robo un'azione su uno dei widget dell'interfaccia utente ignorati).

Uno script Robo può contenere una combinazione di azioni ignoranti, condizionali e non condizionali. A differenza di altre azioni di script Robo, le azioni di ignoranza vengono applicate purché contextDescriptor dello script Robo contenente corrisponda a una schermata durante una scansione di Robo, indipendentemente dai valori degli attributi priority e maxNumberOfRuns .

Quello che segue è un esempio di un file con due script Robo. Il primo script Robo fa sì che Robo ignori tutti i widget dell'interfaccia utente su una schermata contenente un widget dell'interfaccia utente con un ID risorsa "my.app.package:id/ignored_screen" . Il secondo script Robo fa sì che Robo ignori i widget dell'interfaccia utente i cui ID risorsa corrispondono alla regex Java ".*:id/done" su una schermata contenente un widget dell'interfaccia utente con un ID risorsa "my.app.package:id/main_screen" :

[
  {
    "id": 1000,
    "contextDescriptor": {
      "condition": "element_present",
      "elementDescriptors": [
        {
          "resourceId": "my.app.package:id/ignored_screen"
        }
      ]
    },
    "actions": [
      {
        "eventType": "ALL_ELEMENTS_IGNORED"
      }
    ]
  },
  {
    "id": 1001,
    "contextDescriptor": {
      "condition": "element_present",
      "elementDescriptors": [
        {
          "resourceId": "my.app.package:id/main_screen"
        }
      ]
    },
    "actions": [
      {
        "eventType": "ELEMENT_IGNORED",
        "elementDescriptors": [
          {
            "resourceIdRegex": ".*:id/done"
          }
        ]
      }
    ]
  }
]

Supporto per RecyclerView e AdapterView

I widget secondari di RecyclerView e AdapterView vengono caricati dinamicamente e potrebbero essere visualizzati a molti passaggi dalla schermata corrente. Poiché le dimensioni di uno schermo e il numero di passaggi necessari per raggiungere questo bambino variano a seconda dei diversi fattori di forma del dispositivo, è molto più affidabile fare affidamento sulla posizione dei dati del bambino, che è assoluta. È un approccio meno efficace fare affidamento sul numero di passaggi necessari per portare il bambino sullo schermo e quindi utilizzare la sua posizione sullo schermo.

Pertanto, lo script Robo acquisisce le posizioni assolute dei dati dei figli RecyclerView che sono obiettivi delle azioni dello script Robo come recyclerViewChildPosition . Lo script Robo acquisisce anche le posizioni assolute dei dati degli elementi secondari AdapterView che sono obiettivi delle azioni dello script Robo come adapterViewChildPosition .

Le azioni sui figli RecyclerView e AdapterView vengono eseguite nei seguenti passaggi:

  1. Robo test garantisce che il figlio corrispondente venga visualizzato sullo schermo attraverso un'azione di posizionamento sul suo contenitore RecyclerView o AdapterView.

  2. Robo test esegue l'azione registrata direttamente sull'elemento figlio, poiché è già visualizzato sullo schermo.

Di seguito è riportato un esempio di un'azione di clic su un elemento figlio AdapterView ( android.widget.GridView ):

{
  "eventType": "VIEW_CLICKED",
  "elementDescriptors": [
    {
      "className": "com.google.samples.apps.topeka.widget.AvatarView",
      "adapterViewChildPosition": 5,
      "resourceId": "com.google.samples.apps.topeka:id/avatar",
      "contentDescription": "Avatar 6"
    },
    {
      "className": "android.widget.GridView",
      "groupViewChildPosition": 1,
      "resourceId": "com.google.samples.apps.topeka:id/avatars"
    },
    {
      "className": "android.widget.LinearLayout",
      "groupViewChildPosition": 1
    },
    {
      "className": "android.widget.LinearLayout",
      "groupViewChildPosition": 0
    }
  ]
}

Di seguito è riportato un esempio di un'azione di clic su un figlio RecyclerView ( android.support.v7.widget.RecyclerView ):

{
  "eventType": "VIEW_CLICKED",
  "elementDescriptors": [
    {
      "className": "android.support.v7.widget.AppCompatTextView",
      "groupViewChildPosition": 1,
      "resourceId": "com.google.samples.apps.topeka:id/category_title"
    },
    {
      "className": "android.widget.FrameLayout",
      "recyclerViewChildPosition": 8,
      "resourceId": "com.google.samples.apps.topeka:id/category_item"
    },
    {
      "className": "android.support.v7.widget.RecyclerView",
      "groupViewChildPosition": 1,
      "resourceId": "com.google.samples.apps.topeka:id/categories"
    },
    {
      "className": "android.widget.FrameLayout",
      "groupViewChildPosition": 1,
      "resourceId": "com.google.samples.apps.topeka:id/category_container"
    },
    {
      "className": "android.widget.LinearLayout",
      "groupViewChildPosition": 0
    }
  ]
}

Registra uno script Robo in Android Studio ed eseguilo in Test Lab

Puoi creare uno script Robo in Android Studio, che salva lo script come file JSON. Puoi quindi caricare il file JSON su Firebase Test Lab con l'applicazione ed eseguire il test di conseguenza.

Quando esegui un test Robo con uno script allegato, Robo test esegue innanzitutto le azioni predefinite e quindi esplora l'app come al solito.

Per creare un file JSON di script Robo in Android Studio, segui i passaggi in Registrare uno script Robo utilizzando Test Lab in Android Studio .

Azioni dello script Robo

Il seguente attributo facoltativo comune si applica a tutte le azioni:

  • description : aiuta a tenere traccia dell'esecuzione di questa azione dello script Robo negli output del test Robo.

Asserzione

Se la condizione asserita è vera, lo script Robo continua con l'azione successiva, che potrebbe essere un'altra asserzione. In caso contrario, l'esecuzione dello script Robo verrà interrotta a causa di un'asserzione non riuscita.

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "ASSERTION" --
contextDescriptor Descrive il contesto o la condizione asserita. Ha la stessa struttura e offre funzionalità simili al contextDescriptor dello script Robo .

Di seguito è riportato un esempio di un'asserzione di script Robo che verifica che l'app in fase di test sia in primo piano:

{
  "eventType": "ASSERTION",
  "contextDescriptor": {
    "condition": "app_under_test_shown"
  }
}

Di seguito è riportato un esempio di un'asserzione di script Robo che controlla che un widget dell'interfaccia utente con l'ID risorsa "com.google.samples.apps.topeka:id/done" sia presente su uno schermo:

{
  "eventType": "ASSERTION",
  "contextDescriptor": {
    "condition": "element_present",
    "elementDescriptors": [
      {
        "resourceId": "com.google.samples.apps.topeka:id/done"
      }
    ]
  }
}

Quello che segue è un esempio di un'asserzione di script Robo che controlla che "Settings" NON venga rilevato su uno schermo utilizzando l'OCR:

{
  "eventType": "ASSERTION",
  "contextDescriptor": {
    "condition": "element_present",
    "negateCondition": true,
    "visionText": "Settings"
  }
}

Clic

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
eventType Specifica il tipo di azione dello script Robo.
"eventType": "VIEW_CLICKED" Fa clic sull'elemento di destinazione dell'app in fase di test.
"eventType": "SOFT_KEYBOARD_CLICK" Fa clic sull'elemento di destinazione della tastiera virtuale.
"eventType": "SOFT_KEYBOARD_RANDOM_CLICK" Fa clic su elementi casuali della tastiera virtuale fino a maxNumberOfRuns volte.
"eventType": "LIST_ITEM_CLICKED" Utilizzato dal registratore di script Robo in Android Studio per fare clic sugli elementi dell'elenco.
elementDescriptors Identifica il widget dell'interfaccia utente su cui è stato fatto clic utilizzando la gerarchia dell'interfaccia utente di Android. Si escludono a vicenda con visionText .
visionText Identifica l'elemento cliccato utilizzando l'OCR. Si escludono a vicenda con elementDescriptors .
maxNumberOfRuns Specifica quante volte fare clic su un elemento casuale della tastiera virtuale, quando eventType è SOFT_KEYBOARD_RANDOM_CLICK . Il valore predefinito è 1 .

Di seguito è riportato un esempio di un'azione di script Robo che fa clic su un pulsante con l'ID risorsa "com.google.samples.apps.topeka:id/done" :

{
  "eventType": "VIEW_CLICKED",
  "elementDescriptors": [
    {
      "resourceId": "com.google.samples.apps.topeka:id/done"
    }
  ]
}

Di seguito è riportato un esempio di un'azione di script Robo che fa clic su "Privacy Policy" rilevata su uno schermo utilizzando l'OCR:

{
  "eventType": "VIEW_CLICKED",
  "visionText": "Privacy Policy"
}

Di seguito è riportato un esempio di un'azione di script Robo che fa clic su un elemento della tastiera virtuale con una descrizione del contenuto "Emoji button" :

{
  "eventType": "SOFT_KEYBOARD_CLICK",
  "elementDescriptors": [
    {
      "contentDescription": "Emoji button"
    }
  ]
}

Di seguito è riportato un esempio di un'azione di script Robo che fa clic su elementi casuali della tastiera virtuale fino a cinque volte:

{
  "eventType": "SOFT_KEYBOARD_RANDOM_CLICK",
  "maxNumberOfRuns": 5
}

Disabilita la tastiera virtuale

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "DISABLE_KEYBOARD" --

Di seguito è riportato un esempio di un'azione di script Robo che disabilita la tastiera virtuale:

{
  "eventType": "DISABLE_KEYBOARD"
}

Esegui il comando della shell adb

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "ADB_SHELL_COMMAND" --
command Il comando della shell Android Debug Bridge (adb) da eseguire.

Il seguente attributo è facoltativo:

  • expectedOutputRegex : l'output previsto del comando come espressione regolare Java. Se l'output non corrisponde, l'azione dello script Robo fallisce. Per impostazione predefinita, è una stringa vuota, il che significa che l'output non viene controllato.

Di seguito è riportato un esempio di un'azione di script Robo che cancella i dati utente dell'app in fase di test:

{
  "eventType": "ADB_SHELL_COMMAND",
  "command": "pm clear __%APP_PACKAGE_NAME%__"
}

Concedere autorizzazioni

Questa azione viene registrata dal registratore di script Robo in Android Studio per compatibilità con le versioni precedenti di Espresso Test Recorder . Il test Robo concede tutte le autorizzazioni all'app in fase di test all'inizio di ogni scansione e, pertanto, questa azione non è operativa. NON utilizzare questa azione nei tuoi script Robo.

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "PERMISSIONS_REQUEST" --

Ignora tutti gli elementi su uno schermo

Questa azione fa sì che Robo ignori tutti gli elementi su qualsiasi schermata che attiva lo script Robo contenente.

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "ALL_ELEMENTS_IGNORED" --

Quello che segue è un esempio di un'azione di script Robo che fa sì che Robo ignori tutti gli elementi su uno schermo:

{
  "eventType": "ALL_ELEMENTS_IGNORED"
}

Ignorare un elemento

Questa azione fa sì che Robo ignori uno o più elementi che corrispondono agli elementDescriptors specificati.

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "ELEMENT_IGNORED" --
elementDescriptors Identifica i widget dell'interfaccia utente ignorati utilizzando la gerarchia dell'interfaccia utente di Android.

Il seguente attributo è facoltativo:

  • ignoreChildren : se impostato su true , Robo ignora anche tutti i discendenti dei widget dell'interfaccia utente ignorati. Per impostazione predefinita, è false .

Quello che segue è un esempio di un'azione di script Robo che fa sì che Robo ignori tutti gli elementi, le cui descrizioni dei contenuti iniziano con "Avatar" :

{
  "eventType": "ELEMENT_IGNORED",
  "elementDescriptors": [
    {
      "contentDescriptionRegex": "Avatar.*"
    }
  ]
}

Testo di input

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
eventType Specifica il tipo di azione dello script Robo.
"eventType": "VIEW_TEXT_CHANGED" Inserisce il testo specificato nel widget dell'interfaccia utente di destinazione.
"eventType": "ENTER_TEXT" inserisce il testo specificato nel widget dell'interfaccia utente di destinazione e quindi invia un evento KEYCODE_ENTER a questo widget dell'interfaccia utente.
elementDescriptors Identifica il widget dell'interfaccia utente di destinazione utilizzando la gerarchia dell'interfaccia utente Android.
replacementText Il testo da inserire nel widget dell'interfaccia utente di destinazione.

Di seguito è riportato un esempio di un'azione di script Robo che inserisce "John" in un widget dell'interfaccia utente con l'ID risorsa "com.google.samples.apps.topeka:id/first_name" :

{
  "eventType": "VIEW_TEXT_CHANGED",
  "replacementText": "John",
  "elementDescriptors": [
    {
      "resourceId": "com.google.samples.apps.topeka:id/first_name"
    }
  ]
}

Clic lungo

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "VIEW_LONG_CLICKED" --
elementDescriptors Identifica il widget dell'interfaccia utente di destinazione utilizzando la gerarchia dell'interfaccia utente Android. Si escludono a vicenda con visionText .
visionText Identifica l'elemento su cui si è fatto clic a lungo utilizzando l'OCR. Si escludono a vicenda con elementDescriptors .

Il seguente attributo è facoltativo:

  • delayTime : specifica quanto dura la pressione di un clic lungo, in millisecondi.

Di seguito è riportato un esempio di un'azione di script Robo che esegue un clic di cinque secondi su un widget dell'interfaccia utente con la descrizione del contenuto "Avatar 8" :

{
  "eventType": "VIEW_LONG_CLICKED",
  "elementDescriptors": [
    {
      "contentDescription": "Avatar 8"
    }
  ],
  "delayTime": 5000
}

Esegui un gesto a un punto

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "ONE_POINT_GESTURE" --
coordinates Due coordinate per un gesto a un punto, formattate come "(x1,y1)->(x2,y2)" come percentuali o pixel.

Il seguente attributo è facoltativo:

  • dragAndDrop : se impostato su true , il gesto a un punto esegue un'azione di trascinamento. Per impostazione predefinita, è false .

Di seguito è riportato un esempio di un'azione di gesto a un punto dello script Robo che esegue uno scorrimento verso il basso:

{
  "eventType": "ONE_POINT_GESTURE",
  "coordinates": "(50%,25%)->(50%,75%)"
}

Esegui un gesto in due punti

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "TWO_POINT_GESTURE" --
coordinates Quattro coordinate per un gesto a due punti, formattato come "(x1,y1)->(x2,y2),(x3,y3)->(x4,y4)" come percentuali o pixel.

Di seguito è riportato un esempio di un'azione di script Robo che esegue un gesto di allontanamento:

{
  "eventType": "TWO_POINT_GESTURE",
  "coordinates": "(50%,50%)->(25%,50%),(50%,50%)->(75%,50%)"
}

Eseguire un'azione IME

Questa azione preme il pulsante dell'azione corrente, ad esempio Avanti, Fatto e Cerca, nell'IME (Input Method Editor) per il widget dell'interfaccia utente di destinazione specificato.

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "PRESSED_EDITOR_ACTION" --
elementDescriptors Identifica il widget dell'interfaccia utente di destinazione utilizzando la gerarchia dell'interfaccia utente Android.

Di seguito è riportato un esempio di un'azione di script Robo che esegue un'azione IME su un widget dell'interfaccia utente con l'ID risorsa "com.google.samples.apps.topeka:id/first_name" :

{
  "eventType": "PRESSED_EDITOR_ACTION",
  "elementDescriptors": [
    {
      "resourceId": "com.google.samples.apps.topeka:id/first_name"
    }
  ]
}

Premi indietro

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
eventType Specifica il tipo di azione dello script Robo.
"eventType": "PRESSED_BACK" Invia un evento KEYCODE_BACK al dispositivo.
"eventType": "PRESSED_BACK_EMULATOR_28" Utilizzato dal registratore di script Robo in Android Studio per ripristinare l'API 28 degli emulatori.

Quello che segue è un esempio di un'azione di script Robo che preme indietro:

{
  "eventType": "PRESSED_BACK"
}

Premi a casa

Questa azione invia un evento KEYCODE_HOME al dispositivo.

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "GO_HOME" --

Quello che segue è un esempio di un'azione dello script Robo che preme home:

{
  "eventType": "GO_HOME"
}

Scorri un elemento per visualizzarlo

Questa azione fa sì che il test Robo scorra in avanti il ​​widget dell'interfaccia utente che corrisponde agli elementDescriptors specificati finché il widget dell'interfaccia utente che corrisponde ai childElementDescriptors specificati non è presente sullo schermo, oppure il widget a cui è stato fatto scorrere non può più essere fatto scorrere o finché non viene raggiunto il numero massimo di 50 scorrimenti.

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "ELEMENT_SCROLL_INTO_VIEW" --
elementDescriptors Identifica il widget dell'interfaccia utente con scorrimento utilizzando la gerarchia dell'interfaccia utente Android.
childElementDescriptors Identifica il widget dell'interfaccia utente a cui scorrere utilizzando la gerarchia dell'interfaccia utente di Android.

Di seguito è riportato un esempio di un'azione di script Robo che fa scorrere il widget dell'interfaccia utente con l'ID risorsa "my.app.package:id/scrollable_card_container" finché il widget dell'interfaccia utente con il testo "Orange" non è presente sullo schermo (o non è più possibile effettuare alcuno scorrimento). essere eseguito, oppure viene raggiunto il numero massimo di 50 scorrimenti):

{
  "eventType": "ELEMENT_SCROLL_INTO_VIEW",
  "elementDescriptors": [
    {
      "resourceId": "my.app.package:id/scrollable_card_container"
    }
  ],
  "childElementDescriptors": [
    {
      "text": "Orange"
    }
  ]
}

Scorri

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "VIEW_SWIPED" --
swipeDirection Specifica la direzione dello scorrimento:
  • Left
  • Right
  • Up
  • Down
  • Forward : verso Down o Right a seconda della scorrevolezza verticale o orizzontale del widget dell'interfaccia utente di destinazione.
  • Backward : Up o Left a seconda della scorrevolezza verticale o orizzontale del widget dell'interfaccia utente di destinazione.
elementDescriptors Identifica il widget dell'interfaccia utente di destinazione utilizzando la gerarchia dell'interfaccia utente Android.

Di seguito è riportato un esempio di un'azione di script Robo che fa scorrere verso l'alto un widget dell'interfaccia utente con l'ID risorsa "my.app.package:id/custom_content" :

{
  "eventType": "VIEW_SWIPED",
  "swipeDirection": "Up",
  "elementDescriptors": [
    {
      "resourceId": "my.app.package:id/custom_content"
    }
  ]
}

Fai uno screenshot

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "TAKE_SCREENSHOT" --
screenshotName Specifica il nome del file della schermata.

Di seguito è riportato un esempio di un'azione di script Robo che acquisisce uno screenshot:

{
  "eventType": "TAKE_SCREENSHOT",
  "screenshotName": "my_screenshot"
}

Tocca un punto sullo schermo

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "POINT_TAP" --
pointTapXCoordinate La coordinata X del pixel del punto toccato. Si escludono a vicenda con pointTapXPercent e pointTapYPercent .
pointTapYCoordinate La coordinata Y del pixel del punto toccato. Si escludono a vicenda con pointTapXPercent e pointTapYPercent .
pointTapXPercent La coordinata X percentuale del punto toccato. Si escludono a vicenda con pointTapXCoordinate e pointTapYCoordinate .
pointTapYPercent La coordinata Y percentuale del punto toccato. Si escludono a vicenda con pointTapXCoordinate e pointTapYCoordinate .

Di seguito è riportato un esempio di un'azione di script Robo che tocca il centro di uno schermo:

{
  "eventType": "POINT_TAP",
  "pointTapXPercent": 50,
  "pointTapYPercent": 50
}

Tocca un punto all'interno di un elemento

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "POINT_TAP_ELEMENT" --
pointTapXPercent La coordinata X percentuale all'interno dell'elemento di destinazione.
pointTapYPercent La coordinata Y percentuale all'interno dell'elemento di destinazione.
elementDescriptors Identifica il widget dell'interfaccia utente di destinazione utilizzando la gerarchia dell'interfaccia utente Android.

Quello che segue è un esempio di un'azione di script Robo che sposta il cursore della barra di ricerca verso destra:

{
  "eventType": "POINT_TAP_ELEMENT",
  "pointTapXPercent": 80,
  "pointTapYPercent": 50,
  "elementDescriptors": [
    {
      "resourceId": "my.app.package:id/my_seekbar"
    }
  ]
}

Termina la scansione

Questa azione interrompe il test Robo.

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "TERMINATE_CRAWL" --

Di seguito è riportato un esempio di un'azione di script Robo che interrompe un test Robo:

{
  "eventType": "TERMINATE_CRAWL"
}

Aspettare

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "DELAYED_MESSAGE_POSTED" --
delayTime Specifica il tempo di attesa, in millisecondi.

Di seguito è riportato un esempio di un'azione di script Robo che attende per tre secondi:

{
  "eventType": "DELAYED_MESSAGE_POSTED",
  "delayTime": 3000
}

Attendi un elemento

Questa azione fa sì che Robo test attenda che un elemento appaia sullo schermo fino al timeout specificato.

La tabella seguente elenca gli attributi richiesti:

Attributo Descrizione
"eventType": "WAIT_FOR_ELEMENT" --
delayTime Specifica il timeout di attesa, in millisecondi.
elementDescriptors Identifica il widget dell'interfaccia utente atteso utilizzando la gerarchia dell'interfaccia utente di Android.

Di seguito è riportato un esempio di un'azione di script Robo che attende fino a 30 secondi affinché un widget dell'interfaccia utente con l'ID risorsa "my.app.package:id/confirmation_button" venga visualizzato sullo schermo:

{
  "eventType": "WAIT_FOR_ELEMENT",
  "delayTime": 30000,
  "elementDescriptors": [
    {
      "resourceId": "my.app.package:id/confirmation_button"
    }
  ]
}

Prossimi passi