cypress-validate 1.0.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/LICENSE +21 -0
- package/README.md +302 -0
- package/bin/cypress-validate.js +153 -0
- package/lib/commands/generate.js +200 -0
- package/lib/commands/info.js +68 -0
- package/lib/commands/install.js +78 -0
- package/lib/commands/open.js +43 -0
- package/lib/commands/record.js +62 -0
- package/lib/commands/run.js +138 -0
- package/lib/commands/screenshot.js +106 -0
- package/lib/commands/show-report.js +96 -0
- package/lib/commands/verify.js +45 -0
- package/lib/index.js +21 -0
- package/lib/utils/config-finder.js +46 -0
- package/lib/utils/logger.js +31 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 cypress-validate contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# cypress-validate
|
|
2
|
+
|
|
3
|
+
> A **Playwright-style CLI** for Cypress projects — run, open, generate, screenshot, report and more with a single unified command.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/cypress-validate)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 🚀 Quick Start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Run once without installing
|
|
15
|
+
npx cypress-validate --help
|
|
16
|
+
|
|
17
|
+
# Or install globally
|
|
18
|
+
npm install -g cypress-validate
|
|
19
|
+
cypress-validate --help
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 📋 Command Reference
|
|
25
|
+
|
|
26
|
+
### `run` — Run tests headlessly *(like `npx playwright test`)*
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx cypress-validate run [options]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
| Flag | Playwright Equivalent | Description |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `--spec <pattern>` | `playwright test file.spec.ts` | Spec file(s) to run |
|
|
35
|
+
| `--browser <name>` | `--project=chromium` | Browser: `chrome`, `firefox`, `edge`, `electron` |
|
|
36
|
+
| `--headed` | `--headed` | Run with visible browser window |
|
|
37
|
+
| `--grep <pattern>` | `--grep "pattern"` | Filter tests by title/pattern |
|
|
38
|
+
| `--workers <n>` | `--workers=4` | Parallel worker count (Cypress Cloud) |
|
|
39
|
+
| `--timeout <ms>` | `--timeout=10000` | Default timeout per test |
|
|
40
|
+
| `--retry <count>` | `--retries=2` | Number of retries on failure |
|
|
41
|
+
| `--reporter <name>` | `--reporter=html` | Reporter: `spec`, `mochawesome`, `json`, `junit` |
|
|
42
|
+
| `--last-failed` | `--last-failed` | Re-run only specs that failed last time |
|
|
43
|
+
| `--forbid-only` | `--forbid-only` | Fail if `.only()` is used in any test |
|
|
44
|
+
| `--record` | *(cloud)* | Record run to Cypress Cloud |
|
|
45
|
+
| `--key <key>` | *(cloud)* | Cypress Cloud record key |
|
|
46
|
+
| `--env <k=v>` | *(env vars)* | Set Cypress environment variables |
|
|
47
|
+
| `--config <json>` | *(config override)* | Override cypress.config.js values |
|
|
48
|
+
|
|
49
|
+
**Examples:**
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Run all tests
|
|
53
|
+
npx cypress-validate run
|
|
54
|
+
|
|
55
|
+
# Run specific spec file
|
|
56
|
+
npx cypress-validate run --spec cypress/e2e/login.cy.js
|
|
57
|
+
|
|
58
|
+
# Run in Chrome, headed, retry twice
|
|
59
|
+
npx cypress-validate run --browser chrome --headed --retry 2
|
|
60
|
+
|
|
61
|
+
# Filter tests by name
|
|
62
|
+
npx cypress-validate run --grep "should login"
|
|
63
|
+
|
|
64
|
+
# Run with HTML report
|
|
65
|
+
npx cypress-validate run --reporter mochawesome
|
|
66
|
+
|
|
67
|
+
# Re-run only failed tests from last run
|
|
68
|
+
npx cypress-validate run --last-failed
|
|
69
|
+
|
|
70
|
+
# Run all tests and forbid .only()
|
|
71
|
+
npx cypress-validate run --forbid-only
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### `open` — Interactive Test Runner *(like `npx playwright test --ui`)*
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npx cypress-validate open [options]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
| Flag | Description |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `--browser <name>` | Browser to open |
|
|
85
|
+
| `--spec <pattern>` | Open directly to a specific spec |
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx cypress-validate open
|
|
89
|
+
npx cypress-validate open --browser chrome
|
|
90
|
+
npx cypress-validate open --spec cypress/e2e/login.cy.js
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
### `install` — Install Cypress *(like `npx playwright install`)*
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npx cypress-validate install [options]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
| Flag | Description |
|
|
102
|
+
|---|---|
|
|
103
|
+
| `--version <ver>` | Install a specific Cypress version |
|
|
104
|
+
| `--global` | Install globally (`-g`) |
|
|
105
|
+
| `--force` | Force reinstall even if already installed |
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npx cypress-validate install
|
|
109
|
+
npx cypress-validate install --version 13.6.0
|
|
110
|
+
npx cypress-validate install --force
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
### `verify` — Verify installation *(like `npx playwright install --verify`)*
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npx cypress-validate verify [--force]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
### `info` — Print system info *(like `npx playwright --version` + system info)*
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npx cypress-validate info
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Displays: Node.js version, OS, Cypress version, available browsers.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### `generate` — Scaffold test files *(like `npx playwright codegen`)*
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npx cypress-validate generate [options]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
| Flag | Description |
|
|
140
|
+
|---|---|
|
|
141
|
+
| `--name <name>` | File name (without extension) |
|
|
142
|
+
| `--type <type>` | `spec` \| `fixture` \| `command` \| `page` |
|
|
143
|
+
| `--url <url>` | Base URL to include in the spec |
|
|
144
|
+
| `--typescript` | Generate `.cy.ts` TypeScript files |
|
|
145
|
+
| `--dir <dir>` | Custom output directory |
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Interactive (prompts wizard)
|
|
149
|
+
npx cypress-validate generate
|
|
150
|
+
|
|
151
|
+
# Non-interactive
|
|
152
|
+
npx cypress-validate generate --name login --type spec --url http://localhost:3000
|
|
153
|
+
npx cypress-validate generate --name user --type fixture
|
|
154
|
+
npx cypress-validate generate --name auth --type command
|
|
155
|
+
npx cypress-validate generate --name dashboard --type page
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Generated file types:
|
|
159
|
+
|
|
160
|
+
| Type | Output path |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `spec` | `cypress/e2e/<name>.cy.js` |
|
|
163
|
+
| `fixture` | `cypress/fixtures/<name>.json` |
|
|
164
|
+
| `command` | `cypress/support/commands/<name>.js` |
|
|
165
|
+
| `page` | `cypress/pages/<name>.page.js` |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
### `screenshot` — Take a URL screenshot *(like `npx playwright screenshot`)*
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
npx cypress-validate screenshot --url <url> [options]
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
| Flag | Description |
|
|
176
|
+
|---|---|
|
|
177
|
+
| `--url <url>` | **(required)** URL to screenshot |
|
|
178
|
+
| `--output <path>` | Output file (default: `cypress/screenshots/screenshot.png`) |
|
|
179
|
+
| `--browser <name>` | Browser to use |
|
|
180
|
+
| `--full-page` | Full-page screenshot |
|
|
181
|
+
| `--viewport <WxH>` | Viewport dimensions (default: `1280x720`) |
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
npx cypress-validate screenshot --url https://example.com
|
|
185
|
+
npx cypress-validate screenshot --url https://example.com --full-page --output ./my-shot.png
|
|
186
|
+
npx cypress-validate screenshot --url https://example.com --viewport 390x844
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### `show-report` — Open HTML report *(like `npx playwright show-report`)*
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
npx cypress-validate show-report [options]
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
| Flag | Description |
|
|
198
|
+
|---|---|
|
|
199
|
+
| `--path <dir>` | Report directory (default: `cypress/reports`) |
|
|
200
|
+
| `--serve` | Start a local HTTP server instead of opening |
|
|
201
|
+
| `--port <n>` | Port for `--serve` mode (default: `9323`) |
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# Open most recent report in browser
|
|
205
|
+
npx cypress-validate show-report
|
|
206
|
+
|
|
207
|
+
# Serve on a local port
|
|
208
|
+
npx cypress-validate show-report --serve --port 8080
|
|
209
|
+
|
|
210
|
+
# Point to a custom report path
|
|
211
|
+
npx cypress-validate show-report --path cypress/reports/mochawesome.html
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
### `record` — Record to Cypress Cloud
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
npx cypress-validate record [options]
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
| Flag | Description |
|
|
223
|
+
|---|---|
|
|
224
|
+
| `--key <key>` | Record key (or set `CYPRESS_RECORD_KEY` env) |
|
|
225
|
+
| `--spec <pattern>` | Spec pattern |
|
|
226
|
+
| `--browser <name>` | Browser to use |
|
|
227
|
+
| `--tag <tags>` | Run tags |
|
|
228
|
+
| `--group <name>` | Group name |
|
|
229
|
+
| `--parallel` | Enable parallel recording |
|
|
230
|
+
| `--ci-build-id <id>` | CI build ID for grouping |
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
npx cypress-validate record --key abc123
|
|
234
|
+
CYPRESS_RECORD_KEY=abc123 npx cypress-validate record --parallel --group "CI Run"
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 🧪 Full Playwright → cypress-validate Mapping
|
|
240
|
+
|
|
241
|
+
| Playwright CLI | cypress-validate |
|
|
242
|
+
|---|---|
|
|
243
|
+
| `npx playwright test` | `npx cypress-validate run` |
|
|
244
|
+
| `npx playwright test file.spec.ts` | `npx cypress-validate run --spec cypress/e2e/file.cy.js` |
|
|
245
|
+
| `npx playwright test --headed` | `npx cypress-validate run --headed` |
|
|
246
|
+
| `npx playwright test --project=chromium` | `npx cypress-validate run --browser chrome` |
|
|
247
|
+
| `npx playwright test --grep "pattern"` | `npx cypress-validate run --grep "pattern"` |
|
|
248
|
+
| `npx playwright test --workers=4` | `npx cypress-validate run --workers 4` |
|
|
249
|
+
| `npx playwright test --timeout=5000` | `npx cypress-validate run --timeout 5000` |
|
|
250
|
+
| `npx playwright test --retries=2` | `npx cypress-validate run --retry 2` |
|
|
251
|
+
| `npx playwright test --reporter=html` | `npx cypress-validate run --reporter mochawesome` |
|
|
252
|
+
| `npx playwright test --last-failed` | `npx cypress-validate run --last-failed` |
|
|
253
|
+
| `npx playwright test --forbid-only` | `npx cypress-validate run --forbid-only` |
|
|
254
|
+
| `npx playwright test --ui` | `npx cypress-validate open` |
|
|
255
|
+
| `npx playwright test --debug` | `npx cypress-validate open --spec <file>` |
|
|
256
|
+
| `npx playwright install` | `npx cypress-validate install` |
|
|
257
|
+
| `npx playwright show-report` | `npx cypress-validate show-report` |
|
|
258
|
+
| `npx playwright screenshot [url]` | `npx cypress-validate screenshot --url [url]` |
|
|
259
|
+
| `npx playwright codegen` | `npx cypress-validate generate` |
|
|
260
|
+
| `npx playwright info` | `npx cypress-validate info` |
|
|
261
|
+
| `npx playwright --version` | `npx cypress-validate --version` |
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## ⚙️ Programmatic API
|
|
266
|
+
|
|
267
|
+
```js
|
|
268
|
+
const { run, open, info, generate } = require('cypress-validate');
|
|
269
|
+
|
|
270
|
+
// Run tests programmatically
|
|
271
|
+
await run({ browser: 'chrome', headed: true, spec: 'cypress/e2e/login.cy.js' });
|
|
272
|
+
|
|
273
|
+
// Open interactive runner
|
|
274
|
+
await open({ browser: 'firefox' });
|
|
275
|
+
|
|
276
|
+
// Generate a spec file
|
|
277
|
+
await generate({ name: 'dashboard', type: 'spec', url: 'http://localhost:3000' });
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## 📦 Publishing to npm
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
# Login to npm
|
|
286
|
+
npm login
|
|
287
|
+
|
|
288
|
+
# Publish publicly
|
|
289
|
+
npm publish --access public
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## 🤝 Contributing
|
|
295
|
+
|
|
296
|
+
Issues and PRs welcome at [github.com/mvsaran/cypress-validate](https://github.com/mvsaran/cypress-validate).
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## 📄 License
|
|
301
|
+
|
|
302
|
+
[MIT](./LICENSE) © 2026 cypress-validate contributors
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { Command } = require('commander');
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const pkg = require('../package.json');
|
|
7
|
+
|
|
8
|
+
const program = new Command();
|
|
9
|
+
|
|
10
|
+
// ─── Banner ───────────────────────────────────────────────────────────────────
|
|
11
|
+
function printBanner() {
|
|
12
|
+
console.log(chalk.cyan.bold('\n ██████╗██╗ ██╗██████╗ ██████╗ ███████╗███████╗███████╗'));
|
|
13
|
+
console.log(chalk.cyan.bold(' ██╔════╝╚██╗ ██╔╝██╔══██╗██╔══██╗██╔════╝██╔════╝██╔════╝'));
|
|
14
|
+
console.log(chalk.cyan.bold(' ██║ ╚████╔╝ ██████╔╝██████╔╝█████╗ ███████╗███████╗'));
|
|
15
|
+
console.log(chalk.cyan.bold(' ██║ ╚██╔╝ ██╔═══╝ ██╔══██╗██╔══╝ ╚════██║╚════██║'));
|
|
16
|
+
console.log(chalk.cyan.bold(' ╚██████╗ ██║ ██║ ██║ ██║███████╗███████║███████║'));
|
|
17
|
+
console.log(chalk.cyan.bold(' ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝'));
|
|
18
|
+
console.log(chalk.gray(`\n validate — Playwright-style CLI for Cypress v${pkg.version}\n`));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ─── Program setup ────────────────────────────────────────────────────────────
|
|
22
|
+
program
|
|
23
|
+
.name('cypress-validate')
|
|
24
|
+
.description(chalk.white('A Playwright-style CLI for Cypress projects'))
|
|
25
|
+
.version(pkg.version, '-v, --version', 'Show version number')
|
|
26
|
+
.addHelpText('before', () => {
|
|
27
|
+
printBanner();
|
|
28
|
+
return '';
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// ─── run ──────────────────────────────────────────────────────────────────────
|
|
32
|
+
program
|
|
33
|
+
.command('run')
|
|
34
|
+
.description('Run Cypress tests in headless mode (like npx playwright test)')
|
|
35
|
+
.option('-s, --spec <pattern>', 'Spec file or glob pattern to run')
|
|
36
|
+
.option('-b, --browser <name>', 'Browser to run tests in (chrome, firefox, edge, electron)', 'electron')
|
|
37
|
+
.option('--headed', 'Run in headed (visible) browser mode')
|
|
38
|
+
.option('--headless', 'Run in headless mode (default)')
|
|
39
|
+
.option('-g, --grep <pattern>', 'Only run tests matching the grep pattern')
|
|
40
|
+
.option('-w, --workers <number>', 'Number of parallel test workers (requires Cypress Cloud)', '1')
|
|
41
|
+
.option('--timeout <ms>', 'Default timeout in milliseconds', '30000')
|
|
42
|
+
.option('--retry <count>', 'Number of times to retry a failing test', '0')
|
|
43
|
+
.option('--reporter <name>', 'Reporter to use (mochawesome, spec, json, junit)', 'spec')
|
|
44
|
+
.option('--record', 'Record test results to Cypress Cloud')
|
|
45
|
+
.option('--key <key>', 'Cypress Cloud record key (or set CYPRESS_RECORD_KEY env)')
|
|
46
|
+
.option('--last-failed', 'Re-run only the specs that failed in the last run')
|
|
47
|
+
.option('--forbid-only', 'Fail if test.only() is used anywhere in the suite')
|
|
48
|
+
.option('--config <json>', 'Override cypress.config.js values (JSON string)')
|
|
49
|
+
.option('--env <key=value>', 'Set environment variables (e.g. --env baseUrl=http://localhost)')
|
|
50
|
+
.option('--tag <tags>', 'Associate tags with this run (Cypress Cloud)')
|
|
51
|
+
.option('-p, --port <number>', 'Override default Cypress port')
|
|
52
|
+
.action((opts) => {
|
|
53
|
+
require('../lib/commands/run')(opts);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// ─── open ─────────────────────────────────────────────────────────────────────
|
|
57
|
+
program
|
|
58
|
+
.command('open')
|
|
59
|
+
.description('Open Cypress interactive Test Runner (like npx playwright test --ui)')
|
|
60
|
+
.option('-b, --browser <name>', 'Browser to open (chrome, firefox, edge, electron)')
|
|
61
|
+
.option('-s, --spec <pattern>', 'Spec file to open directly')
|
|
62
|
+
.option('--config <json>', 'Override cypress.config.js values (JSON string)')
|
|
63
|
+
.option('--env <key=value>', 'Set environment variables')
|
|
64
|
+
.option('-p, --port <number>', 'Override default Cypress port')
|
|
65
|
+
.action((opts) => {
|
|
66
|
+
require('../lib/commands/open')(opts);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// ─── install ──────────────────────────────────────────────────────────────────
|
|
70
|
+
program
|
|
71
|
+
.command('install')
|
|
72
|
+
.description('Install Cypress in the current project (like npx playwright install)')
|
|
73
|
+
.option('--force', 'Force re-install even if Cypress is already installed')
|
|
74
|
+
.option('--global', 'Install Cypress globally')
|
|
75
|
+
.option('--version <version>', 'Install a specific version of Cypress')
|
|
76
|
+
.action((opts) => {
|
|
77
|
+
require('../lib/commands/install')(opts);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// ─── verify ───────────────────────────────────────────────────────────────────
|
|
81
|
+
program
|
|
82
|
+
.command('verify')
|
|
83
|
+
.description('Verify that Cypress is installed correctly')
|
|
84
|
+
.option('--force', 'Force re-verification')
|
|
85
|
+
.action((opts) => {
|
|
86
|
+
require('../lib/commands/verify')(opts);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// ─── info ─────────────────────────────────────────────────────────────────────
|
|
90
|
+
program
|
|
91
|
+
.command('info')
|
|
92
|
+
.description('Print Cypress, system, and browser information')
|
|
93
|
+
.action(() => {
|
|
94
|
+
require('../lib/commands/info')();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// ─── generate ─────────────────────────────────────────────────────────────────
|
|
98
|
+
program
|
|
99
|
+
.command('generate')
|
|
100
|
+
.description('Scaffold test files interactively (like npx playwright codegen)')
|
|
101
|
+
.option('-n, --name <name>', 'Name for the generated file (without extension)')
|
|
102
|
+
.option('-t, --type <type>', 'Type to generate: spec | fixture | command | page', 'spec')
|
|
103
|
+
.option('-d, --dir <directory>', 'Target directory (defaults to Cypress convention)')
|
|
104
|
+
.option('--url <url>', 'Base URL to include in the generated spec')
|
|
105
|
+
.option('--typescript', 'Generate TypeScript (.cy.ts) files')
|
|
106
|
+
.action((opts) => {
|
|
107
|
+
require('../lib/commands/generate')(opts);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ─── screenshot ───────────────────────────────────────────────────────────────
|
|
111
|
+
program
|
|
112
|
+
.command('screenshot')
|
|
113
|
+
.description('Take a screenshot of a URL (like npx playwright screenshot)')
|
|
114
|
+
.requiredOption('--url <url>', 'URL to screenshot')
|
|
115
|
+
.option('-o, --output <path>', 'Output file path', 'cypress/screenshots/screenshot.png')
|
|
116
|
+
.option('-b, --browser <name>', 'Browser to use', 'electron')
|
|
117
|
+
.option('--full-page', 'Capture full-page screenshot')
|
|
118
|
+
.option('--viewport <WxH>', 'Viewport size (e.g. 1280x720)', '1280x720')
|
|
119
|
+
.action((opts) => {
|
|
120
|
+
require('../lib/commands/screenshot')(opts);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// ─── show-report ──────────────────────────────────────────────────────────────
|
|
124
|
+
program
|
|
125
|
+
.command('show-report')
|
|
126
|
+
.description('Open Cypress HTML test report in the browser (like npx playwright show-report)')
|
|
127
|
+
.option('--path <path>', 'Path to the HTML report file or directory', 'cypress/reports')
|
|
128
|
+
.option('--serve', 'Serve the report on a local HTTP server')
|
|
129
|
+
.option('--port <number>', 'Port for the local server (with --serve)', '9323')
|
|
130
|
+
.action((opts) => {
|
|
131
|
+
require('../lib/commands/show-report')(opts);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// ─── record ───────────────────────────────────────────────────────────────────
|
|
135
|
+
program
|
|
136
|
+
.command('record')
|
|
137
|
+
.description('Run tests and record results to Cypress Cloud')
|
|
138
|
+
.option('-s, --spec <pattern>', 'Spec file or glob pattern')
|
|
139
|
+
.option('-b, --browser <name>', 'Browser to run tests in', 'electron')
|
|
140
|
+
.option('--key <key>', 'Cypress Cloud record key')
|
|
141
|
+
.option('--tag <tags>', 'Tags for this run')
|
|
142
|
+
.option('--group <name>', 'Group name for this run')
|
|
143
|
+
.option('--parallel', 'Run tests in parallel on Cypress Cloud')
|
|
144
|
+
.option('--ci-build-id <id>', 'CI build ID for grouping parallel runs')
|
|
145
|
+
.action((opts) => {
|
|
146
|
+
require('../lib/commands/record')(opts);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// ─── Global error handler ─────────────────────────────────────────────────────
|
|
150
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
151
|
+
console.error(chalk.red('\n ✖ Error: ') + err.message);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
});
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const { logger, createSpinner } = require('../utils/logger');
|
|
7
|
+
const { findProjectRoot } = require('../utils/config-finder');
|
|
8
|
+
|
|
9
|
+
// ─── Templates ────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
function specTemplate({ name, url, typescript }) {
|
|
12
|
+
const ext = typescript ? 'cy.ts' : 'cy.js';
|
|
13
|
+
const baseUrl = url || 'http://localhost:3000';
|
|
14
|
+
return `/// <reference types="cypress" />
|
|
15
|
+
|
|
16
|
+
describe('${name}', () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
cy.visit('${baseUrl}');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should load the page', () => {
|
|
22
|
+
cy.title().should('not.be.empty');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should display the main heading', () => {
|
|
26
|
+
cy.get('h1').should('be.visible');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should be responsive', () => {
|
|
30
|
+
cy.viewport(375, 667); // iPhone SE
|
|
31
|
+
cy.get('body').should('be.visible');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function fixtureTemplate({ name }) {
|
|
38
|
+
return JSON.stringify({
|
|
39
|
+
id: 1,
|
|
40
|
+
name: name,
|
|
41
|
+
email: `${name.toLowerCase()}@example.com`,
|
|
42
|
+
createdAt: new Date().toISOString(),
|
|
43
|
+
}, null, 2) + '\n';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function commandTemplate({ name }) {
|
|
47
|
+
return `// cypress/support/commands/${name}.js
|
|
48
|
+
// Custom Cypress commands for "${name}"
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @example cy.${name}Login('user@example.com', 'password')
|
|
52
|
+
*/
|
|
53
|
+
Cypress.Commands.add('${name}Login', (email, password) => {
|
|
54
|
+
cy.session([email, password], () => {
|
|
55
|
+
cy.visit('/login');
|
|
56
|
+
cy.get('[data-cy="email"]').type(email);
|
|
57
|
+
cy.get('[data-cy="password"]').type(password);
|
|
58
|
+
cy.get('[data-cy="submit"]').click();
|
|
59
|
+
cy.url().should('not.include', '/login');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @example cy.${name}Api('GET', '/api/users')
|
|
65
|
+
*/
|
|
66
|
+
Cypress.Commands.add('${name}Api', (method, url, body) => {
|
|
67
|
+
return cy.request({ method, url, body, failOnStatusCode: false });
|
|
68
|
+
});
|
|
69
|
+
`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function pageTemplate({ name }) {
|
|
73
|
+
const ClassName = name.charAt(0).toUpperCase() + name.slice(1) + 'Page';
|
|
74
|
+
return `// cypress/pages/${name}.page.js
|
|
75
|
+
// Page Object Model for "${name}" page
|
|
76
|
+
|
|
77
|
+
class ${ClassName} {
|
|
78
|
+
// ─── Selectors ───────────────────────────────────────────
|
|
79
|
+
get heading() { return cy.get('h1'); }
|
|
80
|
+
get submitButton(){ return cy.get('[data-cy="submit"]'); }
|
|
81
|
+
get errorMessage(){ return cy.get('[data-cy="error"]'); }
|
|
82
|
+
|
|
83
|
+
// ─── Actions ─────────────────────────────────────────────
|
|
84
|
+
visit() {
|
|
85
|
+
cy.visit('/${name}');
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
clickSubmit() {
|
|
90
|
+
this.submitButton.click();
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── Assertions ──────────────────────────────────────────
|
|
95
|
+
shouldBeVisible() {
|
|
96
|
+
this.heading.should('be.visible');
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = new ${ClassName}();
|
|
102
|
+
`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── Main ─────────────────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
module.exports = async function generateCommand(opts) {
|
|
108
|
+
logger.title('Cypress Validate — Generate');
|
|
109
|
+
logger.divider();
|
|
110
|
+
|
|
111
|
+
const projectRoot = findProjectRoot();
|
|
112
|
+
let { name, type, dir, url, typescript } = opts;
|
|
113
|
+
|
|
114
|
+
// If not passed via flags, use interactive prompts
|
|
115
|
+
if (!name || !type) {
|
|
116
|
+
try {
|
|
117
|
+
const { prompt } = require('enquirer');
|
|
118
|
+
const answers = await prompt([
|
|
119
|
+
!name && {
|
|
120
|
+
type: 'input',
|
|
121
|
+
name: 'name',
|
|
122
|
+
message: 'File name (without extension):',
|
|
123
|
+
initial: 'example',
|
|
124
|
+
validate: (v) => v.trim() ? true : 'Name is required',
|
|
125
|
+
},
|
|
126
|
+
!type && {
|
|
127
|
+
type: 'select',
|
|
128
|
+
name: 'type',
|
|
129
|
+
message: 'What would you like to generate?',
|
|
130
|
+
choices: [
|
|
131
|
+
{ name: 'spec', message: '📄 Spec file (cypress/e2e/<name>.cy.js)' },
|
|
132
|
+
{ name: 'fixture', message: '📦 Fixture file (cypress/fixtures/<name>.json)' },
|
|
133
|
+
{ name: 'command', message: '🔧 Custom command (cypress/support/commands/<name>.js)' },
|
|
134
|
+
{ name: 'page', message: '📐 Page Object (cypress/pages/<name>.page.js)' },
|
|
135
|
+
],
|
|
136
|
+
},
|
|
137
|
+
!url && type === 'spec' && {
|
|
138
|
+
type: 'input',
|
|
139
|
+
name: 'url',
|
|
140
|
+
message: 'Base URL for spec (optional):',
|
|
141
|
+
initial: 'http://localhost:3000',
|
|
142
|
+
},
|
|
143
|
+
].filter(Boolean));
|
|
144
|
+
name = answers.name || name;
|
|
145
|
+
type = answers.type || type;
|
|
146
|
+
url = answers.url || url;
|
|
147
|
+
} catch (_) {
|
|
148
|
+
// enquirer not available or TTY not supported — use defaults
|
|
149
|
+
name = name || 'example';
|
|
150
|
+
type = type || 'spec';
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Determine output path
|
|
155
|
+
let content, outPath;
|
|
156
|
+
switch (type) {
|
|
157
|
+
case 'spec': {
|
|
158
|
+
const ext = typescript ? 'cy.ts' : 'cy.js';
|
|
159
|
+
const targetDir = dir || path.join(projectRoot, 'cypress', 'e2e');
|
|
160
|
+
outPath = path.join(targetDir, `${name}.${ext}`);
|
|
161
|
+
content = specTemplate({ name, url, typescript });
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case 'fixture': {
|
|
165
|
+
const targetDir = dir || path.join(projectRoot, 'cypress', 'fixtures');
|
|
166
|
+
outPath = path.join(targetDir, `${name}.json`);
|
|
167
|
+
content = fixtureTemplate({ name });
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case 'command': {
|
|
171
|
+
const targetDir = dir || path.join(projectRoot, 'cypress', 'support', 'commands');
|
|
172
|
+
outPath = path.join(targetDir, `${name}.js`);
|
|
173
|
+
content = commandTemplate({ name });
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
case 'page': {
|
|
177
|
+
const targetDir = dir || path.join(projectRoot, 'cypress', 'pages');
|
|
178
|
+
outPath = path.join(targetDir, `${name}.page.js`);
|
|
179
|
+
content = pageTemplate({ name });
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
default:
|
|
183
|
+
logger.error(`Unknown type: ${type}. Use: spec | fixture | command | page`);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const spinner = createSpinner(`Generating ${type} → ${path.relative(projectRoot, outPath)}`);
|
|
188
|
+
spinner.start();
|
|
189
|
+
|
|
190
|
+
await fs.ensureDir(path.dirname(outPath));
|
|
191
|
+
await fs.writeFile(outPath, content, 'utf-8');
|
|
192
|
+
|
|
193
|
+
spinner.succeed(chalk.green(`Generated: ${chalk.cyan(path.relative(process.cwd(), outPath))}`));
|
|
194
|
+
logger.blank();
|
|
195
|
+
logger.info(`Type: ${chalk.bold(type)}`);
|
|
196
|
+
logger.info(`Path: ${chalk.cyan(outPath)}`);
|
|
197
|
+
logger.blank();
|
|
198
|
+
logger.step('Next: ' + chalk.cyan(`npx cypress-validate run --spec "${path.relative(process.cwd(), outPath)}"`));
|
|
199
|
+
logger.blank();
|
|
200
|
+
};
|