@letsrunit/bdd 0.2.6 → 0.3.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.
@@ -0,0 +1,324 @@
1
+ import { locatorParameter, valueParameter, keysParameter, booleanParameter, enumParameter } from '@letsrunit/gherkin';
2
+ import { locator, setFieldValue, waitAfterInteraction, suppressInterferences, waitForIdle } from '@letsrunit/playwright';
3
+ import { expect } from '@playwright/test';
4
+ import { asFilename, eventually, splitUrl, pathRegexp, sleep, textToHtml } from '@letsrunit/utils';
5
+ import { receiveMail, toEml } from '@letsrunit/mailbox';
6
+ import ISO6391 from 'iso-639-1';
7
+ import metascraper from 'metascraper';
8
+ import metascraperLang from 'metascraper-lang';
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var typeDefinitions = [
16
+ locatorParameter(),
17
+ valueParameter(),
18
+ keysParameter(),
19
+ booleanParameter("visible", "hidden"),
20
+ booleanParameter("enabled", "disabled", /((?:en|dis)abled)/),
21
+ booleanParameter("checked", "unchecked", /((?:un)?checked)/),
22
+ booleanParameter("contains", "not contains", /((?:not )?contains)/),
23
+ booleanParameter("check", "uncheck", /((?:un)?check)/),
24
+ booleanParameter("focus", "blur"),
25
+ enumParameter(["click", "double-click", "right-click", "hover"], /((?:double-|right-)?click|hover)/)
26
+ ];
27
+
28
+ // src/steps/assert.ts
29
+ var assert_exports = {};
30
+ __export(assert_exports, {
31
+ contain: () => contain,
32
+ see: () => see
33
+ });
34
+ function expectOrNot(actual, toBe) {
35
+ return toBe ? expect(actual) : expect(actual).not;
36
+ }
37
+
38
+ // src/steps/wrappers.ts
39
+ function Given(expression, fn, comment) {
40
+ return { type: "Given", expression, fn, comment };
41
+ }
42
+ function When(expression, fn, comment) {
43
+ return { type: "When", expression, fn, comment };
44
+ }
45
+ function Then(expression, fn, comment) {
46
+ return { type: "Then", expression, fn, comment };
47
+ }
48
+
49
+ // src/steps/assert.ts
50
+ var WAIT_TIMEOUT = 5e3;
51
+ var see = Then(
52
+ "The page {contains|not contains} {locator}",
53
+ async function(visible, selector) {
54
+ const el = await locator(this.page, selector);
55
+ await expectOrNot(el, visible).toBeVisible({ timeout: WAIT_TIMEOUT });
56
+ }
57
+ );
58
+ var contain = Then(
59
+ "{locator} {contains|not contains} {locator}",
60
+ async function(selector, contain2, child) {
61
+ const el = await locator(this.page, selector);
62
+ const childElement = el.locator(child);
63
+ await expectOrNot(childElement, contain2).toBeAttached({ timeout: WAIT_TIMEOUT });
64
+ }
65
+ );
66
+
67
+ // src/steps/clipboard.ts
68
+ var clipboard_exports = {};
69
+ __export(clipboard_exports, {
70
+ copy: () => copy,
71
+ paste: () => paste
72
+ });
73
+ var TIMEOUT = 500;
74
+ async function copyInput(el) {
75
+ try {
76
+ return await el.inputValue();
77
+ } catch {
78
+ }
79
+ }
80
+ async function copyLink(el) {
81
+ try {
82
+ const tag = await el.evaluate((n) => n.tagName.toLowerCase());
83
+ const href = tag === "a" ? await el.getAttribute("href") : null;
84
+ if (href) {
85
+ return href.startsWith("mailto:") ? href.replace(/^mailto:/i, "") : href;
86
+ }
87
+ } catch {
88
+ }
89
+ }
90
+ async function copyText(el) {
91
+ return await el.textContent() ?? null;
92
+ }
93
+ var copy = When("I copy {locator} to the clipboard", async function(selector) {
94
+ const el = await locator(this.page, selector);
95
+ let value = await copyInput(el) ?? await copyLink(el) ?? await copyText(el);
96
+ this.clipboard = { value };
97
+ });
98
+ var paste = When("I paste from the clipboard into {locator}", async function(selector) {
99
+ const el = await locator(this.page, selector);
100
+ const value = this.clipboard?.value || "";
101
+ await el.fill(String(value), { timeout: TIMEOUT });
102
+ });
103
+
104
+ // src/steps/form.ts
105
+ var form_exports = {};
106
+ __export(form_exports, {
107
+ check: () => check,
108
+ clear: () => clear,
109
+ focus: () => focus,
110
+ set: () => set,
111
+ setRange: () => setRange
112
+ });
113
+ var TIMEOUT2 = 500;
114
+ var DELAY = 500;
115
+ var set = When("I set {locator} to {value}", async function(selector, value) {
116
+ const el = await locator(this.page, selector);
117
+ await setFieldValue(el, value, { timeout: TIMEOUT2 });
118
+ await sleep(DELAY);
119
+ });
120
+ var setRange = When(
121
+ "I set {locator} to range of {value} to {value}",
122
+ async function(selector, from, to) {
123
+ const el = await locator(this.page, selector);
124
+ await setFieldValue(el, { from, to }, { timeout: TIMEOUT2 });
125
+ await sleep(DELAY);
126
+ }
127
+ );
128
+ var clear = When("I clear {locator}", async function(selector) {
129
+ const el = await locator(this.page, selector);
130
+ await setFieldValue(el, null, { timeout: TIMEOUT2 });
131
+ await sleep(DELAY);
132
+ });
133
+ var check = When(
134
+ "I {check|uncheck} {locator}",
135
+ async function(check2, selector) {
136
+ const el = await locator(this.page, selector);
137
+ await setFieldValue(el, check2, { timeout: TIMEOUT2 });
138
+ await sleep(DELAY);
139
+ },
140
+ "For checkbox input or switch component"
141
+ );
142
+ var focus = When("I {focus|blur} {locator}", async function(focus2, selector) {
143
+ const el = await locator(this.page, selector);
144
+ if (focus2) {
145
+ await el.focus({ timeout: TIMEOUT2 });
146
+ } else {
147
+ await el.blur({ timeout: TIMEOUT2 });
148
+ }
149
+ });
150
+
151
+ // src/steps/keyboard.ts
152
+ var keyboard_exports = {};
153
+ __export(keyboard_exports, {
154
+ press: () => press,
155
+ type: () => type
156
+ });
157
+ var DELAY2 = 500;
158
+ var press = When("I press {keys}", async function(combo) {
159
+ for (const m of combo.modifiers) await this.page.keyboard.down(m);
160
+ await this.page.keyboard.press(combo.key);
161
+ for (const m of combo.modifiers.toReversed()) await this.page.keyboard.up(m);
162
+ await sleep(DELAY2);
163
+ });
164
+ var type = When("I type {string}", async function(value) {
165
+ await this.page.keyboard.type(value, { delay: 200 });
166
+ await sleep(DELAY2);
167
+ });
168
+
169
+ // src/steps/mailbox.ts
170
+ var mailbox_exports = {};
171
+ __export(mailbox_exports, {
172
+ receive: () => receive,
173
+ view: () => view
174
+ });
175
+ var MAX_RECEIVE_WAIT = 12e4;
176
+ var view = Given(
177
+ `I'm viewing an email sent to {string} with subject {string}`,
178
+ async function(address, subject) {
179
+ const emails = await receiveMail(address, { full: true, after: this.startTime, subject, limit: 1 });
180
+ if (emails.length === 0) {
181
+ throw new Error(`Did not receive an email with subject "${subject}"`);
182
+ }
183
+ const email = emails[0];
184
+ await this.page.goto("about:blank", { waitUntil: "load" });
185
+ await this.page.setContent(email.html ?? textToHtml(email.text), { waitUntil: "domcontentloaded" });
186
+ }
187
+ );
188
+ var receive = Then(
189
+ "I received an email sent to {string} with subject {string}",
190
+ async function(address, subject) {
191
+ const emails = await receiveMail(address, {
192
+ after: this.startTime,
193
+ full: true,
194
+ subject,
195
+ wait: true,
196
+ timeout: MAX_RECEIVE_WAIT,
197
+ limit: 1
198
+ });
199
+ if (emails.length === 0) {
200
+ throw new Error(`Did not receive an email with subject "${subject}"`);
201
+ }
202
+ const email = emails[0];
203
+ this.attach(toEml(email), {
204
+ mediaType: "message/rfc822",
205
+ fileName: asFilename(email.subject, "eml")
206
+ });
207
+ }
208
+ );
209
+
210
+ // src/steps/mouse.ts
211
+ var mouse_exports = {};
212
+ __export(mouse_exports, {
213
+ click: () => click,
214
+ clickHold: () => clickHold,
215
+ scroll: () => scroll
216
+ });
217
+ var TIMEOUT3 = 2500;
218
+ async function press2(el, action) {
219
+ if (action === "hover") {
220
+ await el.hover({ timeout: TIMEOUT3 });
221
+ } else {
222
+ await el.click({
223
+ button: action === "right-click" ? "right" : "left",
224
+ clickCount: action === "double-click" ? 2 : 1,
225
+ timeout: TIMEOUT3
226
+ });
227
+ }
228
+ }
229
+ var click = When(
230
+ "I {click|double-click|right-click|hover} {locator}",
231
+ async function(action, selector) {
232
+ const prevUrl = this.page.url();
233
+ const el = await locator(this.page, selector);
234
+ await press2(el, action);
235
+ await waitAfterInteraction(this.page, el, { prevUrl });
236
+ }
237
+ );
238
+ var clickHold = When(
239
+ "I {click|double-click|right-click|hover} {locator} while holding {keys}",
240
+ async function(action, selector, combo) {
241
+ const prevUrl = this.page.url();
242
+ const el = await locator(this.page, selector);
243
+ const keys = [...combo.modifiers, combo.key];
244
+ for (const m of keys) await this.page.keyboard.down(m);
245
+ await press2(el, action);
246
+ for (const m of keys.reverse()) await this.page.keyboard.up(m);
247
+ await waitAfterInteraction(this.page, el, { prevUrl });
248
+ }
249
+ );
250
+ var scroll = When("I scroll {locator} into view", async function(selector) {
251
+ const el = await locator(this.page, selector);
252
+ await el.scrollIntoViewIfNeeded({ timeout: TIMEOUT3 });
253
+ });
254
+
255
+ // src/steps/navigation.ts
256
+ var navigation_exports = {};
257
+ __export(navigation_exports, {
258
+ assertPath: () => assertPath,
259
+ back: () => back,
260
+ navHome: () => navHome,
261
+ navPath: () => navPath,
262
+ popupClosed: () => popupClosed
263
+ });
264
+ var scrapeLang = metascraper([metascraperLang()]);
265
+ async function getLang(page) {
266
+ const html = "html" in page ? page.html : await page.content();
267
+ const url = typeof page.url === "function" ? page.url() : page.url;
268
+ const { lang = null } = await scrapeLang({ html, url });
269
+ if (!lang) return null;
270
+ const code = lang.substring(0, 2);
271
+ const name = ISO6391.getName(code) || code;
272
+ return { code, name };
273
+ }
274
+
275
+ // src/steps/navigation.ts
276
+ async function openPage(world, path) {
277
+ const { page } = world;
278
+ const result = await page.goto(path);
279
+ expect(result?.status()).toBeLessThan(400);
280
+ await waitForIdle(page);
281
+ world.lang ??= await getLang(page) || void 0;
282
+ }
283
+ var navHome = Given("I'm on the homepage", async function() {
284
+ await openPage(this, "/");
285
+ });
286
+ var navPath = Given("I'm on page {string}", async function(path) {
287
+ await openPage(this, path);
288
+ });
289
+ var popupClosed = Given("all popups are closed", async function() {
290
+ await suppressInterferences(this.page, { lang: this.lang?.code });
291
+ });
292
+ var assertPath = Then("I should be on page {string}", async function(expectedPath) {
293
+ await eventually(async () => {
294
+ const { path: actualPath } = splitUrl(this.page.url());
295
+ if (expectedPath.includes(":")) {
296
+ const { regexp, names } = pathRegexp(expectedPath);
297
+ expect(actualPath, `Expected path ${actualPath} to match pattern ${expectedPath}`).toMatch(regexp);
298
+ const match = actualPath.match(regexp);
299
+ this.pathParams = Object.fromEntries(names.map((name, i) => [name, decodeURIComponent(match[i + 1])]));
300
+ } else {
301
+ expect(actualPath).toEqual(expectedPath);
302
+ delete this.pathParams;
303
+ }
304
+ });
305
+ });
306
+ var back = When("I go back to the previous page", async function() {
307
+ await this.page.goBack();
308
+ await waitForIdle(this.page);
309
+ });
310
+
311
+ // src/steps/index.ts
312
+ var stepsDefinitions = [
313
+ ...Object.values(assert_exports),
314
+ ...Object.values(navigation_exports),
315
+ ...Object.values(mouse_exports),
316
+ ...Object.values(form_exports),
317
+ ...Object.values(keyboard_exports),
318
+ ...Object.values(mailbox_exports),
319
+ ...Object.values(clipboard_exports)
320
+ ];
321
+
322
+ export { stepsDefinitions, typeDefinitions };
323
+ //# sourceMappingURL=chunk-YT56PTQF.js.map
324
+ //# sourceMappingURL=chunk-YT56PTQF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/parameters.ts","../src/steps/assert.ts","../src/utils/test-helpers.ts","../src/steps/wrappers.ts","../src/steps/clipboard.ts","../src/steps/form.ts","../src/steps/keyboard.ts","../src/steps/mailbox.ts","../src/steps/mouse.ts","../src/steps/navigation.ts","../src/utils/get-lang.ts","../src/steps/index.ts"],"names":["contain","locator","TIMEOUT","check","focus","DELAY","sleep","press","expect"],"mappings":"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","file":"chunk-YT56PTQF.js","sourcesContent":["import { booleanParameter, enumParameter, keysParameter, locatorParameter, valueParameter } from '@letsrunit/gherkin';\n\nexport const typeDefinitions = [\n locatorParameter(),\n valueParameter(),\n keysParameter(),\n\n booleanParameter('visible', 'hidden'),\n booleanParameter('enabled', 'disabled', /((?:en|dis)abled)/),\n booleanParameter('checked', 'unchecked', /((?:un)?checked)/),\n booleanParameter('contains', 'not contains', /((?:not )?contains)/),\n\n booleanParameter('check', 'uncheck', /((?:un)?check)/),\n booleanParameter('focus', 'blur'),\n\n enumParameter(['click', 'double-click', 'right-click', 'hover'], /((?:double-|right-)?click|hover)/),\n];\n","import { locator } from '@letsrunit/playwright';\nimport { expectOrNot } from '../utils/test-helpers';\nimport { Then } from './wrappers';\n\nconst WAIT_TIMEOUT = 5000;\n\nexport const see = Then(\n 'The page {contains|not contains} {locator}',\n async function (visible: boolean, selector: string) {\n const el = await locator(this.page, selector);\n await expectOrNot(el, visible).toBeVisible({ timeout: WAIT_TIMEOUT });\n },\n);\n\nexport const contain = Then(\n '{locator} {contains|not contains} {locator}',\n async function (selector: string, contain: boolean, child: string) {\n const el = await locator(this.page, selector);\n const childElement = el.locator(child);\n await expectOrNot(childElement, contain).toBeAttached({ timeout: WAIT_TIMEOUT });\n },\n);\n","import { expect, type Expect } from '@playwright/test';\n\nexport function expectOrNot<T>(\n actual: T,\n toBe: boolean\n): ReturnType<Expect<T>> | ReturnType<ReturnType<Expect<T>>['not']> {\n return toBe ? expect(actual) : expect(actual).not;\n}\n","import type { StepDefinition, StepHandler } from '../types';\n\nexport function Given(expression: string | RegExp, fn: StepHandler, comment?: string): StepDefinition {\n return { type: 'Given', expression, fn, comment };\n}\n\nexport function When(expression: string | RegExp, fn: StepHandler, comment?: string): StepDefinition {\n return { type: 'When', expression, fn, comment };\n}\n\nexport function Then(expression: string | RegExp, fn: StepHandler, comment?: string): StepDefinition {\n return { type: 'Then', expression, fn, comment };\n}\n","import { locator } from '@letsrunit/playwright';\nimport type { Locator } from '@playwright/test';\nimport { When } from './wrappers';\n\nconst TIMEOUT = 500;\n\nasync function copyInput(el: Locator): Promise<string | undefined> {\n try {\n return await el.inputValue();\n } catch {}\n}\n\nasync function copyLink(el: Locator): Promise<string | undefined> {\n try {\n const tag = await el.evaluate<string>((n: Element) => n.tagName.toLowerCase());\n const href = tag === 'a' ? await el.getAttribute('href') : null;\n if (href) {\n return href.startsWith('mailto:') ? href.replace(/^mailto:/i, '') : href;\n }\n } catch {}\n}\n\nasync function copyText(el: Locator): Promise<string | null> {\n return (await el.textContent()) ?? null;\n}\n\nexport const copy = When('I copy {locator} to the clipboard', async function (selector: string) {\n const el = await locator(this.page, selector);\n let value = (await copyInput(el)) ?? (await copyLink(el)) ?? (await copyText(el));\n\n this.clipboard = { value };\n});\n\nexport const paste = When('I paste from the clipboard into {locator}', async function (selector: string) {\n const el = await locator(this.page, selector);\n const value = this.clipboard?.value || '';\n\n await el.fill(String(value), { timeout: TIMEOUT });\n});\n","import { locator, setFieldValue } from '@letsrunit/playwright';\nimport { type Scalar, sleep } from '@letsrunit/utils';\nimport { When } from './wrappers';\n\nconst TIMEOUT = 500;\nconst DELAY = 500;\n\nexport const set = When('I set {locator} to {value}', async function (selector: string, value: Scalar | Scalar[]) {\n const el = await locator(this.page, selector);\n await setFieldValue(el, value, { timeout: TIMEOUT });\n await sleep(DELAY);\n});\n\nexport const setRange = When(\n 'I set {locator} to range of {value} to {value}',\n async function (selector: string, from: Scalar, to: Scalar) {\n const el = await locator(this.page, selector);\n await setFieldValue(el, { from, to }, { timeout: TIMEOUT });\n await sleep(DELAY);\n },\n);\n\n\nexport const clear = When('I clear {locator}', async function (selector) {\n const el = await locator(this.page, selector);\n await setFieldValue(el, null, { timeout: TIMEOUT });\n await sleep(DELAY);\n});\n\nexport const check = When(\n 'I {check|uncheck} {locator}',\n async function (check: boolean, selector: string) {\n const el = await locator(this.page, selector);\n await setFieldValue(el, check, { timeout: TIMEOUT });\n await sleep(DELAY);\n },\n 'For checkbox input or switch component',\n);\n\nexport const focus = When('I {focus|blur} {locator}', async function (focus: boolean, selector: string) {\n const el = await locator(this.page, selector);\n\n if (focus) {\n await el.focus({ timeout: TIMEOUT });\n } else {\n await el.blur({ timeout: TIMEOUT });\n }\n});\n","import type { KeyCombo } from '@letsrunit/gherkin';\nimport { sleep } from '@letsrunit/utils';\nimport { When } from './wrappers';\n\nconst DELAY = 500;\n\nexport const press = When('I press {keys}', async function (combo: KeyCombo) {\n // Hold modifiers\n for (const m of combo.modifiers) await this.page.keyboard.down(m);\n\n // Press final key\n await this.page.keyboard.press(combo.key);\n\n // Release modifiers (reverse order)\n for (const m of combo.modifiers.toReversed()) await this.page.keyboard.up(m);\n\n await sleep(DELAY);\n});\n\nexport const type = When('I type {string}', async function (value: string) {\n await this.page.keyboard.type(value, { delay: 200 });\n await sleep(DELAY);\n});\n","import { receiveMail, toEml } from '@letsrunit/mailbox';\nimport { asFilename, textToHtml } from '@letsrunit/utils';\nimport { Given, Then } from './wrappers';\n\nconst MAX_RECEIVE_WAIT = 120_000; // 2 minutes\n\nexport const view = Given(\n `I'm viewing an email sent to {string} with subject {string}`,\n async function (address: string, subject: string) {\n const emails = await receiveMail(address, { full: true, after: this.startTime, subject, limit: 1 });\n if (emails.length === 0) {\n throw new Error(`Did not receive an email with subject \"${subject}\"`);\n }\n\n const email = emails[0];\n\n await this.page.goto('about:blank', { waitUntil: 'load' });\n await this.page.setContent(email.html ?? textToHtml(email.text!), { waitUntil: 'domcontentloaded' });\n },\n);\n\nexport const receive = Then(\n 'I received an email sent to {string} with subject {string}',\n async function (address: string, subject: string) {\n const emails = await receiveMail(address, {\n after: this.startTime,\n full: true,\n subject,\n wait: true,\n timeout: MAX_RECEIVE_WAIT,\n limit: 1,\n });\n\n if (emails.length === 0) {\n throw new Error(`Did not receive an email with subject \"${subject}\"`);\n }\n\n const email = emails[0];\n\n this.attach(toEml(email), {\n mediaType: 'message/rfc822',\n fileName: asFilename(email.subject, 'eml'),\n });\n },\n);\n","import type { KeyCombo } from '@letsrunit/gherkin';\nimport { locator, waitAfterInteraction } from '@letsrunit/playwright';\nimport type { Locator } from '@playwright/test';\nimport { When } from './wrappers';\n\nconst TIMEOUT = 2500;\n\ntype MouseAction = 'click' | 'double-click' | 'right-click' | 'hover';\n\nasync function press(el: Locator, action: MouseAction) {\n if (action === 'hover') {\n await el.hover({ timeout: TIMEOUT });\n } else {\n await el.click({\n button: action === 'right-click' ? 'right' : 'left',\n clickCount: action === 'double-click' ? 2 : 1,\n timeout: TIMEOUT,\n });\n }\n}\n\nexport const click = When(\n 'I {click|double-click|right-click|hover} {locator}',\n async function (action: MouseAction, selector: string) {\n const prevUrl = this.page.url();\n\n const el = await locator(this.page, selector);\n await press(el, action);\n\n await waitAfterInteraction(this.page, el, { prevUrl });\n },\n);\n\nexport const clickHold = When(\n 'I {click|double-click|right-click|hover} {locator} while holding {keys}',\n async function (action: MouseAction, selector: string, combo: KeyCombo) {\n const prevUrl = this.page.url();\n\n const el = await locator(this.page, selector);\n const keys = [...combo.modifiers, combo.key];\n\n for (const m of keys) await this.page.keyboard.down(m);\n await press(el, action);\n for (const m of keys.reverse()) await this.page.keyboard.up(m);\n\n await waitAfterInteraction(this.page, el, { prevUrl });\n },\n);\n\nexport const scroll = When('I scroll {locator} into view', async function (selector: string) {\n const el = await locator(this.page, selector);\n await el.scrollIntoViewIfNeeded({ timeout: TIMEOUT });\n});\n","import { suppressInterferences, waitForIdle } from '@letsrunit/playwright';\nimport { eventually, pathRegexp, splitUrl } from '@letsrunit/utils';\nimport { expect } from '@playwright/test';\nimport { World } from '../types';\nimport { getLang } from '../utils/get-lang';\nimport { Given, Then, When } from './wrappers';\n\nasync function openPage(world: World, path: string): Promise<void> {\n const { page } = world;\n\n const result = await page.goto(path);\n expect(result?.status()).toBeLessThan(400);\n\n await waitForIdle(page);\n\n world.lang ??= (await getLang(page)) || undefined;\n}\n\nexport const navHome = Given(\"I'm on the homepage\", async function () {\n await openPage(this, '/');\n});\n\nexport const navPath = Given(\"I'm on page {string}\", async function (path: string) {\n await openPage(this, path);\n});\n\nexport const popupClosed = Given('all popups are closed', async function (){\n await suppressInterferences(this.page, { lang: this.lang?.code });\n});\n\nexport const assertPath = Then('I should be on page {string}', async function (expectedPath: string) {\n await eventually(async () => {\n const { path: actualPath } = splitUrl(this.page.url());\n\n if (expectedPath.includes(':')) {\n const { regexp, names } = pathRegexp(expectedPath);\n\n expect(actualPath, `Expected path ${actualPath} to match pattern ${expectedPath}`).toMatch(regexp);\n\n const match = actualPath.match(regexp);\n this.pathParams = Object.fromEntries(names.map((name, i) => [name, decodeURIComponent(match![i + 1])]));\n } else {\n expect(actualPath).toEqual(expectedPath);\n delete this.pathParams;\n }\n });\n});\n\nexport const back = When('I go back to the previous page', async function () {\n await this.page.goBack();\n await waitForIdle(this.page);\n});\n","import type { Snapshot } from '@letsrunit/playwright';\nimport { Page } from '@playwright/test';\nimport ISO6391 from 'iso-639-1';\nimport metascraper from 'metascraper';\nimport metascraperLang from 'metascraper-lang';\n\nconst scrapeLang = metascraper([metascraperLang()]);\n\nexport async function getLang(\n page: Pick<Page, 'content' | 'url'> | Snapshot,\n): Promise<{ code: string; name: string} | null> {\n const html = 'html' in page ? page.html : await page.content();\n const url = typeof page.url === 'function' ? page.url() : page.url;\n\n const { lang = null } = await scrapeLang({ html, url });\n if (!lang) return null;\n\n const code = lang.substring(0, 2);\n const name = ISO6391.getName(code) || code;\n\n return { code, name };\n}\n","import type { StepDefinition } from '../types';\nimport * as assert from './assert';\nimport * as clipboard from './clipboard';\nimport * as form from './form';\nimport * as keyboard from './keyboard';\nimport * as mailbox from './mailbox';\nimport * as mouse from './mouse';\nimport * as navigation from './navigation';\n\n// The order matters for the LLM\nexport const stepsDefinitions: StepDefinition[] = [\n ...Object.values(assert),\n ...Object.values(navigation),\n ...Object.values(mouse),\n ...Object.values(form),\n ...Object.values(keyboard),\n ...Object.values(mailbox),\n ...Object.values(clipboard),\n];\n"]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/define.js ADDED
@@ -0,0 +1,34 @@
1
+ import { typeDefinitions, stepsDefinitions } from './chunk-YT56PTQF.js';
2
+ import { defineParameterType, Given, When, Then, BeforeAll, Before, After } from '@cucumber/cucumber';
3
+ import { createFieldEngine, createDateEngine, browse } from '@letsrunit/playwright';
4
+ import { selectors, chromium } from '@playwright/test';
5
+ import { sanitizeStepDefinition } from '@letsrunit/gherkin';
6
+
7
+ for (const type of typeDefinitions) {
8
+ defineParameterType(type);
9
+ }
10
+ for (const step of stepsDefinitions) {
11
+ const def = step.type === "Given" ? Given : step.type === "When" ? When : Then;
12
+ def(sanitizeStepDefinition(step.expression), step.fn);
13
+ }
14
+ var selectorsRegistered = false;
15
+ BeforeAll(async function() {
16
+ if (selectorsRegistered) return;
17
+ try {
18
+ await selectors.register("field", createFieldEngine);
19
+ await selectors.register("date", createDateEngine);
20
+ selectorsRegistered = true;
21
+ } catch {
22
+ }
23
+ });
24
+ Before(async function() {
25
+ const browser = await chromium.launch({ headless: true });
26
+ this._browser = browser;
27
+ this.page = await browse(browser, this.parameters);
28
+ this.startTime = Date.now();
29
+ });
30
+ After(async function() {
31
+ await this._browser?.close();
32
+ });
33
+ //# sourceMappingURL=define.js.map
34
+ //# sourceMappingURL=define.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/define.ts"],"names":[],"mappings":";;;;;;AAOA,KAAA,MAAW,QAAQ,eAAA,EAAiB;AAClC,EAAA,mBAAA,CAAoB,IAAI,CAAA;AAC1B;AAEA,KAAA,MAAW,QAAQ,gBAAA,EAAkB;AACnC,EAAA,MAAM,GAAA,GAAM,KAAK,IAAA,KAAS,OAAA,GAAU,QAAQ,IAAA,CAAK,IAAA,KAAS,SAAS,IAAA,GAAO,IAAA;AAC1E,EAAA,GAAA,CAAI,sBAAA,CAAuB,IAAA,CAAK,UAAU,CAAA,EAAG,KAAK,EAAE,CAAA;AACtD;AAEA,IAAI,mBAAA,GAAsB,KAAA;AAE1B,SAAA,CAAU,iBAAkB;AAC1B,EAAA,IAAI,mBAAA,EAAqB;AACzB,EAAA,IAAI;AACF,IAAA,MAAM,SAAA,CAAU,QAAA,CAAS,OAAA,EAAS,iBAAiB,CAAA;AACnD,IAAA,MAAM,SAAA,CAAU,QAAA,CAAS,MAAA,EAAQ,gBAAgB,CAAA;AACjD,IAAA,mBAAA,GAAsB,IAAA;AAAA,EACxB,CAAA,CAAA,MAAQ;AAAA,EAAC;AACX,CAAC,CAAA;AAED,MAAA,CAAO,iBAAkB;AACvB,EAAA,MAAM,UAAU,MAAM,QAAA,CAAS,OAAO,EAAE,QAAA,EAAU,MAAM,CAAA;AACxD,EAAA,IAAA,CAAK,QAAA,GAAW,OAAA;AAChB,EAAA,IAAA,CAAK,IAAA,GAAO,MAAM,MAAA,CAAO,OAAA,EAAS,KAAK,UAAU,CAAA;AACjD,EAAA,IAAA,CAAK,SAAA,GAAY,KAAK,GAAA,EAAI;AAC5B,CAAC,CAAA;AAED,KAAA,CAAM,iBAAkB;AACtB,EAAA,MAAM,IAAA,CAAK,UAAU,KAAA,EAAM;AAC7B,CAAC,CAAA","file":"define.js","sourcesContent":["import { After, Before, BeforeAll, defineParameterType, Given, Then, When } from '@cucumber/cucumber';\nimport { browse, createDateEngine, createFieldEngine } from '@letsrunit/playwright';\nimport { chromium, selectors } from '@playwright/test';\nimport { typeDefinitions } from './parameters';\nimport { stepsDefinitions } from './steps';\nimport { sanitizeStepDefinition } from '@letsrunit/gherkin';\n\nfor (const type of typeDefinitions) {\n defineParameterType(type);\n}\n\nfor (const step of stepsDefinitions) {\n const def = step.type === 'Given' ? Given : step.type === 'When' ? When : Then;\n def(sanitizeStepDefinition(step.expression), step.fn);\n}\n\nlet selectorsRegistered = false;\n\nBeforeAll(async function () {\n if (selectorsRegistered) return;\n try {\n await selectors.register('field', createFieldEngine);\n await selectors.register('date', createDateEngine);\n selectorsRegistered = true;\n } catch {}\n});\n\nBefore(async function () {\n const browser = await chromium.launch({ headless: true });\n this._browser = browser;\n this.page = await browse(browser, this.parameters);\n this.startTime = Date.now();\n});\n\nAfter(async function () {\n await this._browser?.close();\n});\n"]}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import * as _letsrunit_utils from '@letsrunit/utils';
2
1
  import * as _letsrunit_gherkin from '@letsrunit/gherkin';
2
+ import * as _letsrunit_utils from '@letsrunit/utils';
3
3
  import { World as World$1 } from '@cucumber/cucumber';
4
4
  import { BrowserContextOptions, Page } from '@playwright/test';
5
5
  import { Readable } from 'node:stream';
6
6
 
7
- declare const typeDefinitions: (_letsrunit_gherkin.ParameterTypeDefinition<boolean> | _letsrunit_gherkin.ParameterTypeDefinition<_letsrunit_utils.Scalar | _letsrunit_utils.Scalar[]> | _letsrunit_gherkin.ParameterTypeDefinition<_letsrunit_gherkin.KeyCombo>)[];
7
+ declare const typeDefinitions: (_letsrunit_gherkin.ParameterTypeDefinition<_letsrunit_utils.Scalar | _letsrunit_utils.Scalar[]> | _letsrunit_gherkin.ParameterTypeDefinition<_letsrunit_gherkin.KeyCombo> | _letsrunit_gherkin.ParameterTypeDefinition<boolean>)[];
8
8
 
9
9
  interface World extends World$1<BrowserContextOptions> {
10
10
  page: Page;
package/dist/index.js CHANGED
@@ -1,339 +1,20 @@
1
- import { locatorParameter, valueParameter, keysParameter, booleanParameter, enumParameter } from '@letsrunit/gherkin';
2
- import { locator, setFieldValue, waitAfterInteraction, suppressInterferences, waitForIdle } from '@letsrunit/playwright';
3
- import { expect } from '@playwright/test';
4
- import { asFilename, eventually, splitUrl, pathRegexp, sleep, textToHtml } from '@letsrunit/utils';
5
- import { receiveMail, toEml } from '@letsrunit/mailbox';
6
- import ISO6391 from 'iso-639-1';
7
- import metascraper from 'metascraper';
8
- import metascraperLang from 'metascraper-lang';
9
-
10
- var __defProp = Object.defineProperty;
11
- var __export = (target, all) => {
12
- for (var name in all)
13
- __defProp(target, name, { get: all[name], enumerable: true });
14
- };
15
- var typeDefinitions = [
16
- locatorParameter(),
17
- valueParameter(),
18
- keysParameter(),
19
- booleanParameter("visible", "hidden"),
20
- booleanParameter("enabled", "disabled", /((?:en|dis)abled)/),
21
- booleanParameter("checked", "unchecked", /((?:un)?checked)/),
22
- booleanParameter("contains", "not contains", /((?:not )?contains)/),
23
- booleanParameter("check", "uncheck", /((?:un)?check)/),
24
- booleanParameter("focus", "blur"),
25
- enumParameter(["click", "double-click", "right-click", "hover"], /((?:double-|right-)?click|hover)/)
26
- ];
27
-
28
- // src/steps/assert.ts
29
- var assert_exports = {};
30
- __export(assert_exports, {
31
- contain: () => contain,
32
- see: () => see
33
- });
34
- function expectOrNot(actual, toBe) {
35
- return toBe ? expect(actual) : expect(actual).not;
36
- }
37
-
38
- // src/steps/wrappers.ts
39
- function Given(expression, fn, comment) {
40
- return { type: "Given", expression, fn, comment };
41
- }
42
- function When(expression, fn, comment) {
43
- return { type: "When", expression, fn, comment };
44
- }
45
- function Then(expression, fn, comment) {
46
- return { type: "Then", expression, fn, comment };
47
- }
48
-
49
- // src/steps/assert.ts
50
- var WAIT_TIMEOUT = 5e3;
51
- var see = Then(
52
- "The page {contains|not contains} {locator}",
53
- async function(visible, selector) {
54
- const el = await locator(this.page, selector);
55
- await expectOrNot(el, visible).toBeVisible({ timeout: WAIT_TIMEOUT });
56
- }
57
- );
58
- var contain = Then(
59
- "{locator} {contains|not contains} {locator}",
60
- async function(selector, contain2, child) {
61
- const el = await locator(this.page, selector);
62
- const childElement = el.locator(child);
63
- await expectOrNot(childElement, contain2).toBeAttached({ timeout: WAIT_TIMEOUT });
64
- }
65
- );
66
-
67
- // src/steps/clipboard.ts
68
- var clipboard_exports = {};
69
- __export(clipboard_exports, {
70
- copy: () => copy,
71
- paste: () => paste
72
- });
73
- var TIMEOUT = 500;
74
- async function copyInput(el) {
75
- try {
76
- return await el.inputValue();
77
- } catch {
78
- }
79
- }
80
- async function copyLink(el) {
81
- try {
82
- const tag = await el.evaluate((n) => n.tagName.toLowerCase());
83
- const href = tag === "a" ? await el.getAttribute("href") : null;
84
- if (href) {
85
- return href.startsWith("mailto:") ? href.replace(/^mailto:/i, "") : href;
86
- }
87
- } catch {
88
- }
89
- }
90
- async function copyText(el) {
91
- return await el.textContent() ?? null;
92
- }
93
- var copy = When("I copy {locator} to the clipboard", async function(selector) {
94
- const el = await locator(this.page, selector);
95
- let value = await copyInput(el) ?? await copyLink(el) ?? await copyText(el);
96
- this.clipboard = { value };
97
- });
98
- var paste = When("I paste from the clipboard into {locator}", async function(selector) {
99
- const el = await locator(this.page, selector);
100
- const value = this.clipboard?.value || "";
101
- await el.fill(String(value), { timeout: TIMEOUT });
102
- });
103
-
104
- // src/steps/form.ts
105
- var form_exports = {};
106
- __export(form_exports, {
107
- check: () => check,
108
- clear: () => clear,
109
- focus: () => focus,
110
- set: () => set,
111
- setRange: () => setRange
112
- });
113
- var TIMEOUT2 = 500;
114
- var DELAY = 500;
115
- var set = When("I set {locator} to {value}", async function(selector, value) {
116
- const el = await locator(this.page, selector);
117
- await setFieldValue(el, value, { timeout: TIMEOUT2 });
118
- await sleep(DELAY);
119
- });
120
- var setRange = When(
121
- "I set {locator} to range of {value} to {value}",
122
- async function(selector, from, to) {
123
- const el = await locator(this.page, selector);
124
- await setFieldValue(el, { from, to }, { timeout: TIMEOUT2 });
125
- await sleep(DELAY);
126
- }
127
- );
128
- var clear = When("I clear {locator}", async function(selector) {
129
- const el = await locator(this.page, selector);
130
- await setFieldValue(el, null, { timeout: TIMEOUT2 });
131
- await sleep(DELAY);
132
- });
133
- var check = When(
134
- "I {check|uncheck} {locator}",
135
- async function(check2, selector) {
136
- const el = await locator(this.page, selector);
137
- await setFieldValue(el, check2, { timeout: TIMEOUT2 });
138
- await sleep(DELAY);
139
- },
140
- "For checkbox input or switch component"
141
- );
142
- var focus = When("I {focus|blur} {locator}", async function(focus2, selector) {
143
- const el = await locator(this.page, selector);
144
- if (focus2) {
145
- await el.focus({ timeout: TIMEOUT2 });
146
- } else {
147
- await el.blur({ timeout: TIMEOUT2 });
148
- }
149
- });
150
-
151
- // src/steps/keyboard.ts
152
- var keyboard_exports = {};
153
- __export(keyboard_exports, {
154
- press: () => press,
155
- type: () => type
156
- });
157
- var DELAY2 = 500;
158
- var press = When("I press {keys}", async function(combo) {
159
- for (const m of combo.modifiers) await this.page.keyboard.down(m);
160
- await this.page.keyboard.press(combo.key);
161
- for (const m of combo.modifiers.toReversed()) await this.page.keyboard.up(m);
162
- await sleep(DELAY2);
163
- });
164
- var type = When("I type {string}", async function(value) {
165
- await this.page.keyboard.type(value, { delay: 200 });
166
- await sleep(DELAY2);
167
- });
168
-
169
- // src/steps/mailbox.ts
170
- var mailbox_exports = {};
171
- __export(mailbox_exports, {
172
- receive: () => receive,
173
- view: () => view
174
- });
175
- var MAX_RECEIVE_WAIT = 12e4;
176
- var view = Given(
177
- `I'm viewing an email sent to {string} with subject {string}`,
178
- async function(address, subject) {
179
- const emails = await receiveMail(address, { full: true, after: this.startTime, subject, limit: 1 });
180
- if (emails.length === 0) {
181
- throw new Error(`Did not receive an email with subject "${subject}"`);
182
- }
183
- const email = emails[0];
184
- await this.page.goto("about:blank", { waitUntil: "load" });
185
- await this.page.setContent(email.html ?? textToHtml(email.text), { waitUntil: "domcontentloaded" });
186
- }
187
- );
188
- var receive = Then(
189
- "I received an email sent to {string} with subject {string}",
190
- async function(address, subject) {
191
- const emails = await receiveMail(address, {
192
- after: this.startTime,
193
- full: true,
194
- subject,
195
- wait: true,
196
- timeout: MAX_RECEIVE_WAIT,
197
- limit: 1
198
- });
199
- if (emails.length === 0) {
200
- throw new Error(`Did not receive an email with subject "${subject}"`);
201
- }
202
- const email = emails[0];
203
- this.attach(toEml(email), {
204
- mediaType: "message/rfc822",
205
- fileName: asFilename(email.subject, "eml")
206
- });
207
- }
208
- );
209
-
210
- // src/steps/mouse.ts
211
- var mouse_exports = {};
212
- __export(mouse_exports, {
213
- click: () => click,
214
- clickHold: () => clickHold,
215
- scroll: () => scroll
216
- });
217
- var TIMEOUT3 = 2500;
218
- async function press2(el, action) {
219
- if (action === "hover") {
220
- await el.hover({ timeout: TIMEOUT3 });
221
- } else {
222
- await el.click({
223
- button: action === "right-click" ? "right" : "left",
224
- clickCount: action === "double-click" ? 2 : 1,
225
- timeout: TIMEOUT3
226
- });
227
- }
228
- }
229
- var click = When(
230
- "I {click|double-click|right-click|hover} {locator}",
231
- async function(action, selector) {
232
- const prevUrl = this.page.url();
233
- const el = await locator(this.page, selector);
234
- await press2(el, action);
235
- await waitAfterInteraction(this.page, el, { prevUrl });
236
- }
237
- );
238
- var clickHold = When(
239
- "I {click|double-click|right-click|hover} {locator} while holding {keys}",
240
- async function(action, selector, combo) {
241
- const prevUrl = this.page.url();
242
- const el = await locator(this.page, selector);
243
- const keys = [...combo.modifiers, combo.key];
244
- for (const m of keys) await this.page.keyboard.down(m);
245
- await press2(el, action);
246
- for (const m of keys.reverse()) await this.page.keyboard.up(m);
247
- await waitAfterInteraction(this.page, el, { prevUrl });
248
- }
249
- );
250
- var scroll = When("I scroll {locator} into view", async function(selector) {
251
- const el = await locator(this.page, selector);
252
- await el.scrollIntoViewIfNeeded({ timeout: TIMEOUT3 });
253
- });
254
-
255
- // src/steps/navigation.ts
256
- var navigation_exports = {};
257
- __export(navigation_exports, {
258
- assertPath: () => assertPath,
259
- back: () => back,
260
- navHome: () => navHome,
261
- navPath: () => navPath,
262
- popupClosed: () => popupClosed
263
- });
264
- var scrapeLang = metascraper([metascraperLang()]);
265
- async function getLang(page) {
266
- const html = "html" in page ? page.html : await page.content();
267
- const url = typeof page.url === "function" ? page.url() : page.url;
268
- const { lang = null } = await scrapeLang({ html, url });
269
- if (!lang) return null;
270
- const code = lang.substring(0, 2);
271
- const name = ISO6391.getName(code) || code;
272
- return { code, name };
273
- }
274
-
275
- // src/steps/navigation.ts
276
- async function openPage(world, path) {
277
- const { page } = world;
278
- const result = await page.goto(path);
279
- expect(result?.status()).toBeLessThan(400);
280
- await waitForIdle(page);
281
- world.lang ??= await getLang(page) || void 0;
282
- }
283
- var navHome = Given("I'm on the homepage", async function() {
284
- await openPage(this, "/");
285
- });
286
- var navPath = Given("I'm on page {string}", async function(path) {
287
- await openPage(this, path);
288
- });
289
- var popupClosed = Given("all popups are closed", async function() {
290
- await suppressInterferences(this.page, { lang: this.lang?.code });
291
- });
292
- var assertPath = Then("I should be on page {string}", async function(expectedPath) {
293
- await eventually(async () => {
294
- const { path: actualPath } = splitUrl(this.page.url());
295
- if (expectedPath.includes(":")) {
296
- const { regexp, names } = pathRegexp(expectedPath);
297
- expect(actualPath, `Expected path ${actualPath} to match pattern ${expectedPath}`).toMatch(regexp);
298
- const match = actualPath.match(regexp);
299
- this.pathParams = Object.fromEntries(names.map((name, i) => [name, decodeURIComponent(match[i + 1])]));
300
- } else {
301
- expect(actualPath).toEqual(expectedPath);
302
- delete this.pathParams;
303
- }
304
- });
305
- });
306
- var back = When("I go back to the previous page", async function() {
307
- await this.page.goBack();
308
- await waitForIdle(this.page);
309
- });
310
-
311
- // src/steps/index.ts
312
- var stepsDefinitions = [
313
- ...Object.values(assert_exports),
314
- ...Object.values(navigation_exports),
315
- ...Object.values(mouse_exports),
316
- ...Object.values(form_exports),
317
- ...Object.values(keyboard_exports),
318
- ...Object.values(mailbox_exports),
319
- ...Object.values(clipboard_exports)
320
- ];
1
+ export { stepsDefinitions, typeDefinitions } from './chunk-YT56PTQF.js';
321
2
 
322
3
  // src/utils/file.ts
323
4
  function toFile(data, options) {
324
5
  const opt = typeof options === "string" ? { mediaType: options } : options ?? { mediaType: "application/octet-stream" };
325
- const type2 = opt.mediaType || "application/octet-stream";
6
+ const type = opt.mediaType || "application/octet-stream";
326
7
  const name = opt.fileName || `attachment-${Date.now()}`;
327
8
  if (isReadable(data)) {
328
9
  throw new Error("toFile does not support Readable streams; provide Buffer or string");
329
10
  }
330
11
  const part = typeof data === "string" ? data : new Uint8Array(data);
331
- return new File([part], name, { type: type2 });
12
+ return new File([part], name, { type });
332
13
  }
333
14
  function isReadable(value) {
334
15
  return !!value && typeof value === "object" && (typeof value.pipe === "function" || typeof value.read === "function");
335
16
  }
336
17
 
337
- export { stepsDefinitions, toFile, typeDefinitions };
18
+ export { toFile };
338
19
  //# sourceMappingURL=index.js.map
339
20
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/parameters.ts","../src/steps/assert.ts","../src/utils/test-helpers.ts","../src/steps/wrappers.ts","../src/steps/clipboard.ts","../src/steps/form.ts","../src/steps/keyboard.ts","../src/steps/mailbox.ts","../src/steps/mouse.ts","../src/steps/navigation.ts","../src/utils/get-lang.ts","../src/steps/index.ts","../src/utils/file.ts"],"names":["contain","locator","TIMEOUT","check","focus","DELAY","sleep","press","expect","type"],"mappings":"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","file":"index.js","sourcesContent":["import { booleanParameter, enumParameter, keysParameter, locatorParameter, valueParameter } from '@letsrunit/gherkin';\n\nexport const typeDefinitions = [\n locatorParameter(),\n valueParameter(),\n keysParameter(),\n\n booleanParameter('visible', 'hidden'),\n booleanParameter('enabled', 'disabled', /((?:en|dis)abled)/),\n booleanParameter('checked', 'unchecked', /((?:un)?checked)/),\n booleanParameter('contains', 'not contains', /((?:not )?contains)/),\n\n booleanParameter('check', 'uncheck', /((?:un)?check)/),\n booleanParameter('focus', 'blur'),\n\n enumParameter(['click', 'double-click', 'right-click', 'hover'], /((?:double-|right-)?click|hover)/),\n];\n","import { locator } from '@letsrunit/playwright';\nimport { expectOrNot } from '../utils/test-helpers';\nimport { Then } from './wrappers';\n\nconst WAIT_TIMEOUT = 5000;\n\nexport const see = Then(\n 'The page {contains|not contains} {locator}',\n async function (visible: boolean, selector: string) {\n const el = await locator(this.page, selector);\n await expectOrNot(el, visible).toBeVisible({ timeout: WAIT_TIMEOUT });\n },\n);\n\nexport const contain = Then(\n '{locator} {contains|not contains} {locator}',\n async function (selector: string, contain: boolean, child: string) {\n const el = await locator(this.page, selector);\n const childElement = el.locator(child);\n await expectOrNot(childElement, contain).toBeAttached({ timeout: WAIT_TIMEOUT });\n },\n);\n","import { expect, type Expect } from '@playwright/test';\n\nexport function expectOrNot<T>(\n actual: T,\n toBe: boolean\n): ReturnType<Expect<T>> | ReturnType<ReturnType<Expect<T>>['not']> {\n return toBe ? expect(actual) : expect(actual).not;\n}\n","import type { StepDefinition, StepHandler } from '../types';\n\nexport function Given(expression: string | RegExp, fn: StepHandler, comment?: string): StepDefinition {\n return { type: 'Given', expression, fn, comment };\n}\n\nexport function When(expression: string | RegExp, fn: StepHandler, comment?: string): StepDefinition {\n return { type: 'When', expression, fn, comment };\n}\n\nexport function Then(expression: string | RegExp, fn: StepHandler, comment?: string): StepDefinition {\n return { type: 'Then', expression, fn, comment };\n}\n","import { locator } from '@letsrunit/playwright';\nimport type { Locator } from '@playwright/test';\nimport { When } from './wrappers';\n\nconst TIMEOUT = 500;\n\nasync function copyInput(el: Locator): Promise<string | undefined> {\n try {\n return await el.inputValue();\n } catch {}\n}\n\nasync function copyLink(el: Locator): Promise<string | undefined> {\n try {\n const tag = await el.evaluate<string>((n: Element) => n.tagName.toLowerCase());\n const href = tag === 'a' ? await el.getAttribute('href') : null;\n if (href) {\n return href.startsWith('mailto:') ? href.replace(/^mailto:/i, '') : href;\n }\n } catch {}\n}\n\nasync function copyText(el: Locator): Promise<string | null> {\n return (await el.textContent()) ?? null;\n}\n\nexport const copy = When('I copy {locator} to the clipboard', async function (selector: string) {\n const el = await locator(this.page, selector);\n let value = (await copyInput(el)) ?? (await copyLink(el)) ?? (await copyText(el));\n\n this.clipboard = { value };\n});\n\nexport const paste = When('I paste from the clipboard into {locator}', async function (selector: string) {\n const el = await locator(this.page, selector);\n const value = this.clipboard?.value || '';\n\n await el.fill(String(value), { timeout: TIMEOUT });\n});\n","import { locator, setFieldValue } from '@letsrunit/playwright';\nimport { type Scalar, sleep } from '@letsrunit/utils';\nimport { When } from './wrappers';\n\nconst TIMEOUT = 500;\nconst DELAY = 500;\n\nexport const set = When('I set {locator} to {value}', async function (selector: string, value: Scalar | Scalar[]) {\n const el = await locator(this.page, selector);\n await setFieldValue(el, value, { timeout: TIMEOUT });\n await sleep(DELAY);\n});\n\nexport const setRange = When(\n 'I set {locator} to range of {value} to {value}',\n async function (selector: string, from: Scalar, to: Scalar) {\n const el = await locator(this.page, selector);\n await setFieldValue(el, { from, to }, { timeout: TIMEOUT });\n await sleep(DELAY);\n },\n);\n\n\nexport const clear = When('I clear {locator}', async function (selector) {\n const el = await locator(this.page, selector);\n await setFieldValue(el, null, { timeout: TIMEOUT });\n await sleep(DELAY);\n});\n\nexport const check = When(\n 'I {check|uncheck} {locator}',\n async function (check: boolean, selector: string) {\n const el = await locator(this.page, selector);\n await setFieldValue(el, check, { timeout: TIMEOUT });\n await sleep(DELAY);\n },\n 'For checkbox input or switch component',\n);\n\nexport const focus = When('I {focus|blur} {locator}', async function (focus: boolean, selector: string) {\n const el = await locator(this.page, selector);\n\n if (focus) {\n await el.focus({ timeout: TIMEOUT });\n } else {\n await el.blur({ timeout: TIMEOUT });\n }\n});\n","import type { KeyCombo } from '@letsrunit/gherkin';\nimport { sleep } from '@letsrunit/utils';\nimport { When } from './wrappers';\n\nconst DELAY = 500;\n\nexport const press = When('I press {keys}', async function (combo: KeyCombo) {\n // Hold modifiers\n for (const m of combo.modifiers) await this.page.keyboard.down(m);\n\n // Press final key\n await this.page.keyboard.press(combo.key);\n\n // Release modifiers (reverse order)\n for (const m of combo.modifiers.toReversed()) await this.page.keyboard.up(m);\n\n await sleep(DELAY);\n});\n\nexport const type = When('I type {string}', async function (value: string) {\n await this.page.keyboard.type(value, { delay: 200 });\n await sleep(DELAY);\n});\n","import { receiveMail, toEml } from '@letsrunit/mailbox';\nimport { asFilename, textToHtml } from '@letsrunit/utils';\nimport { Given, Then } from './wrappers';\n\nconst MAX_RECEIVE_WAIT = 120_000; // 2 minutes\n\nexport const view = Given(\n `I'm viewing an email sent to {string} with subject {string}`,\n async function (address: string, subject: string) {\n const emails = await receiveMail(address, { full: true, after: this.startTime, subject, limit: 1 });\n if (emails.length === 0) {\n throw new Error(`Did not receive an email with subject \"${subject}\"`);\n }\n\n const email = emails[0];\n\n await this.page.goto('about:blank', { waitUntil: 'load' });\n await this.page.setContent(email.html ?? textToHtml(email.text!), { waitUntil: 'domcontentloaded' });\n },\n);\n\nexport const receive = Then(\n 'I received an email sent to {string} with subject {string}',\n async function (address: string, subject: string) {\n const emails = await receiveMail(address, {\n after: this.startTime,\n full: true,\n subject,\n wait: true,\n timeout: MAX_RECEIVE_WAIT,\n limit: 1,\n });\n\n if (emails.length === 0) {\n throw new Error(`Did not receive an email with subject \"${subject}\"`);\n }\n\n const email = emails[0];\n\n this.attach(toEml(email), {\n mediaType: 'message/rfc822',\n fileName: asFilename(email.subject, 'eml'),\n });\n },\n);\n","import type { KeyCombo } from '@letsrunit/gherkin';\nimport { locator, waitAfterInteraction } from '@letsrunit/playwright';\nimport type { Locator } from '@playwright/test';\nimport { When } from './wrappers';\n\nconst TIMEOUT = 2500;\n\ntype MouseAction = 'click' | 'double-click' | 'right-click' | 'hover';\n\nasync function press(el: Locator, action: MouseAction) {\n if (action === 'hover') {\n await el.hover({ timeout: TIMEOUT });\n } else {\n await el.click({\n button: action === 'right-click' ? 'right' : 'left',\n clickCount: action === 'double-click' ? 2 : 1,\n timeout: TIMEOUT,\n });\n }\n}\n\nexport const click = When(\n 'I {click|double-click|right-click|hover} {locator}',\n async function (action: MouseAction, selector: string) {\n const prevUrl = this.page.url();\n\n const el = await locator(this.page, selector);\n await press(el, action);\n\n await waitAfterInteraction(this.page, el, { prevUrl });\n },\n);\n\nexport const clickHold = When(\n 'I {click|double-click|right-click|hover} {locator} while holding {keys}',\n async function (action: MouseAction, selector: string, combo: KeyCombo) {\n const prevUrl = this.page.url();\n\n const el = await locator(this.page, selector);\n const keys = [...combo.modifiers, combo.key];\n\n for (const m of keys) await this.page.keyboard.down(m);\n await press(el, action);\n for (const m of keys.reverse()) await this.page.keyboard.up(m);\n\n await waitAfterInteraction(this.page, el, { prevUrl });\n },\n);\n\nexport const scroll = When('I scroll {locator} into view', async function (selector: string) {\n const el = await locator(this.page, selector);\n await el.scrollIntoViewIfNeeded({ timeout: TIMEOUT });\n});\n","import { suppressInterferences, waitForIdle } from '@letsrunit/playwright';\nimport { eventually, pathRegexp, splitUrl } from '@letsrunit/utils';\nimport { expect } from '@playwright/test';\nimport { World } from '../types';\nimport { getLang } from '../utils/get-lang';\nimport { Given, Then, When } from './wrappers';\n\nasync function openPage(world: World, path: string): Promise<void> {\n const { page } = world;\n\n const result = await page.goto(path);\n expect(result?.status()).toBeLessThan(400);\n\n await waitForIdle(page);\n\n world.lang ??= (await getLang(page)) || undefined;\n}\n\nexport const navHome = Given(\"I'm on the homepage\", async function () {\n await openPage(this, '/');\n});\n\nexport const navPath = Given(\"I'm on page {string}\", async function (path: string) {\n await openPage(this, path);\n});\n\nexport const popupClosed = Given('all popups are closed', async function (){\n await suppressInterferences(this.page, { lang: this.lang?.code });\n});\n\nexport const assertPath = Then('I should be on page {string}', async function (expectedPath: string) {\n await eventually(async () => {\n const { path: actualPath } = splitUrl(this.page.url());\n\n if (expectedPath.includes(':')) {\n const { regexp, names } = pathRegexp(expectedPath);\n\n expect(actualPath, `Expected path ${actualPath} to match pattern ${expectedPath}`).toMatch(regexp);\n\n const match = actualPath.match(regexp);\n this.pathParams = Object.fromEntries(names.map((name, i) => [name, decodeURIComponent(match![i + 1])]));\n } else {\n expect(actualPath).toEqual(expectedPath);\n delete this.pathParams;\n }\n });\n});\n\nexport const back = When('I go back to the previous page', async function () {\n await this.page.goBack();\n await waitForIdle(this.page);\n});\n","import type { Snapshot } from '@letsrunit/playwright';\nimport { Page } from '@playwright/test';\nimport ISO6391 from 'iso-639-1';\nimport metascraper from 'metascraper';\nimport metascraperLang from 'metascraper-lang';\n\nconst scrapeLang = metascraper([metascraperLang()]);\n\nexport async function getLang(\n page: Pick<Page, 'content' | 'url'> | Snapshot,\n): Promise<{ code: string; name: string} | null> {\n const html = 'html' in page ? page.html : await page.content();\n const url = typeof page.url === 'function' ? page.url() : page.url;\n\n const { lang = null } = await scrapeLang({ html, url });\n if (!lang) return null;\n\n const code = lang.substring(0, 2);\n const name = ISO6391.getName(code) || code;\n\n return { code, name };\n}\n","import type { StepDefinition } from '../types';\nimport * as assert from './assert';\nimport * as clipboard from './clipboard';\nimport * as form from './form';\nimport * as keyboard from './keyboard';\nimport * as mailbox from './mailbox';\nimport * as mouse from './mouse';\nimport * as navigation from './navigation';\n\n// The order matters for the LLM\nexport const stepsDefinitions: StepDefinition[] = [\n ...Object.values(assert),\n ...Object.values(navigation),\n ...Object.values(mouse),\n ...Object.values(form),\n ...Object.values(keyboard),\n ...Object.values(mailbox),\n ...Object.values(clipboard),\n];\n","import type { Readable } from 'node:stream';\n\ntype AttachData = string | Buffer | Readable;\ntype AttachOptions = string | {\n mediaType: string;\n fileName?: string;\n};\n\nexport function toFile(data: AttachData, options?: AttachOptions): File {\n const opt =\n typeof options === 'string'\n ? { mediaType: options }\n : options ?? { mediaType: 'application/octet-stream' };\n\n const type = opt.mediaType || 'application/octet-stream';\n const name = opt.fileName || `attachment-${Date.now()}`;\n\n if (isReadable(data)) {\n throw new Error('toFile does not support Readable streams; provide Buffer or string');\n }\n\n const part = typeof data === 'string' ? data : new Uint8Array(data);\n\n return new File([part], name, { type });\n}\n\nfunction isReadable(value: unknown): value is Readable {\n return (\n !!value &&\n typeof value === 'object' &&\n (typeof (value as any).pipe === 'function' || typeof (value as any).read === 'function')\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/utils/file.ts"],"names":[],"mappings":";;;AAQO,SAAS,MAAA,CAAO,MAAkB,OAAA,EAA+B;AACtE,EAAA,MAAM,GAAA,GACJ,OAAO,OAAA,KAAY,QAAA,GACf,EAAE,SAAA,EAAW,OAAA,EAAQ,GACrB,OAAA,IAAW,EAAE,SAAA,EAAW,0BAAA,EAA2B;AAEzD,EAAA,MAAM,IAAA,GAAO,IAAI,SAAA,IAAa,0BAAA;AAC9B,EAAA,MAAM,OAAO,GAAA,CAAI,QAAA,IAAY,CAAA,WAAA,EAAc,IAAA,CAAK,KAAK,CAAA,CAAA;AAErD,EAAA,IAAI,UAAA,CAAW,IAAI,CAAA,EAAG;AACpB,IAAA,MAAM,IAAI,MAAM,oEAAoE,CAAA;AAAA,EACtF;AAEA,EAAA,MAAM,OAAO,OAAO,IAAA,KAAS,WAAW,IAAA,GAAO,IAAI,WAAW,IAAI,CAAA;AAElE,EAAA,OAAO,IAAI,KAAK,CAAC,IAAI,GAAG,IAAA,EAAM,EAAE,MAAM,CAAA;AACxC;AAEA,SAAS,WAAW,KAAA,EAAmC;AACrD,EAAA,OACE,CAAC,CAAC,KAAA,IACF,OAAO,KAAA,KAAU,QAAA,KAChB,OAAQ,KAAA,CAAc,IAAA,KAAS,UAAA,IAAc,OAAQ,KAAA,CAAc,IAAA,KAAS,UAAA,CAAA;AAEjF","file":"index.js","sourcesContent":["import type { Readable } from 'node:stream';\n\ntype AttachData = string | Buffer | Readable;\ntype AttachOptions = string | {\n mediaType: string;\n fileName?: string;\n};\n\nexport function toFile(data: AttachData, options?: AttachOptions): File {\n const opt =\n typeof options === 'string'\n ? { mediaType: options }\n : options ?? { mediaType: 'application/octet-stream' };\n\n const type = opt.mediaType || 'application/octet-stream';\n const name = opt.fileName || `attachment-${Date.now()}`;\n\n if (isReadable(data)) {\n throw new Error('toFile does not support Readable streams; provide Buffer or string');\n }\n\n const part = typeof data === 'string' ? data : new Uint8Array(data);\n\n return new File([part], name, { type });\n}\n\nfunction isReadable(value: unknown): value is Readable {\n return (\n !!value &&\n typeof value === 'object' &&\n (typeof (value as any).pipe === 'function' || typeof (value as any).read === 'function')\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letsrunit/bdd",
3
- "version": "0.2.6",
3
+ "version": "0.3.2",
4
4
  "description": "BDD step definitions for browser automation with letsrunit",
5
5
  "keywords": [
6
6
  "testing",
@@ -29,6 +29,11 @@
29
29
  "types": "./dist/index.d.ts",
30
30
  "import": "./dist/index.js",
31
31
  "default": "./dist/index.js"
32
+ },
33
+ "./define": {
34
+ "types": "./dist/define.d.ts",
35
+ "import": "./dist/define.js",
36
+ "default": "./dist/define.js"
32
37
  }
33
38
  }
34
39
  },
@@ -47,10 +52,10 @@
47
52
  "packageManager": "yarn@4.10.3",
48
53
  "dependencies": {
49
54
  "@cucumber/cucumber-expressions": "^18.1.0",
50
- "@letsrunit/gherkin": "0.2.6",
51
- "@letsrunit/mailbox": "0.2.6",
52
- "@letsrunit/playwright": "0.2.6",
53
- "@letsrunit/utils": "0.2.6",
55
+ "@letsrunit/gherkin": "0.3.2",
56
+ "@letsrunit/mailbox": "0.3.2",
57
+ "@letsrunit/playwright": "0.3.2",
58
+ "@letsrunit/utils": "0.3.2",
54
59
  "@playwright/test": "^1.57.0",
55
60
  "iso-639-1": "^3.1.5",
56
61
  "metascraper": "^5.49.15",
@@ -70,6 +75,11 @@
70
75
  "types": "./dist/index.d.ts",
71
76
  "import": "./dist/index.js",
72
77
  "default": "./dist/index.js"
78
+ },
79
+ "./define": {
80
+ "types": "./dist/define.d.ts",
81
+ "import": "./dist/define.js",
82
+ "default": "./dist/define.js"
73
83
  }
74
84
  }
75
85
  }
package/src/define.ts CHANGED
@@ -1,4 +1,6 @@
1
- import { defineParameterType, Given, When, Then } from '@cucumber/cucumber';
1
+ import { After, Before, BeforeAll, defineParameterType, Given, Then, When } from '@cucumber/cucumber';
2
+ import { browse, createDateEngine, createFieldEngine } from '@letsrunit/playwright';
3
+ import { chromium, selectors } from '@playwright/test';
2
4
  import { typeDefinitions } from './parameters';
3
5
  import { stepsDefinitions } from './steps';
4
6
  import { sanitizeStepDefinition } from '@letsrunit/gherkin';
@@ -11,3 +13,25 @@ for (const step of stepsDefinitions) {
11
13
  const def = step.type === 'Given' ? Given : step.type === 'When' ? When : Then;
12
14
  def(sanitizeStepDefinition(step.expression), step.fn);
13
15
  }
16
+
17
+ let selectorsRegistered = false;
18
+
19
+ BeforeAll(async function () {
20
+ if (selectorsRegistered) return;
21
+ try {
22
+ await selectors.register('field', createFieldEngine);
23
+ await selectors.register('date', createDateEngine);
24
+ selectorsRegistered = true;
25
+ } catch {}
26
+ });
27
+
28
+ Before(async function () {
29
+ const browser = await chromium.launch({ headless: true });
30
+ this._browser = browser;
31
+ this.page = await browse(browser, this.parameters);
32
+ this.startTime = Date.now();
33
+ });
34
+
35
+ After(async function () {
36
+ await this._browser?.close();
37
+ });