以後不同專案就能用直接切不同 profiles 修改 map、rewrite、proxy 等各種設定了!
「聽說自己寫過才不會忘。」 「會忘記的就寫起來,忘記了就看一下。」 「你知道2036的時候,今年就是10年前了嗎?」 「遇到的問題雖然有時候不值得一提,可是如果紀錄起來感覺也很有趣!」 「就是大家可以看到的筆記本而已。」
2026-07-08
Charles 4.x.x - Profiles 功能,快速切換一堆 map 和 rewrite
以後不同專案就能用直接切不同 profiles 修改 map、rewrite、proxy 等各種設定了!
2026-05-05
[AVD] Android 步數模擬
在AVD的更多選項中,sensors 的 move,拉動 Y 軸可以模擬前後晃動,對部分 app 有模擬步數的效果。
不想一直手拉,可以用下面的指令不段重複,直到 crtl + c 離開:
trap "echo Stopped; exit" INT while true; do { echo "auth $(cat ~/.emulator_console_auth_token)" echo "sensor set acceleration 0:9:0" } | nc localhost 5554 sleep 0.1 { echo "auth $(cat ~/.emulator_console_auth_token)" echo "sensor set acceleration 0:13:0" } | nc localhost 5554 sleep 0.1 done
2026-04-29
Devlog 離職集點小卡(???
2026-01-16
Devlog 易經籌策占卦網頁 I-Ching Stalk Divination v2.0
先上網頁連結 https://goescat.github.io/i-ching-stalk-divination/ 。
-
其實我先前就做了易經籌策占卦,v1.0 還在這裡 https://goescat.github.io/iching_test.html)。
應該可以感受到 v1.0的界面非常陽春,不過最重要的是裡面取卦的方式是依照傳統籌策占卦的方式取出,沒有馬虎,所以到 v2.0這部分就沒有更動了。
-
先介紹一下占卦方式,畢竟是 P1 在 v1.0 時就完成的功能,整個工具最核心的部分XD,我從 v2.0 repo 裡的 README 複製一下:
1. 籌策占卦需要準備 50 根籌策,第一步是取出一根至於上方不動(如界面呈現)代表太極。 下方可撥動的籌策為 49 根。
2. 在心中想好問題,誠心的提問。
3. 將 49 根籌策絡在一起,從中間分一半。
4. 將左邊的籌策取出一根至於手上(程式中會將左邊絡 -1)。
5. 將左邊的籌策以4根一組做計算,剩下不足4根即取出,若都沒有剩下,即取出4根(程式中會左邊絡會減去餘數,同樣餘數為0則-4)。
6. 換右邊的籌策,不需取出一根,直接4根一組計算,取出的規則如上。
7. 三變成一爻。因此將剩下的籌策再絡在一起,從中間分一半,如上操作再進行2次(共3次,所以程式中會引導分半3次。)
8. 進行 3 次後,剩餘的組數必定為6、7、8、9其中一數。 單數為陽爻,雙數為陰爻。9為老陽,6為老陰,將變爻。(若有變爻,程式在結果會註明。) 此時得到1爻。
9. 重複上述6次,即得6爻,也就是完整的一卦。
具體操作影片可參考臺大開放式課程傅佩榮教授 影片 示範。
-
總之 v2.0我幫卦象加上了卦辭、爻辭的介紹:
總共64卦的頁面,內容都來自中國哲學書電子化計劃 http://ctext.org/zh。做完實在滿開心的。
之後應該還可以補充上怎麼解卦的說明,方便大家加減參考XDD
2025-07-30
Github Actions matrix + Pytest mark 多節點同步執行 case
隨著自動化測試的 case 越來越多,總有一天會遇到該來平行跑測試這件事。
根據使用的框架、CI/CD 工具不同,實作方式也會有所差異。這邊紀錄一下我之前用 pytest mark 搭配 GitHub Actions 做平行執行的做法。
mark 拿來執行一系列 case 很方便,所以放在 Github Actions 時,決定以 matrix 同時開多個同 mark 名稱的節點來執行,類似下面這樣:strategy:matrix:mark: ['node_serviceA', 'node_serviceB', 'node_serviceC']fail-fast: false
run: pytest -m ${{ matrix.mark }}@pytest.mark.node_serviceAdef test_api_service_a():#...
2025-07-27
Devlog 離職遺照產生器 v1.0
分享在FB之後,最多的回饋就是需要RWD。也是啦,這個時代哪有人還在用電腦版網站呢(淚
於是快速的加上了簡單的 RWD~目前長這樣(標題也偷改了):
把背景小故事收到了「這是啥?」裡面,點開來之後,點「不要幹話」可以收合!
另外加入了照片滿版、裝飾置中的功能~
照片滿版 demo
(圖片為 ChatGPT 生成的「我」,不過與實物不能說一模一樣,只能說毫無關聯):
裝飾置中 demo雖然說手機版的圖片編輯,應該要加上適合手機的手勢操作(雙指捏合縮放、旋轉等),但因為如果要加上手勢操作會需要整個大改寫,所以暫時還沒加。
還有收集到其他需求,像是直接轉成黑白照片、自己輸入文字等,之後有空再更新~
-
上面都是使用者回報的部分,直到我多加了一些圖片之後,才發現了一個超級嚴重的問題⋯⋯
完全忘記圖片要壓縮,有些圖片大小甚至有 1MB 之多⋯⋯我都在家裡超快的網路上測試,完全忽略了圖片出來超慢,而且有點耗使用者的網路流量QQ
於是修正了用 webp 讓圖片都降到 100KB 以下,加入 prefetch catch。
-
另外調整了一下裝飾品的順序,一開始是加入的先後順序,後來是只要點擊選中的裝飾品就要到最上面,感覺更直覺一點。
-
最後總結一下學到的、印象深刻的東西:
- 圖片要壓縮
- 承上,測試的時候記得用 F12 network throttling 注意一下不同網路狀況,應該能及早發現
2025-07-24
Devlog 離職照片產生器 v0.1
最近有一些奇妙的體悟,讓我好像比較有動力做一些不同的東西。
一是覺得 vibe coding 做一些小東西真的很有趣、很方便,大幅節省了原本沒有 AI 前,完全不熟某種開發時,要稍微搜尋,大致知道用什麼工具比較適合、選了可能還要寫出一部份試試的時間。似乎可以把以前想做,但一直用「沒時間」當藉口的東西都做出來了。另外 AI 也省掉很多原本工作上的苦工,有時間做一些真正重要的事之外,還有時間想一些工作之外好玩的東西。
二是最近剛好受到了滿多類似的鼓勵,大意是「一個軟體的生命是在上線後,有人使用才開始」,以前覺得有點雞湯,但後來覺得也是事實。以前覺得自己工作之外做的東西不管是主題(可能我感興趣的東西太怪)還是介面之類的都很鳥⋯⋯實在不敢拿出來,但是現在想想,我不過就只是要做一個自己覺得有趣的東西,根本不用先想這麼多。
先求有再求好。紀錄各種過程,一定是很有趣而且很有收穫的一件事。
-
前幾天 FB 回顧我上一份工作離職的紀念貼文。當時我把 slack 頭像換成像是遺照的樣子,同事問我這是哪裡來的產生器。於是我當時想說來做個產生器好了!一等就是一年——直到現在終於想起來,然後速速做了0.1版~
-
請看這精(ㄐㄧㄢˇ)美(ㄌㄡˋ)的介面:
- 回憶起在 local file:// 測試會遇到 CROS error。因為平常寫 python,所以在目錄下 python -m http.server 8000 就可以。
- 當時花了比較多時間調整照片和新增上面的緞帶、裝飾品時怎麼樣會比較直覺一點。比方最基本的,一新增裝飾品,裝飾品應該就要被選中。原本用了send to front 之類的方式,但其實沒有用,其實被選中就會出現在最上方了。
- 因為照片容易不小心點到就跑上來,所以我新增了一個「照片到最下面」的按鈕。目前完全是以能最快開發完來設計😂覺得這樣子的過程也很有趣,可以體會到為什麼有些趕鴨子上架的產品,在缺少規格時有很多突兀的設計。
- 下載的部分,發現手機的網頁上 link.click() 是無效的,所以後來是開新分頁顯示圖片,讓使用者長按圖片存檔。忽然知道為什麼手機小測驗存圖片都是長按儲存,自己做過才知道😂
2025-05-12
Selenium Chrome WebDriver 定位 之2
driver.execute_cdp_cmd("Target.getTargets", {})driver.execute_cdp_cmd("Target.attachToTarget", {
"targetId": target_id,
"flatten": True
})
"Emulation.setGeolocationOverride", {
"latitude": lat,
"longitude": lng,
"accuracy": 10,
"sessionId" : session_id
}2025-04-04
自動測試的其他操作元素方式 - 使用 execute_script 執行 JavaScript 操作 DOM
在進行 click、input、scroll 等操作時,有時會遇到元素無法或很難操作的情況。這時可以考慮使用 WebDriver 的 execute_script() 執行 JavaScript 操作 DOM。
以下整理一些常見範例:
選取元素
- 使用 ID 選取
document.getElementById("example");
- 使用 class name 選取
document.getElementsByClassName("example");
- 使用 name 選取
document.getElementsByName("example");
- 使用 CSS 選取
document.querySelector("[attribute='value1']"); // 選取第一個符合的元素
document.querySelectorAll("input[attribute='value2']"); // 選取所有符合的元素
- 使用 tag name 選取
document.getElementsByTagName("input"); // 選取所有 input 元素
- 使用 XPath 選取
document.evaluate("//*[text()='example']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
操作元素
選取後可進行修改、點擊、滾動等操作。
- 點擊元素
document.getElementById("example").click();
- 點擊多個元素中的其中一個
var a = document.getElementsByClassName("ui example button");
a[2].click(); // 選取第三個符合的按鈕並點擊
- 修改 value 值
document.getElementById("example").value = "20";
修改其他 attribute 的值
- document.getElementById("Mobility").selectedIndex = 12; // 設定 select 選擇的索引值
- 移除屬性
document.getElementsByName("example")[0].removeAttribute("hidden"); // 移除 hidden 屬性
- 捲動元素到視窗可見範圍
arguments[0].scrollIntoView();
- 捲動元素到視窗中間
arguments[0].scrollIntoView({ block: 'center' });
⋯⋯
2025-03-18
Github Actions cron 排程
查了一下才發現,文件上說,workflow 可以執行的最短間隔是5分鐘,而且在負載較高的時候可能會延後,也可能被跳過。
-
補充個人實際經驗,有遇過 cron 延後30分鐘才執行。
2025-03-16
Chrome 開發者工具 console 拿 xpath 階層下所有元素的 text
每個人習慣的抓 xpath 的插件不太一樣。
但是假如不想用任何插件,Chrome 的開發人員工具其實很夠用了。
像是大家都知道的,在開發人員工具 > console 中輸入:
$x("//*[text()='隨便一個範例的 xpath']") 可以用來確認 xpath 是否有選到元素、選到幾個元素等。
要確認文字也很容易,一般只要「這個」xpath 本身的文字的話,直接看 element 就可以了,但確認的場景可能是想取得這個 xpath 下「所有階層」的文字。
可以像這樣:
$x("xpath").map(el => el.innerText || el.textContent);至於 innerText 和 textContent 有什麼差異,可能之後再筆記一下。
2025-03-01
[Mac] Python venv 設定
2.
python venv venv_name (看你 python 怎麼裝,可能要 python3 -m venv venv_name)這樣就有叫 venv_name 的資料夾了
3.
source venv_name/bin/activate就可以使用這個 venv
deactivate可以離開2024-12-18
Selenium Chrome WebDriver 進行定位模擬
from selenium import webdriver
import time
# 設定位置的經緯度,以大慶夜市為例
latitude = 24.122629
longitude = 120.655638
accuracy = 10
driver = webdriver.Chrome()
# 用 CDP command 設定座標經緯度
driver.execute_cdp_cmd(
"Emulation.setGeolocationOverride",
{
"latitude": latitude,
"longitude": longitude,
"accuracy": accuracy
}
)
# 打開頁面試試看定位,這裡以 OpenStreetMap 為例
driver.get("https://www.openstreetmap.org/")
driver.find_element("css selector", "span.geolocate").click()
# 給你看個10秒
time.sleep(10)
driver.quit()- 模擬定位在 headless (無頭)模式也可以正確定位,之前看到一些資訊說不行,那很可能是其他問題。
- web driver 如果禁用定位,例如 options 時禁用:
options.add_experimental_option( "prefs", {"profile.default_content_setting_values.geolocation": 2} )模擬定位就不能使用。 - 可以先用 CDP command 加上權限:
driver.execute_cdp_cmd(
再設定定位。
"Browser.grantPermissions",
{
"permissions": ["geolocation"],
} ) - Google Maps 也可以使用這樣來定位。有些資訊說 Google Maps 以 IP 定位,但實際上是剛載入時抓 IP 位置的國家(OSM也會這樣),而當要精確定位目前位置時,會使用裝置位置,也就是 Emulation.setGeolocationOverride 模擬的座標。
2024-11-28
Postman (newman) 到 Github actions 上的一些小記錄
1. 在 Postman CLI 和 Newman 做選擇
2. 上傳檔案的測試
3. 執行失敗的 log
當時還有一點痛苦的,就是失敗時沒有 response 的 log ⋯⋯Postman CLI 也一樣。
可以做到的方法大致上是在每一個 API 的 post-script 加上紀錄:
pm.test("Response status code is 200", function () {
if (pm.response.code !== 200) {
throw new Error(`Response Code: ${pm.response.code} | Response Body: ${pm.response.text()} | Request Body: ${JSON.stringify(pm.request.body)}`);
}
pm.response.to.have.status(200);
});;
2024-11-25
Selenium 4.6 之後你真的不用下載 webdriver 或使用 WebDriverManager
如題。
在 Selenium 4.6 之後,已經不用另外下載 webdriver,不用帶路徑,不用使用 WebDriverManager 解決下載和帶路徑的問題。
你只需要:
from selenium import webdriver driver = webdriver.Chrome()這樣就可以跑了!預設是目前的 stable 版本。
-
如果你需要特定版本的 chrome,可以用Options指定,selenium manager 會自動下載你指定的版本:
options = webdriver.ChromeOptions() options.browser_version = '120' driver = webdriver.Chrome(options=options)
除了特定版本號,也可以帶標籤如stable(目前 CfT 版本)、beta(下一個版本穩定)、dev(目前正在開發版本)、canary(為開發人員每晚建置)、esr(擴充支援版本,僅用於 Firefox)。
2024-11-19
Selenium + ChromeDriver - 開啟 DevTools 的行動裝置模式
在自動化的時候也可以用 add_experimental_option 來設定 webdriver 開啟行動裝置模式,上個最小 Python 範例:
from selenium import webdriver
mobile_emulation = { "deviceName": "iPhone 12 Pro" }
chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option("mobileEmulation", mobile_emulation)
driver = webdriver.Chrome(options=options)
from selenium import webdriver
mobile_emulation = {
"deviceMetrics": { "width": 360, "height": 640, "pixelRatio": 3.0 },
"userAgent": "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19",
"clientHints": {"platform": "Android", "mobile": True} }
chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option("mobileEmulation", mobile_emulation)
driver = webdriver.Chrome("/driver/path", options=options)
2024-11-14
Appium iOS - 你確定 build 了 simulator 可以執行的 app,但還是一直噴錯怎辦?要不要用一下 bundleId?
{ "appium:deviceName": "iPhone 15 Pro", "appium:automationName": "XCUITest", "platformName": "iOS", "appium:platformVersion": "17.5", "appium:app": "/Users/username/Desktop/folder/Demo.app" }Failed to create session. An unknown server-side error occurred while processing the command. Original error: The com.testdomain.test-demo-ios.dev application does not support the x86_64 Simulator architecture: Non-fat file: /Users/username/Desktop/folder/Demo.app/Demo is architecture: arm64 Please rebuild your application to support the x86_64 platform. { "appium:deviceName": "iPhone 15 Pro", "appium:automationName": "XCUITest", "platformName": "iOS", "appium:platformVersion": "17.5", "appium:bundleId": "com.testdomain.test-demo-ios.dev"}ADB - 從裝了 app 的手機拿到 apk 檔
adb shell "pm list packages -f | grep 你想抓的Package名稱" adb -d shell "pm list packages -f|grep shopee" package:/data/app/com.shopee.tw.int-X1xsowqyHAerEoslb2fxj00g==/base.apk=com.shopee.tw.int /data/app/com.shopee.tw.int-X1xsowqyHAerEoslb2fxj00g==/base.apk 就是 app 安裝的位置!adb pull /data/app/com.shopee.tw.int-X1xsowqyHAerEoslb2fxj00g==/base.apk 2024-11-11
Appium Android 起手式 - 取得 appPackage 和 appActivity
取得方式很多,記錄一下我習慣用的方法:
adb shell dumpsys window | grep -E "mCurrentFocus"
確定已經連到了 Android 裝置,並開啟 app,輸入上面的指令。
回傳的結果會是目前開啟的 app 和 app 頁面,如:appPackage/appActivity 的格式的資訊。
實際例子,開啟 Chrome app 之後:
mCurrentFocus=Window{1c35821 u0 com.android.chrome/com.google.android.apps.chrome.Main}
appPackage 是 com.android.chrome,appActivity 是 com.google.android.apps.chrome.Main。
-
要注意的是,appActivity 是能進 app 的第一個畫面,有些 app 開啟時可能閃現一下就跳到下一個畫面。沒有找到正確的可能會有類似以下的錯誤:
exited with code 255'; Command output: Exception occurred while executing 'start': java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000
這種時候可以拼手速,在一開啟載入閃現那個第一個畫面就趕快 enter 指令,但也可以用 Android Studio 或其他編輯器打開 apk 檔的 AndroidMainfest.xml,找到含有 android.intent.action.MAIN 的 Activity。
實際例子,有個 Activity 有這樣的內容 :
2024-11-06
Appium 2.0 錯誤 Could not find a driver for automationName 'xxxxxx' and platformName 'xxxxxx'
appium driver install uiautomator2 appium driver list ✔ Listing available drivers- uiautomator2@3.8.1 [installed (npm)]- xcuitest [not installed]- espresso [not installed]- mac2 [not installed]- windows [not installed]- safari [not installed]- gecko [not installed]- chromium [not installed]appium driver list 有參數可以只看有裝的 driver:appium driver list --installed
✔ Listing installed drivers - uiautomator2@3.8.1 [installed (npm)] 

















