@chidchanun/bcp 0.2.12 → 0.2.14

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,274 @@
1
+ import {
2
+ executePageActionFunction,
3
+ normalizePageActionMethod,
4
+ type PageAction,
5
+ type PageActionExecution,
6
+ type PageActionMethod,
7
+ } from "./form-action.js";
8
+ import {
9
+ executePageGuardFunctions,
10
+ type PageGuard,
11
+ type PageGuardData,
12
+ type PageGuardDefinition,
13
+ type PageGuardExecution,
14
+ } from "./page-guard.js";
15
+ import {
16
+ executePageLoaderFunction,
17
+ type PageLoader,
18
+ type PageLoaderExecution,
19
+ } from "./page-loader.js";
20
+
21
+ export interface TestPageContextOptions {
22
+ params?: Record<string, any>;
23
+ url?: string | URL;
24
+ guardData?: PageGuardData;
25
+ hasGuard?: boolean;
26
+ }
27
+
28
+ export interface TestPageGuardOptions
29
+ extends Omit<
30
+ TestPageContextOptions,
31
+ "guardData" | "hasGuard"
32
+ > {
33
+ label?: string;
34
+ }
35
+
36
+ export interface TestPageActionOptions
37
+ extends TestPageContextOptions {
38
+ actionName?: string;
39
+ method?: PageActionMethod | string;
40
+ formData?: FormData;
41
+ fields?: Record<
42
+ string,
43
+ string | Blob | Array<string | Blob>
44
+ >;
45
+ label?: string;
46
+ }
47
+
48
+ export async function runTestPageLoader(
49
+ loader: PageLoader,
50
+ options: TestPageContextOptions = {}
51
+ ): Promise<PageLoaderExecution> {
52
+ assertFunction(
53
+ loader,
54
+ "page loader"
55
+ );
56
+
57
+ return executePageLoaderFunction(
58
+ loader,
59
+ {
60
+ ...(options.params ?? {}),
61
+ },
62
+ resolveTestPageUrl(
63
+ options.url
64
+ ),
65
+ "bcp/testing loader",
66
+ {
67
+ ...(options.guardData ?? {}),
68
+ },
69
+ options.hasGuard ??
70
+ Object.keys(
71
+ options.guardData ?? {}
72
+ ).length > 0
73
+ );
74
+ }
75
+
76
+ export async function runTestPageGuards(
77
+ guards:
78
+ | PageGuard
79
+ | PageGuard[]
80
+ | PageGuardDefinition[],
81
+ options: TestPageGuardOptions = {}
82
+ ): Promise<PageGuardExecution> {
83
+ const list =
84
+ Array.isArray(guards)
85
+ ? guards
86
+ : [guards];
87
+ const definitions:
88
+ PageGuardDefinition[] =
89
+ list.map(
90
+ (candidate, index) => {
91
+ if (
92
+ typeof candidate === "object" &&
93
+ candidate !== null &&
94
+ "guard" in candidate
95
+ ) {
96
+ const definition =
97
+ candidate as PageGuardDefinition;
98
+ assertFunction(
99
+ definition.guard,
100
+ `page guard ${index + 1}`
101
+ );
102
+ return {
103
+ filePath:
104
+ definition.filePath ||
105
+ `${options.label ?? "bcp/testing guard"}#${index + 1}`,
106
+ guard:
107
+ definition.guard,
108
+ };
109
+ }
110
+
111
+ assertFunction(
112
+ candidate,
113
+ `page guard ${index + 1}`
114
+ );
115
+
116
+ return {
117
+ filePath:
118
+ `${options.label ?? "bcp/testing guard"}#${index + 1}`,
119
+ guard:
120
+ candidate as PageGuard,
121
+ };
122
+ }
123
+ );
124
+
125
+ return executePageGuardFunctions(
126
+ definitions,
127
+ {
128
+ ...(options.params ?? {}),
129
+ },
130
+ resolveTestPageUrl(
131
+ options.url
132
+ )
133
+ );
134
+ }
135
+
136
+ export async function runTestPageAction(
137
+ action: PageAction,
138
+ options: TestPageActionOptions = {}
139
+ ): Promise<PageActionExecution> {
140
+ assertFunction(
141
+ action,
142
+ "page action"
143
+ );
144
+
145
+ if (
146
+ options.formData &&
147
+ options.fields
148
+ ) {
149
+ throw new TypeError(
150
+ "BCP Testing: page action test cannot specify both formData and fields."
151
+ );
152
+ }
153
+
154
+ const actionName =
155
+ normalizeText(
156
+ options.actionName ?? "action",
157
+ "page action name"
158
+ );
159
+ const method =
160
+ normalizePageActionMethod(
161
+ options.method ?? "POST"
162
+ );
163
+ const formData =
164
+ options.formData ??
165
+ createTestFormData(
166
+ options.fields ?? {}
167
+ );
168
+
169
+ return executePageActionFunction(
170
+ action,
171
+ actionName,
172
+ method,
173
+ formData,
174
+ {
175
+ ...(options.params ?? {}),
176
+ },
177
+ resolveTestPageUrl(
178
+ options.url
179
+ ),
180
+ {
181
+ ...(options.guardData ?? {}),
182
+ },
183
+ options.hasGuard ??
184
+ Object.keys(
185
+ options.guardData ?? {}
186
+ ).length > 0,
187
+ options.label ??
188
+ "bcp/testing actions"
189
+ );
190
+ }
191
+
192
+ export function createTestFormData(
193
+ fields: Record<
194
+ string,
195
+ string | Blob | Array<string | Blob>
196
+ > = {}
197
+ ): FormData {
198
+ const formData =
199
+ new FormData();
200
+
201
+ for (
202
+ const [name, rawValue]
203
+ of Object.entries(fields)
204
+ ) {
205
+ const values =
206
+ Array.isArray(rawValue)
207
+ ? rawValue
208
+ : [rawValue];
209
+
210
+ for (const value of values) {
211
+ formData.append(
212
+ name,
213
+ value
214
+ );
215
+ }
216
+ }
217
+
218
+ return formData;
219
+ }
220
+
221
+ function resolveTestPageUrl(
222
+ value?: string | URL
223
+ ): URL {
224
+ if (value instanceof URL) {
225
+ return new URL(
226
+ value.href
227
+ );
228
+ }
229
+
230
+ const candidate =
231
+ value ??
232
+ "http://bcp.test/";
233
+
234
+ try {
235
+ return new URL(
236
+ candidate
237
+ );
238
+ } catch {
239
+ return new URL(
240
+ String(candidate).replace(
241
+ /^\//,
242
+ ""
243
+ ),
244
+ "http://bcp.test/"
245
+ );
246
+ }
247
+ }
248
+
249
+ function assertFunction(
250
+ value: unknown,
251
+ label: string
252
+ ): asserts value is (...args: any[]) => unknown {
253
+ if (typeof value !== "function") {
254
+ throw new TypeError(
255
+ `BCP Testing: ${label} must be a function.`
256
+ );
257
+ }
258
+ }
259
+
260
+ function normalizeText(
261
+ value: unknown,
262
+ label: string
263
+ ): string {
264
+ const text =
265
+ String(value ?? "").trim();
266
+
267
+ if (!text) {
268
+ throw new TypeError(
269
+ `BCP Testing: ${label} must be a non-empty string.`
270
+ );
271
+ }
272
+
273
+ return text;
274
+ }