Après un peu moins de 2 semaines de travail ; J'ai enfin atteint la première étape majeure pour Go-DOM.
Maintenant, le navigateur va télécharger et exécuter du JavaScript à distance lors de la construction de l'arborescence DOM.
Le projet est parti d'une idée folle ; sachant que Go et HTMX sont une pile qui gagne en popularité ;
Go dispose déjà de tous les outils dont vous avez besoin pour tester le rendu pur côté serveur. Mais lors de l'ajout d'une bibliothèque comme HTMX, le comportement de l'application est le résultat d'une chorégraphie entre le DOM initial ; les attributs sur les éléments interactifs ; les points de terminaison HTTP atteints et le contenu fourni par ces points de terminaison ; les en-têtes et le corps de la réponse. Pour vérifier le comportement du point de vue de l'utilisateur, vous avez besoin d'un navigateur ; ou du moins un harnais de test qui se comporte... un peu comme un navigateur.1
La recherche de "navigateur sans tête en go" n'a conduit qu'à des résultats suggérant d'utiliser un vrai navigateur en mode sans tête. Cette combinaison qui entraîne d’énormes frais généraux ; décourageant la boucle TDD rapide et efficace. S'appuyer sur un vrai navigateur vous ralentira au lieu de vous accélérera.2
L'idée est donc née ; écrire un navigateur sans tête en Go pur comme outil de test pour les applications Web ;
La première incertitude à résoudre était l'analyse du HTML ; ainsi que l'exécution du script. J'y suis parvenu assez rapidement ; dans les 2 jours pour résoudre ces deux problèmes. J'avais un analyseur HTML très rudimentaire ; ainsi que j'avais intégré la v8 dans la base de code3 et rendu les objets Go accessibles au code JavaScript.
L'analyseur HTML a ensuite été supprimé, car go x/net/html implémente déjà un analyseur HTML ; traitant de toutes les bizarreries de l'analyse HTML. Analyser un document bien formé n'est pas un problème très difficile à résoudre. Il s'agit de gérer gracieusement le HTML mal formé là où cela devient délicat.
Après un certain temps, j'ai également réussi à faire en sorte que l'exécution du script inline s'exécute au bon moment ; c'est-à-dire que le script s'exécute lorsque l'élément est réellement connecté au DOM, et non après que le HTML complet a été analysé.
Après avoir pu traiter un document HTML avec un script en ligne ; l'étape suivante consistait à télécharger les scripts à partir des sources. Cela nécessitait d'intégrer une couche HTTP ; de telle sorte que le navigateur récupère lui-même le contenu ; plutôt que d'être nourri de contenu.
Le http.Client vous permet également de contrôler la couche de transport réelle à l'aide de l'interface http.RoundTripper. Normalement, vous démarrez un serveur ; qui écoutera les requêtes sur un port TCP. Dans ce contexte, TCP sert de couche de transport ; mais n'est en soi pas pertinent pour le traitement des requêtes HTTP. En raison de la simplicité de la pile HTTP standard dans Go ; un serveur HTTP entier est représenté par une seule fonction, func Handle(http.ResponseWriter, *http.Request).
Le navigateur sans tête peut contourner complètement la surcharge de la pile TCP et appeler cette fonction directement à l'aide d'un RoundTripper personnalisé.
Le navigateur peut désormais effectuer des requêtes HTTP, mais le code du navigateur lui-même ignore le fait que la couche HTTP est contournée. Et avec cela est venue la possibilité de télécharger le script pendant l'analyse DOM.
Explorons un test simple, tel qu'il apparaît maintenant dans la base de code (le code utilise Ginkgo et Gomega, une combinaison quelque peu négligée à mon humble avis)
Tout d'abord, le test crée un gestionnaire HTTP simple qui dessert deux points de terminaison, /index.html et /js/script.js.
It("Should download and execute script from script tags", func() { // Setup a server with test content server := http.NewServeMux() server.HandleFunc( "GET /index.html", func(res http.ResponseWriter, req *http.Request) { res.Write( []byte( `<html><head><script src="/js/script.js"></script></head><body>Hello, World!</body>`, ), ) }, ) server.HandleFunc( "GET /js/script.js", func(res http.ResponseWriter, req *http.Request) { res.Header().Add("Content-Type", "text/javascript") res.Write([]byte(`var scriptLoaded = true`)) }, ) // ...
L'intention ici est simplement de vérifier que le script est exécuté. Pour ce faire, le script produit un effet secondaire observable : il définit une valeur dans une portée globale.
Pour vérifier que le script a été exécuté, il suffit d'examiner la portée globale, ce qui se fait en exécutant du JavaScript ad hoc à partir du test lui-même ; vérifier le résultat de l'expression.
Le code pour créer le navigateur, charger le fichier d'index et vérifier l'effet secondaire observé
browser := ctx.NewBrowserFromHandler(server) Expect(browser.OpenWindow("/index.html")).Error().ToNot(HaveOccurred()) Expect(ctx.RunTestScript("window.scriptLoaded")).To(BeTrue())
L'exécution des tests est également assez rapide. La partie de la suite de tests impliquant l'exécution de JavaScript comprend actuellement 32 tests exécutés en 23 millisecondes.
Comme le projet a été initialement conçu en essayant de vérifier une application HTMX, un prochain objectif raisonnable est de prendre en charge uniquement ce cas. Une application HTMX simple avec un bouton et un compteur, qui augmente lorsque le bouton est enfoncé.
Ensuite, une interaction utilisateur plus avancée ; gestion appropriée du formulaire, par exemple, ligne de saisie (par exemple, appuyer sur enter dans un champ pour soumettre le formulaire. Cela implique généralement également une sorte de redirection d'URL ; ce qui entraîne le besoin d'un objet d'historique, etc. .
Avec la possibilité de contrôler la couche de transport ; nous pouvons proposer des tests dotés de capacités uniques ; nous pouvons simuler des sites externes dont dépendrait le système au moment de l'exécution. Par exemple, pour un nom d'hôte donné, le test pourrait fournir un autre gestionnaire HTTP Go simulant le comportement.
L'exemple le plus évident est l'utilisation de fournisseurs d'identité externes. Le test pourrait simuler le comportement d'un flux de connexion ; ne pas avoir à vous forcer à créer des comptes factices dans un système externe, à avoir des échecs de tests en raison de pannes dans un système externe, ou simplement à être incapable d'automatiser le processus du tout à cause de 2FA ou d'un Captcha introduit par le fournisseur d'identité.
Un autre cas d'utilisation est l'utilisation de bibliothèques riches en API, comme les bibliothèques de cartes, qui entraînent un coût d'utilisation. Maquette du site externe pour ne pas recevoir de facture supplémentaire pour l'exécution de votre suite de tests.
Créer une implémentation 100 % conforme aux normes Whatwg est tout un effort ; Je n'ai pas entièrement compris la portée jusqu'à ce que je commence à lire certaines parties des spécifications Whatwg. Le but est de créer un outil d'aide à l'écriture de tests pour les applications web. La compatibilité totale est un objectif à long terme ; mais jusqu'à ce que le projet ait atteint un certain niveau d'utilisabilité, vais-je commencer à combler les trous.
Pour cette raison ; les fonctionnalités qui sont plus susceptibles d’être utilisées dans des applications réelles sont plus susceptibles d’être prioritaires. Une demande de fonctionnalité pointant vers un test réel donnant un résultat erroné sera probablement priorisée. Une demande de fonctionnalité pour la mise en œuvre d'une norme spécifique est susceptible d'être rejetée.
Je pense que cela peut être un outil très utile pour de nombreux développeurs, donc si vous lisez ceci, faites savoir à vos collègues qu'il existe. Il s'agit jusqu'à présent d'un projet de temps libre, dont j'ai beaucoup à faire en ce moment ; mais ce ne sera pas le cas pour toujours.
Si vous voulez voir ça en live, faites passer le message...
Peut-être pourriez-vous même parrainer ceci ? Avez-vous une grande entreprise qui crée des applications Web dans Go ? N'hésitez pas à me contacter.
Retrouvez le projet ici : https://github.com/stroiman/go-dom
Félicitations si vous avez réussi à capter l'hommage à une pièce radiophonique populaire de la BBC. ↩
Ceci est basé sur une expérience personnelle. Faire correctement le TDD vous accélérera en raison du cycle de rétroaction rapide. La surcharge d'un vrai navigateur a tendance à vous obliger à écrire des tests après le code de production ; perdant le bénéfice de la boucle de rétroaction qu'une suite de tests rapides vous offre. ↩
Les bases avaient déjà été posées par le projet v8go. Cependant; Toutes les fonctionnalités de la v8 ne sont pas exposées au code Go ; y compris les fonctionnalités nécessaires à l’intégration d’objets natifs. J'ai pu les ajouter dans un fork séparé ; qui est toujours en cours. ↩
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!