0

0

Go-DOM - 用 Go 编写的无头浏览器

碧海醫心

碧海醫心

发布时间:2024-11-09 15:21:10

|

444人浏览过

|

来源于dev.to

转载

go-dom - 用 go 编写的无头浏览器

无事可做有时会导致疯狂的想法,而这一次;目的是通过嵌入 v8 引擎,用 go 编写一个无头浏览器,具有完整的 dom 实现和 javascript 支持。

这一切都是从编写 htmx 应用程序开始的,测试它的需要让我很好奇是否有无头浏览器的纯 go 实现。

搜索“go headless browser”只会导致搜索结果谈论自动化无头浏览器,即在无头模式下使用真正的浏览器,例如 firefox 的 chrome。

但是在纯 go 中什么都没有。

所以我开始建造一个。

为什么 go 中使用无头浏览器?

这可能看起来很愚蠢,因为编写无头浏览器永远不会像真正的浏览器一样工作;因此并不能真正验证您的应用程序是否在您决定支持的所有浏览器中正常工作。这也不允许您在停止工作时获得良好的功能,例如应用程序的屏幕截图。

那为什么呢?

极品速度!

为了在有效的 tdd 循环中工作,测试必须很快。缓慢的测试执行会阻碍 tdd,并且您会失去快速反馈循环提供的效率优势。

使用浏览器自动化进行此类验证会产生严重的开销,并且此类测试通常是在代码编写后编写;因此,它们不再有助于编写正确的实现;但事后却减少了维护负担;只是偶尔会在您的付费客户之前检测到错误。

目标是创建一个支持 tdd 流程的工具。为了可用,它需要在进程内运行。

需要用go编写。

减少不稳定的测试

让 dom 处于进程内可以在 dom 之上编写更好的包装器;这可以帮助为您的测试提供一个不太不稳定的界面,就像测试库为 javascript 所做的那样。

您不用依赖 css 类名、元素 id 或 dom 结构,而是使用以用户为中心的语言编写测试,如下所示。

在带有“电子邮件”标签的文本框中输入“me@example.com”

或者用假设的代码。

testing.getelement(query{
  role: "textbox",
  // the accessibility "name" of a textbox _is_ the label
  name: "email",
}).type("me@example.com")

此测试不关心标签是否实现为

这将行为验证与 ui 更改解耦;但它确实强制文本“电子邮件”以可访问的方式与输入字段关联。这将测试与用户如何与页面交互结合起来;包括那些依赖屏幕阅读器使用您的页面的人。

这实现了tdd最重要的方面;编写与具体行为相结合的测试。1

虽然在技术上可能可以为进程外浏览器编写相同的测试;原生代码的好处对于这些类型的助手最可能需要的 dom 随机访问类型至关重要。

示例:javascript

为了举例说明测试类型,我将使用 javascript 中的类似示例;也是一个使用 htmx 的应用程序。该测试验证请求需要身份验证的页面的一般登录流程。

有点长,因为我在这里将所有设置和帮助程序代码合并到一个测试函数中。

it("Redirects to /local after a successful login", async () => {
  // Setup - stub the authentication, and create a stubbed user
  // using a test helper
  sinon
    .stub(auth, "authenticate")
    .withArgs({
      email: "jd@example.com",
      // matchPassword helper is used, as passwords are wrapped in a class
      // preventing accidental disclosure in logs, console out, etc.
      password: matchPassword("s3cret"),
    })
    .resolves(
      auth.AuthenticateResult.success(createUser({ firstName: "John" })),
    );
  const url = `http://127.0.0.1:${port}/auth/login?redirectUrl=%2Flocal`;
  // Request private page. This _should_ generate a redirect
  const wrapper = await DOMWrapper.open(url); // Just a helper around jsdom
  const browser = wrapper.browser;
  // Once HTMX is ready, it emits an `htmx:load` event. Then verify that it was 
  // correctly redirected.
  await wrapper.waitFor("htmx:load");
  expect(wrapper.url.pathname).to.equal("/auth/login");
  // Use testing-library to fill out and submit the form
  let screen = wrapper.screen;
  const username = screen.getByRole("textbox", { name: "Email" });
  const password = screen.getByLabelText("Password");
  await userEvent.type(username, "jd@example.com");
  await userEvent.type(password, "s3cret"); // password has no role
  // Wait for a new `htmx:load` event, while clicking the submit button
  // at the same time.
  await wrapper.runAndWaitFor(
    ["htmx:load"],
    userEvent.click(screen.getByRole("button", { name: "Sign in" })),
  );
  // After the new new page has been loaded, verify that the username
  // is displayed (i.e. the stubbed user is used), and the correct
  // URL is used.
  screen = testingLibrary.within(browser.window.document.body);
  const heading = screen.getByRole("heading", { level: 1 });
  expect(heading.innerHTML).to.equal("Hi, John");
  expect(wrapper.url.pathname).to.equal("/local");
});

简单来说,测试执行以下操作:

  1. 删除身份验证函数,模拟成功的响应。
  2. 请求需要身份验证的页面
  3. 验证浏览器是否重定向到登录页面,并且浏览器 url 是否已更新。 2
  4. 在表格中填写预期值,然后提交。
  5. 验证浏览器是否重定向到最初请求的页面,并且它显示了存根用户的信息。

测试在内部启动 http 服务器。因为 this 在测试过程中运行,所以可以对业务逻辑进行模拟和存根。测试使用jsdom与http服务器通信;它既将 html 响应解析为 dom,又在已初始化的沙箱中执行客户端脚本,例如以 window 作为全局范围。3

这使得能够编写 http 层的测试,其中验证响应的内容是不够的。在这种情况下; htmx 按预期处理响应。

ExcelFormulaBot
ExcelFormulaBot

在AI帮助下将文本指令转换为Excel函数公式

下载

但是除了等待一些 htmx 事件,以免过早(或太晚)进行之外,测试实际上并不关心 htmx。事实上,如果我从表单中删除 htmx,采用经典重定向,测试仍然可以通过。

(如果我完全删除 htmx

速度?查看!

虽然之前的测试比预期慢了一点;它相当快,通常在 150-180 毫秒内完成。对于大多数测试套件来说这太慢了,但它足够快,可以在处理该特定功能时充当反馈循环。

此测试不是正常 tdd 运行的一部分。当我处理该功能时,它们就会运行;或在提交之前;确保没有任何损坏。这是处理“慢测试”的完全正常的方式。

潜在的速度改进

javascript 示例使用在随机端口上启动的真实 http 服务器。服务器在测试运行器的进程中运行,这就是为什么我们可以存根和模拟业务逻辑。

在 go 中,http 请求由 http.handler 处理,因此无需实际启动 http 服务器即可轻松使用 http 处理逻辑。

这是 go-dom 代码 现在 处理的事情,目前测试套件的运行时间为零毫秒,四舍五入到最接近的毫秒。4

模拟和并行测试?

运行并行测试的能力仅取决于您的代码并行运行的能力。由于这可以消耗 http.handler,因此每个测试都可以创建自己的处理程序;每个都有不同的依赖项,替换为测试双倍,以适合单独的测试。

这允许您测试整个 http 层;使用存根业务逻辑。

项目现状?

几乎没有任何实施;目前的状态是大约一天半工作的结果。我有一个基本的流标记生成器,可以使用 http 响应流,该响应流被传递到返回 node 的解析器。

代码当前可以将字符串 (尚不允许空格)处理为 htmlhtmlelement。

接下来的步骤是

  • 稍微改进解析器,并实现更多元素类型
  • 嵌入v8引擎,解决主要不确定性; javascript 如何访问 go 对象,以及 go 代码如何检查 javascript 代码的突变结果。
    • 我使用 v8go 制作了一个极其简单的 poc,以及似乎维护得最好的 fork。它缺乏必要的功能;我已将其添加到我自己的叉子中

该项目的未来?

这很可能会死:(

我什至没有在 go 项目上工作,这将是有价值的(我当时在 node.js 项目上工作)。很高兴看到 jsdom 如何帮助充当身份验证流程的反馈循环,从而激发了一个有趣的愚蠢想法。作为一个患有多动症的人,这对我来说是一个典型的模式。我开始做一些有趣的事情,并努力去做;直到有其他东西引起我的注意并引起我的兴趣。

除非...

其他开发人员认为这是一个好主意,并希望帮助构建它。

我相信这样的工具对于任何将服务器端渲染与客户端脚本相结合的 go 项目都非常有帮助,包括基于 htmx 的应用程序。

该项目可以在这里找到:https://github.com/stroiman/go-dom


  1. tdd 的目标是而不是编写单元测试。这是一个极其常见的现象;但完全不正确的误解。 ↩

  2. 重要的不是我们被重定向;而是我们被重定向了。但浏览器历史记录具有正确的条目,提供浏览器后退/前进功能的合理行为。测试应该确实验证了历史的内容,或者甚至可能主动使用导航api来来回。像这样;该测试在描述从用户角度的预期行为方面还可以改进。 ↩

  3. 用 javascript 编写无头浏览器具有不公平的优势;因为你的模拟 dom 已经是有效的 javascript 对象。在 go 中,需要额外的工作来允许客户端脚本改变 dom,并让结果可以在测试代码中访问。 ↩

  4. 据 ginkgo 报道。这不包括构建和启动的开销,这有一个明显但非常短的延迟。 ↩

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
chrome什么意思
chrome什么意思

chrome是浏览器的意思,由Google开发的网络浏览器,它在2008年首次发布,并迅速成为全球最受欢迎的浏览器之一。本专题为大家提供chrome相关的文章、下载、课程内容,供大家免费下载体验。

886

2023.08.11

chrome无法加载插件怎么办
chrome无法加载插件怎么办

chrome无法加载插件可以通过检查插件是否已正确安装、禁用和启用插件、清除插件缓存、更新浏览器和插件、检查网络连接和尝试在隐身模式下加载插件方法解决。更多关于chrome相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

761

2023.11.06

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

381

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

213

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1506

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

629

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

758

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

690

2024.04.29

抖音网页版入口与视频观看指南 抖音官网视频在线访问
抖音网页版入口与视频观看指南 抖音官网视频在线访问

本专题汇总了抖音网页版的入口链接、官方登录页面以及视频观看入口,帮助用户快速访问抖音网页版,提供免登录访问方式和直接进入视频播放页面的方法,确保顺利浏览和观看抖音视频。

61

2026.02.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Sass 教程
Sass 教程

共14课时 | 0.8万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.2万人学习

CSS教程
CSS教程

共754课时 | 27.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号