Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions apps/e2e/src/generated/test-ids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2960,6 +2960,44 @@ export const TestIds = {
/** button · Unlock */
cloneUnlock: 'clone-unlock',
},
ColumnChangePanel: {
/** input */
columnChangeBatch: 'column-change-batch',
/** select · Pick a column */
columnChangeColumn: 'column-change-column',
/** button */
columnChangeCopyAll: 'column-change-copy-all',
/** p */
columnChangeHint: 'column-change-hint',
/** section · What it touches */
columnChangeImpact: 'column-change-impact',
/** p */
columnChangeLoadError: 'column-change-load-error',
/** input */
columnChangeNewName: 'column-change-new-name',
/** input */
columnChangeNewType: 'column-change-new-type',
/** button · Open in editor */
columnChangeOpenEditor: 'column-change-open-editor',
/** div · What it touches */
columnChangePanel: 'column-change-panel',
/** input */
columnChangePassword: 'column-change-password',
/** p */
columnChangeRefused: 'column-change-refused',
/** article */
columnChangeStep: (value: string | number) => `column-change-step-${value}`,
/** button */
columnChangeStepCopy: (value: string | number) => `column-change-step-${value}-copy`,
/** section · Steps ( ) */
columnChangeSteps: 'column-change-steps',
/** select */
columnChangeTable: 'column-change-table',
/** button · Unlock */
columnChangeUnlock: 'column-change-unlock',
/** ul */
columnChangeWarnings: 'column-change-warnings',
},
DatabaseAccessModal: {
/** div */
dbAccessAllForm: 'db-access-all-form',
Expand Down Expand Up @@ -3229,6 +3267,8 @@ export const TestIds = {
sqlSidebarUtilities: 'sql-sidebar-utilities',
/** data · Backup & Restore */
utilitiesBackup: 'utilities-backup',
/** data · Change Column */
utilitiesChangeColumn: 'utilities-change-column',
/** data · Clone Table */
utilitiesCloneTable: 'utilities-clone-table',
/** select */
Expand Down Expand Up @@ -4077,6 +4117,24 @@ export type TestId =
| 'cloud-cred-save'
| `cloud-cred-status-${string}`
| 'cloud-provider-credentials'
| 'column-change-batch'
| 'column-change-column'
| 'column-change-copy-all'
| 'column-change-hint'
| 'column-change-impact'
| 'column-change-load-error'
| 'column-change-new-name'
| 'column-change-new-type'
| 'column-change-open-editor'
| 'column-change-panel'
| 'column-change-password'
| 'column-change-refused'
| `column-change-step-${string}`
| `column-change-step-${string}-copy`
| 'column-change-steps'
| 'column-change-table'
| 'column-change-unlock'
| 'column-change-warnings'
| 'command-palette'
| 'command-palette-btn'
| 'command-palette-input'
Expand Down Expand Up @@ -5190,6 +5248,7 @@ export type TestId =
| 'user-valid-until'
| 'user-warnings'
| 'utilities-backup'
| 'utilities-change-column'
| 'utilities-clone-table'
| 'utilities-connection'
| 'utilities-connection-pool'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { TableSchema } from '@/shared/lib/types';

vi.mock('@/features/compare', () => {
const state = { connections: [{ id: 'pg', name: 'Shop', dialect: 'postgres', schema: 'shop', hasPassword: true }] };
const useSyncStore = (sel: (s: typeof state) => unknown) => sel(state);
useSyncStore.getState = () => state;
return { useSyncStore };
});

import { useSqlEditorStore } from '@/features/sql-editor/state';
import { useUiStore } from '@/platform/uiStore';
import { ColumnChangePanel } from './ColumnChangePanel';

const orders: TableSchema = {
name: 'orders',
objectType: 'TABLE',
columns: [
{ name: 'id', type: 'integer', nullable: false, primaryKey: true },
{ name: 'status', type: 'varchar(20)', nullable: false, primaryKey: false },
],
primaryKey: { columns: ['id'] },
indices: [{ name: 'orders_status_idx', columns: ['status'], unique: false }],
foreignKeys: [],
};

const pick = (testId: string, value: string) => fireEvent.change(screen.getByTestId(testId), { target: { value } });

describe('ColumnChangePanel', () => {
beforeEach(() => {
useSqlEditorStore.setState({
schemaCache: { pg: { status: 'ready', tables: [orders] } },
ensureSchema: async () => undefined,
ensureConnectionSelected: () => undefined,
} as never);
});

it('shows what a type change touches and its steps, and opens them in a new tab for this connection only', () => {
const setActiveView = vi.spyOn(useUiStore.getState(), 'setActiveView');
render(<ColumnChangePanel lockedConnectionId="pg" />);
pick('column-change-table', 'orders');
pick('column-change-column', 'status');
expect(screen.getByTestId('column-change-hint')).toBeTruthy();
pick('column-change-new-type', 'varchar(40)');

expect(screen.getByTestId('column-change-impact').textContent).toContain('orders_status_idx');
expect(screen.getAllByTestId(/^column-change-step-\d+$/)).toHaveLength(7);
expect(screen.getByTestId('column-change-step-1').textContent).toContain(
'ALTER TABLE "shop"."orders" ADD COLUMN IF NOT EXISTS "status__new" varchar(40);'
);

const tabsBefore = useSqlEditorStore.getState().tabs.length;
fireEvent.click(screen.getByTestId('column-change-open-editor'));
const editor = useSqlEditorStore.getState();
const tab = editor.activeTab();
expect(editor.tabs).toHaveLength(tabsBefore + 1);
expect(tab.title).toBe('Change orders.status');
expect(tab.sql.startsWith('-- Step 1: Add the new column status__new (varchar(40))\n')).toBe(true);
expect(tab.sql).toContain('-- Step 7: Drop the old column status__old');
// Only this connection: the steps belong to one database.
expect(editor.activeConnectionIds()).toEqual(['pg']);
expect(setActiveView).toHaveBeenCalledWith('sqlEditor');
});

it('gives a rename alone as one step', () => {
render(<ColumnChangePanel lockedConnectionId="pg" />);
pick('column-change-table', 'orders');
pick('column-change-column', 'status');
pick('column-change-new-name', 'state');
expect(screen.getAllByTestId(/^column-change-step-\d+$/)).toHaveLength(1);
expect(screen.getByTestId('column-change-step-1').textContent).toContain(
'ALTER TABLE "shop"."orders" RENAME COLUMN "status" TO "state";'
);
});

it('says why when the change is refused', () => {
render(<ColumnChangePanel lockedConnectionId="pg" />);
pick('column-change-table', 'orders');
pick('column-change-column', 'id');
pick('column-change-new-type', 'bigint');
expect(screen.getByTestId('column-change-refused').textContent).toMatch(/primary key column/);
expect(screen.queryByTestId('column-change-steps')).toBeNull();
});

it('starts a new change when another column is picked', () => {
render(<ColumnChangePanel lockedConnectionId="pg" />);
pick('column-change-table', 'orders');
pick('column-change-column', 'status');
pick('column-change-new-name', 'state');
pick('column-change-column', 'id');
expect((screen.getByTestId('column-change-new-name') as HTMLInputElement).value).toBe('');
});
});
Loading
Loading