之前我曾經介紹過用 BrowserStack 來測試網站在舊版本的瀏覽器上的表現;本篇中我們將更進一步把這個測試給自動化。

前情接續

在本系列第 14 篇當中,我談到了關於瀏覽器相容性測試的重要性、以及如何用 BrowserStack 的免費方案來進行測試的方法。說來有趣:那篇文章到現在也已經過了幾乎兩年了,當時我把 BPS 支援的 Safari 最低版本設定在 11.1(或者手機版是 11.3),會這樣設定當然是在「我能夠向下 polyfill 到什麼地步」跟「我確實有訪客還在使用那麼舊的版本」之間取得一個平衡;那麼在科技日新月異的這個時代,過了兩年之後,各位猜看看到底有沒有人還在使用 Safari 11?

從 Google Analytics 當中,我們可以得到的確切結論是:還是有的,全世界大概還有一個人在用 🤣

話說,BrowserStack 也不可能無止盡支援老舊的裝置/瀏覽器組合,他們會定期根據市占率和裝置支援狀況來決定是否淘汰掉一些太老的組合 1;截至本文撰寫為止,Safari 10 以下的版本都已經被 BrowserStack 淘汰、而 Safari 11 也已經是老到下次就輪到它的程度了,結果還是有人在用……真的是有點哭笑不得。但總之,等到連 BrowserStack 都把 Safari 11 淘汰掉的那一天,我就不得不提昇我的最低版本支援設定了、無論是否還有人在用都一樣。沒辦法,畢竟我已經無法繼續確認 BPS 是否能在那樣的裝置上正常運作了,這個理由非常充分了。不過,在那一天到來之前,反正我也沒有什麼大理由非得拉高門檻不可,我就繼續支援 Safari 11 到不行為止吧。

自動化測試

不過,既然是要持續提供舊版支援,那麼設法把舊版的相容性測試加以自動化就很重要了。要不然,每次我有什麼比較大的架構更新、我就得手動打開 BrowserStack 來把一些想測的功能在三大瀏覽器(Chromium、Firefox、Safari)上頭都測一遍,這樣做也實在是挺麻煩的。

幸好,BrowserStack 也有提供自動化測試的功能,而且支援很多測試工具與框架的組合,其中也包括了平常我慣用的 Playwright。只不過,Playwright 的版本跟瀏覽器版本之間是綁死的,例如假如我想要測試 Chromium v100,那我就一定要安裝對應的 Playwright v1.19.1 才行,這個限制是即使跟 BrowserStack 整合也還是不變的。更傷腦筋的是,Playwright 怎麼說都是比較年輕的工具,即使是我找得到最舊最舊的 Playwright 版本,也遠遠跟我要測的 Safari 11 差太遠了。所以結論很明顯:要用 BrowserStack + Playwright 去測很舊的瀏覽器是行不通的。

還好,接下來我嘗試改用 Selenium 來驅動測試,結果就可以完美地自由選擇任何 BrowserStack 上面支援的裝置/瀏覽器組合了。雖然過去我從來沒用過 Selenium,但是 BrowserStack 的文件寫得還算滿清楚的,所以沒有花上太多力氣就設置完成整個測試了。

要設置這個自動化測試,我會建議這部份的東西不要放在應用程式自身的 repo 之中,而是另外再開一個小的私有專案,原因有二:

  1. 這裡面會需要寫下 BrowserStack 的帳號與 access key,不適合放在公開 repo 中。當然這兩個資訊也是可以放在環境變數裡面、而不要明文寫在設定檔之中,但是其它開發者反正無法執行這個測試,用不著把這些東西放在一起。
  2. 這裡面會用到 browserstack-node-sdkselenium-webdriver 這兩個套件以及它們的所有相依,這有數百 MB 的下載大小,不需要讓其它開發者多下載安裝這些反正他們用不到的東西。

在開好新專案之後,會需要安裝的東西包括:

pnpm add -D browserstack-node-sdk selenium-webdriver @types/selenium-webdriver

最後一個是方便 VS Code 做型別檢查。除了這些之外,也要安裝跟測試框架有關的東西,我自己慣用 mocha + chai,所以就是:

pnpm add -D mocha chai @types/mocha @types/chai

然後首先我們要建立一個 browserstack.yml 的設定檔,這個部份 BrowserStack 非常貼心地在它們的文件網站裡面提供了清楚的 GUI 介面(以 Selenium + NodeJS 為例是在這裡)來引導各位進行設定、並且自動產生設定檔,所以我不需要解釋太多。但總之在我的情況中,最後的設定檔是長這樣:

userName: ... # 帳號名
accessKey: ... # BrowserStack accessKey
# 這個是重點;列出我們要在哪些平台(即裝置/瀏覽器版本的組合)上面執行測試
platforms:
  - os: OS X
    osVersion: High Sierra
    browserName: Safari
    browserVersion: 11.1
  - os: Windows
    osVersion: 11
    browserName: Chrome
    browserVersion: 66.0
  - os: Windows
    osVersion: 11
    browserName: Firefox
    browserVersion: 78.0
  - browserName: safari
    osVersion: 11
    deviceName: iPhone 8
# 底下這些其它設定這邊不多解釋
parallelsPerPlatform: 1
browserstackLocal: false
buildName: bp-studio
buildIdentifier: ${BUILD_NUMBER}
projectName: Box Pleating Studio
testObservability: false
debug: true
networkLogs: false
consoleLogs: verbose
percy: false
percyCaptureMode: auto
disableAutoCaptureLogs: true

一個重點在於,BrowserStack Automate 目前並沒有自由到可以讓我們精確指定「哪些測試要在哪些平台中執行」,而是我們撰寫的所有測試都會在設定檔中列出的所有平台上執行;頂多就是我們可以在測試程式碼中去偵測當前平台,如果是不需要執行的就跳出測試,如此而已。

另外一個重點是,與桌機不同地、在 iOS Safari 的情況中,這邊只能夠指定說「我要 iOS 11 的手機」、但是並不能精確地指定到是要「11.多少」;而實際上執行測試的時候,連線過去到底會被分配到哪一個版本的裝置,也是無法預測的(我從 11.0 到 11.4 都有遇到過,他們有好幾台版本不一的裝置會隨機分配給連線請求,這點我跟客服確認過了)。偏偏這邊有個小麻煩:我設定的 iOS 最低支援版本是 11.3,如果遇到 11.2 或更低的裝置、我的測試程式碼就會失敗。而解決的辦法,就跟剛才一樣,我必須去偵測拿到的版本是什麼,如果遇到 11.2 以下的話、我就改成去測試頁面上是否有正確顯示出錯誤提示訊息,否則就正常執行測試這樣。

於是我的主體測試程式就長這樣:

import { Builder, By, Capabilities, until } from "selenium-webdriver";

const buildDriver = function() {
    return new Builder()
        .usingServer("http://localhost:4444/wd/hub")
        .withCapabilities(Capabilities.chrome())
        .build();
};

describe("Older browser compatibility", function() {
    this.timeout(0);

    /** @type {import("selenium-webdriver").ThenableWebDriver} */
    let driver;
    let capability;

    before(async function() {
        driver = buildDriver();
        capability = await driver.getCapabilities();
    });

    it("Supports older browsers", async function() {
        // 這邊我載入了一個帶有範本專案 query 字串的網址,於是只要該網址有正確開啟並且載入專案,
        // 就一次測試了各種我想要測試的主要相容性功能了。
        await driver.get("...");
        // 等候網頁標題來確定網頁已經載入
        await driver.wait(until.titleMatches(/(Box Pleating|BP) Studio/i), 10000);

        // 撈出手機系統版本(不是手機的話會得到 NaN)
        const version = Number(capability.get("mobile")?.version?.match(/\d+\.\d+$/)?.[0]);
        if(version < 11.3) {
            console.log(`Get iOS v${version}`);
            // 檢查錯誤訊息是否有正確顯示出來
            await driver.wait(
                until.elementTextContains(
                    driver.findElement(By.id("requirement")),
                    "Service worker is required in BP Studio, but is not supported in your browser."
                ),
                10000
            );
        } else {
            // 等候專案的 tab 出現,表示專案載入成功
            await driver.wait(until.elementLocated(By.css("#divTab .tab.active")), 10000);
        }
    });

    after(async function() {
        await driver.quit();
    });
});

把這個存檔成 test.mjs,到這邊就都設置好了。然後只要執行

browserstack-node-sdk mocha test.mjs

就可以跑整個自動測試,並且可以在 BrowserStack 上面仔細檢視執行過程(包括有影片、網路存取 log 等等)與測試結果,非常方便。


  1. 根據我詢問客服的說法,當他們準備這麼做的時候,原則上它們會提早一個月在對應的組合旁邊放上警告的圖示以告知使用者,不過也有可能因為特殊狀況而提前直接淘汰、無法保證一定有一個月的緩衝期。 


分享此頁至:
最後修改日期: 2025/04/18

留言

撰寫回覆或留言

發佈留言必須填寫的電子郵件地址不會公開。您的留言可能會在審核之後才出現在頁面上。