{"id":16706,"date":"2026-08-13T12:34:55","date_gmt":"2026-08-13T10:34:55","guid":{"rendered":"https:\/\/ttc-testing.io\/?page_id=16706"},"modified":"2026-08-13T12:34:55","modified_gmt":"2026-08-13T10:34:55","slug":"developpement-outils-automatisation-sur-mesure","status":"publish","type":"page","link":"https:\/\/ttc-testing.io\/en\/developpement-outils-automatisation-sur-mesure\/","title":{"rendered":"Development of custom test automation tools"},"content":{"rendered":"\n<style>\n  :root{--logo-blanc:url(https:\/\/ttc-testing.io\/wp-content\/uploads\/2024\/08\/Logo-TTC-1.png);--logo-couleur:url(https:\/\/ttc-testing.io\/wp-content\/uploads\/2024\/08\/Logo-TTC-1.png)}\n  :root{\n    \/* Palette officielle \u2014 Charte TTC Testing *\/\n    --nuit:#0F1E3C; --lime:#C6E643; --bleu:#1A67B8; --cyan:#00B8D4;\n    --gris-clair:#F5F7FA; --gris-2:#6B7280; --blanc:#FFFFFF;\n    --encre:#0F1E3C; --glace:var(--lime); --turq:var(--bleu); --creme:var(--gris-clair);\n    --bord:#DDE3EC; --ambre:#F5A623;\n    \/* Charte : Helvetica\/Arial, ou \u00e9quivalent sans-serif moderne (Inter, Manrope) *\/\n    --geo:\"Helvetica Neue\",Helvetica,Arial,Inter,Manrope,system-ui,sans-serif;\n    --texte:\"Helvetica Neue\",Helvetica,Arial,Inter,Manrope,system-ui,sans-serif;\n    --f:var(--creme); --t:var(--encre); --s:#5D7186; --b:var(--bord);\n  }\n  @media (prefers-color-scheme: dark){:root{--f:#0A1638;--t:#EEF5FA;--s:#93A9C4;--b:#243863;--blanc:#132449}}\n  :root[data-theme=\"dark\"]{--f:#0A1638;--t:#EEF5FA;--s:#93A9C4;--b:#243863;--blanc:#132449}\n  :root[data-theme=\"light\"]{--f:var(--creme);--t:var(--encre);--s:#5D7186;--b:var(--bord);--blanc:#FFFFFF}\n\n  *{box-sizing:border-box}\n  html{scroll-behavior:smooth}\n  body{margin:0;background:var(--nuit);color:#fff;font:17px\/1.62 var(--texte);\n       -webkit-font-smoothing:antialiased;transition:color .3s ease}\n  \/* La nappe de fond : une seule surface, dont la teinte se d\u00e9place au fil de la page.\n     Le contenu flotte au-dessus, les sections n'ont plus de fond propre. *\/\n  #nappe{position:fixed;inset:0;z-index:-1;background:var(--nuit);\n         transition:background-color .16s linear}\n  \/* Deux r\u00e9gimes de lisibilit\u00e9 : texte clair sur fonds profonds, texte navy sur fonds vifs. *\/\n  :root.sur-sombre body{color:#fff}\n  :root.sur-clair body{color:var(--nuit)}\n  :root.sur-sombre .s{color:rgba(255,255,255,.72)}\n  :root.sur-clair .s{color:rgba(15,30,60,.72)}\n  :root.sur-clair .oeil{color:var(--nuit)}\n  :root.sur-sombre .oeil{color:var(--lime)}\n  :root.sur-clair .kpi .n{color:var(--nuit)}\n  :root.sur-sombre .kpi .n{color:var(--lime)}\n  :root.sur-clair .kpi{border-top-color:var(--nuit)}\n  :root.sur-clair .exp article{border-top-color:rgba(15,30,60,.25)}\n  :root.sur-clair .pas{border-top-color:rgba(15,30,60,.25)}\n  :root.sur-clair .pas>div::before{background:var(--nuit)}\n  :root.sur-clair .pas .num{color:var(--nuit)}\n  :root.sur-clair .logos span{opacity:.85}\n  :root.sur-clair .cas{border-left-color:var(--nuit)}\n  .w{max-width:1180px;margin:0 auto;padding:0 clamp(20px,5vw,52px)}\n  section{padding:clamp(58px,9vw,120px) 0;position:relative;overflow:hidden}\n  .nuit,.clair{background:transparent}\n\n  \/* ---- titres g\u00e9om\u00e9triques, en capitales, tr\u00e8s grands : la signature TTC ---- *\/\n  .geo{font-family:var(--geo);font-weight:800;letter-spacing:-.02em}\n  h1{font-family:var(--geo);font-weight:800;font-size:clamp(40px,8.2vw,104px);line-height:.96;\n     margin:0 0 28px;text-transform:uppercase;text-wrap:balance;max-width:12ch}\n  \/* \u00ab G\u00e9n\u00e9ralistes \u00bb est un mot long : le titre du hero se r\u00e8gle sur lui pour ne jamais\n     empi\u00e9ter sur le logo, quelle que soit la largeur de la fen\u00eatre. *\/\n  .hero h1{font-size:clamp(28px,4.1vw,54px);max-width:26ch;text-wrap:nowrap}\n  @media(max-width:760px){.hero h1{text-wrap:balance;max-width:14ch}}\n  @media(min-width:1051px){.hero .sous-hero{max-width:46ch}}\n  .sous-hero{font-family:var(--texte);font-size:clamp(17px,1.85vw,21px);line-height:1.55;\n             max-width:50ch;margin:0 0 34px}\n  h2{font-family:var(--geo);font-weight:800;font-size:clamp(27px,4.4vw,50px);line-height:1.04;\n     margin:0 0 18px;text-transform:uppercase;text-wrap:balance;max-width:19ch}\n  h3{font-family:var(--geo);font-weight:700;margin:0;font-size:21px;letter-spacing:0}\n  .oeil{font:700 15px\/1.3 var(--texte);letter-spacing:.24em;text-transform:uppercase;margin:0 0 24px;\n        display:flex;align-items:center;gap:16px;color:var(--bleu)}\n  .oeil::before{content:\"\";flex:0 0 auto;width:40px;height:2px;background:currentColor}\n  .nuit .oeil{color:var(--lime)}\n  .lede{font-size:clamp(17px,1.9vw,20px);line-height:1.55;max-width:58ch;margin:0}\n  .intro{max-width:62ch;margin:0 0 44px}\n\n  .pill{display:inline-block;font:600 14px\/1 var(--texte);letter-spacing:.1em;text-transform:uppercase;\n        padding:16px 32px;border-radius:999px;border:1.6px solid currentColor;text-decoration:none;\n        color:inherit;transition:background .2s,color .2s}\n  .nuit .pill:hover{background:var(--lime);color:var(--nuit);border-color:var(--lime)}\n  .barre .pill:hover{background:var(--lime);color:var(--nuit);border-color:var(--lime)}\n  .clair .pill:hover{background:var(--nuit);color:#fff;border-color:var(--nuit)}\n\n  \/* ---- navigation ---- *\/\n  .barre{position:sticky;top:0;z-index:30;background:color-mix(in srgb,var(--nuit) 92%,transparent);\n         color:#fff;backdrop-filter:blur(16px);transition:box-shadow .25s ease}\n  .barre.pose{box-shadow:0 6px 26px -18px rgba(16,31,77,.55)}\n  .marque-ttc{display:block;height:42px;width:86px;background:var(--logo-blanc) left center\/contain no-repeat}\n  \/* La barre porte huit liens, la bascule de langue et l'appel \u00e0 l'action : tout doit tenir\n     sur une ligne jusqu'aux \u00e9crans de portable, sinon le bouton de contact d\u00e9croche. *\/\n  nav{display:flex;flex-wrap:wrap;align-items:center;gap:10px 20px;padding:18px 0}\n\n  nav ul{display:flex;flex-wrap:wrap;gap:6px clamp(12px,1.5vw,22px);list-style:none;margin:0;padding:0;\n         font:600 12.5px\/1 var(--texte);letter-spacing:.1em;text-transform:uppercase}\n  nav a{color:inherit;text-decoration:none;opacity:.78}\n  nav a:hover{opacity:1;color:var(--lime)}\n  nav .pill{margin-left:auto;padding:11px 20px;font-size:12px;letter-spacing:.08em;white-space:nowrap}\n  @media(max-width:1180px){nav .pill{padding:10px 17px}}\n\n  \/* ---- bascule de langue ---- *\/\n  .langue{display:flex;align-items:center;gap:2px;border:1.4px solid rgba(255,255,255,.34);\n          border-radius:999px;padding:3px;flex:0 0 auto}\n  .langue button{background:none;border:0;cursor:pointer;color:inherit;opacity:.62;\n                 font:700 11.5px\/1 var(--texte);letter-spacing:.1em;padding:7px 11px;\n                 border-radius:999px;transition:background .18s,color .18s,opacity .18s}\n  .langue button:hover{opacity:1}\n  .langue button[aria-pressed=\"true\"]{background:var(--lime);color:var(--nuit);opacity:1}\n\n  \/* ---- hero ---- *\/\n  .hero{padding-top:clamp(54px,7vw,92px);padding-bottom:clamp(54px,7vw,96px);\n         min-height:min(78vh,760px);display:flex;align-items:center}\n  .hero::after{content:\"\";position:absolute;right:-16%;top:-30%;width:64vw;height:64vw;max-width:860px;\n    max-height:860px;border-radius:50%;border:1px solid rgba(198,230,67,.25);\n    background:radial-gradient(circle at 38% 36%,rgba(198,230,67,.07),transparent 62%);\n    pointer-events:none}\n  .hero .w{position:relative;z-index:1;width:100%}\n  \/* Le logo tient le c\u00f4t\u00e9 droit du hero, en grand, d\u00e9bordant l\u00e9g\u00e8rement du cadre. *\/\n  .logo-fixe{position:fixed;right:2%;top:50%;transform:translate(0,-50%) scale(1);z-index:0;\n             width:min(42vw,482px);height:min(30vh,244px);\n             pointer-events:none;filter:drop-shadow(0 24px 60px rgba(0,0,0,.3));\n             transition:right 1s cubic-bezier(.62,.02,.2,1), transform 1s cubic-bezier(.62,.02,.2,1)}\n  \/* \u00c0 l'ouverture, le logo se pose au centre de l'\u00e9cran nu, puis rejoint son bord. *\/\n  :root.logo-centre .logo-fixe{right:50%;transform:translate(50%,-50%) scale(1.14)}\n  .logo-fixe span{position:absolute;inset:0;display:block;\n                  background-repeat:no-repeat;background-position:right center;background-size:contain;\n                  transition:opacity .45s ease}\n  \/* Sur les fonds profonds c'est la version blanche qui tient, sans d\u00e9pendre du script. *\/\n  .logo-fixe .l-blanc{opacity:.88;background-image:var(--logo-blanc)}\n  .logo-fixe .l-couleur{opacity:0;background-image:var(--logo-couleur)}\n  \/* Deux versions du logo : la blanche sur les fonds profonds, la couleur sur les fonds vifs. *\/\n  :root.sur-sombre .logo-fixe .l-blanc{opacity:.88}\n  :root.sur-sombre .logo-fixe .l-couleur{opacity:0}\n  :root.sur-clair  .logo-fixe .l-blanc{opacity:0}\n  :root.sur-clair  .logo-fixe .l-couleur{opacity:.9}\n  \/* Sur les fen\u00eatres \u00e9troites le logo r\u00e9tr\u00e9cit, mais il ne s'efface jamais. *\/\n  @media(max-width:1150px){.logo-fixe{width:min(38vw,336px);height:min(21vh,170px)}}\n  @media(max-width:700px){.logo-fixe{display:none}}\n  \/* Le contenu passe au-dessus du filigrane. *\/\n  section{z-index:1}\n  \/* R\u00e9seau de connexions : le trait se dessine au chargement, puis reste. *\/\n  .reseau{position:absolute;right:-6%;top:50%;transform:translateY(-50%);\n          width:min(56%,660px);height:auto;pointer-events:none;opacity:.28}\n  @media(max-width:900px){.reseau{display:none}}\n  .reseau path{stroke-dasharray:600;stroke-dashoffset:600;animation:tirer 2.6s ease-out forwards}\n  .reseau path:nth-child(2){animation-delay:.25s}\n  .reseau path:nth-child(3){animation-delay:.5s}\n  .reseau path:nth-child(4){animation-delay:.75s}\n  .reseau circle{opacity:0;animation:poindre .5s ease-out forwards;animation-delay:1.6s}\n  @keyframes tirer{to{stroke-dashoffset:0}}\n  @keyframes poindre{to{opacity:1}}\n  @media (prefers-reduced-motion:reduce){\n    .reseau path{stroke-dashoffset:0;animation:none}\n    .reseau circle{opacity:1;animation:none}\n  }\n\n  \/* ---- bandes de chiffres ---- *\/\n  .bande{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:40px 26px}\n  \/* Le tiers droit reste au logo : les chiffres s'arr\u00eatent avant lui. *\/\n  @media(min-width:1051px){.bande{max-width:70%}}\n  .kpi .n{font-family:var(--geo);font-weight:900;font-size:clamp(44px,6.6vw,76px);line-height:.9;\n          letter-spacing:-.035em;font-variant-numeric:tabular-nums;color:var(--lime)}\n  .clair .kpi .n{color:var(--nuit)}\n  .kpi .u{font-size:.46em;font-weight:600}\n  .kpi{padding-top:16px;border-top:3px solid var(--lime)}\n  .kpi .l{margin-top:12px;font:700 12px\/1.4 var(--texte);letter-spacing:.16em;text-transform:uppercase}\n\n  \/* ---- grilles ---- *\/\n  .g3{display:grid;grid-template-columns:repeat(auto-fit,minmax(290px,1fr));gap:22px;align-items:stretch}\n  .g2{display:grid;grid-template-columns:repeat(auto-fit,minmax(330px,1fr));gap:22px;align-items:stretch}\n  .exp{display:grid;grid-template-columns:repeat(auto-fit,minmax(248px,1fr));gap:24px 34px}\n  .exp article{padding-top:22px;border-top:1px solid rgba(255,255,255,.22)}\n  .exp p{margin:8px 0 0;font-size:15.5px;line-height:1.55}\n  .exp .out{margin-top:10px;font-size:13px;letter-spacing:.04em;opacity:.75}\n\n  .carte{background:rgba(255,255,255,.94);border:1px solid rgba(15,30,60,.10);color:var(--nuit);border-radius:10px;overflow:hidden;\n         display:flex;flex-direction:column;transition:transform .22s ease,box-shadow .22s ease}\n  .carte:hover{transform:translateY(-5px);box-shadow:0 18px 44px -22px rgba(16,31,77,.45)}\n  .carte .filet{height:5px;background:var(--lime)}\n  .carte .in{padding:28px 26px;display:flex;flex-direction:column;gap:14px;flex:1}\n  .carte .quoi{font:700 11.5px\/1 var(--texte);letter-spacing:.22em;text-transform:uppercase;color:var(--bleu)}\n  .carte p{margin:0;font-size:15.5px;line-height:1.55}\n  .carte .s,.cite .s{color:rgba(15,30,60,.72)!important}\n  .carte ul{margin:0;padding-left:19px;font-size:14.5px;display:grid;gap:6px}\n  .carte .pied{margin-top:auto;padding-top:16px;border-top:1px solid rgba(15,30,60,.12);font-size:13.5px}\n  .carte .pied b{color:var(--bleu)}\n\n  \/* ---- d\u00e9marche ---- *\/\n  .pas{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:0;\n       border-top:1px solid rgba(255,255,255,.25)}\n  .pas>div{padding:24px 24px 0 0;position:relative}\n  .pas>div::before{content:\"\";position:absolute;top:-1px;left:0;width:70%;height:3px;\n                   background:var(--lime)}\n  .pas .num{font-family:var(--geo);font-weight:800;font-size:34px;line-height:1;color:var(--lime)}\n  .pas b{display:block;font-family:var(--geo);font-weight:800;font-size:19px;margin:8px 0 6px}\n  .pas span{font-size:14.5px;line-height:1.5}\n\n  \/* ---- citations ---- *\/\n  .cites{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:22px}\n  .cite{background:rgba(255,255,255,.94);border:1px solid rgba(15,30,60,.10);border-radius:10px;\n        padding:26px;color:var(--nuit)}\n  .cite p{margin:0 0 16px;font-size:16.5px;line-height:1.5;font-style:italic}\n  .cite footer{font-size:14px;line-height:1.45}\n  .cite b{font-family:var(--geo);font-weight:700;font-style:normal}\n\n  .logos{display:flex;flex-wrap:wrap;gap:11px}\n  .logos span{border:1px solid currentColor;border-radius:999px;padding:10px 20px;font:600 14px\/1 var(--texte);\n              letter-spacing:.06em;opacity:.72;transition:opacity .18s}\n  .logos span:hover{opacity:1}\n  .logos span.nous{border-width:2px;opacity:1;font-weight:700}\n\n  \/* ---- actualit\u00e9s ---- *\/\n  .actus{display:grid;grid-template-columns:repeat(auto-fit,minmax(290px,1fr));gap:22px}\n  .actu{background:rgba(255,255,255,.94);border:1px solid rgba(15,30,60,.10);border-radius:10px;\n        color:var(--nuit);display:flex;flex-direction:column;overflow:hidden;\n        transition:transform .22s ease,box-shadow .22s ease}\n  .actu:hover{transform:translateY(-5px);box-shadow:0 18px 44px -22px rgba(16,31,77,.45)}\n  .actu .filet{height:5px;background:var(--lime)}\n  .actu .in{padding:26px;display:flex;flex-direction:column;gap:12px;flex:1}\n  .actu .quand{font:700 11px\/1 var(--texte);letter-spacing:.19em;text-transform:uppercase;\n               color:var(--bleu)}\n  .actu h3{font-size:19px;line-height:1.25}\n  .actu h3 a{color:inherit;text-decoration:none}\n  .actu h3 a:hover{text-decoration:underline}\n  .actu p{margin:0;font-size:15px;line-height:1.55;color:rgba(15,30,60,.72)}\n  .actu .lire{margin-top:auto;padding-top:14px;font:700 12px\/1 var(--texte);letter-spacing:.13em;\n              text-transform:uppercase;color:var(--bleu);text-decoration:none}\n  .actu .lire:hover{text-decoration:underline}\n\n  \/* ---- profils disponibles : acc\u00e8s r\u00e9serv\u00e9 ---- *\/\n  .cadenas{display:flex;flex-wrap:wrap;gap:12px;align-items:center;margin-top:30px}\n  .cadenas[hidden]{display:none}\n  .cadenas input{font:500 16px\/1 var(--texte);padding:15px 20px;border-radius:999px;\n                 border:1px solid rgba(255,255,255,.4);background:rgba(255,255,255,.07);\n                 color:inherit;min-width:230px}\n  .cadenas input::placeholder{color:currentColor;opacity:.5}\n  .cadenas input:focus{outline:2px solid var(--lime);outline-offset:2px}\n  .cadenas button{cursor:pointer}\n  .refus{flex-basis:100%;margin:2px 0 0;font-size:14.5px;color:#FFB4A8}\n  .indice{margin-top:14px;font-size:13.5px;opacity:.6}\n  .profils{display:grid;grid-template-columns:repeat(auto-fit,minmax(345px,1fr));gap:22px;margin-top:34px}\n  .profil .tete{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px}\n  .profil .code{font-family:var(--geo);font-weight:800;font-size:22px;letter-spacing:-.01em;\n                white-space:nowrap}\n  .profil .filiere{font:700 10.5px\/1 var(--texte);letter-spacing:.15em;text-transform:uppercase;\n                   background:var(--lime);color:var(--nuit);border-radius:999px;padding:7px 12px}\n  .profil .secteurs{margin-top:-6px;font-size:14.5px;color:rgba(15,30,60,.62)}\n  .profil .prix{display:flex;align-items:baseline;flex-wrap:wrap;gap:9px;padding:14px 0;\n                border-top:1px solid rgba(15,30,60,.12);border-bottom:1px solid rgba(15,30,60,.12)}\n  .profil .tjm{font-family:var(--geo);font-weight:800;font-size:27px;line-height:1;color:var(--nuit)}\n  .profil .tjm-l{font:700 10px\/1 var(--texte);letter-spacing:.14em;text-transform:uppercase;\n                 color:rgba(15,30,60,.45)}\n  .profil .dispo{margin-left:auto;font:700 10.5px\/1 var(--texte);letter-spacing:.13em;\n                 text-transform:uppercase;color:rgba(15,30,60,.5);white-space:nowrap;\n                 border:1px solid currentColor;border-radius:999px;padding:7px 11px}\n  .profil .dispo.libre{color:#1F7A4A;background:rgba(31,122,74,.09);border-color:rgba(31,122,74,.35)}\n  .profil .tags{display:flex;flex-wrap:wrap;gap:6px;margin:0}\n  .profil .tags span{border:1px solid rgba(15,30,60,.22);border-radius:999px;padding:6px 11px;\n                     font:600 12px\/1 var(--texte)}\n  .profil .tags span.cle{background:var(--lime);border-color:transparent;font-weight:700}\n  .profil .band{display:grid;grid-template-columns:1fr 1fr;gap:14px 16px;margin:0}\n  .profil .band div{border-left:2px solid var(--lime);padding-left:11px}\n  .profil .band dt{font:700 9.5px\/1 var(--texte);letter-spacing:.14em;text-transform:uppercase;\n                   color:rgba(15,30,60,.45)}\n  .profil .band dd{margin:5px 0 0;font-size:13.5px;font-weight:600;line-height:1.35}\n  .profil .cv{display:inline-flex;align-items:center;gap:9px;align-self:flex-start;margin-top:auto;\n              border:1.5px solid var(--nuit);border-radius:999px;padding:11px 20px;\n              font:700 12.5px\/1 var(--texte);letter-spacing:.09em;text-transform:uppercase;\n              color:var(--nuit);text-decoration:none;transition:background .18s,color .18s}\n  .profil .cv:hover{background:var(--nuit);color:#fff}\n  .sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}\n\n  .faq details{border-bottom:1px solid rgba(128,128,128,.35);padding:18px 0}\n  .faq summary{cursor:pointer;font-family:var(--geo);font-weight:700;font-size:18px;list-style:none}\n  .faq summary::-webkit-details-marker{display:none}\n  .faq summary::after{content:\"+\";float:right;font-weight:400;opacity:.5}\n  .faq details[open] summary::after{content:\"\u2013\"}\n  .faq p{margin:12px 0 0;font-size:15.5px;max-width:64ch}\n\n  .liste-defis{margin:0;padding:0;list-style:none;display:grid;gap:14px}\n  .liste-defis li{padding-left:20px;position:relative;font-size:16px;line-height:1.5}\n  .liste-defis li::before{content:\"\";position:absolute;left:0;top:.62em;width:9px;height:2px;\n                          background:var(--lime)}\n  .cas{border-left:4px solid var(--lime);padding:0 0 0 26px}\n  .cas ul{margin:18px 0 0;padding-left:19px;display:grid;gap:9px;font-size:16px}\n  .cas b{font-weight:650}\n\n  .final{text-align:center}\n  .final h2{margin-inline:auto;max-width:20ch}\n  .final .lede{margin-inline:auto}\n  .final .pill{margin-top:30px}\n  .coord{display:flex;flex-wrap:wrap;justify-content:center;gap:14px 38px;margin:38px 0 0;\n         padding:0;list-style:none;font-size:15.5px}\n  .coord a{color:inherit;text-decoration:none;border-bottom:1px solid currentColor}\n  .coord a:hover{color:var(--lime);border-color:var(--lime)}\n  footer.pied{padding:44px 0;font-size:14px}\n  footer.pied a{color:inherit;text-decoration:none;border-bottom:1px solid rgba(255,255,255,.3)}\n\n  \/* ---------- Rideau d'ouverture ----------\n     Une nu\u00e9e de T et de C arrive de tous les c\u00f4t\u00e9s, se colle en mosa\u00efque, et c'est cette mosa\u00efque\n     qui donne la couleur du fond. Pos\u00e9 par le script UNIQUEMENT : sans JavaScript il n'existe pas.\n     Deux minuteurs le retirent quoi qu'il arrive. *\/\n  #rideau{position:fixed;inset:0;z-index:100;overflow:hidden;background:var(--nuit);\n          transition:opacity .5s ease,visibility .5s}\n  #rideau.parti{opacity:0;visibility:hidden}\n  #rideau .tuile{position:absolute;display:grid;place-items:center;line-height:1;\n                 font-family:\"Arial Rounded MT Bold\",\"Varela Round\",Nunito,Quicksand,Verdana,\n                             \"Century Gothic\",\"Avenir Next\",Futura,sans-serif;\n                 font-weight:900;will-change:transform,opacity;\n                 animation:atterrir 1.05s cubic-bezier(.24,.86,.3,1) backwards}\n  @keyframes atterrir{\n    from{transform:translate(var(--dx),var(--dy)) rotate(var(--r)) scale(.2);opacity:0}\n    55%{opacity:1}\n    78%{transform:translate(calc(var(--dx) * .06),calc(var(--dy) * .06))\n                  rotate(calc(var(--r) * .08)) scale(1.06)}\n    to{transform:translate(0,0) rotate(0) scale(1);opacity:1}\n  }\n  \/* En vol, chaque tuile est un disque : la mosa\u00efque se lit comme une suspension de mol\u00e9cules.\n     \u00c0 la fusion, les disques se carrent et se soudent en un champ continu. *\/\n  #rideau .tuile{border-radius:50%;\n                 transition:background-color .9s ease,color .7s ease,border-radius .75s ease}\n  #rideau.fusion .tuile{background-color:var(--nuit)!important;color:transparent!important;\n                        border-radius:0!important}\n  \/* Chacune d\u00e9rive \u00e0 son propre rythme \u2014 le mouvement ne se synchronise jamais. *\/\n  #rideau .tuile i{display:block;font-style:normal;\n                   animation:flotter var(--d) ease-in-out var(--dd) infinite alternate}\n  @keyframes flotter{\n    from{transform:translate(0,0) rotate(0)}\n    to{transform:translate(var(--fx),var(--fy)) rotate(var(--fr))}\n  }\n  #rideau.fusion .tuile i{animation-play-state:paused}\n  \/* Le texte du hero n'entre qu'une fois le d\u00e9cor d\u00e9couvert. Classe pos\u00e9e par le script\n     uniquement : sans JavaScript, le texte est visible d'embl\u00e9e. *\/\n  :root.avant-texte .hero .w > *,\n  :root.avant-texte .hero + section .w > *{opacity:0}\n  .hero .w > *, .hero + section .w > *{transition:opacity .7s ease}\n  #rideau .passer{position:absolute;bottom:24px;right:24px;z-index:3;background:none;border:0;\n                  cursor:pointer;color:rgba(255,255,255,.6);font:600 11px\/1 var(--texte);\n                  letter-spacing:.2em;text-transform:uppercase;padding:10px}\n  #rideau .passer:hover{color:#fff}\n  \/* Mouvement r\u00e9duit : personne ne perd l'ouverture, elle se joue en fondu.\n     Ni lettres qui volent, ni glissement \u2014 l'\u00e9cran bleu, le logo, puis le texte. *\/\n  @media (prefers-reduced-motion:reduce){\n    #rideau .tuile, #rideau .tuile i{animation:none!important}\n    #rideau .passer{display:none}\n    .logo-fixe{transition:opacity .6s ease}\n    :root.logo-centre .logo-fixe{right:2%;transform:translate(0,-50%) scale(1);opacity:0}\n  }\n  :focus-visible{outline:2px solid var(--lime);outline-offset:3px}\n\n  \/* R\u00e9v\u00e9lation au d\u00e9filement. Le contenu est visible PAR D\u00c9FAUT : la classe \u00ab js \u00bb n'est pos\u00e9e\n     que si le script tourne, donc rien ne peut rester invisible. *\/\n  .js .rev{opacity:0;transform:translateY(26px);\n           transition:opacity .72s cubic-bezier(.22,1,.36,1),transform .72s cubic-bezier(.22,1,.36,1)}\n  .js .rev.vu{opacity:1;transform:none}\n  @media (prefers-reduced-motion:reduce){.js .rev{opacity:1;transform:none;transition:none}}\n\n  \/* barre de progression de lecture *\/\n  .bandeau-lime{height:6px;background:var(--lime)}\n  .jauge{position:fixed;top:6px;left:0;height:3px;width:0;z-index:40;\n         background:var(--lime);transition:width .12s linear}\n\n\/* ---- Acc\u00e8s au catalogue ---- *\/\n.coffre[hidden]{display:none}\n.coffre{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:22px 0 6px}\n.coffre input{font:inherit;font-size:15px;padding:11px 15px;min-width:230px;color:inherit;\n  background:rgba(255,255,255,.09);border:1px solid rgba(255,255,255,.28);border-radius:999px}\n.coffre input::placeholder{color:rgba(255,255,255,.55)}\n.coffre input:focus-visible{outline:2px solid var(--lime);outline-offset:2px}\n#coffre-msg[hidden]{display:none}\n#coffre-msg{color:#FFC9C0}\n.fiches{display:grid;gap:16px;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));margin-top:20px}\n.fiche{background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.14);border-radius:14px;padding:18px}\n.fiche .ref{font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;opacity:.72}\n.fiche h3{margin:6px 0 2px;font-size:18px;line-height:1.25}\n.fiche .spec{margin:0 0 12px;font-size:13.5px;opacity:.78}\n.fiche dl{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;margin:0 0 12px;font-size:13.5px}\n.fiche dl div{display:contents}\n.fiche dt{opacity:.62}\n.fiche dd{margin:0}\n.fiche .resume{font-size:13.5px;line-height:1.5;opacity:.9;margin:0 0 12px}\n.fiche .tags{list-style:none;display:flex;flex-wrap:wrap;gap:6px;padding:0;margin:0}\n.fiche .tags li{font-size:11.5px;padding:3px 9px;border-radius:999px;background:rgba(255,255,255,.1)}\n\n\n\/* ---- Repli quand la page est affich\u00e9e dans un cadre : mailto\/tel y sont bloqu\u00e9s ---- *\/\n.copie-faite{display:inline-block;margin-left:12px;font-size:13px;color:var(--lime);\n  opacity:0;transition:opacity .25s}\n.copie-faite.vue{opacity:1}\n\n\/* ---- H\u00e9breu : la page se retourne ---- *\/\n:root[dir=\"rtl\"] body{font-family:\"Varela Round\",\"Arial Hebrew\",\"Noto Sans Hebrew\",var(--texte)}\n:root[dir=\"rtl\"] .cas{border-left:0;border-right:4px solid var(--lime)}\n:root[dir=\"rtl\"].sur-clair .cas{border-left-color:transparent;border-right-color:var(--nuit)}\n:root[dir=\"rtl\"] nav .pill{margin-left:0;margin-right:auto}\n:root[dir=\"rtl\"] .carte ul,:root[dir=\"rtl\"] .cas ul{padding-left:0;padding-right:19px}\n:root[dir=\"rtl\"] .liste-defis li{padding-left:0;padding-right:20px}\n:root[dir=\"rtl\"] .liste-defis li::before,:root[dir=\"rtl\"] .pas>div::before{left:auto;right:0}\n:root[dir=\"rtl\"] .faq summary::after{float:left}\n:root[dir=\"rtl\"] #rideau .passer{right:auto;left:24px}\n:root[dir=\"rtl\"] .hero::after{right:auto;left:-16%}\n:root[dir=\"rtl\"] .reseau{right:auto;left:-6%}\n\/* les nombres et les noms de produits restent lus de gauche \u00e0 droite *\/\n:root[dir=\"rtl\"] .chiffre,:root[dir=\"rtl\"] .ref,:root[dir=\"rtl\"] .tags li{direction:ltr;unicode-bidi:isolate}\n\/* le contenu des fiches est r\u00e9dig\u00e9 en fran\u00e7ais : il se lit de gauche \u00e0 droite m\u00eame en h\u00e9breu.\n   Seuls les libell\u00e9s, eux traduits, restent align\u00e9s \u00e0 droite. *\/\n:root[dir=\"rtl\"] .fiche{direction:ltr;text-align:left}\n:root[dir=\"rtl\"] .fiche dt{direction:rtl;text-align:right}\n:root[dir=\"rtl\"] .fiche dl{grid-template-columns:1fr auto}\n:root[dir=\"rtl\"] .fiche dd{grid-column:1;text-align:left}\n:root[dir=\"rtl\"] .fiche dt{grid-column:2}\n\n\n.barre{position:fixed;top:0;left:0;right:0;z-index:30}\n.barre.pose{background:rgba(15,30,60,.86);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}\n:root.sur-clair .barre.pose{background:rgba(255,255,255,.88)}\n\/* la cible d'une ancre ne doit pas se loger sous la barre *\/\nsection[id]{scroll-margin-top:82px}\n\/* WordPress connect\u00e9 : sa propre barre occupe d\u00e9j\u00e0 les 32 premiers pixels *\/\nbody.admin-bar .barre{top:32px}\n\n\/* Le logo blanc est le logo couleur, d\u00e9color\u00e9 puis invers\u00e9 : une seule image pour les deux. *\/\n.marque-ttc,.logo-fixe .l-blanc{filter:brightness(0) invert(1)}\n\n\/* L'h\u00e9breu retir\u00e9 du s\u00e9lecteur flottant de TranslatePress. C'est un rideau, pas une\n   suppression : \/he\/ reste servi et d\u00e9clar\u00e9 en hreflang. Le retrait r\u00e9el est un r\u00e9glage du\n   plugin (R\u00e9glages \u2192 TranslatePress \u2192 langues), \u00e0 faire une fois. *\/\n\/* TranslatePress pose display:flex!important depuis une feuille charg\u00e9e apr\u00e8s la n\u00f4tre :\n   \u00e0 sp\u00e9cificit\u00e9 \u00e9gale, c'est la sienne qui gagne. On monte donc la sp\u00e9cificit\u00e9. *\/\nhtml body div#trp-floater-ls a[href*=\"\/he\/\"],\nhtml body div#trp-floater-ls div[id*=\"language-list\"] a[href*=\"\/he\/\"],\nhtml body .trp-language-switcher a[href*=\"\/he\/\"]{display:none!important}\n\n\/* les deux langues sont des liens, pas des boutons : chacune a sa propre adresse *\/\n.langue a{color:inherit;text-decoration:none;opacity:.62;font:700 11.5px\/1 var(--texte);\n  letter-spacing:.1em;padding:7px 11px;border-radius:999px;transition:background .18s,color .18s,opacity .18s}\n.langue a:hover{opacity:1}\n.langue a[aria-current=\"true\"]{background:var(--lime);color:var(--nuit);opacity:1}\n\nh1,h2,h3,h4,h5,h6{color:inherit!important;font-family:var(--geo)!important}\n<\/style><style>.hero h1{text-wrap:balance;white-space:normal;max-width:19ch;font-size:clamp(30px,4.6vw,60px)}.hero .sous-hero{max-width:52ch}<\/style><div id=\"nappe\"><\/div>\n<div class=\"logo-fixe\" aria-hidden=\"true\"><span class=\"l-blanc\"><\/span><span class=\"l-couleur\"><\/span><\/div>\n<div class=\"bandeau-lime\"><\/div>\n<div class=\"jauge\" id=\"jauge\"><\/div>\n<div class=\"barre\"><div class=\"w\"><nav>\n  <a href=\"\/\" aria-label=\"TTC \u2014 accueil\"><span class=\"marque-ttc\" role=\"img\" aria-label=\"TTC \u2014 Testing Training Consulting\"><\/span><\/a>\n  <ul>\n    <li><a href=\"\/#expertises\" data-en=\"Expertise\">Expertises<\/a><\/li>\n    <li><a href=\"\/#demarche\" data-en=\"Approach\">D\u00e9marche<\/a><\/li>\n    <li><a href=\"\/#solutions\" data-en=\"Solutions\">Solutions<\/a><\/li>\n    <li><a href=\"\/#secteurs\" data-en=\"Industries\">Secteurs<\/a><\/li>\n    <li><a href=\"https:\/\/claude.ai\/code\/artifact\/515cc0ec-2f96-4dd0-8fe3-014011357c9a\" target=\"_blank\" rel=\"noopener\" data-en=\"Consultants\">Profils<\/a><\/li>\n    <li><a href=\"\/#actualites\" data-en=\"News\">Actualit\u00e9s<\/a><\/li>\n    <li><a href=\"\/#faq\" data-en=\"FAQ\">FAQ<\/a><\/li>\n  <\/ul>\n  <div class=\"langue\" role=\"group\" aria-label=\"Langue \/ Language\">\n    <a href=\"\/\" hreflang=\"fr\" lang=\"fr\">FR<\/a>\n    <a href=\"\/en\/\" hreflang=\"en\" lang=\"en\">EN<\/a>\n  <\/div>\n  <a class=\"pill\" href=\"#contact\" data-en=\"Contact us\">Nous contacter<\/a>\n<\/nav><\/div><\/div><section class=\"nuit hero\" data-fond=\"#0F1E3C\" data-sur=\"sombre\"><div class=\"w\"><p class=\"oeil\">Automatisation sur mesure<\/p><h1>Quand aucun outil du march\u00e9 ne sait tester votre syst\u00e8me.<\/h1><p class=\"sous-hero s\">Selenium, Cypress et Playwright couvrent le web. Au-del\u00e0 \u2014 <b>client lourd, PowerBuilder, borne de commande, \u00e9cran de production, terminal m\u00e9tier<\/b> \u2014 la plupart des prestataires s&#8217;arr\u00eatent l\u00e0 o\u00f9 l&#8217;outil s&#8217;arr\u00eate. Nous \u00e9crivons ce qui manque.<\/p><a class=\"pill\" href=\"\/#contact\">Prendre rendez-vous<\/a><\/div><\/section><section class=\"nuit\" id=\"mur\" data-fond=\"#16304F\" data-sur=\"sombre\"><div class=\"w\"><p class=\"oeil\">Le mur<\/p><h2>\u00ab Ce n&#x27;est pas automatisable \u00bb veut souvent dire \u00ab pas avec l&#x27;outil que je connais \u00bb.<\/h2><p class=\"intro s lede\">C&#8217;est la phrase qu&#8217;entendent les DSI dont le c\u0153ur du m\u00e9tier ne tourne pas dans un navigateur.<\/p><p class=\"s lede\" style=\"max-width:62ch\">Une application <b>PowerBuilder<\/b> qui pilote la production depuis vingt ans. Un client lourd Windows dont les champs n&#8217;ont pas d&#8217;identifiant stable. Une borne dont le navigateur embarqu\u00e9 change de port \u00e0 chaque red\u00e9marrage. Un \u00e9cran de production qui n&#8217;expose aucune interface. Dans tous ces cas, l&#8217;outillage standard ne mord pas \u2014 et la recette reste manuelle, donc partielle, donc lente.<\/p><p class=\"s lede\" style=\"max-width:62ch\">Ces syst\u00e8mes ont un point commun : ce sont rarement les moins critiques. Ce sont les applications qu&#8217;on n&#8217;a pas remplac\u00e9es parce qu&#8217;elles marchent, et qu&#8217;on teste \u00e0 la main parce qu&#8217;on croit ne pas avoir le choix.<\/p><\/div><\/section><section class=\"nuit\" id=\"faire\" data-fond=\"#122744\" data-sur=\"sombre\"><div class=\"w\"><p class=\"oeil\">Ce que nous faisons<\/p><h2>Nous d\u00e9veloppons l&#x27;automate qui n&#x27;existe pas.<\/h2><div class=\"g3\"><article class=\"carte\"><div class=\"filet\"><\/div><div class=\"in\"><h3>Clients lourds et applications historiques<\/h3><p class=\"s\">PowerBuilder, Windows natif, applications m\u00e9tier sans interface de test.<\/p><ul class=\"s\"><li>Reconnaissance des \u00e9crans par l&#x27;accessibilit\u00e9 ou l&#x27;image<\/li><li>Ancrage r\u00e9sistant aux changements de version<\/li><li>Jeux de donn\u00e9es et remise \u00e0 l&#x27;\u00e9tat initial<\/li><\/ul><\/div><\/article><article class=\"carte\"><div class=\"filet\"><\/div><div class=\"in\"><h3>Bornes, kiosques et \u00e9crans<\/h3><p class=\"s\">Pilotage au niveau de ce qui s&#x27;affiche, sans rien installer sur la machine du client.<\/p><ul class=\"s\"><li>Aucun code d\u00e9ploy\u00e9 sur l&#x27;\u00e9quipement en exploitation<\/li><li>V\u00e9rification de la pr\u00e9sence, de l&#x27;absence et de l&#x27;ordre<\/li><li>Preuve visuelle \u00e0 chaque \u00e9tape<\/li><\/ul><\/div><\/article><article class=\"carte\"><div class=\"filet\"><\/div><div class=\"in\"><h3>Harnais autour des syst\u00e8mes ferm\u00e9s<\/h3><p class=\"s\">Quand l&#x27;interface manque, on va chercher la v\u00e9rit\u00e9 l\u00e0 o\u00f9 elle est : base, journal, interface applicative, flux d&#x27;impression.<\/p><ul class=\"s\"><li>Lecture des interfaces de configuration et de transaction<\/li><li>Comparaison automatique au document de r\u00e9f\u00e9rence<\/li><li>Rapport lisible par le m\u00e9tier, pas seulement par la technique<\/li><\/ul><\/div><\/article><\/div><\/div><\/section><section class=\"nuit\" id=\"preuve\" data-fond=\"#16304F\" data-sur=\"sombre\"><div class=\"w\"><p class=\"oeil\">La preuve<\/p><h2>Un outil \u00e9crit pour un client, aujourd&#x27;hui en production sur son parc.<\/h2><p class=\"intro s lede\">Nous ne vendons pas une intention : l&#8217;atelier a d\u00e9j\u00e0 produit un automate complet pour une enseigne de restauration rapide.<\/p><div class=\"bande\" style=\"margin-top:40px\"><div class=\"kpi\"><div class=\"n\">3<\/div><div class=\"l s\">modules : alimentation, audit, pilotage borne<\/div><\/div><div class=\"kpi\"><div class=\"n\">32<\/div><div class=\"l s\">sc\u00e9narios de test rejou\u00e9s sur commande<\/div><\/div><div class=\"kpi\"><div class=\"n\">0<\/div><div class=\"l s\">ligne de code d\u00e9ploy\u00e9e sur l&#x27;\u00e9quipement du client<\/div><\/div><\/div><p class=\"s lede\" style=\"max-width:62ch\">Il lit le brief de campagne, \u00e9crit la configuration dans le syst\u00e8me de caisse, la relit pour v\u00e9rifier qu&#8217;elle correspond, pilote la borne pour contr\u00f4ler ce que le client voit \u00e0 l&#8217;\u00e9cran, et compare les points de vente entre eux. <b>25 704 comparaisons en une minute<\/b>, l\u00e0 o\u00f9 le m\u00eame contr\u00f4le manuel demandait environ soixante-dix heures.<\/p><p class=\"s lede\" style=\"max-width:62ch\">Ce qu&#8217;il a trouv\u00e9 au premier passage : quatre articles d&#8217;une campagne en cours sans aucun prix dans un restaurant, donc invendables, la veille de leur mise en avant. Aucun outil du march\u00e9 ne savait faire \u00e7a \u2014 parce que personne ne l&#8217;avait \u00e9crit.<\/p><\/div><\/section><section class=\"nuit\" id=\"engagement\" data-fond=\"#122744\" data-sur=\"sombre\"><div class=\"w\"><p class=\"oeil\">Comment on s&#x27;engage<\/p><h2>Un outil qui vous appartient, pas une d\u00e9pendance de plus.<\/h2><div class=\"g3\"><article class=\"carte\"><div class=\"filet\"><\/div><div class=\"in\"><h3>Le code est \u00e0 vous<\/h3><p class=\"s\">Livr\u00e9 dans votre d\u00e9p\u00f4t, document\u00e9 en fran\u00e7ais, avec la proc\u00e9dure d&#x27;installation et de mise \u00e0 jour.<\/p><\/div><\/article><article class=\"carte\"><div class=\"filet\"><\/div><div class=\"in\"><h3>Une preuve avant l&#x27;engagement<\/h3><p class=\"s\">Deux \u00e0 trois semaines pour d\u00e9montrer que le pilotage tient sur votre syst\u00e8me. Si \u00e7a ne marche pas, on vous le dit et on s&#x27;arr\u00eate l\u00e0.<\/p><\/div><\/article><article class=\"carte\"><div class=\"filet\"><\/div><div class=\"in\"><h3>Pas d&#x27;installation en production<\/h3><p class=\"s\">Sur les \u00e9quipements exploit\u00e9s, aucun code source, aucun outil de d\u00e9veloppement : le binaire, ou rien du tout.<\/p><\/div><\/article><\/div><\/div><\/section><section class=\"nuit\" id=\"questions\" data-fond=\"#16304F\" data-sur=\"sombre\"><div class=\"w\"><p class=\"oeil\">Questions franches<\/p><h2>Ce qu&#x27;on nous demande<\/h2><details><summary>Pourquoi ne pas simplement acheter un outil du march\u00e9 ?<\/summary><p class=\"s\">Parce qu&#x27;il faut d&#x27;abord v\u00e9rifier qu&#x27;il couvre votre cas. Quand c&#x27;est le cas, nous le disons et nous l&#x27;utilisons \u2014 c&#x27;est moins cher pour vous et plus rapide pour nous. Le d\u00e9veloppement sur mesure ne se justifie que l\u00e0 o\u00f9 l&#x27;outillage standard ne mord pas : client lourd, \u00e9quipement embarqu\u00e9, protocole propri\u00e9taire, syst\u00e8me ferm\u00e9.<\/p><\/details><details><summary>Une application PowerBuilder, c&#x27;est vraiment automatisable ?<\/summary><p class=\"s\">Oui, \u00e0 condition d&#x27;accepter que l&#x27;ancrage ne se fasse pas comme sur le web. On s&#x27;appuie sur les couches d&#x27;accessibilit\u00e9 quand elles existent, sur la reconnaissance visuelle sinon, et on stabilise l&#x27;ensemble par des jeux de donn\u00e9es ma\u00eetris\u00e9s. Le premier travail est toujours un essai de faisabilit\u00e9 sur un parcours r\u00e9el, pas une promesse.<\/p><\/details><details><summary>Qui maintient l&#x27;outil ensuite ?<\/summary><p class=\"s\">Vous, si vous le souhaitez : le code vous appartient et il est document\u00e9 pour \u00e7a. Nous, si vous pr\u00e9f\u00e9rez, dans un cadre de maintenance. Ce que nous refusons, c&#x27;est la bo\u00eete noire que personne d&#x27;autre que nous ne peut ouvrir.<\/p><\/details><details><summary>Combien de temps pour un premier automate utile ?<\/summary><p class=\"s\">Deux \u00e0 trois semaines pour l&#x27;essai de faisabilit\u00e9, six \u00e0 dix semaines pour un premier p\u00e9rim\u00e8tre en service selon la complexit\u00e9 de l&#x27;interface et l&#x27;acc\u00e8s aux environnements. L&#x27;acc\u00e8s est presque toujours le vrai d\u00e9lai, pas le d\u00e9veloppement.<\/p><\/details><details><summary>Et si notre syst\u00e8me change dans un an ?<\/summary><p class=\"s\">C&#x27;est une question qu&#x27;il faut poser avant, pas apr\u00e8s. Si un remplacement est pr\u00e9vu \u00e0 court terme, nous orientons vers une v\u00e9rification par la donn\u00e9e plut\u00f4t que par l&#x27;\u00e9cran : elle survit au changement d&#x27;interface, l\u00e0 o\u00f9 une automatisation d&#x27;\u00e9cran serait \u00e0 refaire.<\/p><\/details><\/div><\/section><section class=\"nuit final\" id=\"contact\" data-fond=\"#0F1E3C\" data-sur=\"sombre\"><div class=\"w\">\n  <h2 data-en=\"Let us talk about what you cannot test yet.\">Parlons de ce que vous n&#8217;arrivez pas encore \u00e0 tester.<\/h2>\n  <p class=\"lede s\" data-en=\"An audit of your testing practice, with no commitment, to find out where you actually stand.\">Un audit de votre d\u00e9marche de test, sans engagement, pour savoir o\u00f9 vous en \u00eates\n    vraiment.<\/p>\n  <a class=\"pill\" href=\"https:\/\/outlook.office.com\/bookwithme\/user\/176ca71218304b0094d4799dc009d7dc@ttc-testing.io\/meetingtype\/hfvBA8ifokaSYaNxIMUbzg2?anonymous\" target=\"_blank\" rel=\"noopener\" data-en=\"Book a meeting\">Prendre rendez-vous<\/a>\n  <ul class=\"coord s\">\n    <li><a href=\"tel:+33173049197\">+33 1 73 04 91 97<\/a><\/li>\n    <li><a href=\"mailto:Contact@ttc-testing.com\">Contact@ttc-testing.com<\/a><\/li>\n    <li>195 avenue Charles de Gaulle, 92200 Neuilly-sur-Seine<\/li>\n  <\/ul>\n<\/div><\/section><script type=\"application\/ld+json\">{\"@context\": \"https:\/\/schema.org\", \"@type\": \"Service\", \"name\": \"D\u00e9veloppement d'outils d'automatisation de test sur mesure\", \"serviceType\": \"D\u00e9veloppement d'outils d'automatisation de test sur mesure\", \"provider\": {\"@id\": \"https:\/\/ttc-testing.io\/#organisation\"}, \"areaServed\": [{\"@type\": \"Country\", \"name\": \"France\"}], \"description\": \"Quand aucun outil du march\u00e9 ne sait piloter votre application \u2014 client lourd, PowerBuilder, borne, \u00e9cran industriel \u2014 nous \u00e9crivons l'automate qui manque.\", \"url\": \"https:\/\/ttc-testing.io\/developpement-outils-automatisation-sur-mesure\/\"}<\/script><script type=\"application\/ld+json\">{\"@context\": \"https:\/\/schema.org\", \"@type\": \"FAQPage\", \"mainEntity\": [{\"@type\": \"Question\", \"name\": \"Pourquoi ne pas simplement acheter un outil du march\u00e9 ?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"Parce qu'il faut d'abord v\u00e9rifier qu'il couvre votre cas. Quand c'est le cas, nous le disons et nous l'utilisons \u2014 c'est moins cher pour vous et plus rapide pour nous. Le d\u00e9veloppement sur mesure ne se justifie que l\u00e0 o\u00f9 l'outillage standard ne mord pas : client lourd, \u00e9quipement embarqu\u00e9, protocole propri\u00e9taire, syst\u00e8me ferm\u00e9.\"}}, {\"@type\": \"Question\", \"name\": \"Une application PowerBuilder, c'est vraiment automatisable ?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"Oui, \u00e0 condition d'accepter que l'ancrage ne se fasse pas comme sur le web. On s'appuie sur les couches d'accessibilit\u00e9 quand elles existent, sur la reconnaissance visuelle sinon, et on stabilise l'ensemble par des jeux de donn\u00e9es ma\u00eetris\u00e9s. Le premier travail est toujours un essai de faisabilit\u00e9 sur un parcours r\u00e9el, pas une promesse.\"}}, {\"@type\": \"Question\", \"name\": \"Qui maintient l'outil ensuite ?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"Vous, si vous le souhaitez : le code vous appartient et il est document\u00e9 pour \u00e7a. Nous, si vous pr\u00e9f\u00e9rez, dans un cadre de maintenance. Ce que nous refusons, c'est la bo\u00eete noire que personne d'autre que nous ne peut ouvrir.\"}}, {\"@type\": \"Question\", \"name\": \"Combien de temps pour un premier automate utile ?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"Deux \u00e0 trois semaines pour l'essai de faisabilit\u00e9, six \u00e0 dix semaines pour un premier p\u00e9rim\u00e8tre en service selon la complexit\u00e9 de l'interface et l'acc\u00e8s aux environnements. L'acc\u00e8s est presque toujours le vrai d\u00e9lai, pas le d\u00e9veloppement.\"}}, {\"@type\": \"Question\", \"name\": \"Et si notre syst\u00e8me change dans un an ?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"C'est une question qu'il faut poser avant, pas apr\u00e8s. Si un remplacement est pr\u00e9vu \u00e0 court terme, nous orientons vers une v\u00e9rification par la donn\u00e9e plut\u00f4t que par l'\u00e9cran : elle survit au changement d'interface, l\u00e0 o\u00f9 une automatisation d'\u00e9cran serait \u00e0 refaire.\"}}]}<\/script><script>\n\n  \/\/ ---- Bascule FR \/ EN ----\n  \/\/ Le fran\u00e7ais est dans le document, l'anglais dans data-en. On m\u00e9morise le fran\u00e7ais au premier\n  \/\/ passage, puis on \u00e9change le contenu des deux c\u00f4t\u00e9s autant de fois qu'on veut.\n  (() => {\n    const boutons = document.querySelectorAll('.langue button');\n    if (!boutons.length) return;\n\n    const poser = (lang) => {\n      for (const el of document.querySelectorAll('[data-en]')) {\n        if (el.dataset.fr === undefined) el.dataset.fr = el.innerHTML;\n        el.innerHTML = lang === 'en' ? el.dataset.en : (lang === 'he' && el.dataset.he) ? el.dataset.he : el.dataset.fr;\n      }\n      \/\/ Les libell\u00e9s qui ne sont pas du contenu : marque-page des champs de saisie.\n      for (const el of document.querySelectorAll('[data-ph-en]')) {\n        if (el.dataset.phFr === undefined) el.dataset.phFr = el.placeholder;\n        el.placeholder = lang === 'en' ? el.dataset.phEn : (lang === 'he' && el.dataset.phHe) ? el.dataset.phHe : el.dataset.phFr;\n      }\n      document.documentElement.lang = lang;\n      \/\/ l'h\u00e9breu s'\u00e9crit de droite \u00e0 gauche : tout le document se retourne\n      document.documentElement.setAttribute('dir', lang === 'he' ? 'rtl' : 'ltr');\n      for (const b of boutons) b.setAttribute('aria-pressed', String(b.dataset.lang === lang));\n      try { localStorage.setItem('ttc-langue', lang); } catch (e) { \/* navigation priv\u00e9e *\/ }\n    };\n\n    for (const b of boutons) b.addEventListener('click', () => poser(b.dataset.lang));\n\n    \/\/ Choix m\u00e9moris\u00e9 s'il existe, sinon la langue du navigateur, sinon le fran\u00e7ais.\n    let choix = null;\n    try { choix = localStorage.getItem('ttc-langue'); } catch (e) { \/* ignor\u00e9 *\/ }\n    if (choix !== 'fr' && choix !== 'en') {\n      choix = (navigator.language || 'fr').toLowerCase().startsWith('fr') ? 'fr' : 'en';\n    }\n    poser(choix);\n  })();\n\n  \/\/ ---- Rideau d'ouverture : une nu\u00e9e de T et de C forme le fond ----\n  (() => {\n    const sobre = matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n    const r = document.createElement('div');\n    r.id = 'rideau'; r.setAttribute('aria-hidden', 'true');\n    document.body.appendChild(r);\n    document.body.style.overflow = 'hidden';\n    document.documentElement.classList.add('avant-texte', 'logo-centre');\n\n    \/\/ Une mosa\u00efque qui couvre l'\u00e9cran : chaque tuile porte une lettre et sa couleur.\n    \/\/ C'est l'accumulation des tuiles qui \u00ab donne la couleur du fond \u00bb.\n    const COTE = Math.max(210, Math.round(Math.min(innerWidth, innerHeight) \/ 3.4));\n    const cols = Math.ceil(innerWidth \/ COTE), lignes = Math.ceil(innerHeight \/ COTE);\n    \/\/ Toutes les lettres partagent la m\u00eame teinte bleu-gris : aucune ne se distingue.\n    const NAVY = '#0F1E3C', TUILE = NAVY, LETTRE = 'rgba(255,255,255,.17)';\n    let i = 0;\n\n    for (let y = 0; sobre ? false : y < lignes; y++) {\n      for (let x = 0; x < cols; x++, i++) {\n        const t = document.createElement('span');\n        t.className = 'tuile';\n        t.innerHTML = '<i>' + ((x + y) % 3 === 0 ? 'C' : 'T') + '<\/i>';\n        t.style.cssText =\n          `left:${x * COTE}px;top:${y * COTE}px;width:${COTE + 1}px;height:${COTE + 1}px;` +\n          `background:${TUILE};color:${LETTRE};font-size:${Math.round(COTE * .78)}px;`;\n        \/\/ Chaque tuile vient de l'ext\u00e9rieur, dans une direction qui lui est propre.\n        const angle = Math.atan2(y - lignes \/ 2, x - cols \/ 2) + (Math.random() - .5);\n        const loin = Math.max(innerWidth, innerHeight) * (1.1 + Math.random() * .5);\n        t.style.setProperty('--dx', Math.round(Math.cos(angle) * loin) + 'px');\n        t.style.setProperty('--dy', Math.round(Math.sin(angle) * loin) + 'px');\n        t.style.setProperty('--r', Math.round((Math.random() - .5) * 300) + 'deg');\n        \/\/ D\u00e9rive propre \u00e0 la tuile : faible amplitude, p\u00e9riode irr\u00e9guli\u00e8re.\n        t.style.setProperty('--fx', ((Math.random() - .5) * 9).toFixed(1) + 'px');\n        t.style.setProperty('--fy', ((Math.random() - .5) * 9).toFixed(1) + 'px');\n        t.style.setProperty('--fr', ((Math.random() - .5) * 14).toFixed(1) + 'deg');\n        t.style.setProperty('--d', (2.2 + Math.random() * 2.4).toFixed(2) + 's');\n        t.style.setProperty('--dd', (Math.random() * 1.6).toFixed(2) + 's');\n        \/\/ Arriv\u00e9e du centre vers les bords : la mosa\u00efque se remplit en s'\u00e9talant.\n        const d = Math.hypot(x - cols \/ 2, y - lignes \/ 2) \/ Math.hypot(cols \/ 2, lignes \/ 2);\n        t.style.animationDelay = (d * 620 + Math.random() * 120) + 'ms';\n        r.appendChild(t);\n      }\n    }\n\n    \/\/ Une fois la mosa\u00efque compl\u00e8te, elle s'absorbe : plus de lettres, plus de tuiles, le fond.\n    if (!sobre) setTimeout(() => r.classList.add('fusion'), 1250);\n\n    const bouton = document.createElement('button');\n    bouton.type = 'button'; bouton.className = 'passer';\n    bouton.textContent = (document.documentElement.lang === 'en') ? 'Skip' : 'Passer';\n    r.appendChild(bouton);\n\n    let leve = false;\n    const lever = () => {\n      if (leve) return;\n      leve = true;\n      r.classList.add('parti');\n      document.body.style.overflow = '';\n      setTimeout(() => r.remove(), 600);\n      \/\/ Le logo tient le centre le temps que l'\u00e9cran se d\u00e9nude, puis glisse vers son bord.\n      setTimeout(() => document.documentElement.classList.remove('logo-centre'), sobre ? 0 : 650);\n      \/\/ Le logo arrive \u00e0 1650 ms ; on marque un temps d'arr\u00eat avant que le texte n'entre.\n      setTimeout(() => document.documentElement.classList.remove('avant-texte'), sobre ? 1100 : 2600);\n    };\n    bouton.addEventListener('click', lever);\n    addEventListener('keydown', (e) => { if (e.key === 'Escape') lever(); }, { once: true });\n    setTimeout(lever, sobre ? 550 : 1800);   \/\/ d\u00e9roul\u00e9 normal\n    setTimeout(lever, 5000);   \/\/ filet de s\u00e9curit\u00e9\n  })();\n\n  \/\/ Le contenu reste visible sans script : on ne passe en mode anim\u00e9 qu'une fois le script en vie.\n  document.documentElement.classList.add('js', 'sur-sombre');\n\n  const cibles = [];\n  for (const sec of document.querySelectorAll('section')) {\n    const enfants = sec.querySelectorAll(':scope > .w > *, :scope > .w > .g3 > *, :scope > .w > .g2 > *, :scope > .w > .bande > *, :scope > .w > .exp > *, :scope > .w > .cites > *, :scope > .w > .actus > *');\n    let i = 0;\n    for (const el of enfants) { el.classList.add('rev'); el.style.transitionDelay = (i++ % 6) * 70 + 'ms'; cibles.push(el); }\n  }\n\n  const obs = new IntersectionObserver((entrees) => {\n    for (const e of entrees) if (e.isIntersecting) { e.target.classList.add('vu'); obs.unobserve(e.target); }\n  }, { rootMargin: '0px 0px -8% 0px', threshold: 0.08 });\n  cibles.forEach((c) => obs.observe(c));\n\n  \/\/ Filet de s\u00e9curit\u00e9 : si l'observateur n'a rien d\u00e9clench\u00e9, on montre tout.\n  setTimeout(() => cibles.forEach((c) => c.classList.add('vu')), 2500);\n\n  \/\/ ---- Nappe de fond : la page change de couleur au fil du d\u00e9filement ----\n  const nappe = document.getElementById('nappe');\n  const zones = [...document.querySelectorAll('section[data-fond]')];\n  const hex = (h) => [1,3,5].map(i => parseInt(h.slice(i, i+2), 16));\n  const melange = (a, b, t) => 'rgb(' + a.map((v,i) => Math.round(v + (b[i]-v)*t)).join(',') + ')';\n\n  function teinter() {\n    if (!zones.length) return;\n    const milieu = scrollY + innerHeight * 0.42;\n    let i = 0;\n    for (let k = 0; k < zones.length; k++) if (zones[k].offsetTop <= milieu) i = k;\n    const a = zones[i], b = zones[Math.min(i+1, zones.length-1)];\n    const haut = a.offsetTop, bas = b === a ? haut + a.offsetHeight : b.offsetTop;\n    const t = bas > haut ? Math.min(1, Math.max(0, (milieu - haut) \/ (bas - haut))) : 0;\n    nappe.style.backgroundColor = melange(hex(a.dataset.fond), hex(b.dataset.fond), t);\n    \/\/ Le r\u00e9gime de lisibilit\u00e9 bascule \u00e0 mi-chemin, jamais au milieu d'un bloc de texte.\n    const sur = (t > .5 ? b : a).dataset.sur;\n    document.documentElement.classList.toggle('sur-clair', sur === 'clair');\n    document.documentElement.classList.toggle('sur-sombre', sur !== 'clair');\n  }\n  teinter();\n  addEventListener('resize', teinter, { passive: true });\n\n  const jauge = document.getElementById('jauge');\n  const barre = document.querySelector('.barre');\n  addEventListener('scroll', () => {\n    const h = document.documentElement;\n    jauge.style.width = (h.scrollTop \/ (h.scrollHeight - h.clientHeight) * 100) + '%';\n    barre.classList.toggle('pose', h.scrollTop > 8);\n    teinter();\n  }, { passive: true });\n\n  \/\/ ---- Acc\u00e8s au catalogue des consultants disponibles ----\n  \/\/ Le catalogue n'est pas dans la page : il y est chiffr\u00e9 (AES-GCM, cl\u00e9 d\u00e9riv\u00e9e du mot de passe\n  \/\/ par PBKDF2). Un mauvais mot de passe ne donne pas un refus poli, il donne un \u00e9chec de\n  \/\/ d\u00e9chiffrement \u2014 il n'y a donc rien \u00e0 lire sans lui, code source compris.\n  (() => {\n    const f = document.getElementById('coffre');\n    if (!f) return;\n    const champ = document.getElementById('coffre-mdp'),\n          msg   = document.getElementById('coffre-msg'),\n          cat   = document.getElementById('catalogue'),\n          bouton = f.querySelector('button');\n    const b64 = document.getElementById('coffre-donnees').textContent.trim();\n    const octets = Uint8Array.from(atob(b64), c => c.charCodeAt(0));\n    const sel = octets.slice(0, 16), iv = octets.slice(16, 28), blob = octets.slice(28);\n\n    const ouvrir = async (mdp) => {\n      const base = await crypto.subtle.importKey('raw', new TextEncoder().encode(mdp),\n                                                 'PBKDF2', false, ['deriveKey']);\n      const cle = await crypto.subtle.deriveKey(\n        { name: 'PBKDF2', salt: sel, iterations: 1200000, hash: 'SHA-256' },\n        base, { name: 'AES-GCM', length: 256 }, false, ['decrypt']);\n      const clair = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, cle, blob);\n      cat.querySelector('.fiches').innerHTML = new TextDecoder().decode(clair);\n      cat.hidden = false; f.hidden = true; msg.hidden = true;\n      \/\/ les fiches viennent d'arriver : on leur applique la langue courante\n      const actif = document.querySelector('.langue button[aria-pressed=\"true\"]');\n      if (actif) actif.click();\n      try { sessionStorage.setItem('ttc-catalogue', mdp); } catch (e) { \/* navigation priv\u00e9e *\/ }\n    };\n\n    f.addEventListener('submit', async (e) => {\n      e.preventDefault();\n      const mdp = champ.value.trim();\n      if (!mdp) return;\n      bouton.disabled = true;\n      const avant = bouton.textContent;\n      bouton.textContent = '\u2026';\n      try { await ouvrir(mdp); }\n      catch (err) { msg.hidden = false; champ.select(); }\n      finally { bouton.disabled = false; bouton.textContent = avant; }\n    });\n\n    \/\/ rouvert sans ressaisie le temps de la visite\n    try {\n      const garde = sessionStorage.getItem('ttc-catalogue');\n      if (garde) ouvrir(garde).catch(() => sessionStorage.removeItem('ttc-catalogue'));\n    } catch (e) { \/* ignor\u00e9 *\/ }\n  })();\n\n  \/\/ ---- \u00ab Prendre rendez-vous \u00bb, le t\u00e9l\u00e9phone, l'adresse ----\n  \/\/ Affich\u00e9e dans un cadre (l'aper\u00e7u d'un artefact), la page n'a pas le droit d'ouvrir un\n  \/\/ mailto: ni un tel: \u2014 le clic ne fait alors rien du tout. Dans ce cas seulement, on copie\n  \/\/ l'adresse et on le dit. Sur le site, le comportement normal est conserv\u00e9.\n  (() => {\n    const dansUnCadre = window.self !== window.top;\n    if (!dansUnCadre) return;\n    for (const a of document.querySelectorAll('a[href^=\"mailto:\"], a[href^=\"tel:\"]')) {\n      a.addEventListener('click', async (e) => {\n        e.preventDefault();\n        const valeur = decodeURIComponent(a.getAttribute('href').replace(\/^(mailto|tel):\/, '').split('?')[0]);\n        let ok = true;\n        try { await navigator.clipboard.writeText(valeur); } catch (err) { ok = false; }\n        let mot = a.parentNode.querySelector('.copie-faite');\n        if (!mot) {\n          mot = document.createElement('span');\n          mot.className = 'copie-faite';\n          mot.setAttribute('role', 'status');\n          a.after(mot);\n        }\n        const en = document.documentElement.lang === 'en';\n        mot.textContent = ok ? (en ? 'Copied \u2014 ' : 'Copi\u00e9 \u2014 ') + valeur : valeur;\n        requestAnimationFrame(() => mot.classList.add('vue'));\n        setTimeout(() => mot.classList.remove('vue'), 6000);\n      });\n    }\n  })();\n\n  \/\/ ---- Les ancres du menu ----\n  \/\/ Le saut natif vers une ancre ne fonctionne plus une fois la page dans WordPress : le th\u00e8me\n  \/\/ pose overflow sur html et body, ce qui d\u00e9place l'\u00e9l\u00e9ment d\u00e9filant. On fait donc le\n  \/\/ d\u00e9filement nous-m\u00eames, en retranchant la hauteur de la barre pour ne pas cacher le titre\n  \/\/ vis\u00e9 dessous.\n  document.addEventListener('click', (e) => {\n    const a = e.target.closest('a[href^=\"#\"]');\n    if (!a) return;\n    const id = a.getAttribute('href').slice(1);\n    const cible = id ? document.getElementById(id) : null;\n    if (id && !cible) return;              \/\/ ancre inconnue : on laisse le navigateur d\u00e9cider\n    e.preventDefault();\n    const barre = document.querySelector('.barre');\n    const marge = (barre ? barre.getBoundingClientRect().height : 0) + 14;\n    const y = id ? window.scrollY + cible.getBoundingClientRect().top - marge : 0;\n    window.scrollTo({ top: Math.max(0, y), behavior: 'smooth' });\n    history.replaceState(null, '', id ? '#' + id : location.pathname);\n  });\n\n\n  \/\/ L'h\u00e9breu quitte le s\u00e9lecteur flottant de TranslatePress. Le CSS n'y suffit pas : la feuille\n  \/\/ du plugin, charg\u00e9e apr\u00e8s la n\u00f4tre, impose display:flex!important \u00e0 sp\u00e9cificit\u00e9 comparable.\n  \/\/ On retire donc l'\u00e9l\u00e9ment. C'est un rideau, pas une suppression : \/he\/ reste servi et d\u00e9clar\u00e9\n  \/\/ en hreflang \u2014 le retrait r\u00e9el est un r\u00e9glage du plugin, \u00e0 faire une fois dans son \u00e9cran.\n  (() => {\n    const oter = () => document.querySelectorAll('a[href*=\"\/he\/\"]').forEach((a) => {\n      if (a.closest('.trp-language-switcher, [id*=\"trp-floater\"]')) a.remove();\n    });\n    oter();\n    setTimeout(oter, 1500);\n  })();\n\n  \/\/ La langue courante se d\u00e9duit de l'adresse : \/en\/ pour l'anglais, la racine pour le fran\u00e7ais.\n  (() => {\n    const anglais = location.pathname.startsWith('\/en');\n    const a = document.querySelector('.langue a[hreflang=\"' + (anglais ? 'en' : 'fr') + '\"]');\n    if (a) a.setAttribute('aria-current', 'true');\n  })();\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>Expertises D\u00e9marche Solutions Secteurs Profils Actualit\u00e9s FAQ FR EN Nous contacter Automatisation sur mesure Quand aucun outil du march\u00e9 ne sait tester votre syst\u00e8me. Selenium, Cypress et Playwright couvrent le web. Au-del\u00e0 \u2014 client lourd, PowerBuilder, borne de commande, \u00e9cran de production, terminal m\u00e9tier \u2014 la plupart des prestataires s&#8217;arr\u00eatent l\u00e0 o\u00f9 l&#8217;outil s&#8217;arr\u00eate. Nous [&hellip;]<\/p>\n","protected":false},"author":254301877,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"_monsterinsights_skip_tracking":false,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"_wpcom_ai_launchpad_about_page":false,"_wpcom_ai_launchpad_gallery_page":false,"_wpcom_ai_launchpad_contact_page":false,"_wpcom_ai_launchpad_events_page":false,"_wpcom_ai_launchpad_video_page":false,"_wpcom_ai_launchpad_portfolio_piece":false,"footnotes":"","big_sky_generated":false},"class_list":["post-16706","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>D\u00e9veloppement d&#039;outils d&#039;automatisation de test sur mesure - TTC<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ttc-testing.io\/en\/developpement-outils-automatisation-sur-mesure\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"D\u00e9veloppement d&#039;outils d&#039;automatisation de test sur mesure - TTC\" \/>\n<meta property=\"og:description\" content=\"Expertises D\u00e9marche Solutions Secteurs Profils Actualit\u00e9s FAQ FR EN Nous contacter Automatisation sur mesure Quand aucun outil du march\u00e9 ne sait tester votre syst\u00e8me. Selenium, Cypress et Playwright couvrent le web. Au-del\u00e0 \u2014 client lourd, PowerBuilder, borne de commande, \u00e9cran de production, terminal m\u00e9tier \u2014 la plupart des prestataires s&#8217;arr\u00eatent l\u00e0 o\u00f9 l&#8217;outil s&#8217;arr\u00eate. Nous [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ttc-testing.io\/en\/developpement-outils-automatisation-sur-mesure\/\" \/>\n<meta property=\"og:site_name\" content=\"TTC\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/ttc-testing.io\\\/developpement-outils-automatisation-sur-mesure\\\/\",\"url\":\"https:\\\/\\\/ttc-testing.io\\\/developpement-outils-automatisation-sur-mesure\\\/\",\"name\":\"D\u00e9veloppement d'outils d'automatisation de test sur mesure - TTC\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/ttc-testing.io\\\/#website\"},\"datePublished\":\"2026-08-13T10:34:55+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/ttc-testing.io\\\/developpement-outils-automatisation-sur-mesure\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/ttc-testing.io\\\/developpement-outils-automatisation-sur-mesure\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/ttc-testing.io\\\/developpement-outils-automatisation-sur-mesure\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\\\/\\\/ttc-testing.io\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"D\u00e9veloppement d&#8217;outils d&#8217;automatisation de test sur mesure\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/ttc-testing.io\\\/#website\",\"url\":\"https:\\\/\\\/ttc-testing.io\\\/\",\"name\":\"TTC\",\"description\":\"Soci\u00e9t\u00e9 de test logiciel \u2013 Testeur, Automaticien &amp; Expert Performance QA\",\"publisher\":{\"@id\":\"https:\\\/\\\/ttc-testing.io\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/ttc-testing.io\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/ttc-testing.io\\\/#organization\",\"name\":\"TTC \u2013 Testing Training Consulting\",\"alternateName\":\"TTC Testing\",\"url\":\"https:\\\/\\\/ttc-testing.io\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/ttc-testing.io\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/ttc-testing.io\\\/wp-content\\\/uploads\\\/2024\\\/08\\\/Logo-TTC-1.png?fit=709%2C348&ssl=1\",\"contentUrl\":\"https:\\\/\\\/i0.wp.com\\\/ttc-testing.io\\\/wp-content\\\/uploads\\\/2024\\\/08\\\/Logo-TTC-1.png?fit=709%2C348&ssl=1\",\"width\":709,\"height\":348,\"caption\":\"TTC \u2013 Testing Training Consulting\"},\"image\":{\"@id\":\"https:\\\/\\\/ttc-testing.io\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"D\u00e9veloppement d'outils d'automatisation de test sur mesure - TTC","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ttc-testing.io\/en\/developpement-outils-automatisation-sur-mesure\/","og_locale":"en_US","og_type":"article","og_title":"D\u00e9veloppement d'outils d'automatisation de test sur mesure - TTC","og_description":"Expertises D\u00e9marche Solutions Secteurs Profils Actualit\u00e9s FAQ FR EN Nous contacter Automatisation sur mesure Quand aucun outil du march\u00e9 ne sait tester votre syst\u00e8me. Selenium, Cypress et Playwright couvrent le web. Au-del\u00e0 \u2014 client lourd, PowerBuilder, borne de commande, \u00e9cran de production, terminal m\u00e9tier \u2014 la plupart des prestataires s&#8217;arr\u00eatent l\u00e0 o\u00f9 l&#8217;outil s&#8217;arr\u00eate. Nous [&hellip;]","og_url":"https:\/\/ttc-testing.io\/en\/developpement-outils-automatisation-sur-mesure\/","og_site_name":"TTC","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/ttc-testing.io\/developpement-outils-automatisation-sur-mesure\/","url":"https:\/\/ttc-testing.io\/developpement-outils-automatisation-sur-mesure\/","name":"D\u00e9veloppement d'outils d'automatisation de test sur mesure - TTC","isPartOf":{"@id":"https:\/\/ttc-testing.io\/#website"},"datePublished":"2026-08-13T10:34:55+00:00","breadcrumb":{"@id":"https:\/\/ttc-testing.io\/developpement-outils-automatisation-sur-mesure\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ttc-testing.io\/developpement-outils-automatisation-sur-mesure\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/ttc-testing.io\/developpement-outils-automatisation-sur-mesure\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/ttc-testing.io\/"},{"@type":"ListItem","position":2,"name":"D\u00e9veloppement d&#8217;outils d&#8217;automatisation de test sur mesure"}]},{"@type":"WebSite","@id":"https:\/\/ttc-testing.io\/#website","url":"https:\/\/ttc-testing.io\/","name":"INCL. VAT","description":"Software Testing Company \u2013 Tester, Automation Engineer &amp; Performance QA Expert","publisher":{"@id":"https:\/\/ttc-testing.io\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ttc-testing.io\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ttc-testing.io\/#organization","name":"TTC \u2013 Testing Training Consulting","alternateName":"TTC Testing","url":"https:\/\/ttc-testing.io\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ttc-testing.io\/#\/schema\/logo\/image\/","url":"https:\/\/i0.wp.com\/ttc-testing.io\/wp-content\/uploads\/2024\/08\/Logo-TTC-1.png?fit=709%2C348&ssl=1","contentUrl":"https:\/\/i0.wp.com\/ttc-testing.io\/wp-content\/uploads\/2024\/08\/Logo-TTC-1.png?fit=709%2C348&ssl=1","width":709,"height":348,"caption":"TTC \u2013 Testing Training Consulting"},"image":{"@id":"https:\/\/ttc-testing.io\/#\/schema\/logo\/image\/"}}]}},"jetpack_likes_enabled":true,"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/PfY5O8-4ls","jetpack-related-posts":[{"id":16688,"url":"https:\/\/ttc-testing.io\/en\/","url_meta":{"origin":16706,"position":0},"title":"TTC Testing \u2014 QA, Software Testing and Automation Specialists","author":"laurent Abid","date":"13 August 2026","format":false,"excerpt":"Expertises D\u00e9marche Solutions Secteurs Profils Actualit\u00e9s FAQ FR EN Nous contacter Test logiciel \u00b7 Automatisation \u00b7 Qualit\u00e9 Sp\u00e9cialistes QA.Pas g\u00e9n\u00e9ralistes. Depuis 27 ans, nous testons et automatisons les applications critiques des grands comptes. 65 consultants, 100 % certifi\u00e9s ISTQB, 40 projets livr\u00e9s. D\u00e9couvrir notre d\u00e9marche TTC en bref L'ann\u00e9e 2026,\u2026","rel":"","context":"Similar post","block_context":{"text":"Similar post","link":""},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":16703,"url":"https:\/\/ttc-testing.io\/en\/ferme-de-mobiles-tests-multi-appareils\/","url_meta":{"origin":16706,"position":1},"title":"Cross-device testing on a mobile farm","author":"laurent Abid","date":"13 August 2026","format":false,"excerpt":"Expertise Approach Solutions Sectors Profiles News FAQ FR EN Contact us Mobile farm Your app never crashes on your phone. That is precisely the problem. A mobile app is not tested on a single device, but on the actual fleet of devices used by your audience: hundreds of models, sizes...","rel":"","context":"Similar post","block_context":{"text":"Similar post","link":""},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":16710,"url":"https:\/\/ttc-testing.io\/en\/suivi-exploitation-restauration-rapide-simphony\/","url_meta":{"origin":16706,"position":2},"title":"Quick service restaurant operational monitoring and Oracle Simphony expertise","author":"laurent Abid","date":"13 August 2026","format":false,"excerpt":"Expertise Approach Solutions Sectors Profiles News FAQ FR EN Contact us Operations monitoring An order that disappears between the register and the kitchen triggers no alert. It passes through five systems before reaching a hand. Each boundary is a place where it can become distorted, get stuck, or remain open for weeks\u2026","rel":"","context":"Similar post","block_context":{"text":"Similar post","link":""},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":16705,"url":"https:\/\/ttc-testing.io\/en\/test-bornes-commande-ecrans-cuisine\/","url_meta":{"origin":16706,"position":3},"title":"Testing ordering kiosks and kitchen screens","author":"laurent Abid","date":"13 August 2026","format":false,"excerpt":"Expertise Approach Solutions Sectors Profiles News FAQ FR EN Contact us Terminals, kiosks and production screens Are your terminals displaying the campaign you approved? Between the marketing brief and the screen the customer sees, there is manual entry, a network of restaurants, and no one to check. We...","rel":"","context":"Similar post","block_context":{"text":"Similar post","link":""},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":16727,"url":"https:\/\/ttc-testing.io\/en\/test-logiciels-restauration-rapide\/","url_meta":{"origin":16706,"position":4},"title":"Test des logiciels de restauration rapide","author":"laurent Abid","date":"13 August 2026","format":false,"excerpt":"Expertises D\u00e9marche Solutions Secteurs Profils Actualit\u00e9s FAQ FR EN Nous contacter Restauration rapideDeux campagnes par mois, et une recette qui ne suit plus.Le rythme des campagnes a doubl\u00e9. Le temps de recette, lui, n'a pas boug\u00e9 \u2014 et il se prend toujours la veille de l'ouverture, sur un seul restaurant,\u2026","rel":"","context":"Similar post","block_context":{"text":"Similar post","link":""},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":16729,"url":"https:\/\/ttc-testing.io\/en\/tests-de-performance-automatises-ci-cd\/","url_meta":{"origin":16706,"position":5},"title":"Automatisation des tests de performance en int\u00e9gration continue","author":"laurent Abid","date":"13 August 2026","format":false,"excerpt":"Expertises D\u00e9marche Solutions Secteurs Profils Actualit\u00e9s FAQ FR EN Nous contacter Performance en continuUn test de charge une fois par an ne prot\u00e8ge de rien.La d\u00e9gradation ne survient pas le jour du tir annuel. Elle s'installe livraison apr\u00e8s livraison, dix millisecondes \u00e0 la fois, jusqu'au jour o\u00f9 le trafic monte\u2026","rel":"","context":"Similar post","block_context":{"text":"Similar post","link":""},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/ttc-testing.io\/en\/wp-json\/wp\/v2\/pages\/16706","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ttc-testing.io\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/ttc-testing.io\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/ttc-testing.io\/en\/wp-json\/wp\/v2\/users\/254301877"}],"replies":[{"embeddable":true,"href":"https:\/\/ttc-testing.io\/en\/wp-json\/wp\/v2\/comments?post=16706"}],"version-history":[{"count":1,"href":"https:\/\/ttc-testing.io\/en\/wp-json\/wp\/v2\/pages\/16706\/revisions"}],"predecessor-version":[{"id":16715,"href":"https:\/\/ttc-testing.io\/en\/wp-json\/wp\/v2\/pages\/16706\/revisions\/16715"}],"wp:attachment":[{"href":"https:\/\/ttc-testing.io\/en\/wp-json\/wp\/v2\/media?parent=16706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}