在 GitHub Actions 里使用 Lighthouse 和 Cypress

GitHub 给我们每人每月 2000 分钟的 Actions 免费额度,可以用来跑 CI,不好好利用就太浪费了。正好最近想学学 Cypress,于是就拿前面说的 mywordle.org 项目练手,加上了自动化 Lighthouse+Cypress。下面主要分享过程,希望对大家有帮助。

0. 开启 GitHub Action 并完成 hello world

点击项目里的 Actions 选项卡,如果该项目之前没有 workflow,就会自动进入创建 workflow 的界面。

项目首页
新建 workflow

建议直接选择 node.js 模版,可以节省很多配置成本。因为免费额度只有 2000分钟/月,所以我建议只保留一个 node.js 版本;又因为我要用 pnpm 管理依赖,所以改造后的配置是这样的:

name: Node.js CI

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [16.x]

    steps:
    - uses: actions/checkout@v2
    - uses: pnpm/action-setup@v2.2.1
      with:
        version: 6.32.2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v2
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'pnpm'
    - run: pnpm i
    - run: npm run build --if-present

这个阶段我只需要确保代码可以完成构建就行了,没有运行更多脚本。

1. 使用 Lighthouse 校验 CLS

Lighthouse 是 Google 提供的网页评价工具,可以对网页的众多指标进行打分,帮助我们提升网页的运行效率和用户体验。CLS=Cumulative Layout Shift,即页面初始化阶段,布局变化的统计值,变化越多值就越大,页面抖动越厉害,评分就越低。

据说这个值很影响 Google 对页面的评价,所以我们必须时时关注。

首先,使用 pnpm 安装 Lighthouse:pnpm i lighthouse -D

我们还需要静态服务器提供网页服务。如果你仔细看前面的配置文件,会发现这个环境基于 Ubuntu 搭建。在配置里增加一行 - run: nginx -v,确认镜像里集成了 nginx 1.18,那就好办了,添加 mime.types 和 nginx 配置文件:

pid logs/travis.nginx.pid;

events {
    accept_mutex off;
}

http {

    server {
        access_log logs/travis.access.log;
        error_log logs/travis.error.log warn;

        listen 9000;

        include mime.types;

        location / {
            alias dist/;
            # 下面这行是 SPA 的关键
            try_files $uri $uri/ /index.php$args;
        }
    }
}

放到 workflow 里跑一下,报错了:

Run nginx -c conf/travis.conf -p `pwd`
nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (13: Permission denied)
2022/03/18 09:38:08 [emerg] 1870#1870: open() "/var/log/nginx/access.log" failed (13: Permission denied)
Error: Process completed with exit code 1.

因为 Nginx 一定要把全局错误日志放在 /var/log/nginx/error.log,而又没有权限,所以报错。解决方案有两个:

  1. 升级到 nginx 1.19,便可以使用 -e 参数自定义全局错误日志的路径。不过这意味着我们每次都必须更新镜像,毫无疑问会浪费宝贵的免费额度。基本放弃。
  2. 确保 nginx 可以操作这个文件,这可能需要 root 权限。

在配置文件里随便加一行 sudo touch /var/log/nginx/error.log,顺利完成,说明我们拥有 root 权限。于是加入以下配置,完成 nginx 配置:

    - run: sudo chmod -R 755 /var/log/nginx
    - run: sudo touch /var/log/nginx/error.log
    - run: sudo chmod 777 /var/log/nginx/error.log
    - run: nginx -c conf/travis.conf -p `pwd`

接下来就简单了,使用下面的命令可以调用 Lighthouse 给网页打分,并且将数据输出到 lighthouse.json,然后我们写个脚本分析即可:

lighthouse --chrome-flags="--headless --disable-gpu --no-sandbox" --output json --output-path=lighthouse.json http://localhost:9000

2. Cypress

接下来搞 Cypress,时间因素我就不详细介绍 Cypress 的用法了,他们官网有很详细的视频,虽然是英文的,不过我觉得大概也看得懂。建议大家先看一下,了解个大概:Installing Cypress | Cypress Documentation

这次我打算添加两个测试:过关,和失败。

尝试 Cypress 的过程并不顺利,主要原因是 Cypress 的语法设计有点难懂。比如下面两行代码:

cy.visit('http://localhost:9000/');
cy.get('#header').should('contain', 'Wordle Unlimited');

看起来它要完成两步操作,其实不然。Cypress 只是暂时把这两步操作存入操作队列,未来满足某个条件才会执行。所以,我们不能将其它操作插入这些步骤中间。比如我的游戏数据存在 localStroage 里,我希望在网页打开后校验这些数据,于是我就后面读取 localStorage,但怎么也读不到。正确的做法是在第二句的后面用 .then(() => {}) ,然后把操作放在里面。

哦,对了,基于同样的原因,Cypress 的测试函数也不能是 Async Function。

不知道如果将来需要做条件判断或者循环的话,应该怎么写。

接下来,只要配置对应的 action 即可。我选择使用官方的 cypress-io/github-action@v2,虽然我也不知道它比自己写多了些什么,不过我觉得能省事总是好的。需要注意的是,因为我使用了 pnpm,所以我不需要官方配置里的 install 步骤。

3. 使用环境变量

有时候,我们的项目依赖一些第三方工具,这就需要我们能够在构建或测试的时候配置第三方工具的鉴权方式,比如 access_key,secret_key 之类的东西。很显然,我们不能把这些鉴权信息入库。(实际上,随便搜一搜,能找到大量这么做的代码。)所以我们需要用别的方式来配置这些信息。

最常见的做法是环境变量。比如我们配置一个 WX_PAY_PRIVATE_KEY,然后我们就可以在代码中使用 process.env.WX_PAY_PRIVATE_KEY。GitHub Actions 可以使用 env: 配置环境变量,但是这些变量一样不适合入库,所以我们需要配置安全信息。

配置位于上图所示的地方,添加完之后,就可以在配置文件中使用:

- name: 步骤
  env:
    AIRTABLE_API_KEY: ${{ secrets.AIRTABLE_API_KEY }}
  run: 命令

4. 完整配置文件

name: Node.js CI

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [16.x]

    steps:
    - uses: actions/checkout@v2
    - uses: pnpm/action-setup@v2.2.1
      with:
        version: 6.32.2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v2
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'pnpm'
    # setup local server
    - run: pnpm i
    - name: build repo
      env: 
        AIRTABLE_API_KEY: ${{ secrets.AIRTABLE_API_KEY }}
      run: npm run build --if-present
    - run: mkdir logs
    - run: sudo chmod -R 755 /var/log/nginx
    - run: sudo touch /var/log/nginx/error.log
    - run: sudo chmod 777 /var/log/nginx/error.log
    - run: nginx -c conf/travis.conf -p `pwd`
    # run lighthouse
    - run: pnpm add lighthouse -g
    - run: lighthouse --chrome-flags="--headless --disable-gpu --no-sandbox" --output json --output-path=lighthouse.json http://localhost:9000
    - run: node tools/lighthouse.js

    - name: Cypress run
      uses: cypress-io/github-action@v2
      with:
          install: false

贴上完整配置,供各位参考。

5. 总结

实际体验下来,大约花了 60 分钟,每次运行大约两分半钟,看起来一个月 2000 分钟额度还是蛮充足的。

建议大家都学习一下,CI/CD 是现代化开发的基础设施,可以大大提升我们的开发效率。Lighthouse 是非常重要的网站评价工具,Cypress 可能是现在最好的 UI e2e 测试工具,结合这几个工具可以保障我们的网站始终可用,始终好用。

如果你看过我的上一篇文章《在 Code.fun 做 Code Review》,其实偿还技术债最好的办法就是写自动化测试,因为只有自动化测试才能告诉你重构有没有引入新问题、能不能上线,优化有没有成功;以及,能否推进下一步重构与优化。

如果您觉得文章内容对您有用,不妨支持我创作更多有价值的分享:


已发布

分类

来自

评论

欢迎吐槽,共同进步

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据