@zerotal/testing 1.6.3 → 1.7.2
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.
- package/api-surface.md +360 -0
- package/package.json +7 -6
- package/src/TestApp.ts +15 -0
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.
|
|
3
|
+
"version": "1.7.2",
|
|
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.
|
|
35
|
-
"@zerotal/orm": "1.
|
|
36
|
-
"@zerotal/queue": "1.
|
|
37
|
-
"@zerotal/notifications": "1.
|
|
35
|
+
"@zerotal/core": "1.7.2",
|
|
36
|
+
"@zerotal/orm": "1.7.2",
|
|
37
|
+
"@zerotal/queue": "1.7.2",
|
|
38
|
+
"@zerotal/notifications": "1.7.2"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"typescript": "^5.8.0",
|
|
41
|
-
"@zerotal/session": "1.
|
|
42
|
+
"@zerotal/session": "1.7.2"
|
|
42
43
|
},
|
|
43
44
|
"description": "Testing utilities for Zerotal — an in-process test app, HTTP helpers, and database refresh.",
|
|
44
45
|
"keywords": [
|
package/src/TestApp.ts
CHANGED
|
@@ -603,6 +603,21 @@ export async function createTestApp(
|
|
|
603
603
|
// here, the fresh one after its own reset. Adopting before the reset is what is
|
|
604
604
|
// unsafe: it hands back an app whose scope has just been torn down, and the first
|
|
605
605
|
// facade to touch it throws E_FACADE_BEFORE_BOOT.
|
|
606
|
+
// Declare the runtime mode before anything boots.
|
|
607
|
+
//
|
|
608
|
+
// The suite runs under `zt test`, which is a console command, so `APP_TYPE`
|
|
609
|
+
// arrives as "console". Providers scoped to the test runtime are then filtered
|
|
610
|
+
// out before a single request is made — `SessionProvider` is `["web", "test"]`,
|
|
611
|
+
// so a test app had no session at all: flashed validation errors were
|
|
612
|
+
// unreadable and `assertInvalid()` reported "the response had none", blaming
|
|
613
|
+
// the app for a binding the harness had excluded. `actingAs()` failed for the
|
|
614
|
+
// same reason.
|
|
615
|
+
//
|
|
616
|
+
// Set rather than defaulted: calling `createTestApp` *is* the declaration that
|
|
617
|
+
// this process is a test, and the value it would otherwise inherit is the one
|
|
618
|
+
// that breaks it.
|
|
619
|
+
Bun.env["APP_TYPE"] = "test";
|
|
620
|
+
|
|
606
621
|
if (!setup && _sharedApps.size > 0) {
|
|
607
622
|
resetTestState();
|
|
608
623
|
const booted = await bootstrap();
|