Skip to content

Commit e6bc2ae

Browse files
georgRusanovgeorgiy.rusanov
andauthored
test: add browser test to check websocket constructor (#1471)
* returned test:integration:browser * added test --------- Co-authored-by: georgiy.rusanov <[email protected]>
1 parent c131801 commit e6bc2ae

File tree

8 files changed

+276
-15
lines changed

8 files changed

+276
-15
lines changed

.github/workflows/ci.yml

Lines changed: 92 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,43 @@ env:
2323
NODE_VERSION: '20'
2424

2525
jobs:
26+
build-package:
27+
name: Build supabase-js package
28+
runs-on: ubuntu-latest
29+
outputs:
30+
tgz-name: ${{ steps.pack.outputs.filename }}
31+
steps:
32+
- name: Checkout code
33+
uses: actions/checkout@v4
34+
35+
- uses: actions/setup-node@v4
36+
with:
37+
node-version: ${{ env.NODE_VERSION }}
38+
39+
- name: Install dependencies and build
40+
run: |
41+
npm ci
42+
npm run build
43+
npm run build:umd
44+
45+
- name: Pack npm module
46+
id: pack
47+
run: |
48+
PKG=$(npm pack)
49+
echo "filename=$PKG" >> "$GITHUB_OUTPUT"
50+
51+
- name: Upload .tgz package
52+
uses: actions/upload-artifact@v4
53+
with:
54+
name: supabase-tgz
55+
path: ${{ steps.pack.outputs.filename }}
56+
57+
- name: Upload UMD build
58+
uses: actions/upload-artifact@v4
59+
with:
60+
name: supabase-umd
61+
path: dist/umd/supabase.js
62+
2663
test:
2764
name: Unit + Type Check / Node.js ${{ matrix.node }} / OS ${{ matrix.os }}
2865
runs-on: ${{ matrix.os }}
@@ -97,10 +134,9 @@ jobs:
97134
npm ci
98135
npm test || npm test
99136
100-
# - name: Run integration and browser tests on Deno 2.x only
101-
# if: ${{ matrix.deno == '2.x' }}
102-
# run: |
103-
# npm run test:integration:browser
137+
- name: Run integration and browser tests on Deno 2.x only
138+
if: ${{ matrix.deno == '2.x' }}
139+
run: npm run test:integration:browser
104140

105141
- name: Stop Supabase
106142
if: always()
@@ -109,6 +145,7 @@ jobs:
109145
node-integration:
110146
name: Node Integration
111147
runs-on: ubuntu-latest
148+
needs: build-package
112149
steps:
113150
- name: Checkout code
114151
uses: actions/checkout@v4
@@ -142,6 +179,7 @@ jobs:
142179
next-integration:
143180
name: Next.js Integration
144181
runs-on: ubuntu-latest
182+
needs: build-package
145183
steps:
146184
- name: Checkout code
147185
uses: actions/checkout@v4
@@ -177,6 +215,7 @@ jobs:
177215
expo-tests:
178216
name: Expo Tests
179217
runs-on: ubuntu-latest
218+
needs: build-package
180219
steps:
181220
- name: Checkout code
182221
uses: actions/checkout@v4
@@ -192,23 +231,63 @@ jobs:
192231
with:
193232
version: latest
194233

234+
- name: Download artifact
235+
uses: actions/download-artifact@v4
236+
with:
237+
name: supabase-tgz
238+
path: ./supabase-pkg
239+
195240
- name: Start Supabase
196241
run: supabase start
197242

198-
- name: Install dependencies and build
243+
- name: Install dependencies and run tests
199244
run: |
200-
npm ci
201-
npm run build
202-
203-
- name: Build and test expo
204-
run: |
205-
PKG=$(npm pack)
206-
echo "Packed: $PKG"
207245
cd test/integration/expo
246+
cp ../../../supabase-pkg/supabase-supabase-js-0.0.0-automated.tgz .
208247
npm install
209-
npm install "../../../$PKG"
210248
npm test || npm test
211249
212250
- name: Stop Supabase
213251
if: always()
214252
run: supabase stop
253+
254+
websocket-browser-tests:
255+
name: WebSocket Browser Tests
256+
runs-on: ubuntu-latest
257+
needs: build-package
258+
steps:
259+
- name: Checkout code
260+
uses: actions/checkout@v4
261+
262+
- name: Setup Node.js
263+
uses: actions/setup-node@v4
264+
with:
265+
node-version: ${{ env.NODE_VERSION }}
266+
267+
- name: Download artifact
268+
uses: actions/download-artifact@v4
269+
with:
270+
name: supabase-umd
271+
path: ./dist/umd
272+
273+
- name: Setup Supabase CLI
274+
uses: supabase/setup-cli@v1
275+
with:
276+
version: latest
277+
278+
- name: Install Playwright browsers and dependencies
279+
run: npx playwright install --with-deps
280+
281+
- name: Start Supabase
282+
run: supabase start
283+
284+
- name: Run WebSocket tests
285+
run: |
286+
cd test/integration/node-browser
287+
npm install
288+
cp ../../../dist/umd/supabase.js .
289+
npm run test
290+
291+
- name: Stop Supabase
292+
if: always()
293+
run: supabase stop

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"test:integration": "jest --runInBand --detectOpenHandles test/integration.test.ts",
3737
"test:integration:browser": "deno test --allow-all test/integration.browser.test.ts",
3838
"test:watch": "jest --watch --verbose false --silent false",
39+
"test:node:playwright": "cd test/integration/node-browser && npm install && cp ../../../dist/umd/supabase.js . && npm run test",
3940
"test:types": "run-s build:module && tsd --files test/types/*.test-d.ts",
4041
"docs": "typedoc --entryPoints src/index.ts --out docs/v2 --includes src/**/*.ts",
4142
"docs:json": "typedoc --entryPoints src/index.ts --includes src/**/*.ts --json docs/v2/spec.json --excludeExternals",

test/integration/expo/package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/integration/expo/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"@react-navigation/bottom-tabs": "^7.3.10",
1717
"@react-navigation/elements": "^2.3.8",
1818
"@react-navigation/native": "^7.1.6",
19-
"@supabase/supabase-js": "file:../../../supabase-supabase-js-0.0.0-automated.tgz",
19+
"@supabase/supabase-js": "file:supabase-supabase-js-0.0.0-automated.tgz",
2020
"expo": "~53.0.9",
2121
"expo-blur": "~14.1.4",
2222
"expo-constants": "~17.1.6",
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<body>
4+
<pre id="log" data-testid="log" style="font-family: monospace"></pre>
5+
<script src="http://localhost:8004/supabase.js"></script>
6+
<script>
7+
const log = (msg) => {
8+
document.getElementById('log').textContent += msg + '\n'
9+
console.log(msg)
10+
}
11+
12+
log('Starting WebSocket test (Playwright)...')
13+
14+
// Add global error handler
15+
window.addEventListener('error', (event) => {
16+
log('Global error: ' + event.message)
17+
log('Error source: ' + event.filename + ':' + event.lineno)
18+
})
19+
20+
// Add unhandled promise rejection handler
21+
window.addEventListener('unhandledrejection', (event) => {
22+
log('Unhandled promise rejection: ' + event.reason)
23+
})
24+
25+
try {
26+
log('Attempting to access createClient...')
27+
28+
// Check if supabase is available
29+
if (typeof window.supabase === 'undefined') {
30+
throw new Error('window.supabase is not defined')
31+
}
32+
log('Supabase UMD loaded successfully')
33+
34+
const { createClient } = window.supabase
35+
log('createClient function available')
36+
37+
// Intercept WebSocket constructor
38+
const originalWebSocket = window.WebSocket
39+
let wsConstructorCalls = []
40+
41+
window.WebSocket = function (...args) {
42+
wsConstructorCalls.push(args.length)
43+
log(
44+
'WebSocket constructor called with ' +
45+
args.length +
46+
' parameters: ' +
47+
JSON.stringify(args)
48+
)
49+
return new originalWebSocket(...args)
50+
}
51+
52+
// Intercept fetch
53+
const originalFetch = window.fetch
54+
let fetchCalls = []
55+
56+
window.fetch = function (...args) {
57+
fetchCalls.push(args[0])
58+
log('Fetch called with URL: ' + args[0])
59+
return originalFetch.apply(this, args)
60+
}
61+
62+
// Intercept setTimeout/setInterval for polling
63+
const originalSetTimeout = window.setTimeout
64+
const originalSetInterval = window.setInterval
65+
let timeoutCalls = []
66+
let intervalCalls = []
67+
68+
window.setTimeout = function (fn, delay, ...args) {
69+
timeoutCalls.push({ fn: fn.toString().substring(0, 50), delay })
70+
log('setTimeout called with delay: ' + delay)
71+
return originalSetTimeout.apply(this, [fn, delay, ...args])
72+
}
73+
74+
window.setInterval = function (fn, delay, ...args) {
75+
intervalCalls.push({ fn: fn.toString().substring(0, 50), delay })
76+
log('setInterval called with delay: ' + delay)
77+
return originalSetInterval.apply(this, [fn, delay, ...args])
78+
}
79+
80+
log('Creating Supabase client with WebSocket transport...')
81+
const supabase = createClient(
82+
'http://127.0.0.1:54321',
83+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0',
84+
{
85+
realtime: {
86+
transport: window.WebSocket,
87+
heartbeatIntervalMs: 500,
88+
},
89+
}
90+
)
91+
92+
log('Creating channel...')
93+
const channel = supabase.channel('realtime:public:todos')
94+
95+
log('Subscribing to channel...')
96+
channel.subscribe((status) => {
97+
log('WebSocket subscribe callback called with: ' + status)
98+
})
99+
100+
setTimeout(() => {
101+
log('WebSocket calls: ' + JSON.stringify(wsConstructorCalls))
102+
log('Fetch calls: ' + JSON.stringify(fetchCalls))
103+
log('setTimeout calls: ' + JSON.stringify(timeoutCalls))
104+
log('setInterval calls: ' + JSON.stringify(intervalCalls))
105+
}, 3000)
106+
} catch (error) {
107+
log('Error in WebSocket test: ' + error.message)
108+
log('Error stack: ' + error.stack)
109+
log('Error name: ' + error.name)
110+
}
111+
</script>
112+
</body>
113+
</html>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "websocket-playwright-tests",
3+
"version": "1.0.0",
4+
"description": "WebSocket browser tests using Playwright",
5+
"scripts": {
6+
"test": "playwright test",
7+
"serve:node-browser": "serve . -p 8004"
8+
},
9+
"devDependencies": {
10+
"@playwright/test": "^1.40.0",
11+
"serve": "^14.2.1"
12+
}
13+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { defineConfig, devices } from '@playwright/test'
2+
3+
export default defineConfig({
4+
testDir: './',
5+
fullyParallel: true,
6+
forbidOnly: !!process.env.CI,
7+
retries: process.env.CI ? 2 : 0,
8+
workers: process.env.CI ? 1 : undefined,
9+
reporter: 'html',
10+
use: {
11+
baseURL: 'http://localhost:8004',
12+
trace: 'on-first-retry',
13+
},
14+
projects: [
15+
{
16+
name: 'chromium',
17+
use: { ...devices['Desktop Chrome'] },
18+
},
19+
{
20+
name: 'firefox',
21+
use: { ...devices['Desktop Firefox'] },
22+
},
23+
{
24+
name: 'webkit',
25+
use: { ...devices['Desktop Safari'] },
26+
},
27+
],
28+
webServer: {
29+
command: 'cp ../../../dist/umd/supabase.js . && npm run serve:node-browser',
30+
url: 'http://localhost:8004',
31+
reuseExistingServer: !process.env.CI,
32+
},
33+
})
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { test, expect } from '@playwright/test'
2+
3+
test.describe('WebSocket Browser Tests', () => {
4+
test('should test WebSocket transport', async ({ page }) => {
5+
await page.goto('/')
6+
await expect(page.locator('#log')).toBeVisible()
7+
await page.waitForTimeout(5000)
8+
9+
const logContent = await page.locator('#log').textContent()
10+
console.log('WebSocket test log content:', logContent)
11+
12+
expect(logContent).toContain('WebSocket constructor called')
13+
//Try to check fix for https://github.com/supabase/realtime-js/issues/493
14+
expect(logContent).not.toContain('WebSocket constructor called with 3 parameters')
15+
expect(logContent).not.toContain('CHANNEL_ERROR')
16+
expect(logContent).not.toContain('Global error')
17+
expect(logContent).not.toContain('Unhandled promise rejection')
18+
19+
// Verify subscription worked
20+
expect(logContent).toContain('WebSocket subscribe callback called with: SUBSCRIBED')
21+
})
22+
})

0 commit comments

Comments
 (0)