Skip to content

Commit c0f2d83

Browse files
authored
fix: ensure non-TTY agents dont hang (#172)
* dont block on override * detect non-tty * add --test-dir * address feedback
1 parent 213d6ee commit c0f2d83

4 files changed

Lines changed: 35 additions & 6 deletions

File tree

‎src/cli.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ program
3434
.option('--quiet', 'do not ask for interactive input prompts')
3535
.option('--gha', 'install GitHub Actions')
3636
.option('--lang <language>', 'language to use (js, TypeScript)')
37+
.option('--test-dir <directory>', 'directory for test files (default: "tests" if it does not exist, otherwise "e2e")')
3738
.action(async (rootDir, options) => {
3839

3940
const cliOptions: CliOptions = {
@@ -47,6 +48,7 @@ program
4748
quiet: options.quiet,
4849
gha: options.gha,
4950
lang: options.lang,
51+
testDir: options.testDir,
5052
};
5153
const resolvedRootDir = path.resolve(process.cwd(), rootDir || '.');
5254
const generator = new Generator(resolvedRootDir, cliOptions);

‎src/generator.ts‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export type CliOptions = {
4545
ct?: boolean;
4646
quiet?: boolean;
4747
gha?: boolean;
48+
testDir?: string;
4849
};
4950

5051
export class Generator {
@@ -67,7 +68,7 @@ export class Generator {
6768
return acc;
6869
}, [[] as Command[], [] as Command[]]);
6970
executeCommands(this.rootDir, preCommands);
70-
await createFiles(this.rootDir, files);
71+
await createFiles(this.rootDir, files, false, !!this.options.quiet);
7172
this._patchGitIgnore();
7273
await this._patchPackageJSON(answers);
7374
executeCommands(this.rootDir, postCommands);
@@ -85,12 +86,15 @@ export class Generator {
8586
private async _askQuestions(): Promise<PromptOptions> {
8687
if (process.env.TEST_OPTIONS)
8788
return JSON.parse(process.env.TEST_OPTIONS);
89+
90+
const testDir = this.options.testDir || (fs.existsSync(path.join(this.rootDir, 'tests')) ? 'e2e' : 'tests');
91+
8892
if (this.options.quiet) {
8993
return {
9094
installGitHubActions: !!this.options.gha,
9195
language: this.options.lang === 'js' ? 'JavaScript' : 'TypeScript',
9296
installPlaywrightDependencies: !!this.options.installDeps,
93-
testDir: fs.existsSync(path.join(this.rootDir, 'tests')) ? 'e2e' : 'tests',
97+
testDir,
9498
framework: undefined,
9599
installPlaywrightBrowsers: !this.options.noBrowsers,
96100
};
@@ -127,7 +131,8 @@ export class Generator {
127131
type: 'text',
128132
name: 'testDir',
129133
message: 'Where to put your end-to-end tests?',
130-
initial: fs.existsSync(path.join(this.rootDir, 'tests')) ? 'e2e' : 'tests',
134+
initial: testDir,
135+
skip: !!this.options.testDir,
131136
},
132137
!this.options.ct && {
133138
type: 'confirm',
@@ -316,7 +321,7 @@ export class Generator {
316321

317322
const files = new Map<string, string>();
318323
files.set('package.json', JSON.stringify(packageJSON, null, 2) + '\n'); // NPM keeps a trailing new-line
319-
await createFiles(this.rootDir, files, true);
324+
await createFiles(this.rootDir, files, true, false);
320325
}
321326

322327
private _printEpilogue(answers: PromptOptions) {

‎src/utils.ts‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,20 @@ export function executeCommands(cwd: string, commands: Command[]) {
3737
}
3838
}
3939

40-
export async function createFiles(rootDir: string, files: Map<string, string>, force: boolean = false) {
40+
export async function createFiles(rootDir: string, files: Map<string, string>, force: boolean, quiet: boolean) {
41+
const existingFiles = [...files.keys()].filter(f => fs.existsSync(path.join(rootDir, f)));
42+
if (quiet && !force && existingFiles.length) {
43+
console.log('These files already exist:');
44+
for (const f of existingFiles)
45+
console.log(` ${path.relative(process.cwd(), f)}`);
46+
console.log('If you want to override them, run again with --force.');
47+
process.exit(1);
48+
return;
49+
}
50+
4151
for (const [relativeFilePath, value] of files) {
4252
const absoluteFilePath = path.join(rootDir, relativeFilePath);
43-
if (fs.existsSync(absoluteFilePath) && !force) {
53+
if (existingFiles.includes(absoluteFilePath) && !force) {
4454
const { override } = await prompt<{ override: boolean }>({
4555
type: 'confirm',
4656
name: 'override',

‎tests/integration.spec.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,18 @@ test('should install with "npm i" in GHA when using npm with package-lock disabl
175175
expect(workflowContent).not.toContain('run: npm ci');
176176
});
177177

178+
test('should not prompt and skip existing files in --quiet mode', async ({ run, dir }) => {
179+
// First run: generate the project normally
180+
await run([], { installGitHubActions: false, testDir: 'tests', language: 'TypeScript', installPlaywrightDependencies: false, installPlaywrightBrowsers: false });
181+
const originalConfig = fs.readFileSync(path.join(dir, 'playwright.config.ts'), 'utf8');
182+
183+
// Second run: use --quiet, existing files should be skipped without prompting
184+
await expect(run(['--quiet'], { installGitHubActions: false, testDir: 'tests', language: 'TypeScript', installPlaywrightDependencies: false, installPlaywrightBrowsers: false })).rejects.toThrowError("run again with --force");
185+
186+
// Verify the existing file was not overwritten
187+
expect(fs.readFileSync(path.join(dir, 'playwright.config.ts'), 'utf8')).toBe(originalConfig);
188+
});
189+
178190
test('is proper yarn classic', async ({ packageManager, exec }) => {
179191
test.skip(packageManager !== 'yarn-classic');
180192
const result = await exec('yarn --version', [], { cwd: test.info().outputDir, shell: true });

0 commit comments

Comments
 (0)