@ochorocho/playwright-db-connector 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,313 @@
1
+ import * as _playwright_test from '@playwright/test';
2
+ import { Knex } from 'knex';
3
+
4
+ /**
5
+ * Database client identifiers supported by the plugin.
6
+ */
7
+ type DatabaseClient = 'pg' | 'mysql2' | 'better-sqlite3' | 'sqlite3';
8
+ /**
9
+ * Connection configuration for server-based databases (PostgreSQL, MySQL, MariaDB).
10
+ */
11
+ interface ServerConnectionConfig {
12
+ host: string;
13
+ port?: number;
14
+ user: string;
15
+ password: string;
16
+ database: string;
17
+ ssl?: boolean | Record<string, unknown>;
18
+ }
19
+ /**
20
+ * Connection configuration for SQLite databases.
21
+ */
22
+ interface SqliteConnectionConfig {
23
+ filename: string;
24
+ }
25
+ /**
26
+ * Union type for all supported connection configurations.
27
+ * Also accepts a raw connection string (e.g., postgres://user:pass@host/db).
28
+ */
29
+ type ConnectionConfig = ServerConnectionConfig | SqliteConnectionConfig | string;
30
+ /**
31
+ * Pool configuration options.
32
+ */
33
+ interface PoolConfig {
34
+ min?: number;
35
+ max?: number;
36
+ idleTimeoutMillis?: number;
37
+ }
38
+ /**
39
+ * Cleanup strategy after each test.
40
+ * - 'transaction': Wrap each test in a transaction and rollback (fastest, default).
41
+ * - 'truncate': TRUNCATE all tables that were modified.
42
+ * - 'delete': DELETE rows that were inserted by haveInDatabase.
43
+ * - 'none': No automatic cleanup (user manages state).
44
+ */
45
+ type CleanupStrategy = 'transaction' | 'truncate' | 'delete' | 'none';
46
+ /**
47
+ * Top-level configuration object for the db fixture.
48
+ * Users provide this in playwright.config.ts via `use: { dbConfig: { ... } }`.
49
+ */
50
+ interface DbConnectorConfig {
51
+ /** Database client identifier */
52
+ client: DatabaseClient;
53
+ /** Connection details */
54
+ connection: ConnectionConfig;
55
+ /** Connection pool settings (ignored for SQLite) */
56
+ pool?: PoolConfig;
57
+ /** How to clean up test data after each test. Default: 'transaction' */
58
+ cleanupStrategy?: CleanupStrategy;
59
+ /** SQL file path(s) to execute before the test suite starts (worker-scoped) */
60
+ seedFiles?: string | string[];
61
+ /** CSV file path(s) in TYPO3 format to import before the test suite starts (worker-scoped) */
62
+ seedCsvFiles?: string | string[];
63
+ /** Name of the primary key column. Default: 'id'. TYPO3 uses 'uid'. */
64
+ primaryKeyColumn?: string;
65
+ /** Additional knex configuration (escape hatch) */
66
+ knexConfig?: Partial<Knex.Config>;
67
+ }
68
+ /**
69
+ * A record of column-value pairs used for WHERE clauses.
70
+ */
71
+ type WhereCriteria = Record<string, unknown>;
72
+ /**
73
+ * A record of column-value pairs for INSERT or UPDATE operations.
74
+ */
75
+ type RecordData = Record<string, unknown>;
76
+ /**
77
+ * An insert tracked for automatic cleanup.
78
+ */
79
+ interface TrackedInsert {
80
+ table: string;
81
+ primaryKey: string;
82
+ primaryKeyValues: unknown[];
83
+ }
84
+ /**
85
+ * The main API surface exposed to Playwright tests as the `db` fixture.
86
+ */
87
+ interface DatabaseConnector {
88
+ /** Assert that at least one record matching criteria exists. */
89
+ seeInDatabase(table: string, criteria: WhereCriteria): Promise<void>;
90
+ /** Assert that no records matching criteria exist. */
91
+ dontSeeInDatabase(table: string, criteria: WhereCriteria): Promise<void>;
92
+ /** Assert that exactly `expectedCount` records match criteria. */
93
+ seeNumRecords(expectedCount: number, table: string, criteria?: WhereCriteria): Promise<void>;
94
+ /** Retrieve a single column value from the first matching record. */
95
+ grabFromDatabase<T = unknown>(table: string, column: string, criteria?: WhereCriteria): Promise<T | undefined>;
96
+ /** Retrieve all values of a single column from matching records. */
97
+ grabColumnFromDatabase<T = unknown>(table: string, column: string, criteria?: WhereCriteria): Promise<T[]>;
98
+ /** Retrieve all matching records as an array of objects. */
99
+ grabEntriesFromDatabase(table: string, criteria?: WhereCriteria): Promise<RecordData[]>;
100
+ /** Retrieve the count of matching records. */
101
+ grabNumRecords(table: string, criteria?: WhereCriteria): Promise<number>;
102
+ /** Insert a record. Tracked for auto-cleanup. Returns the primary key value. */
103
+ haveInDatabase(table: string, data: RecordData): Promise<unknown>;
104
+ /** Update records matching criteria. Returns number of affected rows. */
105
+ updateInDatabase(table: string, data: RecordData, criteria: WhereCriteria): Promise<number>;
106
+ /** Delete records matching criteria. Returns number of deleted rows. */
107
+ deleteFromDatabase(table: string, criteria: WhereCriteria): Promise<number>;
108
+ /** Execute a raw SQL query with parameter bindings. */
109
+ query<T = RecordData[]>(sql: string, bindings?: readonly unknown[]): Promise<T>;
110
+ /** Execute a callback within an explicit transaction (or savepoint if already in one). */
111
+ transaction<T>(callback: (trx: DatabaseConnector) => Promise<T>): Promise<T>;
112
+ /** Load and execute a SQL file against the database. */
113
+ loadSqlFile(filePath: string): Promise<void>;
114
+ /**
115
+ * Import a CSV dataset file (TYPO3 testing framework format).
116
+ *
117
+ * Format: table name in first column, column headers following on the same row,
118
+ * data rows start with an empty first field. Multiple tables per file supported.
119
+ *
120
+ * Example:
121
+ * ```csv
122
+ * "users","uid","name","email"
123
+ * ,1,"Alice","alice@example.com"
124
+ * ```
125
+ */
126
+ importCsvFile(filePath: string): Promise<void>;
127
+ /**
128
+ * Assert that the database matches the expected data in a CSV file.
129
+ * Looks up each row by its first column (primary key) and compares all values.
130
+ * Throws with a detailed diff on mismatch.
131
+ */
132
+ assertCsvDataSet(filePath: string): Promise<void>;
133
+ /** Access the underlying knex instance for advanced usage. */
134
+ readonly knex: Knex;
135
+ }
136
+ /**
137
+ * Test-scoped fixtures provided by the plugin.
138
+ */
139
+ interface DbTestFixtures {
140
+ db: DatabaseConnector;
141
+ }
142
+ /**
143
+ * Worker-scoped fixtures provided by the plugin.
144
+ */
145
+ interface DbWorkerFixtures {
146
+ dbConfig: DbConnectorConfig;
147
+ _dbKnexPool: Knex;
148
+ }
149
+
150
+ declare const test: _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & DbTestFixtures, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & DbWorkerFixtures>;
151
+
152
+ declare const expect: _playwright_test.Expect<{
153
+ toHaveRecord(this: _playwright_test.ExpectMatcherState, db: DatabaseConnector, table: string, criteria: WhereCriteria): Promise<{
154
+ pass: false;
155
+ message: () => string;
156
+ name: string;
157
+ expected?: undefined;
158
+ actual?: undefined;
159
+ } | {
160
+ pass: boolean;
161
+ message: () => string;
162
+ name: string;
163
+ expected: string | number;
164
+ actual: number;
165
+ }>;
166
+ toHaveRecordCount(this: _playwright_test.ExpectMatcherState, db: DatabaseConnector, expectedCount: number, table: string, criteria?: WhereCriteria): Promise<{
167
+ pass: false;
168
+ message: () => string;
169
+ name: string;
170
+ expected?: undefined;
171
+ actual?: undefined;
172
+ } | {
173
+ pass: boolean;
174
+ message: () => string;
175
+ name: string;
176
+ expected: number;
177
+ actual: number;
178
+ }>;
179
+ }>;
180
+
181
+ declare class CleanupTracker {
182
+ private inserts;
183
+ track(table: string, primaryKey: string, primaryKeyValues: unknown[]): void;
184
+ /**
185
+ * Delete tracked rows in reverse insertion order (respects foreign keys).
186
+ */
187
+ cleanupByDelete(queryable: Knex): Promise<void>;
188
+ /**
189
+ * Truncate all tables that received inserts. Dialect-aware.
190
+ */
191
+ cleanupByTruncate(queryable: Knex, client: DatabaseClient): Promise<void>;
192
+ clear(): void;
193
+ get trackedInserts(): ReadonlyArray<TrackedInsert>;
194
+ }
195
+
196
+ declare class DatabaseConnectorImpl implements DatabaseConnector {
197
+ private readonly queryable;
198
+ private readonly tracker;
199
+ private readonly client;
200
+ private readonly primaryKeyColumn;
201
+ constructor(queryable: Knex | Knex.Transaction, tracker: CleanupTracker, client: DatabaseClient, primaryKeyColumn?: string);
202
+ get knex(): Knex;
203
+ seeInDatabase(table: string, criteria: WhereCriteria): Promise<void>;
204
+ dontSeeInDatabase(table: string, criteria: WhereCriteria): Promise<void>;
205
+ seeNumRecords(expectedCount: number, table: string, criteria?: WhereCriteria): Promise<void>;
206
+ grabFromDatabase<T = unknown>(table: string, column: string, criteria?: WhereCriteria): Promise<T | undefined>;
207
+ grabColumnFromDatabase<T = unknown>(table: string, column: string, criteria?: WhereCriteria): Promise<T[]>;
208
+ grabEntriesFromDatabase(table: string, criteria?: WhereCriteria): Promise<RecordData[]>;
209
+ grabNumRecords(table: string, criteria?: WhereCriteria): Promise<number>;
210
+ haveInDatabase(table: string, data: RecordData): Promise<unknown>;
211
+ updateInDatabase(table: string, data: RecordData, criteria: WhereCriteria): Promise<number>;
212
+ deleteFromDatabase(table: string, criteria: WhereCriteria): Promise<number>;
213
+ query<T = RecordData[]>(sql: string, bindings?: readonly unknown[]): Promise<T>;
214
+ transaction<T>(callback: (trx: DatabaseConnector) => Promise<T>): Promise<T>;
215
+ loadSqlFile(filePath: string): Promise<void>;
216
+ importCsvFile(filePath: string): Promise<void>;
217
+ assertCsvDataSet(filePath: string): Promise<void>;
218
+ }
219
+
220
+ declare class SqlLoader {
221
+ /**
222
+ * Read a SQL file and execute its statements against the database.
223
+ */
224
+ static executeFile(db: Knex, filePath: string): Promise<void>;
225
+ /**
226
+ * Execute a SQL string containing one or more semicolon-separated statements.
227
+ */
228
+ static executeStatements(db: Knex, sql: string): Promise<void>;
229
+ /**
230
+ * Split a SQL string on semicolons while respecting:
231
+ * - Single-quoted strings ('it''s ok')
232
+ * - Double-quoted identifiers ("table")
233
+ * - Single-line comments (-- comment)
234
+ * - Multi-line comments (/* comment *​/)
235
+ */
236
+ static splitStatements(sql: string): string[];
237
+ }
238
+
239
+ /**
240
+ * Parsed representation of a CSV dataset: table name → array of row objects.
241
+ */
242
+ type CsvDataSet = Map<string, RecordData[]>;
243
+ /**
244
+ * Loads and asserts CSV datasets using the TYPO3 testing framework format.
245
+ *
246
+ * Format:
247
+ * - A row starting with a quoted table name begins a new table section.
248
+ * The remaining values on that row are the column headers.
249
+ * - Subsequent rows (starting with a comma / empty first field) are data rows.
250
+ * - `\NULL` represents a SQL NULL value.
251
+ * - Multiple tables can appear in one file.
252
+ *
253
+ * Example:
254
+ * ```csv
255
+ * "users","uid","name","email","active"
256
+ * ,1,"Alice","alice@example.com",1
257
+ * ,2,"Bob","bob@example.com",1
258
+ * "posts","uid","user_id","title"
259
+ * ,1,1,"Hello World"
260
+ * ```
261
+ */
262
+ declare class CsvLoader {
263
+ /**
264
+ * Parse a CSV file into a CsvDataSet.
265
+ */
266
+ static parseFile(filePath: string): CsvDataSet;
267
+ /**
268
+ * Parse CSV content string into a CsvDataSet.
269
+ */
270
+ static parse(content: string): CsvDataSet;
271
+ /**
272
+ * Import a CSV file into the database.
273
+ * Inserts all rows from the dataset into their respective tables.
274
+ */
275
+ static importFile(db: Knex, filePath: string): Promise<void>;
276
+ /**
277
+ * Import a parsed CsvDataSet into the database.
278
+ */
279
+ static importDataSet(db: Knex, dataset: CsvDataSet): Promise<void>;
280
+ /**
281
+ * Assert that the database contains exactly the data described in a CSV file.
282
+ * For each table/row in the CSV, looks up the row by its first column (typically
283
+ * the primary key) and compares all other column values.
284
+ *
285
+ * Throws DatabaseAssertionError with a detailed diff on mismatch.
286
+ */
287
+ static assertFile(db: Knex, filePath: string): Promise<void>;
288
+ /**
289
+ * Assert that the database matches a parsed CsvDataSet.
290
+ */
291
+ static assertDataSet(db: Knex, dataset: CsvDataSet): Promise<void>;
292
+ /**
293
+ * Parse a single CSV line into an array of field values.
294
+ * Handles quoted fields and escaped quotes (double-double-quotes).
295
+ */
296
+ static parseLine(line: string): string[];
297
+ /**
298
+ * Convert a CSV field value to a JavaScript value.
299
+ * - `\NULL` → null
300
+ * - numeric strings → number
301
+ * - everything else → string
302
+ */
303
+ private static convertValue;
304
+ }
305
+
306
+ declare class DatabaseDriverNotFoundError extends Error {
307
+ constructor(client: string);
308
+ }
309
+ declare class DatabaseAssertionError extends Error {
310
+ constructor(message: string);
311
+ }
312
+
313
+ export { type CleanupStrategy, CleanupTracker, type ConnectionConfig, type CsvDataSet, CsvLoader, DatabaseAssertionError, type DatabaseClient, type DatabaseConnector, DatabaseConnectorImpl, DatabaseDriverNotFoundError, type DbConnectorConfig, type DbTestFixtures, type DbWorkerFixtures, type PoolConfig, type RecordData, type ServerConnectionConfig, SqlLoader, type SqliteConnectionConfig, type WhereCriteria, expect, test };