mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
snowflake
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import { expect, Locator, Page } from '@playwright/test'
|
||||
import { getDbFeatures } from '../src/lib/components/apps/components/display/dbtable/dbFeatures'
|
||||
import { ConfirmationModal, Toast } from './utils'
|
||||
import { ConfirmationModal, Dropdown, Toast } from './utils'
|
||||
import { DbInput, DbType } from '../src/lib/components/dbTypes'
|
||||
import { DB_TYPES } from '../src/lib/consts'
|
||||
|
||||
@@ -10,12 +10,12 @@ export async function runDbManagerSimpleCRUDTest(page: Page, dbType: _DbType) {
|
||||
let dbManager = new DbManagerPage(page)
|
||||
await dbManager.expectToBeVisible()
|
||||
|
||||
let friendTableName = `friend_${Date.now()}`
|
||||
let friendTableName = identifier(dbType, `friend_${Date.now()}`)
|
||||
|
||||
// Create table
|
||||
const tableEditor = await dbManager.openCreateTableDrawer()
|
||||
await tableEditor.setTableName(friendTableName)
|
||||
await tableEditor.addColumn('name', getDbDatatype(dbType, 'TEXT'))
|
||||
await tableEditor.addColumn(identifier(dbType, 'name'), getDbDatatype(dbType, 'TEXT'))
|
||||
await tableEditor.getColumn('id').delete() // remove default id column
|
||||
await tableEditor.createTable()
|
||||
|
||||
@@ -26,7 +26,7 @@ export async function runDbManagerSimpleCRUDTest(page: Page, dbType: _DbType) {
|
||||
|
||||
// Insert a row
|
||||
const insertDrawer = await dbManager.openInsertDrawer()
|
||||
await insertDrawer.fillField('name', 'Alice')
|
||||
await insertDrawer.fillField(identifier(dbType, 'name'), 'Alice')
|
||||
await insertDrawer.insert()
|
||||
|
||||
await Toast.expectSuccess(page, 'Row inserted')
|
||||
@@ -53,12 +53,12 @@ export async function runDbManagerAlterTableTest(page: Page, dbType: _DbType) {
|
||||
await dbManager.expectToBeVisible()
|
||||
|
||||
if (dbFeatures.schemas) {
|
||||
const schemaName = `schema_${timestamp}`
|
||||
const schemaName = identifier(dbType, `schema_${timestamp}`)
|
||||
await dbManager.setCurrentSchema(schemaName, { create: true })
|
||||
}
|
||||
|
||||
// Create friend table
|
||||
let friendTableName = `friend_${timestamp}`
|
||||
let friendTableName = identifier(dbType, `friend_${timestamp}`)
|
||||
let tableEditor = await dbManager.openCreateTableDrawer()
|
||||
await tableEditor.setTableName(friendTableName)
|
||||
let friendIdCol = await tableEditor.getColumn('id') // deafult id column
|
||||
@@ -68,28 +68,36 @@ export async function runDbManagerAlterTableTest(page: Page, dbType: _DbType) {
|
||||
} else {
|
||||
await expect(await friendIdCol.primaryKeyCheckbox()).toBeHidden()
|
||||
}
|
||||
await tableEditor.addColumn('name', getDbDatatype(dbType, 'TEXT'))
|
||||
await tableEditor.addColumn('created_at', getDbDatatype(dbType, 'TIMESTAMP'))
|
||||
await tableEditor.addColumn(identifier(dbType, 'name'), getDbDatatype(dbType, 'TEXT'))
|
||||
await tableEditor.addColumn(identifier(dbType, 'created_at'), getDbDatatype(dbType, 'TIMESTAMP'))
|
||||
await tableEditor.createTable()
|
||||
await Toast.expectSuccess(page, `${friendTableName} created`)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// Create message table
|
||||
let messageTableName = `message_${timestamp}`
|
||||
let messageTableName = identifier(dbType, `message_${timestamp}`)
|
||||
tableEditor = await dbManager.openCreateTableDrawer()
|
||||
await tableEditor.setTableName(messageTableName)
|
||||
let messageIdCol = await tableEditor.getColumn('id') // deafult id column
|
||||
await messageIdCol.setType(getDbDatatype(dbType, 'INT'))
|
||||
if (dbFeatures.primaryKeys) messageIdCol.setPrimaryKey(true)
|
||||
await tableEditor.addColumn('friend_id', getDbDatatype(dbType, 'INT'))
|
||||
let contentColumn = await tableEditor.addColumn('content', getDbDatatype(dbType, 'TEXT'))
|
||||
await tableEditor.addColumn(identifier(dbType, 'friend_id'), getDbDatatype(dbType, 'INT'))
|
||||
let contentColumn = await tableEditor.addColumn(
|
||||
identifier(dbType, 'content'),
|
||||
getDbDatatype(dbType, 'TEXT')
|
||||
)
|
||||
await contentColumn.setSettings({ nullable: true })
|
||||
await tableEditor.addColumn('created_at', getDbDatatype(dbType, 'TIMESTAMP'))
|
||||
await tableEditor.addColumn(identifier(dbType, 'created_at'), getDbDatatype(dbType, 'TIMESTAMP'))
|
||||
if (dbFeatures.foreignKeys) {
|
||||
await tableEditor.addForeignKey(friendTableName, 'friend_id', 'id', {
|
||||
onDelete: 'Cascade',
|
||||
onUpdate: 'Cascade'
|
||||
})
|
||||
await tableEditor.addForeignKey(
|
||||
friendTableName,
|
||||
identifier(dbType, 'friend_id'),
|
||||
identifier(dbType, 'id'),
|
||||
{
|
||||
onDelete: 'Cascade',
|
||||
onUpdate: 'Cascade'
|
||||
}
|
||||
)
|
||||
} else {
|
||||
await expect(tableEditor.foreignKeySection()).toBeHidden()
|
||||
}
|
||||
@@ -100,10 +108,10 @@ export async function runDbManagerAlterTableTest(page: Page, dbType: _DbType) {
|
||||
// Alter message table
|
||||
await (await dbManager.openActionsMenu(messageTableName)).alterTable()
|
||||
await tableEditor.expectNoChangesDetected()
|
||||
let postsTableName = `posts_${timestamp}`
|
||||
let friendCol = tableEditor.getColumn('friend_id')
|
||||
let createdAtCol = tableEditor.getColumn('created_at')
|
||||
let idCol = tableEditor.getColumn('id')
|
||||
let postsTableName = identifier(dbType, `posts_${timestamp}`)
|
||||
let friendCol = tableEditor.getColumn(identifier(dbType, 'friend_id'))
|
||||
let createdAtCol = tableEditor.getColumn(identifier(dbType, 'created_at'))
|
||||
let idCol = tableEditor.getColumn(identifier(dbType, 'id'))
|
||||
|
||||
// Predicate checks
|
||||
if (dbFeatures.primaryKeys) {
|
||||
@@ -120,7 +128,7 @@ export async function runDbManagerAlterTableTest(page: Page, dbType: _DbType) {
|
||||
// Apply alterations
|
||||
await tableEditor.setTableName(postsTableName)
|
||||
await idCol.delete()
|
||||
await friendCol.setName('person_id')
|
||||
await friendCol.setName(identifier(dbType, 'person_id'))
|
||||
if (dbType !== 'bigquery' && dbType !== 'snowflake') {
|
||||
await friendCol.setType(getDbDatatype(dbType, 'BIGINT'))
|
||||
}
|
||||
@@ -144,7 +152,7 @@ export async function runDbManagerAlterTableTest(page: Page, dbType: _DbType) {
|
||||
await tableEditor.expectNoChangesDetected()
|
||||
tableEditor = new TableEditorDrawer(page)
|
||||
await idCol.checkNotExists()
|
||||
await friendCol.checkNameIs('person_id')
|
||||
await friendCol.checkNameIs(identifier(dbType, 'person_id'))
|
||||
if (dbType !== 'bigquery' && dbType !== 'snowflake') {
|
||||
await friendCol.checkTypeIs(getDbDatatype(dbType, 'BIGINT'))
|
||||
}
|
||||
@@ -152,7 +160,7 @@ export async function runDbManagerAlterTableTest(page: Page, dbType: _DbType) {
|
||||
await friendCol.checkSettingsIs({ defaultValue: /123/ })
|
||||
}
|
||||
await createdAtCol.checkTypeIs(getDbDatatype(dbType, 'TIMESTAMP'))
|
||||
await createdAtCol.checkNameIs('created_at')
|
||||
await createdAtCol.checkNameIs(identifier(dbType, 'created_at'))
|
||||
if (dbFeatures.primaryKeys && dbType !== 'bigquery') {
|
||||
await friendCol.checkPrimaryKeyIs(true)
|
||||
await createdAtCol.checkPrimaryKeyIs(true)
|
||||
@@ -171,9 +179,9 @@ export class DbManagerPage {
|
||||
const schemaSelect = this.dbManager().locator('input[id="db-schema-select"]')
|
||||
await schemaSelect.click()
|
||||
await schemaSelect.fill(schemaName)
|
||||
const option = this.page
|
||||
.locator(`.select-dropdown-open li:has(:text-is("${schemaName}"))`)
|
||||
.or(this.page.locator('.select-dropdown-open li:has-text("Add new")'))
|
||||
const option = Dropdown.getOption(this.page, schemaName).or(
|
||||
Dropdown.getOption(this.page, 'Add new', { exact: false })
|
||||
)
|
||||
await option.click()
|
||||
if (options?.create) {
|
||||
await ConfirmationModal.confirm(this.page, '#db-create-schema-confirmation-modal', 'Create')
|
||||
@@ -270,21 +278,13 @@ class TableEditorDrawer {
|
||||
const fkSection = this.foreignKeySection()
|
||||
const addFkButton = fkSection.locator('button:has-text("Add")')
|
||||
await addFkButton.click()
|
||||
const lastFkRow = fkSection.locator('tr').nth(-2)
|
||||
const lastFk = fkSection.locator('tr').nth(-2)
|
||||
|
||||
const tableSelect = lastFkRow.locator('input.fk-table-select')
|
||||
await tableSelect.click()
|
||||
await this.page.locator(`.select-dropdown-open li:has(:text-is("${referencedTable}"))`).click()
|
||||
await Dropdown.selectOption(this.page, lastFk.locator('input.fk-table-select'), referencedTable)
|
||||
await Dropdown.selectOption(this.page, lastFk.locator('.fk-source-col-select input'), fromCol)
|
||||
await Dropdown.selectOption(this.page, lastFk.locator('.fk-target-col-select input'), toCol)
|
||||
|
||||
const fromColSelect = lastFkRow.locator('.fk-source-col-select')
|
||||
await fromColSelect.click()
|
||||
await this.page.locator(`.select-dropdown-open li:has(:text-is("${fromCol}"))`).click()
|
||||
|
||||
const toColSelect = lastFkRow.locator('.fk-target-col-select')
|
||||
await toColSelect.click()
|
||||
await this.page.locator(`.select-dropdown-open li:has(:text-is("${toCol}"))`).click()
|
||||
|
||||
const fkSettings = lastFkRow.locator('.fk-settings-btn')
|
||||
const fkSettings = lastFk.locator('.fk-settings-btn')
|
||||
if (options?.onDelete || options?.onUpdate) {
|
||||
await fkSettings.click()
|
||||
if (options?.onDelete) {
|
||||
@@ -352,9 +352,11 @@ class Column {
|
||||
}
|
||||
|
||||
async setType(columnType: string) {
|
||||
const newColTypeSelect = (await this.row()).locator('td').nth(1).locator('input')
|
||||
await newColTypeSelect.click()
|
||||
await this.page.locator(`.select-dropdown-open li:has(:text-is("${columnType}"))`).click()
|
||||
await Dropdown.selectOption(
|
||||
this.page,
|
||||
(await this.row()).locator('td').nth(1).locator('input'),
|
||||
columnType
|
||||
)
|
||||
}
|
||||
|
||||
async checkTypeIs(columnType: string) {
|
||||
@@ -507,3 +509,9 @@ function getDbDatatype(dbType: _DbType, datatype: string): string {
|
||||
const allDataTypes = DB_TYPES[dbType == 'ducklake' ? 'duckdb' : dbType] || []
|
||||
return allDataTypes.find((dt) => dt.toLowerCase() === datatype.toLowerCase()) || datatype
|
||||
}
|
||||
|
||||
function identifier(dbType: _DbType, baseName: string): string {
|
||||
baseName = baseName.replace(/[^a-zA-Z0-9_]/g, '_').trim()
|
||||
if (dbType === 'snowflake') return baseName.toUpperCase()
|
||||
return baseName
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { test, expect, Page, Locator } from '@playwright/test'
|
||||
import { runDbManagerAlterTableTest, runDbManagerSimpleCRUDTest } from './DbManagerPage'
|
||||
import { DbType } from '../src/lib/components/dbTypes'
|
||||
import { Toast, prettify } from './utils'
|
||||
import { Dropdown, Toast, prettify } from './utils'
|
||||
|
||||
const resourceByDbType = {
|
||||
postgresql: getJsonEnv('POSTGRESQL_RESOURCE') ?? {
|
||||
@@ -94,7 +94,7 @@ async function openDucklakeDbManager(page: Page, resource_type: StorageResourceT
|
||||
let lastRow = page.locator('.ducklake-settings-table').locator('tr').nth(-2)
|
||||
await lastRow.locator('input.ducklake-name').fill(`ducklake_${timestamp}`)
|
||||
await lastRow.locator('.ducklake-workspace-storage-select').click()
|
||||
await page.locator(`.select-dropdown-open li:has(:has-text("${storage}"))`).click()
|
||||
await Dropdown.getOption(page, storage, { exact: false }).click()
|
||||
await lastRow.locator('input.ducklake-storage-data-path').fill(`ducklake_${timestamp}`)
|
||||
await setupCustomInstanceDb(lastRow, page, storage)
|
||||
await page.locator('button:has-text("Save ducklake settings")').click()
|
||||
@@ -173,8 +173,9 @@ async function setupCustomInstanceDb(row: Locator, page: Page, name: string) {
|
||||
await customInstanceDbSelect.fill(name)
|
||||
|
||||
// Check if the database already exists in the dropdown
|
||||
if (await page.locator(`.select-dropdown-open li:has(:text-is("${name}"))`).isVisible()) {
|
||||
await page.locator(`.select-dropdown-open li:has(:text-is("${name}"))`).click()
|
||||
|
||||
if (await Dropdown.getOption(page, name).isVisible()) {
|
||||
await Dropdown.getOption(page, name).click()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -264,11 +265,12 @@ async function setupWsStorage(
|
||||
await lastRow.locator('#storage-resource-type-select').click()
|
||||
const dropdownResourceTypeLabel =
|
||||
storageResourceTypeDropdownLabels[resource_type] ?? resource_type
|
||||
await page.locator(`.select-dropdown-open li:has(:text-is("${dropdownResourceTypeLabel}"))`)
|
||||
|
||||
await Dropdown.getOption(page, dropdownResourceTypeLabel).click()
|
||||
|
||||
// Select resource
|
||||
await lastRow.locator('#resource-picker-select').click()
|
||||
await page.locator(`.select-dropdown-open li:has(:has-text("${resourceName}"))`).click()
|
||||
await Dropdown.getOption(page, resourceName, { exact: false }).click()
|
||||
|
||||
await page.locator('button:has-text("Save storage settings")').click()
|
||||
await Toast.expectSuccess(page, 'storage settings changed')
|
||||
|
||||
+19
-1
@@ -1,4 +1,4 @@
|
||||
import { expect, Page } from '@playwright/test'
|
||||
import { expect, Locator, Page } from '@playwright/test'
|
||||
|
||||
export class Toast {
|
||||
static async expectSuccess(page: Page, message: string) {
|
||||
@@ -26,3 +26,21 @@ export class ConfirmationModal {
|
||||
}
|
||||
|
||||
export const prettify = (s: string) => (s.charAt(0).toUpperCase() + s.slice(1)).replace(/_/g, ' ')
|
||||
|
||||
export class Dropdown {
|
||||
static getOption(page: Page, optionText: string, { exact = true } = {}) {
|
||||
if (exact) return page.locator(`.select-dropdown-open li:has(:text-is("${optionText}"))`)
|
||||
else return page.locator(`.select-dropdown-open li:has-text("${optionText}")`)
|
||||
}
|
||||
static async selectOption(
|
||||
page: Page,
|
||||
selectInput: Locator,
|
||||
optionText: string,
|
||||
{ exact = true } = {}
|
||||
) {
|
||||
await selectInput.click()
|
||||
await selectInput.fill(optionText)
|
||||
const option = this.getOption(page, optionText, { exact })
|
||||
await option.click()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,14 +220,10 @@
|
||||
let newSchemaName = $state('')
|
||||
|
||||
// Check if the sanitized schema name already exists
|
||||
const sanitizedNewSchemaName = $derived(
|
||||
newSchemaName
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-zA-Z0-9_]/g, '')
|
||||
)
|
||||
const sanitizedNewSchemaName = $derived(newSchemaName.trim().replace(/[^a-zA-Z0-9_]/g, ''))
|
||||
const schemaAlreadyExists = $derived(
|
||||
sanitizedNewSchemaName !== '' && schemaKeys.includes(sanitizedNewSchemaName)
|
||||
sanitizedNewSchemaName !== '' &&
|
||||
schemaKeys.map((s) => s.toLowerCase()).includes(sanitizedNewSchemaName.toLowerCase())
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -247,10 +243,7 @@
|
||||
placeholder="Search or create schema..."
|
||||
showPlaceholderOnOpen
|
||||
onCreateItem={(schema) => {
|
||||
schema = schema
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-zA-Z0-9_]/g, '')
|
||||
schema = schema.trim().replace(/[^a-zA-Z0-9_]/g, '')
|
||||
askingForConfirmation = {
|
||||
confirmationText: `Create ${schema}`,
|
||||
type: 'reload',
|
||||
|
||||
@@ -314,7 +314,8 @@
|
||||
items={getFlatTableNamesFromSchema(dbSchema).map((o) => ({
|
||||
value: o,
|
||||
label:
|
||||
(currentSchema && o.startsWith(currentSchema)) || !features?.schemas
|
||||
!features?.schemas ||
|
||||
(currentSchema && o.toLowerCase().startsWith(currentSchema.toLowerCase()))
|
||||
? o.split('.')[1]
|
||||
: o
|
||||
}))}
|
||||
|
||||
Reference in New Issue
Block a user