e2e runs on all databases

This commit is contained in:
Diego Imbert
2026-01-09 12:26:28 +01:00
parent cf831b2e79
commit 63afc26f96
7 changed files with 70 additions and 22 deletions
+3
View File
@@ -0,0 +1,3 @@
DATABASE_URL=postgres://postgres:changeme@db/windmill?sslmode=disable
WM_IMAGE=ghcr.io/windmill-labs/windmill-ee:main
+25
View File
@@ -34,11 +34,14 @@ services:
deploy:
replicas: 1
restart: unless-stopped
ports:
- 8000:8000
expose:
- 8000
- 2525
environment:
- DATABASE_URL=${DATABASE_URL}
- LICENSE_KEY=${LICENSE_KEY}
- MODE=server
depends_on:
db:
@@ -65,6 +68,7 @@ services:
# privileged: true
environment:
- DATABASE_URL=${DATABASE_URL}
- LICENSE_KEY=${LICENSE_KEY}
- MODE=worker
- WORKER_GROUP=default
# Uncomment to enable PID namespace isolation (requires privileged: true above)
@@ -100,6 +104,7 @@ services:
# privileged: true
environment:
- DATABASE_URL=${DATABASE_URL}
- LICENSE_KEY=${LICENSE_KEY}
- MODE=worker
- WORKER_GROUP=native
- NUM_WORKERS=8
@@ -113,6 +118,25 @@ services:
- worker_logs:/tmp/windmill/logs
logging: *default-logging
caddy:
image: ghcr.io/windmill-labs/caddy-l4:latest
restart: unless-stopped
# Configure the mounted Caddyfile and the exposed ports or use another reverse proxy if needed
volumes:
- ../Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
# - ../certs:/certs # Provide custom certificate files like cert.pem and key.pem to enable HTTPS - See the corresponding section in the Caddyfile
ports:
# To change the exposed port, simply change 80:80 to <desired_port>:80. No other changes needed
- 80:80
- 25:25
# - 443:443 # Uncomment to enable HTTPS handling by Caddy
environment:
- BASE_URL=":80"
# - BASE_URL=":443" # uncomment and comment line above to enable HTTPS via custom certificate and key files
# - BASE_URL=mydomain.com # Uncomment and comment line above to enable HTTPS handling by Caddy
logging: *default-logging
# E2E Testing Services
mysql_e2e:
image: mysql:8.0
@@ -227,6 +251,7 @@ volumes:
db_data: null
worker_dependency_cache: null
worker_logs: null
caddy_data: null
mysql_data: null
mssql_data: null
oracle_data: null
+22 -12
View File
@@ -4,8 +4,9 @@ import { expect, Locator, Page } from '@playwright/test'
import { getDbFeatures } from '../src/lib/components/apps/components/display/dbtable/dbFeatures'
import { ConfirmationModal, Toast } from './utils'
import { DbInput, DbType } from '../src/lib/components/dbTypes'
import { DB_TYPES } from '../src/lib/consts'
export async function runDbManagerSimpleCRUDTest(page: Page, db: _DbType) {
export async function runDbManagerSimpleCRUDTest(page: Page, dbType: _DbType) {
let dbManager = new DbManagerPage(page)
await dbManager.expectToBeVisible()
@@ -14,7 +15,8 @@ export async function runDbManagerSimpleCRUDTest(page: Page, db: _DbType) {
// Create table
const tableEditor = await dbManager.openCreateTableDrawer()
await tableEditor.setTableName(friendTableName)
await tableEditor.addColumn('name', 'TEXT')
await tableEditor.addColumn('name', getDbDatatype(dbType, 'TEXT'))
await tableEditor.getColumn('id').delete() // remove default id column
await tableEditor.createTable()
await Toast.expectSuccess(page, `${friendTableName} created`)
@@ -60,14 +62,14 @@ export async function runDbManagerAlterTableTest(page: Page, dbType: _DbType) {
let tableEditor = await dbManager.openCreateTableDrawer()
await tableEditor.setTableName(friendTableName)
let friendIdCol = await tableEditor.getColumn('id') // deafult id column
await friendIdCol.setType('INT')
await friendIdCol.setType(getDbDatatype(dbType, 'INT'))
if (dbFeatures.primaryKeys) {
friendIdCol.setPrimaryKey(true)
} else {
await expect(await friendIdCol.primaryKeyCheckbox()).toBeHidden()
}
await tableEditor.addColumn('name', 'TEXT')
await tableEditor.addColumn('created_at', dbType === 'ms_sql_server' ? 'DATETIME2' : 'TIMESTAMP')
await tableEditor.addColumn('name', getDbDatatype(dbType, 'TEXT'))
await tableEditor.addColumn('created_at', getDbDatatype(dbType, 'TIMESTAMP'))
await tableEditor.createTable()
await Toast.expectSuccess(page, `${friendTableName} created`)
await page.waitForTimeout(100)
@@ -77,12 +79,12 @@ export async function runDbManagerAlterTableTest(page: Page, dbType: _DbType) {
tableEditor = await dbManager.openCreateTableDrawer()
await tableEditor.setTableName(messageTableName)
let messageIdCol = await tableEditor.getColumn('id') // deafult id column
await messageIdCol.setType('INT')
await messageIdCol.setType(getDbDatatype(dbType, 'INT'))
if (dbFeatures.primaryKeys) messageIdCol.setPrimaryKey(true)
await tableEditor.addColumn('friend_id', 'INT')
let contentColumn = await tableEditor.addColumn('content', 'TEXT')
await tableEditor.addColumn('friend_id', getDbDatatype(dbType, 'INT'))
let contentColumn = await tableEditor.addColumn('content', getDbDatatype(dbType, 'TEXT'))
await contentColumn.setSettings({ nullable: true })
await tableEditor.addColumn('created_at', dbType === 'ms_sql_server' ? 'DATETIME2' : 'TIMESTAMP')
await tableEditor.addColumn('created_at', getDbDatatype(dbType, 'TIMESTAMP'))
if (dbFeatures.foreignKeys) {
await tableEditor.addForeignKey(friendTableName, 'friend_id', 'id', {
onDelete: 'Cascade',
@@ -119,7 +121,7 @@ export async function runDbManagerAlterTableTest(page: Page, dbType: _DbType) {
await tableEditor.setTableName(postsTableName)
await idCol.delete()
await friendCol.setName('person_id')
await friendCol.setType('BIGINT')
await friendCol.setType(getDbDatatype(dbType, 'BIGINT'))
await friendCol.setSettings({
defaultValue: dbFeatures.defaultValues ? '123' : undefined,
nullable: false
@@ -141,11 +143,11 @@ export async function runDbManagerAlterTableTest(page: Page, dbType: _DbType) {
tableEditor = new TableEditorDrawer(page)
await idCol.checkNotExists()
await friendCol.checkNameIs('person_id')
await friendCol.checkTypeIs('BIGINT')
await friendCol.checkTypeIs(getDbDatatype(dbType, 'BIGINT'))
if (dbFeatures.defaultValues) {
await friendCol.checkSettingsIs({ defaultValue: /123/ })
}
await createdAtCol.checkTypeIs('TIMESTAMP')
await createdAtCol.checkTypeIs(getDbDatatype(dbType, 'TIMESTAMP'))
await createdAtCol.checkNameIs('created_at')
if (dbFeatures.primaryKeys) {
await friendCol.checkPrimaryKeyIs(true)
@@ -359,6 +361,7 @@ class Column {
async delete() {
const deleteBtn = (await this.row()).locator('button.delete-column-btn')
await deleteBtn.click()
await this.page.waitForTimeout(50)
}
async setPrimaryKey(isPrimaryKey: boolean) {
@@ -490,3 +493,10 @@ function getDbInput(dbType: _DbType): DbInput {
return { type: 'database', resourceType: dbType, resourcePath: '' }
}
}
// Ensure exact casing of datatype as per DB_TYPES
function getDbDatatype(dbType: _DbType, datatype: string): string {
if (dbType === 'ms_sql_server' && datatype.toLowerCase() === 'timestamp') datatype = 'datetime2'
const allDataTypes = DB_TYPES[dbType == 'ducklake' ? 'duckdb' : dbType] || []
return allDataTypes.find((dt) => dt.toLowerCase() === datatype.toLowerCase()) || datatype
}
+10
View File
@@ -47,6 +47,16 @@ async function globalSetup(config: FullConfig) {
await page.waitForURL('**/')
await page.locator('text=Home').first().waitFor({ state: 'visible' })
const hubSyncBtn = page.locator('text=u/admin/hub_sync')
await hubSyncBtn.waitFor({ state: 'visible' })
await hubSyncBtn.click()
const runBtn = page.locator('#run-form-run-button')
await runBtn.waitFor({ state: 'visible' })
await runBtn.click()
await page.waitForURL(/\/run\/.+/)
await page.locator('text=Success').waitFor({ timeout: 30000, state: 'visible' })
// Save the authenticated state
await context.storageState({ path: './e2e/auth.json' })
await browser.close()
+8 -9
View File
@@ -14,15 +14,15 @@ test.describe('DB Manager', () => {
const resourceByDbType = {
postgresql: {
host: 'localhost',
host: 'postgres_e2e',
port: 5432,
dbname: 'testing',
user: 'postgres',
password: 'changeme',
dbname: 'test_db',
user: 'test_user',
password: 'postgres_password',
sslmode: 'disable'
},
mysql: {
host: 'localhost',
host: 'mysql_e2e',
port: 3306,
user: 'test_user',
database: 'test_db',
@@ -32,10 +32,10 @@ const resourceByDbType = {
oracle: {
user: 'test_user',
password: 'test_password',
database: 'localhost:1521/test_db'
database: 'oracle_e2e:1521/test_db'
},
ms_sql_server: {
host: 'localhost',
host: 'mssql_e2e',
user: 'sa',
password: 'MsSql_Pass123!',
port: 1433,
@@ -79,8 +79,6 @@ async function setupNewResource(
const jsonEditor = page.locator('.simple-editor .view-lines')
await expect(jsonEditor).toBeVisible()
await jsonEditor.click({ clickCount: 4 }) // Select all existing text
// We do copy paste to avoid issues with monaco helping with autocomplete and messing up the test
await page.context().grantPermissions(['clipboard-read', 'clipboard-write'])
await page.evaluate((c) => navigator.clipboard.writeText(c), JSON.stringify(resourceObj))
await page.keyboard.press('ControlOrMeta+V')
@@ -101,3 +99,4 @@ async function setupNewResourceAndOpenDbManager(page: Page, dbType: DbType) {
const manageButton = resourceRow.locator('button:has-text("Manage")')
await manageButton.click()
}
declare const process: any // avoid TS errors
@@ -283,6 +283,7 @@
<div class="flex gap-2 items-start flex-wrap justify-between mt-2 md:mt-6 mb-6">
<div class="flex-row-reverse flex-wrap flex w-full gap-4">
<Button
id="run-form-run-button"
{loading}
variant="accent"
btnClasses="!px-6 !py-1 !h-8 inline-flex gap-2"
@@ -100,7 +100,7 @@
}
let {
id = '',
id,
aiId = undefined,
aiDescription = undefined,
size = 'md',