首页  >  文章  >  后端开发  >  Go-DOM - 重大里程碑

Go-DOM - 重大里程碑

Mary-Kate Olsen
Mary-Kate Olsen原创
2024-11-22 02:44:17175浏览

Go-DOM -  major milestone

工作了不到两周;我终于达到了 Go-DOM 的第一个重要里程碑。

现在,浏览器将在构建 DOM 树时下载并执行远程 JavaScript

简史

这个项目最初是一个疯狂的想法;看到 Go 和 HTMX 是一个越来越受欢迎的堆栈;

Go 已经拥有测试纯服务器端渲染所需的所有工具。但是,当添加像 HTMX 这样的库时,应用程序的行为是初始 DOM 之间编排的结果;交互元素的属性;到达的 HTTP 端点以及这些端点传送的内容;响应标头和正文。要从用户的角度验证行为,您需要一个浏览器;或者至少是一个行为...与浏览器并不完全不同的测试工具。1

搜索“Go 中的无头浏览器”只会导致建议在无头模式下使用真实浏览器的结果。这种组合有巨大的开销;阻碍快速高效的 TDD 循环。依赖真实的浏览器通常会减慢您的速度而不是加快您的速度2

于是这个想法就被激发了;用纯 Go 编写一个无头浏览器作为 Web 应用程序的测试工具;

首先要解决的不确定性是 HTML 的解析;以及脚本执行。我很快就做到了; 2天内解决这两个问题。我有一个非常基本的 HTML 解析器;我还将 v8 集成到代码库中3

并使 Go 对象可以被 JavaScript 代码访问。

HTML 解析器后来被删除,因为 go x/net/html 已经实现了 HTML 解析器;处理 HTML 解析的所有怪癖。解析格式良好的文档并不是一个非常难解决的问题。它可以优雅地处理格式错误的 HTML,但在这方面却变得棘手。

过了一段时间,我还设法让内联

脚本执行在正确的时刻运行;即当元素实际连接到 DOM 时执行脚本,而不是在解析完整的 HTML 之后执行。

处理 HTTP 请求

能够使用内联脚本处理 HTML 文档之后;下一步是实际从源下载

脚本。这就需要集成一个HTTP层;以便浏览器自行获取内容;而不是被灌输内容。

http.Client 还允许您使用 http.RoundTripper 接口控制实际的传输层。通常你会启动一个服务器;它将侦听 TCP 端口上的请求。在这种情况下,TCP 充当传输层;但本身与 HTTP 请求的处理无关。由于 Go 中标准 HTTP 堆栈的简单性;整个 HTTP 服务器由单个函数 func Handle(http.ResponseWriter, *http.Request) 表示。

无头浏览器可以完全绕过 TCP 堆栈的开销,并使用自定义的 RoundTripper 直接调用此函数。

现在浏览器可以执行HTTP请求,但浏览器代码本身不知道HTTP层被绕过的事实。随之而来的是在 DOM 解析期间下载脚本的能力。

示例代码

让我们探索一个简单的测试,就像它现在在代码库中一样(代码使用 Ginkgo 和 Gomega,恕我直言,这是一个有点被忽视的组合)

首先,测试创建一个简单的 HTTP 处理程序,该处理程序提供两个端点:/index.html 和 /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`))
    },
  )

  // ...

此处的目的只是验证脚本是否已执行。为此,脚本会产生一个可观察到的副作用:它在全局范围内设置一个值。

要验证脚本是否已执行,只需检查全局范围,这是通过从测试本身执行临时 JavaScript 来完成的;验证表达式的结果。

创建浏览器、加载索引文件并验证观察到的副作用的代码

browser := ctx.NewBrowserFromHandler(server)
Expect(browser.OpenWindow("/index.html")).Error().ToNot(HaveOccurred())
Expect(ctx.RunTestScript("window.scriptLoaded")).To(BeTrue())

测试执行也相当快。测试套件中涉及 JavaScript 执行的部分目前由 32 个测试组成,运行时间为 23 毫秒。

下一个里程碑是集成 HTMX。

由于该项目最初是在尝试验证 HTMX 应用程序时构思的,因此合理的下一个目标是支持这种情况。一个简单的 HTMX 应用程序,带有一个按钮和一个计数器,按下按钮时计数器会增加。

  • AnXMLHttpRequest 实现需要就位。为此,工作正在进行中。
  • XPathEvaluator。我相信一开始就可以填充。
  • 事件传播。现在仅发出 DOMContentLoaded 和 load 事件。元素需要支持更多的事件;比如点击;以及触发它们的方法。
    • 这可能还需要正确的事件捕获和冒泡。

进而 ...

接下来是更高级的用户交互;正确的表单处理,例如,输入手线(例如,在 字段中按 enter 提交表单。这通常还涉及某种 URL 重定向;这驱动了对历史对象等的需求.

集成外部站点

具有控制传输层的能力;我们可以提供具有独特能力的测试;我们可以模拟系统在运行时依赖的外部站点。例如,对于给定的主机名,测试可以提供另一个模拟行为的 Go HTTP 处理程序。

最明显的例子是使用外部身份提供商。该测试可以模拟登录流程的行为;不必强迫您在外部系统中创建虚拟帐户,由于外部系统中断而导致测试失败,或者由于身份提供商引入的 2FA 或验证码而根本无法自动化该过程。

另一个用例是使用 API 密集型库,例如地图库,这会产生使用成本。模拟外部站点,以免因运行测试套件而收到额外费用。

可用性胜于兼容性

创建 100% 符合 Whatwg 标准的实现是一项艰巨的任务;直到我真正开始阅读 Whatwg 规范的部分内容之前,我并没有完全理解其范围。目标是创建一个工具帮助为 Web 应用程序编写测试。完全兼容是一个长期目标;但在项目达到某种程度的可用性之前,我会开始填补漏洞。

因此;在实际应用中更可能使用的功能更有可能被优先考虑。指向给出错误结果的实际测试的功能请求可能会被优先考虑。 实施特定标准的功能请求可能会被拒绝。

传播这个词

我相信这对于许多开发人员来说都是一个非常有用的工具,所以如果您阅读了本文,请让您的同事知道它的存在。到目前为止,这只是一个业余时间项目,目前我有很多空闲时间;但情况不会永远如此。

如果你想现场观看,请传播出去......

也许您甚至会赞助这个?您有一家使用 Go 构建 Web 应用程序的大公司吗?欢迎联系我。

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


  1. 如果您成功地听到了对 BBC 流行广播剧的致敬,那就太好了。 ↩

  2. 这是基于个人经验。由于快速的反馈周期,正确执行 TDD 会加快你的速度。真实浏览器的开销往往会让您在生产代码之后编写测试;失去了快速测试套件为您提供的反馈循环的好处。 ↩

  3. v8go 项目已经奠定了基础。然而;并非 v8 的所有功能都暴露给 Go 代码;包括嵌入本机对象的必要功能。我能够将它们添加到单独的叉子中;这仍然是 WIP。 ↩

以上是Go-DOM - 重大里程碑的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn