diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6aad8f68..1645e8502 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,7 +53,7 @@ jobs: matrix: os: [ubuntu-latest] node: ['20'] - deno: ['2.x'] + deno: ['1.x', '2.x'] runs-on: ${{ matrix.os }} @@ -78,12 +78,22 @@ jobs: run: | supabase start - - name: Run tests + - name: Build run: | npm clean-install + npm run build + + - name: Run tests + if: ${{ matrix.deno == '2.x' }} + run: | npm run test:integration || npm run test:integration npm run test:integration:browser + - name: Run Deno tests + run: | + cd test/deno + npm test || npm test + - name: Stop Supabase run: | supabase stop diff --git a/deno.lock b/deno.lock deleted file mode 100644 index 3b0519e69..000000000 --- a/deno.lock +++ /dev/null @@ -1,89 +0,0 @@ -{ - "version": "5", - "redirects": { - "https://deno.land/x/sleep/mod.ts": "https://deno.land/x/sleep@v1.3.0/mod.ts" - }, - "remote": { - "https://deno.land/std@0.192.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", - "https://deno.land/std@0.192.0/async/abortable.ts": "fd682fa46f3b7b16b4606a5ab52a7ce309434b76f820d3221bdfb862719a15d7", - "https://deno.land/std@0.192.0/async/deadline.ts": "58f72a3cc0fcb731b2cc055ba046f4b5be3349ff6bf98f2e793c3b969354aab2", - "https://deno.land/std@0.192.0/async/debounce.ts": "adab11d04ca38d699444ac8a9d9856b4155e8dda2afd07ce78276c01ea5a4332", - "https://deno.land/std@0.192.0/async/deferred.ts": "42790112f36a75a57db4a96d33974a936deb7b04d25c6084a9fa8a49f135def8", - "https://deno.land/std@0.192.0/async/delay.ts": "73aa04cec034c84fc748c7be49bb15cac3dd43a57174bfdb7a4aec22c248f0dd", - "https://deno.land/std@0.192.0/async/mod.ts": "f04344fa21738e5ad6bea37a6bfffd57c617c2d372bb9f9dcfd118a1b622e576", - "https://deno.land/std@0.192.0/async/mux_async_iterator.ts": "70c7f2ee4e9466161350473ad61cac0b9f115cff4c552eaa7ef9d50c4cbb4cc9", - "https://deno.land/std@0.192.0/async/pool.ts": "f1b8d3df4d7fd3c73f8cbc91cc2e8b8e950910f1eab94230b443944d7584c657", - "https://deno.land/std@0.192.0/async/retry.ts": "6521c061a5ab24e8b1ae624bdc581c4243d1d574f99dc7f5a2a195c2241fb1b8", - "https://deno.land/std@0.192.0/async/tee.ts": "47e42d35f622650b02234d43803d0383a89eb4387e1b83b5a40106d18ae36757", - "https://deno.land/std@0.192.0/http/server.ts": "1b23463b5b36e4eebc495417f6af47a6f7d52e3294827a1226d2a1aab23d9d20", - "https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975", - "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834", - "https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293", - "https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7", - "https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74", - "https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd", - "https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff", - "https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46", - "https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b", - "https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c", - "https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491", - "https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68", - "https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3", - "https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7", - "https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29", - "https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a", - "https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a", - "https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8", - "https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693", - "https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31", - "https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5", - "https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8", - "https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb", - "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917", - "https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47", - "https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68", - "https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3", - "https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73", - "https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19", - "https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5", - "https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6", - "https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2", - "https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e", - "https://deno.land/std@0.224.0/testing/_test_suite.ts": "f10a8a6338b60c403f07a76f3f46bdc9f1e1a820c0a1decddeb2949f7a8a0546", - "https://deno.land/std@0.224.0/testing/asserts.ts": "d0cdbabadc49cc4247a50732ee0df1403fdcd0f95360294ad448ae8c240f3f5c", - "https://deno.land/std@0.224.0/testing/bdd.ts": "3e4de4ff6d8f348b5574661cef9501b442046a59079e201b849d0e74120d476b", - "https://deno.land/x/sleep@v1.3.0/mod.ts": "e9955ecd3228a000e29d46726cd6ab14b65cf83904e9b365f3a8d64ec61c1af3", - "https://deno.land/x/sleep@v1.3.0/sleep.ts": "b6abaca093b094b0c2bba94f287b19a60946a8d15764d168f83fcf555f5bb59e" - }, - "workspace": { - "packageJson": { - "dependencies": [ - "npm:@sebbo2002/semantic-release-jsr@1", - "npm:@supabase/auth-js@2.69.1", - "npm:@supabase/functions-js@2.4.4", - "npm:@supabase/node-fetch@2.6.15", - "npm:@supabase/postgrest-js@1.19.4", - "npm:@supabase/realtime-js@2.11.9", - "npm:@supabase/storage-js@2.7.1", - "npm:@types/jest@^29.2.5", - "npm:husky@^4.3.0", - "npm:jest@^29.3.1", - "npm:npm-run-all@^4.1.5", - "npm:prettier@^2.5.1", - "npm:pretty-quick@^3.1.3", - "npm:puppeteer@^24.9.0", - "npm:rimraf@^3.0.2", - "npm:semantic-release-plugin-update-version-in-files@^1.1.0", - "npm:serve@^14.2.1", - "npm:ts-jest@^29.0.5", - "npm:ts-loader@^8.0.11", - "npm:ts-node@^10.9.1", - "npm:tsd@~0.30.4", - "npm:typedoc@~0.22.16", - "npm:typescript@^4.5.5", - "npm:webpack-cli@^4.9.2", - "npm:webpack@^5.69.1" - ] - } - } -} diff --git a/package-lock.json b/package-lock.json index ff1e289e1..77f99448e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@sebbo2002/semantic-release-jsr": "^1.0.0", "@solana/wallet-standard-features": "^1.3.0", "@types/jest": "^29.2.5", - "husky": "^4.3.0", + "husky": "^4.3.8", "jest": "^29.3.1", "npm-run-all": "^4.1.5", "prettier": "^2.5.1", @@ -3882,6 +3882,7 @@ "integrity": "sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { "chalk": "^4.0.0", "ci-info": "^2.0.0", diff --git a/package.json b/package.json index 85ec91429..ca33cd59f 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "test": "run-s test:types test:run", "test:all": "run-s test:types test:run test:integration test:integration:browser", "test:run": "jest --runInBand --detectOpenHandles", - "test:coverage": "jest --runInBand --coverage --testPathIgnorePatterns=\"test/integration.*\"", + "test:coverage": "jest --runInBand --coverage --testPathIgnorePatterns=\"test/integration.*|test/deno.*\"", "test:integration": "jest --runInBand --detectOpenHandles test/integration.test.ts", "test:integration:browser": "deno test --allow-all test/integration.browser.test.ts", "test:db": "cd infra/db && docker-compose down && docker-compose up -d && sleep 5", @@ -54,7 +54,7 @@ "@sebbo2002/semantic-release-jsr": "^1.0.0", "@solana/wallet-standard-features": "^1.3.0", "@types/jest": "^29.2.5", - "husky": "^4.3.0", + "husky": "^4.3.8", "jest": "^29.3.1", "npm-run-all": "^4.1.5", "prettier": "^2.5.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4883c0537..7da1b0106 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,7 +36,7 @@ importers: specifier: ^29.2.5 version: 29.5.14 husky: - specifier: ^4.3.0 + specifier: ^4.3.8 version: 4.3.8 jest: specifier: ^29.3.1 diff --git a/supabase/.temp/cli-latest b/supabase/.temp/cli-latest index f47ab0840..eb410900a 100644 --- a/supabase/.temp/cli-latest +++ b/supabase/.temp/cli-latest @@ -1 +1 @@ -v2.22.12 \ No newline at end of file +v2.24.3 \ No newline at end of file diff --git a/test/deno/.gitignore b/test/deno/.gitignore new file mode 100644 index 000000000..6d2777165 --- /dev/null +++ b/test/deno/.gitignore @@ -0,0 +1 @@ +deno.json \ No newline at end of file diff --git a/test/deno/integration.test.ts b/test/deno/integration.test.ts new file mode 100644 index 000000000..ca06e275f --- /dev/null +++ b/test/deno/integration.test.ts @@ -0,0 +1,147 @@ +import { assertEquals, assertExists } from 'https://deno.land/std@0.220.1/assert/mod.ts' +import { createClient, SupabaseClient } from '../../dist/module/index.js' +import type { RealtimeChannel } from '../../dist/module/index.js' + +// These tests assume that a local Supabase server is already running +// Start a local Supabase instance with 'supabase start' before running these tests + +// TODO: Remove sanitizeOps and sanitizeResources once the issue is fixed +Deno.test( + 'Supabase Integration Tests', + { sanitizeOps: false, sanitizeResources: false }, + async (t) => { + // Default local dev credentials from Supabase CLI + const SUPABASE_URL = 'http://127.0.0.1:54321' + const ANON_KEY = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' + + const supabase = createClient(SUPABASE_URL, ANON_KEY, { + realtime: { heartbeatIntervalMs: 500 }, + }) + + // Cleanup function to be called after all tests + const cleanup = async () => { + await supabase.auth.signOut() + await supabase.auth.stopAutoRefresh() + await supabase.removeAllChannels() + // Give some time for cleanup to complete + await new Promise((resolve) => setTimeout(resolve, 1000)) + } + + try { + await t.step('should connect to Supabase instance', () => { + assertExists(supabase) + assertEquals(supabase instanceof SupabaseClient, true) + }) + + await t.step('PostgREST - should query data from public schema', async () => { + const { data, error } = await supabase.from('todos').select('*').limit(5) + + // The default schema includes a 'todos' table, but it might be empty + assertEquals(error, null) + assertEquals(Array.isArray(data), true) + }) + + await t.step('PostgREST - should create and delete a todo', async () => { + // Create a new todo + const { data: createdTodo, error: createError } = await supabase + .from('todos') + .insert({ task: 'Integration Test Todo', is_complete: false }) + .select() + .single() + + assertEquals(createError, null) + assertExists(createdTodo) + assertEquals(createdTodo!.task, 'Integration Test Todo') + assertEquals(createdTodo!.is_complete, false) + + // Delete the created todo + const { error: deleteError } = await supabase + .from('todos') + .delete() + .eq('id', createdTodo!.id) + + assertEquals(deleteError, null) + + // Verify the todo was deleted + const { data: fetchedTodo, error: fetchError } = await supabase + .from('todos') + .select('*') + .eq('id', createdTodo!.id) + .single() + + assertExists(fetchError) + assertEquals(fetchedTodo, null) + }) + + await t.step('Authentication - should sign up a user', async () => { + const email = `test-${Date.now()}@example.com` + const password = 'password123' + + const { data, error } = await supabase.auth.signUp({ + email, + password, + }) + + assertEquals(error, null) + assertExists(data.user) + assertEquals(data.user!.email, email) + }) + + await t.step('Realtime - is able to connect and broadcast', async () => { + const channelName = `channel-${crypto.randomUUID()}` + let channel: RealtimeChannel + const email = `test-${Date.now()}@example.com` + const password = 'password123' + + // Sign up and create channel + await supabase.auth.signUp({ email, password }) + const config = { broadcast: { self: true }, private: true } + channel = supabase.channel(channelName, { config }) + await supabase.realtime.setAuth() + + const testMessage = { message: 'test' } + let receivedMessage: any + let subscribed = false + let attempts = 0 + + channel + .on('broadcast', { event: '*' }, (payload: unknown) => (receivedMessage = payload)) + .subscribe((status: string) => { + if (status == 'SUBSCRIBED') subscribed = true + }) + + // Wait for subscription + while (!subscribed) { + if (attempts > 50) throw new Error('Timeout waiting for subscription') + await new Promise((resolve) => setTimeout(resolve, 100)) + attempts++ + } + + attempts = 0 + + channel.send({ + type: 'broadcast', + event: 'test-event', + payload: testMessage, + }) + + // Wait on message + while (!receivedMessage) { + if (attempts > 50) throw new Error('Timeout waiting for message') + await new Promise((resolve) => setTimeout(resolve, 100)) + attempts++ + } + + assertExists(receivedMessage) + assertEquals(supabase.realtime.getChannels().length, 1) + + // Cleanup channel + await channel.unsubscribe() + }) + } finally { + // Ensure cleanup runs even if tests fail + await cleanup() + } + } +) diff --git a/test/deno/package-lock.json b/test/deno/package-lock.json new file mode 100644 index 000000000..640f9aef1 --- /dev/null +++ b/test/deno/package-lock.json @@ -0,0 +1,52 @@ +{ + "name": "test-deno", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "test-deno", + "dependencies": { + "@supabase/supabase-js": "file:../../" + } + }, + "../..": { + "name": "@supabase/supabase-js", + "version": "0.0.0-automated", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.70.0", + "@supabase/functions-js": "2.4.4", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.19.4", + "@supabase/realtime-js": "next", + "@supabase/storage-js": "2.7.1" + }, + "devDependencies": { + "@sebbo2002/semantic-release-jsr": "^1.0.0", + "@solana/wallet-standard-features": "^1.3.0", + "@types/jest": "^29.2.5", + "husky": "^4.3.0", + "jest": "^29.3.1", + "npm-run-all": "^4.1.5", + "prettier": "^2.5.1", + "pretty-quick": "^3.1.3", + "puppeteer": "^24.9.0", + "rimraf": "^3.0.2", + "semantic-release-plugin-update-version-in-files": "^1.1.0", + "serve": "^14.2.1", + "ts-jest": "^29.0.5", + "ts-loader": "^8.0.11", + "ts-node": "^10.9.1", + "tsd": "^0.30.4", + "typedoc": "^0.22.16", + "typescript": "^4.5.5", + "webpack": "^5.69.1", + "webpack-cli": "^4.9.2" + } + }, + "node_modules/@supabase/supabase-js": { + "resolved": "../..", + "link": true + } + } +} diff --git a/test/deno/package.json b/test/deno/package.json new file mode 100644 index 000000000..f635f21cc --- /dev/null +++ b/test/deno/package.json @@ -0,0 +1,11 @@ +{ + "name": "test-deno", + "private": true, + "scripts": { + "test": "npm run setup-deps && deno test --allow-all --unstable-sloppy-imports integration.test.ts", + "setup-deps": "node setup-deps.js" + }, + "dependencies": { + "@supabase/supabase-js": "file:../../" + } +} diff --git a/test/deno/setup-deps.js b/test/deno/setup-deps.js new file mode 100755 index 000000000..625ea3ec9 --- /dev/null +++ b/test/deno/setup-deps.js @@ -0,0 +1,57 @@ +#!/usr/bin/env node + +const fs = require('node:fs') +const path = require('node:path') + +// Get the directory of the script +const scriptDir = __dirname +const projectRoot = path.dirname(path.dirname(scriptDir)) + +// Read package-lock.json +const packageLockPath = path.join(projectRoot, 'package-lock.json') +const packageLock = JSON.parse(fs.readFileSync(packageLockPath, 'utf8')) + +// Extract versions from package-lock.json +const getVersion = (packageName) => { + const packagePath = `node_modules/${packageName}` + return packageLock.packages[packagePath]?.version +} + +const versions = { + realtime: getVersion('@supabase/realtime-js'), + functions: getVersion('@supabase/functions-js'), + postgrest: getVersion('@supabase/postgrest-js'), + auth: getVersion('@supabase/auth-js'), + storage: getVersion('@supabase/storage-js'), + node_fetch: getVersion('@supabase/node-fetch'), +} + +// Read or create deno.json +const denoJsonPath = path.join(scriptDir, 'deno.json') +let denoJson = { + lock: false, + imports: {}, +} + +try { + if (fs.existsSync(denoJsonPath)) { + denoJson = JSON.parse(fs.readFileSync(denoJsonPath, 'utf8')) + } +} catch (error) { + console.warn('Warning: Could not read existing deno.json, creating new one') +} + +// Update imports in deno.json +denoJson.imports = { + '@supabase/realtime-js': `npm:@supabase/realtime-js@${versions.realtime}`, + '@supabase/functions-js': `npm:@supabase/functions-js@${versions.functions}`, + '@supabase/postgrest-js': `npm:@supabase/postgrest-js@${versions.postgrest}`, + '@supabase/auth-js': `npm:@supabase/auth-js@${versions.auth}`, + '@supabase/storage-js': `npm:@supabase/storage-js@${versions.storage}`, + '@supabase/node-fetch': `npm:@supabase/node-fetch@${versions.node_fetch}`, +} + +// Write updated deno.json +fs.writeFileSync(denoJsonPath, JSON.stringify(denoJson, null, 2) + '\n') + +console.log('Updated deno.json with versions from package-lock.json') diff --git a/test/integration.browser.test.ts b/test/integration.browser.test.ts index 8d0cf53de..ec9cf0bd9 100644 --- a/test/integration.browser.test.ts +++ b/test/integration.browser.test.ts @@ -27,8 +27,10 @@ const content = ` const [realtimeStatus, setRealtimeStatus] = React.useState(null) const channel = supabase.channel('realtime:public:todos') React.useEffect(() => { - if (channel.state === 'closed') { - channel.subscribe((status) => { if (status === 'SUBSCRIBED') setRealtimeStatus(status) }) + channel.subscribe((status) => { if (status === 'SUBSCRIBED') setRealtimeStatus(status) }) + + return () => { + channel.unsubscribe() } }, []) if (realtimeStatus) {