Skip to content

Commit 0318093

Browse files
authored
fix(login): better indicators to input project subdomain (#728)
* fix(login): better indicators to input project subdomain * refactor: rename helper function/file * refactor: separate validator function, add tests * chore: rename another file * fix: lint * chore: this was bugging me
1 parent b8090fd commit 0318093

File tree

7 files changed

+78
-41
lines changed

7 files changed

+78
-41
lines changed

__tests__/lib/checkFile.test.ts

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import fs from 'fs';
2+
3+
import { validateFilePath, validateSubdomain } from '../../src/lib/validatePromptInput';
4+
5+
describe('#validateFilePath', () => {
6+
afterEach(() => {
7+
jest.clearAllMocks();
8+
});
9+
10+
it('should return error for empty path value', () => {
11+
return expect(validateFilePath('')).toBe('An output path must be supplied.');
12+
});
13+
14+
it('should return error if path already exists', () => {
15+
expect.assertions(2);
16+
const testPath = 'path-that-already-exists';
17+
18+
fs.existsSync = jest.fn(() => true);
19+
20+
expect(validateFilePath(testPath)).toBe('Specified output path already exists.');
21+
expect(fs.existsSync).toHaveBeenCalledWith(testPath);
22+
});
23+
24+
it("should return true if the path doesn't exist", () => {
25+
expect.assertions(2);
26+
const testPath = 'path-that-does-not-exist';
27+
28+
fs.existsSync = jest.fn(() => false);
29+
30+
expect(validateFilePath(testPath)).toBe(true);
31+
expect(fs.existsSync).toHaveBeenCalledWith(testPath);
32+
});
33+
});
34+
35+
describe('#validateSubdomain', () => {
36+
it('should validate basic subdomain', () => {
37+
expect(validateSubdomain('subdomain')).toBe(true);
38+
});
39+
40+
it('should validate subdomain with other characters', () => {
41+
expect(validateSubdomain('test-Subdomain123')).toBe(true);
42+
});
43+
44+
it('should reject subdomain with spaces', () => {
45+
expect(validateSubdomain('test subdomain')).toBe(
46+
'Project subdomain must contain only letters, numbers and dashes.'
47+
);
48+
});
49+
50+
it('should reject subdomain with special characters', () => {
51+
expect(validateSubdomain('test-subdomain!')).toBe(
52+
'Project subdomain must contain only letters, numbers and dashes.'
53+
);
54+
});
55+
});

src/cmds/openapi/convert.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import chalk from 'chalk';
88
import prompts from 'prompts';
99

1010
import Command, { CommandCategories } from '../../lib/baseCommand';
11-
import { checkFilePath } from '../../lib/checkFile';
1211
import prepareOas from '../../lib/prepareOas';
1312
import promptTerminal from '../../lib/promptWrapper';
13+
import { validateFilePath } from '../../lib/validatePromptInput';
1414

1515
export interface Options {
1616
spec?: string;
@@ -72,7 +72,7 @@ export default class OpenAPIConvertCommand extends Command {
7272
const extension = path.extname(specPath);
7373
return `${path.basename(specPath).split(extension)[0]}.openapi${extension}`;
7474
},
75-
validate: value => checkFilePath(value),
75+
validate: value => validateFilePath(value),
7676
},
7777
]);
7878

src/cmds/openapi/reduce.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import ora from 'ora';
1111
import prompts from 'prompts';
1212

1313
import Command, { CommandCategories } from '../../lib/baseCommand';
14-
import { checkFilePath } from '../../lib/checkFile';
1514
import { oraOptions } from '../../lib/logger';
1615
import prepareOas from '../../lib/prepareOas';
1716
import promptTerminal from '../../lib/promptWrapper';
17+
import { validateFilePath } from '../../lib/validatePromptInput';
1818

1919
export interface Options {
2020
spec?: string;
@@ -170,7 +170,7 @@ export default class OpenAPIReduceCommand extends Command {
170170
const extension = path.extname(specPath);
171171
return `${path.basename(specPath).split(extension)[0]}.reduced${extension}`;
172172
},
173-
validate: value => checkFilePath(value),
173+
validate: value => validateFilePath(value),
174174
},
175175
]);
176176

src/lib/createGHA/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import chalk from 'chalk';
99
import prompts from 'prompts';
1010
import simpleGit from 'simple-git';
1111

12-
import { checkFilePath, cleanFileName } from '../checkFile';
1312
import configstore from '../configstore';
1413
import { getMajorPkgVersion } from '../getPkgVersion';
1514
import isCI, { isNpmScript, isTest } from '../isCI';
1615
import { debug, info } from '../logger';
1716
import promptTerminal from '../promptWrapper';
17+
import { cleanFileName, validateFilePath } from '../validatePromptInput';
1818

1919
import yamlBase from './baseFile';
2020

@@ -234,7 +234,7 @@ export default async function createGHA(
234234
type: 'text',
235235
initial: cleanFileName(`rdme-${command}`),
236236
format: prev => getGHAFileName(prev),
237-
validate: value => checkFilePath(value, getGHAFileName),
237+
validate: value => validateFilePath(value, getGHAFileName),
238238
},
239239
],
240240
{

src/lib/loginFlow.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import fetch, { handleRes } from './fetch';
77
import getCurrentConfig from './getCurrentConfig';
88
import { debug } from './logger';
99
import promptTerminal from './promptWrapper';
10+
import { validateSubdomain } from './validatePromptInput';
1011

1112
interface LoginBody {
1213
email?: string;
@@ -51,8 +52,9 @@ export default async function loginFlow(otp?: string) {
5152
{
5253
type: 'text',
5354
name: 'project',
54-
message: 'What project are you logging into?',
55+
message: 'What project subdomain are you logging into?',
5556
initial: storedConfig.project,
57+
validate: validateSubdomain,
5658
},
5759
]);
5860

src/lib/checkFile.ts renamed to src/lib/validatePromptInput.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const cleanFileName = (input: string) => input.replace(/[^a-z0-9]/gi, '-'
1616
* @returns true if path is valid (i.e. is non-empty and doesn't already exist),
1717
* otherwise a string containing the error message
1818
*/
19-
export function checkFilePath(value: string, getFullPath: (file: string) => string = file => file) {
19+
export function validateFilePath(value: string, getFullPath: (file: string) => string = file => file) {
2020
if (value.length) {
2121
const fullPath = getFullPath(value);
2222
if (!fs.existsSync(fullPath)) {
@@ -28,3 +28,16 @@ export function checkFilePath(value: string, getFullPath: (file: string) => stri
2828

2929
return 'An output path must be supplied.';
3030
}
31+
32+
/**
33+
* Validates that a project subdomain value is valid.
34+
*
35+
* @param value the terminal input
36+
* @returns true if the subdomain value is valid, else an error message
37+
*/
38+
export function validateSubdomain(value: string) {
39+
return (
40+
// eslint-disable-next-line unicorn/no-unsafe-regex
41+
/^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$/.test(value) || 'Project subdomain must contain only letters, numbers and dashes.'
42+
);
43+
}

0 commit comments

Comments
 (0)