Skip to content

Commit 9df4b3a

Browse files
authored
Merge branch 'main' into szhang/sdk-1556/oxygen-sdk-hello-example
2 parents b45215a + c9c4f95 commit 9df4b3a

File tree

4 files changed

+183
-0
lines changed

4 files changed

+183
-0
lines changed

.github/workflows/browser.yml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,89 @@ jobs:
4242
package_name: '@launchdarkly/js-client-sdk'
4343
pr_number: ${{ github.event.number }}
4444
size_limit: 25000
45+
46+
# Contract Tests
47+
- name: Install contract test dependencies
48+
run: yarn workspaces focus browser-contract-test-adapter browser-contract-test-service
49+
50+
- name: Install Playwright browsers
51+
run: yarn workspace browser-contract-test-service install-playwright-browsers
52+
53+
- name: Build contract test adapter
54+
run: yarn workspace browser-contract-test-adapter run build
55+
56+
- name: Build contract test entity (browser app)
57+
run: yarn workspace browser-contract-test-service run build
58+
59+
- name: Start contract test adapter in background
60+
run: |
61+
yarn workspace browser-contract-test-adapter run start > /tmp/adapter.log 2>&1 &
62+
echo $! > /tmp/adapter.pid
63+
64+
- name: Serve browser app with http-server
65+
run: |
66+
npx http-server packages/sdk/browser/contract-tests/entity/dist -p 5173 --cors > /tmp/http-server.log 2>&1 &
67+
echo $! > /tmp/http-server.pid
68+
69+
- name: Wait for services to be ready
70+
run: |
71+
echo "Waiting for adapter on port 8001..."
72+
for i in {1..30}; do
73+
if nc -z localhost 8001; then
74+
echo "Adapter WebSocket ready"
75+
break
76+
fi
77+
if [ $i -eq 30 ]; then
78+
echo "Timeout waiting for adapter"
79+
cat /tmp/adapter.log
80+
exit 1
81+
fi
82+
sleep 1
83+
done
84+
85+
echo "Waiting for HTTP server on port 5173..."
86+
for i in {1..30}; do
87+
if curl -s http://localhost:5173 > /dev/null; then
88+
echo "HTTP server ready"
89+
break
90+
fi
91+
if [ $i -eq 30 ]; then
92+
echo "Timeout waiting for HTTP server"
93+
cat /tmp/http-server.log
94+
exit 1
95+
fi
96+
sleep 1
97+
done
98+
99+
- name: Open browser app in headless Chromium
100+
run: |
101+
node packages/sdk/browser/contract-tests/entity/open-browser.mjs http://localhost:5173 > /tmp/playwright.log 2>&1 &
102+
echo $! > /tmp/playwright.pid
103+
sleep 5 # Give the browser time to initialize and connect via WebSocket
104+
105+
- name: Run contract tests
106+
uses: launchdarkly/gh-actions/actions/contract-tests@21174f3a7f3aa3e3121227ec91842e8a1ebeec6e
107+
with:
108+
test_service_port: 8000
109+
token: ${{ secrets.GITHUB_TOKEN }}
110+
extra_params: '--skip-from=${{ github.workspace }}/packages/sdk/browser/contract-tests/suppressions.txt --stop-service-at-end'
111+
112+
- name: Print logs on failure
113+
if: failure()
114+
run: |
115+
echo "=== Adapter Log ==="
116+
cat /tmp/adapter.log || echo "No adapter log"
117+
echo "=== HTTP Server Log ==="
118+
cat /tmp/http-server.log || echo "No http-server log"
119+
echo "=== Playwright Log ==="
120+
cat /tmp/playwright.log || echo "No playwright log"
121+
122+
- name: Cleanup contract test services
123+
if: always()
124+
run: |
125+
[ -f /tmp/playwright.pid ] && kill $(cat /tmp/playwright.pid) || true
126+
[ -f /tmp/http-server.pid ] && kill $(cat /tmp/http-server.pid) || true
127+
[ -f /tmp/adapter.pid ] && kill $(cat /tmp/adapter.pid) || true
128+
pkill -f "playwright" || true
129+
pkill -f "http-server" || true
130+
pkill -f "browser-contract-test-adapter" || true
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Browser SDK Contract Tests
2+
3+
This directory contains the contract test implementation for the LaunchDarkly Browser SDK using the [SDK Test Harness](https://github.com/launchdarkly/sdk-test-harness).
4+
5+
## Architecture
6+
7+
The browser contract tests consist of three components:
8+
9+
1. **Adapter** (`adapter/`): A Node.js server that:
10+
- Exposes a REST API on port 8000 for the test harness
11+
- Runs a WebSocket server on port 8001 for browser communication
12+
- Translates REST commands to WebSocket messages
13+
14+
2. **Entity** (`entity/`): A browser application (Vite app) that:
15+
- Connects to the adapter via WebSocket
16+
- Implements the actual SDK test logic
17+
- Runs the Browser SDK in a real browser environment
18+
19+
3. **Test Harness**: The SDK test harness that:
20+
- Sends test commands via REST API to the adapter (port 8000)
21+
- Validates SDK behavior across different scenarios
22+
23+
## Running Locally
24+
25+
### Prerequisites
26+
27+
- Node.js 18 or later
28+
- Yarn
29+
- A modern browser (for manual testing)
30+
31+
### Quick Start
32+
33+
```bash
34+
# From the repository root
35+
./packages/sdk/browser/contract-tests/run-test-service.sh
36+
```
37+
38+
This script will:
39+
1. Start the adapter (WebSocket bridge)
40+
2. Start the entity (browser app with Vite dev server)
41+
3. Open the browser app in your default browser
42+
43+
The services will be available at:
44+
- Adapter REST API: http://localhost:8000
45+
- Adapter WebSocket: ws://localhost:8001
46+
- Browser App: http://localhost:5173
47+
48+
You then run the `sdk-test-harness`. More information is available here: https://github.com/launchdarkly/sdk-test-harness
49+
50+
Example with local clone of the test harness:
51+
```bash
52+
go run . --url http://localhost:8123 -skip-from path-to-your-js-core-clone/packages/sdk/browser/contract-tests/suppressions.txt
53+
```
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Opens a headless browser and navigates to the contract test entity page.
5+
* Keeps the browser open until the process is terminated.
6+
*
7+
* Usage: node open-browser.mjs [url]
8+
* Default URL: http://localhost:5173
9+
*/
10+
11+
import { chromium } from 'playwright';
12+
13+
const url = process.argv[2] || 'http://localhost:5173';
14+
15+
console.log(`Opening headless browser at ${url}...`);
16+
17+
const browser = await chromium.launch({
18+
headless: true,
19+
args: ['--no-sandbox', '--disable-setuid-sandbox']
20+
});
21+
22+
const context = await browser.newContext();
23+
const page = await context.newPage();
24+
25+
// Log console messages from the browser
26+
page.on('console', (msg) => {
27+
console.log(`[Browser Console] ${msg.type()}: ${msg.text()}`);
28+
});
29+
30+
// Log page errors
31+
page.on('pageerror', (error) => {
32+
console.error(`[Browser Error] ${error.message}`);
33+
});
34+
35+
await page.goto(url);
36+
37+
console.log('Browser is open and running. Press Ctrl+C to close.');
38+
39+
// Keep the process alive
40+
await new Promise(() => {
41+
// Intentionally never resolve - keeps browser open until process is killed
42+
});

packages/sdk/browser/contract-tests/entity/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"type": "module",
66
"description": "Contract test service implementation for @launchdarkly/js-client-sdk",
77
"scripts": {
8+
"install-playwright-browsers": "playwright install --with-deps chromium",
89
"start": "tsc --noEmit && vite --open=true",
910
"build": "tsc --noEmit && vite build",
1011
"lint": "eslint ./src",
@@ -24,6 +25,7 @@
2425
"eslint-plugin-import": "^2.27.5",
2526
"eslint-plugin-jest": "^27.6.3",
2627
"eslint-plugin-prettier": "^5.0.0",
28+
"playwright": "^1.49.1",
2729
"prettier": "^3.0.0",
2830
"typescript": "^5.5.3",
2931
"vite": "^5.4.1"

0 commit comments

Comments
 (0)