Script de navigateur
Vous pouvez utiliser des scripts basés sur Node.js et les API de Selenium pour activer les tests de navigateur sur Instana. Grâce aux variables globales prédéfinies et aux API de test de navigateur d' Instana, vous pouvez créer et exécuter un script de test de navigateur qui simule l'interaction de l'utilisateur dans un navigateur Web.
Instana références de scripts de navigateur
Pour créer un test de script de navigateur afin d'activer le test de navigateur, vous avez besoin de variables globales et de modules.
Variables globales
Les variables globales prédéfinies décrites dans le tableau accélèrent le développement de script.
| Nom | Type | Informations |
|---|---|---|
$driver |
@types/selenium-webdriver |
Définitions de types pour Selenium WebDriverJS. |
$browser |
ThenableWebDriver |
Encapsuleur thenable d'un client WebDriver , qui permet de contrôler un navigateur et d'appeler les méthodes WebDriver directement sur Promise<WebDriver> sans appeler then. |
$secure |
Dict<string> |
Variable permettant d'accéder aux données d'identification de l'utilisateur. |
$util |
UserUtil |
Fonctions d'utilitaire, telles que $util.secrets. |
$network |
Network |
Utilitaire réseau permettant de configurer un proxy. |
$synthetic |
Dict<string> |
Variables d'environnement. |
$attributes |
UserInsight |
Variable permettant d'ajouter et de manipuler des attributs personnalisés. |
Les utilisations et la déclaration des variables globales prédéfinies sont les suivantes:
$synthétique
Vous pouvez utiliser $synthetic.var_name pour accéder aux variables d'environnement et d'exécution dans le script. Certaines variables prédéfinies sont les suivantes:
$synthetic.LOCATIONou$synthetic.pop: Permet d'accéder à l'étiquette d'emplacement, qui correspond à la première partie decontroller.locationla variable.$synthetic.TEST_IDou$synthetic.id: Permet d'accéder à l'identifiant du test synthétique en cours d'exécution.$synthetic.TEST_NAMEou$synthetic.testName: Permet d'accéder à l'étiquette du test synthétique en cours d'exécution.$synthetic.TIME_ZONEou$synthetic.timeZone: Permet d'accéder au fuseau horaire de l' PoP e sur lequel s'exécute le test synthétique.$synthetic.JOB_IDou$synthetic.taskId: Permet d'accéder à l'identifiant d'une tâche ou d'un travail de lecture; il s'agit également de l'identifiant du résultat.$synthetic.testType: Permet d'accéder au type de test.$synthetic.description: Permet d'accéder à la description du test.$synthetic.labels.xxx: Permet d'accéder aux propriétés personnalisées définies dans lacustomPropertiessection de la définition du test.
Vous pouvez également définir des variables d'environnement personnalisées dans le values.yaml fichier du graphique « helm ». Voir l'exemple suivant :
controller:
customProperties: "tag1=value1;tag2=value2"
Pour accéder à ces variables personnalisées, utilisez $synthetic.tag1 et $synthetic.tag2 dans le script.
Pour accéder aux propriétés personnalisées définies dans la section customProperties de la définition de test et à la valeur de propriété dans le script, utilisez $synthetic.labels.xxx. Voir l'exemple suivant :
// to print test custom property defined in customProperties of test definition
console.info('Test custom property -> Team: ' + $synthetic.labels.Team);
console.info('Test custom property -> Purpose: ' + $synthetic.labels.Purpose);
$secure
Le script de navigateur « Instana » prend en charge les identifiants utilisateur afin de stocker les informations confidentielles en toute sécurité, telles que le nom d'utilisateur et le mot de passe. Vous pouvez utiliser $secure.MY_SECURE_CREDENTIAL pour faire référence à des données d'identification sécurisées prédéfinies dans votre script.
Pour créer un identifiant avec l' API Synthetic Open, procédez comme suit :
- Assurez-vous que les droits d'accès sont correctement configurés.
- Utilisez Synthetic OpenAPI ou la commande synctl pour créer un identifiant en transmettant les
credentialNamevariablescredentialValueet.
Ensuite, dans votre script de navigateur, utilisez $secure.credentialName pour faire référence aux données d'identification créées, par exemple, $secure.password ou $secure.username.
$secure.credentialName format est pris en charge. Ce $secure[credentialName] format n'est pas pris en charge.$secure.credentialName) sont résolues avant l'exécution du script. Vous devez indiquer directement le nom des informations d'identification. Ne créez pas la référence de manière dynamique à l'aide de variables, de concaténation de chaînes ou eval()de, car ces méthodes ne sont pas prises en charge et ne sont pas évaluées.$attributs
Vous pouvez utiliser la variable $attributes pour ajouter ou obtenir des attributs personnalisés aux données de surveillance. Ces attributs personnalisés sont également stockés dans les résultats de surveillance avec les autres attributs de résultat de surveillance par défaut.
Instana La surveillance synthétique prend en charge les API suivantes pour la configuration d'attributs personnalisés :
$attributes.set(key, value): définit la clé ou la valeur.$attributes.get(key): Renvoie la valeur de la clé fournie.$attributes.getKeys(): renvoie un tableau de toutes les clés.$attributes.has(key): Renvoietruesi la clé existe.$attributes.unset(key): supprime la clé ou la valeur spécifiée.$attributes.unsetAll(): supprime toutes les données personnalisées.
L'exemple suivant illustre le $attributes API :
$attributes.set(xxx, yyy) // sets tag xxx to value yyy
let v = $attributes.get(xxx) // sets variable v to the value of tag xxx
$attributesAPI ` dans le script et les propriétés personnalisées définies dans customProperties la section de la définition du test sont accessibles dans les résultats du test via synthetic.tags la métrique; vous pouvez utiliser synthetic.tags cette métrique pour filtrer les résultats du test ou transmettre une valeur personnalisée à la charge utile personnalisée de Smart Alert.$network
Les API suivantes sont prises en charge dans la surveillance synthétique d' Instana pour la configuration d'un serveur proxy :
$network.setProxy(string proxy): Définit un serveur proxy à utiliser pour toutes les requêtes ( HTTP, HTTPS ).$network.setProxyForHttp(string proxy): Définit un serveur proxy à utiliser uniquement pour les requêtes HTTP.$network.setProxyForHttps(string proxy): définit un serveur proxy à utiliser uniquement pour les demandes HTTPs.$network.setProxyPAC(string pac, object auth): définit un serveur proxy via un script de configuration automatique de proxy (PAC).$network.setProxyAdvanced(object proxy): Définit la configuration du proxy en utilisant le format pris en charge par l'extension Chrome/ Firefox API pour la gestion du proxy.$network.clearProxy(): supprime la configuration de proxy.$network.getProxy(): Renvoie la configuration du proxy.
$util.secrets
Vous pouvez utiliser l' API$util.secrets de sécurité pour masquer les données sensibles.
Pour occulter les secrets des URL, utilisez la commande $util.secrets.setURLSecretsRegExps dans votre script. Voir l'exemple suivant :
// to redact 'key', 'password' and 'secret' query parameters in URL
$util.secrets.setURLSecretsRegExps([/key/i, /password/i, /secret/i]);
Une fois que vous avez appelé cette méthode API, l' URLhttps://example.com/accounts/status?key=mykey123&secret=mysecret est récupérée et affichée comme https://example.com/accounts/status?key=*&secret=* dans l'interface utilisateur de Instana.
Instana Synthetic supprime tous les en-têtes de requête et de réponse afin de rester en cohérence avec les conceptions actuelles d' Instana.
Par défaut, Instana ne collecte pas les en-têtes HTTP lorsqu'il trace les appels vers HTTP, afin d'éviter l'envoi de données sensibles.
Références sur l' API des tests de navigateurs
Les tests de navigateur prennent en charge les API basées sur Seleniumet plus de 30 autres API de test de navigateur étendu. Pour plus d'informations, consultez la documentation de API.
Rédaction d'un test de script pour navigateur
Vous pouvez écrire un test de script de navigateur pour les actions suivantes afin d'activer le test du navigateur. Les exemples de code montrent comment écrire un test de script de navigateur avec des API couramment utilisées. Pour plus d'informations sur l'exemple de code complet, voir la section Exemples de script .
Mise en œuvre des mesures de base
Les exemples de code suivants montrent comment écrire un test de script de navigateur pour implémenter les actions de base suivantes:
Préparer les en-têtes: pour personnaliser les en-têtes de demande avant d'accéder aux sites Web, utilisez la commande suivante:
await $browser.addHeader("test", "test"); await $browser.addHeaders({ "test1": "test1", "test2": "test2" }); await $browser.deleteHeaders(["test1", "test2"]); $browser.getHeaders().forEach((value, key) => console.log(">>>>>>>>>>>>>>>>>>>", "User customized headers: ", key + "=" + value));Contrôler l'accès: pour contrôler l'accès avec la liste de refus et d'autorisation, utilisez les commandes suivantes:
await $browser.addHostnamesToDenylist(["*google*", "*timeanddate*"]); await $browser.deleteHostnameFromDenylist("*timeanddate*"); await $browser.get("https://www.google.com");Lorsque vous effectuez une recherche sur Google après avoir exécuté ces commandes, vous verrez s'afficher le message
wwww.google.com is blocked.Ouvrir URL : pour ouvrir URL dans votre navigateur et accéder au site web, utilisez la commande suivante :
await $browser.get("http://www.bing.com");Définir le temps de veille: Pour définir l'option de veille dans le navigateur, utilisez la commande suivante. Le paramètre correspond à la durée, en millisecondes, de la mise en veille. Dans l'exemple suivant, le navigateur est en veille pendant 2 secondes.
await $browser.sleep(2000);Utilisez l'assertion avec le titre de la page: l'assertion est essentielle dans vos tests pour déterminer les résultats de test. Pour utiliser l'assertion avec un titre de page ou une source de page, utilisez le code suivant. Vous pouvez utiliser la bibliothèque
const assert = require("assert").strict;ou la bibliothèque d'assertion Chaiconst { assert } = require("chai");. Utilisezconsole.logpour vous connecter à la sortie de la console et au fichier journal de la console.const { assert } = require("chai"); await $browser.get("http://www.timeanddate.com"); let page = await $browser.getPageSource(); assert.isTrue(page.includes("<title>timeanddate.com</title>")); console.log(">>>>>>>>>>>>>>>>>>>", "Page title: ", await $browser.getTitle());Obtenir une session: pour obtenir des informations sur une session, utilisez la commande suivante:
let session = await $browser.getSession(); console.log(">>>>>>>>>>>>>>>>>>>", "Session id: ", session.getId());Agrandir la taille de la fenêtre: pour agrandir la taille de la fenêtre du navigateur, utilisez la commande suivante:
$browser.manage().window().maximize();Capture d'écran: Pour prendre une capture d'écran du navigateur, utilisez la commande suivante:
await $browser.takeScreenshot();
Rechercher et interagir avec les éléments d'une page
Attendre et rechercher un élément sur la page: vous pouvez utiliser les commandes du tableau suivant pour des recherches spécifiques:
Nom Utilisation $browser.waitForAndFindElement($driver.By.id("boxyear"), 1000)Attendre et rechercher l'élément par ID jusqu'à ce que l'élément soit visible ou que la valeur de délai d'attente soit atteinte $browser.findElements($driver.By.css('select'))Recherche de plusieurs éléments sur la page à l'aide du sélecteur CSS $browser.findElement($driver.By.linkText("About"))Recherchez l'élément par linkText. $browser.findElement($driver.By.xpath('//input[@value='f']'))Rechercher un élément par XPath. $browser.$('#boxyear')Rechercher un élément par ID. $browser.$$(".rd-box")Rechercher des éléments par classe. elem.$('h2').$('a').getText()Rechercher un élément par nom d'élément. L'exemple de code suivant montre comment attendre et trouver un élément sur la page:
// Find element by XPath and wait for the element until specified timeout value reached await $browser.waitForAndFindElement($driver.By.xpath(`//textarea[@inputmode='search']`), 10000); // Find element by id let elem = await $browser.waitForAndFindElement($driver.By.id("boxyear"), 1000); assert.equal("2022", await elem.getAttribute("value")); assert.equal("2022", await $browser.$('#boxyear').getAttribute("value")); // Find elements by CSS selector let selects = await $browser.findElements($driver.By.css('select')); assert.equal(2, selects.length); // Find elements by class let elems = await $browser.$$(".rd-box"); console.log(await elems[0].$('h2').$('a').getText());Rechercher des éléments et effectuer des actions : l'exemple suivant montre comment rechercher un élément à l'aide d 'un sélecteur CSS et envoyer des touches à l'aide d 'ActionSequence :
<input id="sb_form_q" name="q" type="search">let textarea = await $browser.waitForAndFindElement($driver.By.css('#sb_form_q'), 10000); // Actions to send keys await $browser .actions() .move({ origin: textarea }) .sendKeys("synthetic") .pause(5000) .sendKeys($driver.Key.ENTER) .perform();
Conditions d'application
Wait until title comes condition: pour appliquer l'attente jusqu'à ce que le titre vienne à la condition, utilisez la commande suivante:
await $browser.wait($driver.until.titleContains('Year 2022 Calendar'), 10000);Condition d'attente et d'interrogation jusqu'au délai d'attente: pour appliquer la condition d'attente et d'interrogation jusqu'au délai d'attente, utilisez les commandes suivantes:
Remarque : L'exemple suivant montre un appel de chaîne de promesses compatible avec une version antérieure. N'utilisez pas ce code dans les versions récentes d' JavaScript Utilisez plutôt les techniques modernes d' JavaScript asynchrone avec Async et Await.$browser.get("https://www.google.com"); $browser.wait(function () { return $browser.getTitle().then(function (title) {return title.includes("Google");}); }, 10000).then(function () { return $browser.findElement($driver.By.linkText("About")).click(); }).then(function () { return $browser.navigate().back(); });
Parcourir l'historique du navigateur
Revenir en arrière: pour revenir à la page précédemment consultée dans le navigateur, utilisez la commande suivante:
await $browser.navigate().back();Passer aux nouveaux onglets: pour obtenir le descripteur de fenêtre en cours et passer à une nouvelle fenêtre pour ouvrir une nouvelle page dans un nouvel onglet et revenir à la fenêtre d'origine, utilisez le code suivant:
console.log("Windows handle: ", await $browser.getWindowHandle(), "Current url: ", await $browser.getCurrentUrl()); let originalWindow = await $browser.getWindowHandle(); await $browser.switchTo().newWindow(); await $browser.get("http://www.timeanddate.com"); await $browser.switchTo().window(originalWindow); await $browser.takeScreenshot();
Déplacer la souris et envoyer des touches
Clic de souris: pour déplacer la souris, utilisez la commande suivante:
const element = await $browser.waitForAndFindElement(by, timeout); const out = $browser.actions().move({origin: element}).press().release().perform();Infobulle: pour survoler un élément du navigateur avec la souris, utilisez la commande suivante:
const element = await $browser.waitForAndFindElement(by, timeout); const out = $browser.actions().move({origin: element, duration: 2000}).perform();Touches d'envoi: Pour envoyer des touches, utilisez la commande suivante:
await $browser.actions().sendKeys("synthetic").perform();
Gestion des éléments Shadow DOM
Pour accéder aux éléments Shadow DOM, vous pouvez utiliser la fonction getShadowRoot() . La nouvelle méthode est prise en charge pour la charte PoP Helm 1.1.1 ou ultérieure. Vous devez mettre à niveau votre version Synthetic PoP vers 1.1.1 ou une version ultérieure pour activer la prise en charge de l'interaction Shadow DOM.
let shadowHost = await $browser.waitForAndFindElement(shadowHostSelector, timeout);
let shadowRoot = await shadowHost.getShadowRoot();
let element = await shadowRoot.findElement(elementSelector);
console.log("element text is:", await element.getText(), ", id is:", await element.getId());
Configuration d'un serveur proxy pour accéder à des sites web
Pour configurer un proxy pour les requêtes adressées à HTTP et HTTPS à l'aide des
$networkvariables$browserou, utilisez la commande suivante. Vous pouvez définir le proxy avecstringouurl.URL.await $network.clearProxy(); await $network.setProxyForHttp(proxyServer); website = await accessWebSite(); checkHostname("www.google.com.hk", website) await $network.clearProxy(); let testURL = new URL("http://" + sslProxyServer); await $network.setProxyForHttps(testURL); website = await accessWebSite(); checkHostname("www.google.com", website);Pour définir un proxy pour toutes les demandes avec la liste de contournement à filtrer, utilisez la commande suivante. Une fois que vous avez exécuté cette commande dans l'exemple suivant, aucun proxy n'est disponible pour www.google.com.
await $network.clearProxy(); await $network.setProxy(proxyServer, "www.google.com"); console.log(">>>>>>>>>>>>>>>>>>>", "Bypass list: ", $network.getProxy().rules.bypassList); website = await accessWebSite(); checkHostname("www.google.com.hk", website);
Suppression des informations confidentielles
Pour masquer les données sensibles dans l' URL, utilisez la commande suivante. Dans l'exemple suivant, l' URL e est masquée par "url": "https://www.bing.com/?mkt=zh-CN&toHttps=1&redig=*".
$util.secrets.setURLSecretsRegExps([/redig/i]);
console.log(">>>>>>>>>>>>>>>>>>>", "Access bing");
await $browser.get("http://cn.bing.com");
await $browser.wait($driver.until.titleContains('Bing'), 10000);
Gestion des modules
Vous pouvez importer des modules tiers, de base ou facultatifs dans votre script de test de navigateur.
Importation d'un module facultatif
L'exemple suivant illustre la commande d'importation du module d'assertion chai:
var assert = require('chai').assert;
Modules de base pris en charge
Instana Les tests de navigateur prennent en charge les modules principaux d' Node.js, disponibles sur Node.js v18 ou dans une version ultérieure.
process les opérations child_processd'écriture fs ou de suppression. Si vous utilisez ces API dans votre script de test ou dans des scripts intégrés, votre résultat de test risque d'échouer avec le message d'erreur suivant. N'utilisez donc pas ces API dans vos scripts de test.[SyntheticPoP] [ERROR] process.exit is not allowed. This API is blocked by playback engine.
Modules tiers pris en charge
Instana La surveillance synthétique prend en charge les modules tiers suivants :
- @aws-sdk/client-s3 3.626.0
- atob 2.1.2
- authentificateur 1.1.5
- 4.6.6 de base sur le FTP
- btoa 1.2.1
- chai 4.3.10
- couleurs 1.4.0
- consoleplusplus 1.4.4
- crypto-js 4.2.0
- faker 5.5.3
- obtenu 11.8.6
- joi 17.7.1
- js-yaml 4.1.0
- ldapauth-fork 5.0.2
- lodash 4.17.21
- instant 2.29.4
- 3.6.3 de net-snmp
- protocol-buffers 4.2.0
- q 1.5.1
- re2 1.18.3
- ssh2-sftp-client 7.2.3
- ssl-checker 2.0.7
- codage de texte 0.7.0
- 0.14.2 de seconde main
- 0.0.14 de totp-generator
- Cookie-difficile 4.1.3
- trait de soulignement 1.13.3
- url-parse 1.5.10
- urllib 2.41.0
- uuid 3.4.0
- valideur 13.7.0
- ws 7.5.10
- xml2js 0.5.0
Exemples de script
Les exemples suivants incluent la plupart des API de test de navigateur et un exemple de code complet.
Ces exemples sont fournis à des fins de démonstration uniquement. Vous trouverez les derniers exemples dans le dépôt public GitHub.
Exemple de code complet pour les actions
let click = async (message, by, timeout = 60000) => {
console.log(`Click on ${message} >> ${by} << `);
try {
const element = await $browser.waitForAndFindElement(by, timeout);
return $browser.actions().move({ origin: element }).click().perform();
} catch (err) {
console.error(`\ncatch(click): ${err.message}`);
throw err;
}
};
let hover = async (message, by, timeout = 60000) => {
console.log(`Hover on ${message} >> ${by} << `);
try {
const element = await $browser.waitForAndFindElement(by, timeout);
return $browser.actions().move({ origin: element, duration: 3000 }).perform();
} catch (err) {
console.error(`\ncatch(hover): ${err.message}`);
throw err;
}
};
(async function () {
console.log(">>>>>>>>>>>>>>>>>>>", "Access bing page");
await $browser.get("http://www.bing.com");
console.log(">>>>>>>>>>>>>>>>>>>", "Actions of search");
await hover("search button", $driver.By.xpath("//label[@id='search_icon']"), 10000);
await $browser.takeScreenshot();
await $browser.actions().sendKeys("synthetic").perform();
await click("search button", $driver.By.xpath("//label[@id='search_icon']"), 10000);
})();
Exemples de code complets pour les API
const { assert } = require("chai");
(async function () {
/**
* manipulate headers and retrieve all the customized headers
*/
await $browser.addHeader("test", "test");
await $browser.addHeaders({ "test1": "test1", "test2": "test2" });
await $browser.deleteHeaders(["test1", "test2"]);
$browser.getHeaders().forEach((value, key) => console.log(">>>>>>>>>>>>>>>>>>>", "User customized headers: ", key + "=" + value));
/**
* manipulate deny and allow list
*/
await $browser.addHostnamesToDenylist(["*google*", "*timeanddate*"]);
await $browser.deleteHostnameFromDenylist("*timeanddate*");
/**
* open url www.bing.com
* find element by class selector
* send keys to search Synthetic in the page
*/
console.log(">>>>>>>>>>>>>>>>>>>", "Access bing page");
await $browser.get("http://www.bing.com");
console.log(">>>>>>>>>>>>>>>>>>>", "Actions of search");
// Find element by XPath and wait for the element until specified timeout value reached
await $browser.waitForAndFindElement($driver.By.xpath(`//textarea[@inputmode='search']`), 10000);
// Sleep or pause to wait for specified milliseconds
await $browser.sleep(5000);
// Take a screenshot
await $browser.takeScreenshot();
// Actions to send keys
await $browser.actions().sendKeys("synthetic").sendKeys($driver.Key.ENTER).perform();
/* Find element by CSS selector and perform actions
let textarea = await $browser.waitForAndFindElement($driver.By.css('#sb_form_q'), 10000);
// Actions to send keys
await $browser
.actions()
.move({ origin: textarea })
.sendKeys("synthetic")
.pause(5000)
.sendKeys($driver.Key.ENTER)
.perform();
*/
/**
* samples of promise chain invocation, which is not recommended but backwards compatible
* samples of waiting and polling function with timeout value
*/
await $browser.deleteHostnameFromDenylist("*google*");
await $browser.get("https://www.google.com");
// Call the wait function.
await $browser.wait(function () {
return $browser.getTitle().then(function (title) {
return title.includes("Google");
});
//If the condition isn't satisfied within 10000 milliseconds (10 seconds), proceed anyway.
}, 10000).then(function () {
return $browser.findElement($driver.By.linkText("About")).click();
}).then(function () {
return $browser.navigate().back();
});
})();
Exemple de code complet pour un proxy
const url = require("url");
const URL = url.URL;
const { assert } = require("chai");
let website;
let proxyServer = "proxyHost:proxyPort";
let sslProxyServer = "username:password@proxyHost:proxyPort";
async function accessWebSite(url) {
console.log(">>>>>>>>>>>>>>>>>>>", "Proxy configuration is: ", JSON.stringify($network.getProxy()));
console.log(">>>>>>>>>>>>>>>>>>>", `Access website ${url}`);
await $browser.get(url);
await $browser.findElement($driver.By.name('q')).click();
await $browser.actions().sendKeys('synthetic').sendKeys($driver.Key.ENTER).perform();
await $browser.takeScreenshot();
return $browser.getCurrentUrl();
}
function checkHostname(hostname, website) {
let actualURL = new URL(website);
console.log(">>>>>>>>>>>>>>>>>>>", "Hostname is: ", actualURL.hostname);
assert.equal(actualURL.hostname, hostname);
}
(async function () {
/**
* samples of proxy configuration settings with await asynchronous
* using $network tools
*/
console.log(">>>>>>>>>>>>>>>>>>>", "Clear proxy configuration");
await $network.clearProxy();
console.log(">>>>>>>>>>>>>>>>>>>", "Set proxy configuration for HTTPS request with string");
await $network.setProxyForHttps(proxyServer);
website = await accessWebSite("https://www.bing.com");
checkHostname("www.bing.com", website)
console.log(">>>>>>>>>>>>>>>>>>>", "Clear proxy configuration");
await $network.clearProxy();
console.log(">>>>>>>>>>>>>>>>>>>", "Set proxy configuration for HTTPS request with URL object");
let testURL = new URL("http://" + sslProxyServer);
await $network.setProxyForHttps(testURL);
website = await accessWebSite("https://www.google.com");
checkHostname("www.google.com", website);
/**
* samples of proxy configuration settings with bypass list
*/
await $network.clearProxy();
await $network.setProxy(proxyServer, "www.google.com");
console.log(">>>>>>>>>>>>>>>>>>>", "Proxy config: ", $network.getProxy());
website = await accessWebSite("https://www.bing.com");
checkHostname("www.bing.com", website);
})();