Skip to content

Commit

Permalink
fix(login): better indicators to input project subdomain (#728)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
kanadgupta authored Jan 3, 2023
1 parent b8090fd commit 0318093
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 41 deletions.
33 changes: 0 additions & 33 deletions __tests__/lib/checkFile.test.ts

This file was deleted.

55 changes: 55 additions & 0 deletions __tests__/lib/validatePromptInput.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import fs from 'fs';

import { validateFilePath, validateSubdomain } from '../../src/lib/validatePromptInput';

describe('#validateFilePath', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should return error for empty path value', () => {
return expect(validateFilePath('')).toBe('An output path must be supplied.');
});

it('should return error if path already exists', () => {
expect.assertions(2);
const testPath = 'path-that-already-exists';

fs.existsSync = jest.fn(() => true);

expect(validateFilePath(testPath)).toBe('Specified output path already exists.');
expect(fs.existsSync).toHaveBeenCalledWith(testPath);
});

it("should return true if the path doesn't exist", () => {
expect.assertions(2);
const testPath = 'path-that-does-not-exist';

fs.existsSync = jest.fn(() => false);

expect(validateFilePath(testPath)).toBe(true);
expect(fs.existsSync).toHaveBeenCalledWith(testPath);
});
});

describe('#validateSubdomain', () => {
it('should validate basic subdomain', () => {
expect(validateSubdomain('subdomain')).toBe(true);
});

it('should validate subdomain with other characters', () => {
expect(validateSubdomain('test-Subdomain123')).toBe(true);
});

it('should reject subdomain with spaces', () => {
expect(validateSubdomain('test subdomain')).toBe(
'Project subdomain must contain only letters, numbers and dashes.'
);
});

it('should reject subdomain with special characters', () => {
expect(validateSubdomain('test-subdomain!')).toBe(
'Project subdomain must contain only letters, numbers and dashes.'
);
});
});
4 changes: 2 additions & 2 deletions src/cmds/openapi/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import chalk from 'chalk';
import prompts from 'prompts';

import Command, { CommandCategories } from '../../lib/baseCommand';
import { checkFilePath } from '../../lib/checkFile';
import prepareOas from '../../lib/prepareOas';
import promptTerminal from '../../lib/promptWrapper';
import { validateFilePath } from '../../lib/validatePromptInput';

export interface Options {
spec?: string;
Expand Down Expand Up @@ -72,7 +72,7 @@ export default class OpenAPIConvertCommand extends Command {
const extension = path.extname(specPath);
return `${path.basename(specPath).split(extension)[0]}.openapi${extension}`;
},
validate: value => checkFilePath(value),
validate: value => validateFilePath(value),
},
]);

Expand Down
4 changes: 2 additions & 2 deletions src/cmds/openapi/reduce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import ora from 'ora';
import prompts from 'prompts';

import Command, { CommandCategories } from '../../lib/baseCommand';
import { checkFilePath } from '../../lib/checkFile';
import { oraOptions } from '../../lib/logger';
import prepareOas from '../../lib/prepareOas';
import promptTerminal from '../../lib/promptWrapper';
import { validateFilePath } from '../../lib/validatePromptInput';

export interface Options {
spec?: string;
Expand Down Expand Up @@ -170,7 +170,7 @@ export default class OpenAPIReduceCommand extends Command {
const extension = path.extname(specPath);
return `${path.basename(specPath).split(extension)[0]}.reduced${extension}`;
},
validate: value => checkFilePath(value),
validate: value => validateFilePath(value),
},
]);

Expand Down
4 changes: 2 additions & 2 deletions src/lib/createGHA/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import chalk from 'chalk';
import prompts from 'prompts';
import simpleGit from 'simple-git';

import { checkFilePath, cleanFileName } from '../checkFile';
import configstore from '../configstore';
import { getMajorPkgVersion } from '../getPkgVersion';
import isCI, { isNpmScript, isTest } from '../isCI';
import { debug, info } from '../logger';
import promptTerminal from '../promptWrapper';
import { cleanFileName, validateFilePath } from '../validatePromptInput';

import yamlBase from './baseFile';

Expand Down Expand Up @@ -234,7 +234,7 @@ export default async function createGHA(
type: 'text',
initial: cleanFileName(`rdme-${command}`),
format: prev => getGHAFileName(prev),
validate: value => checkFilePath(value, getGHAFileName),
validate: value => validateFilePath(value, getGHAFileName),
},
],
{
Expand Down
4 changes: 3 additions & 1 deletion src/lib/loginFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import fetch, { handleRes } from './fetch';
import getCurrentConfig from './getCurrentConfig';
import { debug } from './logger';
import promptTerminal from './promptWrapper';
import { validateSubdomain } from './validatePromptInput';

interface LoginBody {
email?: string;
Expand Down Expand Up @@ -51,8 +52,9 @@ export default async function loginFlow(otp?: string) {
{
type: 'text',
name: 'project',
message: 'What project are you logging into?',
message: 'What project subdomain are you logging into?',
initial: storedConfig.project,
validate: validateSubdomain,
},
]);

Expand Down
15 changes: 14 additions & 1 deletion src/lib/checkFile.ts → src/lib/validatePromptInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const cleanFileName = (input: string) => input.replace(/[^a-z0-9]/gi, '-'
* @returns true if path is valid (i.e. is non-empty and doesn't already exist),
* otherwise a string containing the error message
*/
export function checkFilePath(value: string, getFullPath: (file: string) => string = file => file) {
export function validateFilePath(value: string, getFullPath: (file: string) => string = file => file) {
if (value.length) {
const fullPath = getFullPath(value);
if (!fs.existsSync(fullPath)) {
Expand All @@ -28,3 +28,16 @@ export function checkFilePath(value: string, getFullPath: (file: string) => stri

return 'An output path must be supplied.';
}

/**
* Validates that a project subdomain value is valid.
*
* @param value the terminal input
* @returns true if the subdomain value is valid, else an error message
*/
export function validateSubdomain(value: string) {
return (
// eslint-disable-next-line unicorn/no-unsafe-regex
/^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$/.test(value) || 'Project subdomain must contain only letters, numbers and dashes.'
);
}

0 comments on commit 0318093

Please sign in to comment.