Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ root = true
end_of_line = lf
insert_final_newline = true

[*.{js,json,yml,ts,tsx,jsx,cjs,mjs}]
[*.{js,json,ts,tsx,jsx,cjs,mjs}]
charset = utf-8
indent_style = tab
indent_size = 2

[*.{yml,yaml}]
charset = utf-8
indent_style = space
indent_size = 2
158 changes: 158 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
name: Playwright Tests

on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Run every week
schedule:
- cron: '43 5 * * 0'
# Run on pushes to main and develop if files in the playwright/ folder changed
push:
branches: [main, develop, 'feat/browserstack']
paths:
- playwright/**

permissions:
contents: read

jobs:
prepare:
name: Prepare Companion build
timeout-minutes: 10
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'

- name: Prepare
run: |
corepack enable

# try and avoid timeout errors
yarn config set httpTimeout 100000

yarn --immutable

- name: Build companion
run: |
yarn dist:prepare
env:
VITE_DISABLE_WHATS_NEW: 'true' # Disable What's New dialog for testing

- name: Upload companion build
uses: actions/upload-artifact@v4
with:
name: companion-dist
path: dist/

- name: Build matrix JSON from browserstack.yml
id: matrix
run: |
# Create a JSON matrix that GH Actions can consume, reading platforms from playwright/browserstack.yml
python - <<'PY'
import yaml, json, sys, os

path = 'playwright/browserstack.yml'
with open(path) as f:
data = yaml.safe_load(f)

platforms = data.get('platforms', [])
if not platforms:
print('::error::No platforms found in browserstack.yml')
sys.exit(1)

matrix = { 'include': [] }
for p in platforms:
# build a human-friendly name for the matrix entry
platform_os = p.get('os') or p.get('deviceName') or ''
osVersion = p.get('osVersion') or ''
browserName = p.get('browserName') or ''
browserVersion = p.get('browserVersion') or ''
name = f"{platform_os}@{osVersion} - {browserName}@{browserVersion}"
matrix['include'].append({'platform': json.dumps(p), 'name': name})

# emit matrix using the GITHUB_OUTPUT file (set-output is deprecated)
gha_out = os.environ.get('GITHUB_OUTPUT')
if gha_out:
with open(gha_out, 'a') as fh:
fh.write('matrix=' + json.dumps(matrix) + "\n")
else:
# Fallback for older runners: print to stdout (not recommended)
print('matrix=' + json.dumps(matrix))
PY

test:
needs: prepare
name: Playwright ${{ matrix.name }}
timeout-minutes: 60
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{fromJson(needs.prepare.outputs.matrix)}}
max-parallel: 3
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'

- name: Prepare
id: prepare
run: |
corepack enable

# try and avoid timeout errors
yarn config set httpTimeout 100000

yarn --immutable

- name: Download companion build
uses: actions/download-artifact@v4
with:
name: companion-dist
path: dist

- name: 'BrowserStack Env Setup'
uses: 'browserstack/github-actions/[email protected]'
with:
username: ${{ secrets.BROWSERSTACK_USERNAME }}
access-key: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}

- name: Prepare single-platform browserstack.yml
run: |
mkdir -p playwright
python - <<'PY'
import json, yaml, sys
entry = json.loads('''${{ matrix.platform }}''')
cfg = yaml.safe_load(open('playwright/browserstack.yml'))
cfg['platforms'] = [entry]
with open('playwright/browserstack.yml', 'w') as f:
yaml.safe_dump(cfg, f)
print('Wrote single-platform playwright/browserstack.yml')
PY

- name: Spawn Companion
run: |
# Start Companion from the downloaded build
node dist/main.js --machine-id ci_browserstack --log-level debug &
export COMPANION_PID=$!

yarn wait-on http://localhost:8000 --timeout 30000 --delay 5000

- name: Run Playwright tests
run: |
cd playwright
# Ensure single worker to avoid parallelism inside the runner
yarn test:ci --workers=1
2 changes: 1 addition & 1 deletion _typos.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[default]
extend-ignore-re = [
".*// typos:disable-line[^\\n]*\\n[^\\n]*\\n",
".*[//|#] typos:disable-line[^\\n]*\\n[^\\n]*\\n",
]

[default.extend-words]
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"shared-lib",
"webui",
"launcher",
"launcher-ui"
"launcher-ui",
"playwright"
],
"type": "module",
"scripts": {
Expand All @@ -18,6 +19,8 @@
"dev:webui": "yarn workspace @companion-app/webui dev",
"build:ts": "yarn workspaces foreach --all run build:ts",
"dist:webui": "run build:ts && yarn workspace @companion-app/webui build",
"dist:prepare": "tsx ./tools/build/dist.mts",
"dist:package": "tsx ./tools/build/package.mts",
"dist": "tsx ./tools/build/complete.mts",
"macdist": "run dist mac-x64",
"macarmdist": "run dist mac-arm64",
Expand Down Expand Up @@ -68,6 +71,7 @@
"udev-generator": "^1.0.2",
"vitest": "^3.2.4",
"vitest-mock-extended": "^3.1.0",
"wait-on": "^9.0.1",
"yaml": "^2.8.1",
"zx": "^8.8.4"
},
Expand Down
5 changes: 5 additions & 0 deletions playwright/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/log
/playwright-report/
/test-results/
/browserstackSetupConfig.json
/playwright-browserstack*
3 changes: 3 additions & 0 deletions playwright/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Playwright test

Danger: This will wipe your current configuration and will take over control of it to run the tests.
92 changes: 92 additions & 0 deletions playwright/browserstack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# ======================
# BrowserStack Reporting
# ======================
projectName: companion-local # This gets overridden in CI
buildName: browserstack-playwright
# `buildIdentifier` is a unique id to differentiate every execution that gets appended to
# buildName. Choose your buildIdentifier format from the available expressions:
# ${BUILD_NUMBER} (Default): Generates an incremental counter with every execution
# ${DATE_TIME}: Generates a Timestamp with every execution. Eg. 05-Nov-19:30
# Read more about buildIdentifiers here -> https://www.browserstack.com/docs/automate/selenium/organize-tests
buildIdentifier: '#${BUILD_NUMBER}' # Supports strings along with either/both ${expression}

# =======================================
# Platforms (Browsers / Devices to test)
# =======================================
# Platforms object contains all the browser / device combinations you want to test on.
# Entire list available here -> (https://www.browserstack.com/list-of-browsers-and-platforms/automate)
platforms:
# macos
- os: OS X
osVersion: Sequoia
browserName: playwright-webkit
browserVersion: latest
# - os: OS X
# # typos:disable-line
# osVersion: Big Sur
# browserName: playwright-webkit
# browserVersion: 15.4 # not compatible with modern playwright

# Windows
- os: Windows
osVersion: 11
browserName: chrome
browserVersion: latest
- os: Windows
osVersion: 11
browserName: chrome
browserVersion: 122

# Android
- deviceName: Samsung Galaxy S22 Ultra
osVersion: 12.0
browserName: chrome
- deviceName: Pixel 10 Pro
osVersion: 16.0
browserName: chrome

# iOS
# - deviceName: iPad Air 13 2025
# osVersion: 26 # not available while in beta
# browserName: safari
- deviceName: iPad Air 13 2025
osVersion: 17.5
browserName: safari
- deviceName: iPad Pro 12.9 2018
osVersion: 12.1
browserName: safari
- deviceName: iPad 9th
osVersion: 15.6
browserName: safari

# =======================
# Parallels per Platform
# =======================
# Force non-parallel because of shared app state
parallelsPerPlatform: 1

# ==========================================
# BrowserStack Local
# (For localhost, staging/private websites)
# ==========================================
# Set browserStackLocal to true if your website under test is not accessible publicly over the internet
# Learn more about how BrowserStack Local works here -> https://www.browserstack.com/docs/automate/selenium/local-testing-introduction
browserstackLocal: true
# browserStackLocalOptions:
# Options to be passed to BrowserStack local in-case of advanced configurations
# localIdentifier: # <string> (Default: null) Needed if you need to run multiple instances of local.
# forceLocal: true # <boolean> (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel.
# Entire list of arguments available here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections

# ===================
# Debugging features
# ===================
debug: false # <boolean> # Set to true if you need screenshots for every selenium command ran
networkLogs: false # <boolean> Set to true to enable HAR logs capturing
consoleLogs: verbose # <string> Remote browser's console debug levels to be printed (Default: errors)
# Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors)
# CUSTOM_TAG_<INT>: # <string> (Default: parent folder name of the test file) Custom tag for your test suite

# Test Reporting And Analytics is an intelligent test reporting & debugging product. It collects data using the SDK. Read more about what data is collected at https://www.browserstack.com/docs/test-reporting-and-analytics/references/terms-and-conditions
# Visit automation.browserstack.com to see your test reports and insights. To disable test reporting and analytics, specify `testReporting: false` in the key below.
testReporting: true
17 changes: 17 additions & 0 deletions playwright/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@companion-app/playwright",
"version": "4.2.0",
"private": true,
"type": "module",
"scripts": {
"test:ci": "COMPANION_URL=http://bs-local.com:8000 browserstack-node-sdk playwright test --config=./playwright.config.ts",
"test:local": "COMPANION_URL=http://bs-local.com:8000 browserstack-node-sdk playwright test --config=./playwright.config.ts",
"test:watch": "PWTEST_WATCH=1 run test",
"test": "COMPANION_URL=http://localhost:5173 playwright test --config=./playwright.config.ts"
},
"devDependencies": {
"@playwright/test": "^1.55.1",
"browserstack-node-sdk": "^1.43.1"
},
"packageManager": "[email protected]"
}
37 changes: 37 additions & 0 deletions playwright/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { defineConfig } from '@playwright/test'

// This is a sample config for what users might be running locally
export default defineConfig({
testDir: './tests',

/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* We must run tests sequentially, as they rely on common app state. */
workers: 1,
fullyParallel: false,

/* Maximum time one test can run for. */
timeout: 90 * 1000,
expect: {
/**
* Maximum time expect() should wait for the condition to be met.
* For example in `await expect(locator).toHaveText();`
*/
timeout: 5000,
},
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Configure projects for major browsers */
projects: [
// This is only used for local testing
{
name: 'chrome',
use: {
browserName: 'chromium',
channel: 'chrome',
},
},
],
})
Loading
Loading