Skip to content

Commit

Permalink
feat: add tests, allow scaffolding from URLs (#38)
Browse files Browse the repository at this point in the history
* tests: add tests for open command

* migrate to cli-testing-tool name

* feat: allow scaffolding projects from url templates
  • Loading branch information
saurabhdaware authored Feb 29, 2024
1 parent 90450c6 commit 9e2c9a5
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 9 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
module.exports = {
env: {
es6: true,
node: true
node: true,
jest: true
},
extends: ['google', 'prettier'],
plugins: ['prettier'],
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,5 @@ typings/

# OSX specific files
.DS_Store

test-app
33 changes: 32 additions & 1 deletion lib/commands/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const gitIgnoreParser = require('gitignore-parser');
// internal modules
const fs = require('fs');
const path = require('path');
const spawn = require('child_process').spawn;

// helper functions
const color = require('../colors.js');
Expand All @@ -14,7 +15,9 @@ const {
onCancel,
selectProject,
copyFolderSync,
getSettings
getSettings,
isURL,
logger
} = require('../helper.js');

// pm create [projectName]
Expand Down Expand Up @@ -68,6 +71,34 @@ async function createProject(projectName) {
const newProjectDirectoryName = projectName.toLowerCase().replace(/ /g, '-');
const newProjectDirectory = path.join(process.cwd(), newProjectDirectoryName);

if (isURL(selectedTemplate.path)) {
const childProcess = spawn(
'git',
['clone', selectedTemplate.path, newProjectDirectory],
{
stdio: 'inherit'
}
);

childProcess.on('exit', (code) => {
if (code === 0) {
console.log('');
logger.success(
// eslint-disable-next-line max-len
`${newProjectDirectoryName} is successfully scaffolded from ${selectedTemplate.path}`
);
} else {
console.log('');
logger.error(
// eslint-disable-next-line max-len
`Could not scaffold project. Git clone exitted with 1. Check error above`
);
}
});

return;
}

let gitignoreContent = '.git/\n';
const gitignorePath = path.join(selectedTemplate.path, '.gitignore');

Expand Down
13 changes: 9 additions & 4 deletions lib/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ const DEFAULT_SETTINGS = {
};

// Create settings.json if does not exist or just require it if it does exist
const SETTINGS_PATH = path.join(os.homedir(), '.projectman', 'settings.json');
const SETTINGS_DIR =
process.env.NODE_ENV === 'test'
? path.join(__dirname, '..', 'tests', 'dotprojectman')
: path.join(os.homedir(), '.projectman');

const SETTINGS_PATH = path.join(SETTINGS_DIR, 'settings.json');

/**
* Returns settings if already exists, else creates default settings and returns it
Expand All @@ -33,9 +38,8 @@ const getSettings = () => {
} catch (err) {
if (err.code === 'MODULE_NOT_FOUND') {
// Create if doesn't exist
const settingsDir = path.join(os.homedir(), '.projectman');
if (!fs.existsSync(settingsDir)) {
fs.mkdirSync(settingsDir);
if (!fs.existsSync(SETTINGS_DIR)) {
fs.mkdirSync(SETTINGS_DIR);
}
fs.writeFileSync(
SETTINGS_PATH,
Expand Down Expand Up @@ -272,6 +276,7 @@ function copyFolderSync(from, to, gitignore, ignoreEmptyDirs = true, basePath) {

module.exports = {
settings,
logger,
getSettings,
SETTINGS_PATH,
throwCreateIssueError,
Expand Down
49 changes: 47 additions & 2 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "projectman",
"version": "2.0.0",
"version": "2.1.0",
"description": "Hate opening folders? Select and open your projects in your favourite editor straight from your command line without 'CD'ing into the deeply nested folders.",
"main": "bin/index.js",
"bin": {
Expand Down Expand Up @@ -38,6 +38,7 @@
"prompts": "^2.3.0"
},
"devDependencies": {
"cli-testing-tool": "^0.2.0",
"eslint": "^7.0.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^6.11.0",
Expand Down
2 changes: 2 additions & 0 deletions tests/helper.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const { isURL } = require('../lib/helper');
const cleanUp = require('./utils/cleanup');

describe('helper', () => {
afterEach(cleanUp);
test('#isURL()', () => {
// Links
expect(isURL('https://www.google.com')).toBe(true);
Expand Down
73 changes: 73 additions & 0 deletions tests/open.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const path = require('path');
const { createCommandInterface, parseOutput } = require('cli-testing-tool');
const cleanUp = require('./utils/cleanup');
const { STRING_ESC } = require('cli-testing-tool/lib/cli-ansi-parser');

const NO_PROJECT_FOUND_ERROR =
'[BOLD_START][RED_START]>>>[COLOR_END][BOLD_END] No projects to open :(';
const CD_TO_ADD_PROJECT_MSG =
// eslint-disable-next-line max-len
'[BOLD_START][BLUE_START]>>>[COLOR_END][BOLD_END] cd /till/project/directory/ and run [YELLOW_START]pm add[COLOR_END] to add projects and get started';

async function addProject(dir) {
const addCommandInterface = createCommandInterface(
`node ${__dirname}/../bin/index.js add`,
{
cwd: dir
}
);
await addCommandInterface.keys.enter(); // selects default name
return addCommandInterface.getOutput();
}

describe('projectman open', () => {
afterEach(() => {
cleanUp();
});

test('should log no error found and pm add instructions', async () => {
const commandInterface = createCommandInterface(
'node ../bin/index.js open',
{
cwd: __dirname
}
);

const terminal = await commandInterface.getOutput();
expect(terminal.tokenizedOutput).toBe(
NO_PROJECT_FOUND_ERROR + '\n' + CD_TO_ADD_PROJECT_MSG
);
});

// eslint-disable-next-line max-len
test('should show dropdown with tests and utils and with arrow on utils', async () => {
// add tests and utils
await addProject(__dirname);
await addProject(path.join(__dirname, 'utils'));

const openCommandInterface = createCommandInterface(
'node ../bin/index.js open',
{
cwd: __dirname
}
);
await openCommandInterface.keys.arrowDown(); // move down to select utils
const { rawOutput } = await openCommandInterface.getOutput();
/**
* Why slice from last clear line?
*
* Even though we see one output in terminal, sometimes libraries can create multiple outputs
* slicing from the last clear line just makes sure we only get final output.
*/
const outputAfterLastLineClear = rawOutput.slice(
rawOutput.lastIndexOf(`${STRING_ESC}2K`)
);
const parsedOutputAfterLastLineClear = parseOutput(
outputAfterLastLineClear
);

expect(parsedOutputAfterLastLineClear.stringOutput).toBe(
`? Select project to open:› \ntests \nutils`
);
});
});
9 changes: 9 additions & 0 deletions tests/utils/cleanup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const fs = require('fs');
const path = require('path');

const dotProjectMan = path.join(__dirname, '..', 'dotprojectman');
const cleanUp = () => {
fs.rmSync(dotProjectMan, { recursive: true });
};

module.exports = cleanUp;

0 comments on commit 9e2c9a5

Please sign in to comment.