Skip to content

Commit

Permalink
Merge pull request #22 from chsergey/master
Browse files Browse the repository at this point in the history
Added Postman tests
  • Loading branch information
kielabokkie committed May 29, 2016
2 parents f40fdcc + ae620e9 commit dd0b332
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 19 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Thumbs.db
nbproject

# Folders to ignore
build
vendor
composer.lock
collection.json
blueman.phar
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,27 @@ Lastly, if you don't do either of the above you'll be prompted to set the host w
```sh
$ blueman convert api.json --host=https://api.{{host}}/v1
```

### Setting the Postman tests

You also may to define Postman's tests for your named Actions of Resources.

To use this feature do:

- create a Markdown formatted file in your path where `api.json` located (see --path option). By default, Blueman will be try get `blueman.tests.md` filename.
- read the markdown file example [test/api.test.md](test/api.test.md)
- write Postman tests for any Resource Action you want
- use `--include-tests` (default: unset) and `--tests-filename` (default: blueman.tests.md) options
- enjoy :)

#### Usage example

```sh
$ blueman convert api.json --include-tests
```

or

```sh
$ blueman convert api.json --include-tests --tests-filename=<YOUR_FILNAME>.md
```
2 changes: 1 addition & 1 deletion bin/console
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ set_time_limit(0);

use Symfony\Component\Console\Application;

$app = new Application('Blueman CLI', '1.0.0');
$app = new Application('Blueman CLI', '1.1.0');
$app->addCommands(array(
new Blueman\Console\Command\ConvertCommand(),
));
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
],
"require": {
"symfony/console": "~2.4",
"rhumsaa/uuid": "~2.7"
"ramsey/uuid": "~2.7"
},
"require-dev": {
"phpunit/phpunit": "~3.7",
Expand Down
117 changes: 111 additions & 6 deletions src/Blueman/Console/Command/ConvertCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ protected function configure()
InputOption::VALUE_REQUIRED,
'The base host of your API (e.g. https://api.example.com/v1).'
)
->addOption(
'tests-filename',
null,
InputOption::VALUE_OPTIONAL,
'The JSON file name with Postman tests (located at --path)',
'blueman.tests.md'
)
->addOption(
'include-tests',
null,
InputOption::VALUE_NONE,
'Add Postman tests to result JSON collection file (see --tests-filename)'
)
->setHelp(<<<EOT
The <info>convert</info> command converts an API Blueprint JSON file into a Postman collection.
EOT
Expand All @@ -49,7 +62,9 @@ protected function execute(InputInterface $input, OutputInterface $output)
{
$filePath = $input->getOption('path');

$file = $filePath.DIRECTORY_SEPARATOR.$input->getArgument('input-file');
$output->writeln('<info>Working Path: ' . $filePath . '</info>');

$file = $filePath . DIRECTORY_SEPARATOR . $input->getArgument('input-file');

if (!file_exists($file)) {
throw new \Exception(
Expand All @@ -65,10 +80,23 @@ protected function execute(InputInterface $input, OutputInterface $output)
);
}

$tests = array();
if($input->getOption('include-tests')) {

$testsFile = $filePath . $input->getOption('tests-filename');

if(file_exists($testsFile)) {
$output->writeln('<info>Using Blueman file with Postman tests: ' . $testsFile . '</info>');
$tests = $this->parseTestsFile($testsFile);
} else {
$output->writeln('<comment>Blueman file with Postman tests NOT found:' . $testsFile . '</comment>');
}
}

$blueprint = $blueprint->ast;

$collection = array();
$collection['id'] = (string)Uuid::uuid4();
$collection['id'] = (string) Uuid::uuid4();
$collection['name'] = $blueprint->name;
$collection['description'] = $blueprint->description;

Expand Down Expand Up @@ -101,6 +129,7 @@ protected function execute(InputInterface $input, OutputInterface $output)

$folders['order'] = array();
foreach ($resourceGroup->resources as $resource) {
/** @var object $action */
foreach ($resource->actions as $action) {
$actionId = (string)Uuid::uuid4();

Expand All @@ -114,13 +143,17 @@ protected function execute(InputInterface $input, OutputInterface $output)
$headers[] = sprintf('%s: %s', $header->name, $header->value);
}
$request['headers'] = implode("\n", $headers);
$request['data'] = (string)$exampleRequest->body;
$request['data'] = (string) $exampleRequest->body;
$request['dataMode'] = 'raw';
$request['collectionId'] = $collection['id'];
}
$request['url'] = $host.$this->parseUri($resource, $action);
$request['url'] = $host . $this->parseUri($resource, $action);
$request['name'] = $resource->uriTemplate;
$request['method'] = $action->method;
if($tests) {
$request['tests'] = $this->getTest($action->name, $tests);
}

$requests[] = $request;
}
}
Expand Down Expand Up @@ -150,8 +183,8 @@ protected function execute(InputInterface $input, OutputInterface $output)
/**
* Parses the URI to make sure any parameters are replaced with actual values
*
* @param stdObject $resource The current resource
* @param stdObject $action The current action
* @param object $resource The current resource
* @param object $action The current action
* @return string The parsed URI
*/
private function parseUri($resource, $action)
Expand Down Expand Up @@ -272,4 +305,76 @@ private function hasUriParams($uri)
{
return strpos($uri, '{') !== false;
}

/**
* Find test by resource Action name
* @param string$actionName
* @param object $tests [0 - prepend, 1 - tests by actions]
* @return string
*/
private function getTest($actionName, $tests)
{

return isset($tests[1][$actionName])
? $tests[0] . $tests[1][$actionName]
: '';
}

/**
* Parse Markdown with tests
* @todo to parse class?
* @param string $testsFile
* @return array
*/
private function parseTestsFile($testsFile)
{
if(!$markdown = file($testsFile, FILE_SKIP_EMPTY_LINES)) {
return array();
}

$tests = array();
$prepend = '';
$mode = false;
$head = false;
$append = false;

$heading = '/^(#+)\s+(.*)(\s*)$/';
$code = '/^(```)(.*)/';

foreach ($markdown as $line) {
$matches = array();
$head = $head ? $head : false;
if(preg_match($heading, $line, $matches)) {
$mode = $matches[1];
$head = trim($matches[2]);
$tests[$head] = '';
continue;
}
if(preg_match($code, $line, $matches) && $head) {
$append = !$append;
continue;
}
if($head && $mode && $append) {
switch ($mode) {
case '##':
$prepend .= $line;
break;
case '###':
$tests[$head] .= $line;
break;
}
}
}

foreach ($tests as $action => $test) {
if(!$test) {
unset($tests[$action]);
}
}

return array(
$prepend,
$tests,
);
}
}
49 changes: 38 additions & 11 deletions test/Blueman/ConvertCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ public function testNonAstException()
public function testParsingUriWithoutParams()
{
$cmd = new ConvertCommand();
$method = $this->getAccessibleParseUriMethod();

$method = $this->getAccessibleMethod('parseUri');
$resource = new stdClass();
$resource->uriTemplate = '/players';
$action = new stdClass();
Expand All @@ -82,7 +82,7 @@ public function testParsingUriWithoutParams()
public function testParsingUriWithSingleQueryParam()
{
$cmd = new ConvertCommand();
$method = $this->getAccessibleParseUriMethod();
$method = $this->getAccessibleMethod('parseUri');

$resource = new stdClass();
$resource->uriTemplate = '/players{?name}';
Expand All @@ -102,7 +102,7 @@ public function testParsingUriWithSingleQueryParam()
public function testParsingUriWithMultipleQueryParams()
{
$cmd = new ConvertCommand();
$method = $this->getAccessibleParseUriMethod();
$method = $this->getAccessibleMethod('parseUri');

$resource = new stdClass();
$resource->uriTemplate = '/players{?name,age}';
Expand All @@ -126,7 +126,7 @@ public function testParsingUriWithMultipleQueryParams()
public function testParsingUriWithSingleUriParam()
{
$cmd = new ConvertCommand();
$method = $this->getAccessibleParseUriMethod();
$method = $this->getAccessibleMethod('parseUri');

$resource = new stdClass();
$resource->uriTemplate = '/players/{name}';
Expand All @@ -146,7 +146,7 @@ public function testParsingUriWithSingleUriParam()
public function testParsingUriWithMultipleUriParams()
{
$cmd = new ConvertCommand();
$method = $this->getAccessibleParseUriMethod();
$method = $this->getAccessibleMethod('parseUri');

$resource = new stdClass();
$resource->uriTemplate = '/players/{name}/games/{game_id}';
Expand All @@ -170,7 +170,7 @@ public function testParsingUriWithMultipleUriParams()
public function testParsingUriWithMultipleUriAndQueryParams()
{
$cmd = new ConvertCommand();
$method = $this->getAccessibleParseUriMethod();
$method = $this->getAccessibleMethod('parseUri');

$resource = new stdClass();
$resource->uriTemplate = '/players/{name}/games/{game_id}{?filter,locale}';
Expand Down Expand Up @@ -198,16 +198,43 @@ public function testParsingUriWithMultipleUriAndQueryParams()

$this->assertEquals('/players/John/games/52387?filter=flunkyball&locale=US', $result);
}

public function testPostmanTests() {
$cmd = new ConvertCommand();
$method = $this->getAccessibleMethod('parseTestsFile');

$result = $method->invokeArgs($cmd, array('test/api.test.md'));

/**
* Isset code to prepend
*/
$this->assertArrayHasKey(0, $result);
$this->assertNotEmpty($result[0]);
/**
* Isset tests for action
*/
$this->assertArrayHasKey(1, $result);
$this->assertArrayHasKey('Create a Player', $result[1]);
$this->assertNotEmpty($result[1]['Create a Player'], 'Empty tests for action: Create a Player');
$this->assertNotEmpty($result[1]['Example 1'], 'Empty tests for action: Example 1');
/**
* No test for actions
*/
$this->assertArrayNotHasKey('Another action', $result[1]);
$this->assertArrayNotHasKey('Example 0', $result[1]);
$this->assertArrayNotHasKey('Example 2', $result[1]);
}

/**
* Helper to call `parseUri` on `ConvertCommand`
* Helper to get accessible method from `ConvertCommand`
*
* @return ReflectionMethod Accessible `ConvertCommand::parseUri`
* @param string $method
* @return ReflectionMethod
*/
private function getAccessibleParseUriMethod()
private function getAccessibleMethod($method)
{
$reflection = new ReflectionClass('\Blueman\Console\Command\ConvertCommand');
$method = $reflection->getMethod('parseUri');
$method = $reflection->getMethod($method);
$method->setAccessible(true);

return $method;
Expand Down
60 changes: 60 additions & 0 deletions test/api.test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Tests for Postman
- [Postman Sandbox Doc](https://www.getpostman.com/docs/sandbox)
- [Postman Sandbox Examples](https://www.getpostman.com/docs/testing_examples)

This is an example of [Blueman](https://github.com/pixelfusion/blueman) file with test scripts for Postman.

`### H3` heading **must** contains [API Blueprint](https://apiblueprint.org)'s [Action Section](https://github.com/apiaryio/api-blueprint/blob/master/API%20Blueprint%20Specification.md#def-action-section) <identifier>: action defined by name, i.e. `## <identifier> [<HTTP request method>]`.

Postman test script **must** be followed by this heading section.

All scripts **must** be started with Markdown code block definition [example](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code).

All test scripts for Actions are optional.

## Initial code
Header `## H2` above wiil be ignored.

This section **may** by empty or not defined.

Code below will be prepend to all test scripts. **May** be empty.

```javascript
var jsonData = JSON.parse(responseBody);
```

### Create a Player
This text will be ignored. Use it to comments.

```javascript
tests["Content-Type is present"] = postman.getResponseHeader("Content-Type"); //Note: the getResponseHeader() method returns the header value, if it exists.
tests["Status code is 200"] = responseCode.code === 200;
```

This text also will be ignored.


### Another action
No tests.


### Example 0
Empty test

```
```


### Example 1
Code without language definition

```
var a = 1;
```


### Example 2
Empty code block with language definition

```javascript
```

0 comments on commit dd0b332

Please sign in to comment.