Passer au contenu principal

CI/CD

Écrit par Ines

Présentation

L'API CI de Thunders vous permet de mettre en file d'attente des exécutions de tests, d'interroger leur état et de récupérer des rapports structurés (JUnit XML ou JSON), le tout via 3 points de terminaison situés sous /api/ci/.

Quand vous testez une application mobile, le même pipeline peut aussi téléverser chaque nouveau build avant de mettre les exécutions en file d'attente. Consultez la section Téléverser un build d'application mobile plus bas.

Points de terminaison de l'API

POST  https://api.thunders.ai/api/ci/run
GET https://api.thunders.ai/api/ci/run/{runId}
GET https://api.thunders.ai/api/ci/run/{runId}/report

Authentification

L'API nécessite une authentification à l'aide d'un jeton Bearer :

Authorization: Bearer YOUR_THUNDER_TEST_TOKEN

Vous devez obtenir un jeton API valide auprès de la plateforme Thunders. Ce jeton doit être stocké en toute sécurité en tant que secret dans votre environnement ou votre dépôt GitHub.

Où trouver votre jeton API

Les jetons API appartiennent à l'organisation plutôt qu'à un projet spécifique ; un même jeton fonctionne donc pour tous les projets de cette organisation. Vous devez disposer d'un rôle d'administrateur pour en créer un.

  1. Sélectionnez le nom de votre organisation en haut de la barre latérale gauche pour ouvrir le menu de l'organisation.

  2. Sélectionnez Paramètres.

  3. Sélectionnez Jetons API. La page Jetons API de l'organisation répertorie tous les jetons avec leur statut, leur date de création et le nom de leur créateur.

  4. Créez un nouveau jeton, puis confirmez lorsque le message "Êtes-vous sûr de vouloir créer un nouveau jeton API ?" s'affiche.

  5. Sélectionnez Afficher pour révéler le nouveau jeton, puis Copier.

  6. Collez-le directement dans votre magasin de secrets. Traitez-le comme un mot de passe, car toute personne en possession de ce jeton peut mettre en file d'attente des exécutions au nom de votre organisation.

Pour révoquer un jeton, sélectionnez Révoquer dans la ligne correspondante. La révocation est irréversible, et tout pipeline utilisant encore ce jeton recevra une erreur 401 Unauthorized ; veillez donc à émettre un jeton de remplacement au préalable.

En-têtes

En-tête

Valeur

Description

Authorization

Bearer YOUR_THUNDER_TEST_TOKEN

Jeton d'authentification

X-MS-API-ROLE

M2M

Indique un appel API de machine à machine

Content-Type

application/json

Spécifie le format du corps de la requête

POST /api/ci/run — Mettre en file d'attente des exécutions

Met en file d'attente les exécutions d'un ou plusieurs cas de test et/ou groupes de test. Toutes les exécutions sont regroupées sous un runId commun. Renvoie des URL que vous pouvez utiliser pour interroger l'état d'avancement et récupérer le rapport final.

Corps de la requête

Au moins l'un des paramètres testCaseIds ou testSetIds doit être fourni. Les deux peuvent être fournis dans la même requête ; dans ce cas, les deux seront exécutés.

Exemple

{
"projectId": "c8e34ec4-2464-43c7-8db8-1b3a47a22337",
"testCaseIds": ["a836fadc-377a-46fe-96b0-21f37c626bf9"],
"testSetIds": ["8eea2fd3-da6d-4434-a310-176be84c5646"],
"environmentId": "eca24252-e566-40a8-b2b0-707b7efa85d8",
"personaId": "70d0ac52-fe94-4d89-aba9-8ef28dd8c04c",
"maxParallelism": 5,
"maxRetries": 1,
"browserSettings": { "location": "Paris", "resolution": "1440x900", "deviceType": "Desktop" }
}

Paramètres

Paramètre

Type

Obligatoire

Valeur par défaut

Description

projectId

string (UUID)

Oui

-

Identifiant unique du projet.

testCaseIds

array (UUID)

Non

-

Liste des identifiants des cas de test à exécuter.

testSetIds

array (UUID)

Non

-

Liste des identifiants des groupes de test. Chaque groupe se décompose en ses cas de test.

environmentId

string (UUID)

Oui

-

L'environnement à utiliser.

personaId

string (UUID)

Oui

-

La Persona utilisée pour l'exécution du test.

maxParallelism

int

Non

4

Nombre maximal de tests en parallèle. 0 = maximum.

maxRetries

int

Non

0

Nombre de nouvelles tentatives pour les tests en échec (0-3).

executionMode

string

Non

parallel

parallel ou sequential. En mode sequential, les unités s'exécutent une par une, par ordre alphabétique de leurs noms.

failureBehavior

string

Non

continue

continue ou stop. Ne s'applique que lorsque executionMode = sequential. stop annule les exécutions restantes après le premier cas de test en échec, une fois ses tentatives épuisées.

branchId

string (UUID)

Non

main

La branche dont les tests s'exécutent. Consultez la section Exécuter les tests d'une branche plus bas.

testAssetReferences

array

Non

-

Références des ressources de test pour l'exécution par lots.

browserSettings.location

string

Non

"Paris"

Cluster géographique : Paris, London, Amsterdam, SanFrancisco, NewYork.

browserSettings.browserType

string

Non

"Chromium"

Chromium, Chrome, Firefox ou Safari.

browserSettings.deviceType

string

Non

"Desktop"

Desktop, Mobile ou Tablet.

browserSettings.resolution

string

Non

"1280x720"

Résolution de la fenêtre d'affichage (par exemple "1440x900").

Réponse — 202 Accepted

{
"runId": "01966a3e-...",
"statusUrl": "/api/ci/run/01966a3e-...",
"reportUrl": "/api/ci/run/01966a3e-.../report"
}

GET /api/ci/run/{runId} — Interroger l'état de l'exécution

Renvoie l'état agrégé de toutes les exécutions de test. Interrogez jusqu'à ce que status soit définitif (passed, failed, stopped, cancelled).

Réponse — 200 OK

{
"runId": "01966a3e-...",
"status": "running",
"testCount": 5,
"passedCount": 2,
"failedCount": 0,
"runningCount": 2,
"queuedCount": 1,
"startedAt": "2026-03-20T14:30:00Z",
"endedAt": null,
"durationMs": null
}

GET /api/ci/run/{runId}/report — Récupérer le rapport

Renvoie le rapport d'exécution du test. Le format dépend de l'en-tête Accept :

  • Par défaut (ou application/xml) : JUnit XML, compatible avec Jenkins, GitHub Actions (dorny/test-reporter), GitLab et d'autres.

  • Accept: application/json : rapport JSON avec des données structurées.

Exécuter les tests d'une branche

Une exécution CI utilise les tests de main, sauf si vous indiquez une autre branche. Pour exécuter la version des tests d'une branche, ajoutez branchId au corps de la requête :

{
"projectId": "c8e34ec4-2464-43c7-8db8-1b3a47a22337",
"testSetIds": ["8eea2fd3-da6d-4434-a310-176be84c5646"],
"environmentId": "eca24252-e566-40a8-b2b0-707b7efa85d8",
"personaId": "70d0ac52-fe94-4d89-aba9-8ef28dd8c04c",
"branchId": "0199b2c4-7e1a-7c3d-9f20-5a6b8c9d0e1f"
}
  • L'exécution utilise les étapes et les paramètres que les tests ont sur cette branche.

  • Les champs statusUrl et reportUrl de la réponse portent le même branchId. Utilisez-les tels que la réponse les renvoie.

  • L'API refuse une branche fusionnée, abandonnée ou d'un autre projet de test, avec 400 Bad Request. Elle n'exécute jamais main à sa place.

  • Sans branchId, l'exécution utilise main : vos pipelines existants ne changent pas.

Pour trouver l'identifiant d'une branche, listez les branches actives du projet de test avec le même jeton et les mêmes en-têtes :

GET   https://api.thunders.ai/api/branches?projectId={projectId}

Chaque branche de la réponse porte son id et son name. main figure toujours dans la liste.

Pour créer une branche et la fusionner dans Thunders, consultez Comment modifier mes tests sur une branche et les fusionner dans main ?

Gestion des erreurs

L'API utilise les codes d'état HTTP standard :

  • 202 Accepted : POST /api/ci/run — les tests ont été mis en file d'attente avec succès

  • 200 OK : points de terminaison GET — la requête a abouti

  • 400 Bad Request : requête mal formée ou paramètres obligatoires manquants

  • 401 Unauthorized : échec de l'authentification ou jeton invalide

  • 403 Forbidden : autorisations insuffisantes

  • 404 Not Found : aucune exécution de test trouvée pour le runId indiqué

  • 500 Internal Server Error : erreur inattendue du serveur

Utilisation avec GitHub Actions

Intégrez l'API CI de Thunders à GitHub Actions pour automatiser l'exécution des tests dans votre pipeline CI/CD.

Exemple de workflow GitHub Actions

name: Run Thunders Tests
on:
workflow_dispatch:
permissions:
contents: read
checks: write
jobs:
run-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Queue Thunders CI Run
id: trigger
run: |
set -euo pipefail
RESP=$(curl -sS --fail-with-body -X POST https://api.thunders.ai/api/ci/run \
-H "Authorization: Bearer ${{ secrets.THUNDER_TEST_TOKEN }}" \
-H "X-MS-API-ROLE: M2M" \
-H "Content-Type: application/json" \
-d '{"projectId":"YOUR_PROJECT_ID","testSetIds":["YOUR_TEST_SET_ID"],"environmentId":"YOUR_ENVIRONMENT_ID","personaId":"YOUR_PERSONA_ID","maxParallelism":5,"maxRetries":1}')
echo "status_url=$(echo "$RESP" | jq -r '.statusUrl')" >> $GITHUB_OUTPUT
echo "report_url=$(echo "$RESP" | jq -r '.reportUrl')" >> $GITHUB_OUTPUT
- name: Wait for completion
id: run-tests
run: |
set -euo pipefail
MAX_WAIT=5400; INTERVAL=30; ELAPSED=0
while [ $ELAPSED -lt $MAX_WAIT ]; do
RESP=$(curl -sS --fail-with-body \
"https://api.thunders.ai${{ steps.trigger.outputs.status_url }}" \
-H "Authorization: Bearer ${{ secrets.THUNDER_TEST_TOKEN }}" \
-H "X-MS-API-ROLE: M2M")
STATUS=$(echo "$RESP" | jq -r '.status')
echo "[$ELAPSED s] status=$STATUS"
case "$STATUS" in
passed|failed|stopped|cancelled)
echo "final_status=$STATUS" >> $GITHUB_OUTPUT; break;;
esac
sleep $INTERVAL; ELAPSED=$((ELAPSED+INTERVAL))
done
- name: Download JUnit report
run: |
curl -sS --fail-with-body \
"https://api.thunders.ai${{ steps.trigger.outputs.report_url }}" \
-H "Authorization: Bearer ${{ secrets.THUNDER_TEST_TOKEN }}" \
-H "X-MS-API-ROLE: M2M" \
-o test-results.xml
- name: Publish test results
uses: dorny/test-reporter@v1
continue-on-error: true
with:
name: Thunders CI Results
path: test-results.xml
reporter: java-junit
- name: Fail if tests did not pass
if: steps.run-tests.outputs.final_status != 'passed'
run: exit 1

Configuration des secrets GitHub

  1. Accédez à votre dépôt GitHub

  2. Accédez à Settings, puis Secrets and variables, puis Actions

  3. Cliquez sur "New repository secret"

  4. Nom : THUNDER_TEST_TOKEN

  5. Valeur : votre jeton API Thunders

  6. Cliquez sur "Add secret"

Lot de ressources de test

La fonctionnalité Lot de ressources de test vous permet d'exécuter plusieurs fois le même cas de test avec différentes variations de données, en important des fichiers CSV en tant que ressources de test. Chaque ligne du fichier CSV correspond à une exécution distincte, ce qui permet de réaliser des tests pilotés par les données à grande échelle.

Fonctionnement

  1. Importez des fichiers CSV en tant que ressources de test dans votre projet (via l'application web Thunders ou l'API)

  2. Référencez les fichiers CSV dans votre requête API à l'aide du paramètre testAssetReferences

  3. Chaque ligne du fichier CSV génère une exécution distincte avec ses propres remplacements de variables et ses propres paramètres de navigateur

  4. Toutes les exécutions d'un même lot sont regroupées sous un batchId commun

Avec executionMode: sequential, chaque ligne du fichier CSV s'exécute comme une chaîne indépendante : les cas de test d'une même ligne s'exécutent un par un dans l'ordre alphabétique, tandis que les lignes s'exécutent en parallèle. maxParallelism limite le nombre de lignes s'exécutant simultanément. failureBehavior: stop s'applique à une seule ligne.

Le paramètre testAssetReferences

Ajoutez le champ testAssetReferences au corps de votre requête :

{
"projectId": "your-project-id",
"testCaseIds": ["your-test-case-id"],
"environmentId": "your-environment-id",
"personaId": "your-persona-id",
"testAssetReferences": ["FILE_MYDATA_CSV", "FILE_USERS_CSV"]
}

Format de référence : lorsque vous importez un fichier nommé mydata.csv, sa référence devient FILE_MYDATA_CSV. La convention de nommage est FILE_<NOM_FICHIER>_<EXT>, le nom du fichier étant en majuscules et les caractères spéciaux remplacés par des traits de soulignement.

Pour en savoir plus sur la fonctionnalité de traitement par lots, consultez cette documentation

Combiner plusieurs fichiers CSV

Vous pouvez passer plusieurs références CSV dans le tableau testAssetReferences. Les lignes de tous les fichiers sont fusionnées séquentiellement en un seul lot avec un indexage continu.

Si browsers.csv comporte 3 lignes et mobile.csv 2 lignes, le lot contiendra au total 5 exécutions indexées de 1 à 5.

Téléverser un build d'application mobile

Quand vous testez une application mobile, votre pipeline peut envoyer chaque nouvel APK ou IPA à Thunders avant de mettre les exécutions en file d'attente. Le téléversement remplace le build de cette application dans un environnement, et l'exécution suivante l'installe. Une exécution déjà démarrée se termine sur le build avec lequel elle a démarré.

GET   https://api.thunders.ai/api/projects/{projectId}/apps
POST https://api.thunders.ai/api/apps/{appId}/builds

Les deux points de terminaison utilisent le même jeton API et les mêmes en-têtes Authorization et X-MS-API-ROLE: M2M que les points de terminaison CI ci-dessus.

Trouver les identifiants de l'application et de l'environnement

GET /api/projects/{projectId}/apps liste les applications d'un projet de test. Chaque application porte son id et un tableau environments, avec l'environmentId et l'environmentName de chaque environnement et le build qu'il contient. Copiez ces deux identifiants une fois dans votre pipeline.

Téléverser le build

POST /api/apps/{appId}/builds attend un corps multipart/form-data au lieu de JSON.

Champ

Type

Obligatoire

Description

projectId

chaîne de caractères (UUID)

Oui

Le projet de test qui contient l'application.

environmentId

chaîne de caractères (UUID)

Oui

L'environnement dont le build est remplacé.

file

fichier

Oui

Un fichier .apk ou .ipa, de 500 Mo au maximum.

Réponse, 200 OK

{
"fileName": "app-release.apk",
"fileSizeBytes": 14200000,
"platform": "android",
"source": "ci",
"uploadedAt": "2026-10-13T09:30:00Z",
"appPackage": "com.acme.shop",
"appVersion": "2.4.1",
"appBuildNumber": "241"
}

Champ

Type

Description

platform

chaîne

android ou ios, d'après l'extension du fichier.

source

chaîne

ci pour un build téléversé avec un jeton API. Thunders affiche un badge CI sur ce build.

appPackage

chaîne

Le nom de package Android ou le bundle id iOS, lu dans le fichier.

appVersion

chaîne ?

versionName sur Android, CFBundleShortVersionString sur iOS. null quand le build n'en déclare pas.

appBuildNumber

chaîne ?

versionCode sur Android, CFBundleVersion sur iOS. null quand le build n'en déclare pas.

Chaque exécution sur une application mobile enregistre la version et le numéro du build qu'elle a installé, et son rapport les affiche dans la ligne Build de l'application.

Erreurs

  • 400 Bad Request : aucun fichier, un fichier vide, un fichier autre que .apk ou .ipa, ou un build dont le nom de package est illisible

  • 401 Non autorisé : le jeton est absent ou invalide

  • 404 Non trouvé : l'application ou l'environnement n'existe pas dans ce projet de test, ou appartient à une autre organisation

  • 413 Payload Too Large : le fichier dépasse 500 Mo

Exemple d'étape GitHub Actions

Ajoutez cette étape avant celle qui met les exécutions en file d'attente :

      - name: Upload the new build to Thunders
run: |
curl --fail-with-body -sS -X POST \
"https://api.thunders.ai/api/apps/YOUR_APP_ID/builds" \
-H "Authorization: Bearer ${{ secrets.THUNDER_TEST_TOKEN }}" \
-H "X-MS-API-ROLE: M2M" \
-F "projectId=YOUR_PROJECT_ID" \
-F "environmentId=YOUR_ENVIRONMENT_ID" \
-F "file=@app/build/outputs/apk/release/app-release.apk"

--fail-with-body arrête le job en cas d'erreur et affiche le message qui l'explique.

Lorsque vous n'avez pas besoin d'un pipeline

Si vous souhaitez simplement qu'un groupe de test s'exécute selon une règle récurrente, un Planning s'en charge dans Thunders, sans pipeline à maintenir ni jeton à renouveler. Ouvrez le groupe de test, sélectionnez l'onglet Plannings, puis choisissez l'une des cinq fréquences, de Chaque heure à Mensuel.

Continuez à utiliser la CI lorsque l'exécution doit avoir lieu à un moment précis de votre processus de livraison, par exemple après un déploiement ou sur une pull request, ou lorsque vous avez besoin d'une exécution plus fréquente qu'une fois par heure. Chaque heure est l'intervalle le plus court pris en charge par un Planning.

Avez-vous trouvé la réponse à votre question ?