@zerotal/testing 1.6.2 → 1.7.0

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.
Files changed (2) hide show
  1. package/api-surface.md +360 -0
  2. package/package.json +7 -6
package/api-surface.md ADDED
@@ -0,0 +1,360 @@
1
+ # @zerotal/testing — public API surface
2
+
3
+ <!-- AUTO-GENERATED by scripts/api-surface.ts. Do not edit by hand.
4
+ Run `bun run api:surface` to regenerate after an intentional API change. -->
5
+
6
+ ## . `(./src/index.ts)`
7
+
8
+ class EventFake = {
9
+ new (_app: Application, _original: Binding<unknown> | undefined): EventFake
10
+ static install: () => EventFake
11
+ assertEmitted: <T extends object>(EventType: EventClass<T>, filter?: (event: T) => boolean) => void
12
+ assertEmittedCount: <T extends object>(EventType: EventClass<T>, count: number) => void
13
+ assertNotEmitted: <T extends object>(EventType: EventClass<T>, filter?: (event: T) => boolean) => void
14
+ assertNothingEmitted: () => void
15
+ clear: () => void
16
+ dispatchQueuedListener: (listenerName: string, eventPayload: any) => Promise<void>
17
+ emit: <T extends object>(event: T) => Promise<void>
18
+ emitSync: <T extends object>(event: T) => Promise<void>
19
+ emitted: () => object[]
20
+ emittedOf: <T extends object>(EventType: EventClass<T>) => T[]
21
+ hasListeners: <T extends object>(eventClass: new (...args: unknown[]) => T) => boolean
22
+ off: <T extends object>(eventClass: new (...args: unknown[]) => T, listenerClass: new (...args: unknown[]) => { handle(event: T): Promise<void> | void; queue?: boolean | string; maxAttempts?: number; retryDelay?: number;}) => void
23
+ on: <T extends object>(eventClass: new (...args: unknown[]) => T, listenerClass: new (...args: unknown[]) => { handle(event: T): Promise<void> | void; queue?: boolean | string; maxAttempts?: number; retryDelay?: number;}) => void
24
+ registrations: () => Array<{ event: string; listeners: string[];}>
25
+ restore: () => void
26
+ setBroadcaster: (fn: ((event: object) => void) | null) => void
27
+ }
28
+
29
+ class Factory = {
30
+ new <T extends BaseModel>(_ModelClass: ModelCtor<T>, _definition: DefinitionFn<T>, _config: FactoryConfig<T>): Factory<T>
31
+ static define: <T extends BaseModel>(ModelClass: ModelCtor<T>, definition: DefinitionFn<T>) => Factory<T>
32
+ afterCreate: (callback: (instance: T) => Promise<void> | void) => Factory<T>
33
+ count: (n: number) => FactoryBatch<T>
34
+ create: (overrides?: Partial<InsertPayload<T>>) => Promise<T>
35
+ createMany: (n: number, overrides?: Partial<InsertPayload<T>>) => Promise<T[]>
36
+ dispatchEvents: () => Factory<T>
37
+ for: (model: BaseModel, foreignKey?: string) => Factory<T>
38
+ make: (overrides?: Partial<InsertPayload<T>>) => T
39
+ state: (stateName: string) => Factory<T>
40
+ }
41
+
42
+ class FactoryBatch = {
43
+ new <T extends BaseModel>(_ModelClass: ModelCtor<T>, _definition: DefinitionFn<T>, _config: FactoryConfig<T>, _n: number): FactoryBatch<T>
44
+ afterCreate: (callback: (instance: T) => Promise<void> | void) => FactoryBatch<T>
45
+ create: (overrides?: Partial<InsertPayload<T>>) => Promise<T[]>
46
+ dispatchEvents: () => FactoryBatch<T>
47
+ for: (model: BaseModel, foreignKey?: string) => FactoryBatch<T>
48
+ state: (stateName: string) => FactoryBatch<T>
49
+ }
50
+
51
+ class FakeDisk = {
52
+ new (baseUrl?: string): FakeDisk
53
+ append: (path: string, content: string | Uint8Array) => Promise<void>
54
+ assertContentType: (path: string, contentType: string) => FakeDisk
55
+ assertCount: (expected: number) => FakeDisk
56
+ assertExists: (path: string, contents?: string | Uint8Array) => FakeDisk
57
+ assertExistsMatching: (pattern: RegExp) => FakeDisk
58
+ assertMissing: (path: string) => FakeDisk
59
+ assertNothingStored: () => FakeDisk
60
+ clear: () => void
61
+ copy: (source: string, destination: string) => Promise<void>
62
+ count: number
63
+ delete: (path: string) => Promise<void>
64
+ exists: (path: string) => Promise<boolean>
65
+ file: (path: string) => FakeStoredFile | undefined
66
+ get: (path: string) => Promise<string | null>
67
+ getBuffer: (path: string) => Promise<Uint8Array | null>
68
+ lastModified: (path: string) => Promise<number | null>
69
+ move: (source: string, destination: string) => Promise<void>
70
+ paths: () => string[]
71
+ put: (path: string, content: string | Uint8Array | Blob, options?: PutOptions) => Promise<void>
72
+ size: (path: string) => Promise<number | null>
73
+ stream: (path: string) => Promise<Blob | null>
74
+ temporaryUrl: (path: string, expiresInSeconds: number) => Promise<string>
75
+ url: (path: string) => string
76
+ }
77
+
78
+ class Http = {
79
+ new (): Http
80
+ static assertNotSent: (predicate: (req: { method: string; url: string;}) => boolean) => void
81
+ static assertNothingSent: () => void
82
+ static assertSent: (predicate: (req: { method: string; url: string;}) => boolean) => void
83
+ static assertSentCount: (count: number) => void
84
+ static delete: (url: string) => PendingRequest
85
+ static fake: (stubs?: FakeStub[]) => void
86
+ static get: (url: string) => PendingRequest
87
+ static head: (url: string) => PendingRequest
88
+ static patch: (url: string, data?: unknown) => PendingRequest
89
+ static post: (url: string, data?: unknown) => PendingRequest
90
+ static put: (url: string, data?: unknown) => PendingRequest
91
+ static recorded: () => Array<{ method: string; url: string; options: RequestInit;}>
92
+ static resetFakes: () => void
93
+ static retry: (times: number, delay?: number) => _Builder
94
+ static timeout: (milliseconds: number) => _Builder
95
+ static withBasicAuth: (username: string, password: string) => _Builder
96
+ static withHeaders: (headers: Record<string, string>) => _Builder
97
+ static withToken: (token: string) => _Builder
98
+ }
99
+
100
+ class NotificationFake = {
101
+ new (app: Application, original: Binding): NotificationFake
102
+ static install: () => NotificationFake
103
+ assertNotSentTo: <T extends Notification>(notifiable: Notifiable, NotificationClass: new (...args: never[]) => T) => void
104
+ assertNothingSent: () => void
105
+ assertQueued: <T extends Notification>(notifiable: Notifiable, NotificationClass: new (...args: never[]) => T) => void
106
+ assertSentCount: (count: number) => void
107
+ assertSentOn: <T extends Notification>(notifiable: Notifiable, NotificationClass: new (...args: never[]) => T, channel: string) => void
108
+ assertSentTimes: <T extends Notification>(NotificationClass: new (...args: never[]) => T, count: number) => void
109
+ assertSentTo: <T extends Notification>(notifiable: Notifiable, NotificationClass: new (...args: never[]) => T, callback?: (notification: T) => boolean) => void
110
+ channels: () => string[]
111
+ database: { all(): Promise<NotificationRecord[]>; unread(): Promise<NotificationRecord[]>; unreadCount(): Promise<number>; markAllAsRead(): Promise<void>; markAsRead(): Promise<void>; markAsUnread(): Promise<void>; delete(): Promise<void>; clear(): Promise<void>;}
112
+ extend: () => NotificationFake
113
+ queue: (notifiable: Notifiable, notification: Notification) => Promise<void>
114
+ queueMany: (notifiables: Iterable<Notifiable>, notification: Notification) => Promise<void>
115
+ restore: () => void
116
+ route: (routes: OnDemandRoutes) => { notify(notification: Notification): Promise<void>; notifyLater(notification: Notification): Promise<void>;}
117
+ send: (notifiable: Notifiable, notification: Notification) => Promise<void>
118
+ sendMany: (notifiables: Iterable<Notifiable>, notification: Notification) => Promise<void>
119
+ sent: () => CapturedNotification[]
120
+ sentTo: (notifiable: Notifiable) => CapturedNotification[]
121
+ }
122
+
123
+ class QueueFake = {
124
+ new (app: Application, original: Binding): QueueFake
125
+ static install: () => QueueFake
126
+ assertDispatched: <T extends Job>(JobClass: new (...args: never[]) => T, callback?: (job: T) => boolean) => void
127
+ assertDispatchedCount: (count: number) => void
128
+ assertNotDispatched: <T extends Job>(JobClass: new (...args: never[]) => T) => void
129
+ assertNothingDispatched: () => void
130
+ dispatch: (job: Job) => Promise<void>
131
+ dispatched: () => Job[]
132
+ drain: () => Promise<void>
133
+ flush: () => Promise<void>
134
+ isShuttingDown: boolean
135
+ processNext: () => Promise<boolean>
136
+ restore: () => void
137
+ size: () => Promise<number>
138
+ }
139
+
140
+ class TestApp = {
141
+ new (_app: Application, _errors?: TestExceptionHandler | undefined): TestApp
142
+ _shared: boolean
143
+ actingAs: (user: { id: number | string;}) => TestApp
144
+ actingAsGuest: () => TestApp
145
+ app: Application
146
+ asJson: () => TestApp
147
+ baseUrl: string
148
+ close: () => Promise<void>
149
+ delete: (url: string, headers?: Record<string, string>) => Promise<TestResponse>
150
+ followingRedirects: () => TestApp
151
+ get: (url: string, headers?: Record<string, string>) => Promise<TestResponse>
152
+ head: (url: string, headers?: Record<string, string>) => Promise<TestResponse>
153
+ multipart: (url: string, body?: Record<string, TestFormValue>, headers?: Record<string, string>, method?: 'POST' | 'PUT' | 'PATCH') => Promise<TestResponse>
154
+ options: (url: string, headers?: Record<string, string>) => Promise<TestResponse>
155
+ patch: (url: string, body: unknown, headers?: Record<string, string>) => Promise<TestResponse>
156
+ patchForm: (url: string, body?: Record<string, TestFormValue>, headers?: Record<string, string>) => Promise<TestResponse>
157
+ port: number
158
+ post: (url: string, body: unknown, headers?: Record<string, string>) => Promise<TestResponse>
159
+ postForm: (url: string, body?: Record<string, TestFormValue>, headers?: Record<string, string>) => Promise<TestResponse>
160
+ put: (url: string, body: unknown, headers?: Record<string, string>) => Promise<TestResponse>
161
+ putForm: (url: string, body?: Record<string, TestFormValue>, headers?: Record<string, string>) => Promise<TestResponse>
162
+ request: (url: string, init?: RequestInit) => Promise<TestResponse>
163
+ withCookie: (name: string, value: string) => TestApp
164
+ withCookies: (cookies: Record<string, string>) => TestApp
165
+ withExceptionHandling: () => TestApp
166
+ withHeaders: (headers: Record<string, string>) => TestApp
167
+ withSession: (data: Record<string, unknown>) => TestApp
168
+ withoutCookies: () => TestApp
169
+ withoutExceptionHandling: () => TestApp
170
+ withoutFollowingRedirects: () => TestApp
171
+ }
172
+
173
+ class TestResponse = {
174
+ new (_res: Response, _body: string, context?: TestResponseContext & { decodedSession?: Record<string, unknown> | null | undefined; }): TestResponse
175
+ static of: (res: Response, context?: TestResponseContext) => Promise<TestResponse>
176
+ assertAuthenticated: () => TestResponse
177
+ assertAuthenticatedAs: (user: { id: number | string;} | number | string) => TestResponse
178
+ assertBodyContains: (needle: string) => TestResponse
179
+ assertCookie: (name: string, value?: string) => TestResponse
180
+ assertCookieMissing: (name: string) => TestResponse
181
+ assertCreated: () => TestResponse
182
+ assertDontSee: (needle: string) => TestResponse
183
+ assertDontSeeText: (needle: string) => TestResponse
184
+ assertForbidden: () => TestResponse
185
+ assertGuest: () => TestResponse
186
+ assertHeader: (name: string, value?: string) => TestResponse
187
+ assertHeaderMissing: (name: string) => TestResponse
188
+ assertInertia: (component?: string, props?: Record<string, unknown>) => TestResponse
189
+ assertInertiaProp: (key: string, value?: unknown) => TestResponse
190
+ assertInvalid: (fields?: string | string[] | Record<string, string>) => TestResponse
191
+ assertJson: (expected: Record<string, unknown>) => TestResponse
192
+ assertJsonCount: (count: number, key?: string) => TestResponse
193
+ assertJsonPath: (path: string, expected: unknown) => TestResponse
194
+ assertMovedPermanently: () => TestResponse
195
+ assertNoContent: () => TestResponse
196
+ assertNotFound: () => TestResponse
197
+ assertOk: () => TestResponse
198
+ assertRedirect: (url: string) => TestResponse
199
+ assertSee: (needle: string) => TestResponse
200
+ assertSeeText: (needle: string) => TestResponse
201
+ assertServerError: () => TestResponse
202
+ assertSessionHas: (key: string, value?: unknown) => TestResponse
203
+ assertSessionHasErrors: (fields?: string | string[]) => TestResponse
204
+ assertSessionHasNoErrors: () => TestResponse
205
+ assertSessionMissing: (key: string) => TestResponse
206
+ assertStatus: (expected: number) => TestResponse
207
+ assertSuccessful: () => TestResponse
208
+ assertUnauthorized: () => TestResponse
209
+ assertUnprocessable: () => TestResponse
210
+ assertValid: (fields?: string | string[]) => TestResponse
211
+ exception: () => unknown
212
+ headers: Headers
213
+ inertia: () => InertiaPage | null
214
+ json: <T = unknown>() => T
215
+ ok: boolean
216
+ session: () => Record<string, unknown> | null
217
+ status: number
218
+ text: () => string
219
+ validationErrors: () => Record<string, string[]> | null
220
+ }
221
+
222
+ const fake = { pick<T>(arr: readonly T[]): T; shuffle<T>(arr: readonly T[]): T[]; sample<T>(arr: readonly T[], n: number): T[]; number(min?: number, max?: number): number; float(min?: number, max?: number, decimals?: number): number; boolean(trueWeight?: number): boolean; uuid(): string; string(length?: number): string; maybe<T>(value: T, probability?: number): T | null; date(from?: Date, to?: Date): Date; pastDate(years?: number): Date; futureDate(years?: number): Date; isoDate(from?: Date, to?: Date): string; timestamp(): number; firstName(): string; lastName(): string; name(): string; email(opts?: { name?: string; corporate?: boolean; number?: boolean; }): string; phone(): string; city(): string; province(): string; suburb(): string; streetAddress(): string; postalCode(): string; address(): string; company(): string; jobTitle(): string; department(): string; word(): string; words(n?: number): string; sentence(opts?: { length?: TextLength; words?: number; }): string; sentences(n?: number, opts?: { length?: TextLength; }): string; paragraph(opts?: { length?: TextLength; }): string; paragraphs(n?: number, opts?: { length?: TextLength; }): string; title(): string; slug(text?: string): string; url(opts?: { path?: boolean; }): string; password(opts?: { length?: number; simple?: boolean; }): string;}
223
+
224
+ const fakeFile = { create(name: string, content?: string | Uint8Array, type?: string): File; sized(name: string, size: number, type?: string): File; image(name?: string, options?: { width?: number; height?: number; }): File; jpeg(name?: string): File; gif(name?: string): File; pdf(name?: string): File;}
225
+
226
+ function assertDatabaseCount = (table: string, expected: number, where?: Record<string, unknown>) => Promise<void>
227
+
228
+ function assertDatabaseHas = (table: string, where: Record<string, unknown>) => Promise<void>
229
+
230
+ function assertDatabaseMissing = (table: string, where: Record<string, unknown>) => Promise<void>
231
+
232
+ function assertMissingFile = (diskOrDriver: string | StorageDriver, path: string) => Promise<void>
233
+
234
+ function assertStoredFile = (diskOrDriver: string | StorageDriver, path: string) => Promise<void>
235
+
236
+ function closeSharedTestApps = () => Promise<void>
237
+
238
+ function createTestApp = (bootstrap: () => Application | Promise<Application>, setup?: () => void) => Promise<TestApp>
239
+
240
+ function migrateDatabase = (options?: MigrateDatabaseOptions) => Promise<string[]>
241
+
242
+ function refreshDatabase = (options?: RefreshDatabaseOptions) => void
243
+
244
+ function resetTestState = () => void
245
+
246
+ function withDatabase = (fn: () => Promise<void>) => () => Promise<void>
247
+
248
+ interface FakeStoredFile = {
249
+ contentType: string | undefined
250
+ contents: Uint8Array<ArrayBufferLike>
251
+ lastModified: number
252
+ path: string
253
+ visibility: 'public' | 'private' | undefined
254
+ }
255
+
256
+ interface InertiaPage = {
257
+ component: string
258
+ props: Record<string, unknown>
259
+ url: string
260
+ version: string | null
261
+ }
262
+
263
+ interface MigrateDatabaseOptions = {
264
+ connection?: SQLInstance
265
+ path?: string
266
+ table?: string
267
+ }
268
+
269
+ interface RefreshDatabaseOptions = {
270
+ connection?: SQLInstance
271
+ migrate?: string | boolean
272
+ setup?: (db: SQLInstance) => void | Promise<void>
273
+ teardown?: (db: SQLInstance) => void | Promise<void>
274
+ }
275
+
276
+ interface TestFileInput = {
277
+ content: string | Uint8Array<ArrayBufferLike> | Blob | File
278
+ filename?: string
279
+ type?: string
280
+ }
281
+
282
+ interface TestResponseContext = {
283
+ exception?: unknown
284
+ session?: SessionDecoder | undefined
285
+ }
286
+
287
+ type FactoryPayload = Omit<InsertPayload<T>, FKKeys<T> & Exclude<{ [K in keyof T]-?: (<G>() => G extends { [P in K]: T[P]; } ? 1 : 2) extends <G>() => G extends { -readonly [P in K]: T[P]; } ? 1 : 2 ? K : never; }[keyof T] & { [K in keyof T & string]-?: K extends `_${string}` ? never : T[K] extends (...args: any[]) => any ? never : NonNullable<T[K]> extends { readonly __isZerotalModel: true;} | { readonly __isZerotalModel: true;}[] ? never : K; }[keyof T & string], 'id' | 'createdAt' | 'updatedAt' | 'deletedAt'>> & Partial<Pick<InsertPayload<T>, FKKeys<T> & Exclude<{ [K in keyof T]-?: (<G>() => G extends { [P in K]: T[P]; } ? 1 : 2) extends <G>() => G extends { -readonly [P in K]: T[P]; } ? 1 : 2 ? K : never; }[keyof T] & { [K in keyof T & string]-?: K extends `_${string}` ? never : T[K] extends (...args: any[]) => any ? never : NonNullable<T[K]> extends { readonly __isZerotalModel: true;} | { readonly __isZerotalModel: true;}[] ? never : K; }[keyof T & string], 'id' | 'createdAt' | 'updatedAt' | 'deletedAt'>>>
288
+
289
+ type FakeFile = { create(name: string, content?: string | Uint8Array, type?: string): File; sized(name: string, size: number, type?: string): File; image(name?: string, options?: { width?: number; height?: number; }): File; jpeg(name?: string): File; gif(name?: string): File; pdf(name?: string): File;}
290
+
291
+ type SessionDecoder = (response: Response) => Promise<Record<string, unknown> | null>
292
+
293
+ type TestFormValue = string | number | boolean | Blob | File | TestFileInput | null | undefined
294
+
295
+ ## ./browser `(./src/browser.ts)`
296
+
297
+ class BrowserPage = {
298
+ new (view: Bun.WebView, report: TransportReport, timeout: number): BrowserPage
299
+ attribute: (selector: string, name: string) => Promise<string | null>
300
+ click: (selector: string) => Promise<BrowserPage>
301
+ close: () => void
302
+ connection: () => Promise<string | null>
303
+ count: (selector: string) => Promise<number>
304
+ evaluate: <T = unknown>(expression: string) => Promise<T>
305
+ html: (selector: string) => Promise<string | null>
306
+ press: (key: string) => Promise<BrowserPage>
307
+ socketUpgraded: () => boolean
308
+ text: (selector: string) => Promise<string | null>
309
+ transport: () => TransportReport
310
+ type: (selector: string, text: string) => Promise<BrowserPage>
311
+ url: () => Promise<string>
312
+ waitFor: (expression: string, timeout?: number) => Promise<BrowserPage>
313
+ waitForConnection: (timeout?: number) => Promise<BrowserPage>
314
+ waitForCount: (selector: string, n: number, timeout?: number) => Promise<BrowserPage>
315
+ waitForPatch: (timeout?: number) => Promise<BrowserPage>
316
+ }
317
+
318
+ class FlowBrowser = {
319
+ new (app: TestApp, timeout: number): FlowBrowser
320
+ static availability: () => Promise<BrowserAvailability>
321
+ static serve: (bootstrap: () => Application | Promise<Application>, options?: FlowBrowserOptions) => Promise<FlowBrowser>
322
+ port: number
323
+ stop: () => Promise<void>
324
+ url: string
325
+ visit: (path: string) => Promise<BrowserPage>
326
+ }
327
+
328
+ const CDP_URL_ENV = 'ZT_BROWSER_CDP_URL'
329
+
330
+ function browserAvailability = () => Promise<BrowserAvailability>
331
+
332
+ function browserRequired = () => boolean
333
+
334
+ interface BrowserAvailability = {
335
+ available: boolean
336
+ mode: 'none' | 'spawn' | 'connect'
337
+ reason: string
338
+ }
339
+
340
+ interface FlowBrowserOptions = {
341
+ setup?: () => void
342
+ timeout?: number
343
+ }
344
+
345
+ interface ObservedFrame = {
346
+ direction: 'sent' | 'received'
347
+ payload: string
348
+ }
349
+
350
+ interface TransportReport = {
351
+ created: number
352
+ errored: boolean
353
+ frames: ObservedFrame[]
354
+ statuses: number[]
355
+ upgraded: number
356
+ }
357
+
358
+ ## ./preload `(./src/preload.ts)`
359
+
360
+ _(no exports)_
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zerotal/testing",
3
- "version": "1.6.2",
3
+ "version": "1.7.0",
4
4
  "license": "MIT",
5
5
  "maturity": "stable",
6
6
  "private": false,
@@ -14,6 +14,7 @@
14
14
  },
15
15
  "files": [
16
16
  "CHANGELOG.md",
17
+ "api-surface.md",
17
18
  "src",
18
19
  "!src/**/*.test.ts",
19
20
  "!src/**/*.test.tsx",
@@ -31,14 +32,14 @@
31
32
  "typecheck": "tsc --noEmit"
32
33
  },
33
34
  "dependencies": {
34
- "@zerotal/core": "1.6.2",
35
- "@zerotal/orm": "1.6.2",
36
- "@zerotal/queue": "1.6.2",
37
- "@zerotal/notifications": "1.6.2"
35
+ "@zerotal/core": "1.7.0",
36
+ "@zerotal/orm": "1.7.0",
37
+ "@zerotal/queue": "1.7.0",
38
+ "@zerotal/notifications": "1.7.0"
38
39
  },
39
40
  "devDependencies": {
40
41
  "typescript": "^5.8.0",
41
- "@zerotal/session": "1.6.2"
42
+ "@zerotal/session": "1.7.0"
42
43
  },
43
44
  "description": "Testing utilities for Zerotal — an in-process test app, HTTP helpers, and database refresh.",
44
45
  "keywords": [