Client Node.js
Traduit automatiquement de l’anglais.
Lire en anglaisproxymail-qa enveloppe la REST API pour JavaScript et TypeScriptᅠ: Playwright, tâches Cypress, Vitest, Jest, Detox ou un simple script. Il n’a aucune dépendance, fournit ses propres types et tourne sur Node 18 ou plus récent (ou partout où fetch existe). Le code source se trouve sur github.com/aaquib90/proxymail-qa-js, et chaque version est publiée depuis GitHub Actions avec la provenance npm.
Démarrage rapide
Section intitulée « Démarrage rapide »Installer le package
Section intitulée « Installer le package »npm install --save-dev proxymail-qapnpm add -D proxymail-qayarn add -D proxymail-qabun add -d proxymail-qaDéfinir la clé d’API
Section intitulée « Définir la clé d’API »Créez une clé d’API et définissez-la comme
PROXYMAIL_QA_API_KEY, ou passez-la en paramètre.Créer une adresse et attendre
Section intitulée « Créer une adresse et attendre »Créez une adresse, inscrivez-vous avec elle dans votre app, puis attendez le code ou le lien.
Les attentes renvoient le code ou le lien seul, avec le service qui l’a envoyé et sa date d’expiration, jamais le texte de l’e-mail. Une attente dure jusqu’à 30 secondes, et des
timeoutplus longs la répètent.import { ProxyMailQA } from 'proxymail-qa';const qa = new ProxyMailQA(); // reads PROXYMAIL_QA_API_KEYconst { address, persona, password } = await qa.createAddress({ persona: true, password: true });const submitted = new Date();// …sign up in your app with address, persona and password…const { value: code } = await qa.waitForCode(address, { since: submitted });// or: const { value: link } = await qa.waitForLink(address, { type: 'verify', since: submitted });await qa.deleteAddress(address);
Exemples complets : Playwright et Cypress.
Méthodes
Section intitulée « Méthodes »| Méthode | Ce qu’il fait |
|---|---|
new ProxyMailQA({ apiKey?, baseUrl?, fetch?, run? }) |
Le client. apiKey vaut PROXYMAIL_QA_API_KEY par défaut, et run vaut PROXYMAIL_QA_RUN |
createAddress(options?) |
Une nouvelle adresse de test. Optionsᅠ: label, name (un nom personnalisé avant le @ᅠ; les noms génériques seuls comme test sont refusés), persona, password (renvoyé une seule fois, et typé comme string quand vous passez password: true), lifetimeDays, domain, age ou dateOfBirth, country, et où elle est utiliséeᅠ: site et environment (nouveau en 0.9.0) |
waitForCode(address, options?) |
Le code de vérification ou de connexion le plus récent. Optionsᅠ: since, service, timeout (secondes, 30 par défaut), unused (ignorer les codes déjà utilisésᅠ: renvoyés par une attente précédente, ou copiés ou remplis dans l’app ou Proxy Mail QA for Chromeᅠ; nouveau en 0.5.1), et type: 'coupon' pour un coupon ou un code promo, avec ses discount, valid_until et after_seconds (nouveau en 0.10.0) |
waitForLink(address, options?) |
La même chose pour un lien. Aussi type (verify, magic_signin, invite, reset ou any, ou une liste) et app (true pour le lien qui ouvre une app mobile) |
messages(address, options?) |
Les e-mails avec leurs codes et liens. includeText et includeHtml ajoutent le contenu, pour les tests de templatesᅠ; limit |
countEmails(address, options?) |
Combien d’e-mails sont arrivés, chacun résumé sans son contenu. Optionsᅠ: since, service, type (code ou link), subtype et, depuis 0.10.0, category (auth, transactional ou marketing) |
expectNoEmail(address, options?) |
Réussit si aucun e-mail correspondant n’arrive dans les timeout secondes (30 par défaut, et les minutes fonctionnentᅠ: il redemande)ᅠ; lève ProxyMailQAError (409, email_arrived) dès qu’un e-mail arrive. Mêmes filtres, et since vaut maintenant par défaut |
markUnsubscribed(address, options?) |
Labs. Enregistre que l’adresse s’est désinscrite d’un expéditeur (emailId ou serviceᅠ; at pour antidaterᅠ; grace: '1d' pour le courrier déjà en route), après que le test l’a fait dans son navigateur. Tout e-mail marketing ultérieur de cet expéditeur échoue à after_unsubscribe. Nouveau en 0.10.0 |
markResubscribed(address, options?) |
Labs. L’adresse s’est réabonnéeᅠ: met fin à la surveillance. Nouveau en 0.10.0 |
recordConsent(address, consent) |
Labs. Enregistre ce que le compte a accepté à l’inscription (marketing, prechecked, doubleOptIn), pour les règles marketing par pays. consent(address) le lit. Nouveau en 0.10.0 |
consent(address) |
Labs. Ce que le compte a accepté, tel qu’enregistré. Nouveau en 0.10.0 |
recordCouponResult(address, result, options?) |
Labs. Après l’application d’un coupon au paiement (staging, paiements de test), enregistre accepted, rejected ou error avec le message de l’app (code, oneTime)ᅠ; check est le verdict coupon_result. Nouveau en 0.10.0 |
checkSequence(address, steps, options?) |
Labs. Si une série de bienvenue ou une campagne goutte-à-goutte est arrivée dans l’ordre et à temps après sinceᅠ; chaque étape peut indiquer subject, category, type, after (‘1d’) et within. Nouveau en 0.10.0 |
offers(address) |
Labs. Les coupons de chaque expéditeur et la variante de l’adresse (simple, majuscules, +tag) à laquelle chacun est allé ; passed vaut false quand une personne a reçu une offre deux fois. Nouveau en 0.10.0 |
checkUnsubscribe(address) |
Labs. Chaque désinscription et les e-mails marketing arrivés aprèsᅠ; passed vaut false s’il y en a eu. Nouveau en 0.10.0 |
forTest(name), recordTest(result), recordTestTotals(totals) |
Un client qui nomme son test à chaque appel, et des résultats de test pour le rapport de l’exécutionᅠ; proxymail-qa/playwright et son reporter les utilisent. Nouveau en 0.8.0 (en savoir plus) |
formValues({ fields, address?, age?, country?, … }) |
Quoi saisir ou choisir dans chaque champ d’un formulaire d’inscription, à partir d’une nouvelle adresse créée pour lui. Nouveau en 0.7.0 (en savoir plus) |
checkEmail(address, options?) |
Contrôle l’e-mail correspondant le plus récent (ou emailId)ᅠ: SPF, DKIM, DMARC, liens locaux et http simples, destination de son lien (expectHost), une expiration indiquée (et, depuis 0.10.0, la fin d’un coupon), une partie en texte brut, la taille et les e-mails composés uniquement d’images. Depuis 0.7.0, les phrases qu’il doit contenir (expectText) et sa langue (expectLanguage)ᅠ; depuis 0.10.0, son délai d’arrivée après since (expectWithin: '5m') et ses pièces jointes (expectAttachment: ['.pdf']). Un e-mail marketing reçoit aussi list_unsubscribe, one_click_unsubscribe, unsubscribe_link et preheader, et chaque e-mail harmful_attachment et spam_risk (une estimation de son arrivée en spam). Uniquement des verdicts, jamais son contenu. Nouveau en 0.4.0 |
checkDelivery(address) |
Ce qui a atteint l’adresse et ce qui a été refusé, sans attendre |
variants(address) |
Des variantes inhabituelles de l’adresse qui reçoivent quand même son courrier, et des variantes invalides (en savoir plus) |
nameVariants({ field?, maxLength? }) |
Des noms délicats pour un champ de nomᅠ: ceux à accepter (apostrophes, accents, autres écritures), ceux à rejeter, du HTML ou du SQL à stocker comme texte, et ceux sur lesquels les apps divergent. Nouveau en 0.11.0 (en savoir plus) |
passwordVariants({ minLength?, maxLength?, symbols? }) |
Des mots de passe délicats dans les règles du champᅠ: le minimum et le maximum, chaque classe de symboles, les espaces, le non-ASCII, et ceux sur lesquels les apps divergentᅠ; nouveaux à chaque appel. Nouveau en 0.11.0 (en savoir plus) |
getAddress(address), listAddresses({ limit?, before?, tags?, country?, minAge?, maxAge?, hasPassword?, search?, site?, environment? }) |
Lire des adresses, ou retrouver celles créées plus tôtᅠ: chaque filtre indiqué doit correspondre |
updateAddress(address, patch) |
Modifier label, notes, tags, first_name, last_name, username, date_of_birth ou countryᅠ; null en efface un |
setPassword(address, { password?, label? }) |
Enregistrer le mot de passe que le compte de test utilise maintenant, après que l’app l’a modifié ou réinitialisé. Sans mot de passe fourni, un mot de passe fort est généré et renvoyé une seule fois |
savedPassword(address, label?) |
Lire un mot de passe enregistré. Nécessite une clé avec «ᅠPeut lire les mots de passe enregistrésᅠ»ᅠ; chaque lecture est journalisée |
signInAccount({ site?, environment?, tags? }) |
Le compte de test enregistré avec lequel se connecter, et son mot de passe, en un seul appel : d’abord celui utilisé sur ce site ; null si aucun ne convient. Nécessite une clé avec « Peut lire les mots de passe enregistrés ». Nouveau dans la 0.9.0 (en savoir plus) |
markDevHost(host) |
Marque un hôte tel que localhost:3000 comme environnement de développement du projet, pour que checkEmail accepte les liens vers celui-ci. Nouveau dans la 0.9.0 |
deleteAddress(address) |
Supprimer l’adresse et ses e-mails |
project() |
Le projet de la clé : ses domaines de messagerie, son préfixe d’e-mail et ses limites |
updateProject({ name?, website?, environments?, emailPrefix?, mailDomain? }) |
Modifier le projet, comme dans les paramètres du projet ; null efface website ou emailPrefix. Nécessite le responsable du projet, ou un propriétaire ou administrateur de l’organisation |
checkOutAccount({ tags?, country?, minAge?, maxAge?, hasPassword?, search?, site?, environment?, minutes? }), extendCheckout(account, minutes?), returnAccount(account, { addTags?, removeTags? }), withAccount(options, fn) |
Le pool de comptes : empruntez un compte dans un état connu qu’aucun autre test n’utilise, puis rendez-le. Nouveau dans la 0.6.0 (en savoir plus). À partir de la 0.7.0, withAccount accepte les hooks create, check et reset : comptes dans un état connu |
startRun({ name?, source?, meta? }), finishRun({ failed? }), runReport(), addRunNote(text, { failed? }), getRun(), listRuns() |
Une exécution : les appels de ce client y ajoutent ce qu’ils ont observé, pour un rapport sur la pull request. Nouveau dans la 0.5.0 |
Chaque méthode accepte une adresse simple ou une variante de celle-ci. Attendre sur une adresse +tag ne trouve que les e-mails envoyés à ce tag, donc une seule adresse peut servir à toute une exécution de tests : voir Exécuter dans CI.
Les méthodes marquées Labs font partie des e-mails marketing et promotionnels, une fonctionnalité Labs que le créateur de la clé active dans l’app sous Account settings → Labs ; d’ici là, elles lèvent une erreur 403 qui l’indique. checkEmail omet les vérifications des fonctionnalités Labs désactivées.
Valeurs pour un formulaire d’inscription
Section intitulée « Valeurs pour un formulaire d’inscription »formValues indique quoi saisir ou choisir dans chaque champ d’un formulaire, à partir d’une nouvelle adresse créée pour lui : un nom d’utilisateur et un mot de passe conformes aux règles des champs, la date de naissance au format du champ, un pays proposé par la liste déroulante. Dans Playwright, lisez les champs de la page et remplissez chacun d’eux :
const form = page.locator('form#sign-up');const fields = await form.locator('input, select').evaluateAll((elements) => elements.map((el, i) => { const input = el as HTMLInputElement & HTMLSelectElement; input.dataset.field = String(i); return { id: String(i), type: input.tagName === 'SELECT' ? 'select' : input.type, name: input.name, label: input.labels?.[0]?.textContent ?? input.getAttribute('aria-label') ?? '', placeholder: input.placeholder, autocomplete: input.getAttribute('autocomplete') ?? '', pattern: input.pattern || undefined, minLength: input.minLength > 0 ? input.minLength : undefined, maxLength: input.maxLength > 0 ? input.maxLength : undefined, options: input.tagName === 'SELECT' ? Array.from(input.options, (o) => ({ value: o.value, label: o.text })) : undefined, }; }),);const { address, values } = await qa.formValues({ fields });for (const { id, kind, value } of values) { if (value === null) continue; const field = form.locator(`[data-field="${id}"]`); if (await field.evaluate((el) => el.tagName === 'SELECT')) await field.selectOption(value); else await field.fill(value);}Passez address pour utiliser un compte créé auparavant, et age ou country pour un nouveau. Plus de détails dans le guide REST API.
Vérifier que rien n’arrive, ou qu’un seul e-mail arrive
Section intitulée « Vérifier que rien n’arrive, ou qu’un seul e-mail arrive »// An unsubscribed user gets no newsletter.const sent = new Date();await sendNewsletter();await qa.expectNoEmail(unsubscribed.address, { since: sent, timeout: 20 });
// A sign-up sends one verification email, not two.await qa.waitForLink(address, { since: submitted });await new Promise((resolve) => setTimeout(resolve, 10_000)); // time for a duplicateexpect((await qa.countEmails(address, { since: submitted, type: 'link' })).count).toBe(1);Aucune des deux ne renvoie le contenu des e-mails, seulement leur heure d’arrivée, leur expéditeur et le type de code ou de lien. Plus de détails dans le guide REST API.
Vérifier l’envoi et la construction d’un e-mail
Section intitulée « Vérifier l’envoi et la construction d’un e-mail »await qa.waitForLink(address, { since: submitted });const result = await qa.checkEmail(address, { since: submitted, type: 'link', expectHost: 'staging.example.com' });expect(result.failed).toEqual([]); // e.g. ['local_links'] when the link goes to localhost
// A localised email: the right language, with the user's name in it.const german = await qa.checkEmail(address, { since: submitted, expectLanguage: 'de', expectText: [persona.first_name] });expect(german.failed).toEqual([]);Chaque vérification réussit, échoue, avertit ou est ignorée, avec une raison d’une ligne ; la signification de chacune est dans le guide REST API. Vous testez une application qui tourne sur votre machine ? await qa.markDevHost('localhost:3000') une fois, et les liens vers celle-ci passent local_links ; les liens vers tout autre localhost échouent toujours.
Un pool de comptes pour les tests parallèles
Section intitulée « Un pool de comptes pour les tests parallèles »Taguez les comptes selon leur état, puis laissez chaque test en emprunter un. withAccount en réserve un, exécute votre fonction et le rend toujours ; renvoyez { addTags, removeTags } pour enregistrer ce que le test a modifié :
await qa.withAccount({ tags: ['verified', 'premium'] }, async (account) => { const { secret } = await qa.savedPassword(account.address); await signIn(page, account.address, secret); await cancelSubscription(page); return { addTags: ['cancelled'], removeTags: ['premium'] };});Aucun autre test n’obtient le même compte tant qu’il est réservé (10 minutes sauf si vous passez minutes), et un compte jamais rendu se libère à l’expiration de son délai.
Comptes dans un état connu
Section intitulée « Comptes dans un état connu »Quand un test a besoin d’un compte dans un état particulier dans votre application (vérifié, sur l’offre premium, sans commande), donnez trois hooks à withAccount. Votre code de test les exécute avec son propre accès à votre application ; Proxy Mail QA ne s’y connecte jamais. Ici, app représente votre propre code qui communique avec votre application : une API d’administration, un seed de base de données ou UI.
await qa.withAccount( { tags: ['verified', 'premium'], // When none is free: sign up with the new address, verify it, upgrade it. create: async (account) => { const submitted = new Date(); await app.signUp(account.address, account.password); const { value: link } = await qa.waitForLink(account.address, { type: 'verify', since: submitted }); await app.open(link); await app.admin.setPlan(account.address, 'premium'); }, // Is it still in that state? Return false or throw if not. check: async (account) => (await app.admin.user(account.address)).plan === 'premium', // After the test: put it back. reset: async (account) => app.admin.resetUser(account.address, { plan: 'premium', orders: [] }), }, async (account) => { // …the test… },);- check s’exécute avant que le test reçoive le compte. S’il échoue, reset s’exécute et le compte est revérifié ; s’il échoue encore, le compte suivant est essayé (jusqu’à
attempts, 3), et celui qui a échoué perd les tags du pool et reçoit le tagcheck-failed. - create s’exécute quand aucun compte ne correspond ou que tous les comptes correspondants sont réservés. La nouvelle adresse est réservée à ce test dès le départ et reçoit les tags une fois create et check réussis. Passez
newAccountpour son âge, son libellé ou son domaine ; elle reçoit le pays du filtre. - reset s’exécute après le test, qu’il ait réussi ou non. Si reset échoue, le compte perd les tags du pool et reçoit le tag
needs-reset, afin qu’aucun autre test ne l’obtienne dans un état inconnu.
Dans une exécution, un nouveau compte, un check échoué et un reset échoué ajoutent chacun une ligne au rapport.
Un rapport de l’exécution
Section intitulée « Un rapport de l’exécution »Les appels effectués pendant qu’une exécution est ouverte y ajoutent ce qu’ils ont observé : les adresses, les e-mails arrivés et le délai après since, les codes, la destination de chaque lien, les vérifications, les vérifications de désabonnement, d’offres et de coupons, les tests échoués et les e-mails jamais arrivés. Dans GitHub Actions, l’action Proxy Mail QA démarre et termine l’exécution et publie le rapport sur la pull request ; le client récupère l’exécution depuis PROXYMAIL_QA_RUN sans modifier vos tests. Ailleurs, lancez-la vous-même :
const run = await qa.startRun({ name: 'Sign-up tests', meta: { branch: process.env.BRANCH } });// …the tests, using qa…await qa.finishRun({ failed: testsFailed });console.log(await qa.runReport(run.id)); // Markdown, for a pull request commentD’autres processus rejoignent l’exécution quand vous leur passez son identifiant comme PROXYMAIL_QA_RUN.
Pour indiquer de quel test provient chaque e-mail, utilisez un client par test : qa.forTest('sign-up › sends one code') nomme le test dans chaque appel. Dans Playwright, proxymail-qa/playwright fournit à chaque test une fixture qa qui le fait, et proxymail-qa/playwright/reporter enregistre les tests échoués et instables ainsi que les totaux dans l’exécution : voir Exécuter dans CI. Les autres lanceurs de tests peuvent envoyer eux-mêmes les résultats avec qa.recordTest({ name, status, durationMs, error }) et qa.recordTestTotals({ passed, failed, skipped, flaky }). Nouveau dans la 0.8.0.
Les appels échoués lèvent ProxyMailQAError avec le status HTTP, un code et un message lisible. Quand une attente ne trouve rien, le message indique aussi pourquoi, dans la mesure où Proxy Mail QA peut le savoir (error.hint), par exemple que le fournisseur d’e-mails de l’application n’envoie qu’aux adresses vérifiées, et error.refused liste les e-mails refusés car trop volumineux, au-delà d’une limite ou provenant d’un site avec lequel Proxy Mail QA ne fonctionne pas (refused[].site le nomme) :
import { ProxyMailQAError } from 'proxymail-qa';
try { await qa.waitForCode(address, { since: submitted });} catch (error) { if (error instanceof ProxyMailQAError && error.status === 404) console.log(error.hint); throw error;}Réessayer n’aidera pas tant que l’application ne peut pas envoyer à n’importe quelle adresse. Voir L’e-mail de test n’arrive pas ?
Retrouver un compte de test
Section intitulée « Retrouver un compte de test »Les tests et les agents peuvent revenir à un compte créé auparavant au lieu d’en créer un autre. Chaque filtre fourni doit correspondre ; les âges sont en années entières à ce jour, à partir de la date de naissance du persona, et search cherche dans l’adresse, le libellé, les notes, le nom et le nom d’utilisateur.
// An under-18 in the UK, made for the age-gate tests, with a saved password.const [teen] = await qa.listAddresses({ tags: ['age-gate'], country: 'GB', maxAge: 17, hasPassword: true });
// Record where an account is used, then sign in with it again later: the// account used on this site first, with its password (needs a key with// "Can read saved passwords").await qa.createAddress({ password: true, site: 'staging.example.com', environment: 'staging' });const account = await qa.signInAccount({ site: 'staging.example.com', tags: ['verified'] });Une question sur cette page ?
Posez votre question dans la langue de votre choix. La documentation peut y répondre tout de suite, ou vous pouvez l'envoyer à l'équipe Proxy Mail QA, qui répond en anglais.