Global

Members

(constant) BOX_PAD

Description:
  • 把紅框以sharp疊到截圖buffer上(截圖後合成,不注入DOM),標注本張標準圖之觀看或操作區

    幾何:目標區外擴pad(預設6),四邊夾在bounds內並留margin(預設3);SVG之stroke置中於路徑, 故外緣落在夾邊後之矩形上,等效DOM版border-box之5px內縮框線。框過小(寬或高不足stroke)時預設不畫、回傳原buffer(guardSmall=false則照畫,重現無此守門之專案)。 why不注入DOM:插入後又移除之暫時DOM偶發使整頁光柵化偏1px,量測工具不得改動被測頁

Source:

把紅框以sharp疊到截圖buffer上(截圖後合成,不注入DOM),標注本張標準圖之觀看或操作區

幾何:目標區外擴pad(預設6),四邊夾在bounds內並留margin(預設3);SVG之stroke置中於路徑, 故外緣落在夾邊後之矩形上,等效DOM版border-box之5px內縮框線。框過小(寬或高不足stroke)時預設不畫、回傳原buffer(guardSmall=false則照畫,重現無此守門之專案)。 why不注入DOM:插入後又移除之暫時DOM偶發使整頁光柵化偏1px,量測工具不得改動被測頁

Example
import composeBox from 'w-package-tools-e2e/src/composeBox.mjs'

buf = await composeBox(buf, { left: 100, top: 50, right: 300, bottom: 120 })

(constant) INK_PAD

Description:
  • 文字墨跡之紅框留白:紅框幾何為目標 ±6 且 5px 描邊內縮,目標矩形緊貼文字墨跡時框內僅剩約 1px(紅框壓字)。 墨跡外擴 INK_PAD(4px)後框內留白約 5px,與自帶內距之按鈕相當(2026-09-28)。 itemsUnionBox 之 fit 以同一常數外擴 DOM 文字墨跡;本函數供「無 DOM 之緊貼墨跡矩形」(canvas 圖例列、翻頁箭頭、圖例項等, 由圖表程式庫回報之視窗座標矩形)使用,交給 captureStableWithBox 作為矩形目標。 neighbors(2026-09-28):同一列之其他項目矩形(例:其他圖例項);外擴後框線不越過與鄰項間隙之正中,不壓到鄰項(業主:亂框亂壓遮蔽有效資訊即缺陷)。 矩形須為「實際墨跡」:圖表程式庫回報之文字/項目矩形常比畫出之字形寬數 px,以之取中點框線會偏向鄰項——先經 canvasInkRects 收斂; 鄰項須含畫面上有墨跡之全部項目(被捲動窗裁掉一部分者亦算,canvasInkRects 只回其可見部分),不可只取完整顯示者 (w-web-perm 統計圖例項殷鑑:兩者皆未做時,框線距自身文字 7px、貼下一項圖示 0px)。

Source:

文字墨跡之紅框留白:紅框幾何為目標 ±6 且 5px 描邊內縮,目標矩形緊貼文字墨跡時框內僅剩約 1px(紅框壓字)。 墨跡外擴 INK_PAD(4px)後框內留白約 5px,與自帶內距之按鈕相當(2026-09-28)。 itemsUnionBox 之 fit 以同一常數外擴 DOM 文字墨跡;本函數供「無 DOM 之緊貼墨跡矩形」(canvas 圖例列、翻頁箭頭、圖例項等, 由圖表程式庫回報之視窗座標矩形)使用,交給 captureStableWithBox 作為矩形目標。 neighbors(2026-09-28):同一列之其他項目矩形(例:其他圖例項);外擴後框線不越過與鄰項間隙之正中,不壓到鄰項(業主:亂框亂壓遮蔽有效資訊即缺陷)。 矩形須為「實際墨跡」:圖表程式庫回報之文字/項目矩形常比畫出之字形寬數 px,以之取中點框線會偏向鄰項——先經 canvasInkRects 收斂; 鄰項須含畫面上有墨跡之全部項目(被捲動窗裁掉一部分者亦算,canvasInkRects 只回其可見部分),不可只取完整顯示者 (w-web-perm 統計圖例項殷鑑:兩者皆未做時,框線距自身文字 7px、貼下一項圖示 0px)。

Example
import inkRect from 'w-package-tools-e2e/src/inkRect.mjs'

let buf = await captureStableWithBox(page, inkRect(legend.rect))
let buf2 = await captureStableWithBox(page, inkRect(item.rect, { neighbors: others.map((o) => o.rect) }))

WPackageToolsE2e

Description:
  • e2e 測試與標準圖(pixel baseline)共用設施

Source:

e2e 測試與標準圖(pixel baseline)共用設施

Example
import wpte from 'w-package-tools-e2e/src/WPackageToolsE2e.mjs'

let browser = await wpte.launchBrowser()
let page = await wpte.openCasePage(browser)
await page.goto('http://127.0.0.1:8080')
let buf = await wpte.captureStableWithBox(page, '#btn')
wpte.assertBaselineMatch(buf, './test/pics/demo/demo-eng-E2E-001-click-btn.png', 'demo-eng-E2E-001-click-btn')
await browser.close()

其餘詳見各模組範例

chromiumLaunchArgs

Description:
  • 確定性渲染之Chromium啟動旗標(六旗標),供像素比對之截圖使用

    headless Chromium預設GPU光柵化與次像素字形反鋸齒,於像素比對下非決定性(拉丁字偶發散落差異,CJK灰階反鋸齒較穩故常只英文語系出錯)。 本組關閉GPU(改走CPU Skia)、固定sRGB色彩、關閉LCD與次像素字形定位、關閉Skia執行期最佳化與部分光柵化, 四個web專案實測截圖自我一致(同態連拍)由裸啟動之2/4提升為5/5。旗標組改動即全部標準圖須重產

    陣列已凍結,需增加旗標時由launchBrowser之opt.args附加

Source:

確定性渲染之Chromium啟動旗標(六旗標),供像素比對之截圖使用

headless Chromium預設GPU光柵化與次像素字形反鋸齒,於像素比對下非決定性(拉丁字偶發散落差異,CJK灰階反鋸齒較穩故常只英文語系出錯)。 本組關閉GPU(改走CPU Skia)、固定sRGB色彩、關閉LCD與次像素字形定位、關閉Skia執行期最佳化與部分光柵化, 四個web專案實測截圖自我一致(同態連拍)由裸啟動之2/4提升為5/5。旗標組改動即全部標準圖須重產

陣列已凍結,需增加旗標時由launchBrowser之opt.args附加

Example
import chromiumLaunchArgs from 'w-package-tools-e2e/src/chromiumLaunchArgs.mjs'

console.log(chromiumLaunchArgs)
// => ['--disable-gpu', '--force-color-profile=srgb', '--disable-lcd-text', '--disable-font-subpixel-positioning', '--disable-skia-runtime-opts', '--disable-partial-raster']

Methods

assertBaselineMatch(buf, baselinePath, labelopt, optopt) → {undefined}

Description:
  • 截圖與標準圖比對(反鋸齒感知之像素比對加容差),不一致時保留三聯組證據後拋錯

    比對:pixelmatch之includeAA:false自動忽略反鋸齒邊緣像素,threshold預設0.1,真不同像素數超過maxDiffPixels(預設100)即失敗; 尺寸不同必為真差異,直接失敗;標準圖不存在直接拋錯。像素層為補強,呼叫前每案仍須先有語意斷言。 證據:失敗時把當次截圖、標準圖與差異圖存到pendingDir(預設'./testPending'),檔名為<label>__<毫秒時間戳>__{capture,baseline,diff}.png,撞名加-N,永不覆蓋、不自動刪除。 餘裕告警:通過但真不同像素數超過maxDiffPixels之headroomRatio(預設0.5)時印告警——計數貼近上限之標準圖為零餘裕之flake源(2026-09-28 實測兩張恰為100),應重產或查因。 本函數只比對不寫檔:標準圖之寫檔一律經runBaselineCase(全部斷言通過後才寫);原opt.regen寫檔旁路(第二條寫檔路徑、先寫圖後斷言)已於2026-09-28移除。

    本函數為同步函數

Source:
Example
import assertBaselineMatch from 'w-package-tools-e2e/src/assertBaselineMatch.mjs'

assertBaselineMatch(buf, './test/pics/tokens/tokens-eng-E2E-001-list-loaded.png', 'tokens-eng-E2E-001-list-loaded')
Parameters:
Name Type Attributes Default Description
buf Buffer

輸入當次截圖PNG之Buffer

baselinePath String

輸入標準圖路徑字串

label String <optional>
''

輸入證據檔名用之可讀標籤字串,預設''代表使用標準圖檔名

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
maxDiffPixels Number <optional>
100

輸入容許之真不同像素數,預設100

threshold Number <optional>
0.1

輸入pixelmatch之色差門檻,預設0.1

pendingDir String <optional>
'./testPending'

輸入失敗證據目錄字串,預設'./testPending'

headroomRatio Number <optional>
0.5

輸入餘裕告警比例,通過但真不同像素數大於maxDiffPixels乘此比例時告警,預設0.5;設為1以上即不告警

warn function <optional>
console.warn

輸入告警輸出函數,預設console.warn

Returns:

通過時無回傳,不一致時拋出錯誤

Type
undefined

(async) assertTextSpec(page, spec, optopt) → {Promise}

Description:
  • 以spec衍生之期望文字斷言頁面:mode為'text'時頁面須含value,'absentText'時頁面不得含value;不符即拋出斷言錯誤並附頁面文字供診斷

    期望文字應自spec與語系檔衍生(可觀察之訊息原文),不可取自現狀;像素比對只是補強層,每案仍須先有此類語意斷言

Source:
Example
import assertTextSpec from 'w-package-tools-e2e/src/assertTextSpec.mjs'

await assertTextSpec(page, { mode: 'text', value: 'Save tokens successfully' }, { label: 'E2E-002' })
Parameters:
Name Type Attributes Default Description
page Page

輸入Playwright之Page

spec Object

輸入期望物件{mode,value},mode為'text'或'absentText'

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
label String <optional>
''

輸入錯誤訊息用之案例標籤字串,預設''

Returns:

回傳Promise,符合時resolve,不符時reject斷言錯誤

Type
Promise

(async) canvasInkRects(page, rects, optopt) → {Promise.<Array>}

Description:
  • 畫布(canvas)目標之實際墨跡範圍:逐矩形讀其所在 canvas 之像素,回傳矩形內實際有墨跡之最小外接矩形(視窗座標)

    圖表程式庫(echarts/zrender 等)回報之文字、圖例項矩形常比畫出之字形寬數 px(字寬量測與實際繪字之差、項目群組含互動熱區); 以之交 inkRect 外擴留白或取與鄰項間隙之中點,框線會偏向一側而貼到鄰項(2026-09-28 w-web-perm 統計圖例項殷鑑: 框線距自身文字 7px、貼下一項圖示 0px)。先以本函數收斂到實際墨跡再交 inkRect。 被捲動窗裁掉一部分之項目只回其可見墨跡、完全未繪製者回 null——同一列之鄰項應取「畫面上有墨跡者」, 不可只取完整顯示者(否則緊鄰之半顯示項不算鄰項,框線照常外擴而貼上去;同一殷鑑)。 canvas 預設以各矩形中心點命中測試取得(該點最上層之 canvas),亦可由 opt.canvas 指定(選擇器字串或 Locator)。 墨跡判準:未繪製處須為透明(圖表未設不透明背景色),以 alpha 大於門檻判定;背景不透明者以 opt.bg 給背景色,改以色差判定。

Source:
Example
import canvasInkRects from 'w-package-tools-e2e/src/canvasInkRects.mjs'

let inks = await canvasInkRects(page, legend.items.map((o) => o.whole))
let target = inks[0]
let buf = await captureStableWithBox(page, inkRect(target, { neighbors: inks.slice(1).filter(Boolean) }))
Parameters:
Name Type Attributes Default Description
page Object

輸入Playwright Page

rects Array

輸入視窗座標矩形陣列,每個為{x,y,width,height}

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
canvas String | Object <optional>
null

輸入canvas之選擇器字串或Locator(取第一個),預設null代表逐矩形以中心點命中測試取得

alpha Number <optional>
40

輸入墨跡判準之alpha門檻(0~255),預設40

bg Array <optional>
null

輸入不透明背景色[r,g,b],給定時改以「與背景之最大通道差 > 24」判定墨跡,預設null

trimY Boolean <optional>
false

輸入是否亦收斂上下,預設false(只收左右,上下沿用原矩形:同一行字之高度不因字形而異)

Returns:

回傳與rects同序之矩形陣列,矩形內無墨跡、找不到canvas或canvas非2d者為null

Type
Promise.<Array>

(async) captureStable(page, optopt) → {Promise}

Description:
  • 標準圖截圖之唯一入口:先排除「還沒穩定」與「穩定到錯的狀態」兩類成因,再連拍至相鄰兩張完全相同才回傳

    順序(不可調換):滑鼠移到(0,0)消除hover → 初始等待(涵蓋setTimeout延遲顯示與hover離開動畫,animations:'disabled'不快轉setTimeout) → settle掛鉤(元件狀態機優先,例如WDrawer之waitColResizeOverlay、waitDrawerReady) → 凍結inline SVG之SMIL動畫 → 等字型就緒 → beforeShots掛鉤(例如ag-grid水平捲動歸零) → 偵測內含之SVG區域(截圖後貼上該SVG去掉動畫元素之靜態影格,兩端同區同圖) → 連拍:每張先遮罩再與前一張比對位元組,相同即回傳;重試用盡仍不同時,strict拋錯(產製端拒絕寫入未穩定畫面),否則回傳最後一張交由比對揭露真實flake。 strict之決定:opt.strict為布林值時依之;否則呼叫strictDefault()(於每次呼叫時求值,預設讀環境變數E2E_STRICT_CAPTURE為'1')。 動畫區座標須與截圖之座標系一致:全頁截圖為頁面座標(視窗座標加捲動量)、視窗截圖為視窗座標,給clip時再減去clip原點 (smilRectBasis預設'auto'即依shotOpts自動決定)。2026-09-28前四個姊妹專案之全頁截圖皆用視窗座標,頁面捲動時遮罩落錯位置、動畫露出, 且Chrome暫停視窗外之動畫而使連拍「穩定」於不固定影格。smilRectBasis設'viewport'可重現舊行為(僅供等價對照,不建議) 動畫區之處理(imgSmilFill,2026-09-28):'static'(預設)先以區外左側像素色填底,再貼上以sharp算繪之「去掉動畫元素之SVG」靜態影格—— 手冊用圖看得到真實圖示(技能 §8.2「該讓讀者看到的畫面不填黑」,原填黑使連線中畫面之轉圈成黑方塊),且輸入固定故輸出決定; 區域超出截圖邊界者退回填黑。'black'為2026-09-28前之行為(僅供等價對照)。

Source:
Example
import captureStable from 'w-package-tools-e2e/src/captureStable.mjs'
import waitColResizeOverlay from 'w-package-tools-e2e/src/waitColResizeOverlay.mjs'
import waitDrawerReady from 'w-package-tools-e2e/src/waitDrawerReady.mjs'

let buf = await captureStable(page, { settle: [waitColResizeOverlay, waitDrawerReady] })
Parameters:
Name Type Attributes Default Description
page Page

輸入Playwright之Page

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
maxRetries Number <optional>
8

輸入最多重拍次數,預設8

intervalMs Number <optional>
200

輸入重拍間隔毫秒數,預設200

initialWaitMs Number <optional>
1500

輸入初始等待毫秒數,預設1500

strict Boolean <optional>

輸入是否未穩定即拋錯布林值,未給(或null)時由strictDefault決定

strictDefault function <optional>

輸入strict未給時之判斷函數()=>Boolean,預設()=>process.env.E2E_STRICT_CAPTURE==='1'

settle Array <optional>
[]

輸入settle掛鉤陣列,每個為async(page)

beforeShots Array <optional>
[]

輸入連拍前掛鉤陣列,每個為async(page)

freezeSvg Boolean <optional>
true

輸入是否凍結inline SVG之SMIL動畫布林值,預設true

waitFonts Boolean <optional>
true

輸入是否等document.fonts.ready布林值,預設true

maskImgSmil Boolean <optional>
true

輸入是否處理內之SVG動畫區布林值,預設true

imgSmilFill String <optional>
'static'

輸入動畫區之處理方式字串,'static'(貼靜態影格)或'black'(填黑),預設'static'

smilRectBasis String <optional>
'auto'

輸入動畫區之座標基準字串,'auto'(全頁截圖用'page'、視窗截圖用'viewport')、'page'(視窗座標加捲動量)或'viewport',預設'auto';有shotOpts.clip時一律再減去clip原點

shotOpts Object <optional>
{fullPage:true,animations:'disabled'}

輸入page.screenshot之參數物件,與預設值淺合併(例如{fullPage:false}或{clip})

maskFn function <optional>
maskRegions

輸入遮罩函數,預設maskRegions

Returns:

回傳Promise,resolve回傳PNG之Buffer

Type
Promise

(async) captureStableWithBox(page, target, optopt) → {Promise}

Description:
  • 全頁穩定截圖並以紅框標注本張之觀看或操作區(截圖後以sharp合成,不注入DOM),可另遮黑非決定性區域

    target:CSS選擇器字串、Playwright Locator、視窗座標矩形{x,y,width,height}、量測型目標{scroll,measure,label},或以上混合之陣列(多個取聯集畫「一個」框)。 量測型目標:measure(page)回傳視窗矩形、矩形陣列或null,於「捲入與等待之後」呼叫(故量到的是截圖當下之版面);scroll為捲入用之選擇器或Locator(可省略); probe為被蓋住檢查用之選擇器或Locator(可省略,省略者不做被蓋住檢查)。 框對象須由頁面內容決定(例如表格之標頭與可見資料列、清單之可見項目)時用之,見gridContentBox、itemsUnionBox。 流程:第一個非矩形目標先捲入視窗(逾時不中斷) → 等待preWaitMs並把滑鼠移到(0,0) → 量各目標與遮罩之視窗矩形及捲動量(皆在截圖「之前」量) → capture(預設captureStable,其餘opt原樣透傳,例如strict、settle) → 遮罩(先) → 紅框(後,框永遠可見)。 mask:選擇器字串,或{sel,fixedWidth}(自元素右緣往左取固定寬,數字位數變動時黑塊邊界不浮動)。 clampTo:'buffer'(框夾在整張截圖內)或'viewport'(框夾在截圖當下之視窗內,全頁截圖時加捲動量) 靜默無框防護(2026-09-28):目標全數找不到或量得尺寸為 0 時於截圖前拋錯;框夾邊後過小(composeBox 將不畫)時於合成後拋錯—— 標準圖每張皆須有框(技能 §7.1),原「找不到即回無框圖」會產出永遠比對通過之無框標準圖(SSO stainfor E2E-002 殷鑑)。 確屬設計如此者(極少)須顯式傳 allowNoBox:true。 被蓋住檢查(2026-09-28):選擇器/Locator 目標(與量測型目標之 probe)之中心點(夾在視窗內)以 elementFromPoint 取最上層元素, 若非目標本身、其子孫或祖先(例如彈窗遮罩、蓋在上面之面板)即視為「框了被蓋住的東西」——技能 §7.3-4 規定改框蓋在上面者。 coverCheck:'throw'(預設,可由環境變數 E2E_COVER_CHECK 覆寫)、'warn'(印 [box-covered] 告警,供全量盤查)、'off'(確屬設計者)。

Source:
Example
import captureStableWithBox from 'w-package-tools-e2e/src/captureStableWithBox.mjs'

let buf = await captureStableWithBox(page, '.ag-root-wrapper')
let buf2 = await captureStableWithBox(page, [page.locator('.ag-row').filter({ hasText: '帳號' })], { mask: [{ sel: '.clock', fixedWidth: 80 }] })
Parameters:
Name Type Attributes Default Description
page Page

輸入Playwright之Page

target String | Object | Array

輸入框選目標

opt Object <optional>
{}

輸入設定物件,預設{},除下列鍵外皆透傳給capture

Properties
Name Type Attributes Default Description
mask Array <optional>
[]

輸入遮罩目標陣列,預設[]

clampTo String <optional>
'buffer'

輸入紅框夾邊範圍字串,'buffer'或'viewport',預設'buffer'

guardSmall Boolean <optional>
true

輸入框過小時是否不畫布林值,預設true

allowNoBox Boolean <optional>
false

輸入是否容許無框(目標找不到或框過小時回傳無框圖而不拋錯)布林值,預設false

coverCheck String <optional>

輸入被蓋住檢查模式字串,'throw'、'warn'或'off',預設讀環境變數E2E_COVER_CHECK,未設為'throw'

boxOpts Object <optional>
{}

輸入透傳給composeBox之樣式參數物件(pad、margin、strokeWidth、color、radius),預設{}

scrollTimeoutMs Number <optional>
8000

輸入捲入視窗之逾時毫秒數,預設8000

scrollTall Boolean <optional>
true

輸入目標比其可視區大且已部分入鏡時是否仍捲入布林值,預設true(向後相容,並印[box-scroll]告警);false則不捲、框其可見部分

preWaitMs Number <optional>
300

輸入捲入後之等待毫秒數,預設300

capture function <optional>
captureStable

輸入截圖函數(page,opt)=>Promise,預設captureStable

Returns:

回傳Promise,resolve回傳PNG之Buffer

Type
Promise

(async) collectDomText(page, optopt) → {Promise}

Description:
  • 收集頁面DOM之文字節點內容(略過SCRIPT、STYLE、NOSCRIPT),以分隔字串串接,供語意斷言失敗時印出頁面內容

    注意:只走訪DOM文字節點,不判斷元素是否可見(隱藏元素之文字亦會收入);每個文字節點先trim,空白者略過

Source:
Example
import collectDomText from 'w-package-tools-e2e/src/collectDomText.mjs'

console.log(await collectDomText(page))
// => 'Tokens | 金鑰清單 | test-token-1 | ...'
Parameters:
Name Type Attributes Default Description
page Page

輸入Playwright之Page

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
sep String <optional>
' | '

輸入分隔字串,預設' | '

maxLen Number <optional>
2000

輸入回傳字串最大長度,預設2000

Returns:

回傳Promise,resolve回傳串接後之字串

Type
Promise

compareImageDirs(dirA, dirB, optopt) → {Object}

Description:
  • 逐檔比對兩個目錄內同名之PNG:位元組是否相同、解碼後RGBA是否相同、反鋸齒感知之像素差異數

    用途:行為等價證明——把新管線之產製輸出導到暫存目錄(dirA),與現行標準圖目錄(dirB)逐檔比對,不動標準圖本身。 判準分層:bytesEqual(檔案位元組相同,受PNG編碼器與版本影響) ⊂ rgbaEqual(像素完全相同) ⊂ withinTolerance(pixelmatch差異數≤maxDiffPixels)。 等價層(2026-09-28):equivalent=rgbaEqual,或RGBA相異之像素全數落在opt.drift登錄之漂移點(座標+各通道差上限)內—— 不以「pixelmatch計數0」為等價:threshold 0.1時整片均勻亮度差在約26級以內之像素一律不計,會掩蓋肉眼可見之全域變化。 各檔另報rgbaDiff(RGBA相異像素數)與rgbaDiffBox(其外接框),供歸因。 以dirA之檔案為準逐一比對;只存在於dirB者列於onlyInB(部分產製時屬正常,由呼叫端判斷)

Source:
Example
import compareImageDirs from 'w-package-tools-e2e/src/compareImageDirs.mjs'

let r = compareImageDirs('./test/_tmp/regen-out', './test/pics/tokens')
console.log(r.summary)
// => { total: 50, bytesEqual: 50, rgbaEqual: 50, equivalent: 50, withinTolerance: 50, exceeded: 0, missingInB: 0, sizeMismatch: 0, onlyInB: 0 }
Parameters:
Name Type Attributes Default Description
dirA String

輸入受檢目錄字串(例如產製輸出之暫存目錄)

dirB String

輸入對照目錄字串(例如現行標準圖目錄)

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
pattern RegExp <optional>
/\.png$/i

輸入納入之檔名樣式,預設/.png$/i

threshold Number <optional>
0.1

輸入pixelmatch之色差門檻,預設0.1

maxDiffPixels Number <optional>
100

輸入容許之真不同像素數,預設100

drift Array <optional>
[]

輸入登錄漂移點陣列[{x,y,maxDelta,files}],x,y為像素座標、maxDelta為各通道差上限、files為適用檔名之RegExp(省略則全部),預設[]

Returns:

回傳物件{files,onlyInB,summary},files為各檔結果陣列[{file,status,bytesEqual,rgbaEqual,rgbaDiff,rgbaDiffBox,equivalent,numDiff,withinTolerance}],status為'ok'、'missingInB'或'sizeMismatch'

Type
Object

createBaselineGate(opt) → {Object}

Description:
  • 建立標準圖產製之篩選器:解析--names、--langs、--write-mode與E2E_BASELINE_OUT_DIR,於「截圖之前」決定要跑哪些案例、寫哪些階段圖

    案例宣告:cases為[{name,stages,compareOnly,langs}],name為案例鍵;stages為該案產出之階段圖鍵陣列(可省略,省略時單張案例之圖鍵即name, 多階段案例則於執行後以編號前綴比對);compareOnly為true代表只比對不產圖(共用他案標準圖);langs限定語系(預設全部)。

    --names 每項之解析順序(每項可帶<語系>-前綴限定語系,不帶則套用全部已選語系): ①完全等於某案例宣告之階段圖鍵 → 只寫該張(案例鍵與首張階段圖鍵同名時亦只寫該張); ②完全等於案例鍵,或為案例鍵之邊界前綴(E2E-005命中E2E-005-xxx,不命中E2E-0051-xxx) → 寫該案全部階段; ③為已宣告階段圖鍵之邊界前綴 → 寫命中之各張; ④案例未宣告stages而編號相符 → 執行該案,只寫圖鍵等於該項或以「該項-」開頭者,執行後未產出即拋錯(不靜默略過); 命中compareOnly案例、或不符任何案例與圖鍵者一律拋錯並列出可用鍵。--langs須完全等於已宣告語系。 未篩選(全部案例)時casesFor含只比對案例:產製端照跑其流程與語意斷言、不寫圖(runBaselineCase於regen遇compareOnly即不寫), 與mocha REGEN型專案一致(2026-09-28前直跑型專案之產製端從不執行只比對案例)。 孤兒檢查:給pathOf(或由runBaselineCase經usePathOf告知)時,finalize()以findOrphanBaselines靜態比對宣告圖鍵與標準圖目錄,有孤兒即拋錯並列出。 --write-mode:all(預設,全寫)、missing(只寫標準圖不存在者,追加案例用)、changed(只寫與現行標準圖差異超過容差或尺寸不同者,避免重寫已審過之圖)。 E2E_BASELINE_OUT_DIR:有值時寫檔改導至該目錄(等價驗證用),比對端讀取之標準圖路徑不受影響

Source:
Example
import createBaselineGate from 'w-package-tools-e2e/src/createBaselineGate.mjs'

let gate = createBaselineGate({
    langs: ['eng', 'cht'],
    cases: [
        { name: 'E2E-001-list-loaded' },
        { name: 'E2E-005-grant-perms-save-success', stages: ['E2E-005-1-click-perms', 'E2E-005-2-perms-list'] },
    ],
})
console.log(gate.describe())
for (let lang of gate.langs) {
    for (let c of gate.casesFor(lang)) {
        //...執行案例, 每張圖以 gate.shouldWrite(lang, c.name, key) 決定是否寫檔...
    }
}
gate.finalize()
Parameters:
Name Type Description
opt Object

輸入設定物件

Properties
Name Type Attributes Default Description
langs Array

輸入已宣告之語系陣列

cases Array

輸入案例宣告陣列(元素為物件或案例鍵字串)

argv Array <optional>
process.argv

輸入命令列參數陣列,預設process.argv

env Object <optional>
process.env

輸入環境變數物件,預設process.env

idPattern RegExp <optional>
/^(E2E-\d+)(?=-|$)/

輸入自鍵取出案例編號之樣式,預設/^(E2E-\d+)(?=-|$)/

outDirEnv String <optional>
'E2E_BASELINE_OUT_DIR'

輸入輸出目錄之環境變數名稱字串,預設'E2E_BASELINE_OUT_DIR'

threshold Number <optional>
0.1

輸入write-mode為changed時pixelmatch之色差門檻,預設0.1

maxDiffPixels Number <optional>
100

輸入write-mode為changed時容許之真不同像素數,預設100

pathOf function <optional>

輸入標準圖路徑函數(lang,key)=>路徑字串,供孤兒檢查;省略時由runBaselineCase首次產製時經usePathOf告知,皆無則不檢查孤兒

Returns:

回傳篩選器物件{filtered,langs,writeMode,outDir,casesFor(lang),shouldWrite(lang,caseName,stageKey),noteProduced(lang,caseName,stageKey),decideWrite(buf,baselinePath),outPath(baselinePath),usePathOf(fn),orphans(),finalize(),describe()}

Type
Object

createKnownDefect(message, optopt) → {Error}

Description:
  • 建立「已知缺陷」錯誤:案例走到已登錄於spec〈已知落差〉之產品缺陷徵狀時拋出, 產製端跳過該案例不寫圖(壞掉的畫面不得凍結為標準圖),比對端交由測試框架標為pending

    錯誤物件帶knownDefect:true與ref(〈已知落差〉之條目識別);runBaselineCase據此分流

Source:
Example
import createKnownDefect from 'w-package-tools-e2e/src/createKnownDefect.mjs'

if (await page.locator('.webpack-dev-server-client-overlay').count() > 0) {
    throw createKnownDefect('儲存後出現未捕捉例外覆蓋層', { ref: 'spec/流程_xxx.md 已知落差 2026-09-27' })
}
Parameters:
Name Type Attributes Default Description
message String

輸入徵狀描述字串

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
ref String <optional>
''

輸入已知落差條目識別字串(例如spec檔名與日期),預設''

Returns:

回傳帶knownDefect標記之錯誤物件

Type
Error

createServiceManager(opt) → {Object}

Description:
  • 建立e2e服務生命週期管理器(沿用政策reuse):port已有服務即沿用(不spawn也不負責關),沒有才spawn並等就緒;只殺自己spawn的

    startServersOnce:依services順序逐一處理;每個服務各自一個一次性狀態(進行中之呼叫共用同一個promise)。 失敗語意failureMode:'once'(預設,首次呼叫拋錯,之後之呼叫不再重試也不再拋錯;沿用w-web-sso現行語意)或'sticky'(之後之呼叫持續拋出同一錯誤,直到cleanup)。 restart:有自建行程者先同步殺整棵樹並等port釋放(有上限);等不到釋放、或無自建行程而port被佔用者,killForeignOnRestart為true才以killPortListeners殺監聽者 (只可用於該port專屬本專案且已於專案映射表明文登錄之情形),否則拋錯(自建殺後仍被佔者只記錄並繼續,沿用前版語意);之後以args與env(與process.env淺合併,只作用於本次)spawn並等就緒。restart不改變一次性狀態。 服務可給beforeSpawn掛鉤(async({phase,args,env,hookArg})):每次spawn之前呼叫並等待,phase為'start'(startServersOnce確定要spawn時)或'restart', hookArg為restart之opt.hookArg(例如{reseed:true});用於須在服務啟動前完成之前置(例如資料庫須在後端開啟前重建)。 cleanup:同步(exit與訊號處理器內非同步不會被等待);依services反序殺自建行程、重置所有一次性狀態(使之後之startServersOnce重新偵測)、呼叫onCleanup。 另有政策own(只用自建實例、port被佔即報錯、跨行程互斥)未於本版提供,參考w-web-task之test/tools/harnessLifecycle.mjs

Source:
Example
import { spawn } from 'child_process'
import createServiceManager from 'w-package-tools-e2e/src/createServiceManager.mjs'

let sm = createServiceManager({
    services: [
        { name: 'backend', port: 11007, readyTimeoutMs: 30000, spawn: ({ args, env }) => spawn('node', ['srv.mjs', ...args], { stdio: 'ignore', env }) },
        { name: 'frontend', port: 8080, readyTimeoutMs: 90000, spawn: () => spawn('npm', ['run', 'serve'], { stdio: 'ignore', shell: true }) },
    ],
    killForeignOnRestart: true,
})
await sm.startServersOnce()
await sm.restart('backend', { args: ['./test/_tmp/settings-e2e.json'] })
sm.cleanup()
Parameters:
Name Type Description
opt Object

輸入設定物件

Properties
Name Type Attributes Default Description
services Array

輸入服務陣列,每個為{name,port,spawn({args,env})=>ChildProcess,readyTimeoutMs,url,startNote,beforeSpawn},readyTimeoutMs預設30000,url預設http://:/,beforeSpawn可省略

host String <optional>
'127.0.0.1'

輸入主機字串,預設'127.0.0.1'

probe function <optional>

輸入就緒探測函數async(svc)=>Boolean,預設probeHttp(svc.url,{timeoutMs:1500})(任何HTTP回應即就緒)

pollMs Number <optional>
500

輸入就緒輪詢間隔毫秒數,預設500

failureMode String <optional>
'once'

輸入啟動失敗後之語意字串,'once'或'sticky',預設'once'

killForeignOnRestart Boolean <optional>
false

輸入restart時可否殺非自建之port監聽者布林值,預設false

portReleaseTimeoutMs Number <optional>
5000

輸入等待port釋放之上限毫秒數,預設5000

portReleasePollMs Number <optional>
200

輸入等待port釋放之輪詢間隔毫秒數,預設200

killTree function <optional>
killOwnTree

輸入同步殺自建行程樹函數(child),預設killOwnTree

killListeners function <optional>
killPortListeners

輸入同步殺port監聽者函數(port)=>{pids,killed},預設killPortListeners

sleep function <optional>

輸入等待函數async(ms),預設setTimeout

onCleanup function <optional>
null

輸入cleanup末尾之同步回呼(例如刪臨時設定檔),預設null

log function <optional>
console.log

輸入輸出函數,預設console.log

Returns:

回傳物件{startServersOnce({only}),restart(name,{args,env,hookArg}),cleanup(),state(name)}

Type
Object

createTempSettings(opt) → {Object}

Description:
  • 建立臨時設定檔之產生器:以基底設定(JSON5)淺合併overrides後寫出純JSON,供e2e以不同設定重啟後端

    落點須為測試中介資料目錄(例如test/_tmp,gitignore),絕不可為AI暫存區./tmp(隨時被整個清除,後端讀不到設定即假失敗)。 檔名為settings-e2e-<pid>-<序號>.json;cleanupTempSettings逐一刪除本產生器建立之檔案,目錄空了再刪目錄。 forbiddenKeys內之鍵不可覆寫(例如測試實例固定之port與log目錄),覆寫即拋錯

Source:
Example
import createTempSettings from 'w-package-tools-e2e/src/createTempSettings.mjs'

let { genTempSettings, cleanupTempSettings } = createTempSettings({ basePath: './settings.json', tmpDir: './test/_tmp' })
let p = genTempSettings({ allowUserRegistration: false })
//...以p重啟後端...
cleanupTempSettings()
Parameters:
Name Type Description
opt Object

輸入設定物件

Properties
Name Type Attributes Default Description
basePath String

輸入基底設定檔路徑字串(JSON5),於每次產生時讀取;相對路徑以當下工作目錄解析

tmpDir String

輸入臨時設定檔目錄字串

forbiddenKeys Array <optional>
[]

輸入不可覆寫之鍵陣列,預設[]

prefix String <optional>
'settings-e2e'

輸入檔名前綴字串,預設'settings-e2e'

Returns:

回傳物件{genTempSettings(overrides),cleanupTempSettings(),files()}

Type
Object

(async) cropRegion(buf, rect) → {Promise}

Description:
  • 自截圖buffer裁切指定矩形(夾在buffer內),供把動態區域存成參考小圖

Source:
Example
import cropRegion from 'w-package-tools-e2e/src/cropRegion.mjs'

let ref = await cropRegion(buf, { x: 240, y: 330, w: 1000, h: 380 })
Parameters:
Name Type Description
buf Buffer

輸入PNG之Buffer

rect Object

輸入矩形物件{x,y,w,h}(buffer座標)

Returns:

回傳Promise,resolve回傳裁切後之PNG Buffer,矩形完全在圖外時reject

Type
Promise

diffBaselineSnapshots(before, after) → {Object}

Description:
  • 比對兩次snapshotBaselines之結果,列出新增、刪除、內容改變、內容相同但被重寫(mtime改變)之檔案

    touched(內容相同但mtime改變)亦視為「被重寫」:已審過之圖被重產等於換掉真理,即使內容相同, 故「零標準圖變動」須added、removed、changed、touched皆為空(unchanged為true)

Source:
Example
import snapshotBaselines from 'w-package-tools-e2e/src/snapshotBaselines.mjs'
import diffBaselineSnapshots from 'w-package-tools-e2e/src/diffBaselineSnapshots.mjs'

let before = snapshotBaselines('./test/pics')
//...
let r = diffBaselineSnapshots(before, snapshotBaselines('./test/pics'))
if (!r.unchanged) throw new Error(`標準圖被改動: ${JSON.stringify(r)}`)
Parameters:
Name Type Description
before Object

輸入先前之快照物件

after Object

輸入之後之快照物件

Returns:

回傳物件{added,removed,changed,touched,unchanged},前四者為相對路徑陣列(已排序),unchanged為四者皆空之布林值

Type
Object

findOrphanBaselines(opt) → {Array}

Description:
  • 依案例宣告找出標準圖目錄中之孤兒圖(靜態比對宣告,與實際執行了哪些案例無關)

    目錄與檔名前後綴由pathOf(lang,key)推得(圖鍵須在檔名內);目錄內符合「前綴…後綴」之檔案,若其圖鍵不是任何案例(含只比對案例)於該語系所宣告之圖鍵即為孤兒。 預期圖鍵:案例之語系(langs,預設全部)含該語系者之stages;未宣告stages者以案例鍵為單張圖鍵,並視以其編號(E2E-NNN)為前綴之圖鍵為該案所有(執行後才決定之多階段圖)。 以_開頭之貼圖參考片段不符前綴而自然排除。因不依賴執行紀錄,--grep/--names局部執行、案例失敗、knownDefect皆不致誤報。 why:改名後重產只寫新名、從不刪舊名,舊圖成為無人產出亦無人比對之孤兒(PERM 2026-09-17 改版留下12張,至09-28才被發現)。

Source:
Example
import findOrphanBaselines from 'w-package-tools-e2e/src/findOrphanBaselines.mjs'

let orphans = findOrphanBaselines({ cases, langs: ['eng', 'cht'], pathOf: (lang, key) => `./test/pics/login/login-${lang}-${key}.png` })
console.log(orphans)
// => [] 或 ['test\\pics\\login\\login-eng-E2E-099-old.png', ...]
Parameters:
Name Type Description
opt Object

輸入設定物件

Properties
Name Type Attributes Default Description
cases Array

輸入案例宣告陣列[{name,stages,langs}]或案例鍵字串

langs Array

輸入已宣告之語系陣列

pathOf function

輸入標準圖路徑函數(lang,key)=>路徑字串,須與runBaselineCase之pathOf相同

idPattern RegExp <optional>
/^(E2E-\d+)(?=-|$)/

輸入自圖鍵取出案例編號之樣式,預設/^(E2E-\d+)(?=-|$)/

Returns:

回傳孤兒圖路徑陣列(依語系、檔名排序)

Type
Array

getE2eMode(optopt) → {Object}

Description:
  • 判定e2e執行模式:是否為標準圖產製(regen)、是否處於診斷環境、截圖是否採strict

    regen:命令列含--baseline或環境變數E2E_REGEN為'1';diag:環境變數E2E_BARE或E2E_DIAG有值;strictCapture:環境變數E2E_STRICT_CAPTURE為'1'。 診斷閘門:regen且diag時拋錯(診斷環境下絕不可寫入正式標準圖,否則把診斷態凍結為真理),可以opt.guard=false關閉(僅供測試)

Source:
Example
import getE2eMode from 'w-package-tools-e2e/src/getE2eMode.mjs'

let { regen } = getE2eMode()
Parameters:
Name Type Attributes Default Description
opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
argv Array <optional>
process.argv

輸入命令列參數陣列,預設process.argv

env Object <optional>
process.env

輸入環境變數物件,預設process.env

guard Boolean <optional>
true

輸入是否啟用診斷閘門布林值,預設true

Returns:

回傳模式物件{regen,diag,strictCapture}

Type
Object

gridContentBox(gridSel, optopt) → {Object}

Description:
  • 【ag-grid】紅框「表格內容」之量測型目標:標頭(含浮動篩選列) ∪ 可見資料列,夾在表格框內;無資料列時改取標頭 ∪「無資料」訊息(外擴 INK_PAD)

    交給captureStableWithBox作為target(或其陣列之一員)。技能 §7.2「表格出現/重算 → 標頭+有資料的列,夾在表格框內」、 §7.3-2「不框整區空白」:直接框表格外框(.ag-root-wrapper)時,列數少的表格會把下方大片空白一起框進去(業主問「這到底是圈什麼」)。 量測於捲入與等待之後、截圖之前進行;可見資料列=與資料捲動區(.ag-body-viewport)有交集且高度大於0之.ag-row(含左側固定欄之列)。 欄位未撐滿表格寬時(標頭容器撐滿、欄位只佔左側,例:兩欄之 IP 清單),右側空白 ≥ 200px 且 ≥ 框寬 30% 即把右緣收在最後一欄(2026-09-28)。 表格框找不到、或標頭與資料列、訊息皆量不到時回傳null(captureStableWithBox因此拋錯,不產出無框圖)。 「無資料」訊息為無可見邊界之文字,其矩形即文字行框:直接取聯集時框之內緣只距行框 1px,字形墨跡至框內緣僅 3–4px (2026-09-30 量測 w-web-sso/w-web-perm 之空表標準圖),低於技能 §7.3-8 要求之約 5px;比照 itemsUnionBox 之 fit 與 inkRect (無可見邊界之文字一律外擴 INK_PAD),訊息矩形四周外擴 noRowsPad 後再取聯集。noRowsPad:0 可重現舊行為(供等價對照)。

Source:
Example
import gridContentBox from 'w-package-tools-e2e/src/gridContentBox.mjs'

let buf = await captureStableWithBox(page, gridContentBox('.ag-root-wrapper'))
Parameters:
Name Type Attributes Default Description
gridSel String

輸入表格外框之選擇器字串,例如'.ag-root-wrapper'或'.dlg .ag-root-wrapper',取第一個命中者

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
noRowsSel String <optional>
'.ag-overlay-no-rows-center'

輸入「無資料」訊息之選擇器字串(於表格框內查找),預設'.ag-overlay-no-rows-center'

noRowsPad Number <optional>
INK_PAD

輸入「無資料」訊息矩形之外擴像素,預設INK_PAD(4)

Returns:

回傳量測型目標物件{label,scroll,measure}

Type
Object

isChildAlive(child) → {Boolean}

Description:
  • 判斷子行程是否仍存活('exit'事件尚未發生)

Source:
Example
import isChildAlive from 'w-package-tools-e2e/src/isChildAlive.mjs'

console.log(isChildAlive(child))
// => true
Parameters:
Name Type Description
child ChildProcess

輸入子行程

Returns:

回傳是否存活

Type
Boolean

itemsUnionBox(itemSel, optopt) → {Object}

Description:
  • 紅框「可見項目之聯集」之量測型目標:清單、樹、選單、訊息時間軸、工具列上之項目、一行訊息文字等,框實際有內容之項目而非整欄/整區

    交給captureStableWithBox作為target(或其陣列之一員)。技能 §7.2「清單/樹重新列出、展開縮合、進入頁面 → 當時可見之項目列的聯集」、 §7.3-2「不框整欄/整區的空白」。量測於捲入與等待之後、截圖之前進行。 項目:選擇器字串、其陣列(以逗號併為一個選擇器),或Playwright Locator(例:getByText 取得之訊息文字);選擇器與Locator皆經同一個頁內量測函式。 可見項目=命中項目、寬高大於0、非display:none/visibility:hidden/opacity:0,且與範圍容器(within;未給則為視窗)有交集者; 聯集再夾在範圍容器內(容器可捲動時,捲出可視範圍之項目不計入)。一個都量不到時回傳null(captureStableWithBox因此拋錯)。 fit:依「元素自身有無可見邊界」決定量法——有(底色異於其背後底色、至少三邊之邊框、陰影、背景圖,或圖片/表單等替換元素;晶片、按鈕、 灰頁上之白卡、底色列)量元素本身;無(透明容器、白頁上之白底區塊、整行文字、開關與其標籤、勾選列、無內距之欄位與表單欄) 改量其可見內容(有可見邊界之子元素框與文字行框之聯集;透明子元素之內距不計),且一律外擴 inkPad。 用於「一行字卻框整列寬」(技能 §7.2 提示訊息/狀態文字→訊息本體)、「一列晶片卻框整列寬」與「無邊界之元件緊貼文字」。 inkPad(2026-09-28):紅框幾何為目標 ±6 且 5px 描邊內縮,無可見邊界之目標緊貼墨跡時框內僅剩約 1px、紅框壓字 (SSO login E2E-006、stainfor E2E-002、tokens 權限勾選列與編輯模式開關、連線狀態文字殷鑑);外擴 4px 後框內留白約 5px,與自帶內距之按鈕相當。 子元素(勾選框、開關軌道、圖示、輸入框)與文字同外擴(同日改:原只外擴文字,子元素框貼框線 0–1px、四邊留白不一致)。 fit 下框線不壓框外內容(2026-09-28,業主:「亂框亂壓導致遮蔽有效資訊或使用者誤解就是問題」): ①相鄰項目(自元素往上至多 4 層之緊鄰兄弟,上下左右四向各取最近一層):框線置於兩者可見範圍(有可見邊界者為元素框,否則為子元素與文字之聯集) 間隙正中,不壓到鄰項之字(例:權限清單緊鄰列、模式段與名稱段),也不因鄰項元素框之透明內距而貼回目標自身之字(例:開關右側之圖示鈕); 無可見內容之兄弟(空白間隔)不算鄰項; ②浮層(自身或祖先為 position:fixed,例:下拉清單、浮出面板;全畫面者除外):浮層外之頁面內容不是 DOM 兄弟,改以命中測試找目標四周、 浮層範圍外最近之可見內容(文字、晶片/按鈕/觸發區級之有邊界元件、圖示與表單元件;大容器之底色與整張圖表不算;遮罩後之背景不算), 框線置於與其間隙正中;浮層本身為目標時以其內容之可見範圍計,框線可退入浮層內距;四周無內容者照常外擴 (同日首版一律畫在浮層內側,內距小之清單框線反而貼字:PERM 時間清單下緣 2px、關聯模式清單左緣 1px)。

Source:
Example
import itemsUnionBox from 'w-package-tools-e2e/src/itemsUnionBox.mjs'

let buf = await captureStableWithBox(page, itemsUnionBox('.tree-item', { within: '[data-fmid="channel-tree"]' }))
let buf2 = await captureStableWithBox(page, ['img.spinner', itemsUnionBox(page.getByText('Connecting...').first(), { fit: true })])
Parameters:
Name Type Attributes Default Description
itemSel String | Array | Object

輸入項目之選擇器字串、其陣列,或Playwright Locator

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
within String <optional>
''

輸入範圍容器之選擇器字串(項目於其內查找,聯集夾在其內),取第一個命中者,預設''代表整頁與視窗

scroll String | Object <optional>

輸入先捲入視窗之選擇器字串或Locator,預設同within;within亦未給時,項目為Locator者捲該Locator,否則不捲動

fit Boolean <optional>
false

輸入無可見邊界之項目是否改量其內容範圍並外擴文字墨跡布林值,預設false

inkPad Number <optional>
4

輸入fit下文字墨跡之外擴像素,預設4

Returns:

回傳量測型目標物件{label,scroll,measure}

Type
Object

killOwnTree(child, optopt) → {Boolean}

Description:
  • 同步殺自建子行程之整棵行程樹;已結束或無PID者不下殺(其PID可能已被系統回收給無關行程)

    Windows以同步taskkill /F /T /PID <pid>;其他平台先殺行程群組,失敗再退回child.kill('SIGKILL')。 同步:exit/SIGINT/SIGTERM處理器內非同步spawn不會被等待。是否真的結束由呼叫端回驗(waitChildExit或pidExists)。 移植自w-web-task test/tools/harnessProc.mjs

Source:
Example
import killOwnTree from 'w-package-tools-e2e/src/killOwnTree.mjs'

killOwnTree(backendChild)
Parameters:
Name Type Attributes Default Description
child ChildProcess

輸入自建子行程

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
exec function <optional>
execSync

輸入同步執行指令函數,預設execSync

platform String <optional>
process.platform

輸入作業系統字串,預設process.platform

killGroup function <optional>

輸入殺行程群組函數,預設process.kill(-pid,'SIGKILL')

Returns:

回傳是否有下殺

Type
Boolean

killPortListeners(port, optopt) → {Object}

Description:
  • 殺監聽指定port之全部行程(含其行程樹;同步)

    【政策】本函數會殺非本行程所建立之行程,只可用於「該port專屬本專案」且已於專案映射表明文登錄之情形 (例如重啟本專案後端而port被手動啟動之同專案後端佔用、逐檔隔離runner每檔前換新後端);絕不可用於他專案或共用port。 Windows以taskkill /F /T /PID,其他平台以kill -9;exclude內之PID(預設本行程)不殺

Source:
Example
import killPortListeners from 'w-package-tools-e2e/src/killPortListeners.mjs'

let r = killPortListeners(11007)
console.log(r)
// => { pids: ['18168'], killed: ['18168'] }
Parameters:
Name Type Attributes Default Description
port Number

輸入port

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
exec function <optional>
execSync

輸入同步執行指令函數,預設execSync

platform String <optional>
process.platform

輸入作業系統字串,預設process.platform

exclude Array <optional>
[process.pid]

輸入不殺之PID陣列,預設[process.pid]

Returns:

回傳物件{pids,killed},pids為查得之監聽PID陣列(查詢工具不可用時為null),killed為已下殺之PID陣列

Type
Object

(async) launchBrowser(optopt) → {Promise}

Description:
  • 以確定性渲染旗標啟動無頭Chromium,為e2e測試端、標準圖產製端與探測腳本之唯一啟動出口

    預設為無頭並帶chromiumLaunchArgs六旗標;opt.args另附加於六旗標之後,其餘鍵原樣傳給launchChromium(即chromium.launch)。 所需版本之瀏覽器不存在時由launchChromium先行下載

Source:
Example
import launchBrowser from 'w-package-tools-e2e/src/launchBrowser.mjs'

let browser = await launchBrowser()
let page = await browser.newPage()
await page.goto('http://127.0.0.1:8080')
await browser.close()
Parameters:
Name Type Attributes Default Description
opt Object <optional>
{}

輸入chromium.launch之設定物件,預設{}

Properties
Name Type Attributes Default Description
headless Boolean <optional>
true

輸入是否無頭布林值,預設true

args Array <optional>
[]

輸入附加於六旗標後之旗標字串陣列,預設[]

Returns:

回傳Promise,resolve回傳Browser,reject回傳錯誤

Type
Promise

(async) launchChromium(optopt) → {Promise}

Description:
  • 啟動Playwright之Chromium,所需版本之瀏覽器不存在時先自動下載再啟動

    不使用套件安裝腳本(npm 12起預設略過相依套件之install/postinstall),改於第一次啟動時以Playwright自身之CLI下載, 下載之瀏覽器為本套件所用Playwright版本指定之版本,與本機已安裝者相同,像素比對得以一致

    Linux缺少系統相依套件時先執行install-deps(非root時Playwright會自行使用sudo)再啟動; 指定系統瀏覽器(channel如'chrome')或自訂executablePath時不自動安裝

    多個程序同時首次啟動時,Playwright安裝過程有目錄鎖,只會下載一次,其餘等待後直接使用

Source:
Example
import launchChromium from 'w-package-tools-e2e/src/launchChromium.mjs'

let browser = await launchChromium({ headless: true, args: ['--disable-gpu'] })
let page = await browser.newPage()
await page.goto('http://127.0.0.1:8080')
await browser.close()
Parameters:
Name Type Attributes Default Description
opt Object <optional>
{}

輸入chromium.launch之設定物件,原樣傳入,預設{}

Returns:

回傳Promise,resolve回傳Browser,reject回傳錯誤

Type
Promise

(async) launchChromiumCore(optopt, deps) → {Promise}

Description:
  • 啟動瀏覽器之核心流程,缺少Playwright所需版本之瀏覽器時先安裝再啟動,Linux缺少系統相依套件時先安裝相依再啟動

    依賴以參數注入,供單元測試模擬各種失敗情形,實際使用請呼叫launchChromium

    僅在使用Playwright管理之瀏覽器時自動安裝(未指定executablePath,且channel為空或'chromium'), 指定系統瀏覽器(channel如'chrome')或自訂執行檔時不安裝,錯誤直接拋出

    各補救措施(安裝瀏覽器、安裝系統相依)至多各執行一次,補救後仍失敗則拋出錯誤

Source:
Parameters:
Name Type Attributes Default Description
opt Object <optional>
{}

輸入chromium.launch之設定物件,預設{}

deps Object

輸入依賴物件

Properties
Name Type Description
launch function

輸入啟動函數,傳入opt,回傳Promise

runCli function

輸入執行Playwright CLI函數,傳入參數陣列,回傳Promise

platform String

輸入作業系統字串,同process.platform

Returns:

回傳Promise,resolve回傳deps.launch之結果,reject回傳錯誤

Type
Promise

listenerPids(port, optopt) → {Array|null}

Description:
  • 查詢監聽指定port之PID(同步)

    Windows以netstat -ano(不可加-p TCP,該參數只列IPv4,會漏掉以::雙堆疊監聽者)取得後以parseListenerPids解析; 其他平台以lsof -nP -iTCP:<port> -sTCP:LISTEN -t。無人監聽回傳[],查詢工具不可用回傳null(呼叫端不得把null當成無人)。 依賴可注入,供單元測試。移植自w-web-task test/tools/harnessProc.mjs

Source:
Example
import listenerPids from 'w-package-tools-e2e/src/listenerPids.mjs'

console.log(listenerPids(11007))
// => ['18168']
Parameters:
Name Type Attributes Default Description
port Number

輸入port

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
exec function <optional>
execSync

輸入同步執行指令函數,預設execSync

platform String <optional>
process.platform

輸入作業系統字串,預設process.platform

Returns:

回傳PID字串陣列,無人回傳[],工具不可用回傳null

Type
Array | null

(async) maskRegions(buf, rects, coloropt) → {Promise}

Description:
  • 對截圖buffer之指定矩形填色(截圖後以sharp合成,不改動被測頁DOM)

    用途限於DOM層凍不到之動態內容(例如img內SVG SMIL動畫、無法固定之即時數值);絕不用來遮應被偵測之靜態UI。 預設黑色,一眼可辨為刻意遮蔽。矩形座標為buffer座標(全頁截圖時為視窗座標加捲動量),超出buffer之部分自動裁切,寬或高為0者略過

Source:
Example
import maskRegions from 'w-package-tools-e2e/src/maskRegions.mjs'

let buf = await page.screenshot({ fullPage: true })
buf = await maskRegions(buf, [{ x: 10, y: 20, w: 100, h: 30 }])
Parameters:
Name Type Attributes Default Description
buf Buffer

輸入PNG之Buffer

rects Array

輸入矩形陣列,每個元素為{x,y,w,h}

color Object <optional>
{r:0,g:0,b:0}

輸入填色物件,預設黑色

Returns:

回傳Promise,resolve回傳填色後之PNG Buffer,無有效矩形時回傳原buffer

Type
Promise

normalizeShots(result, name) → {Array}

Description:
  • 把案例執行函數之各種回傳形狀正規化為截圖陣列[{key,buf}]

    支援:①Buffer(單張,圖鍵為name);②物件{圖鍵:Buffer}(多階段);③陣列[{name,buf}]或[{key,buf}]; ④包裝物件{buf,page}或{shots,page}(執行中換頁者,page另由呼叫端取用);⑤null或undefined(無截圖)。 圖鍵重複、元素不是Buffer一律拋錯

Source:
Example
import normalizeShots from 'w-package-tools-e2e/src/normalizeShots.mjs'

normalizeShots(buf, 'E2E-001-list-loaded')
// => [{ key: 'E2E-001-list-loaded', buf }]
normalizeShots({ 'E2E-002-1-a': b1, 'E2E-002-2-b': b2 }, 'E2E-002-x')
// => [{ key: 'E2E-002-1-a', buf: b1 }, { key: 'E2E-002-2-b', buf: b2 }]
Parameters:
Name Type Description
result *

輸入案例執行函數之回傳值

name String

輸入案例鍵字串(單張時作為圖鍵)

Returns:

回傳截圖陣列[{key,buf}]

Type
Array

(async) openCasePage(browser, optopt) → {Promise}

Description:
  • 為單一案例開新的瀏覽器context與頁面,並掛上對話框(alert/confirm/prompt)處理器

    每案例fresh context,避免cookie、localStorage與快取跨案例殘留;contextOptions透傳給browser.newContext (viewport、locale、timezoneId等確定性參數依專案給定,不給即沿用Playwright預設)

Source:
Example
import openCasePage from 'w-package-tools-e2e/src/openCasePage.mjs'

let page = await openCasePage(browser)
let page2 = await openCasePage(browser, { contextOptions: { viewport: { width: 1440, height: 900 } } })
Parameters:
Name Type Attributes Default Description
browser Browser

輸入Playwright之Browser

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
contextOptions Object <optional>
{}

輸入browser.newContext之參數物件,預設{}

onDialog String | function | null <optional>
'accept'

輸入對話框處理方式,'accept'自動接受、'dismiss'自動取消、函數則自行處理(傳入dialog)、null不掛,預設'accept'

Returns:

回傳Promise,resolve回傳Playwright之Page

Type
Promise

(async) overlayImageAt(buf, overlayBuf, left, top) → {Promise}

Description:
  • 把一張已裁切成區域尺寸之小圖overlayBuf貼到截圖buffer之(left,top),超出buffer邊界之部分自動裁切

    用於貼圖覆蓋時只存「動態區域那一塊」之參考小圖(而非整頁),一看即知只覆蓋該區,其餘區域仍逐像素比對

Source:
Example
import overlayImageAt from 'w-package-tools-e2e/src/overlayImageAt.mjs'

buf = await overlayImageAt(buf, refChartBuf, 240, 330)
Parameters:
Name Type Description
buf Buffer

輸入PNG之Buffer

overlayBuf Buffer

輸入要貼上之小圖PNG Buffer

left Number

輸入貼上位置左緣像素

top Number

輸入貼上位置上緣像素

Returns:

回傳Promise,resolve回傳貼上後之PNG Buffer,貼上位置完全在圖外時回傳原buffer

Type
Promise

(async) overlayRegions(buf, rects, refBuf) → {Promise}

Description:
  • 以參考圖refBuf同座標之內容覆蓋截圖buffer之指定矩形(貼圖覆蓋,取代填黑)

    用途:動態圖表(canvas)跨行程渲染無法像素穩定、又需保留真實畫面者;產製端與比對端貼同一張參考圖,該區永遠一致,其餘區域仍逐像素比對。 refBuf須與buf同版面同尺寸(同座標取用),矩形夾在兩者邊界內

Source:
Example
import overlayRegions from 'w-package-tools-e2e/src/overlayRegions.mjs'

buf = await overlayRegions(buf, [{ x: 240, y: 330, w: 1000, h: 380 }], fs.readFileSync('./test/pics/stainfor/_staref-eng-chart.png'))
Parameters:
Name Type Description
buf Buffer

輸入PNG之Buffer

rects Array

輸入矩形陣列,每個元素為{x,y,w,h}(buffer座標)

refBuf Buffer

輸入參考圖PNG之Buffer

Returns:

回傳Promise,resolve回傳覆蓋後之PNG Buffer,無有效矩形時回傳原buffer

Type
Promise

(async) pageHasText(page, text) → {Promise}

Description:
  • 判斷頁面DOM是否有某個文字節點含指定字串(略過SCRIPT、STYLE、NOSCRIPT)

    注意:逐一比對單一文字節點,跨多個節點(例如被切開)之字串不會命中;不判斷元素是否可見

Source:
Example
import pageHasText from 'w-package-tools-e2e/src/pageHasText.mjs'

console.log(await pageHasText(page, '儲存金鑰數據成功'))
// => true
Parameters:
Name Type Description
page Page

輸入Playwright之Page

text String

輸入要尋找之字串

Returns:

回傳Promise,resolve回傳是否找到之布林值

Type
Promise

parseListenerPids(text, port) → {Array}

Description:
  • 解析netstat -ano之輸出,取出本地位址為指定port之TCP監聽列之PID(字串、去重)

    監聽列之判定:外部位址為0.0.0.0:0、[::]:0或*:*(監聽中之socket沒有對端),或狀態欄為LISTENING;前者與顯示語言無關(部分語系之Windows會在地化狀態字)。 以「本地位址結尾為:port」比對,避開外部位址含:port之連線列;IPv6位址含zone id亦適用;UDP列與表頭(在地化文字)皆略過。 移植自w-web-task test/tools/harnessProc.mjs(含其單元測試)

Source:
Example
import parseListenerPids from 'w-package-tools-e2e/src/parseListenerPids.mjs'

parseListenerPids('  TCP    0.0.0.0:11007   0.0.0.0:0   LISTENING   1234', 11007)
// => ['1234']
Parameters:
Name Type Description
text String

輸入netstat -ano之輸出字串

port Number

輸入port

Returns:

回傳PID字串陣列,無監聽者回傳[]

Type
Array

pidExists(pid, optopt) → {Boolean}

Description:
  • 同步判斷PID是否存在(exit/signal處理器內使用:事件迴圈不再推進,isChildAlive不會更新)

    以kill(pid,0)探測:成功為存在、ESRCH為不存在、EPERM為存在(無權限)。PID回收只會造成誤報存在,不會誤殺

Source:
Example
import pidExists from 'w-package-tools-e2e/src/pidExists.mjs'

console.log(pidExists(process.pid))
// => true
Parameters:
Name Type Attributes Default Description
pid Number | String

輸入PID

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Description
kill function <optional>

輸入kill函數,預設process.kill

Returns:

回傳是否存在

Type
Boolean

(async) pollUntil(label, fn, optopt) → {Promise}

Description:
  • 測試行程端之偵測驅動等待:反覆執行判斷函數直到回傳truthy,逾時即拋錯(代表真實異常,而非固定等待不夠)

    用於等待瀏覽器外之非同步結果——後端週期計時器寫入資料庫(封鎖、補登記)、背景程序產檔、日誌出現某行—— 取代「固定等N秒再讀」:計時器於負載高時延遲,固定秒數只在一台機器、一種負載下成立(2026-09-28 w-web-sso autoblock 以固定3秒等2秒週期計時器殷鑑)。與waitUntilExist分工:waitUntilExist之fn於瀏覽器頁面內執行(DOM、localStorage), 本函數之fn於測試行程執行(資料庫、檔案系統、HTTP),可使用閉包。 fn拋錯視為「尚未成立」並繼續輪詢(例如查詢之紀錄尚不存在而取值失敗),逾時訊息附最後一次錯誤。

Source:
Example
import pollUntil from 'w-package-tools-e2e/src/pollUntil.mjs'

//等後端計時器把新IP補登記進ips表, 回傳查得之紀錄
let ips = await pollUntil('ips 補登記', async () => {
    let rs = await woItems.ips.select({ ip })
    return rs.length > 0 ? rs : null
}, { timeout: 60000 })
Parameters:
Name Type Attributes Default Description
label String

輸入錯誤訊息用之對象描述字串

fn function

輸入判斷函數,可為async函數,回傳truthy代表成立

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
timeout Number <optional>
30000

輸入逾時毫秒數,預設30000

interval Number <optional>
200

輸入輪詢間隔毫秒數,預設200

Returns:

回傳Promise,resolve為fn最後一次之truthy回傳值,逾時reject

Type
Promise

(async) probeHttp(url, optopt) → {Promise}

Description:
  • 探測HTTP服務是否有回應(用於「port已有服務則沿用、否則啟動」之判斷)

    預設只要有任何HTTP回應即為true(連線失敗或逾時為false);opt.accept可限制狀態碼(例如status<500); opt.identify可讀回應內文判斷是否為本專案之服務(契約C2:health回專案識別才沿用,避免沿用到他專案之服務)

Source:
Example
import probeHttp from 'w-package-tools-e2e/src/probeHttp.mjs'

let up = await probeHttp('http://127.0.0.1:11007/')
Parameters:
Name Type Attributes Default Description
url String

輸入網址字串

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
timeoutMs Number <optional>
1500

輸入逾時毫秒數,預設1500

accept function <optional>

輸入狀態碼判斷函數(status)=>Boolean,預設全部接受

identify function <optional>

輸入內文判斷函數(text)=>Boolean,預設不讀內文

fetchFn function <optional>
fetch

輸入fetch函數(供測試注入),預設全域fetch

Returns:

回傳Promise,resolve回傳是否有(符合條件之)回應

Type
Promise

(async) probeStuckTooltip(page, optopt) → {Promise}

Description:
  • 【w-component-vue】截圖前偵測提示框殘留(captureStable之beforeShots掛鉤):captureStable已將游標移至(0,0)並初始等待後,仍顯示之hover型提示框(w-component-vue WTooltip mode='tooltip')必為殘留, 即mouseleave未送達其觸發區,拋錯使該案失敗(殘留畫面不得凍結為標準圖)。點開型浮層(mode='popup':WPopup、下拉清單)為刻意開啟,不在此列; display:none與visibility:hidden(popper之data-popper-reference-hidden)皆非畫面可見。

    背景:w-component-vue ≤2.5.23之WButtonCircle於點擊時以v-if換掉游標下之圖示(promiseUnlock或loading之載入圖示、停用遮罩),其後出現全頁遮罩或游標隨即移開時, 未採「命中節點被移除後以最近仍在DOM之祖先為目標」之瀏覽器(Chrome 144以前;Playwright ≤1.62之預設啟動參數停用BoundaryEventDispatchTracksNodeRemoval)不派發mouseleave, 提示框殘留;2.5.24起圖示層與停用遮罩pointer-events:none已修正。本函數為回歸守門,亦涵蓋他處同型成因(自寫元件之內層被移除替換)。 以提示文字判斷殘留會漏(對話框標題列Save鈕等其他文字之提示框),且.WPopperFix亦用於下拉浮層,故依元件設定(mode)辨識,文字不限。

    WTooltip未宣告name,以結構辨識:$refs含divTrigger與divContent、props.mode、data.valueTrans(w-component-vue 2.5.24之WTooltip.vue)。 本函數依賴上述內部欄位:元件改名或改寫時會找不到任何提示框而一律通過(靜默失效),升級w-component-vue時須以真元件頁複驗(hover中偵測得到、移開後為空、已開之WPopup不誤判)。 根實例:opt.rootSel有給時取該元素之__vue__;否則取window.$vo(應用掛上者),再否則取body直屬元素之__vue__;皆無則不檢查(非Vue頁面)。

Source:
Example
import captureStable from 'w-package-tools-e2e/src/captureStable.mjs'
import probeStuckTooltip from 'w-package-tools-e2e/src/probeStuckTooltip.mjs'

let buf = await captureStable(page, { beforeShots: [probeStuckTooltip] })
Parameters:
Name Type Attributes Default Description
page Object

輸入Playwright頁面物件

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
rootSel String <optional>
''

輸入掛有Vue 2根實例(vue)之元素選擇器字串,預設''(用window.$vo或body直屬元素之__vue__)

createError function <optional>

輸入依殘留文字陣列產生錯誤物件之函數(texts)=>Error,預設產生一般Error(訊息列出各提示文字)

Returns:

回傳Promise,無殘留時resolve回傳空陣列;有殘留時reject回傳錯誤物件

Type
Promise

registerCleanupHooks(cleanup, optopt) → {Object}

Description:
  • 註冊e2e收尾之兩條觸發來源與中斷備援:mocha root after(框架環境)、process之exit與中斷訊號

    背景:spawn之子行程會拖住事件迴圈,process.on('exit')要等迴圈清空才觸發而形成互鎖,故框架環境須由root after主動收尾; 直跑(非mocha,如--baseline產製)時globalThis.after不存在,由直跑主函式末尾顯式呼叫cleanup,exit與訊號處理器為備援。 處理器內只能同步收尾(非同步spawn不會被等待),故cleanup須為同步函數;teardown(可非同步,例如殺後回驗)只用於root after

Source:
Example
import registerCleanupHooks from 'w-package-tools-e2e/src/registerCleanupHooks.mjs'

registerCleanupHooks(cleanup, { afterTimeoutMs: 20000 })
Parameters:
Name Type Attributes Default Description
cleanup function

輸入同步收尾函數

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
afterTimeoutMs Number <optional>
20000

輸入root after之逾時毫秒數,預設20000

teardown function <optional>
null

輸入root after改呼叫之收尾函數(可回傳Promise),預設null代表呼叫cleanup

signals Object <optional>
{SIGINT:130,SIGTERM:143}

輸入要處理之訊號與結束碼物件,預設{SIGINT:130,SIGTERM:143}

proc Object <optional>
process

輸入process物件(供測試注入),預設process

globals Object <optional>
globalThis

輸入全域物件(供測試注入),預設globalThis

Returns:

回傳物件{mochaAfter},mochaAfter代表是否已註冊root after

Type
Object

(async) resetAgGridScroll(page, optopt) → {Promise}

Description:
  • 【ag-grid】把表格水平捲動歸零並稍候,使欄位重排(例如切換編輯模式)後之視圖確定

Source:
Example
import resetAgGridScroll from 'w-package-tools-e2e/src/resetAgGridScroll.mjs'

await resetAgGridScroll(page)
Parameters:
Name Type Attributes Default Description
page Page

輸入Playwright之Page

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
settleMs Number <optional>
300

輸入歸零後等待毫秒數,預設300

Returns:

回傳Promise,頁面無ag-grid時亦正常resolve

Type
Promise

rowBoxSel(rowIndex, optopt) → {Array}

Description:
  • 【ag-grid】框「整列」用之選擇器陣列:一列跨pinned-left與center兩容器(勾選框欄在pinned-left), 交給captureStableWithBox取聯集即框出涵蓋整列之紅框;單一.ag-row選擇器只會命中其中一個容器

    順序不是中性的:captureStableWithBox只把「第一個」目標捲入視窗,故順序決定捲動對象,改順序可能改變截圖。 預設'pinned-left','center';w-web-perm、w-web-task之現行實作為['center','pinned-left'],沿用時須以order指定

Source:
Example
import rowBoxSel from 'w-package-tools-e2e/src/rowBoxSel.mjs'

let buf = await captureStableWithBox(page, rowBoxSel(3))
let buf2 = await captureStableWithBox(page, rowBoxSel(3, { order: ['center', 'pinned-left'] }))
Parameters:
Name Type Attributes Default Description
rowIndex Number | String

輸入列之row-index

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
scope String <optional>
''

輸入限定範圍之選擇器前綴字串(例如對話框內之表格),預設''

order Array <optional>
['pinned-left','center']

輸入容器順序陣列,元素為'pinned-left'或'center',預設['pinned-left','center']

Returns:

回傳選擇器字串陣列

Type
Array

(async) runBaselineCase(opt) → {Promise}

Description:
  • 單一案例之標準圖管線:產製端(regen)與比對端(compare)呼叫同一函數,使兩端之瀏覽器取得、每案fresh、前置、執行、語意斷言與順序逐項對稱

    順序:prepare(開瀏覽器前,例如資料庫重置) → launch → openPage → beforeRun → run → 正規化截圖 → semantic → verify → 產製端:依gate逐張決定是否寫檔(全部斷言通過後才寫,任何斷言失敗則該案一張都不寫);比對端:逐張assertBaselineMatch → finally關閉瀏覽器(若run中換了瀏覽器,以ctx.browserRef.current為準)並呼叫afterCase。 各掛鉤皆收到ctx物件{mode,lang,name,browser,browserRef,page,shots,result},可依ctx.mode分流(例如只能在測試框架內執行之檢查)。 已知缺陷(createKnownDefect):產製端略過該案不寫圖並回傳status為'knownDefect';比對端呼叫onKnownDefect(例如mocha之this.skip),未給則原樣拋出

Source:
Example
import runBaselineCase from 'w-package-tools-e2e/src/runBaselineCase.mjs'

//比對端(mocha)
it(name, async function() {
    await runBaselineCase({ mode: 'compare', lang, name, launch: launchBrowser, run: fn, semantic, pathOf, onKnownDefect: () => this.skip() })
})
//產製端(直跑 --baseline)
await runBaselineCase({ mode: 'regen', lang, name, launch: launchBrowser, run: fn, semantic, pathOf, gate })
Parameters:
Name Type Description
opt Object

輸入設定物件

Properties
Name Type Attributes Default Description
mode String

輸入模式字串,'regen'或'compare'

lang String

輸入語系字串

name String

輸入案例鍵字串

launch function

輸入開瀏覽器函數,async()=>Browser

run function

輸入案例執行函數,async(page,lang,ctx)=>Buffer|{圖鍵:Buffer}|[{name,buf}]|{buf|shots,page}|null

pathOf function

輸入標準圖路徑函數,(lang,圖鍵)=>路徑字串

openPage function <optional>

輸入開頁函數,async(browser,ctx)=>Page,預設openCasePage(browser)

prepare function <optional>

輸入開瀏覽器前之前置函數,async(ctx)

beforeRun function <optional>

輸入開頁後、執行前之前置函數,async(ctx)

semantic function <optional>

輸入語意斷言函數,async(ctx),寫檔或比對前必過

verify function <optional>

輸入資料庫或端到端不變式檢查函數,async(ctx),寫檔或比對前必過

afterCase function <optional>

輸入收尾函數(finally,關瀏覽器之後),async(ctx)

labelOf function <optional>

輸入比對失敗證據檔名標籤函數,(lang,圖鍵)=>字串,預設標準圖檔名

gate Object <optional>
null

輸入createBaselineGate之篩選器,產製端用;null代表全寫至pathOf

stages Array <optional>
null

輸入宣告之圖鍵陣列,給定時產出之圖鍵集合須與之相同(防宣告、產製、比對三處命名不同步),預設null不檢查

compareOnly Boolean <optional>
false

輸入是否為只比對之案例(共用他案標準圖)布林值;產製端照常執行並驗語意斷言但一張都不寫(避免共用圖被兩案重寫),回傳status為'compareOnly',預設false

allowEmpty Boolean <optional>
false

輸入是否允許案例不產生任何截圖布林值,預設false(無截圖即拋錯,防比對端空轉假綠)

compareAll Boolean <optional>
false

輸入比對端是否比完全部階段再彙總拋錯布林值,預設false(首張不符即拋)

onKnownDefect function <optional>
null

輸入比對端遇已知缺陷之處理函數,async(err),預設null代表原樣拋出

match function <optional>
assertBaselineMatch

輸入比對函數(buf,baselinePath,label),預設assertBaselineMatch

writeFile function <optional>

輸入寫檔函數(path,buf),預設建立上層目錄後fs.writeFileSync

log function <optional>
console.log

輸入輸出函數,預設console.log

Returns:

回傳Promise,resolve回傳物件{status,shots,written,kept,skipped},status為'regen'、'compare'、'compareOnly'或'knownDefect'

Type
Promise

(async) runIsolatedE2e(opt) → {Promise}

Description:
  • 逐檔隔離執行e2e:每個e2e檔以獨立之mocha行程執行,檔與檔之間由beforeEachFile換新後端等狀態

    why:多個e2e檔塞在單一mocha行程時,會共用被前面測試改過狀態之後端(記憶體內計數、快取、換過設定之實例、RPC正規化過之欄位順序), 與各檔單獨產製標準圖之環境不同;逐檔各給全新後端即回到單獨執行之狀態。前端通常無狀態且啟動慢,由各檔之startServersOnce沿用。 測試檔以pattern於testDir動態列舉(新增之檔自動納入,不因寫死清單而靜默漏跑),依檔名排序執行。 只重跑受改動影響之案例時(依差異範圍驗證),以targets逐檔指定檔案與--grep:每檔仍換新後端,並自動加--fail-zero(grep對不到任何案例時失敗,不假綠)。 殺port等屬專案政策,一律以beforeEachFile/afterAll回呼注入,本函數不殺任何行程

    啟動方式:有mocha執行檔(mochaBin,預設projRoot下之node_modules/mocha/bin/mocha.js)時以目前之node直接執行之(不經shell),否則沿用npx mocha(Windows下經shell)。 why:Windows下npx須經cmd.exe,參數被拼接成一行後由cmd.exe重新解析(2026-09-30實測):| & 切出另一指令(如'E2E-0(0[2-9]|1[0-5])-'被切成管線而exit 255), < > 為轉向(> 把輸出寫成cwd下之檔案且exit 0),^ 與 " 被吃掉、空白拆成多個參數、%名稱% 展開為環境變數(皆exit 0,靜默改變篩選); 括號、[ ]、! , ; = * ? \ 與中文原樣傳遞。Node 24另警告DEP0190。故經npx且於Windows時,參數含空白或 " & | < > ^ % 即於執行前拋錯,不跑被改寫之指令。 mocha執行檔之路徑一律以projRoot解析為絕對路徑(spawn之cwd為projRoot,相對路徑會被重複疊加);明確給定之mochaBin不存在即拋錯,不默默改用npx。

Source:
Example
import runIsolatedE2e from 'w-package-tools-e2e/src/runIsolatedE2e.mjs'
import killPortListeners from 'w-package-tools-e2e/src/killPortListeners.mjs'

let { failed } = await runIsolatedE2e({
    projRoot, testDir,
    beforeEachFile: async () => { killPortListeners(11007); await new Promise((r) => setTimeout(r, 2000)) },
    afterAll: () => { killPortListeners(11007) },
})
process.exit(failed === 0 ? 0 : 1)

//只重跑受影響之案例(每檔仍換新後端)
await runIsolatedE2e({
    projRoot, testDir,
    targets: [{ file: 'e2e-adduser.test.mjs', grep: 'E2E-0(0[2-9]|1[0-5])-' }, { file: 'e2e-ips.test.mjs', grep: 'E2E-004-' }],
    beforeEachFile, afterAll,
})
Parameters:
Name Type Description
opt Object

輸入設定物件

Properties
Name Type Attributes Default Description
projRoot String

輸入專案根目錄字串(mocha之工作目錄)

testDir String

輸入測試檔目錄字串

pattern RegExp <optional>
/^e2e-.*\.test\.mjs$/

輸入測試檔名樣式,預設/^e2e-.*.test.mjs$/,有給targets時不使用

targets Array <optional>

輸入指定執行之目標陣列,元素為檔名字串或{file,grep}物件,依陣列順序執行;有grep者附加['--grep',grep,'--fail-zero'];檔案不存在即拋錯。未給時依pattern列舉

beforeEachFile function <optional>

輸入每檔執行前之回呼(可回傳Promise),傳入檔名,預設無動作

afterAll function <optional>

輸入全部執行後之回呼(可回傳Promise),預設無動作

mochaArgs Array <optional>
['--reporter','list','--timeout','300000']

輸入附加於檔案路徑後之mocha參數陣列

mochaBin String <optional>

輸入mocha執行檔路徑字串(相對路徑以projRoot解析),預設為projRoot下之node_modules/mocha/bin/mocha.js,預設者不存在時改用npx;明確給定而不存在即拋錯

spawnSyncFn function <optional>
spawnSync

輸入同步spawn函數(供測試注入),預設spawnSync

platform String <optional>
process.platform

輸入作業系統字串(供測試注入),預設process.platform

log function <optional>
console.log

輸入輸出函數,預設console.log

Returns:

回傳Promise,resolve回傳物件{results,failed},results為{file,code},failed為失敗檔數

Type
Promise

sleepSync(ms) → {undefined}

Description:
  • 同步睡眠(exit/signal處理器內回驗用;該處不能使用setTimeout)

Source:
Example
import sleepSync from 'w-package-tools-e2e/src/sleepSync.mjs'

sleepSync(100)
Parameters:
Name Type Description
ms Number

輸入毫秒數

Returns:

無回傳

Type
undefined

snapshotBaselines(dir, optopt) → {Object}

Description:
  • 盤點標準圖目錄之快照:每個圖檔之相對路徑、sha256、位元組數與mtime

    用途:重產或改動共用設施之前後各取一次,交diffBaselineSnapshots比對,機械化「產前記下既有圖mtime,產後回驗未變」 (已審過之圖被重產等於換掉真理,即使內容看起來一樣)。只讀檔,不改動任何檔案。 相對路徑一律以/分隔(跨平台可比);目錄不存在時回傳空物件

Source:
Example
import snapshotBaselines from 'w-package-tools-e2e/src/snapshotBaselines.mjs'

let before = snapshotBaselines('./test/pics')
//...重產或改動...
let after = snapshotBaselines('./test/pics')
Parameters:
Name Type Attributes Default Description
dir String

輸入標準圖根目錄字串

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
pattern RegExp <optional>
/\.png$/i

輸入納入之檔名樣式,預設/.png$/i

recursive Boolean <optional>
true

輸入是否遞迴子目錄布林值,預設true

Returns:

回傳物件,鍵為相對路徑,值為{sha256,size,mtimeMs}

Type
Object

(async) stepShots(page, opt) → {Promise}

Description:
  • 「每步兩張」之單步:操作前框住將被操作之元素整顆 → 執行操作 → 等反應就緒 → 框住有反應之內容

    依全域技能 role-coder-for-test-e2e §7.1(每個操作步驟兩張、兩張皆須有框)與 §7.2(先決定反應是什麼再決定框什麼)。 相鄰兼任(§7.3-1 相鄰兩物取聯集為一框):前一步之反應與本步之目標相鄰時,前一步之後圖即本步之前圖—— 此時呼叫端傳 before:null 表示不另拍;before 不可省略(undefined 即拋錯),強制每一步明確決定是否兼任。 泛化自 w-web-perm 之 setDialogModeWithShots/openChipsAllWithShots/setChipsAllModeWithShots 與 w-web-sso tokens 之 capturePermsChangeSave。

Source:
Example
import stepShots from 'w-package-tools-e2e/src/stepShots.mjs'

let s = await stepShots(page, {
    capture: captureStableWithBox,
    before: saveBtnLoc(page),
    act: () => saveBtnLoc(page).click(),
    ready: () => waitCheckYes(page),
    after: '.w-checkyes-panel',
})
shots['E2E-003-5-click-save'] = s.before
shots['E2E-003-6-save-success-modal'] = s.after
Parameters:
Name Type Description
page Page

輸入Playwright之Page

opt Object

輸入設定物件

Properties
Name Type Attributes Default Description
capture function

輸入框圖截圖函數(page,target,opt)=>Promise,通常為專案之captureStableWithBox(已帶settle與strict組態)

before *

輸入操作前之框選目標(選擇器、Locator、矩形或其陣列);null代表由前一步之後圖兼任、不另拍

act function

輸入執行操作之函數async()=>{}(真滑鼠/鍵盤,技能 §4)

ready function <optional>
null

輸入等待反應就緒之函數async()=>{}(偵測driven,技能 §4.4),預設null

after *

輸入操作後之框選目標;為函數時於ready之後呼叫取得(目標於操作後才出現者)

beforeOpt Object <optional>
{}

輸入操作前截圖之設定物件(透傳capture),預設{}

afterOpt Object <optional>
{}

輸入操作後截圖之設定物件(透傳capture),預設{}

Returns:

回傳Promise,resolve回傳{before,after},before為Buffer或null(兼任),after為Buffer

Type
Promise

(async) typeIntoInput(page, locator, value) → {Promise}

Description:
  • 以真人鍵盤輸入文字至輸入框(Pattern D),供Vue v-model等受控輸入框

    流程:等可見 → 等1000ms(元件掛載與綁定穩定) → 點擊 → 驗證焦點確實落在該元素(防父層mousedown.prevent攔截造成靜默漏字) → 以End加Backspace逐字清空(不用Ctrl+A或剪貼簿,避免與系統快捷鍵衝突) → keyboard.insertText一次注入 → 驗證值,不符重試至多3次。 why insertText:逐字type於受控輸入框每字觸發重繪,焦點可能中途離開而漏字

Source:
Example
import typeIntoInput from 'w-package-tools-e2e/src/typeIntoInput.mjs'

await typeIntoInput(page, page.locator('input[type="password"]').first(), 'Pw@login1')
Parameters:
Name Type Description
page Page

輸入Playwright之Page

locator Locator

輸入輸入框之Locator

value String

輸入要填入之文字字串

Returns:

回傳Promise,resolve代表值已正確填入,3次仍不符則reject

Type
Promise

(async) typeIntoNthInput(page, idx, value) → {Promise}

Description:
  • 以真人鍵盤輸入文字至頁面第idx個input(Pattern D,與typeIntoInput同機制),供無穩定選擇器之表單

Source:
Example
import typeIntoNthInput from 'w-package-tools-e2e/src/typeIntoNthInput.mjs'

await typeIntoNthInput(page, 2, '10.0.0.9')
Parameters:
Name Type Description
page Page

輸入Playwright之Page

idx Number

輸入input之索引(document.querySelectorAll('input')之順序)

value String

輸入要填入之文字字串

Returns:

回傳Promise,resolve代表值已正確填入,3次仍不符則reject

Type
Promise

(async) waitAlertGone(page, optopt) → {Promise}

Description:
  • 等提示浮窗(wsemi domAlert,即 w-component-vue WAlert/vo.$alert;元素 id 以 alt- 開頭)全部消失

    截圖之反應目標不是提示浮窗本身時(例:行內紅字),浮窗在不在畫面上取決於時序(domAlert 預設數秒後自動移除),截圖因而不確定。 以偵測取代「固定等 N 秒」:先等反應目標就緒,再以本函數等浮窗消失後截圖 (w-web-sso login E2E-016 殷鑑:固定等待改偵測式後,文字一出現即截圖,截到尚未消失之錯誤浮窗而與標準圖不符)。 反應目標即為提示浮窗者不可用本函數(應等其滑入定位後截圖,技能 §8.1)。

Source:
Example
import waitAlertGone from 'w-package-tools-e2e/src/waitAlertGone.mjs'

await waitUntilExist(page, '行內錯誤', (s) => document.body.innerText.includes(s), { arg: msg })
await waitAlertGone(page)
let buf = await captureStableWithBox(page, page.getByText(msg).last())
Parameters:
Name Type Attributes Default Description
page Object

輸入Playwright Page

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
sel String <optional>
'[id^="alt-"]'

輸入提示浮窗之選擇器字串,預設'[id^="alt-"]'(wsemi domAlert)

timeout Number <optional>
30000

輸入等待上限毫秒數,預設30000

Returns:

回傳Promise,浮窗全部消失時resolve;逾時reject(含仍在之浮窗數)

Type
Promise

waitChildExit(child, timeoutMs) → {Promise}

Description:
  • 等子行程真正結束('exit'事件),逾時回傳false;以事件判定,不受PID回收影響,逾時亦不留監聽器

Source:
Example
import waitChildExit from 'w-package-tools-e2e/src/waitChildExit.mjs'

let ok = await waitChildExit(child, 5000)
Parameters:
Name Type Description
child ChildProcess

輸入子行程

timeoutMs Number

輸入逾時毫秒數

Returns:

回傳Promise,resolve回傳是否已結束

Type
Promise

(async) waitColResizeOverlay(page, optopt) → {Promise}

Description:
  • 【w-component-vue】等WDrawer拖曳分隔條(cursor:col-resize)之覆蓋層opacity變為1

    該覆蓋層之opacity由setTimeout(300ms)控制0到1,CPU忙時可能被延後超過初始等待,故於頁面內輪詢(每50ms)至全部為1; 頁面無此元素時立即放行,逾時(預設5000ms)不拋錯

Source:
Example
import waitColResizeOverlay from 'w-package-tools-e2e/src/waitColResizeOverlay.mjs'

await waitColResizeOverlay(page)
Parameters:
Name Type Attributes Default Description
page Page

輸入Playwright之Page

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
timeout Number <optional>
5000

輸入逾時毫秒數,預設5000

Returns:

回傳Promise,resolve代表已就緒或已逾時

Type
Promise

(async) waitDrawerReady(page, optopt) → {Promise}

Description:
  • 【w-component-vue】等WDrawer抽屜到達穩定態(opened或hidden)才放行

    讀WDrawer根節點之[state]屬性(平移transitionend決定性標記hidden/opening/opened/hiding),所有抽屜皆為opened或hidden即放行; 事件驅動,不受主執行緒負載影響(取代易被減速尾段騙而截到半途之座標取樣)。頁面無WDrawer時立即放行;逾時(預設10000ms)不拋錯,交由呼叫端之連拍穩定兜底

Source:
Example
import waitDrawerReady from 'w-package-tools-e2e/src/waitDrawerReady.mjs'

await waitDrawerReady(page)
Parameters:
Name Type Attributes Default Description
page Page

輸入Playwright之Page

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
timeout Number <optional>
10000

輸入逾時毫秒數,預設10000

Returns:

回傳Promise,resolve代表已穩定或已逾時

Type
Promise

(async) waitGridIdle(page, optopt) → {Promise}

Description:
  • 【ag-grid】等表格靜止:以「內容簽章+版面幾何」每intervalMs取樣一次,連續stableMs毫秒完全相同才放行

    簽章:.ag-cell總數、指定列(rowIndex)各儲存格之欄位id與前30字、中央欄容器/標頭/首列之矩形、水平與垂直捲動量。 why:只看文字之簽章抓不到1px位移,兩次雙重requestAnimationFrame(約4個frame)之觀察窗太短,技能列為已驗證無效之手法; 本函數取代各測試檔手寫之雙重rAF判斷。頁面無表格(.ag-center-cols-viewport)時立即放行,但minCells大於0時視為未就緒(等表格出現); requireSelector未出現、儲存格數少於minCells或(requireScrollLeftZero時)水平捲動量不為0之取樣視為未就緒並重新計時;逾時拋錯

Source:
Example
import waitGridIdle from 'w-package-tools-e2e/src/waitGridIdle.mjs'

await waitGridIdle(page, { requireSelector: '.ag-header-cell[col-id="token"]', requireScrollLeftZero: true })
Parameters:
Name Type Attributes Default Description
page Page

輸入Playwright之Page

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
stableMs Number <optional>
1000

輸入須連續相同之毫秒數,預設1000

intervalMs Number <optional>
100

輸入取樣間隔毫秒數,預設100

timeout Number <optional>
15000

輸入逾時毫秒數,預設15000

scope String <optional>
''

輸入限定範圍之選擇器前綴字串(例如對話框內之表格),預設''

requireSelector String <optional>
null

輸入須已出現之選擇器字串(例如某欄之標頭),預設null

rowIndex Number <optional>
0

輸入納入簽章之列row-index,預設0

minCells Number <optional>
0

輸入至少須有之儲存格數,預設0;大於0時表格尚未出現亦視為未就緒

requireScrollLeftZero Boolean <optional>
false

輸入是否要求水平捲動量為0布林值,預設false

Returns:

回傳Promise,resolve代表已靜止,逾時reject

Type
Promise

(async) waitUntilExist(page, label, fn, optopt) → {Promise}

Description:
  • 偵測驅動之等待:每步驟先偵測對象存在或就緒再進下一步,逾時即拋錯(代表真實異常,而非固定等待不夠)

    fn於瀏覽器端執行,跨行程序列化故不可使用閉包變數,傳值一律經opt.arg。 fn為async函數(例如頁面內fetch後端再判斷)時改為逐次page.evaluate並await其結果輪詢:Playwright之waitForFunction同步呼叫predicate, async函數回傳之Promise為truthy而第一次即放行(不重試、逾時無效,條件不成立也照樣通過;2026-09-28查得共20處: w-web-sso之ag-grid等待以async判斷直呼waitForFunction 14處、w-web-task以async判斷呼叫waitUntilExist 6處)

Source:
Example
import waitUntilExist from 'w-package-tools-e2e/src/waitUntilExist.mjs'

await waitUntilExist(page, '表格列', () => document.querySelectorAll('.ag-row').length > 0)
await waitUntilExist(page, '訊息', (t) => document.body.innerText.includes(t), { arg: '儲存成功' })
Parameters:
Name Type Attributes Default Description
page Page

輸入Playwright之Page

label String

輸入錯誤訊息用之對象描述字串

fn function

輸入於頁面執行之判斷函數,回傳truthy代表就緒;可為async函數

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
timeout Number <optional>
10000

輸入逾時毫秒數,預設10000

arg * <optional>
null

輸入傳給fn之參數,預設null

polling Number <optional>
100

輸入async函數之輪詢間隔毫秒數,預設100

Returns:

回傳Promise,resolve代表就緒,逾時reject

Type
Promise