From 229f6478f3298fcfed7976bef324c12caa99e9fd Mon Sep 17 00:00:00 2001 From: "Andrey A." <56412611+aantti@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:22:20 +0200 Subject: [PATCH] test(self-hosted): add an e2e test for exposed schemas in data api ui (#47602) --- .../features/data-api-type-generation.spec.ts | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 e2e/studio/features/data-api-type-generation.spec.ts diff --git a/e2e/studio/features/data-api-type-generation.spec.ts b/e2e/studio/features/data-api-type-generation.spec.ts new file mode 100644 index 00000000000..7e274785b93 --- /dev/null +++ b/e2e/studio/features/data-api-type-generation.spec.ts @@ -0,0 +1,63 @@ +import fs from 'fs' +import { expect } from '@playwright/test' + +import { env } from '../env.config.js' +import { test } from '../utils/test.js' +import { toUrl } from '../utils/to-url.js' + +/** + * Regression guard for #47577: on self-hosted, "Generate and download types" + * must produce types for exactly the schemas exposed via PostgREST — not every + * schema in the database. + * + */ +test.describe('Data API docs - type generation', () => { + // The download runs through the self-hosted pg-meta route; on platform the + // request goes to the managed API instead. + test.skip( + env.IS_PLATFORM, + 'Type generation via pg-meta is the self-hosted (IS_PLATFORM=false) code path' + ) + + test('generates types for exposed schemas only', async ({ page, ref }) => { + await page.goto(toUrl(`/project/${ref}/integrations/data_api/docs?page=tables-intro`)) + + const generateButton = page.getByRole('button', { name: 'Generate and download types' }) + await expect( + generateButton, + 'Generate types button should render on the Tables introduction page' + ).toBeVisible({ timeout: 30000 }) + + // Set up the download waiter before triggering the action. + const downloadPromise = page.waitForEvent('download', { timeout: 30000 }) + await generateButton.click() + const download = await downloadPromise + + expect(download.suggestedFilename()).toBe('supabase.ts') + + const downloadPath = await download.path() + const types = fs.readFileSync(downloadPath, 'utf-8') + fs.unlinkSync(downloadPath) + + // The public schema is always exposed. + expect(types, 'Generated types should include the public schema').toMatch(/\n\s*public:\s*\{/) + + // A non-public exposed schema is included — proves non-public schemas flow + // through, not just `public`. graphql_public is guaranteed here because the + // e2e stack pins `[api].schemas = ["public", "graphql_public"]` in + // e2e/studio/supabase/config.toml (and the CLI's Postgres ships pg_graphql, + // which creates the schema). + expect(types, 'Generated types should include the exposed graphql_public schema').toContain( + 'graphql_public' + ) + + // Internal, non-exposed schemas must NOT leak in. Before #47577 the include + // list was ignored, so every schema (auth, vault, ...) ended up in the output. + expect(types, 'Generated types should not include the internal auth schema').not.toMatch( + /\n\s*auth:\s*\{/ + ) + expect(types, 'Generated types should not include the internal vault schema').not.toMatch( + /\n\s*vault:\s*\{/ + ) + }) +})