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 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
+ [![npm version](https://img.shields.io/npm/v/cypress-validate.svg)](https://www.npmjs.com/package/cypress-validate)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
7
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D14-brightgreen.svg)](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
+ };