Files
supabase/e2e/studio/features/wrappers.spec.ts
Gildas Garcia 914677ed4b chore: migrate foreign wrapper forms to react-hook-form (#44512)
## Problem

Foreign wrapper forms still use `formik` but we now use
`react-hook-form` everywhere and we'd like to reduce our dependencies.

## Solution

- [x] Write e2e tests for wrappers
- [x] Migrate to `react-hook-form`

## Notes

I tried to cover the 3 cases I identified for foreign wrappers with e2e
tests:
- Add all available tables to a new schema (stripe)
- Add selected tables to a new table (stripe)
- Create dynamic columns (s3 wrapper)

However, they are not exhaustive as I can't test the integration
actually works, only that it was created successfully. Besides, I can't
test the Iceberg wrapper case as it needs actual S3 buckets.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Enhanced column-type selector with searchable combobox, enum support,
icons, and optional recommendation prompts.

* **Refactor**
* Migrated many wrapper/table/editor forms to a schema-driven form
system with stronger validation, dynamic field arrays, and consistent
form controls.
* Updated input field integration to work with the new form control
model.

* **Bug Fixes**
* Improved handling of missing wrapper/error states during wrapper
loading.

* **Tests**
* Added unit tests for form schemas and end-to-end tests for wrapper
creation flows.

* **Chores**
  * Removed legacy dynamic-columns component.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-09 09:20:27 +02:00

143 lines
6.0 KiB
TypeScript

import { expect } from '@playwright/test'
import { env } from '../env.config.js'
import { query } from '../utils/db/client.js'
import { test, withSetupCleanup } from '../utils/test.js'
import { toUrl } from '../utils/to-url.js'
const testRunner = env.IS_PLATFORM ? test.describe.serial : test.describe
testRunner('Stripe', () => {
test('can create a stripe wrapper with schema', async ({ page, ref }) => {
const wrapperName = 'stripe_schema'
const schemaName = 'stripe'
await using _ = await withSetupCleanup(
async () => {
await query(`
create schema if not exists extensions;
create extension if not exists wrappers
schema extensions
version '0.5.7'
cascade;
`)
},
async () => {
// Make the request in the page context so that the it can delete wrappers that belongs to supabase_admin
await page.request.post(toUrl(`/api/platform/pg-meta/${ref}/query`), {
failOnStatusCode: true,
data: {
query: `
drop foreign data wrapper if exists ${wrapperName} cascade;
delete from vault.secrets where name = '${wrapperName}_api_key_id';
drop schema ${schemaName} cascade;`,
},
})
}
)
await page.goto(toUrl(`/project/${ref}/integrations/stripe_wrapper/overview`))
await page.getByRole('button', { name: 'Add new wrapper' }).click()
await page.getByRole('textbox', { name: 'Wrapper Name' }).fill(wrapperName)
await page.getByRole('textbox', { name: 'Stripe Secret Key' }).fill('my secret')
await page.getByRole('radio', { name: 'Schema' }).click()
await page
.getByRole('textbox', { name: 'Specify a new schema to create all wrapper tables in' })
.fill(schemaName)
await page.getByRole('button', { name: 'Create wrapper' }).click()
await expect(page.getByText('Successfully created Stripe foreign data wrapper')).toBeVisible()
})
test('can create a stripe wrapper with tables', async ({ page, ref }) => {
const wrapperName = 'stripe_tables'
const tableName = 'stripe_accounts'
await using _ = await withSetupCleanup(
async () => {
await query(`
create schema if not exists extensions;
create extension if not exists wrappers
schema extensions
version '0.5.7'
cascade;
`)
},
async () => {
// Make the request in the page context so that the it can delete wrappers that belongs to supabase_admin
await page.request.post(toUrl(`/api/platform/pg-meta/${ref}/query`), {
failOnStatusCode: true,
data: {
query: `
drop foreign data wrapper if exists ${wrapperName} cascade;
delete from vault.secrets where name = '${wrapperName}_api_key_id';
drop table if exists public.${tableName};`,
},
})
}
)
await page.goto(toUrl(`/project/${ref}/integrations/stripe_wrapper/overview`))
await page.getByRole('button', { name: 'Add new wrapper' }).click()
await page.getByRole('textbox', { name: 'Wrapper Name' }).fill(wrapperName)
await page.getByRole('textbox', { name: 'Stripe Secret Key' }).fill('my secret')
await page.getByRole('button', { name: 'Add foreign table' }).click()
await page.getByRole('combobox').click()
await page.getByRole('option', { name: 'Accounts List of accounts on' }).click()
await page.getByRole('textbox', { name: 'Table name' }).fill(tableName)
await page.getByRole('button', { name: 'Save' }).click()
await page.getByRole('button', { name: 'Create wrapper' }).click()
await expect(page.getByText('Successfully created Stripe foreign data wrapper')).toBeVisible()
})
})
testRunner('S3 Wrapper', () => {
test('can create an S3 wrapper', async ({ page, ref }) => {
const wrapperName = 'test_s3_wrapper'
const tableName = 'test_s3_wrapper_table'
await using _ = await withSetupCleanup(
async () => {
await query(`
create schema if not exists extensions;
create extension if not exists wrappers
schema extensions
version '0.5.7'
cascade;
`)
},
async () => {
// Make the request in the page context so that the it can delete wrappers that belongs to supabase_admin
await page.request.post(toUrl(`/api/platform/pg-meta/${ref}/query`), {
failOnStatusCode: true,
data: {
query: `
drop foreign data wrapper if exists ${wrapperName} cascade;
delete from vault.secrets where name = '${wrapperName}_vault_access_key_id';
delete from vault.secrets where name = '${wrapperName}_vault_secret_access_key';
drop table if exists public.${tableName};`,
},
})
}
)
await page.goto(toUrl(`/project/${ref}/integrations/s3_wrapper/overview`))
await page.getByRole('button', { name: 'Add new wrapper' }).click()
await page.getByRole('textbox', { name: 'Wrapper Name' }).fill(wrapperName)
await page.getByRole('textbox', { name: 'Access Key ID' }).fill('s3 access id')
await page.getByRole('textbox', { name: 'Access Key Secret' }).fill('s3 access secret')
await page.getByRole('button', { name: 'Add foreign table' }).click()
await page.getByRole('combobox').click()
await page.getByRole('option', { name: 'S3 File' }).click()
await page.getByRole('textbox', { name: 'Table name' }).fill(tableName)
await page.getByRole('textbox', { name: 'URI' }).fill('s3://bucket/s3_table.csv')
await page.getByRole('button', { name: 'Add column' }).click()
// FIXME: Necessary because this component is somehow remounted
await page.waitForTimeout(500)
await page.getByRole('textbox', { name: 'Name', exact: true }).fill('s3_column')
await page.getByRole('button', { name: 'Save' }).click()
await page.getByRole('button', { name: 'Create wrapper' }).click()
await expect(page.getByText('Successfully created S3 foreign data wrapper')).toBeVisible()
})
})