Software Quality Specialist – Test Automation & Analysis Spécialiste en qualité logicielle – Automatisation & Analyse de tests
Detecting anomalies, automating test scenarios and ensuring software reliability across web, desktop and game applications. Détecter les anomalies, automatiser les scénarios de tests et garantir la fiabilité des logiciels, sites web et jeux vidéo.
I'm a Software Quality Specialist focused on detecting anomalies and weaknesses in software, websites and games. I design, automate and execute test scenarios to ensure every release is reliable and robust.
Je suis un spécialiste en qualité logicielle axé sur la détection des anomalies et des faiblesses dans les logiciels, sites web et jeux vidéo. Je conçois, automatise et exécute des scénarios de tests pour garantir la fiabilité de chaque livraison.
I've built automation frameworks using Playwright (TypeScript) for web apps and FlaUI (C#/NUnit) for WPF desktop interfaces — both following the Page Object Model and connecting natively to Zephyr Scale for execution tracking.
J'ai construit des frameworks en Playwright (TypeScript) pour le web et FlaUI (C#/NUnit) pour les interfaces WPF — les deux suivent le Page Object Model et s'intègrent nativement à Zephyr Scale pour le suivi des exécutions.
I believe quality is a shared responsibility, and I focus on building maintainable, readable, and reliable test suites that last.
La qualité est une responsabilité partagée. Je me concentre sur la création de suites de tests maintenables, lisibles et fiables qui durent dans le temps.
Production-ready framework with TypeScript, Page Object Model, custom fixtures, and data-driven testing via JSON. Integrates Zephyr Scale for result reporting and supports multi-browser execution with screenshot capture on failure. Framework prêt pour la production avec TypeScript, Page Object Model, fixtures personnalisées et tests data-driven via JSON. Intègre Zephyr Scale pour les rapports et supporte l'exécution multi-navigateur avec capture d'écran sur échec.
NUnit-based automation framework for Windows WPF applications using FlaUI UIA3. Features automatic screenshot capture on failure, Zephyr Scale reporting via custom [ZephyrTest] attribute, and GitHub Actions CI integration. Framework NUnit pour applications WPF via FlaUI UIA3. Capture d'écran automatique sur échec, rapport Zephyr Scale via [ZephyrTest], et intégration CI GitHub Actions.
Automated API test suite using Playwright's API context. Validates CRUD operations (200/201/400), tests Bearer token authentication, ensures test isolation, and cleans data post-execution. Suite de tests API automatisée avec le contexte API de Playwright. Valide les opérations CRUD (200/201/400), teste l'auth Bearer token, assure l'isolation des tests et nettoie les données après exécution.
import { test as base, Page } from '@playwright/test'; import { LoginPage } from '../pages/LoginPage'; type Fixtures = { loginPage: LoginPage; }; export const test = base.extend<Fixtures>({ loginPage: async ({ page }: { page: Page }, use) => { const loginPage = new LoginPage(page); await use(loginPage); }, }); export const expect = test.expect;
import { Page } from '@playwright/test'; import { BasePage } from './BasePage'; import { loginLocators } from '../locators/login.locators'; export class LoginPage extends BasePage { async gotoLoginPage() { await this.goto('/'); } async fillUsername(username: string) { await this.page.fill(loginLocators.usernameInput, username); } async fillPassword(password: string) { await this.page.fill(loginLocators.passwordInput, password); } async clickLogin() { await this.page.click(loginLocators.loginButton); } async login(username: string, password: string) { await this.gotoLoginPage(); await this.fillUsername(username); await this.fillPassword(password); await this.clickLogin(); } }
export const loginLocators = { // Login form usernameInput: '[data-test-qa-id="txtUserName"]', passwordInput: '[data-test-qa-id="txtPassword"]', loginButton: '[data-test-qa-id="btnLogin"]', // Logout accountIcon: 'span[role="presentation"].icon-user4', logoutButton: 'div.content.MuiBox-root:has-text(/Se déconnecter|Logout/)', logoutButtonAlt1: 'text=/Se déconnecter|Logout/', logoutButtonAlt2: 'div:has-text(/Se déconnecter|Logout/)', // Feedback errorMessage: '.error-message', successIndicator: '.welcome-message', };
import { test } from '../../fixtures/baseTest'; import testData from '../../data/testData.json'; test.describe('Login Tests', () => { test('Login avec identifiants valides', async ({ loginPage }) => { // Arrange await loginPage.gotoLoginPage(); // Act await loginPage.fillUsername(testData.validUser.username); await loginPage.fillPassword(testData.validUser.password); await loginPage.clickLogin(); // Assert await loginPage.getPage.waitForLoadState('networkidle'); }); test('Login avec mauvais mot de passe – affiche erreur', async ({ loginPage }) => { await loginPage.login(testData.validUser.username, 'wrong_password'); // Assert: error message visible }); });
import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ reporter: [['html', { outputFolder: 'playwright-report', open: 'always' }]], testDir: './tests', timeout: 30 * 1000, use: { headless: false, baseURL: process.env.BASE_URL, screenshot: 'only-on-failure', }, projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] }, }], });
/// Classe de base pour tous les tests FlaUI. [TestFixture] public abstract class TestBase { protected Application App; protected UIA3Automation Automation; protected Window MainWindow; protected virtual string AppPath => TestConfig.AppExecutablePath; protected virtual int WindowTimeoutMs => TestConfig.DefaultTimeoutMs; [SetUp] public virtual void SetUp() { Automation = new UIA3Automation(); App = Application.Launch(AppPath); MainWindow = App.GetMainWindow(Automation, TimeSpan.FromMilliseconds(WindowTimeoutMs)); Assert.IsNotNull(MainWindow, "Fenêtre principale introuvable."); OnSetUp(); } [TearDown] public virtual void TearDown() { OnTearDown(); CaptureScreenshotOnFailure(); CloseApp(); } protected virtual void OnSetUp() { } protected virtual void OnTearDown() { } private void CaptureScreenshotOnFailure() { if (TestContext.CurrentContext.Result.Outcome.Status == TestStatus.Failed) { var dir = Path.Combine(TestConfig.ArtifactsPath, "screenshots"); Directory.CreateDirectory(dir); var file = Path.Combine(dir, $"{TestContext.CurrentContext.Test.Name}_{DateTime.Now:yyyyMMdd_HHmmss}.png"); Capture.Screen().ToFile(file); TestContext.AddTestAttachment(file, "Screenshot à l'échec"); } } private void CloseApp() { try { App?.Close(); Automation?.Dispose(); } catch (Exception ex) { TestContext.WriteLine($"[WARN] Fermeture : {ex.Message}"); } } protected AutomationElement WaitForElement( Func<AutomationElement> finder, int timeoutMs = 5000) { var sw = Stopwatch.StartNew(); while (sw.ElapsedMilliseconds < timeoutMs) { try { var el = finder(); if (el != null) return el; } catch { } Thread.Sleep(200); } throw new TimeoutException($"Élément non trouvé après {timeoutMs} ms."); } }
/// Wrapper Zephyr Scale — envoie les résultats via API REST. public class ZephyrHelper { private static readonly HttpClient _client = new HttpClient(); private readonly string _baseUrl; private readonly string _projectKey; public ZephyrHelper() { _baseUrl = TestConfig.ZephyrBaseUrl; _projectKey = TestConfig.ZephyrProjectKey; _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", TestConfig.ZephyrToken); } public async Task ReportExecutionAsync( string testCaseKey, string status, string comment = "") { var payload = new { projectKey = _projectKey, testCaseKey, statusName = status, comment }; var json = JsonSerializer.Serialize(payload); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _client.PostAsync($"{_baseUrl}/testexecutions", content); TestContext.WriteLine(response.IsSuccessStatusCode ? $"[ZEPHYR] ✓ '{status}' → {testCaseKey}" : $"[ZEPHYR] ✗ {response.StatusCode}"); } public void ReportExecution(string key, string status, string comment = "") => ReportExecutionAsync(key, status, comment).GetAwaiter().GetResult(); public static class Status { public const string Pass = "Pass"; public const string Fail = "Fail"; public const string Blocked = "Blocked"; public const string NotExecuted = "Not Executed"; } } [AttributeUsage(AttributeTargets.Method)] public class ZephyrTestAttribute : Attribute { public string TestCaseKey { get; } public ZephyrTestAttribute(string key) => TestCaseKey = key; }
/// Extension de TestBase avec rapport automatique Zephyr Scale. [TestFixture] public abstract class ZephyrTestBase : TestBase { private readonly ZephyrHelper _zephyr = new ZephyrHelper(); [TearDown] public override void TearDown() { ReportToZephyr(); base.TearDown(); } private void ReportToZephyr() { var methodName = TestContext.CurrentContext.Test.MethodName; var attr = GetType().GetMethod(methodName)? .GetCustomAttribute<ZephyrTestAttribute>(); if (attr == null) return; var outcome = TestContext.CurrentContext.Result.Outcome.Status; var status = outcome == TestStatus.Passed ? ZephyrHelper.Status.Pass : outcome == TestStatus.Failed ? ZephyrHelper.Status.Fail : ZephyrHelper.Status.Blocked; _zephyr.ReportExecution(attr.TestCaseKey, status, TestContext.CurrentContext.Result.Message ?? ""); } }
/// Page Object — Écran de connexion WPF. public class LoginPage { private readonly Window _window; public LoginPage(Window window) => _window = window; private TextBox UsernameField => _window.FindFirstDescendant(cf => cf.ByAutomationId("txtUsername"))?.AsTextBox(); private TextBox PasswordField => _window.FindFirstDescendant(cf => cf.ByAutomationId("txtPassword"))?.AsTextBox(); private Button LoginButton => _window.FindFirstDescendant(cf => cf.ByAutomationId("btnLogin"))?.AsButton(); private Label ErrorLabel => _window.FindFirstDescendant(cf => cf.ByAutomationId("lblError"))?.AsLabel(); public void Login(string username, string password) { if (UsernameField != null) { UsernameField.Click(); UsernameField.Text = username; } if (PasswordField != null) { PasswordField.Click(); PasswordField.Text = password; } LoginButton?.Click(); } public bool IsErrorDisplayed() => ErrorLabel?.IsEnabled == true; public string GetErrorMessage() => ErrorLabel?.Text ?? string.Empty; }
[TestFixture][Category("Authentication")] public class LoginSpec : ZephyrTestBase { private LoginPage _loginPage; private const string ValidUsername = "admin"; private const string ValidPassword = "Admin@123"; private const string WrongPassword = "mauvais_mdp"; protected override void OnSetUp() => _loginPage = new LoginPage(MainWindow); [Test][ZephyrTest("PROJ-T01")] [Description("Connexion réussie avec identifiants valides")] public void Login_AvecIdentifiantsValides_ConnecteUtilisateur() { _loginPage.Login(ValidUsername, ValidPassword); var dashboard = WaitForElement(() => MainWindow.FindFirstDescendant(cf => cf.ByAutomationId("DashboardPanel"))); Assert.IsNotNull(dashboard, "Le dashboard doit être visible après connexion."); } [Test][ZephyrTest("PROJ-T02")] public void Login_AvecMauvaisMotDePasse_AfficheErreur() { _loginPage.Login(ValidUsername, WrongPassword); Assert.IsTrue(_loginPage.IsErrorDisplayed()); } [Test][ZephyrTest("PROJ-T03")] public void Login_ChampsVides_BoutonDesactiveOuErreur() { _loginPage.Login("", ""); Assert.IsTrue(_loginPage.IsErrorDisplayed()); } }
public static class TestConfig { public static string AppExecutablePath => Environment.GetEnvironmentVariable("WPFAUTOMATION_EXE_PATH") ?? @"C:\Apps\YourWpfApp\YourWpfApp.exe"; public static string ArtifactsPath => Environment.GetEnvironmentVariable("TEST_ARTIFACTS_PATH") ?? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestArtifacts"); public static int DefaultTimeoutMs => 10_000; public static string ZephyrBaseUrl => Environment.GetEnvironmentVariable("ZEPHYR_BASE_URL") ?? "https://api.zephyrscale.smartbear.com/v2"; public static string ZephyrToken => Environment.GetEnvironmentVariable("ZEPHYR_TOKEN") ?? throw new InvalidOperationException("ZEPHYR_TOKEN manquant."); public static string ZephyrProjectKey => Environment.GetEnvironmentVariable("ZEPHYR_PROJECT_KEY") ?? "PROJ"; }
Full-featured weather app with real-time data, 15-day forecasts, hourly breakdown, UV index, air quality, pollen levels, geolocation, city search, auto-refresh every 10 minutes, °C/°F toggle, dark mode and lifestyle tips. Application météo complète avec données en temps réel, prévisions sur 15 jours, détail horaire, indice UV, qualité de l'air, taux de pollen, géolocalisation, recherche de ville, rafraîchissement automatique toutes les 10 minutes, bascule °C/°F, mode sombre et conseils quotidiens.
Windows productivity app designed to manage projects and tasks efficiently. Features a clean and intuitive interface, fast performance and a secure local experience with a convenient Windows installer. Application Windows conçue pour gérer efficacement les projets et les tâches. Elle propose une interface simple et intuitive, des performances rapides ainsi qu'une expérience sécurisée avec un installeur Windows pratique.
Python-based desktop tool for video analysis and inspection. Extracts frames, analyzes metadata and provides visual insights from video files — useful for QA validation of video content and media pipelines. Outil bureau en Python pour l'analyse et l'inspection de vidéos. Extrait des frames, analyse les métadonnées et fournit des insights visuels — utile pour la validation QA de contenus vidéo et de pipelines média.
Custom bug and test tracking application tailored for QA workflows. Designed to log defects, track test execution status and manage QA reporting without depending on third-party tools like Jira. Application de suivi de bugs et de tests conçue pour les workflows QA. Permet d'enregistrer des défauts, suivre l'état des exécutions et gérer les rapports QA sans dépendre d'outils tiers comme Jira.
Browser extension that generates reliable XPath selectors for any element on a page with a single click. Built specifically for test automation engineers who need stable, readable locators for Playwright, Selenium or FlaUI. Extension navigateur qui génère des sélecteurs XPath fiables pour n'importe quel élément d'une page en un clic. Conçu pour les ingénieurs en automatisation qui ont besoin de localisateurs stables pour Playwright, Selenium ou FlaUI.
Full-stack carpooling web application that connects drivers and passengers for shared rides. Includes user accounts, ride creation and search, booking management and a clean responsive interface. Application web de covoiturage full-stack qui connecte conducteurs et passagers. Comprend la gestion des comptes, la création et recherche de trajets, la réservation et une interface responsive soignée.
Native Android radio streaming app with worldwide station search via Radio-Browser API, audio recording, sleep timer, radio alarm, dark mode, home screen widget and custom station management. Application Android native de streaming radio avec recherche mondiale via l'API Radio-Browser, enregistrement audio, minuterie de sommeil, réveil radio, mode sombre, widget écran d'accueil et gestion de stations personnalisées.
Chargement des articles...
Looking for a Software Quality Specialist position where I can detect anomalies, automate test scenarios and help build reliable, high-quality software. À la recherche d'un poste de Spécialiste en qualité logicielle où je pourrai détecter les anomalies, automatiser les scénarios de tests et contribuer à des livraisons fiables et de haute qualité.