Compétence Playwright Interactive
Utilisez une session js_repl Playwright persistante pour déboguer des applications web ou Electron locales, conservez les mêmes handles actifs au fil des itérations, et exécutez des tests fonctionnels et d'assurance qualité visuels sans redémarrer toute la chaîne d'outils sauf si la propriété du processus a changé.
Préconditions
js_repldoit être activé pour cette compétence.- Si
js_replest manquant, activez-le dans~/.codex/config.toml:
[features]
js_repl = true
- Vous pouvez également démarrer une nouvelle session avec
--enable js_repl(équivalent à-c features.js_repl=true). - Après avoir activé
js_repl, démarrez une nouvelle session Codex pour que la liste des outils se rafraîchisse. - Pour l'instant, exécutez ce flux de travail avec le sandboxing désactivé : démarrez Codex avec
--sandbox danger-full-access(ou la configuration équivalente poursandbox_mode=danger-full-access). C'est une exigence temporaire tandis que le supportjs_repl+ Playwright à l'intérieur du sandbox est encore en cours de finalisation. - Exécutez la configuration à partir du même répertoire de projet que celui que vous devez déboguer.
- Traitez
js_repl_resetcomme un outil de récupération, pas un nettoyage de routine. Réinitialiser le kernel détruit vos handles Playwright.
Configuration unique
test -f package.json || npm init -y
npm install playwright
# Web uniquement, pour Chromium en mode headed ou émulation mobile :
# npx playwright install chromium
# Electron uniquement, et seulement si l'espace de travail cible est l'application elle-même :
# npm install --save-dev electron
node -e "import('playwright').then(() => console.log('playwright import ok')).catch((error) => { console.error(error); process.exit(1); })"
Si vous basculez vers un autre espace de travail plus tard, répétez la configuration là-bas.
Flux de travail principal
- Rédigez un bref inventaire d'assurance qualité avant de tester :
- Construisez l'inventaire à partir de trois sources : les exigences demandées par l'utilisateur, les fonctionnalités ou comportements visibles par l'utilisateur que vous avez réellement implémentés, et les affirmations que vous vous attendez à faire dans la réponse finale.
- Tout ce qui apparaît dans l'une de ces trois sources doit correspondre à au moins une vérification d'assurance qualité avant la validation.
- Listez les affirmations visibles par l'utilisateur sur lesquelles vous avez l'intention de vous engager.
- Listez tous les contrôles significatifs visibles par l'utilisateur, les commutateurs de mode ou les comportements interactifs implémentés.
- Listez les changements d'état ou les changements d'affichage que chaque contrôle ou comportement implémenté peut causer.
- Utilisez ceci comme la liste de couverture partagée pour les tests d'assurance qualité fonctionnels et visuels.
- Pour chaque affirmation ou paire contrôle-état, notez la vérification fonctionnelle prévue, l'état spécifique où la vérification visuelle doit se produire, et les preuves que vous vous attendez à capturer.
- Si une exigence est visuellement centrale mais subjective, convertissez-la en vérification d'assurance qualité observable plutôt que de la laisser implicite.
- Ajoutez au moins 2 scénarios exploratoires ou hors du chemin heureux qui pourraient exposer des comportements fragiles.
- Exécutez la cellule d'amorçage une fois.
- Démarrez ou confirmez tout serveur de développement requis dans une session TTY persistante.
- Lancez le runtime correct et continuez à réutiliser les mêmes handles Playwright.
- Après chaque modification de code, rechargez pour les modifications du renderer uniquement ou relancez pour les changements de processus principal/démarrage.
- Exécutez l'assurance qualité fonctionnelle avec une entrée utilisateur normale.
- Exécutez une passe d'assurance qualité visuelle séparée.
- Vérifiez l'ajustement de la fenêtre d'affichage et capturez les captures d'écran nécessaires pour soutenir vos affirmations.
- Nettoyez la session Playwright uniquement lorsque la tâche est réellement terminée.
Amorçage (Exécution unique)
var chromium;
var electronLauncher;
var browser;
var context;
var page;
var mobileContext;
var mobilePage;
var electronApp;
var appWindow;
try {
({ chromium, _electron: electronLauncher } = await import("playwright"));
console.log("Playwright loaded");
} catch (error) {
throw new Error(
`Could not load playwright from the current js_repl cwd. Run the setup commands from this workspace first. Original error: ${error}`
);
}
Règles de liaison :
- Utilisez
varpour les handles Playwright partagés de niveau supérieur car les cellulesjs_replultérieures les réutilisent. - Les cellules de configuration ci-dessous sont intentionnellement des chemins heureux courts. Si un handle semble obsolète, définissez cette liaison sur
undefinedet réexécutez la cellule au lieu d'ajouter une logique de récupération partout. - Préférez un handle nommé par surface (
page,mobilePage,appWindow) plutôt que de redécouvrir les pages à partir du contexte.
Helpers web partagés :
var resetWebHandles = function () {
context = undefined;
page = undefined;
mobileContext = undefined;
mobilePage = undefined;
};
var ensureWebBrowser = async function () {
if (browser && !browser.isConnected()) {
browser = undefined;
resetWebHandles();
}
browser ??= await chromium.launch({ headless: false });
return browser;
};
var reloadWebContexts = async function () {
for (const currentContext of [context, mobileContext]) {
if (!currentContext) continue;
for (const p of currentContext.pages()) {
await p.reload({ waitUntil: "domcontentloaded" });
}
}
console.log("Reloaded existing web tabs");
};
Choisissez le mode de session
Pour les applications web, utilisez une fenêtre d'affichage explicite par défaut et traitez le mode de fenêtre native comme une passe de validation séparée.
- Utilisez une fenêtre d'affichage explicite pour l'itération de routine, les vérifications de point d'arrêt, les captures d'écran reproductibles, les différences d'instantanés et la localisation assistée par modèle. C'est la valeur par défaut car elle est stable sur les machines et évite la variabilité du gestionnaire de fenêtres hôte.
- Lorsque vous avez besoin d'un comportement haute résolution déterministe, conservez la fenêtre d'affichage explicite et ajoutez
deviceScaleFactorplutôt que de basculer directement en mode fenêtre native. - Utilisez le mode fenêtre native (
viewport: null) pour une passe headed séparée lorsque vous devez valider la taille de la fenêtre lancée, le comportement DPI au niveau du système d'exploitation, les interactions de chrome du navigateur ou les bugs qui peuvent dépendre de la configuration d'affichage hôte. - Pour Electron, supposez un comportement de fenêtre native en permanence. Electron se lance via Playwright avec
noDefaultViewport, donc traitez-le comme une véritable fenêtre de bureau et vérifiez la taille et la mise en page au lancement avant de redimensionner quoi que ce soit. - Lorsque la validation dépend à la fois des points d'arrêt de disposition et du comportement du bureau réel, exécutez les deux passes : fenêtre d'affichage explicite d'abord pour une assurance qualité déterministe, puis la validation fenêtre native pour les vérifications finales spécifiques à l'environnement.
- Traitez le changement de modes comme une réinitialisation de contexte. Ne réutilisez pas un contexte émulé de fenêtre d'affichage pour une passe fenêtre native ou vice versa ; fermez l'ancienne
pageetcontext, puis créez-en une nouvelle pour le nouveau mode.
Démarrer ou réutiliser une session web
Les sessions web de bureau et mobile partagent le même browser, les mêmes helpers et le même flux d'assurance qualité. La principale différence est la paire de contexte et de page que vous créez.
Contexte web de bureau
Définissez TARGET_URL sur l'application que vous déboguez. Pour les serveurs locaux, préférez 127.0.0.1 à localhost.
var TARGET_URL = "http://127.0.0.1:3000";
if (page?.isClosed()) page = undefined;
await ensureWebBrowser();
context ??= await browser.newContext({
viewport: { width: 1600, height: 900 },
});
page ??= await context.newPage();
await page.goto(TARGET_URL, { waitUntil: "domcontentloaded" });
console.log("Loaded:", await page.title());
Si context ou page est obsolète, définissez context = page = undefined et réexécutez la cellule.
Contexte web mobile
Réutilisez TARGET_URL s'il existe déjà ; sinon, définissez une cible mobile directement.
var MOBILE_TARGET_URL = typeof TARGET_URL === "string"
? TARGET_URL
: "http://127.0.0.1:3000";
if (mobilePage?.isClosed()) mobilePage = undefined;
await ensureWebBrowser();
mobileContext ??= await browser.newContext({
viewport: { width: 390, height: 844 },
isMobile: true,
hasTouch: true,
});
mobilePage ??= await mobileContext.newPage();
await mobilePage.goto(MOBILE_TARGET_URL, { waitUntil: "domcontentloaded" });
console.log("Loaded mobile:", await mobilePage.title());
Si mobileContext ou mobilePage est obsolète, définissez mobileContext = mobilePage = undefined et réexécutez la cellule.
Passe web en mode fenêtre native
var TARGET_URL = "http://127.0.0.1:3000";
await ensureWebBrowser();
await page?.close().catch(() => {});
await context?.close().catch(() => {});
page = undefined;
context = undefined;
browser ??= await chromium.launch({ headless: false });
context = await browser.newContext({ viewport: null });
page = await context.newPage();
await page.goto(TARGET_URL, { waitUntil: "domcontentloaded" });
console.log("Loaded native window:", await page.title());
Démarrer ou réutiliser une session Electron
Définissez ELECTRON_ENTRY sur . lorsque l'espace de travail actuel est l'application Electron et que package.json pointe main vers le fichier d'entrée correct. Si vous devez cibler un fichier de processus principal spécifique directement, utilisez un chemin tel que ./main.js à la place.
var ELECTRON_ENTRY = ".";
if (appWindow?.isClosed()) appWindow = undefined;
if (!appWindow && electronApp) {
await electronApp.close().catch(() => {});
electronApp = undefined;
}
electronApp ??= await electronLauncher.launch({
args: [ELECTRON_ENTRY],
});
appWindow ??= await electronApp.firstWindow();
console.log("Loaded Electron window:", await appWindow.title());
Si js_repl n'est pas déjà exécuté à partir de l'espace de travail de l'application Electron, passez cwd explicitement lors du lancement.
Si le processus de l'application semble obsolète, définissez electronApp = appWindow = undefined et réexécutez la cellule.
Si vous avez déjà une session Electron mais avez besoin d'un processus nouveau après un changement de processus principal, preload ou démarrage, utilisez la cellule de redémarrage dans la section suivante au lieu de réexécuter celle-ci.
Réutiliser les sessions pendant l'itération
Gardez la même session active chaque fois que vous le pouvez.
Rechargement du renderer web :
await reloadWebContexts();
Rechargement du renderer Electron uniquement :
await appWindow.reload({ waitUntil: "domcontentloaded" });
console.log("Reloaded Electron window");
Redémarrage d'Electron après les changements de processus principal, preload ou démarrage :
await electronApp.close().catch(() => {});
electronApp = undefined;
appWindow = undefined;
electronApp = await electronLauncher.launch({
args: [ELECTRON_ENTRY],
});
appWindow = await electronApp.firstWindow();
console.log("Relaunched Electron window:", await appWindow.title());
Si votre lancement nécessite un cwd explicite, incluez le même cwd ici.
Posture par défaut :
- Gardez chaque cellule
js_replcourte et axée sur une rafale d'interaction. - Réutilisez les liaisons de niveau supérieur existantes (
browser,context,page,electronApp,appWindow) au lieu de les redéclarer. - Si vous avez besoin d'isolation, créez une nouvelle page ou un nouveau contexte au sein du même navigateur.
- Pour Electron, utilisez
electronApp.evaluate(...)uniquement pour l'inspection du processus principal ou les diagnostics spécialisés. - Corrigez les erreurs de helper sur place ; ne réinitialisez pas le REPL sauf si le kernel est réellement cassé.
Listes de contrôle
Boucle de session
- Amorcez
js_replune fois, puis gardez les mêmes handles Playwright actifs au fil des itérations. - Lancez le runtime cible à partir de l'espace de travail actuel.
- Effectuez la modification de code.
- Rechargez ou relancez en utilisant le chemin correct pour ce changement.
- Mettez à jour l'inventaire d'assurance qualité partagé si l'exploration révèle un contrôle, un état ou une affirmation visible supplémentaire.
- Réexécutez l'assurance qualité fonctionnelle.
- Réexécutez l'assurance qualité visuelle.
- Capturez les artefacts finaux uniquement après que l'état actuel soit celui que vous évaluez.
Décision de rechargement
- Changement du renderer uniquement : rechargez la page existante ou la fenêtre Electron.
- Changement de processus principal, preload ou démarrage : relancez Electron.
- Nouvelle incertitude sur la propriété du processus ou le code de démarrage : relancez plutôt que de deviner.
Assurance qualité fonctionnelle
- Utilisez les contrôles utilisateur réels pour la validation : clavier, souris, clic, toucher ou API d'entrée Playwright équivalentes.
- Vérifiez au moins un flux critique de bout en bout.
- Confirmez le résultat visible de ce flux, pas seulement l'état interne.
- Pour les applications en temps réel ou intensives en animation, vérifiez le comportement sous le timing d'interaction réel.
- Travaillez à travers l'inventaire d'assurance qualité partagé plutôt que des vérifications ponctuelles ad hoc.
- Couvrez tous les contrôles visibles évidents au moins une fois avant la validation, pas seulement le chemin heureux principal.
- Pour les contrôles réversibles ou les bascules avec état dans l'inventaire, testez le cycle complet : état initial, état modifié et retour à l'état initial.
- Après que les vérifications scriptées réussissent, faites une passe exploratoire courte en utilisant une entrée normale pendant 30-90 secondes au lieu de suivre seulement le chemin prévu.
- Si la passe exploratoire révèle un nouvel état, contrôle ou affirmation, ajoutez-le à l'inventaire d'assurance qualité partagé et couvrez-le avant la validation.
page.evaluate(...)etelectronApp.evaluate(...)peuvent inspecter ou organiser l'état, mais ils ne comptent pas comme entrée de validation.
Assurance qualité visuelle
- Traitez l'assurance qualité visuelle comme distincte de l'assurance qualité fonctionnelle.
- Utilisez le même inventaire d'assurance qualité partagé défini avant les tests et mis à jour lors des tests ; ne commencez pas la couverture visuelle à partir d'une liste implicite différente.
- Reformulez les affirmations visibles par l'utilisateur et vérifiez chacune explicitement ; ne supposez pas qu'une passe fonctionnelle prouve une affirmation visuelle.
- Une affirmation visible par l'utilisateur ne sera pas validée tant qu'elle n'aura pas été inspectée dans l'état spécifique où elle est censée être perçue.
- Inspectez la fenêtre d'affichage initiale avant de faire défiler.
- Confirmez que la vue initiale soutient visiblement les affirmations principales de l'interface ; si un élément promis essentiel n'est pas clairement perceptible là, traitez cela comme un bug.
- Inspectez toutes les régions visibles requises, pas seulement la surface d'interaction principale.
- Inspectez les états et les modes déjà énumérés dans l'inventaire d'assurance qualité partagé, y compris au moins un état significatif post-interaction lorsque la tâche est interactive.
- Si le mouvement ou les transitions font partie de l'expérience, inspectez au moins un état en transition en plus des points finaux établis.
- Si les étiquettes, les superpositions, les annotations, les guides ou les surbrillances sont destinés à suivre le contenu changeant, vérifiez cette relation après le changement d'état pertinent.
- Pour les visuels dynamiques ou dépendant de l'interaction, inspectez assez longtemps pour juger la stabilité, la superposition et la lisibilité ; ne vous fiez pas à une seule capture d'écran pour la validation.
- Pour les interfaces qui peuvent devenir plus denses après le chargement ou l'interaction, inspectez l'état réaliste le plus dense que vous puissiez atteindre pendant l'assurance qualité, pas seulement l'état vide, de chargement ou effondré.
- Si le produit a une taille de fenêtre d'affichage ou de fenêtre minimale prise en charge définie, exécutez une passe d'assurance qualité visuelle séparée là-bas ; sinon, choisissez une taille plus petite mais toujours réaliste et inspectez-la explicitement.
- Distinguez la présence de la mise en œuvre : si une affordance prévue est techniquement présente mais pas clairement perceptible en raison d'un contraste faible, d'une occlusion, d'un écrêtage ou d'une instabilité, traitez cela comme un échec visuel.
- Si une région visible requise est écrêtée, coupée, obscurcie ou poussée hors de la fenêtre d'affichage dans l'état que vous évaluez, traitez cela comme un bug même si les métriques de défilement au niveau de la page semblent acceptables.
- Cherchez l'écrêtage, le débordement, la distorsion, le déséquilibre de la disposition, l'espacement incohérent, les problèmes d'alignement, le texte illisible, le contraste faible, la superposition cassée et les états de mouvement maladroits.
- Jugez la qualité esthétique aussi bien que la correction. L'interface utilisateur devrait sembler intentionnelle, cohérente et visuellement agréable pour la tâche.
- Préférez les captures d'écran de fenêtre d'affichage pour la validation. Utilisez les captures de page complète uniquement comme artefacts de débogage secondaires, et capturez une capture d'écran ciblée lorsqu'une région a besoin d'une inspection plus proche.
- Si le mouvement rend une capture d'écran ambiguë, attendez que l'interface utilisateur se stabilise, puis capturez l'image que vous évaluez réellement.
- Avant la validation, posez-vous explicitement : quelle partie visible de cette interface n'ai-je pas encore inspectée de près ?
- Avant la validation, posez-vous explicitement : quel défaut visible embarrasserait le plus ce résultat si l'utilisateur le regardait de près ?
Validation
- Le chemin fonctionnel a réussi avec une entrée utilisateur normale.
- La couverture est explicite par rapport à l'inventaire d'assurance qualité partagé : notez les exigences, les fonctionnalités implémentées, les contrôles, les états et les affirmations qui ont été exercés, et mettez en avant les exclusions intentionnelles.
- La passe d'assurance qualité visuelle couvrait l'interface entière pertinente.
- Chaque affirmation visible par l'utilisateur a une vérification visuelle correspondante et un artefact de capture d'écran examiné à partir de l'état et de la fenêtre d'affichage ou de la taille de fenêtre où cette affirmation compte.
- Les vérifications d'ajustement de fenêtre d'affichage ont réussi pour la vue initiale prévue et toute taille de fenêtre d'affichage ou de fenêtre minimale prise en charge requise.
- Si le produit se lance dans une fenêtre, la taille, le placement et la disposition initiale au lancement ont été vérifiés avant tout redimensionnement ou repositionnement manuel.
- L'interface utilisateur n'est pas seulement fonctionnelle ; elle est visuellement cohérente et pas esthétiquement faible pour la tâche.
- La correction fonctionnelle, l'ajustement de fenêtre d'affichage et la qualité visuelle doivent chacun réussir de manière indépendante ; l'un n'implique pas les autres.
- Une passe exploratoire courte a été complétée pour les produits interactifs, et la réponse mentionne ce que cette passe couvrait.
- Si la revue de capture d'écran et les vérifications numériques ont divergé à un moment quelconque, le désaccord a été enquêté avant la validation ; l'écrêtage visible dans les captures d'écran est un échec à résoudre, pas quelque chose que les métriques peuvent contourner.
- Inclure une brève confirmation négative des principales classes de défauts que vous avez vérifiées et n'avez pas trouvées.
- Le nettoyage a été exécuté, ou vous avez intentionnellement gardé la session active pour un travail supplémentaire.
Exemples de capture d'écran
Si vous prévoyez d'émettre une capture d'écran via codex.emitImage(...), utilisez les chemins normalisés CSS dans la section suivante par défaut. Ce sont les exemples canoniques pour les captures d'écran qui seront interprétées par le modèle ou utilisées pour les actions de suivi basées sur les coordonnées. Conservez les captures brutes comme une exception pour le débogage sensible à la fidélité uniquement ; les exemples d'exception bruts apparaissent après les conseils de normalisation.
Captures d'écran liées au modèle (par défaut)
Si vous allez émettre une capture d'écran avec codex.emitImage(...) pour l'interprétation du modèle, normalisez-la en pixels CSS pour la région exacte que vous avez capturée avant d'émettre. Cela maintient les coordonnées retournées alignées avec les pixels CSS Playwright si la réponse est ensuite utilisée pour cliquer, et réduit également la taille de la charge utile d'image et le coût des tokens du modèle.
N'émettez pas les captures d'écran brutes en mode fenêtre native par défaut. Ignorez la normalisation uniquement lorsque vous avez explicitement besoin de fidélité aux pixels de périphérique, comme le débogage d'artefacts Retina ou DPI, l'inspection du rendu précis au pixel ou un autre cas où la fidélité aux pixels compte plus que la taille de la charge utile. Pour l'inspection locale uniquement qui ne sera pas émise au modèle, la capture brute est acceptable.
Ne supposez pas que page.screenshot({ scale: "css" }) est suffisant en mode fenêtre native (viewport: null). Dans Chromium sur macOS avec écran Retina, les captures d'écran de fenêtre native headed peuvent toujours revenir à la taille des pixels de périphérique même si scale: "css" est demandé. La même réserve s'applique aux fenêtres Electron lancées via Playwright car Electron s'exécute avec noDefaultViewport, et appWindow.screenshot({ scale: "css" }) peut toujours renvoyer une sortie en pixels de périphérique.
Utilisez des chemins de normalisation séparés pour les pages web et les fenêtres Electron :
- Web : préférez
page.screenshot({ scale: "css" })directement. Si Chromium en mode fenêtre native retourne toujours une sortie en pixels de périphérique, redimensionnez à l'intérieur de la page actuelle avec canvas ; aucune page de scratch n'est requise. - Electron : n'utilisez pas
appWindow.context().newPage()ouelectronApp.context().newPage()comme page de scratch. Les contextes Electron ne supportent pas ce chemin de manière fiable. Capturez dans le processus principal avecBrowserWindow.capturePage(...), redimensionnez avecnativeImage.resize(...), et émettez ces octets directement.
Helpers partagés et conventions :
var emitJpeg = async function (bytes) {
await codex.emitImage({
bytes,
mimeType: "image/jpeg",
detail: "original",
});
};
var emitWebJpeg = async function (surface, options = {}) {
await emitJpeg(await surface.screenshot({
type: "jpeg",
quality: 85,
scale: "css",
...options,
}));
};
var clickCssPoint = async function ({ surface, x, y, clip }) {
await surface.mouse.click(
clip ? clip.x + x : x,
clip ? clip.y + y : y
);
};
var tapCssPoint = async function ({ page, x, y, clip }) {
await page.touchscreen.tap(
clip ? clip.x + x : x,
clip ? clip.y + y : y
);
};
- Utilisez
pageoumobilePagepour le web, ouappWindowpour Electron, commesurface. - Traitez
clipcomme des pixels CSS degetBoundingClientRect()dans le renderer. - Préférez JPEG à
quality: 85sauf si la fidélité sans perte est spécifiquement requise. - Pour les captures d'image complètes, utilisez
{ x, y }retourné directement. - Pour les captures écrêtées, rajoutez l'origine du clip lors du clic.
Normalisation CSS web
Chemin web préféré pour les contextes de fenêtre d'affichage explicite, et souvent pour le web en général :
await emitWebJpeg(page);
Le web mobile utilise le même chemin ; remplacez mobilePage par page :
await emitWebJpeg(mobilePage);
Si le modèle retourne { x, y }, cliquez directement dessus :
await clickCssPoint({ surface: page, x, y });
Chemin de clic web mobile :
await tapCssPoint({ page: mobilePage, x, y });
Pour les captures d'écran web clip ou les captures d'éléments dans ce chemin normal, scale: "css" fonctionne généralement directement. Rajoutez l'origine de la région lors du clic.
await emitWebJpeg(page, { clip })await emitWebJpeg(mobilePage, { clip })await clickCssPoint({ surface: page, clip, x, y })await tapCssPoint({ page: mobilePage, clip, x, y })await clickCssPoint({ surface: page, clip: box, x, y })aprèsconst box = await locator.boundingBox()
Secours web en mode fenêtre native lorsque scale: "css" retourne toujours à la taille des pixels de périphérique :
var emitWebScreenshotCssScaled = async function ({ page, clip, quality = 0.85 } = {}) {
var NodeBuffer = (await import("node:buffer")).Buffer;
const target = clip
? { width: clip.width, height: clip.height }
: await page.evaluate(() => ({
width: window.innerWidth,
height: window.innerHeight,
}));
const screenshotBuffer = await page.screenshot({
type: "png",
...(clip ? { clip } : {}),
});
const bytes = await page.evaluate(
async ({ imageBase64, targetWidth, targetHeight, quality }) => {
const image = new Image();
image.src = `data:image/png;base64,${imageBase64}`;
await image.decode();
const canvas = document.createElement("canvas");
canvas.width = targetWidth;
canvas.height = targetHeight;
const ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = true;
ctx.drawImage(image, 0, 0, targetWidth, targetHeight);
const blob = await new Promise((resolve) =>
canvas.toBlob(resolve, "image/jpeg", quality)
);
return new Uint8Array(await blob.arrayBuffer());
},
{
imageBase64: NodeBuffer.from(screenshotBuffer).toString("base64"),
targetWidth: target.width,
targetHeight: target.height,
quality,
}
);
await emitJpeg(bytes);
};
Pour une capture de secours de fenêtre d'affichage complète, trait