@letsrunit/cli 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.
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
- package/src/index.ts +9 -0
package/dist/index.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'module';
|
|
2
3
|
import { explore, refineSuggestion, generate, run } from '@letsrunit/executor';
|
|
3
4
|
import { makeFeature } from '@letsrunit/gherkin';
|
|
4
5
|
import { Journal, CliSink } from '@letsrunit/journal';
|
|
5
6
|
import { getMailbox } from '@letsrunit/mailbox';
|
|
6
7
|
import { asFilename, randomUUID } from '@letsrunit/utils';
|
|
7
8
|
import { Command } from 'commander';
|
|
9
|
+
import { init } from 'letsrunit';
|
|
8
10
|
import { readFileSync } from 'fs';
|
|
9
11
|
import * as fs2 from 'fs/promises';
|
|
10
12
|
import fs2__default from 'fs/promises';
|
|
11
13
|
import { dirname, join } from 'path';
|
|
12
14
|
import { fileURLToPath } from 'url';
|
|
13
15
|
|
|
16
|
+
createRequire(import.meta.url);
|
|
14
17
|
function disableEcho() {
|
|
15
18
|
process.stdout.write("\x1B[?25l");
|
|
16
19
|
process.stdout.write("\x1B[8m");
|
|
@@ -93,6 +96,9 @@ async function readStdin() {
|
|
|
93
96
|
});
|
|
94
97
|
}
|
|
95
98
|
program.name("letsrunit").description("Vibe testing done right").version(version);
|
|
99
|
+
program.command("init").description("Set up letsrunit in the current project").option("-y, --yes", "Skip confirmation prompts").action(async (opts) => {
|
|
100
|
+
await init({ yes: opts.yes });
|
|
101
|
+
});
|
|
96
102
|
program.command("explore").argument("<target>", "Target URL or project").option("-v, --verbose", "Enable verbose logging", false).option("-s, --silent", "Only output errors", false).option("-o, --save <path>", "Path to save .feature file", "").action(async (target, opts) => {
|
|
97
103
|
const journal = createJournal({ ...opts, artifactPath: opts.save });
|
|
98
104
|
const { status } = await explore(target, { headless: false, journal }, async (info, actions) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/run-explore.ts","../src/index.ts"],"names":["fs","asFilename","makeFeature"],"mappings":"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","file":"index.js","sourcesContent":["import type { Action, AppInfo } from '@letsrunit/executor';\nimport { makeFeature } from '@letsrunit/gherkin';\nimport { asFilename } from '@letsrunit/utils';\nimport * as fs from 'node:fs/promises';\n\nfunction disableEcho() {\n process.stdout.write('\\x1B[?25l'); // hide cursor\n process.stdout.write('\\x1B[8m'); // hide input\n}\n\nfunction enableEcho() {\n process.stdout.write('\\x1B[0m');\n process.stdout.write('\\x1B[?25h');\n}\n\nfunction readKey(): Promise<string> {\n const { stdin } = process;\n\n stdin.setRawMode(true);\n stdin.resume();\n stdin.setEncoding('utf8');\n disableEcho();\n\n return new Promise<string>((resolve) => {\n const handler = (pressed: string) => {\n stdin.removeListener('data', handler);\n stdin.setRawMode(false);\n stdin.pause();\n enableEcho();\n\n resolve(pressed);\n };\n\n stdin.on('data', handler);\n });\n}\n\nasync function readOption(limit: number): Promise<number> {\n while (true) {\n const key = await readKey();\n if (key === '\\u0003') return -1;\n\n const opt = key >= '0' && key <= '9' ? Number(key) : null;\n\n if (!opt || opt > limit) {\n process.stdout.write('\\x1b[33mInvalid option selected\\x1b[0m\\n');\n continue;\n }\n\n return opt - 1;\n }\n}\n\nexport async function runExplore(info: AppInfo, actions: Action[], storagePath?: string) {\n const { stdout } = process;\n\n while (actions.length > 0) {\n stdout.write(`\\n\\x1b[1m${info.title}\\x1b[0m\\n`);\n stdout.write('What do you want to test? Choose one of the following options:\\n');\n\n let count = 1;\n for (const action of actions) {\n stdout.write(`${count++}. ${action.name}\\n`);\n }\n\n const opt = await readOption(actions.length);\n if (opt < 0) return;\n\n stdout.write('\\n');\n const { status, feature } = await actions[opt].run();\n\n actions.splice(opt, 1);\n\n if (storagePath && status === 'passed' && feature) {\n await fs.writeFile(`${storagePath}/${asFilename(feature.name!)}.feature`, makeFeature(feature));\n }\n }\n}\n","import { explore, generate, refineSuggestion, run } from '@letsrunit/executor';\nimport { makeFeature } from '@letsrunit/gherkin';\nimport { CliSink, Journal } from '@letsrunit/journal';\nimport { getMailbox } from '@letsrunit/mailbox';\nimport { asFilename, randomUUID } from '@letsrunit/utils';\nimport { Command } from 'commander';\nimport { readFileSync } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { runExplore } from './run-explore';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst { version } = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')) as { version: string };\n\nconst program = new Command();\n\ninterface JournalOptions {\n verbose: boolean;\n silent: boolean;\n artifactPath?: string;\n}\n\nfunction createJournal({ verbose, silent, artifactPath }: JournalOptions) {\n const verbosity = verbose ? 3 : silent ? 0 : 1;\n return new Journal(new CliSink({ verbosity, artifactPath }));\n}\n\nasync function readStdin(): Promise<string> {\n return await new Promise((resolve) => {\n let data = '';\n process.stdin.setEncoding('utf8');\n\n const isTTY = Boolean(process.stdin.isTTY);\n if (isTTY) {\n // Interactive input: allow user to type multiple lines and finish with EOF\n console.error('Enter instructions. Finish with Ctrl-D (Unix/macOS/Linux) or Ctrl-Z then Enter (Windows).');\n }\n\n process.stdin.on('data', (chunk) => (data += chunk));\n process.stdin.on('end', () => resolve(data));\n process.stdin.resume();\n });\n}\n\nprogram.name('letsrunit').description('Vibe testing done right').version(version);\n\nprogram\n .command('explore')\n .argument('<target>', 'Target URL or project')\n .option('-v, --verbose', 'Enable verbose logging', false)\n .option('-s, --silent', 'Only output errors', false)\n .option('-o, --save <path>', 'Path to save .feature file', '')\n .action(async (target: string, opts: { verbose: boolean; silent: boolean; save: string }) => {\n const journal = createJournal({ ...opts, artifactPath: opts.save });\n\n const { status } = await explore(target, { headless: false, journal }, async (info, actions) => {\n journal.sink.endSection();\n await runExplore(info, actions, opts.save);\n });\n\n process.exit(status === 'passed' ? 0 : 1);\n });\n\nprogram\n .command('generate')\n .argument('<target>', 'Target URL or project')\n .option('-v, --verbose', 'Enable verbose logging', false)\n .option('-s, --silent', 'Only output errors', false)\n .option('-o, --save <path>', 'Path to save .feature file', '')\n .action(async (target: string, opts: { verbose: boolean; silent: boolean; save: string }) => {\n const instructions = (await readStdin()).trim();\n\n if (!instructions) {\n console.error('No instructions provided');\n process.exit(1);\n }\n\n const journal = createJournal({ ...opts, artifactPath: opts.save });\n\n await journal.info('Refining test instructions');\n const suggestion = await refineSuggestion(instructions);\n\n const { feature, status } = await generate(target, suggestion, { headless: false, journal });\n\n if (opts.save && feature) {\n await fs.writeFile(`${opts.save}/${asFilename(feature.name!)}.feature`, makeFeature(feature));\n }\n\n process.exit(status === 'passed' ? 0 : 1);\n });\n\nprogram\n .command('register')\n .argument('<target>', 'Target URL or project')\n .option('-v, --verbose', 'Enable verbose logging', false)\n .option('-s, --silent', 'Only output errors', false)\n .option('-o, --save <path>', 'Path to save .feature file', '')\n .action(async (target: string, opts: { verbose: boolean; silent: boolean; save: string }) => {\n const journal = createJournal({ ...opts, artifactPath: opts.save });\n\n const suggestion = {\n name: 'Register a new user by email',\n description: [\n 'Locate the registration form and fill it out to create a new account.',\n 'Confirm the registration email and log in as the user',\n ].join('\\n'),\n comments: [\n 'If no registration button is visible, try locating it through the login form.',\n 'The feature is complete when a confirmation email is received and verified and the user is logged in.',\n ].join('\\n'),\n };\n\n const email = getMailbox(randomUUID());\n\n const { feature, status } = await generate(target, suggestion, { headless: false, journal, accounts: { email } });\n\n if (opts.save && feature) {\n await fs.writeFile(`${opts.save}/${asFilename(feature.name!)}.feature`, makeFeature(feature));\n }\n\n process.exit(status === 'passed' ? 0 : 1);\n });\n\nprogram\n .command('run')\n .argument('<target>', 'Target URL or project')\n .argument('<feature>', 'Gherkin feature file')\n .option('-v, --verbose', 'Enable verbose logging', false)\n .option('-s, --silent', 'Only output errors', false)\n .action(async (target: string, featureFile: string, opts: { verbose: boolean; silent: boolean }) => {\n const feature = await fs.readFile(featureFile, 'utf-8');\n await run(target, feature, { headless: false, journal: createJournal(opts) });\n });\n\nprogram.parse();\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/run-explore.ts","../src/index.ts"],"names":["fs","asFilename","makeFeature"],"mappings":"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","file":"index.js","sourcesContent":["import type { Action, AppInfo } from '@letsrunit/executor';\nimport { makeFeature } from '@letsrunit/gherkin';\nimport { asFilename } from '@letsrunit/utils';\nimport * as fs from 'node:fs/promises';\n\nfunction disableEcho() {\n process.stdout.write('\\x1B[?25l'); // hide cursor\n process.stdout.write('\\x1B[8m'); // hide input\n}\n\nfunction enableEcho() {\n process.stdout.write('\\x1B[0m');\n process.stdout.write('\\x1B[?25h');\n}\n\nfunction readKey(): Promise<string> {\n const { stdin } = process;\n\n stdin.setRawMode(true);\n stdin.resume();\n stdin.setEncoding('utf8');\n disableEcho();\n\n return new Promise<string>((resolve) => {\n const handler = (pressed: string) => {\n stdin.removeListener('data', handler);\n stdin.setRawMode(false);\n stdin.pause();\n enableEcho();\n\n resolve(pressed);\n };\n\n stdin.on('data', handler);\n });\n}\n\nasync function readOption(limit: number): Promise<number> {\n while (true) {\n const key = await readKey();\n if (key === '\\u0003') return -1;\n\n const opt = key >= '0' && key <= '9' ? Number(key) : null;\n\n if (!opt || opt > limit) {\n process.stdout.write('\\x1b[33mInvalid option selected\\x1b[0m\\n');\n continue;\n }\n\n return opt - 1;\n }\n}\n\nexport async function runExplore(info: AppInfo, actions: Action[], storagePath?: string) {\n const { stdout } = process;\n\n while (actions.length > 0) {\n stdout.write(`\\n\\x1b[1m${info.title}\\x1b[0m\\n`);\n stdout.write('What do you want to test? Choose one of the following options:\\n');\n\n let count = 1;\n for (const action of actions) {\n stdout.write(`${count++}. ${action.name}\\n`);\n }\n\n const opt = await readOption(actions.length);\n if (opt < 0) return;\n\n stdout.write('\\n');\n const { status, feature } = await actions[opt].run();\n\n actions.splice(opt, 1);\n\n if (storagePath && status === 'passed' && feature) {\n await fs.writeFile(`${storagePath}/${asFilename(feature.name!)}.feature`, makeFeature(feature));\n }\n }\n}\n","import { explore, generate, refineSuggestion, run } from '@letsrunit/executor';\nimport { makeFeature } from '@letsrunit/gherkin';\nimport { CliSink, Journal } from '@letsrunit/journal';\nimport { getMailbox } from '@letsrunit/mailbox';\nimport { asFilename, randomUUID } from '@letsrunit/utils';\nimport { Command } from 'commander';\nimport { init } from 'letsrunit';\nimport { readFileSync } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { runExplore } from './run-explore';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst { version } = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')) as { version: string };\n\nconst program = new Command();\n\ninterface JournalOptions {\n verbose: boolean;\n silent: boolean;\n artifactPath?: string;\n}\n\nfunction createJournal({ verbose, silent, artifactPath }: JournalOptions) {\n const verbosity = verbose ? 3 : silent ? 0 : 1;\n return new Journal(new CliSink({ verbosity, artifactPath }));\n}\n\nasync function readStdin(): Promise<string> {\n return await new Promise((resolve) => {\n let data = '';\n process.stdin.setEncoding('utf8');\n\n const isTTY = Boolean(process.stdin.isTTY);\n if (isTTY) {\n // Interactive input: allow user to type multiple lines and finish with EOF\n console.error('Enter instructions. Finish with Ctrl-D (Unix/macOS/Linux) or Ctrl-Z then Enter (Windows).');\n }\n\n process.stdin.on('data', (chunk) => (data += chunk));\n process.stdin.on('end', () => resolve(data));\n process.stdin.resume();\n });\n}\n\nprogram.name('letsrunit').description('Vibe testing done right').version(version);\n\nprogram\n .command('init')\n .description('Set up letsrunit in the current project')\n .option('-y, --yes', 'Skip confirmation prompts')\n .action(async (opts: { yes?: boolean }) => {\n await init({ yes: opts.yes });\n });\n\nprogram\n .command('explore')\n .argument('<target>', 'Target URL or project')\n .option('-v, --verbose', 'Enable verbose logging', false)\n .option('-s, --silent', 'Only output errors', false)\n .option('-o, --save <path>', 'Path to save .feature file', '')\n .action(async (target: string, opts: { verbose: boolean; silent: boolean; save: string }) => {\n const journal = createJournal({ ...opts, artifactPath: opts.save });\n\n const { status } = await explore(target, { headless: false, journal }, async (info, actions) => {\n journal.sink.endSection();\n await runExplore(info, actions, opts.save);\n });\n\n process.exit(status === 'passed' ? 0 : 1);\n });\n\nprogram\n .command('generate')\n .argument('<target>', 'Target URL or project')\n .option('-v, --verbose', 'Enable verbose logging', false)\n .option('-s, --silent', 'Only output errors', false)\n .option('-o, --save <path>', 'Path to save .feature file', '')\n .action(async (target: string, opts: { verbose: boolean; silent: boolean; save: string }) => {\n const instructions = (await readStdin()).trim();\n\n if (!instructions) {\n console.error('No instructions provided');\n process.exit(1);\n }\n\n const journal = createJournal({ ...opts, artifactPath: opts.save });\n\n await journal.info('Refining test instructions');\n const suggestion = await refineSuggestion(instructions);\n\n const { feature, status } = await generate(target, suggestion, { headless: false, journal });\n\n if (opts.save && feature) {\n await fs.writeFile(`${opts.save}/${asFilename(feature.name!)}.feature`, makeFeature(feature));\n }\n\n process.exit(status === 'passed' ? 0 : 1);\n });\n\nprogram\n .command('register')\n .argument('<target>', 'Target URL or project')\n .option('-v, --verbose', 'Enable verbose logging', false)\n .option('-s, --silent', 'Only output errors', false)\n .option('-o, --save <path>', 'Path to save .feature file', '')\n .action(async (target: string, opts: { verbose: boolean; silent: boolean; save: string }) => {\n const journal = createJournal({ ...opts, artifactPath: opts.save });\n\n const suggestion = {\n name: 'Register a new user by email',\n description: [\n 'Locate the registration form and fill it out to create a new account.',\n 'Confirm the registration email and log in as the user',\n ].join('\\n'),\n comments: [\n 'If no registration button is visible, try locating it through the login form.',\n 'The feature is complete when a confirmation email is received and verified and the user is logged in.',\n ].join('\\n'),\n };\n\n const email = getMailbox(randomUUID());\n\n const { feature, status } = await generate(target, suggestion, { headless: false, journal, accounts: { email } });\n\n if (opts.save && feature) {\n await fs.writeFile(`${opts.save}/${asFilename(feature.name!)}.feature`, makeFeature(feature));\n }\n\n process.exit(status === 'passed' ? 0 : 1);\n });\n\nprogram\n .command('run')\n .argument('<target>', 'Target URL or project')\n .argument('<feature>', 'Gherkin feature file')\n .option('-v, --verbose', 'Enable verbose logging', false)\n .option('-s, --silent', 'Only output errors', false)\n .action(async (target: string, featureFile: string, opts: { verbose: boolean; silent: boolean }) => {\n const feature = await fs.readFile(featureFile, 'utf-8');\n await run(target, feature, { headless: false, journal: createJournal(opts) });\n });\n\nprogram.parse();\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@letsrunit/cli",
|
|
3
|
-
"version": "0.2
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Command line interface for letsrunit",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"testing",
|
|
@@ -43,12 +43,13 @@
|
|
|
43
43
|
},
|
|
44
44
|
"packageManager": "yarn@4.10.3",
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@letsrunit/executor": "0.2
|
|
47
|
-
"@letsrunit/gherkin": "0.2
|
|
48
|
-
"@letsrunit/journal": "0.2
|
|
49
|
-
"@letsrunit/mailbox": "0.2
|
|
50
|
-
"@letsrunit/utils": "0.2
|
|
51
|
-
"commander": "^14.0.2"
|
|
46
|
+
"@letsrunit/executor": "0.3.2",
|
|
47
|
+
"@letsrunit/gherkin": "0.3.2",
|
|
48
|
+
"@letsrunit/journal": "0.3.2",
|
|
49
|
+
"@letsrunit/mailbox": "0.3.2",
|
|
50
|
+
"@letsrunit/utils": "0.3.2",
|
|
51
|
+
"commander": "^14.0.2",
|
|
52
|
+
"letsrunit": "0.3.2"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
55
|
"tsx": "^4.21.0"
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { CliSink, Journal } from '@letsrunit/journal';
|
|
|
4
4
|
import { getMailbox } from '@letsrunit/mailbox';
|
|
5
5
|
import { asFilename, randomUUID } from '@letsrunit/utils';
|
|
6
6
|
import { Command } from 'commander';
|
|
7
|
+
import { init } from 'letsrunit';
|
|
7
8
|
import { readFileSync } from 'node:fs';
|
|
8
9
|
import fs from 'node:fs/promises';
|
|
9
10
|
import { dirname, join } from 'node:path';
|
|
@@ -45,6 +46,14 @@ async function readStdin(): Promise<string> {
|
|
|
45
46
|
|
|
46
47
|
program.name('letsrunit').description('Vibe testing done right').version(version);
|
|
47
48
|
|
|
49
|
+
program
|
|
50
|
+
.command('init')
|
|
51
|
+
.description('Set up letsrunit in the current project')
|
|
52
|
+
.option('-y, --yes', 'Skip confirmation prompts')
|
|
53
|
+
.action(async (opts: { yes?: boolean }) => {
|
|
54
|
+
await init({ yes: opts.yes });
|
|
55
|
+
});
|
|
56
|
+
|
|
48
57
|
program
|
|
49
58
|
.command('explore')
|
|
50
59
|
.argument('<target>', 'Target URL or project')
|