@vickbk/tests 0.0.0-stage → 0.1.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.
- package/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +192 -3
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1 -0
- package/dist/pattern-to-regex-BrMJtzu8.cjs +19 -0
- package/dist/pattern-to-regex-BrMJtzu8.cjs.map +1 -0
- package/dist/pattern-to-regex-CO5Y0oDU.mjs +14 -0
- package/dist/pattern-to-regex-CO5Y0oDU.mjs.map +1 -0
- package/dist/playwright.cjs +327 -0
- package/dist/playwright.cjs.map +1 -0
- package/dist/playwright.d.cts +254 -0
- package/dist/playwright.d.cts.map +1 -0
- package/dist/playwright.d.mts +254 -0
- package/dist/playwright.d.mts.map +1 -0
- package/dist/playwright.mjs +308 -0
- package/dist/playwright.mjs.map +1 -0
- package/dist/react.cjs +173 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +17 -0
- package/dist/react.d.cts.map +1 -0
- package/dist/react.d.mts +17 -0
- package/dist/react.d.mts.map +1 -0
- package/dist/react.mjs +170 -0
- package/dist/react.mjs.map +1 -0
- package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
- package/dist/types-BlSFP07Z.d.cts +8 -0
- package/dist/types-BlSFP07Z.d.cts.map +1 -0
- package/dist/types-BlSFP07Z.d.mts +8 -0
- package/dist/types-BlSFP07Z.d.mts.map +1 -0
- package/dist/vitest.d.mts +305 -0
- package/dist/vitest.d.mts.map +1 -0
- package/dist/vitest.mjs +534 -0
- package/dist/vitest.mjs.map +1 -0
- package/package.json +143 -3
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { t as patternToRegex } from "./pattern-to-regex-CO5Y0oDU.mjs";
|
|
2
|
+
import test, { expect } from "@playwright/test";
|
|
3
|
+
//#region src/features/playwright/modules/assertions/utils/isChecked.ts
|
|
4
|
+
/**
|
|
5
|
+
* Asserts that the element selected by the CSS selector is checked.
|
|
6
|
+
*
|
|
7
|
+
* @param page The Playwright page to search.
|
|
8
|
+
* @param selector CSS selector for a checkbox or radio input.
|
|
9
|
+
* @returns The checked locator.
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const checkbox = await isChecked(page, "#terms");
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
async function isChecked(page, selector) {
|
|
16
|
+
const locator = page.locator(selector);
|
|
17
|
+
await expect(locator).toBeChecked();
|
|
18
|
+
return locator;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/features/playwright/modules/assertions/utils/isNotChecked.ts
|
|
22
|
+
/**
|
|
23
|
+
* Asserts that the element selected by the CSS selector is not checked.
|
|
24
|
+
*
|
|
25
|
+
* @param page The Playwright page to search.
|
|
26
|
+
* @param selector CSS selector for a checkbox or radio input.
|
|
27
|
+
* @returns The unchecked locator.
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const checkbox = await isNotChecked(page, "#terms");
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
async function isNotChecked(page, selector) {
|
|
34
|
+
const locator = page.locator(selector);
|
|
35
|
+
await expect(locator).not.toBeChecked();
|
|
36
|
+
return locator;
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/features/playwright/modules/assertions/utils/getMatcher.ts
|
|
40
|
+
function getMatcher(page, ...textes) {
|
|
41
|
+
return textes.map((text) => {
|
|
42
|
+
if (Array.isArray(text)) {
|
|
43
|
+
const [matcher, nth] = text;
|
|
44
|
+
return expect(page.getByText(patternToRegex(matcher)).nth(nth));
|
|
45
|
+
}
|
|
46
|
+
return expect(page.getByText(text));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/features/playwright/modules/assertions/utils/shouldNotSee.ts
|
|
51
|
+
/**
|
|
52
|
+
* Asserts that each text matcher is not visible on the page.
|
|
53
|
+
*
|
|
54
|
+
* @param page The Playwright page to inspect.
|
|
55
|
+
* @param textes Text strings, regular expressions, or `[matcher, occurrenceIndex]` pairs.
|
|
56
|
+
* @returns A promise that resolves when every matching element is not visible.
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* await shouldNotSee(page, "Error message");
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
async function shouldNotSee(page, ...textes) {
|
|
63
|
+
const matchers = getMatcher(page, ...textes);
|
|
64
|
+
for (let i = 0; i < matchers.length; i++) await matchers[i].not.toBeVisible();
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/features/playwright/modules/assertions/utils/shouldSee.ts
|
|
68
|
+
/**
|
|
69
|
+
* Asserts that each text matcher is visible on the page.
|
|
70
|
+
*
|
|
71
|
+
* @param page The Playwright page to inspect.
|
|
72
|
+
* @param textes Text strings, regular expressions, or `[matcher, occurrenceIndex]` pairs.
|
|
73
|
+
* @returns A promise that resolves when every matching element is visible.
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* await shouldSee(page, "Welcome", [/Saved/i, 0]);
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
async function shouldSee(page, ...textes) {
|
|
80
|
+
const matchers = getMatcher(page, ...textes);
|
|
81
|
+
for (let i = 0; i < matchers.length; i++) await matchers[i].toBeVisible();
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/features/playwright/modules/interactions/utils/clickBodyCorner.ts
|
|
85
|
+
/**
|
|
86
|
+
* Clicks the top-left corner of the page body.
|
|
87
|
+
*
|
|
88
|
+
* @param page The Playwright page to interact with.
|
|
89
|
+
* @returns A promise that resolves after the click.
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* await clickBodyCorner(page);
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
async function clickBodyCorner(page) {
|
|
96
|
+
await page.locator("body").click({ position: {
|
|
97
|
+
x: 0,
|
|
98
|
+
y: 0
|
|
99
|
+
} });
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/features/playwright/shared/locators/utils/getLocatorByText.ts
|
|
103
|
+
/**
|
|
104
|
+
* Locates elements matching a selector and text pattern.
|
|
105
|
+
*
|
|
106
|
+
* @param page The Playwright page to search.
|
|
107
|
+
* @param locator Selector paired with text that the element must contain.
|
|
108
|
+
* @returns A locator matching the selector and text.
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* const save = getLocatorByText(page, ["button", "Save"]);
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
function getLocatorByText(page, [locator, hasText]) {
|
|
115
|
+
return page.locator(locator, { hasText });
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/features/playwright/shared/locators/utils/getButton.ts
|
|
119
|
+
/**
|
|
120
|
+
* Locates a button by its contained text.
|
|
121
|
+
*
|
|
122
|
+
* @param page The Playwright page to search.
|
|
123
|
+
* @param hasText Text or regular expression contained by the button.
|
|
124
|
+
* @returns A locator for the matching button.
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* const saveButton = getButton(page, "Save");
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
function getButton(page, hasText) {
|
|
131
|
+
return getLocatorByText(page, ["button", hasText]);
|
|
132
|
+
}
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/features/playwright/shared/locators/utils/getLabel.ts
|
|
135
|
+
/**
|
|
136
|
+
* Locates a label by its contained text.
|
|
137
|
+
*
|
|
138
|
+
* @param page The Playwright page to search.
|
|
139
|
+
* @param hasText Text or regular expression contained by the label.
|
|
140
|
+
* @returns A locator for the matching label.
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* const emailLabel = getLabel(page, "Email");
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
function getLabel(page, hasText) {
|
|
147
|
+
return getLocatorByText(page, ["label", hasText]);
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region src/features/playwright/shared/locators/utils/getLink.ts
|
|
151
|
+
/**
|
|
152
|
+
* Locates a link by its accessible name.
|
|
153
|
+
*
|
|
154
|
+
* @param page The Playwright page to search.
|
|
155
|
+
* @param hasText Text or regular expression matching the link's accessible name.
|
|
156
|
+
* @returns A locator for the matching link.
|
|
157
|
+
* @example
|
|
158
|
+
* ```ts
|
|
159
|
+
* const documentationLink = getLink(page, "Documentation");
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
function getLink(page, hasText) {
|
|
163
|
+
return page.getByRole("link", { name: hasText });
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/features/playwright/modules/interactions/utils/clickButton.ts
|
|
167
|
+
/**
|
|
168
|
+
* Clicks a button matching the supplied text.
|
|
169
|
+
*
|
|
170
|
+
* @param page The Playwright page to search.
|
|
171
|
+
* @param hasText Text or regular expression contained by the button.
|
|
172
|
+
* @returns A promise that resolves after the button is clicked.
|
|
173
|
+
* @example
|
|
174
|
+
* ```ts
|
|
175
|
+
* await clickButton(page, "Save");
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
async function clickButton(page, hasText) {
|
|
179
|
+
await getButton(page, hasText).click();
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/features/playwright/modules/interactions/utils/clickLabelInput.ts
|
|
183
|
+
/**
|
|
184
|
+
* Clicks the label matching the supplied text.
|
|
185
|
+
*
|
|
186
|
+
* @param page The Playwright page to search.
|
|
187
|
+
* @param labelText Text or regular expression contained by the label.
|
|
188
|
+
* @returns A promise that resolves after the label is clicked.
|
|
189
|
+
* @example
|
|
190
|
+
* ```ts
|
|
191
|
+
* await clickLabelInput(page, "Subscribe");
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
async function clickLabelInput(page, labelText) {
|
|
195
|
+
await getLabel(page, labelText).click();
|
|
196
|
+
}
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/features/playwright/modules/interactions/utils/clickLink.ts
|
|
199
|
+
/**
|
|
200
|
+
* Clicks a link matching the supplied accessible name.
|
|
201
|
+
*
|
|
202
|
+
* @param page The Playwright page to search.
|
|
203
|
+
* @param hasText Text or regular expression matching the link name.
|
|
204
|
+
* @returns A promise that resolves after the link is clicked.
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* await clickLink(page, "Documentation");
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
async function clickLink(page, hasText) {
|
|
211
|
+
await getLink(page, hasText).click();
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region src/features/playwright/modules/interactions/utils/clickMultipleLabelInputs.ts
|
|
215
|
+
/**
|
|
216
|
+
* Clicks each matching label in the supplied order.
|
|
217
|
+
*
|
|
218
|
+
* @param page The Playwright page to search.
|
|
219
|
+
* @param labelTexts Text or regular expressions for the labels to click.
|
|
220
|
+
* @returns A promise that resolves after all labels are clicked.
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* await clickMultipleLabelInputs(page, ["Email", "Subscribe"]);
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
async function clickMultipleLabelInputs(page, labelTexts) {
|
|
227
|
+
for (const labelText of labelTexts) await clickLabelInput(page, labelText);
|
|
228
|
+
}
|
|
229
|
+
//#endregion
|
|
230
|
+
//#region src/features/playwright/modules/interactions/utils/fillLocatorWith.ts
|
|
231
|
+
/**
|
|
232
|
+
* Clicks a locator and fills it with the supplied value.
|
|
233
|
+
*
|
|
234
|
+
* @param locator The input-like locator to fill.
|
|
235
|
+
* @param value The value to enter.
|
|
236
|
+
* @returns A promise that resolves after the locator is filled.
|
|
237
|
+
* @example
|
|
238
|
+
* ```ts
|
|
239
|
+
* await fillLocatorWith(page.getByLabel("Email"), "user@example.com");
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
async function fillLocatorWith(locator, value) {
|
|
243
|
+
await locator.click();
|
|
244
|
+
await locator.fill(value);
|
|
245
|
+
}
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/features/playwright/modules/interactions/utils/setLocatorValue.ts
|
|
248
|
+
/**
|
|
249
|
+
* Clicks the label matching the pattern and fills it with the supplied value.
|
|
250
|
+
*
|
|
251
|
+
* @param page The Playwright page to search.
|
|
252
|
+
* @param locatorAndValue Label text or pattern paired with the value to fill.
|
|
253
|
+
* @returns A promise that resolves after the matching label is filled.
|
|
254
|
+
* @example
|
|
255
|
+
* ```ts
|
|
256
|
+
* await setLocatorValue(page, ["Email", "user@example.com"]);
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
async function setLocatorValue(page, [locator, value]) {
|
|
260
|
+
const element = page.locator("label", { hasText: patternToRegex(locator) });
|
|
261
|
+
await element.click();
|
|
262
|
+
await element.fill(value);
|
|
263
|
+
}
|
|
264
|
+
//#endregion
|
|
265
|
+
//#region src/features/playwright/modules/interactions/utils/setValueForLocators.ts
|
|
266
|
+
/**
|
|
267
|
+
* Sets values for a sequence of label/value pairs in order.
|
|
268
|
+
*
|
|
269
|
+
* @param page The Playwright page to search.
|
|
270
|
+
* @param locatorsAndValues Label text or pattern paired with its value.
|
|
271
|
+
* @returns A promise that resolves after all values are set.
|
|
272
|
+
* @example
|
|
273
|
+
* ```ts
|
|
274
|
+
* await setValueForLocators(page, [
|
|
275
|
+
* ["Email", "user@example.com"],
|
|
276
|
+
* ["Name", "Alex"],
|
|
277
|
+
* ]);
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
async function setValueForLocators(page, locatorsAndValues) {
|
|
281
|
+
for (const locatorAndValue of locatorsAndValues) await setLocatorValue(page, locatorAndValue);
|
|
282
|
+
}
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region src/features/playwright/modules/runners/utils/runSimilarTests.ts
|
|
285
|
+
/**
|
|
286
|
+
* Registers tests that share the same page navigation and setup.
|
|
287
|
+
*
|
|
288
|
+
* @param tests Tuples of test names followed by page callbacks.
|
|
289
|
+
* @returns Nothing; registers one Playwright test for each tuple.
|
|
290
|
+
* @example
|
|
291
|
+
* ```ts
|
|
292
|
+
* runSimilarTests([
|
|
293
|
+
* ["shows the home page", async (page) => {
|
|
294
|
+
* await page.getByRole("heading", { name: "Home" }).waitFor();
|
|
295
|
+
* }],
|
|
296
|
+
* ]);
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
function runSimilarTests(...tests) {
|
|
300
|
+
tests.forEach(([testName, ...testFunctions]) => test(testName, async ({ page }) => {
|
|
301
|
+
await page.goto("/");
|
|
302
|
+
for (const testFunction of testFunctions) await testFunction(page);
|
|
303
|
+
}));
|
|
304
|
+
}
|
|
305
|
+
//#endregion
|
|
306
|
+
export { clickBodyCorner, clickButton, clickLabelInput, clickLink, clickMultipleLabelInputs, fillLocatorWith, getButton, getLabel, getLink, getLocatorByText, isChecked, isNotChecked, runSimilarTests, setLocatorValue, setValueForLocators, shouldNotSee, shouldSee };
|
|
307
|
+
|
|
308
|
+
//# sourceMappingURL=playwright.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright.mjs","names":[],"sources":["../src/features/playwright/modules/assertions/utils/isChecked.ts","../src/features/playwright/modules/assertions/utils/isNotChecked.ts","../src/features/playwright/modules/assertions/utils/getMatcher.ts","../src/features/playwright/modules/assertions/utils/shouldNotSee.ts","../src/features/playwright/modules/assertions/utils/shouldSee.ts","../src/features/playwright/modules/interactions/utils/clickBodyCorner.ts","../src/features/playwright/shared/locators/utils/getLocatorByText.ts","../src/features/playwright/shared/locators/utils/getButton.ts","../src/features/playwright/shared/locators/utils/getLabel.ts","../src/features/playwright/shared/locators/utils/getLink.ts","../src/features/playwright/modules/interactions/utils/clickButton.ts","../src/features/playwright/modules/interactions/utils/clickLabelInput.ts","../src/features/playwright/modules/interactions/utils/clickLink.ts","../src/features/playwright/modules/interactions/utils/clickMultipleLabelInputs.ts","../src/features/playwright/modules/interactions/utils/fillLocatorWith.ts","../src/features/playwright/modules/interactions/utils/setLocatorValue.ts","../src/features/playwright/modules/interactions/utils/setValueForLocators.ts","../src/features/playwright/modules/runners/utils/runSimilarTests.ts"],"sourcesContent":["import type { Locator, Page } from \"@playwright/test\";\r\nimport { expect } from \"@playwright/test\";\r\n\r\n/**\r\n * Asserts that the element selected by the CSS selector is checked.\r\n *\r\n * @param page The Playwright page to search.\r\n * @param selector CSS selector for a checkbox or radio input.\r\n * @returns The checked locator.\r\n * @example\r\n * ```ts\r\n * const checkbox = await isChecked(page, \"#terms\");\r\n * ```\r\n */\r\nexport async function isChecked(\r\n page: Page,\r\n selector: string,\r\n): Promise<Locator> {\r\n const locator = page.locator(selector);\r\n await expect(locator).toBeChecked();\r\n return locator;\r\n}\r\n","import type { Locator, Page } from \"@playwright/test\";\r\nimport { expect } from \"@playwright/test\";\r\n\r\n/**\r\n * Asserts that the element selected by the CSS selector is not checked.\r\n *\r\n * @param page The Playwright page to search.\r\n * @param selector CSS selector for a checkbox or radio input.\r\n * @returns The unchecked locator.\r\n * @example\r\n * ```ts\r\n * const checkbox = await isNotChecked(page, \"#terms\");\r\n * ```\r\n */\r\nexport async function isNotChecked(\r\n page: Page,\r\n selector: string,\r\n): Promise<Locator> {\r\n const locator = page.locator(selector);\r\n await expect(locator).not.toBeChecked();\r\n return locator;\r\n}\r\n","import { patternToRegex } from \"@/shared/regex\";\r\nimport { expect, type Locator, type Page } from \"@playwright/test\";\r\nimport { TEXT_MATCHER } from \"../types\";\r\n\r\nexport function getMatcher(\r\n page: Page,\r\n ...textes: TEXT_MATCHER[]\r\n): ReturnType<typeof expect<Locator>>[] {\r\n return textes.map((text) => {\r\n if (Array.isArray(text)) {\r\n const [matcher, nth] = text;\r\n return expect(page.getByText(patternToRegex(matcher)).nth(nth));\r\n }\r\n return expect(page.getByText(text));\r\n });\r\n}\r\n","import type { Page } from \"@playwright/test\";\r\nimport type { TEXT_MATCHER } from \"../types\";\r\nimport { getMatcher } from \"./getMatcher\";\r\n\r\n/**\r\n * Asserts that each text matcher is not visible on the page.\r\n *\r\n * @param page The Playwright page to inspect.\r\n * @param textes Text strings, regular expressions, or `[matcher, occurrenceIndex]` pairs.\r\n * @returns A promise that resolves when every matching element is not visible.\r\n * @example\r\n * ```ts\r\n * await shouldNotSee(page, \"Error message\");\r\n * ```\r\n */\r\nexport async function shouldNotSee(page: Page, ...textes: TEXT_MATCHER[]) {\r\n const matchers = getMatcher(page, ...textes);\r\n for (let i = 0; i < matchers.length; i++) {\r\n await matchers[i].not.toBeVisible();\r\n }\r\n}\r\n","import type { Page } from \"@playwright/test\";\r\nimport type { TEXT_MATCHER } from \"../types\";\r\nimport { getMatcher } from \"./getMatcher\";\r\n\r\n/**\r\n * Asserts that each text matcher is visible on the page.\r\n *\r\n * @param page The Playwright page to inspect.\r\n * @param textes Text strings, regular expressions, or `[matcher, occurrenceIndex]` pairs.\r\n * @returns A promise that resolves when every matching element is visible.\r\n * @example\r\n * ```ts\r\n * await shouldSee(page, \"Welcome\", [/Saved/i, 0]);\r\n * ```\r\n */\r\nexport async function shouldSee(page: Page, ...textes: TEXT_MATCHER[]) {\r\n const matchers = getMatcher(page, ...textes);\r\n for (let i = 0; i < matchers.length; i++) {\r\n await matchers[i].toBeVisible();\r\n }\r\n}\r\n","import type { Page } from \"@playwright/test\";\r\n\r\n/**\r\n * Clicks the top-left corner of the page body.\r\n *\r\n * @param page The Playwright page to interact with.\r\n * @returns A promise that resolves after the click.\r\n * @example\r\n * ```ts\r\n * await clickBodyCorner(page);\r\n * ```\r\n */\r\nexport async function clickBodyCorner(page: Page) {\r\n await page.locator(\"body\").click({ position: { x: 0, y: 0 } });\r\n}\r\n","import type { Page } from \"@playwright/test\";\r\nimport type { LocatorWithText } from \"../types\";\r\n\r\n/**\r\n * Locates elements matching a selector and text pattern.\r\n *\r\n * @param page The Playwright page to search.\r\n * @param locator Selector paired with text that the element must contain.\r\n * @returns A locator matching the selector and text.\r\n * @example\r\n * ```ts\r\n * const save = getLocatorByText(page, [\"button\", \"Save\"]);\r\n * ```\r\n */\r\nexport function getLocatorByText(\r\n page: Page,\r\n [locator, hasText]: LocatorWithText,\r\n) {\r\n return page.locator(locator, { hasText });\r\n}\r\n","import { TEXT_PATTERN } from \"@/shared/regex\";\r\nimport type { Page } from \"@playwright/test\";\r\nimport { getLocatorByText } from \"./getLocatorByText\";\r\n\r\n/**\r\n * Locates a button by its contained text.\r\n *\r\n * @param page The Playwright page to search.\r\n * @param hasText Text or regular expression contained by the button.\r\n * @returns A locator for the matching button.\r\n * @example\r\n * ```ts\r\n * const saveButton = getButton(page, \"Save\");\r\n * ```\r\n */\r\nexport function getButton(page: Page, hasText: TEXT_PATTERN) {\r\n return getLocatorByText(page, [\"button\", hasText]);\r\n}\r\n","import { TEXT_PATTERN } from \"@/shared/regex\";\r\nimport type { Page } from \"@playwright/test\";\r\nimport { getLocatorByText } from \"./getLocatorByText\";\r\n\r\n/**\r\n * Locates a label by its contained text.\r\n *\r\n * @param page The Playwright page to search.\r\n * @param hasText Text or regular expression contained by the label.\r\n * @returns A locator for the matching label.\r\n * @example\r\n * ```ts\r\n * const emailLabel = getLabel(page, \"Email\");\r\n * ```\r\n */\r\nexport function getLabel(page: Page, hasText: TEXT_PATTERN) {\r\n return getLocatorByText(page, [\"label\", hasText]);\r\n}\r\n","import { TEXT_PATTERN } from \"@/shared/regex\";\r\nimport type { Page } from \"@playwright/test\";\r\n\r\n/**\r\n * Locates a link by its accessible name.\r\n *\r\n * @param page The Playwright page to search.\r\n * @param hasText Text or regular expression matching the link's accessible name.\r\n * @returns A locator for the matching link.\r\n * @example\r\n * ```ts\r\n * const documentationLink = getLink(page, \"Documentation\");\r\n * ```\r\n */\r\nexport function getLink(page: Page, hasText: TEXT_PATTERN) {\r\n return page.getByRole(\"link\", { name: hasText });\r\n}\r\n","import { getButton } from \"@/features/playwright/shared/locators\";\r\nimport { TEXT_PATTERN } from \"@/shared/regex\";\r\nimport type { Page } from \"@playwright/test\";\r\n\r\n/**\r\n * Clicks a button matching the supplied text.\r\n *\r\n * @param page The Playwright page to search.\r\n * @param hasText Text or regular expression contained by the button.\r\n * @returns A promise that resolves after the button is clicked.\r\n * @example\r\n * ```ts\r\n * await clickButton(page, \"Save\");\r\n * ```\r\n */\r\nexport async function clickButton(page: Page, hasText: TEXT_PATTERN) {\r\n await getButton(page, hasText).click();\r\n}\r\n","import { getLabel } from \"@/features/playwright/shared/locators\";\r\nimport { TEXT_PATTERN } from \"@/shared/regex\";\r\nimport type { Page } from \"@playwright/test\";\r\n\r\n/**\r\n * Clicks the label matching the supplied text.\r\n *\r\n * @param page The Playwright page to search.\r\n * @param labelText Text or regular expression contained by the label.\r\n * @returns A promise that resolves after the label is clicked.\r\n * @example\r\n * ```ts\r\n * await clickLabelInput(page, \"Subscribe\");\r\n * ```\r\n */\r\nexport async function clickLabelInput(page: Page, labelText: TEXT_PATTERN) {\r\n await getLabel(page, labelText).click();\r\n}\r\n","import { getLink } from \"@/features/playwright/shared/locators\";\r\nimport { TEXT_PATTERN } from \"@/shared/regex\";\r\nimport type { Page } from \"@playwright/test\";\r\n\r\n/**\r\n * Clicks a link matching the supplied accessible name.\r\n *\r\n * @param page The Playwright page to search.\r\n * @param hasText Text or regular expression matching the link name.\r\n * @returns A promise that resolves after the link is clicked.\r\n * @example\r\n * ```ts\r\n * await clickLink(page, \"Documentation\");\r\n * ```\r\n */\r\nexport async function clickLink(page: Page, hasText: TEXT_PATTERN) {\r\n await getLink(page, hasText).click();\r\n}\r\n","import { TEXT_PATTERN } from \"@/shared/regex\";\r\nimport type { Page } from \"@playwright/test\";\r\nimport { clickLabelInput } from \"./clickLabelInput\";\r\n\r\n/**\r\n * Clicks each matching label in the supplied order.\r\n *\r\n * @param page The Playwright page to search.\r\n * @param labelTexts Text or regular expressions for the labels to click.\r\n * @returns A promise that resolves after all labels are clicked.\r\n * @example\r\n * ```ts\r\n * await clickMultipleLabelInputs(page, [\"Email\", \"Subscribe\"]);\r\n * ```\r\n */\r\nexport async function clickMultipleLabelInputs(\r\n page: Page,\r\n labelTexts: TEXT_PATTERN[],\r\n) {\r\n for (const labelText of labelTexts) {\r\n await clickLabelInput(page, labelText);\r\n }\r\n}\r\n","import type { Locator } from \"@playwright/test\";\r\n\r\n/**\r\n * Clicks a locator and fills it with the supplied value.\r\n *\r\n * @param locator The input-like locator to fill.\r\n * @param value The value to enter.\r\n * @returns A promise that resolves after the locator is filled.\r\n * @example\r\n * ```ts\r\n * await fillLocatorWith(page.getByLabel(\"Email\"), \"user@example.com\");\r\n * ```\r\n */\r\nexport async function fillLocatorWith(locator: Locator, value: string) {\r\n await locator.click();\r\n await locator.fill(value);\r\n}\r\n","import { type LABEL_LOCATOR, patternToRegex } from \"@/shared/regex\";\r\nimport type { Page } from \"@playwright/test\";\r\n\r\n/**\r\n * Clicks the label matching the pattern and fills it with the supplied value.\r\n *\r\n * @param page The Playwright page to search.\r\n * @param locatorAndValue Label text or pattern paired with the value to fill.\r\n * @returns A promise that resolves after the matching label is filled.\r\n * @example\r\n * ```ts\r\n * await setLocatorValue(page, [\"Email\", \"user@example.com\"]);\r\n * ```\r\n */\r\nexport async function setLocatorValue(\r\n page: Page,\r\n [locator, value]: LABEL_LOCATOR,\r\n) {\r\n const element = page.locator(\"label\", { hasText: patternToRegex(locator) });\r\n await element.click();\r\n await element.fill(value);\r\n}\r\n","import { LABEL_LOCATOR } from \"@/shared/regex\";\r\nimport type { Page } from \"@playwright/test\";\r\nimport { setLocatorValue } from \"./setLocatorValue\";\r\n\r\n/**\r\n * Sets values for a sequence of label/value pairs in order.\r\n *\r\n * @param page The Playwright page to search.\r\n * @param locatorsAndValues Label text or pattern paired with its value.\r\n * @returns A promise that resolves after all values are set.\r\n * @example\r\n * ```ts\r\n * await setValueForLocators(page, [\r\n * [\"Email\", \"user@example.com\"],\r\n * [\"Name\", \"Alex\"],\r\n * ]);\r\n * ```\r\n */\r\nexport async function setValueForLocators(\r\n page: Page,\r\n locatorsAndValues: LABEL_LOCATOR[],\r\n) {\r\n for (const locatorAndValue of locatorsAndValues) {\r\n await setLocatorValue(page, locatorAndValue);\r\n }\r\n}\r\n","import test from \"@playwright/test\";\r\nimport type { SimpleTest } from \"../types\";\r\n\r\n/**\r\n * Registers tests that share the same page navigation and setup.\r\n *\r\n * @param tests Tuples of test names followed by page callbacks.\r\n * @returns Nothing; registers one Playwright test for each tuple.\r\n * @example\r\n * ```ts\r\n * runSimilarTests([\r\n * [\"shows the home page\", async (page) => {\r\n * await page.getByRole(\"heading\", { name: \"Home\" }).waitFor();\r\n * }],\r\n * ]);\r\n * ```\r\n */\r\nexport function runSimilarTests(...tests: SimpleTest[]) {\r\n tests.forEach(([testName, ...testFunctions]) =>\r\n test(testName, async ({ page }) => {\r\n await page.goto(\"/\");\r\n for (const testFunction of testFunctions) await testFunction(page);\r\n }),\r\n );\r\n}\r\n"],"mappings":"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"}
|
package/dist/react.cjs
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
|
|
3
|
+
let react = require("react");
|
|
4
|
+
react = require_rolldown_runtime.__toESM(react, 1);
|
|
5
|
+
//#region src/features/react/utils/resolveArrayChild.ts
|
|
6
|
+
/**
|
|
7
|
+
* Resolves one array child and supplies a stable key when the result is an
|
|
8
|
+
* element without one.
|
|
9
|
+
*
|
|
10
|
+
* @param child - The child value to resolve.
|
|
11
|
+
* @param index - The child's position in its containing array.
|
|
12
|
+
* @param resolveTree - The recursive tree resolver.
|
|
13
|
+
* @returns The resolved child.
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* await resolveArrayChild(<Page />, 0, resolveRscTree);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
async function resolveArrayChild(child, index, resolveTree) {
|
|
20
|
+
const resolvedChild = await resolveTree(child);
|
|
21
|
+
if (react.isValidElement(resolvedChild)) {
|
|
22
|
+
const keyToUse = resolvedChild.key ?? child?.key ?? `rsc-key-${index}`;
|
|
23
|
+
return react.cloneElement(resolvedChild, { key: keyToUse });
|
|
24
|
+
}
|
|
25
|
+
return resolvedChild;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/features/react/utils/isClassComponent.ts
|
|
29
|
+
/**
|
|
30
|
+
* Determines whether a component type is a React class component.
|
|
31
|
+
*
|
|
32
|
+
* @param type - The value to inspect.
|
|
33
|
+
* @returns `true` when the value is a React class component.
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* isClassComponent(MyClassComponent); // true
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
function isClassComponent(type) {
|
|
40
|
+
return typeof type === "function" && Boolean(type.prototype?.isReactComponent);
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/features/react/utils/executeServerComponent.ts
|
|
44
|
+
/**
|
|
45
|
+
* Executes a server component and awaits an asynchronous result when present.
|
|
46
|
+
* Component errors intentionally propagate to the caller.
|
|
47
|
+
*
|
|
48
|
+
* @param type - The server component to execute.
|
|
49
|
+
* @param props - Props passed to the component.
|
|
50
|
+
* @returns The component's resolved render result.
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* await executeServerComponent(Page, { title: "Hello" });
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
async function executeServerComponent(type, props) {
|
|
57
|
+
if (isClassComponent(type)) return new type(props).render();
|
|
58
|
+
return await type(props);
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/features/react/utils/isClientComponent.ts
|
|
62
|
+
/**
|
|
63
|
+
* Determines whether a component should be kept behind a client boundary.
|
|
64
|
+
* React client/module references and functions containing hook calls are not
|
|
65
|
+
* invoked directly by the resolver.
|
|
66
|
+
*
|
|
67
|
+
* @param type - The React element type to inspect.
|
|
68
|
+
* @returns `true` when the type represents a client boundary.
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* isClientComponent({ $$typeof: Symbol.for("react.client.reference") }); // true
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
function isClientComponent(type) {
|
|
75
|
+
if (type === null || typeof type !== "function" && typeof type !== "object") return false;
|
|
76
|
+
const reference = type;
|
|
77
|
+
if (reference.$$typeof === Symbol.for("react.client.reference") || reference.$$typeof === Symbol.for("react.module.reference") || typeof reference.$$id === "string") return true;
|
|
78
|
+
return typeof type === "function" && /\buse[A-Z0-9_]\w*\s*\(/.test(Function.prototype.toString.call(type));
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/features/react/utils/resolveProps.ts
|
|
82
|
+
/**
|
|
83
|
+
* Resolves React elements and arrays nested in a component's props.
|
|
84
|
+
*
|
|
85
|
+
* @param props - The component props to inspect.
|
|
86
|
+
* @param resolveTree - The recursive tree resolver.
|
|
87
|
+
* @returns Updated props and whether any prop value changed.
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* const result = await resolveProps({ children: <Page /> }, resolveRscTree);
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
async function resolveProps(props, resolveTree) {
|
|
94
|
+
const newProps = { ...props };
|
|
95
|
+
let hasChanged = false;
|
|
96
|
+
for (const key of Object.keys(newProps)) {
|
|
97
|
+
const value = newProps[key];
|
|
98
|
+
if (react.isValidElement(value) || Array.isArray(value)) {
|
|
99
|
+
const resolved = await resolveTree(value);
|
|
100
|
+
if (resolved !== value) {
|
|
101
|
+
newProps[key] = resolved;
|
|
102
|
+
hasChanged = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
newProps,
|
|
108
|
+
hasChanged
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/features/react/utils/resolveElement.ts
|
|
113
|
+
/**
|
|
114
|
+
* Resolves a server component element or recursively updates its element props.
|
|
115
|
+
* Client component boundaries remain untouched and component errors propagate.
|
|
116
|
+
*
|
|
117
|
+
* @param node - The React element to resolve.
|
|
118
|
+
* @param resolveTree - The recursive tree resolver.
|
|
119
|
+
* @returns The resolved render result or the updated original element.
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* await resolveElement(<ServerPage />, resolveRscTree);
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
async function resolveElement(node, resolveTree) {
|
|
126
|
+
const { type, props } = node;
|
|
127
|
+
if (typeof type === "function" && !isClientComponent(type)) return resolveTree(await executeServerComponent(type, props));
|
|
128
|
+
if (props && typeof props === "object") {
|
|
129
|
+
const { newProps, hasChanged } = await resolveProps(props, resolveTree);
|
|
130
|
+
if (hasChanged) return react.cloneElement(node, newProps);
|
|
131
|
+
}
|
|
132
|
+
return node;
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/features/react/utils/traverseTree.ts
|
|
136
|
+
/**
|
|
137
|
+
* Traverses arrays and React elements, delegating component resolution.
|
|
138
|
+
*
|
|
139
|
+
* @param node - The tree value to visit.
|
|
140
|
+
* @param resolveTree - The recursive tree resolver.
|
|
141
|
+
* @returns The traversed value with server components resolved.
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* await traverseTree(tree, resolveRscTree);
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
async function traverseTree(node, resolveTree) {
|
|
148
|
+
if (!node || typeof node !== "object") return node;
|
|
149
|
+
if (Array.isArray(node)) return Promise.all(node.map((child, index) => resolveArrayChild(child, index, resolveTree)));
|
|
150
|
+
if (react.isValidElement(node)) return resolveElement(node, resolveTree);
|
|
151
|
+
return node;
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/features/react/utils/resolveRscTree.ts
|
|
155
|
+
/**
|
|
156
|
+
* Recursively resolves synchronous and asynchronous React Server Components.
|
|
157
|
+
* Client component boundaries are preserved, and component errors are allowed
|
|
158
|
+
* to reject the returned promise.
|
|
159
|
+
*
|
|
160
|
+
* @param node - The React node or tree to resolve.
|
|
161
|
+
* @returns A promise for the resolved React tree.
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* const resolved = await resolveRscTree(<ServerPage />);
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
async function resolveRscTree(node) {
|
|
168
|
+
return traverseTree(node, resolveRscTree);
|
|
169
|
+
}
|
|
170
|
+
//#endregion
|
|
171
|
+
exports.resolveRscTree = resolveRscTree;
|
|
172
|
+
|
|
173
|
+
//# sourceMappingURL=react.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.cjs","names":["React","ClassComponent","FunctionComponent","React","React","React"],"sources":["../src/features/react/utils/resolveArrayChild.ts","../src/features/react/utils/isClassComponent.ts","../src/features/react/utils/executeServerComponent.ts","../src/features/react/utils/isClientComponent.ts","../src/features/react/utils/resolveProps.ts","../src/features/react/utils/resolveElement.ts","../src/features/react/utils/traverseTree.ts","../src/features/react/utils/resolveRscTree.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\r\nimport * as React from \"react\";\r\nimport type { ResolveTree } from \"./types\";\r\n\r\n/**\r\n * Resolves one array child and supplies a stable key when the result is an\r\n * element without one.\r\n *\r\n * @param child - The child value to resolve.\r\n * @param index - The child's position in its containing array.\r\n * @param resolveTree - The recursive tree resolver.\r\n * @returns The resolved child.\r\n * @example\r\n * ```ts\r\n * await resolveArrayChild(<Page />, 0, resolveRscTree);\r\n * ```\r\n */\r\nexport async function resolveArrayChild(\r\n child: ReactNode,\r\n index: number,\r\n resolveTree: ResolveTree,\r\n): Promise<ReactNode> {\r\n const resolvedChild = await resolveTree(child);\r\n if (React.isValidElement(resolvedChild)) {\r\n const keyToUse =\r\n resolvedChild.key ??\r\n (child as { key?: string })?.key ??\r\n `rsc-key-${index}`;\r\n return React.cloneElement(resolvedChild, { key: keyToUse });\r\n }\r\n return resolvedChild;\r\n}\r\n","/**\r\n * Determines whether a component type is a React class component.\r\n *\r\n * @param type - The value to inspect.\r\n * @returns `true` when the value is a React class component.\r\n * @example\r\n * ```ts\r\n * isClassComponent(MyClassComponent); // true\r\n * ```\r\n */\r\nexport function isClassComponent(type: unknown): boolean {\r\n return (\r\n typeof type === \"function\" &&\r\n Boolean(\r\n (type as { prototype?: { isReactComponent?: unknown } }).prototype\r\n ?.isReactComponent,\r\n )\r\n );\r\n}\r\n","import type * as React from \"react\";\r\nimport type { ReactNode } from \"react\";\r\nimport { isClassComponent } from \"./isClassComponent\";\r\n\r\n/**\r\n * Executes a server component and awaits an asynchronous result when present.\r\n * Component errors intentionally propagate to the caller.\r\n *\r\n * @param type - The server component to execute.\r\n * @param props - Props passed to the component.\r\n * @returns The component's resolved render result.\r\n * @example\r\n * ```ts\r\n * await executeServerComponent(Page, { title: \"Hello\" });\r\n * ```\r\n */\r\nexport async function executeServerComponent(\r\n type: React.ComponentType<Record<string, unknown>>,\r\n props: Record<string, unknown>,\r\n): Promise<ReactNode> {\r\n if (isClassComponent(type)) {\r\n const ClassComponent = type as React.ComponentClass<\r\n Record<string, unknown>\r\n >;\r\n return new ClassComponent(props).render();\r\n }\r\n\r\n const FunctionComponent = type as React.FunctionComponent<\r\n Record<string, unknown>\r\n >;\r\n return await FunctionComponent(props);\r\n}\r\n","/**\r\n * Determines whether a component should be kept behind a client boundary.\r\n * React client/module references and functions containing hook calls are not\r\n * invoked directly by the resolver.\r\n *\r\n * @param type - The React element type to inspect.\r\n * @returns `true` when the type represents a client boundary.\r\n * @example\r\n * ```ts\r\n * isClientComponent({ $$typeof: Symbol.for(\"react.client.reference\") }); // true\r\n * ```\r\n */\r\nexport function isClientComponent(type: unknown): boolean {\r\n if (type === null || (typeof type !== \"function\" && typeof type !== \"object\")) {\r\n return false;\r\n }\r\n\r\n const reference = type as { $$typeof?: symbol; $$id?: unknown };\r\n if (\r\n reference.$$typeof === Symbol.for(\"react.client.reference\") ||\r\n reference.$$typeof === Symbol.for(\"react.module.reference\") ||\r\n typeof reference.$$id === \"string\"\r\n ) {\r\n return true;\r\n }\r\n\r\n return (\r\n typeof type === \"function\" &&\r\n /\\buse[A-Z0-9_]\\w*\\s*\\(/.test(Function.prototype.toString.call(type))\r\n );\r\n}\r\n","import * as React from \"react\";\r\nimport type { ResolveTree } from \"./types\";\r\n\r\n/**\r\n * Resolves React elements and arrays nested in a component's props.\r\n *\r\n * @param props - The component props to inspect.\r\n * @param resolveTree - The recursive tree resolver.\r\n * @returns Updated props and whether any prop value changed.\r\n * @example\r\n * ```ts\r\n * const result = await resolveProps({ children: <Page /> }, resolveRscTree);\r\n * ```\r\n */\r\nexport async function resolveProps(\r\n props: Record<string, unknown>,\r\n resolveTree: ResolveTree,\r\n): Promise<{ newProps: Record<string, unknown>; hasChanged: boolean }> {\r\n const newProps: Record<string, unknown> = { ...props };\r\n let hasChanged = false;\r\n\r\n for (const key of Object.keys(newProps)) {\r\n const value = newProps[key];\r\n if (React.isValidElement(value) || Array.isArray(value)) {\r\n const resolved = await resolveTree(value as React.ReactNode);\r\n if (resolved !== value) {\r\n newProps[key] = resolved;\r\n hasChanged = true;\r\n }\r\n }\r\n }\r\n\r\n return { newProps, hasChanged };\r\n}\r\n","import type { ReactNode } from \"react\";\r\nimport * as React from \"react\";\r\nimport { executeServerComponent } from \"./executeServerComponent\";\r\nimport { isClientComponent } from \"./isClientComponent\";\r\nimport { resolveProps } from \"./resolveProps\";\r\nimport type { ResolveTree } from \"./types\";\r\n\r\n/**\r\n * Resolves a server component element or recursively updates its element props.\r\n * Client component boundaries remain untouched and component errors propagate.\r\n *\r\n * @param node - The React element to resolve.\r\n * @param resolveTree - The recursive tree resolver.\r\n * @returns The resolved render result or the updated original element.\r\n * @example\r\n * ```ts\r\n * await resolveElement(<ServerPage />, resolveRscTree);\r\n * ```\r\n */\r\nexport async function resolveElement(\r\n node: React.ReactElement,\r\n resolveTree: ResolveTree,\r\n): Promise<ReactNode> {\r\n const { type, props } = node;\r\n\r\n if (typeof type === \"function\" && !isClientComponent(type)) {\r\n const serverResult = await executeServerComponent(\r\n type as React.ComponentType<Record<string, unknown>>,\r\n props as Record<string, unknown>,\r\n );\r\n return resolveTree(serverResult);\r\n }\r\n\r\n if (props && typeof props === \"object\") {\r\n const { newProps, hasChanged } = await resolveProps(\r\n props as Record<string, unknown>,\r\n resolveTree,\r\n );\r\n if (hasChanged) {\r\n return React.cloneElement(node, newProps);\r\n }\r\n }\r\n\r\n return node;\r\n}\r\n","import * as React from \"react\";\r\nimport type { ReactNode } from \"react\";\r\nimport { resolveArrayChild } from \"./resolveArrayChild\";\r\nimport { resolveElement } from \"./resolveElement\";\r\nimport type { ResolveTree } from \"./types\";\r\n\r\n/**\r\n * Traverses arrays and React elements, delegating component resolution.\r\n *\r\n * @param node - The tree value to visit.\r\n * @param resolveTree - The recursive tree resolver.\r\n * @returns The traversed value with server components resolved.\r\n * @example\r\n * ```ts\r\n * await traverseTree(tree, resolveRscTree);\r\n * ```\r\n */\r\nexport async function traverseTree(\r\n node: ReactNode,\r\n resolveTree: ResolveTree,\r\n): Promise<ReactNode> {\r\n if (!node || typeof node !== \"object\") return node;\r\n\r\n if (Array.isArray(node)) {\r\n return Promise.all(\r\n node.map((child, index) => resolveArrayChild(child, index, resolveTree)),\r\n );\r\n }\r\n\r\n if (React.isValidElement(node)) {\r\n return resolveElement(node, resolveTree);\r\n }\r\n\r\n return node;\r\n}\r\n","import type { ReactNode } from \"react\";\r\nimport { traverseTree } from \"./traverseTree\";\r\n\r\n/**\r\n * Recursively resolves synchronous and asynchronous React Server Components.\r\n * Client component boundaries are preserved, and component errors are allowed\r\n * to reject the returned promise.\r\n *\r\n * @param node - The React node or tree to resolve.\r\n * @returns A promise for the resolved React tree.\r\n * @example\r\n * ```ts\r\n * const resolved = await resolveRscTree(<ServerPage />);\r\n * ```\r\n */\r\nexport async function resolveRscTree(node: ReactNode): Promise<ReactNode> {\r\n return traverseTree(node, resolveRscTree);\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiBA,eAAsB,kBACpB,OACA,OACA,aACoB;CACpB,MAAM,gBAAgB,MAAM,YAAY,KAAK;CAC7C,IAAIA,MAAM,eAAe,aAAa,GAAG;EACvC,MAAM,WACJ,cAAc,OACb,OAA4B,OAC7B,WAAW;EACb,OAAOA,MAAM,aAAa,eAAe,EAAE,KAAK,SAAS,CAAC;CAC5D;CACA,OAAO;AACT;;;;;;;;;;;;;ACrBA,SAAgB,iBAAiB,MAAwB;CACvD,OACE,OAAO,SAAS,cAChB,QACG,KAAwD,WACrD,gBACN;AAEJ;;;;;;;;;;;;;;;ACFA,eAAsB,uBACpB,MACA,OACoB;CACpB,IAAI,iBAAiB,IAAI,GAIvB,OAAO,IAAIC,KAAe,KAAK,CAAC,CAAC,OAAO;CAM1C,OAAO,MAAMC,KAAkB,KAAK;AACtC;;;;;;;;;;;;;;;ACnBA,SAAgB,kBAAkB,MAAwB;CACxD,IAAI,SAAS,QAAS,OAAO,SAAS,cAAc,OAAO,SAAS,UAClE,OAAO;CAGT,MAAM,YAAY;CAClB,IACE,UAAU,aAAa,OAAO,IAAI,wBAAwB,KAC1D,UAAU,aAAa,OAAO,IAAI,wBAAwB,KAC1D,OAAO,UAAU,SAAS,UAE1B,OAAO;CAGT,OACE,OAAO,SAAS,cAChB,yBAAyB,KAAK,SAAS,UAAU,SAAS,KAAK,IAAI,CAAC;AAExE;;;;;;;;;;;;;;AChBA,eAAsB,aACpB,OACA,aACqE;CACrE,MAAM,WAAoC,EAAE,GAAG,MAAM;CACrD,IAAI,aAAa;CAEjB,KAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,GAAG;EACvC,MAAM,QAAQ,SAAS;EACvB,IAAIC,MAAM,eAAe,KAAK,KAAK,MAAM,QAAQ,KAAK,GAAG;GACvD,MAAM,WAAW,MAAM,YAAY,KAAwB;GAC3D,IAAI,aAAa,OAAO;IACtB,SAAS,OAAO;IAChB,aAAa;GACf;EACF;CACF;CAEA,OAAO;EAAE;EAAU;CAAW;AAChC;;;;;;;;;;;;;;;ACdA,eAAsB,eACpB,MACA,aACoB;CACpB,MAAM,EAAE,MAAM,UAAU;CAExB,IAAI,OAAO,SAAS,cAAc,CAAC,kBAAkB,IAAI,GAKvD,OAAO,YAAY,MAJQ,uBACzB,MACA,KACF,CAC+B;CAGjC,IAAI,SAAS,OAAO,UAAU,UAAU;EACtC,MAAM,EAAE,UAAU,eAAe,MAAM,aACrC,OACA,WACF;EACA,IAAI,YACF,OAAOC,MAAM,aAAa,MAAM,QAAQ;CAE5C;CAEA,OAAO;AACT;;;;;;;;;;;;;;AC3BA,eAAsB,aACpB,MACA,aACoB;CACpB,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAE9C,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO,QAAQ,IACb,KAAK,KAAK,OAAO,UAAU,kBAAkB,OAAO,OAAO,WAAW,CAAC,CACzE;CAGF,IAAIC,MAAM,eAAe,IAAI,GAC3B,OAAO,eAAe,MAAM,WAAW;CAGzC,OAAO;AACT;;;;;;;;;;;;;;;ACnBA,eAAsB,eAAe,MAAqC;CACxE,OAAO,aAAa,MAAM,cAAc;AAC1C"}
|
package/dist/react.d.cts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/features/react/utils/resolveRscTree.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Recursively resolves synchronous and asynchronous React Server Components.
|
|
5
|
+
* Client component boundaries are preserved, and component errors are allowed
|
|
6
|
+
* to reject the returned promise.
|
|
7
|
+
*
|
|
8
|
+
* @param node - The React node or tree to resolve.
|
|
9
|
+
* @returns A promise for the resolved React tree.
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const resolved = await resolveRscTree(<ServerPage />);
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveRscTree(node: ReactNode): Promise<ReactNode>;
|
|
16
|
+
//#endregion
|
|
17
|
+
//# sourceMappingURL=react.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.cts","names":[],"sources":["../src/features/react/utils/resolveRscTree.ts"],"mappings":";;;;;;;;;;;;;;wBAesB,eAAe,MAAM,YAAY,QAAQ"}
|
package/dist/react.d.mts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/features/react/utils/resolveRscTree.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Recursively resolves synchronous and asynchronous React Server Components.
|
|
5
|
+
* Client component boundaries are preserved, and component errors are allowed
|
|
6
|
+
* to reject the returned promise.
|
|
7
|
+
*
|
|
8
|
+
* @param node - The React node or tree to resolve.
|
|
9
|
+
* @returns A promise for the resolved React tree.
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const resolved = await resolveRscTree(<ServerPage />);
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveRscTree(node: ReactNode): Promise<ReactNode>;
|
|
16
|
+
//#endregion
|
|
17
|
+
//# sourceMappingURL=react.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.mts","names":[],"sources":["../src/features/react/utils/resolveRscTree.ts"],"mappings":";;;;;;;;;;;;;;wBAesB,eAAe,MAAM,YAAY,QAAQ"}
|