Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cli refactor to stories2csv #15

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 22 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
# Markdown tools
# `markdown-tools`

## Build the image
A checker and code generation tool for our markdown formatted materials.

```bash
docker build -t greenfox/markdown-tools:latest .
```
## Install

### If you have NodeJS and NPM

## Push to Docker Hub
```shell
npm install -g https://github.com/green-fox-academy/markdown-tools
```

Use the `foxyfox` DockerHub user to add yourself to the greenfox organization.
or

```bash
docker login
```shell
git clone https://github.com/green-fox-academy/markdown-tools
cd markdown-tools
npm link
```

Hopefully you'll get Login Succeeded.

```bash
docker push greenfox/markdown-tools:latest
## Usage

### Generate Jira compatible `csv` from markdown

```
markdown-tools stories2csv -i stories.md -o stories.csv
```

For further details see the `--help` flag on the cli tool or read the `features`.

7 changes: 5 additions & 2 deletions bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

'use strict';

const { runCli } = require('../dist/cli.js');
const { runCli } = require('../dist/src/cli.js');

const { argv, stdout, stderr } = process;

runCli({ argv, stdout, stderr }).then(process.exit);

process.exit(runCli(process.argv));
10 changes: 0 additions & 10 deletions docker-compose.yml

This file was deleted.

44 changes: 44 additions & 0 deletions features/cli.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Feature: Show the features if no command is specified
As a Mentor,
I can check the possible commands,
So I know how to use the tool

Scenario:
Given the package installed
Given a bash prompt in "test/" as working directory
When the "markdown-tools" command is executed
Then it should print to the standard error:
"""
markdown-tools <command>

Commands:
markdown-tools stories2csv Creates csv file for Jira from markdown story
definitions

Options:
--help Show help [boolean]
--version Show version number [boolean]

Please specify at least one command!

"""
Then the last exit code should be 1

Scenario:
Given the package installed
Given a bash prompt in "test/" as working directory
When the "markdown-tools --help" command is executed
Then it should print to the standard out:
"""
markdown-tools <command>

Commands:
markdown-tools stories2csv Creates csv file for Jira from markdown story
definitions

Options:
--help Show help [boolean]
--version Show version number [boolean]

"""
Then the last exit code should be 0
46 changes: 46 additions & 0 deletions features/steps/steps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { strict as assert } from 'assert';
import { Given, When, Then, Before, After } from 'cucumber';

Before(async function() {
return this.cli.setup('.test');
})

After(async function() {
return this.cli.tearDown();
})

Given('the package installed', {timeout: 30_000}, async function () {
return this.cli.runSilent('npm link');
});

Given('a bash prompt in {string} as working directory', async function (directory: string) {
return this.cli.cwd(directory);
});

Given('a file at {string}, containing:', async function (name: string, content: string) {
return this.cli.writeFile({name, content});
});

When('the {string} command is executed', async function (command: string) {
return this.cli.run(command);
});

Then('the file at {string}, should contain:', async function (name: string, content: string) {
const actualContent: string = await this.cli.readFile(name);
assert.equal(actualContent, content);
});

Then('it should print to the standard out:', async function (content: string) {
const stdOut: string = await this.cli.getStdOut();
assert.equal(stdOut, content);
});

Then('it should print to the standard error:', async function (content: string) {
const stdErr: string = await this.cli.getStdErr();
assert.equal(stdErr, content);
});

Then('the last exit code should be {int}', async function (exitCode: number) {
const lastExitCode: number = await this.cli.getLastExitCode();
assert.equal(lastExitCode, exitCode);
});
92 changes: 92 additions & 0 deletions features/stories2csv.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
Feature: Generate Jira importable CSV file from markdown story list
As a Project mentor,
I can generate a story CSV file,
So I can import the task easily to Jira

Scenario:
Given the package installed
Given a bash prompt in "test/" as working directory
Given a file at "stories.md", containing:
"""
# Test Story

## As Somebody, I can do something, so I am happy

More details

## Acceptance Criteria

- Something

## Tasks

### Task one

Do something unexpected
"""
When the "markdown-tools stories2csv -i stories.md -o stories.csv" command is executed
Then the file at "stories.csv", should contain:
"""
Summary,Issue id,Parent id,Issue Type,Description
Test Story,1,,Story,"h2.Description
As Somebody, I can do something, so I am happy
More details
h2.Acceptance Criteria
- Something"
Task one,2,1,Sub-task,Do something unexpected
"""
Then the last exit code should be 0

Scenario:
Given the package installed
Given a bash prompt in "test/" as working directory
When the "markdown-tools stories2csv" command is executed
Then it should print to the standard error:
"""
markdown-tools stories2csv

Creates csv file for Jira from markdown story definitions

Options:
--help Show help [boolean]
--version Show version number [boolean]
-i, --input_file [string] [required]
-o, --output_file [string] [required]

Missing required arguments: i, o

"""
Then the last exit code should be 1

Scenario:
Given the package installed
Given a bash prompt in "test/" as working directory
When the "markdown-tools stories2csv -i stories.md" command is executed
Then it should print to the standard error:
"""
markdown-tools stories2csv

Creates csv file for Jira from markdown story definitions

Options:
--help Show help [boolean]
--version Show version number [boolean]
-i, --input_file [string] [required]
-o, --output_file [string] [required]

Missing required argument: o

"""
Then the last exit code should be 1

Scenario:
Given the package installed
Given a bash prompt in "test/" as working directory
When the "markdown-tools stories2csv -i stories.md -o stories.csv" command is executed
Then it should print to the standard error:
"""
Unable to open file: "stories.md"

"""
Then the last exit code should be 1

12 changes: 12 additions & 0 deletions features/world/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export interface Cli {
cwd(directory: string): Promise<void>;
writeFile(args: {name: string, content: string}): Promise<void>;
run(command: string): Promise<void>;
runSilent(command: string): Promise<void>;
readFile(name: string): Promise<string>;
setup(directory: string): Promise<void>;
tearDown(): Promise<void>;
getStdOut(): Promise<string>;
getStdErr(): Promise<string>;
getLastExitCode(): Promise<number>;
}
32 changes: 32 additions & 0 deletions features/world/fakeCli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { Cli } from './cli';

export const fakeCli: () => Cli = () => ({
async cwd(directory: string): Promise<void> {
console.log(directory);
},
async writeFile({name, content}: {name: string, content: string}): Promise<void> {
console.log(name, content);
},
async run(content: string): Promise<void> {
console.log(content);
},
async runSilent(content: string): Promise<void> {
console.log(content);
},
async readFile(name: string): Promise<string> {
return name;
},
async setup(directory: string): Promise<void> {
console.log(`Set up in ${directory}`);
},
async tearDown(): Promise<void> {},
async getStdOut(): Promise<string> {
return '';
},
async getStdErr(): Promise<string> {
return '';
},
async getLastExitCode(): Promise<number> {
return 0;
},
});
83 changes: 83 additions & 0 deletions features/world/realCli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { promises as fs } from 'fs';
import { spawn } from 'child_process';
import path from 'path';
import type { Cli } from './cli';

interface CommandResult {
stdout: string;
stderr: string;
exitCode: number;
}

const runCliLine = (line: string): Promise<CommandResult> =>
new Promise(resolve => {
let stdout: string = '';
let stderr: string = '';
const [command, ...args] = line.split(' ');
const spawned = spawn(command, args);

spawned.stdout.on('data', data => {
stdout += data;
});

spawned.stderr.on('data', data => {
stderr += data;
});

spawned.on('close', exitCode => {
resolve({
stdout,
stderr,
exitCode,
});
})
});

export const realCli: () => Cli = () => {
const originalCwd = process.cwd();
let testDirectory: string = '.test';
let stdout: string = '';
let stderr: string = '';
let lastExitCode: number = 0;

return {
async cwd(directory: string) {
const fullPath = path.join(testDirectory, directory);
await fs.mkdir(fullPath, { recursive: true });
process.chdir(fullPath);
},
async writeFile({name, content}: {name: string, content: string}): Promise<void> {
return fs.writeFile(name, content, 'utf-8');
},
async run(content: string): Promise<void> {
const obj = await runCliLine(content);
stdout += obj.stdout;
stderr += obj.stderr;
lastExitCode = obj.exitCode;
},
async runSilent(content: string): Promise<void> {
await runCliLine(content);
},
async readFile(name: string): Promise<string> {
const content = await fs.readFile(name);
return content.toString();
},
async setup(directory: string): Promise<void> {
testDirectory = directory;
return fs.mkdir(directory, { recursive: true });
},
async tearDown(): Promise<void> {
process.chdir(originalCwd);
return fs.rmdir(testDirectory, { recursive: true })
},
async getStdOut(): Promise<string> {
return stdout;
},
async getStdErr(): Promise<string> {
return stderr;
},
async getLastExitCode(): Promise<number> {
return lastExitCode;
},
}
};
14 changes: 14 additions & 0 deletions features/world/world.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { setWorldConstructor } from 'cucumber';

import type { Cli } from './cli';
import { realCli } from './realCli';
import { fakeCli } from './fakeCli';

export class CliWorld {
cli: Cli;
constructor({parameters: {environment}}: {parameters: {environment: string}}) {
this.cli = environment == 'real' ? realCli() : fakeCli();
}
}

setWorldConstructor(CliWorld);
Loading