@chrryai/waffles 1.1.78
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/LICENSE +664 -0
- package/README.md +235 -0
- package/dist/index.d.mts +107 -0
- package/dist/index.d.ts +107 -0
- package/dist/index.js +1764 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1733 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# ๐ง Waffles
|
|
2
|
+
|
|
3
|
+
**Production-ready Playwright testing utilities** โ Battle-tested helpers from [Vex](https://askvex.com)
|
|
4
|
+
|
|
5
|
+
## ๐ฏ Why Waffles?
|
|
6
|
+
|
|
7
|
+
Waffles provides a collection of battle-tested Playwright utilities that make E2E testing delightful. Born from real-world production testing at Vex, these helpers solve common testing challenges.
|
|
8
|
+
|
|
9
|
+
## โจ Features
|
|
10
|
+
|
|
11
|
+
- ๐ญ **Playwright-first** - Built specifically for Playwright
|
|
12
|
+
- ๐งช **Production-tested** - Used in Vex's extensive test suite
|
|
13
|
+
- ๐ฆ **Zero config** - Works out of the box
|
|
14
|
+
- ๐ฏ **TypeScript** - Full type safety
|
|
15
|
+
- ๐ **Lightweight** - Minimal dependencies
|
|
16
|
+
|
|
17
|
+
## ๐ฆ Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @askvex/waffles @playwright/test
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## ๐ Quick Start
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { test, expect } from "@playwright/test"
|
|
27
|
+
import {
|
|
28
|
+
wait,
|
|
29
|
+
simulateInputPaste,
|
|
30
|
+
waitForElement,
|
|
31
|
+
generateTestEmail,
|
|
32
|
+
} from "@askvex/waffles"
|
|
33
|
+
|
|
34
|
+
test("chat interaction", async ({ page }) => {
|
|
35
|
+
await page.goto("https://yourapp.com")
|
|
36
|
+
|
|
37
|
+
// Wait for chat to load
|
|
38
|
+
await waitForElement(page, '[data-testid="chat-textarea"]')
|
|
39
|
+
|
|
40
|
+
// Simulate pasting text
|
|
41
|
+
await simulateInputPaste(page, "Hello, AI!")
|
|
42
|
+
|
|
43
|
+
// Wait for response
|
|
44
|
+
await wait(1000)
|
|
45
|
+
|
|
46
|
+
// Assert
|
|
47
|
+
await expect(page.locator(".message")).toBeVisible()
|
|
48
|
+
})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## ๐ API Reference
|
|
52
|
+
|
|
53
|
+
### Timing Utilities
|
|
54
|
+
|
|
55
|
+
#### `wait(ms: number)`
|
|
56
|
+
|
|
57
|
+
Wait for a specified number of milliseconds.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
await wait(1000) // Wait 1 second
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### `waitForElement(page, selector, timeout?)`
|
|
64
|
+
|
|
65
|
+
Wait for an element to be visible.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
await waitForElement(page, ".loading-spinner", 5000)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### `waitForElementToDisappear(page, selector, timeout?)`
|
|
72
|
+
|
|
73
|
+
Wait for an element to disappear.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
await waitForElementToDisappear(page, ".loading-spinner")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Input Simulation
|
|
80
|
+
|
|
81
|
+
#### `simulateInputPaste(page, text, selector?)`
|
|
82
|
+
|
|
83
|
+
Simulate pasting text into a textarea.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
await simulateInputPaste(page, "Pasted content")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### `simulatePaste(page, text, buttonSelector?)`
|
|
90
|
+
|
|
91
|
+
Simulate pasting using clipboard API and clicking paste button.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
await simulatePaste(page, "Clipboard content")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Navigation
|
|
98
|
+
|
|
99
|
+
#### `getURL(options)`
|
|
100
|
+
|
|
101
|
+
Generate a URL with optional fingerprint for testing.
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
const url = getURL({
|
|
105
|
+
baseURL: "https://app.com",
|
|
106
|
+
path: "/chat",
|
|
107
|
+
isMember: true,
|
|
108
|
+
memberFingerprint: "abc-123",
|
|
109
|
+
})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### `scrollToBottom(page)`
|
|
113
|
+
|
|
114
|
+
Scroll to the bottom of the page.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
await scrollToBottom(page)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Utilities
|
|
121
|
+
|
|
122
|
+
#### `capitalizeFirstLetter(str: string)`
|
|
123
|
+
|
|
124
|
+
Capitalize the first letter of a string.
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
capitalizeFirstLetter("hello") // "Hello"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### `generateTestEmail(prefix?)`
|
|
131
|
+
|
|
132
|
+
Generate a unique test email.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
const email = generateTestEmail("user") // user-1234567890-abc123@test.com
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### `generateTestPassword(length?)`
|
|
139
|
+
|
|
140
|
+
Generate a random password for testing.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
const password = generateTestPassword(16)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Cleanup
|
|
147
|
+
|
|
148
|
+
#### `clearLocalStorage(page)`
|
|
149
|
+
|
|
150
|
+
Clear browser local storage.
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
await clearLocalStorage(page)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### `clearCookies(page)`
|
|
157
|
+
|
|
158
|
+
Clear browser cookies.
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
await clearCookies(page)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Screenshots
|
|
165
|
+
|
|
166
|
+
#### `takeScreenshot(page, name, fullPage?)`
|
|
167
|
+
|
|
168
|
+
Take a screenshot with a custom name.
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
await takeScreenshot(page, "error-state", true)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## ๐จ Real-World Examples
|
|
175
|
+
|
|
176
|
+
### Testing Chat Flow
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import { test } from "@playwright/test"
|
|
180
|
+
import { simulateInputPaste, waitForElement, wait } from "@askvex/waffles"
|
|
181
|
+
|
|
182
|
+
test("complete chat interaction", async ({ page }) => {
|
|
183
|
+
await page.goto("https://app.com/chat")
|
|
184
|
+
|
|
185
|
+
// Wait for chat to be ready
|
|
186
|
+
await waitForElement(page, '[data-testid="chat-textarea"]')
|
|
187
|
+
|
|
188
|
+
// Send message
|
|
189
|
+
await simulateInputPaste(page, "What's the weather?")
|
|
190
|
+
await page.click('[data-testid="send-button"]')
|
|
191
|
+
|
|
192
|
+
// Wait for AI response
|
|
193
|
+
await wait(2000)
|
|
194
|
+
await waitForElement(page, ".ai-message")
|
|
195
|
+
})
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Testing Authentication
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { test } from "@playwright/test"
|
|
202
|
+
import { generateTestEmail, generateTestPassword, wait } from "@askvex/waffles"
|
|
203
|
+
|
|
204
|
+
test("user registration", async ({ page }) => {
|
|
205
|
+
const email = generateTestEmail("newuser")
|
|
206
|
+
const password = generateTestPassword()
|
|
207
|
+
|
|
208
|
+
await page.goto("https://app.com/signup")
|
|
209
|
+
await page.fill('[name="email"]', email)
|
|
210
|
+
await page.fill('[name="password"]', password)
|
|
211
|
+
await page.click('button[type="submit"]')
|
|
212
|
+
|
|
213
|
+
await wait(1000)
|
|
214
|
+
await expect(page).toHaveURL(/dashboard/)
|
|
215
|
+
})
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## ๐ค Contributing
|
|
219
|
+
|
|
220
|
+
We welcome contributions! Waffles is extracted from Vex's production test suite, and we're always improving it.
|
|
221
|
+
|
|
222
|
+
## ๐ License
|
|
223
|
+
|
|
224
|
+
MIT ยฉ [AskVex](https://askvex.com)
|
|
225
|
+
|
|
226
|
+
## ๐ Links
|
|
227
|
+
|
|
228
|
+
- [GitHub](https://github.com/askvex/waffles)
|
|
229
|
+
- [npm](https://npmjs.com/package/@askvex/waffles)
|
|
230
|
+
- [Issues](https://github.com/askvex/waffles/issues)
|
|
231
|
+
- [Vex - Powered by Waffles](https://askvex.com)
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
**Built with โค๏ธ by the Vex team**
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Page, Browser } from '@playwright/test';
|
|
2
|
+
import { Page as Page$1 } from 'playwright';
|
|
3
|
+
|
|
4
|
+
declare const TEST_GUEST_FINGERPRINTS: string[];
|
|
5
|
+
declare const TEST_MEMBER_FINGERPRINTS: string[];
|
|
6
|
+
declare const TEST_MEMBER_EMAILS: string[];
|
|
7
|
+
declare const VEX_TEST_EMAIL: string;
|
|
8
|
+
declare const VEX_TEST_PASSWORD: string;
|
|
9
|
+
declare const VEX_TEST_FINGERPRINT: string | undefined;
|
|
10
|
+
declare const VEX_TEST_EMAIL_2: string;
|
|
11
|
+
declare const VEX_TEST_PASSWORD_2: string;
|
|
12
|
+
declare const VEX_TEST_FINGERPRINT_2: string | undefined;
|
|
13
|
+
declare const VEX_TEST_EMAIL_3: string;
|
|
14
|
+
declare const VEX_TEST_PASSWORD_3: string;
|
|
15
|
+
declare const VEX_TEST_FINGERPRINT_3: string | undefined;
|
|
16
|
+
declare const TEST_URL: string;
|
|
17
|
+
declare const LIVE_URL = "https://askvex.com";
|
|
18
|
+
declare const wait: (ms: number) => Promise<unknown>;
|
|
19
|
+
declare const isCI: string | undefined;
|
|
20
|
+
declare const getURL: ({ isLive, isMember, path, fingerprint, }?: {
|
|
21
|
+
isLive: boolean;
|
|
22
|
+
isMember?: boolean;
|
|
23
|
+
path?: string;
|
|
24
|
+
fingerprint?: string;
|
|
25
|
+
}) => string;
|
|
26
|
+
declare const simulateInputPaste: (page: Page, text: string) => Promise<void>;
|
|
27
|
+
declare const simulatePaste: (page: Page, text: string) => Promise<void>;
|
|
28
|
+
declare function capitalizeFirstLetter(val: string): string;
|
|
29
|
+
|
|
30
|
+
type modelName = "chatGPT" | "claude" | "deepSeek" | "gemini" | "flux";
|
|
31
|
+
declare const chat: ({ artifacts, page, isMember, isSubscriber, instruction, prompts, agentMessageTimeout, isNewChat, isLiveTest, threadId, creditsConsumed, bookmark, }: {
|
|
32
|
+
isSubscriber?: boolean;
|
|
33
|
+
artifacts?: {
|
|
34
|
+
text?: number;
|
|
35
|
+
paste?: number;
|
|
36
|
+
pdf?: number;
|
|
37
|
+
};
|
|
38
|
+
page: Page;
|
|
39
|
+
isMember?: boolean;
|
|
40
|
+
instruction?: string;
|
|
41
|
+
prompts?: {
|
|
42
|
+
debateAgent?: modelName;
|
|
43
|
+
stop?: boolean;
|
|
44
|
+
text: string;
|
|
45
|
+
instruction?: string;
|
|
46
|
+
deleteMessage?: boolean;
|
|
47
|
+
deleteAgentMessage?: boolean;
|
|
48
|
+
model?: modelName;
|
|
49
|
+
agentMessageTimeout?: number;
|
|
50
|
+
shouldFail?: boolean;
|
|
51
|
+
like?: boolean;
|
|
52
|
+
delete?: boolean;
|
|
53
|
+
webSearch?: boolean;
|
|
54
|
+
image?: number;
|
|
55
|
+
video?: number;
|
|
56
|
+
audio?: number;
|
|
57
|
+
paste?: number;
|
|
58
|
+
pdf?: number;
|
|
59
|
+
mix?: {
|
|
60
|
+
image?: number;
|
|
61
|
+
video?: number;
|
|
62
|
+
audio?: number;
|
|
63
|
+
paste?: number;
|
|
64
|
+
pdf?: number;
|
|
65
|
+
};
|
|
66
|
+
}[];
|
|
67
|
+
agentMessageTimeout?: number;
|
|
68
|
+
isNewChat?: boolean;
|
|
69
|
+
isLiveTest?: boolean;
|
|
70
|
+
threadId?: string;
|
|
71
|
+
creditsConsumed?: number;
|
|
72
|
+
bookmark?: boolean;
|
|
73
|
+
}) => Promise<void>;
|
|
74
|
+
|
|
75
|
+
declare function collaboration({ page, isLive, isMember, browser, withShareLink, fingerprint, collaborate, }: {
|
|
76
|
+
page: Page$1;
|
|
77
|
+
isLive?: boolean;
|
|
78
|
+
isMember?: boolean;
|
|
79
|
+
browser: Browser;
|
|
80
|
+
withShareLink?: boolean;
|
|
81
|
+
collaborate?: string;
|
|
82
|
+
fingerprint?: string;
|
|
83
|
+
}): Promise<void>;
|
|
84
|
+
|
|
85
|
+
declare const limit: ({ page, isMember, isSubscriber, }: {
|
|
86
|
+
page: Page;
|
|
87
|
+
isMember?: boolean;
|
|
88
|
+
isSubscriber?: boolean;
|
|
89
|
+
}) => Promise<void>;
|
|
90
|
+
|
|
91
|
+
declare const signIn: ({ page, isOpened, signOut, register, email, password, }: {
|
|
92
|
+
page: Page;
|
|
93
|
+
isOpened?: boolean;
|
|
94
|
+
signOut?: boolean;
|
|
95
|
+
register?: boolean;
|
|
96
|
+
email?: string;
|
|
97
|
+
password?: string;
|
|
98
|
+
}) => Promise<void>;
|
|
99
|
+
|
|
100
|
+
declare const thread: ({ page, isMember, bookmark, createChat, }: {
|
|
101
|
+
page: Page;
|
|
102
|
+
isMember?: boolean;
|
|
103
|
+
createChat?: boolean;
|
|
104
|
+
bookmark?: boolean;
|
|
105
|
+
}) => Promise<void>;
|
|
106
|
+
|
|
107
|
+
export { LIVE_URL, TEST_GUEST_FINGERPRINTS, TEST_MEMBER_EMAILS, TEST_MEMBER_FINGERPRINTS, TEST_URL, VEX_TEST_EMAIL, VEX_TEST_EMAIL_2, VEX_TEST_EMAIL_3, VEX_TEST_FINGERPRINT, VEX_TEST_FINGERPRINT_2, VEX_TEST_FINGERPRINT_3, VEX_TEST_PASSWORD, VEX_TEST_PASSWORD_2, VEX_TEST_PASSWORD_3, capitalizeFirstLetter, chat, collaboration, getURL, isCI, limit, type modelName, signIn, simulateInputPaste, simulatePaste, thread, wait };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Page, Browser } from '@playwright/test';
|
|
2
|
+
import { Page as Page$1 } from 'playwright';
|
|
3
|
+
|
|
4
|
+
declare const TEST_GUEST_FINGERPRINTS: string[];
|
|
5
|
+
declare const TEST_MEMBER_FINGERPRINTS: string[];
|
|
6
|
+
declare const TEST_MEMBER_EMAILS: string[];
|
|
7
|
+
declare const VEX_TEST_EMAIL: string;
|
|
8
|
+
declare const VEX_TEST_PASSWORD: string;
|
|
9
|
+
declare const VEX_TEST_FINGERPRINT: string | undefined;
|
|
10
|
+
declare const VEX_TEST_EMAIL_2: string;
|
|
11
|
+
declare const VEX_TEST_PASSWORD_2: string;
|
|
12
|
+
declare const VEX_TEST_FINGERPRINT_2: string | undefined;
|
|
13
|
+
declare const VEX_TEST_EMAIL_3: string;
|
|
14
|
+
declare const VEX_TEST_PASSWORD_3: string;
|
|
15
|
+
declare const VEX_TEST_FINGERPRINT_3: string | undefined;
|
|
16
|
+
declare const TEST_URL: string;
|
|
17
|
+
declare const LIVE_URL = "https://askvex.com";
|
|
18
|
+
declare const wait: (ms: number) => Promise<unknown>;
|
|
19
|
+
declare const isCI: string | undefined;
|
|
20
|
+
declare const getURL: ({ isLive, isMember, path, fingerprint, }?: {
|
|
21
|
+
isLive: boolean;
|
|
22
|
+
isMember?: boolean;
|
|
23
|
+
path?: string;
|
|
24
|
+
fingerprint?: string;
|
|
25
|
+
}) => string;
|
|
26
|
+
declare const simulateInputPaste: (page: Page, text: string) => Promise<void>;
|
|
27
|
+
declare const simulatePaste: (page: Page, text: string) => Promise<void>;
|
|
28
|
+
declare function capitalizeFirstLetter(val: string): string;
|
|
29
|
+
|
|
30
|
+
type modelName = "chatGPT" | "claude" | "deepSeek" | "gemini" | "flux";
|
|
31
|
+
declare const chat: ({ artifacts, page, isMember, isSubscriber, instruction, prompts, agentMessageTimeout, isNewChat, isLiveTest, threadId, creditsConsumed, bookmark, }: {
|
|
32
|
+
isSubscriber?: boolean;
|
|
33
|
+
artifacts?: {
|
|
34
|
+
text?: number;
|
|
35
|
+
paste?: number;
|
|
36
|
+
pdf?: number;
|
|
37
|
+
};
|
|
38
|
+
page: Page;
|
|
39
|
+
isMember?: boolean;
|
|
40
|
+
instruction?: string;
|
|
41
|
+
prompts?: {
|
|
42
|
+
debateAgent?: modelName;
|
|
43
|
+
stop?: boolean;
|
|
44
|
+
text: string;
|
|
45
|
+
instruction?: string;
|
|
46
|
+
deleteMessage?: boolean;
|
|
47
|
+
deleteAgentMessage?: boolean;
|
|
48
|
+
model?: modelName;
|
|
49
|
+
agentMessageTimeout?: number;
|
|
50
|
+
shouldFail?: boolean;
|
|
51
|
+
like?: boolean;
|
|
52
|
+
delete?: boolean;
|
|
53
|
+
webSearch?: boolean;
|
|
54
|
+
image?: number;
|
|
55
|
+
video?: number;
|
|
56
|
+
audio?: number;
|
|
57
|
+
paste?: number;
|
|
58
|
+
pdf?: number;
|
|
59
|
+
mix?: {
|
|
60
|
+
image?: number;
|
|
61
|
+
video?: number;
|
|
62
|
+
audio?: number;
|
|
63
|
+
paste?: number;
|
|
64
|
+
pdf?: number;
|
|
65
|
+
};
|
|
66
|
+
}[];
|
|
67
|
+
agentMessageTimeout?: number;
|
|
68
|
+
isNewChat?: boolean;
|
|
69
|
+
isLiveTest?: boolean;
|
|
70
|
+
threadId?: string;
|
|
71
|
+
creditsConsumed?: number;
|
|
72
|
+
bookmark?: boolean;
|
|
73
|
+
}) => Promise<void>;
|
|
74
|
+
|
|
75
|
+
declare function collaboration({ page, isLive, isMember, browser, withShareLink, fingerprint, collaborate, }: {
|
|
76
|
+
page: Page$1;
|
|
77
|
+
isLive?: boolean;
|
|
78
|
+
isMember?: boolean;
|
|
79
|
+
browser: Browser;
|
|
80
|
+
withShareLink?: boolean;
|
|
81
|
+
collaborate?: string;
|
|
82
|
+
fingerprint?: string;
|
|
83
|
+
}): Promise<void>;
|
|
84
|
+
|
|
85
|
+
declare const limit: ({ page, isMember, isSubscriber, }: {
|
|
86
|
+
page: Page;
|
|
87
|
+
isMember?: boolean;
|
|
88
|
+
isSubscriber?: boolean;
|
|
89
|
+
}) => Promise<void>;
|
|
90
|
+
|
|
91
|
+
declare const signIn: ({ page, isOpened, signOut, register, email, password, }: {
|
|
92
|
+
page: Page;
|
|
93
|
+
isOpened?: boolean;
|
|
94
|
+
signOut?: boolean;
|
|
95
|
+
register?: boolean;
|
|
96
|
+
email?: string;
|
|
97
|
+
password?: string;
|
|
98
|
+
}) => Promise<void>;
|
|
99
|
+
|
|
100
|
+
declare const thread: ({ page, isMember, bookmark, createChat, }: {
|
|
101
|
+
page: Page;
|
|
102
|
+
isMember?: boolean;
|
|
103
|
+
createChat?: boolean;
|
|
104
|
+
bookmark?: boolean;
|
|
105
|
+
}) => Promise<void>;
|
|
106
|
+
|
|
107
|
+
export { LIVE_URL, TEST_GUEST_FINGERPRINTS, TEST_MEMBER_EMAILS, TEST_MEMBER_FINGERPRINTS, TEST_URL, VEX_TEST_EMAIL, VEX_TEST_EMAIL_2, VEX_TEST_EMAIL_3, VEX_TEST_FINGERPRINT, VEX_TEST_FINGERPRINT_2, VEX_TEST_FINGERPRINT_3, VEX_TEST_PASSWORD, VEX_TEST_PASSWORD_2, VEX_TEST_PASSWORD_3, capitalizeFirstLetter, chat, collaboration, getURL, isCI, limit, type modelName, signIn, simulateInputPaste, simulatePaste, thread, wait };
|