Captures prises en direct · Code copié de suites validées · v0.8.5

SAPFX en images : SAP GUI, SAP Fiori et API, montrés sur de vrais systèmes

SAPFX (paquet robotframework-sapfx) est un ensemble de bibliothèques Robot Framework open source pour automatiser les tests SAP. Cette page montre ce qu'il fait, écran par écran : chaque capture vient d'un système SAP réel du laboratoire (ABAP Platform 1909 et 2023 sous Docker, une application SAP Fiori® Elements servie en local), chaque extrait de code est copié d'une suite validée en direct, avec sa date et son résultat. Aucun keyword n'est inventé : un contrôle automatique confronte chaque exemple à sa suite d'origine et à la documentation des keywords.

9
tests hybrides, sur deux ou trois canaux
5
environnements montrés : SAP GUI, SAP Fiori, WebGUI, API, SAP BTP
18
captures d'écrans réels
30
extraits de code copiés de suites validées
Le cœur de SAPFX

La force de l'hybridation : un test, deux ou trois canaux

Un test SAPFX n'est pas enfermé dans un canal. Il prépare par l'API, agit à l'écran et vérifie par un canal indépendant, avec les mêmes règles et dans le même rapport Robot Framework. Les neuf tests ci-dessous ont été validés en direct sur des systèmes SAP de développement ; les trois premiers ont été rejoués le 5 octobre 2026 pour cette page, et tous sont restés verts.

Écran + API + visuel

L'écran et l'API comptent les mêmes produits, et l'écran est scellé

Le nombre de produits du modèle de démonstration EPM est compté deux fois sur le même système : par le bouton « Number of Entries » de SE16 dans SAP GUI, et par le $count du service OData qui projette la même table. Les deux doivent être égaux ; un écart trahirait un service qui filtre, une donnée fantôme ou un mandant croisé. L'écran de sélection qui a servi au compte est ensuite comparé à une capture de référence par empreinte perceptuelle (Screen Should Match Baseline), zones volatiles masquées : le canal pixels voit ce que ni l'API ni le scripting ne voient.

SAP GUI (SE16)ODataEmpreinte visuelle

Preuve : Suite « croisement API et écran scellé par une assertion visuelle » : 3/3 sur A4H (release 754), validée le 17/07/2026 et rejouée le 05/10/2026 : « Produits EPM : 205 via OData == 205 via SE16 ».

tests/robot/cross_paradigm_api_visual.robot
Product Count Agrees Across API And GUI Channels
    # …
    ${gui_count}=    Count Table Entries    ${EPM_PRODUCTS_TABLE}
    Should Be True    ${gui_count} > 0
    ...    msg=Table ${EPM_PRODUCTS_TABLE} vide : données EPM absentes sur ce système ?
    ${api_count}=    Get Odata Count    ${EPM_PRODUCTS_PATH}    alias=a4h
    Should Be Equal As Integers    ${api_count}    ${gui_count}
    ...    msg=OData $count (${api_count}) ≠ SE16 ${EPM_PRODUCTS_TABLE} (${gui_count}).
    Log    Produits EPM : ${api_count} via OData == ${gui_count} via SE16.
# …
SE16 Product Screen Matches Its Visual Baseline
    # …
    Open Table In SE16    ${EPM_PRODUCTS_TABLE}
    Log Screenshot    Écran de sélection SE16 de ${EPM_PRODUCTS_TABLE} (source du compte GUI)
    Screen Should Match Baseline    ${VISUAL_BASELINE_NAME}
    ...    threshold=${VISUAL_THRESHOLD}    baseline_directory=${VISUAL_BASELINE_DIR}
    ...    mask_elements=auto    per_resolution=${True}
API + WebGUI + RFC

Le même prix lu par trois canaux, comparé entre eux

Un produit est découvert sur la cible (jamais écrit en dur), puis lu par le service OData, par la grille SE16 du WebGUI et par RFC_READ_TABLE. Les trois prix sont comparés entre eux, pas chacun à une valeur attendue : une valeur commune passerait aussi si deux canaux lisaient la même copie périmée. L'écran affiche 3,25 là où les protocoles rendent 3.25 : la notation décimale de l'utilisateur est lue sur le système, puis vérifiée par un aller-retour. Avant toute lecture, chaque canal prouve sa cible par son propre discriminant (release côté RFC, volume du catalogue Gateway côté API, adresse atteinte côté WebGUI), parce que les deux systèmes du laboratoire annoncent le même identifiant.

ODataWebGUI (SE16)RFC

Preuve : Suite « cycle de vie d'un produit EPM lu par trois canaux » : 7/7 sur la release 754 et 7/7 sur la release 758 le 21/09/2026, rejouée 7/7 sur A4H le 05/10/2026. Lecture seule.

tests/robot/cross/cycle_vie_produit_epm_trois_canaux.robot
6. Assertion reine : les trois canaux rendent le même prix
    # …
    ${prix}=    The Three Channels Should Agree On The Price
    ...    ${API_RECORD}    ${WEBGUI_RECORD}    ${RFC_RECORD}
    ...    ${DECIMAL_NOTATION}
    ${devise}=    The Three Channels Should Agree On The Currency
    ...    ${API_RECORD}    ${WEBGUI_RECORD}    ${RFC_RECORD}
resources/page_objects/sepmra_prod_man.resource
Read Epm Products By Api
    # …
    ${projection}=    Set Variable
    ...    ${EPM_PRODUCT_API_ID_FIELD},${EPM_PRODUCT_API_PRICE_FIELD},${EPM_PRODUCT_API_CURRENCY_FIELD}
    ${chemin}=    Set Variable    ${EPM_PROD_MAN_SERVICE}/${EPM_PRODUCT_ENTITY_SET}
    IF    '${top}' == '${EMPTY}'
        ${entites}=    Get Odata Entities    ${chemin}    alias=${alias}
        ...    $filter=${critere}    $select=${projection}    $orderby=${EPM_PRODUCT_API_ID_FIELD}
# …
Read Epm Product By Webgui
    # …
    [Arguments]    ${product}    ${url}
    Open WebGui Data Browser    ${url}
    Display WebGui Table Contents With Filter    ${EPM_PRODUCT_TABLE}
    ...    ${EPM_PRODUCT_KEY_COLUMN}=${product}
    ${extrait}=    Extract Displayed WebGui Grid
    ...    key=${EPM_PRODUCT_KEY_COLUMN}
    ...    source=produits EPM (${EPM_PRODUCT_TABLE}) filtrés sur ${product}
    Table Extract Should Be Complete    ${extrait}
# …
Read Epm Product By Rfc
    # …
    [Arguments]    ${product}    ${alias}=${RFC_ALIAS}
    ${lignes}=    Read Business Records    ${EPM_PRODUCT_TABLE}
    ...    ${EPM_PRODUCT_COLUMNS}
    ...    filter=${EPM_PRODUCT_KEY_COLUMN} EQ '${product}'    alias=${alias}
Launchpad + RFC + HTTP

La tuile affiche ce que la vue CDS compte

La tuile « Approve Purchase Orders » du launchpad SAP Fiori affiche un compteur. Le test le force à se rafraîchir, prouve que la réponse réseau correspond bien au chiffre affiché, puis le confronte au nombre de commandes en attente compté par RFC sur la vue CDS analytique standard SEPM_I_PurchaseOrder, avant et après le rafraîchissement. Sans arrondi ni tolérance : un écart d'une unité fait échouer le test en nommant les deux nombres.

Launchpad SAP FioriRFCHTTP

Preuve : Suite « agrégat d'une vue CDS confronté au compteur affiché par une tuile du launchpad » : 6/6 le 29/09/2026, rejouée 6/6 le 05/10/2026 : « tuile 19, vue CDS 19 avant et 19 après », réponse du rafraîchissement 28,5 ms après la demande.

A4H, release 754Capture réelle
Launchpad SAP Fiori d'A4H : la tuile Approve Purchase Orders affiche 19
Launchpad SAP Fiori servi par A4H (SAPUI5 1.71.47) : la tuile « Approve Purchase Orders » affiche 19, le nombre que la vue CDS compte par RFC.A4H, release 754 · Capture du 5 octobre 2026Écran d'un logiciel SAP : le logo SAP et les marques SAP sont la propriété de SAP SE.
Voir l'extrait de la suite
tests/robot/cross/cds_tuile_flp_approbation_a4h.robot
2. Le compteur affiché égale l'agrégat de la vue CDS analytique standard (assertion reine)
    # …
    ${annotations}=    La Vue CDS Devrait Etre Analytique
    ${avant}=    Compter Les Commandes En Attente Par La Vue CDS
    ${tuile}=    Lire Le Compteur Frais De La Tuile D Approbation
    ${apres}=    Compter Les Commandes En Attente Par La Vue CDS
    La Mesure Devrait Etre Stable    ${avant}    ${apres}    ${tuile}
    Le Compteur De La Tuile Devrait Egaler    ${tuile}[number]    ${apres}
    ...    ${INTENT_TUILE_APPROBATION} contre ${VUE_CDS_ANALYTIQUE} au statut ${STATUT_EN_ATTENTE}
resources/page_objects/tuile_cds_approbation.resource
Lire Le Compteur Frais De La Tuile
    # …
    [Arguments]    ${intent}    ${budget}=${BUDGET_COMPTEUR_TUILE}
    ${tuile}=    Refresh Flp Tile Counter    ${intent}    timeout=${budget}
    Should Be Equal    ${tuile}[body_check]    matched
    ...    msg=La réponse du rafraîchissement de ${intent} n'est pas reliée à la valeur lue (${tuile}[body_check]) : rien n'établit que le compteur ${tuile}[value] vient de cette réponse.
    RETURN    ${tuile}
# …
Compter Les Commandes Par La Vue CDS
    # …
    [Arguments]    ${statut}
    ${lignes}=    Read Business Records    ${VUE_SQL_ANALYTIQUE}    ${COLONNE_MANDANT},${COLONNE_COMMANDE}
    ...    filter=${COLONNE_STATUT_GLOBAL} EQ '${statut}'    alias=${WF_RFC_ALIAS}
    ${nombre}=    Les Lignes Devraient Etre Des Commandes Du Mandant Servi    ${lignes}    ${VUE_SQL_ANALYTIQUE}
    RETURN    ${nombre}
SAP GUI + RFC

Le rôle d'un Business Partner, ajouté à l'écran, constaté en base

Le rôle est ajouté dans la transaction BP par sa clé technique (jamais par son libellé traduit), puis la vérité est lue par RFC dans la table BUT100 sur la clé exacte, document de modification compris. Le rôle est ensuite retiré par l'écran et sa disparition est constatée sur la même clé. Le mode modification de l'écran se constate sur la modifiabilité d'un champ (Element Should Be Changeable), pas sur un titre traduit.

SAP GUI (BP)RFC

Preuve : Suite « rôle métier d'un Business Partner attribué par l'écran puis retiré » : 7/7 sur A4H le 28/09/2026, écriture sous accord explicite.

tests/robot/cross/bp_role_partenaire_commercial_a4h.robot
1. Attribuer puis retirer le rôle Prospect par l'écran : la liaison apparaît puis disparaît
    # …
    Ouvrir Le Partenaire    ${CAMPAIGN_PARTNER}
    Passer Le Partenaire En Modification
    Choisir Le Role Par Sa Cle    ${ROLE_PROSPECT}
    La Combo Des Roles Devrait Porter    ${ROLE_PROSPECT}
    ${ajout}=    Sauvegarder Le Partenaire
    Le Message Devrait Etre    ${ajout}    ${MSG_SAVED}
    La Liaison De Role Devrait Etre Ouverte    ${CAMPAIGN_PARTNER}    ${ROLE_PROSPECT}
    ${document_ajout}=    Un Document Devrait Journaliser L Ajout Du Role
    ...    ${CAMPAIGN_PARTNER}    ${ROLE_PROSPECT}    ${depart}
    Retirer Le Role Par L Ecran    ${ROLE_PROSPECT}
    ${retrait}=    Sauvegarder Le Partenaire
    Le Message Devrait Etre    ${retrait}    ${MSG_SAVED}
    Aucune Liaison De Role Ne Devrait Exister    ${CAMPAIGN_PARTNER}    ${ROLE_PROSPECT}
resources/page_objects/bp_business_partner.resource
Lire La Liaison De Role
    # …
    [Arguments]    ${numero}    ${role}
    ${lignes}=    Read Business Records    ${BP_ROLE_LINK_TABLE}    ${BP_ROLE_LINK_FIELDS}
    ...    filter=PARTNER EQ '${numero}' AND RLTYP EQ '${role}'    alias=${BP_RFC_ALIAS}
    RETURN    ${lignes}
SAP GUI + RFC

Un job planifié à l'écran, attendu et lu par RFC

Un job au nom unique est défini et sauvé dans SM36 (champs désignés par leur libellé). Son run est retrouvé par RFC dans le journal des jobs, attendu jusqu'à la fin sans aucune pause fixe, puis son spool est lu de deux façons indépendantes, par l'étape du job et par le numéro de spool. Le contenu affiché par SP01 recoupe la lecture RFC.

SAP GUI (SM36, SP01)RFC (XBP)

Preuve : Suite « job de fond planifié par SM36, attendu, son spool lu par deux voies » : 11/11 sur A4H le 01/10/2026.

tests/robot/cross/job_arriere_plan_spool_a4h.robot
1. Job planifié par SM36, attendu par le journal, son spool lu par RFC (assertion reine)
    # …
    ${nom}=    Nom De Job Unique Du Run    S1
    ${constat}=    Planifier Un Job ABAP Par SM36    ${nom}    ${PROGRAM_WITH_SPOOL}    demarrage=immediat
    Le Message De Sauvegarde Devrait Nommer Le Job    ${constat}[message]    ${nom}
    Les Gestes De Planification Devraient Viser Les Champs Releves    ${constat}    ${nom}
    ...    ${PROGRAM_WITH_SPOOL}    immediat
    ${run}=    Retrouver Le Run Du Job    ${nom}
    ${ok}=    Evaluate    bool($run['jobcount']) and $run['scheduled_by'] == $SAP_USER
    Consigner Le Constat    job.run_by_unique_name    ${ok}
    ...    Get Background Job Run rend le run unique du nom, JOBCOUNT, propriétaire et mandant
    ${attente}=    Attendre La Fin Du Job    ${nom}    ${run}[jobcount]
    ${ok}=    Evaluate    $attente['state'] == 'done'
    Consigner Le Constat    wait.done    ${ok}
    ...    Wait For Background Job jobcount= rend done, statut F relu
    ${pas}=    Le Pas Du Job Devrait Porter Un Spool    ${nom}    ${run}    ${PROGRAM_WITH_SPOOL}
    ${par_pas}=    Read Job Spool    ${nom}    ${run}[jobcount]
    ${par_numero}=    Read Spool Request    ${pas}[spool_id]
resources/page_objects/background_job_spool.resource
Ouvrir La Definition D Un Job
    # …
    [Arguments]    ${nom}
    Go To Transaction    ${SM36_TCODE}
    L Ecran Devrait Etre    ${SM36_PROGRAM}    ${SM36_INITIAL_SCREEN}
    ${id_nom}=    Fill Field By Label    ${SM36_LABEL_JOB_NAME}    ${nom}
RFC + SAP GUI

Un IDoc sortant jugé par son code, et son entrant retrouvé

L'IDoc est créé par RFC, expédié, puis attendu jusqu'au statut 03, jugé par son code et jamais par un texte. L'IDoc entrant qu'il produit est retrouvé par le TID tRFC (jamais par l'horodatage) et attendu jusqu'au statut 53. Le moniteur WE02, lu à l'écran, doit afficher exactement les statuts que le canal RFC a lus.

RFCSAP GUI (WE02)

Preuve : Suite « IDoc sortant ALE créé par RFC, expédié et jugé par code, entrant lié par le TID » : 12/12 sur A4H le 01/10/2026, écriture sous accord explicite.

tests/robot/cross/idoc_sortant_ale_a4h.robot
1. Un IDoc sortant émis atteint le statut 03, jugé par son code, et son entrant n'est pas oublié
    # …
    Dispatch Idoc By Number    ${docnum}
    ${sortant}=    Wait For Idoc Status    ${docnum}    expected=success    timeout=${IDOC_WAIT}
    Should Be Equal    ${sortant}[status]    ${STATUS_OUTBOUND_PASSED_TO_PORT}
    Should Be Equal    ${sortant}[category]    success
    Idoc History Should Be    ${sortant}[history]    ${OUTBOUND_HISTORY}
    Should Not Be Empty    ${sortant}[history][-1][TID]
# …
2. L'entrant lié arrive à 53 et la valeur émise se retrouve dans la table métier
    # …
    ${sortant}=    Dispatch New Flight Customer Change    ${TEST_CUSTOMER}    ${email}
    ${tid}=    Set Variable    ${sortant}[history][-1][TID]
    Should Not Be Empty    ${tid}
    ${lien}=    Get Idoc Counterpart    ${sortant}[docnum]    timeout=${COUNTERPART_WAIT}
    Track Run Idoc    ${lien}[counterpart]
    Should Not Be Equal    ${lien}[counterpart]    ${sortant}[docnum]
    Should Be Equal    ${lien}[counterpart_direction]    ${DIRECTION_INBOUND}
    ${entrant}=    Wait For Idoc Status    ${lien}[counterpart]    expected=${STATUS_INBOUND_POSTED}
    ...    timeout=${IDOC_WAIT}
    Should Be Equal    ${entrant}[status]    ${STATUS_INBOUND_POSTED}
    Should Be Equal    ${entrant}[category]    success
    Idoc History Should Be    ${entrant}[history]    ${INBOUND_HISTORY}
    Should Be Equal    ${entrant}[history][0][TID]    ${tid}
# …
4. WE02 affiche, pour le sortant et pour l'entrant, les statuts que lit le RFC
    # …
    Open Idoc In Monitor    ${sortant}
    ${codes_sortant}=    Read Idoc Status Nodes
    Should Be Equal    ${codes_sortant}[0]    ${chaine}[outbound][status]
    Should Be Equal    ${codes_sortant}[0]    ${STATUS_OUTBOUND_PASSED_TO_PORT}
SAP GUI + RFC

Où le refus d'autorisation existe vraiment

Un compte de test au rôle d'affichage, créé par RFC (liste blanche de préfixes, marqueur obligatoire, jamais le mandant 000), tente d'ouvrir la saisie de commande de vente EPM. Le lancement est refusé : le message est jugé par son identité (S#/E/077) et jamais par son texte, et le noyau confirme l'absence de l'autorisation de transaction par un contrôle pour un utilisateur nommé. Témoin positif : SE16 reste autorisée et son compte de lignes égale celui de RFC ; contre-épreuve : T000 est refusée. Constat consigné en avertissement : sur cette application de démonstration, un rôle « lancement seul » modifie la note d'une commande, car aucun contrôle n'existe à la sauvegarde. Le test rougira le jour où un contrôle apparaîtra.

SAP GUI (SEPM_SO)RFC (rôles, noyau)

Preuve : Suite « où le refus d'autorisation existe vraiment sur une commande de vente EPM » : 11/11 sur ABAP Platform 2023 (release 758) le 07/10/2026, 9/9 en conformité (constats exclus), écriture sous accord explicite.

ABAP Platform 2023, release 758Capture réelle
SAP GUI sur ABAP Platform 2023 : « You are not authorized to use transaction SEPM_SO » dans la barre de statut, pour un utilisateur de test au rôle d'affichage
Le lancement de SEPM_SO est refusé à l'utilisateur de test restreint : la barre de statut porte l'erreur, le test juge son identité et non son texte.ABAP Platform 2023, release 758 · Capture du 7 octobre 2026Écran d'un logiciel SAP : le logo SAP et les marques SAP sont la propriété de SAP SE.
HTTP + RFC + SAP GUI

Une erreur provoquée, une entrée au journal, retrouvée à l'écran

Un appel OData vers un service inventé, au nom unique par run et prouvé absent du registre et des catalogues, est refusé en 403 (/IWFND/MED/170). La suite lit par RFC le journal d'erreurs de la Gateway et prouve une entrée neuve, reliée à la réponse reçue par l'identifiant de transaction et servie dans le bon mandant, puis la retrouve à l'écran. Le journal regroupe les erreurs identiques : le même appel rejoué fait monter le compteur de la ligne existante. Compter les entrées avant et après ne prouverait donc rien, d'où la preuve par l'URI et la transaction.

HTTP (OData)RFCSAP GUI (journal d'erreurs)

Preuve : Suite « supervision de la Gateway FES : un service enregistré, servi, et une erreur nommée au journal » : 7/7 sur A4H (release 754) le 07/10/2026. Les entrées de journal créées expirent à 14 jours.

A4H, release 754Capture réelle
Journal d'erreurs de la Gateway sur A4H : une seule ligne /IWFND/MED170 au compteur 2, avec son contexte d'erreur
Journal d'erreurs de la Gateway : le même appel rejoué reste une seule ligne, au compteur 2, avec son contexte d'erreur.A4H, release 754 · Capture du 7 octobre 2026Écran d'un logiciel SAP : le logo SAP et les marques SAP sont la propriété de SAP SE.
Canal 1 · SapEccLibrary · 195 keywords

SAP GUI pour Windows : lire, agir, prouver la cible

SapEccLibrary pilote SAP GUI pour Windows par l'API SAP GUI Scripting, sur les écrans des systèmes SAP ECC et SAP S/4HANA®. Les identifiants techniques des écrans vivent dans une couche de keywords métier : les tests parlent le langage du métier.

Grilles ALV

Lire une grille ALV en entier, et le prouver

Une grille ALV rend ses lignes non chargées en cellules vides au lieu de lever une erreur : un relevé creux a le bon nombre de lignes et passe les contrôles naïfs. Read Full Grid fait défiler la grille pour matérialiser chaque ligne, et Count Blank Grid Rows refuse un relevé creux. Les colonnes se lisent par leur identifiant technique, indépendant de la langue.

Preuve : Suite « smoke système des mandants T000 par SE16 » : 1/1 sur A4H le 21/09/2026. Capture de la même lecture sur SCARR le 05/10/2026.

A4H, release 754Capture réelle
SAP GUI : grille ALV de la table SCARR dans le Data Browser SE16, 18 lignes
Grille ALV de SE16 sur la table SCARR, lue en entier par SapEccLibrary : 18 lignes, aucune ligne vide.A4H, release 754 · Capture du 5 octobre 2026Écran d'un logiciel SAP : le logo SAP et les marques SAP sont la propriété de SAP SE.
tests/robot/ui/ecc/smoke_test_t000_se16.robot
La table des mandants répond avec au moins les mandants standard
    # …
    Display Table Contents    T000
    ${rows}=    Read Full Displayed Grid
    ${row_count}=    Get Length    ${rows}
    Should Be True    ${row_count} >= ${MIN_EXPECTED_CLIENTS}
    ...    msg=T000 should list at least ${MIN_EXPECTED_CLIENTS} clients (000 and 001), got ${row_count}
    ${blank_rows}=    Count Blank Grid Rows    ${rows}
    Should Be Equal As Integers    ${blank_rows}    0
    ...    msg=T000 read is incomplete: ${blank_rows} fully blank row(s) in a grid announcing ${row_count} row(s)
    FOR    ${row}    IN    @{rows}
        Should Not Be Empty    ${row}[MANDT]
        ...    msg=A T000 row is missing its MANDT value: ${row}
    END
Perception

Voir l'écran comme un agent IA : capture annotée et carte numérotée

Pour un agent IA comme pour un humain qui débogue, SapEccLibrary rend l'écran lisible : Get Annotated Screenshot numérote chaque cible actionnable sur l'image, Get Screen Map en donne la liste avec le libellé humain, l'identifiant et la valeur. L'agent lit un numéro, la bibliothèque agit sur un identifiant vérifié. Ces références servent au pilotage interactif, jamais dans une suite livrée.

Preuve : Suite de capture de cette page : 4/4 sur A4H le 05/10/2026.

A4H, release 754Capture réelle
SAP GUI : écran de sélection SE16 de SNWD_PD, chaque champ encadré et numéroté
Capture annotée de l'écran de sélection SE16 de la table SNWD_PD : 86 cibles numérotées, prise par Get Annotated Screenshot.A4H, release 754 · Capture du 5 octobre 2026Écran d'un logiciel SAP : le logo SAP et les marques SAP sont la propriété de SAP SE.
comms/captures-site/capture_site_gui.robot
Ecran De Selection SE16 Annote
    # …
    ${shot}=    Get Annotated Screenshot
    Ecrire Image    ${shot}[image]    gui-se16-annotated
    ${carte}=    Get Screen Map
    Create File    ${OUTPUT DIR}/gui-se16-annotated-legend.txt    ${carte}
Contrôles SAP GUI

Table controls, arbres, listes déroulantes : par titre, par texte, par clé

Un table control de dynpro se lit par titre de colonne (Read Table Control), ses cases à cocher rendues en drapeau ABAP ; un arbre SAP se lit nœud par nœud (Read Tree Nodes) et se sélectionne par son texte ; une liste déroulante se pilote par sa clé technique (Select Combo Box Entry By Key), donc indépendamment de la langue.

Preuve : Campagne SE11 croisée avec le dictionnaire : 7/7 le 05/09/2026. Registre de capacités SAP GUI : 21/21 sur la release 754 le 07/09/2026, 10/10 sur la release 758 le 08/09/2026.

A4H, release 754Capture réelle
SAP GUI : onglet Fields de la transaction SE11 sur la table SNWD_PD, table control de 26 champs
Table control de SE11, onglet « Fields » de la table SNWD_PD : 26 champs lus par titre de colonne.A4H, release 754 · Capture du 5 octobre 2026Écran d'un logiciel SAP : le logo SAP et les marques SAP sont la propriété de SAP SE.
resources/page_objects/se11_dictionary_display.resource
Read Se11 Table Fields
    # …
    Click Element    ${SE11_TAB_FIELDS}
    Wait Until Busy Done
    ${rows}=    Read Table Control    ${SE11_TABLE_FIELDS_TC}
    RETURN    ${rows}
resources/page_objects/sapgui_capability_probes.resource
Lire Les Noeuds Du Menu Utilisateur
    # …
    ${noeuds}=    Read Tree Nodes    ${SM_USER_MENU_TREE}
    RETURN    ${noeuds}
# …
Selectionner Le Dossier Plain ABAP Par Texte
    # …
    ${cle}=    Select Tree Node By Text    ${SM_USER_MENU_TREE}    ${SM_TREE_PLAIN_ABAP_TEXT}    exact=True
    RETURN    ${cle}
# …
Choisir L Evenement Par Cle
    # …
    Select Combo Box Entry By Key    ${SM37_EVENT_COMBO}    ${cle}
Preuve de cible

Prouver à quel système on parle, avant toute écriture

Deux systèmes peuvent annoncer le même identifiant et le même nom d'hôte : c'est le cas des deux conteneurs du laboratoire. Get System Identity lit « System: Status », le kernel et les composants installés, et System Identity Should Be refuse la cible si la release, le kernel ou le mandant ne sont pas ceux attendus. Lu le 5 octobre 2026 : A4H, mandant 001, release 754, kernel 777.

Preuve : Garde de cible des campagnes d'écriture (Business Partner, jobs, IDoc), vue refuser en visant l'autre conteneur du laboratoire.

resources/page_objects/bp_business_partner.resource
La Cible SAP GUI Devrait Etre
    # …
    [Arguments]    ${release}    ${kernel}    ${client}
    ${identite}=    Get System Identity
    System Identity Should Be    identity=${identite}    basis_release=${release}
    ...    kernel_release=${kernel}    client=${client}
    RETURN    ${identite}
Canal 2 · SapFioriLibrary · 98 keywords

SAP Fiori, SAPUI5 et SAP GUI for HTML : des contrôles, pas des identifiants DOM

SapFioriLibrary s'appuie sur la bibliothèque Browser (Playwright). Elle désigne les contrôles UI5 par leur type, leurs propriétés ou leur chemin de liaison, jamais par les identifiants DOM générés, et attend le repos réel de la page (Wait For Ui5 Idle) au lieu d'une pause fixe. Cinq moteurs de résolution couvrent aussi le WebGUI, les UI5 Web Components et les zones non SAP d'une page hybride.

Launchpad

Le launchpad SAP Fiori : ouvrir par intent, lire les tuiles

Une application s'ouvre par son intent sémantique (Open Fiori App), la voie stable qui survit au thème, à la langue et aux réorganisations du catalogue. Le compteur d'une tuile dynamique est attendu jusqu'à ce qu'il soit réellement chargé (Wait For Flp Tile Counter), puis lu comme un nombre : la tuile se déclare chargée alors qu'elle affiche encore une valeur d'attente.

Preuve : Campagnes du launchpad servi par un serveur ABAP : 20/20 sur SAPUI5 1.71 et 22/22 sur SAPUI5 1.120, le 25/08/2026. Capture de la tuile : voir le test hybride ci-dessus.

resources/page_objects/abap_flp.resource
Ouvrir L Application Par Intent
    # …
    [Arguments]    ${intent}
    Open Fiori App    ${intent}
    Attendre Le Fragment    ${intent}
    Wait For Ui5 Idle    settle=${ABAP_FLP_SETTLE}    timeout=${ABAP_FLP_IDLE_TIMEOUT}
    Wait Until Keyword Succeeds    60s    2s    L Application Ouverte Est    ${intent}
resources/page_objects/tuile_cds_approbation.resource
Lire Le Compteur Charge De La Tuile
    # …
    [Arguments]    ${intent}    ${budget}=${BUDGET_COMPTEUR_TUILE}
    ${tuile}=    Wait For Flp Tile Counter    ${intent}    timeout=${budget}
    RETURN    ${tuile}
RAP

L'aperçu Fiori Elements d'un service RAP, sans application déployée

Le serveur ABAP génère à la volée l'interface Fiori Elements de tout service RAP publié. Build Fe Preview Path construit son adresse, dont un paramètre est obscurci, et le navigateur s'authentifie par en-tête HTTP. Les dates se saisissent au format calculé par le type de la liaison (Fill Ui5 Date), puis sont relues dans le modèle.

Preuve : Suite « voyage RAP créé par Fiori Elements, brouillon puis actif, refus et abandon » : 7/7 sur la release 758 le 24/09/2026.

ABAP Platform 2023, release 758Capture réelle
Aperçu SAP Fiori Elements du service RAP de voyages sur ABAP Platform 2023, 4 136 voyages
Aperçu Fiori Elements du service RAP de référence des voyages : 4 136 voyages listés.ABAP Platform 2023, release 758 · Capture du 5 octobre 2026Écran d'une application SAPUI5 : le logo SAP et les marques SAP sont la propriété de SAP SE.
resources/page_objects/rap_travel_fe_preview.resource
Ouvrir Le Navigateur Des Voyages
    # …
    New Browser    ${BROWSER}    headless=${RAP_TRAVEL_HEADLESS}
    New Context    ignoreHTTPSErrors=${True}    locale=${RAP_TRAVEL_LOCALE}
    ...    viewport=${RAP_TRAVEL_VIEWPORT}
    ...    httpCredentials={'username': '$RAP_TRAVEL_USER', 'password': '$RAP_TRAVEL_UI_PASSWORD'}
    Set Browser Timeout    ${RAP_TRAVEL_TIMEOUT}
    New Page    about:blank
    ${chemin}=    Build Fe Preview Path    ${RAP_TRAVEL_BINDING}    ${RAP_TRAVEL_SERVICE}
    ...    ${RAP_TRAVEL_ROOT_SET}    client=${RAP_TRAVEL_CLIENT}
    Set Suite Variable    ${RAP_TRAVEL_PREVIEW_URL}    ${RAP_TRAVEL_UI_URL}${chemin}
SAP GUI for HTML

Le WebGUI : la grille lue par les identifiants de ses cellules

Une grille du WebGUI n'envoie qu'une page de lignes, renumérotées à partir de 1 : un relevé tronqué est propre et indiscernable d'un relevé complet. Read Webgui Grid lit chaque cellule par son SID (le même espace d'identifiants que le scripting de bureau) et le total que la grille publie ; la même garde de complétude que sur les autres canaux refuse un relevé amputé.

Preuve : Suite « extraction d'une grille WebGUI vers cinq formats de fichier » : 8/8 sur A4H le 15/09/2026.

A4H, release 754Capture réelle
SAP GUI for HTML : Data Browser SE16 sur la table SCARR, 18 lignes
SE16 dans SAP GUI for HTML sur la table SCARR : 18 lignes lues pour 18 déclarées.A4H, release 754 · Capture du 5 octobre 2026Écran d'un logiciel SAP : le logo SAP et les marques SAP sont la propriété de SAP SE.
resources/fiori_keywords.resource
Extract Displayed WebGui Grid
    # …
    [Arguments]    ${sid}=${EMPTY}    ${key}=${EMPTY}    ${source}=${EMPTY}
    ${grille}=    Read Webgui Grid    ${sid}
    ${nom}=    Set Variable If    '${source}' == '${EMPTY}'
    ...    grille WebGUI ${grille}[container]    ${source}
    ${extrait}=    Build Table Extract    ${grille}[rows]    columns=${grille}[columns]
    ...    headers=${grille}[headers]    declared_rows=${grille}[declared_rows]
    ...    source=${nom}    key=${key}
tests/robot/ui/fiori/extraction_grille_webgui.robot
    ${partiel}=    Extract Displayed WebGui Grid    key=${KEY_COLUMN}
    ...    source=${EXTRACTED_TABLE} plafonnée à ${OVERFLOW_HITS}
    Should Be True    ${partiel}[declared_rows] > ${partiel}[row_count]
    ...    msg=Le cas dangereux n'a PAS été provoqué : la grille a rendu ses ${partiel}[row_count] lignes en entier, donc cette contre-épreuve ne prouve rien. Monter ${OVERFLOW_HITS}.
    Should Be Equal As Integers    ${partiel}[blank_rows]    ${0}
    ...    msg=Ces lignes devraient être PROPRES : c'est ce qui rend le cas indétectable autrement que par le total déclaré.
    Should Be Equal    ${partiel}[complete]    ${False}
    ${erreur}=    Run Keyword And Expect Error    RELEVÉ REFUSÉ*
    ...    Table Extract Should Be Complete    ${partiel}
SAP BTP · SAP Build Work Zone

SAP BTP : un launchpad cloud derrière son fournisseur d'identité

Un site SAP Build Work Zone sur SAP BTP, protégé par un tenant SAP Cloud Identity Services. SapFioriLibrary s'y connecte par le fournisseur d'identité, perçoit un shell dont la barre est bâtie en UI5 Web Components et entre dans l'application ouverte dans son iframe ; SapApiLibrary ouvre le canal API du même site en OAuth2.

Portée de frame

Le shell et l'application, deux mondes étanches

Depuis le shell, les contrôles de l'application sont invisibles, et réciproquement. Le test le prouve avec un témoin : le champ de recherche du shell, résolu une fois sur le shell et zéro fois depuis l'application. Sans cette étanchéité, un test cliquerait au mauvais endroit en restant vert.

Preuve : Même campagne, scénario de la portée de frame : 9/9 le 24/08/2026.

tests/robot/ui/fiori/exploration_launchpad_workzone.robot
La portee de frame est etanche entre le shell et l application
    # …
    ${apps}=    Inventorier Les Applications Du Catalogue
    Ouvrir L Application Par Intent    ${apps}[0][intent]
    Entrer Dans L Application
    ${dans_l_app}=    Compter Le Champ De Recherche Du Shell
    ${composition_app}=    Lire La Composition De La Portee Courante
    Sortir De L Application
    ${sur_le_shell}=    Compter Le Champ De Recherche Du Shell
    ${composition_shell}=    Lire La Composition De La Portee Courante
    Should Be Equal As Integers    ${dans_l_app}    0
    ...    msg=Le champ de recherche du shell est résolu depuis l'application : la portée fuit.
    Should Be Equal As Integers    ${sur_le_shell}    1
    ...    msg=Le champ de recherche du shell n'est plus résolu après le retour au shell.
Canal API

Le canal API du site, en OAuth2 par une clé de service

Une clé de service du sous-compte ouvre le canal API en OAuth2 (client credentials) avec Open Api Session. La reconnaissance classe chaque refus par sa structure (Classify Http Response : route inconnue du routeur de la plateforme, route absente d'une application, autorisation refusée, page de connexion servie en 200). Elle a établi, au lieu de le supposer, qu'une clé de service ouvre le backend d'un service et pas l'interface d'administration du site.

Preuve : Campagne « reconnaissance du canal API d'une cible BTP » : 9/9 le 26/08/2026.

Captures et campagnes d'août 2026 : le 5 octobre 2026, le compte d'essai SAP BTP du laboratoire ne répondait plus (« The URL does not reference a valid account »), elles n'ont donc pas été rejouées pour cette page.

Canal 3 · SapApiLibrary · 149 keywords

OData v2/v4 et RFC/BAPI : tester sans écran

SapApiLibrary parle HTTP avec la bibliothèque standard de Python (protocole CSRF de SAP compris) et le RFC par pyrfc, optionnel. C'est le canal qui prépare les données, vérifie un résultat sans passer par l'écran et lit la configuration d'un système ABAP.

OData

Le contrat d'un service OData, ses libellés, ses lots

Le $metadata d'un service se lit avec ses libellés humains (Get Odata Metadata, Find Odata Property By Label) : le contrat est vérifié à la source. Plusieurs lectures partent dans un seul aller-retour $batch (Post Odata Batch), chaque opération jugée séparément. Les mêmes keywords valent pour une Gateway OData v2, un service CAP en OData v4 et le bac à sable de SAP Business Accelerator Hub.

Preuve : Suite du canal API : 8/8 sur la Gateway d'A4H et 4/4 en OData v4 le 19/08/2026, 5/5 sur le bac à sable SAP Business Accelerator Hub le 23/08/2026.

resources/api_keywords.resource
Business Service Should Expose Entity Set
    # …
    [Arguments]    ${service}    ${entity_set}    ${alias}=${API_ALIAS}
    ${meta}=    Get Odata Metadata    ${service}    alias=${alias}
    Dictionary Should Contain Key    ${meta}[entity_sets]    ${entity_set}
    ...    msg=Le service ${service} ne publie pas l'ensemble d'entités ${entity_set}.
    RETURN    ${meta}[entity_sets][${entity_set}]
tests/robot/api/canal_api_odata.robot
Plusieurs Lectures Tiennent En Un Seul Aller Retour
    # …
    ${reponses}=    Read Business Entities In One Roundtrip    ${EPM_SHOP_SERVICE}
    ...    Products?$top=2    Products/$count    alias=a4h
    Length Should Be    ${reponses}    2
    FOR    ${reponse}    IN    @{reponses}
        Should Be True    ${reponse}[status] < 300    msg=Opération du lot en erreur : ${reponse}
    END
Écriture réversible

Créer, modifier, relire, supprimer : la disparition prouvée sur l'entité

La fabrique de données de test crée une entité, la modifie, la relit, puis la retire. Le nettoyage se prouve sur l'entité elle-même et non sur un compte revenu à sa valeur de départ : sur un service avec brouillons, un brouillon n'entre pas dans le $count, donc un compte inchangé ne prouve rien.

Preuve : Même suite, voie OData v4 : 4/4 le 19/08/2026.

tests/robot/api/canal_api_odata.robot
Les Donnees De Test Creees Sont Retirees A La Fin
    # …
    ${entite}=    Create Test Entity    ${TRAVEL_ENTITIES}    ${{ {'Description': 'Recette du canal API'} }}
    ...    alias=cap    track=${False}
    ${chemin}=    Build Draft Entity Path    ${TRAVEL_ENTITIES}    ${TRAVEL_KEY_FIELD}
    ...    ${entite}[${TRAVEL_KEY_FIELD}]
    Track Created Entity    ${chemin}    alias=cap
    Patch Odata    ${chemin}    ${{ {'Description': 'Recette du canal API, modifiee'} }}    alias=cap
    ${relue}=    Get Odata    ${chemin}    alias=cap
    Should Be Equal    ${relue}[Description]    Recette du canal API, modifiee
    ...    msg=La modification n'est pas relue : l'écriture n'est pas confirmée.
    Remove Created Test Data    alias=cap
    ${apres}=    Run Keyword And Ignore Error    Get Odata    ${chemin}    alias=cap
    Should Be Equal    ${apres}[0]    FAIL
    ...    msg=L'entité de test répond encore après le nettoyage : donnée laissée derrière.
RFC et BAPI

Tables, BAPI jugées par type, refus nommés par code

Read Rfc Table lit une table projetée, filtrée et bornée. Call Bapi juge une BAPI sur le type de ses messages (E, A ou X font échouer), jamais sur leur texte traduit, et un refus attendu s'asserte par son code technique (Rfc Should Fail With Code). Les jobs de fond s'attendent par sondage borné du journal des jobs (Wait For Background Job).

Preuve : Suite du canal RFC et BAPI : 16/16 sur la release 754 le 27/08/2026, 10/10 sur la release 758 le 28/08/2026.

resources/rfc_keywords.resource
Read User Details
    # …
    [Arguments]    ${user}    ${alias}=${RFC_ALIAS}
    ${detail}=    Call Bapi    ${BAPI_USER_DETAIL}    alias=${alias}    USERNAME=${user}
    RETURN    ${detail}
tests/robot/api/canal_rfc_a4h.robot
Les Refus Se Classent Par Code Jamais Par Texte
    # …
    Reading A Missing Table Should Be Refused With Code    ${CODE_TABLE_ABSENTE}
    Reading A Missing Field Should Be Refused With Code    ${CODE_TABLE_SANS_DONNEES}
    Calling A Missing Function Module Should Be Refused With Code    ${CODE_MODULE_ABSENT}
    Calling With An Unknown Parameter Should Be Refused With Code    ${CODE_PARAMETRE_INVALIDE}
resources/rfc_keywords.resource
Read Business Records
    # …
    [Arguments]    ${table}    ${fields}    ${filter}=${EMPTY}    ${limit}=0
    ...    ${alias}=${RFC_ALIAS}
    ${lignes}=    Read Rfc Table    ${table}    ${fields}    alias=${alias}
    ...    options=${filter}    rowcount=${limit}
    RETURN    ${lignes}
Sécurité ABAP

La configuration de sécurité d'un système ABAP, lue par RFC

Les paramètres de profil se jugent sur leur code de retour avant leur valeur (Read Profile Parameters), parce qu'un paramètre inconnu rend une chaîne vide indiscernable d'un paramètre vide. Profile Parameters Should Be Defined garde la tête d'une campagne, et Security Posture Should Not Have Drifted compare la posture à une référence committée. La surface d'attaque se lit aussi : comptes réellement utilisables, services web servis, couverture du journal d'audit. Lecture seule de bout en bout.

Preuve : Campagnes de configuration de sécurité : 15/15 sur la release 754 et 16/16 sur la release 758 le 29/08/2026 ; surface d'attaque : 12/12 sur la release 758 le 14/09/2026.

Autour des bibliothèques

Enregistreurs, agents IA et documentation des keywords

Les bibliothèques se suffisent à elles-mêmes. Autour d'elles, l'atelier SAPFX ajoute de quoi écrire des tests plus vite et les maintenir. Ses dix outils, en option et sur devis, sont présentés un par un sur la page Outils de l'atelier.

Agents IA et MCP

Des agents qui explorent, écrivent et réparent, sous contrôle

Cinq agents (planifier, générer, réparer, vérifier, documenter selon l'ISTQB) pilotent SAP à travers le serveur MCP rf-mcp et les plugins SAPFX, dans Claude Code ou GitHub Copilot. Ils appliquent la méthode Spec-Test-Driven Development : la spécification est confrontée au système réel, chaque étape d'un test est exécutée avant d'être écrite, et la réparation ne touche jamais aux assertions. Les agents et la surcouche MCP font partie de l'atelier SAPFX, pas du paquet PyPI, qui porte les bibliothèques.

Preuve : Le film « Comment des agents IA pilotent SAP » (8 min 20) les montre à l'œuvre.

Documentation

La référence de chaque keyword, avec ses arguments

Chaque keyword des trois bibliothèques est documenté, avec ses arguments et ses exemples, dans des pages de référence en anglais : SapEccLibrary, SapFioriLibrary et SapApiLibrary, en version 0.8.5.

Preuve : Aussi publiées sur GitHub Pages, avec le dépôt public des bibliothèques.

Comment cette page est faite

Des captures réelles, du code réel, des limites dites

Des captures réelles

Les écrans SAP ont été photographiés par SAPFX lui-même (keywords de capture des bibliothèques, puis bibliothèque Browser) sur les systèmes du laboratoire, le 5 octobre 2026, et le 29 août 2026 pour SAP BTP, sans retouche hormis le recadrage (et le logo SAP, masqué sur les preuves de la cartographie) ; chaque légende porte sa date. Ils montrent des données de démonstration livrées par SAP, aucune donnée client. Le logo SAP et les marques SAP visibles sur ces écrans sont la propriété de SAP SE.

Du code réel

Chaque extrait est copié, ligne pour ligne, d'une suite validée en direct dont le nom, le résultat et la date figurent sous l'extrait ; « # … » marque une coupe. Un contrôle automatique, exécuté avant chaque mise en ligne, refuse un keyword qui n'existe pas en version 0.8.5 et une ligne absente de sa suite d'origine.

Des limites dites

Ces tests tournent sur des systèmes SAP de développement en conteneurs, jamais sur un système client, et ne mesurent aucun gain de productivité. Les noms des keywords métier (en français dans ces suites) appartiennent à la couche de resources du laboratoire : ce sont des exemples à adapter à chaque installation, pas un vocabulaire universel.

Votre cas

Un parcours SAP à automatiser, sur deux ou trois canaux ?

Décrivez-nous le flux : nous vous disons ce que SAPFX en couvre aujourd'hui et ce qu'il faudrait construire. Prestations sur devis.

  • Un échange technique, pas commercial
  • Vos données restent en France
Guide de démarrage

ou directement par courriel :