├── .editorconfig ├── .gitignore ├── .php_cs ├── .travis.yml ├── README.md ├── behat.yml ├── composer.json ├── composer.lock ├── features ├── bootstrap │ └── FeatureContext.php ├── common.feature ├── equivalence.feature ├── files.feature ├── inclusion.feature ├── memory.feature ├── paths.feature ├── sizes.feature └── types.feature ├── phpspec.yml ├── spec ├── JsonSpec │ ├── Behat │ │ └── Helper │ │ │ └── MemoryHelperSpec.php │ └── JsonLoaderSpec.php └── support │ └── files │ ├── one.json │ ├── project │ ├── one.json │ ├── two.json │ └── version │ │ ├── one.json │ │ └── two.json │ └── two.json └── src ├── Behat ├── Context │ ├── ArgumentResolver │ │ └── DependencyResolver.php │ ├── ContextResolver │ │ └── JsonSpecContextResolver.php │ ├── Initializer │ │ ├── JsonHolderAwareInitializer.php │ │ ├── JsonMatcherAwareInitializer.php │ │ └── MemoryHelperAwareInitializer.php │ ├── JsonHolderAware.php │ ├── JsonMatcherAware.php │ ├── JsonSpecContext.php │ ├── MemoryHelperAware.php │ └── Traits │ │ └── JsonMatcherAwareTrait.php ├── Extension.php ├── Helper │ └── MemoryHelper.php ├── JsonProvider │ ├── JsonHolder.php │ └── MinkJsonProvider.php └── Resources │ └── services │ ├── mink_integration.yml │ └── services.yml ├── Exception ├── JsonSpecException.php ├── MissingDirectoryException.php └── MissingFileException.php └── JsonLoader.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [**.feature] 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [**.php] 17 | indent_style = space 18 | indent_size = 4 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /bin 3 | /vendor -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | in(__DIR__.'/src') 5 | ; 6 | 7 | return Symfony\CS\Config\Config::create() 8 | ->finder($finder) 9 | ; 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - hhvm 8 | 9 | matrix: 10 | allow_failures: 11 | - php: hhvm 12 | 13 | before_script: 14 | - composer install 15 | 16 | script: 17 | - bin/phpspec run 18 | - bin/behat 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Json Spec 2 | =================== 3 | 4 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/fesor/json_spec/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/fesor/json_spec/?branch=master) [![Build Status](https://travis-ci.org/fesor/json_spec.svg?branch=master)](https://travis-ci.org/fesor/json_spec) 5 | 6 | Json Spec provides set of easy-to-use matcher that should help you to validate data in JSON responses from your api with less pain. 7 | 8 | If you working with JSON-based REST APIs there are several issues: 9 | 10 | - You can't simply check is a response is equal to given string as there are things like server-generated IDs or keys sorting. 11 | - Key ordering should be the same both for your API and for expected JSON. 12 | - Matching the whole responses breaks DRY for the spec 13 | 14 | `json_spec` solves this problems as it normalize JSON before match it. 15 | 16 | Let's see simple example: 17 | 18 | 19 | 20 | 32 | 42 | 43 |
21 |
 22 | 
 23 | {
 24 |    "id": 1421,
 25 |    "created_at": "1977-05-25 00:00:00"
 26 |    "last_name": "Skywalker",
 27 |    "first_name": "Luke",
 28 | }
 29 |         
 30 |       
31 |
33 |
 34 | 
 35 | {
 36 |    "first_name": "Luke",
 37 |    "last_name": "Skywalker"
 38 | }
 39 |         
 40 |       
41 |
44 | 45 | `json_spec` will assume that this JSON documents are equal. Before asserting, `json_spec` will exclude keys `id`, `created_at` and `updated_at` from response JSON (List of excluded keys is configurable). Then it will normalize JSON (reorder keys, pretty-print) and after, just check for string equality. That's all. Also you can match JSON by given path instead of describing whole response in your specification, check is JSON collection contains some record and many more. 46 | 47 | ## Installation 48 | 49 | To install `json_spec` you may want to use composer: 50 | ``` 51 | composer require --dev fesor/json_spec 52 | ``` 53 | 54 | After that you should enable `json_spec` behat extension and optionally add context provided with json_spec in your ```behat.yml```. For example: 55 | ``` 56 | default: 57 | suites: 58 | default: 59 | contexts: 60 | - FeatureContext 61 | - json_spec 62 | extensions: 63 | JsonSpec\Behat\Extension: ~ 64 | ``` 65 | 66 | 67 | ## Usage 68 | 69 | json_spec provides you two ways of how you can use matchers: 70 | 71 | - Using json_spec context, which implements steps for verifying json responses. This approach best fits for cases, when developers uses feature specs as API documentation and tests. 72 | - Inject `JsonMatcherFactory` into your context, so you can use it in your step definitions. This approach preferable since you can practice BDD with it. For more information about how to write feature specs please read [Modeling by examples](http://everzet.com/post/99045129766/introducing-modelling-by-example). 73 | 74 | ### Using `json_spec` Context 75 | 76 | json_spec provides Behat context which implements steps utilizing all matchers provided by `json_matcher`. This is perfect for testing your app's JSON API. 77 | 78 | One note. `json_spec` should have access to responses. If you are using Mink, that it's just fine. `json_spec` will get responses from Mink. This means that all you need to do to start working, is just to enable MinkExtension in your `behat.yml`: 79 | ``` 80 | default: 81 | suites: 82 | default: 83 | contexts: 84 | - FeatureContext 85 | - json_spec 86 | extensions: 87 | JsonSpec\Behat\Extension: ~ 88 | Behat\MinkExtension: 89 | base_url: 'http://localhost:8047' 90 | sessions: 91 | default: 92 | goutte: ~ 93 | ``` 94 | 95 | That's all, now `json_spec` have access to all responses. You may also want to use `behatch:rest` context from [sanpii/behatch-contexts](https://github.com/sanpii/behatch-contexts) instead of mink context. 96 | 97 | If you are using your own context, which not using Mink, then just implement ```JsonHolderAware``` interface for your context: 98 | 99 | ```php 100 | use \JsonSpec\Behat\Context\JsonHolderAware; 101 | use \Behat\Behat\Context\Context; 102 | 103 | class MyRestApiFeatureContext implements Context, JsonHolderAware 104 | { 105 | /** 106 | * @var \JsonSpec\Behat\JsonProvider\JsonHolder 107 | */ 108 | private $jsonHolder; 109 | 110 | /** 111 | * @When /^I request "([^"]*)"$/ 112 | */ 113 | public function iRequest($pageUrl) 114 | { 115 | // ... make request and get response body as string 116 | $this->jsonHolder->setJson($responseBody); 117 | } 118 | } 119 | ``` 120 | 121 | Now, you can use the json_spec steps in your features: 122 | 123 | ``` 124 | Feature: User API 125 | Background: 126 | Given the following users exist: 127 | | id | first_name | last_name | 128 | | 1 | Steve | Richert | 129 | | 2 | Catie | Richert | 130 | And "Steve Richert" is friends with "Catie Richert" 131 | 132 | Scenario: Index action 133 | When I visit "/users.json" 134 | Then the JSON response should have 2 users 135 | And the JSON response at "0/id" should be 1 136 | And the JSON response at "1/id" should be 2 137 | 138 | Scenario: Show action 139 | When I visit "/users/1.json" 140 | Then the JSON response at "first_name" should be "Steve" 141 | And the JSON response at "last_name" should be "Richert" 142 | And the JSON response should have "created_at" 143 | And the JSON response at "created_at" should be a string 144 | And the JSON response at "friends" should be: 145 | """ 146 | [ 147 | { 148 | "id": 2, 149 | "first_name": "Catie", 150 | "last_name": "Richert" 151 | } 152 | ] 153 | """ 154 | ``` 155 | 156 | The background steps above and the "visit" steps aren't provided by json_spec. The remaining steps, json_spec provides. They're versatile and can be used in plenty of different formats: 157 | 158 | ``` 159 | Then the JSON should be: 160 | """ 161 | { 162 | "key": "value" 163 | } 164 | """ 165 | Then the JSON at "path" should be: 166 | """ 167 | [ 168 | "entry", 169 | "entry" 170 | ] 171 | """ 172 | 173 | Then the JSON should be {"key":"value"} 174 | Then the JSON at "path" should be {"key":"value"} 175 | Then the JSON should be ["entry","entry"] 176 | Then the JSON at "path" should be ["entry","entry"] 177 | Then the JSON at "path" should be "string" 178 | Then the JSON at "path" should be 10 179 | Then the JSON at "path" should be 10.0 180 | Then the JSON at "path" should be 1e+1 181 | Then the JSON at "path" should be true 182 | Then the JSON at "path" should be false 183 | Then the JSON at "path" should be null 184 | 185 | Then the JSON should include: 186 | """ 187 | { 188 | "key": "value" 189 | } 190 | """ 191 | Then the JSON at "path" should include: 192 | """ 193 | [ 194 | "entry", 195 | "entry" 196 | ] 197 | """ 198 | 199 | Then the JSON should include {"key":"value"} 200 | Then the JSON at "path" should include {"key":"value"} 201 | Then the JSON should include ["entry","entry"] 202 | Then the JSON at "path" should include ["entry","entry"] 203 | Then the JSON should include "string" 204 | Then the JSON at "path" should include "string" 205 | Then the JSON should include 10 206 | Then the JSON at "path" should include 10 207 | Then the JSON should include 10.0 208 | Then the JSON at "path" should include 10.0 209 | Then the JSON should include 1e+1 210 | Then the JSON at "path" should include 1e+1 211 | Then the JSON should include true 212 | Then the JSON at "path" should include true 213 | Then the JSON should include false 214 | Then the JSON at "path" should include false 215 | Then the JSON should include null 216 | Then the JSON at "path" should include null 217 | 218 | Then the JSON should have "path" 219 | 220 | Then the JSON should be a hash 221 | Then the JSON at "path" should be an array 222 | Then the JSON at "path" should be a float 223 | 224 | Then the JSON should have 1 entry 225 | Then the JSON at "path" should have 2 entries 226 | Then the JSON should have 3 keys 227 | Then the JSON should have 4 whatevers 228 | ``` 229 | 230 | All instances of "should" above could be followed by "not" and all instances of "JSON" could be downcased and/or followed by "response." 231 | 232 | ### Table Format 233 | Another step exists that uses Behat's table formatting and wraps two of the above steps: 234 | 235 | Then the JSON should have the following: 236 | 237 | ``` 238 | | path/0 | {"key":"value"} | 239 | | path/1 | ["entry","entry"] | 240 | ``` 241 | 242 | Any number of rows can be given. The step above is equivalent to: 243 | 244 | ``` 245 | Then the JSON at "path/0" should be {"key":"value"} 246 | And the JSON at "path/1" should be ["entry","entry"] 247 | ``` 248 | 249 | If only one column is given: 250 | 251 | ``` 252 | Then the JSON should have the following: 253 | | path/0 | 254 | | path/1 | 255 | ``` 256 | 257 | This is equivalent to: 258 | 259 | ``` 260 | Then the JSON should have "path/0" 261 | And the JSON should have "path/1" 262 | ``` 263 | 264 | ### JSON Memory 265 | There's one more Behat step that json_spec provides which hasn't been used above. It's used to memorize JSON for reuse in later steps. You can "keep" all or a portion of the JSON by giving a name by which to remember it. 266 | 267 | ``` 268 | Feature: User API 269 | Scenario: Index action includes full user JSON 270 | Given the following user exists: 271 | | id | first_name | last_name | 272 | | 1 | Steve | Richert | 273 | And I visit "/users/1.json" 274 | And I keep the JSON response as "USER_1" 275 | When I visit "/users.json" 276 | Then the JSON response should be: 277 | """ 278 | [ 279 | {$USER_1} 280 | ] 281 | """ 282 | ``` 283 | 284 | You can memorize JSON at a path: 285 | 286 | ``` 287 | Given I keep the JSON response at "first_name" as "FIRST_NAME" 288 | ``` 289 | 290 | You can remember JSON at a path: 291 | 292 | ``` 293 | Then the JSON response at "0/first_name" should be: 294 | """ 295 | {$FIRST_NAME} 296 | """ 297 | ``` 298 | 299 | You can also remember JSON inline: 300 | 301 | ``` 302 | Then the JSON response at "0/first_name" should be {$FIRST_NAME} 303 | ``` 304 | 305 | Also starting from version `0.2.3` you can inject memory helper into your feature context to define some variables. To do so, your context should implement `MemoryHelperAware` interface. 306 | 307 | #### More Examples 308 | 309 | Check out the [features](https://github.com/fesor/json_spec/blob/master/features) to see all the various ways you can use json_spec. 310 | 311 | ### Using json matcher in your step definitions 312 | 313 | To inject JsonMatcherFactory you can just implement `JsonMatcherAware` interface in your context, or just use `JsonMatcherAwareTrait`: 314 | 315 | ```gherkin 316 | Scenario: User should be able to add another user in friend list 317 | Given I signed in as Bob 318 | And I view Alice's profile 319 | When I add her to my friends list 320 | Then Alice should appear in my friend list 321 | ``` 322 | 323 | ```php 324 | use JsonMatcherAwareTrait; 325 | 326 | /** 327 | * @Then :user should appear in my friend list 328 | */ 329 | function userShouldAppearInFriendList(User $user) 330 | { 331 | $this 332 | ->json($this->lastResponse) 333 | ->haveSize(1) 334 | ->includes($this->serialize($user, ['short_profile']) 335 | ; 336 | } 337 | 338 | ``` 339 | 340 | ## Contributing 341 | If you come across any issues, please [tell me](https://github.com/fesor/json_spec/issues) . Pull requests (with tests) are appreciated. No pull request is too small. Please help with: 342 | 343 | - Reporting bugs 344 | - Suggesting features 345 | - Writing or improving documentation 346 | - Fixing typos 347 | - Cleaning whitespace 348 | - Refactoring code 349 | - Adding tests 350 | - Closing [issues](https://github.com/fesor/json_spec/issues) 351 | 352 | If you report a bug and don't include a fix, please include a failing test. 353 | 354 | ## Credits 355 | - [json_spec](https://github.com/collectiveidea/json_spec) - Ruby's gem for handling JSON in RSpec and Cucumber. This library is mainly just php port of this great library. 356 | 357 | -------------------------------------------------------------------------------- /behat.yml: -------------------------------------------------------------------------------- 1 | default: 2 | suites: 3 | default: 4 | contexts: 5 | - FeatureContext 6 | - json_spec 7 | extensions: 8 | JsonSpec\Behat\Extension: 9 | excluded_keys: [id, created_at, updated_at] 10 | json_directory: './spec/support/files' 11 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fesor/json_spec", 3 | "description": "Easily handle JSON Structures with PhpSpec and Behat", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Sergey Protko", 8 | "email": "fesors@gmail.com" 9 | } 10 | ], 11 | "minimum-stability": "stable", 12 | "autoload": { 13 | "psr-4": { 14 | "JsonSpec\\": "src/" 15 | } 16 | }, 17 | "config": { 18 | "bin-dir": "bin" 19 | }, 20 | "require": { 21 | "php": ">=5.4.0", 22 | "fesor/json_matcher": "~0.2.1" 23 | }, 24 | "require-dev": { 25 | "phpspec/phpspec": "2.1.*@dev", 26 | "behat/behat": "~3.0.13@stable", 27 | "fabpot/php-cs-fixer": "~0.5" 28 | }, 29 | "conflict": { 30 | "behat/behat": "<3.0.13" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "4bb741d104abe8389233e3a57babca78", 8 | "content-hash": "8967055e15043c9eb14bf40788c60a86", 9 | "packages": [ 10 | { 11 | "name": "fesor/json_matcher", 12 | "version": "0.2.3", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/fesor/json_matcher.git", 16 | "reference": "892f0371117990c31bb3199e1279be58fd0f078d" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/fesor/json_matcher/zipball/892f0371117990c31bb3199e1279be58fd0f078d", 21 | "reference": "892f0371117990c31bb3199e1279be58fd0f078d", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": ">=5.4.0" 26 | }, 27 | "require-dev": { 28 | "fabpot/php-cs-fixer": "~0.5", 29 | "henrikbjorn/phpspec-code-coverage": "~1.0", 30 | "phpspec/phpspec": "2.1.*@dev" 31 | }, 32 | "type": "library", 33 | "autoload": { 34 | "psr-4": { 35 | "Fesor\\JsonMatcher\\": "src/", 36 | "spec\\Fesor\\JsonMatcher\\": "spec/" 37 | } 38 | }, 39 | "notification-url": "https://packagist.org/downloads/", 40 | "license": [ 41 | "MIT" 42 | ], 43 | "authors": [ 44 | { 45 | "name": "Sergey Protko", 46 | "email": "fesors@gmail.com" 47 | } 48 | ], 49 | "description": "Collection if JSON matchers for painless usage", 50 | "time": "2015-10-22 21:21:18" 51 | } 52 | ], 53 | "packages-dev": [ 54 | { 55 | "name": "behat/behat", 56 | "version": "v3.0.15", 57 | "source": { 58 | "type": "git", 59 | "url": "https://github.com/Behat/Behat.git", 60 | "reference": "b35ae3d45332d80c532af69cc36f780a9397a996" 61 | }, 62 | "dist": { 63 | "type": "zip", 64 | "url": "https://api.github.com/repos/Behat/Behat/zipball/b35ae3d45332d80c532af69cc36f780a9397a996", 65 | "reference": "b35ae3d45332d80c532af69cc36f780a9397a996", 66 | "shasum": "" 67 | }, 68 | "require": { 69 | "behat/gherkin": "~4.3", 70 | "behat/transliterator": "~1.0", 71 | "ext-mbstring": "*", 72 | "php": ">=5.3.3", 73 | "symfony/class-loader": "~2.1", 74 | "symfony/config": "~2.3", 75 | "symfony/console": "~2.1", 76 | "symfony/dependency-injection": "~2.1", 77 | "symfony/event-dispatcher": "~2.1", 78 | "symfony/translation": "~2.3", 79 | "symfony/yaml": "~2.1" 80 | }, 81 | "require-dev": { 82 | "phpspec/prophecy-phpunit": "~1.0", 83 | "phpunit/phpunit": "~4.0", 84 | "symfony/process": "~2.1" 85 | }, 86 | "suggest": { 87 | "behat/mink-extension": "for integration with Mink testing framework", 88 | "behat/symfony2-extension": "for integration with Symfony2 web framework", 89 | "behat/yii-extension": "for integration with Yii web framework" 90 | }, 91 | "bin": [ 92 | "bin/behat" 93 | ], 94 | "type": "library", 95 | "extra": { 96 | "branch-alias": { 97 | "dev-master": "3.0.x-dev" 98 | } 99 | }, 100 | "autoload": { 101 | "psr-0": { 102 | "Behat\\Behat": "src/", 103 | "Behat\\Testwork": "src/" 104 | } 105 | }, 106 | "notification-url": "https://packagist.org/downloads/", 107 | "license": [ 108 | "MIT" 109 | ], 110 | "authors": [ 111 | { 112 | "name": "Konstantin Kudryashov", 113 | "email": "ever.zet@gmail.com", 114 | "homepage": "http://everzet.com" 115 | } 116 | ], 117 | "description": "Scenario-oriented BDD framework for PHP 5.3", 118 | "homepage": "http://behat.org/", 119 | "keywords": [ 120 | "Agile", 121 | "BDD", 122 | "ScenarioBDD", 123 | "Scrum", 124 | "StoryBDD", 125 | "User story", 126 | "business", 127 | "development", 128 | "documentation", 129 | "examples", 130 | "symfony", 131 | "testing" 132 | ], 133 | "time": "2015-02-22 14:10:33" 134 | }, 135 | { 136 | "name": "behat/gherkin", 137 | "version": "v4.3.0", 138 | "source": { 139 | "type": "git", 140 | "url": "https://github.com/Behat/Gherkin.git", 141 | "reference": "43777c51058b77bcd84a8775b7a6ad05e986b5db" 142 | }, 143 | "dist": { 144 | "type": "zip", 145 | "url": "https://api.github.com/repos/Behat/Gherkin/zipball/43777c51058b77bcd84a8775b7a6ad05e986b5db", 146 | "reference": "43777c51058b77bcd84a8775b7a6ad05e986b5db", 147 | "shasum": "" 148 | }, 149 | "require": { 150 | "php": ">=5.3.1" 151 | }, 152 | "require-dev": { 153 | "phpunit/phpunit": "~4.0", 154 | "symfony/yaml": "~2.1" 155 | }, 156 | "suggest": { 157 | "symfony/yaml": "If you want to parse features, represented in YAML files" 158 | }, 159 | "type": "library", 160 | "extra": { 161 | "branch-alias": { 162 | "dev-master": "4.2-dev" 163 | } 164 | }, 165 | "autoload": { 166 | "psr-0": { 167 | "Behat\\Gherkin": "src/" 168 | } 169 | }, 170 | "notification-url": "https://packagist.org/downloads/", 171 | "license": [ 172 | "MIT" 173 | ], 174 | "authors": [ 175 | { 176 | "name": "Konstantin Kudryashov", 177 | "email": "ever.zet@gmail.com", 178 | "homepage": "http://everzet.com" 179 | } 180 | ], 181 | "description": "Gherkin DSL parser for PHP 5.3", 182 | "homepage": "http://behat.org/", 183 | "keywords": [ 184 | "BDD", 185 | "Behat", 186 | "Cucumber", 187 | "DSL", 188 | "gherkin", 189 | "parser" 190 | ], 191 | "time": "2014-06-06 01:24:32" 192 | }, 193 | { 194 | "name": "behat/transliterator", 195 | "version": "v1.0.1", 196 | "source": { 197 | "type": "git", 198 | "url": "https://github.com/Behat/Transliterator.git", 199 | "reference": "c93521d3462a554332d1ef5bb0e9b5b8ca4106c4" 200 | }, 201 | "dist": { 202 | "type": "zip", 203 | "url": "https://api.github.com/repos/Behat/Transliterator/zipball/c93521d3462a554332d1ef5bb0e9b5b8ca4106c4", 204 | "reference": "c93521d3462a554332d1ef5bb0e9b5b8ca4106c4", 205 | "shasum": "" 206 | }, 207 | "require": { 208 | "php": ">=5.3.3" 209 | }, 210 | "type": "library", 211 | "extra": { 212 | "branch-alias": { 213 | "dev-master": "1.0-dev" 214 | } 215 | }, 216 | "autoload": { 217 | "psr-0": { 218 | "Behat\\Transliterator": "src/" 219 | } 220 | }, 221 | "notification-url": "https://packagist.org/downloads/", 222 | "license": [ 223 | "Artistic-1.0" 224 | ], 225 | "description": "String transliterator", 226 | "keywords": [ 227 | "i18n", 228 | "slug", 229 | "transliterator" 230 | ], 231 | "time": "2014-05-15 22:08:22" 232 | }, 233 | { 234 | "name": "doctrine/instantiator", 235 | "version": "1.0.5", 236 | "source": { 237 | "type": "git", 238 | "url": "https://github.com/doctrine/instantiator.git", 239 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 240 | }, 241 | "dist": { 242 | "type": "zip", 243 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 244 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 245 | "shasum": "" 246 | }, 247 | "require": { 248 | "php": ">=5.3,<8.0-DEV" 249 | }, 250 | "require-dev": { 251 | "athletic/athletic": "~0.1.8", 252 | "ext-pdo": "*", 253 | "ext-phar": "*", 254 | "phpunit/phpunit": "~4.0", 255 | "squizlabs/php_codesniffer": "~2.0" 256 | }, 257 | "type": "library", 258 | "extra": { 259 | "branch-alias": { 260 | "dev-master": "1.0.x-dev" 261 | } 262 | }, 263 | "autoload": { 264 | "psr-4": { 265 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 266 | } 267 | }, 268 | "notification-url": "https://packagist.org/downloads/", 269 | "license": [ 270 | "MIT" 271 | ], 272 | "authors": [ 273 | { 274 | "name": "Marco Pivetta", 275 | "email": "ocramius@gmail.com", 276 | "homepage": "http://ocramius.github.com/" 277 | } 278 | ], 279 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 280 | "homepage": "https://github.com/doctrine/instantiator", 281 | "keywords": [ 282 | "constructor", 283 | "instantiate" 284 | ], 285 | "time": "2015-06-14 21:17:01" 286 | }, 287 | { 288 | "name": "fabpot/php-cs-fixer", 289 | "version": "v0.5.7", 290 | "source": { 291 | "type": "git", 292 | "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", 293 | "reference": "29477d018cb78549a3f96656a36b93e99e1edbaf" 294 | }, 295 | "dist": { 296 | "type": "zip", 297 | "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/29477d018cb78549a3f96656a36b93e99e1edbaf", 298 | "reference": "29477d018cb78549a3f96656a36b93e99e1edbaf", 299 | "shasum": "" 300 | }, 301 | "require": { 302 | "php": ">=5.3.6", 303 | "sebastian/diff": "1.1.*", 304 | "symfony/console": "~2.1", 305 | "symfony/filesystem": "~2.1", 306 | "symfony/finder": "~2.1" 307 | }, 308 | "bin": [ 309 | "php-cs-fixer" 310 | ], 311 | "type": "application", 312 | "extra": { 313 | "branch-alias": { 314 | "dev-master": "0.5.x-dev" 315 | } 316 | }, 317 | "autoload": { 318 | "psr-0": { 319 | "Symfony\\CS": "." 320 | } 321 | }, 322 | "notification-url": "https://packagist.org/downloads/", 323 | "license": [ 324 | "MIT" 325 | ], 326 | "authors": [ 327 | { 328 | "name": "Fabien Potencier", 329 | "email": "fabien@symfony.com" 330 | } 331 | ], 332 | "description": "A script to automatically fix Symfony Coding Standard", 333 | "time": "2014-08-07 12:40:36" 334 | }, 335 | { 336 | "name": "phpdocumentor/reflection-docblock", 337 | "version": "2.0.4", 338 | "source": { 339 | "type": "git", 340 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 341 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" 342 | }, 343 | "dist": { 344 | "type": "zip", 345 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", 346 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", 347 | "shasum": "" 348 | }, 349 | "require": { 350 | "php": ">=5.3.3" 351 | }, 352 | "require-dev": { 353 | "phpunit/phpunit": "~4.0" 354 | }, 355 | "suggest": { 356 | "dflydev/markdown": "~1.0", 357 | "erusev/parsedown": "~1.0" 358 | }, 359 | "type": "library", 360 | "extra": { 361 | "branch-alias": { 362 | "dev-master": "2.0.x-dev" 363 | } 364 | }, 365 | "autoload": { 366 | "psr-0": { 367 | "phpDocumentor": [ 368 | "src/" 369 | ] 370 | } 371 | }, 372 | "notification-url": "https://packagist.org/downloads/", 373 | "license": [ 374 | "MIT" 375 | ], 376 | "authors": [ 377 | { 378 | "name": "Mike van Riel", 379 | "email": "mike.vanriel@naenius.com" 380 | } 381 | ], 382 | "time": "2015-02-03 12:10:50" 383 | }, 384 | { 385 | "name": "phpspec/php-diff", 386 | "version": "v1.0.2", 387 | "source": { 388 | "type": "git", 389 | "url": "https://github.com/phpspec/php-diff.git", 390 | "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a" 391 | }, 392 | "dist": { 393 | "type": "zip", 394 | "url": "https://api.github.com/repos/phpspec/php-diff/zipball/30e103d19519fe678ae64a60d77884ef3d71b28a", 395 | "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a", 396 | "shasum": "" 397 | }, 398 | "type": "library", 399 | "autoload": { 400 | "psr-0": { 401 | "Diff": "lib/" 402 | } 403 | }, 404 | "notification-url": "https://packagist.org/downloads/", 405 | "license": [ 406 | "BSD-3-Clause" 407 | ], 408 | "authors": [ 409 | { 410 | "name": "Chris Boulton", 411 | "homepage": "http://github.com/chrisboulton", 412 | "role": "Original developer" 413 | } 414 | ], 415 | "description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).", 416 | "time": "2013-11-01 13:02:21" 417 | }, 418 | { 419 | "name": "phpspec/phpspec", 420 | "version": "2.1.1", 421 | "source": { 422 | "type": "git", 423 | "url": "https://github.com/phpspec/phpspec.git", 424 | "reference": "66a1df93099282b1514e9e001fcf6e9393f7783d" 425 | }, 426 | "dist": { 427 | "type": "zip", 428 | "url": "https://api.github.com/repos/phpspec/phpspec/zipball/66a1df93099282b1514e9e001fcf6e9393f7783d", 429 | "reference": "66a1df93099282b1514e9e001fcf6e9393f7783d", 430 | "shasum": "" 431 | }, 432 | "require": { 433 | "doctrine/instantiator": "~1.0,>=1.0.1", 434 | "php": ">=5.3.3", 435 | "phpspec/php-diff": "~1.0.0", 436 | "phpspec/prophecy": "~1.1", 437 | "sebastian/exporter": "~1.0", 438 | "symfony/console": "~2.3", 439 | "symfony/event-dispatcher": "~2.1", 440 | "symfony/finder": "~2.1", 441 | "symfony/process": "~2.1", 442 | "symfony/yaml": "~2.1" 443 | }, 444 | "require-dev": { 445 | "behat/behat": "~3.0,>=3.0.11", 446 | "bossa/phpspec2-expect": "~1.0", 447 | "symfony/filesystem": "~2.1" 448 | }, 449 | "suggest": { 450 | "phpspec/nyan-formatters": "~1.0 – Adds Nyan formatters" 451 | }, 452 | "bin": [ 453 | "bin/phpspec" 454 | ], 455 | "type": "library", 456 | "extra": { 457 | "branch-alias": { 458 | "dev-master": "2.1.x-dev" 459 | } 460 | }, 461 | "autoload": { 462 | "psr-0": { 463 | "PhpSpec": "src/" 464 | } 465 | }, 466 | "notification-url": "https://packagist.org/downloads/", 467 | "license": [ 468 | "MIT" 469 | ], 470 | "authors": [ 471 | { 472 | "name": "Konstantin Kudryashov", 473 | "email": "ever.zet@gmail.com", 474 | "homepage": "http://everzet.com" 475 | }, 476 | { 477 | "name": "Marcello Duarte", 478 | "homepage": "http://marcelloduarte.net/" 479 | } 480 | ], 481 | "description": "Specification-oriented BDD framework for PHP 5.3+", 482 | "homepage": "http://phpspec.net/", 483 | "keywords": [ 484 | "BDD", 485 | "SpecBDD", 486 | "TDD", 487 | "spec", 488 | "specification", 489 | "testing", 490 | "tests" 491 | ], 492 | "time": "2015-01-09 13:21:45" 493 | }, 494 | { 495 | "name": "phpspec/prophecy", 496 | "version": "v1.4.1", 497 | "source": { 498 | "type": "git", 499 | "url": "https://github.com/phpspec/prophecy.git", 500 | "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373" 501 | }, 502 | "dist": { 503 | "type": "zip", 504 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", 505 | "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", 506 | "shasum": "" 507 | }, 508 | "require": { 509 | "doctrine/instantiator": "^1.0.2", 510 | "phpdocumentor/reflection-docblock": "~2.0", 511 | "sebastian/comparator": "~1.1" 512 | }, 513 | "require-dev": { 514 | "phpspec/phpspec": "~2.0" 515 | }, 516 | "type": "library", 517 | "extra": { 518 | "branch-alias": { 519 | "dev-master": "1.4.x-dev" 520 | } 521 | }, 522 | "autoload": { 523 | "psr-0": { 524 | "Prophecy\\": "src/" 525 | } 526 | }, 527 | "notification-url": "https://packagist.org/downloads/", 528 | "license": [ 529 | "MIT" 530 | ], 531 | "authors": [ 532 | { 533 | "name": "Konstantin Kudryashov", 534 | "email": "ever.zet@gmail.com", 535 | "homepage": "http://everzet.com" 536 | }, 537 | { 538 | "name": "Marcello Duarte", 539 | "email": "marcello.duarte@gmail.com" 540 | } 541 | ], 542 | "description": "Highly opinionated mocking framework for PHP 5.3+", 543 | "homepage": "https://github.com/phpspec/prophecy", 544 | "keywords": [ 545 | "Double", 546 | "Dummy", 547 | "fake", 548 | "mock", 549 | "spy", 550 | "stub" 551 | ], 552 | "time": "2015-04-27 22:15:08" 553 | }, 554 | { 555 | "name": "sebastian/comparator", 556 | "version": "1.1.0", 557 | "source": { 558 | "type": "git", 559 | "url": "https://github.com/sebastianbergmann/comparator.git", 560 | "reference": "c484a80f97573ab934e37826dba0135a3301b26a" 561 | }, 562 | "dist": { 563 | "type": "zip", 564 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c484a80f97573ab934e37826dba0135a3301b26a", 565 | "reference": "c484a80f97573ab934e37826dba0135a3301b26a", 566 | "shasum": "" 567 | }, 568 | "require": { 569 | "php": ">=5.3.3", 570 | "sebastian/diff": "~1.1", 571 | "sebastian/exporter": "~1.0" 572 | }, 573 | "require-dev": { 574 | "phpunit/phpunit": "~4.1" 575 | }, 576 | "type": "library", 577 | "extra": { 578 | "branch-alias": { 579 | "dev-master": "1.1.x-dev" 580 | } 581 | }, 582 | "autoload": { 583 | "classmap": [ 584 | "src/" 585 | ] 586 | }, 587 | "notification-url": "https://packagist.org/downloads/", 588 | "license": [ 589 | "BSD-3-Clause" 590 | ], 591 | "authors": [ 592 | { 593 | "name": "Jeff Welch", 594 | "email": "whatthejeff@gmail.com" 595 | }, 596 | { 597 | "name": "Volker Dusch", 598 | "email": "github@wallbash.com" 599 | }, 600 | { 601 | "name": "Bernhard Schussek", 602 | "email": "bschussek@2bepublished.at" 603 | }, 604 | { 605 | "name": "Sebastian Bergmann", 606 | "email": "sebastian@phpunit.de" 607 | } 608 | ], 609 | "description": "Provides the functionality to compare PHP values for equality", 610 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 611 | "keywords": [ 612 | "comparator", 613 | "compare", 614 | "equality" 615 | ], 616 | "time": "2014-11-16 21:32:38" 617 | }, 618 | { 619 | "name": "sebastian/diff", 620 | "version": "1.1.0", 621 | "source": { 622 | "type": "git", 623 | "url": "https://github.com/sebastianbergmann/diff.git", 624 | "reference": "1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d" 625 | }, 626 | "dist": { 627 | "type": "zip", 628 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d", 629 | "reference": "1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d", 630 | "shasum": "" 631 | }, 632 | "require": { 633 | "php": ">=5.3.3" 634 | }, 635 | "type": "library", 636 | "extra": { 637 | "branch-alias": { 638 | "dev-master": "1.1-dev" 639 | } 640 | }, 641 | "autoload": { 642 | "classmap": [ 643 | "src/" 644 | ] 645 | }, 646 | "notification-url": "https://packagist.org/downloads/", 647 | "license": [ 648 | "BSD-3-Clause" 649 | ], 650 | "authors": [ 651 | { 652 | "name": "Sebastian Bergmann", 653 | "email": "sebastian@phpunit.de", 654 | "role": "lead" 655 | }, 656 | { 657 | "name": "Kore Nordmann", 658 | "email": "mail@kore-nordmann.de" 659 | } 660 | ], 661 | "description": "Diff implementation", 662 | "homepage": "http://www.github.com/sebastianbergmann/diff", 663 | "keywords": [ 664 | "diff" 665 | ], 666 | "time": "2013-08-03 16:46:33" 667 | }, 668 | { 669 | "name": "sebastian/exporter", 670 | "version": "1.2.0", 671 | "source": { 672 | "type": "git", 673 | "url": "https://github.com/sebastianbergmann/exporter.git", 674 | "reference": "84839970d05254c73cde183a721c7af13aede943" 675 | }, 676 | "dist": { 677 | "type": "zip", 678 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943", 679 | "reference": "84839970d05254c73cde183a721c7af13aede943", 680 | "shasum": "" 681 | }, 682 | "require": { 683 | "php": ">=5.3.3", 684 | "sebastian/recursion-context": "~1.0" 685 | }, 686 | "require-dev": { 687 | "phpunit/phpunit": "~4.4" 688 | }, 689 | "type": "library", 690 | "extra": { 691 | "branch-alias": { 692 | "dev-master": "1.2.x-dev" 693 | } 694 | }, 695 | "autoload": { 696 | "classmap": [ 697 | "src/" 698 | ] 699 | }, 700 | "notification-url": "https://packagist.org/downloads/", 701 | "license": [ 702 | "BSD-3-Clause" 703 | ], 704 | "authors": [ 705 | { 706 | "name": "Jeff Welch", 707 | "email": "whatthejeff@gmail.com" 708 | }, 709 | { 710 | "name": "Volker Dusch", 711 | "email": "github@wallbash.com" 712 | }, 713 | { 714 | "name": "Bernhard Schussek", 715 | "email": "bschussek@2bepublished.at" 716 | }, 717 | { 718 | "name": "Sebastian Bergmann", 719 | "email": "sebastian@phpunit.de" 720 | }, 721 | { 722 | "name": "Adam Harvey", 723 | "email": "aharvey@php.net" 724 | } 725 | ], 726 | "description": "Provides the functionality to export PHP variables for visualization", 727 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 728 | "keywords": [ 729 | "export", 730 | "exporter" 731 | ], 732 | "time": "2015-01-27 07:23:06" 733 | }, 734 | { 735 | "name": "sebastian/recursion-context", 736 | "version": "1.0.0", 737 | "source": { 738 | "type": "git", 739 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 740 | "reference": "3989662bbb30a29d20d9faa04a846af79b276252" 741 | }, 742 | "dist": { 743 | "type": "zip", 744 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/3989662bbb30a29d20d9faa04a846af79b276252", 745 | "reference": "3989662bbb30a29d20d9faa04a846af79b276252", 746 | "shasum": "" 747 | }, 748 | "require": { 749 | "php": ">=5.3.3" 750 | }, 751 | "require-dev": { 752 | "phpunit/phpunit": "~4.4" 753 | }, 754 | "type": "library", 755 | "extra": { 756 | "branch-alias": { 757 | "dev-master": "1.0.x-dev" 758 | } 759 | }, 760 | "autoload": { 761 | "classmap": [ 762 | "src/" 763 | ] 764 | }, 765 | "notification-url": "https://packagist.org/downloads/", 766 | "license": [ 767 | "BSD-3-Clause" 768 | ], 769 | "authors": [ 770 | { 771 | "name": "Jeff Welch", 772 | "email": "whatthejeff@gmail.com" 773 | }, 774 | { 775 | "name": "Sebastian Bergmann", 776 | "email": "sebastian@phpunit.de" 777 | }, 778 | { 779 | "name": "Adam Harvey", 780 | "email": "aharvey@php.net" 781 | } 782 | ], 783 | "description": "Provides functionality to recursively process PHP variables", 784 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 785 | "time": "2015-01-24 09:48:32" 786 | }, 787 | { 788 | "name": "symfony/class-loader", 789 | "version": "v2.7.1", 790 | "source": { 791 | "type": "git", 792 | "url": "https://github.com/symfony/class-loader.git", 793 | "reference": "84843730de48ca0feba91004a03c7c952f8ea1da" 794 | }, 795 | "dist": { 796 | "type": "zip", 797 | "url": "https://api.github.com/repos/symfony/class-loader/zipball/84843730de48ca0feba91004a03c7c952f8ea1da", 798 | "reference": "84843730de48ca0feba91004a03c7c952f8ea1da", 799 | "shasum": "" 800 | }, 801 | "require": { 802 | "php": ">=5.3.9" 803 | }, 804 | "require-dev": { 805 | "symfony/finder": "~2.0,>=2.0.5", 806 | "symfony/phpunit-bridge": "~2.7" 807 | }, 808 | "type": "library", 809 | "extra": { 810 | "branch-alias": { 811 | "dev-master": "2.7-dev" 812 | } 813 | }, 814 | "autoload": { 815 | "psr-4": { 816 | "Symfony\\Component\\ClassLoader\\": "" 817 | } 818 | }, 819 | "notification-url": "https://packagist.org/downloads/", 820 | "license": [ 821 | "MIT" 822 | ], 823 | "authors": [ 824 | { 825 | "name": "Fabien Potencier", 826 | "email": "fabien@symfony.com" 827 | }, 828 | { 829 | "name": "Symfony Community", 830 | "homepage": "https://symfony.com/contributors" 831 | } 832 | ], 833 | "description": "Symfony ClassLoader Component", 834 | "homepage": "https://symfony.com", 835 | "time": "2015-06-08 09:37:21" 836 | }, 837 | { 838 | "name": "symfony/config", 839 | "version": "v2.7.1", 840 | "source": { 841 | "type": "git", 842 | "url": "https://github.com/symfony/config.git", 843 | "reference": "58ded81f1f582a87c528ef3dae9a859f78b5f374" 844 | }, 845 | "dist": { 846 | "type": "zip", 847 | "url": "https://api.github.com/repos/symfony/config/zipball/58ded81f1f582a87c528ef3dae9a859f78b5f374", 848 | "reference": "58ded81f1f582a87c528ef3dae9a859f78b5f374", 849 | "shasum": "" 850 | }, 851 | "require": { 852 | "php": ">=5.3.9", 853 | "symfony/filesystem": "~2.3" 854 | }, 855 | "require-dev": { 856 | "symfony/phpunit-bridge": "~2.7" 857 | }, 858 | "type": "library", 859 | "extra": { 860 | "branch-alias": { 861 | "dev-master": "2.7-dev" 862 | } 863 | }, 864 | "autoload": { 865 | "psr-4": { 866 | "Symfony\\Component\\Config\\": "" 867 | } 868 | }, 869 | "notification-url": "https://packagist.org/downloads/", 870 | "license": [ 871 | "MIT" 872 | ], 873 | "authors": [ 874 | { 875 | "name": "Fabien Potencier", 876 | "email": "fabien@symfony.com" 877 | }, 878 | { 879 | "name": "Symfony Community", 880 | "homepage": "https://symfony.com/contributors" 881 | } 882 | ], 883 | "description": "Symfony Config Component", 884 | "homepage": "https://symfony.com", 885 | "time": "2015-06-11 14:06:56" 886 | }, 887 | { 888 | "name": "symfony/console", 889 | "version": "v2.7.1", 890 | "source": { 891 | "type": "git", 892 | "url": "https://github.com/symfony/console.git", 893 | "reference": "564398bc1f33faf92fc2ec86859983d30eb81806" 894 | }, 895 | "dist": { 896 | "type": "zip", 897 | "url": "https://api.github.com/repos/symfony/console/zipball/564398bc1f33faf92fc2ec86859983d30eb81806", 898 | "reference": "564398bc1f33faf92fc2ec86859983d30eb81806", 899 | "shasum": "" 900 | }, 901 | "require": { 902 | "php": ">=5.3.9" 903 | }, 904 | "require-dev": { 905 | "psr/log": "~1.0", 906 | "symfony/event-dispatcher": "~2.1", 907 | "symfony/phpunit-bridge": "~2.7", 908 | "symfony/process": "~2.1" 909 | }, 910 | "suggest": { 911 | "psr/log": "For using the console logger", 912 | "symfony/event-dispatcher": "", 913 | "symfony/process": "" 914 | }, 915 | "type": "library", 916 | "extra": { 917 | "branch-alias": { 918 | "dev-master": "2.7-dev" 919 | } 920 | }, 921 | "autoload": { 922 | "psr-4": { 923 | "Symfony\\Component\\Console\\": "" 924 | } 925 | }, 926 | "notification-url": "https://packagist.org/downloads/", 927 | "license": [ 928 | "MIT" 929 | ], 930 | "authors": [ 931 | { 932 | "name": "Fabien Potencier", 933 | "email": "fabien@symfony.com" 934 | }, 935 | { 936 | "name": "Symfony Community", 937 | "homepage": "https://symfony.com/contributors" 938 | } 939 | ], 940 | "description": "Symfony Console Component", 941 | "homepage": "https://symfony.com", 942 | "time": "2015-06-10 15:30:22" 943 | }, 944 | { 945 | "name": "symfony/dependency-injection", 946 | "version": "v2.7.1", 947 | "source": { 948 | "type": "git", 949 | "url": "https://github.com/symfony/dependency-injection.git", 950 | "reference": "1a409e52a38ec891de0a7a61a191d1c62080b69d" 951 | }, 952 | "dist": { 953 | "type": "zip", 954 | "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1a409e52a38ec891de0a7a61a191d1c62080b69d", 955 | "reference": "1a409e52a38ec891de0a7a61a191d1c62080b69d", 956 | "shasum": "" 957 | }, 958 | "require": { 959 | "php": ">=5.3.9" 960 | }, 961 | "conflict": { 962 | "symfony/expression-language": "<2.6" 963 | }, 964 | "require-dev": { 965 | "symfony/config": "~2.2", 966 | "symfony/expression-language": "~2.6", 967 | "symfony/phpunit-bridge": "~2.7", 968 | "symfony/yaml": "~2.1" 969 | }, 970 | "suggest": { 971 | "symfony/config": "", 972 | "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", 973 | "symfony/yaml": "" 974 | }, 975 | "type": "library", 976 | "extra": { 977 | "branch-alias": { 978 | "dev-master": "2.7-dev" 979 | } 980 | }, 981 | "autoload": { 982 | "psr-4": { 983 | "Symfony\\Component\\DependencyInjection\\": "" 984 | } 985 | }, 986 | "notification-url": "https://packagist.org/downloads/", 987 | "license": [ 988 | "MIT" 989 | ], 990 | "authors": [ 991 | { 992 | "name": "Fabien Potencier", 993 | "email": "fabien@symfony.com" 994 | }, 995 | { 996 | "name": "Symfony Community", 997 | "homepage": "https://symfony.com/contributors" 998 | } 999 | ], 1000 | "description": "Symfony DependencyInjection Component", 1001 | "homepage": "https://symfony.com", 1002 | "time": "2015-06-11 19:13:11" 1003 | }, 1004 | { 1005 | "name": "symfony/event-dispatcher", 1006 | "version": "v2.7.1", 1007 | "source": { 1008 | "type": "git", 1009 | "url": "https://github.com/symfony/event-dispatcher.git", 1010 | "reference": "be3c5ff8d503c46768aeb78ce6333051aa6f26d9" 1011 | }, 1012 | "dist": { 1013 | "type": "zip", 1014 | "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/be3c5ff8d503c46768aeb78ce6333051aa6f26d9", 1015 | "reference": "be3c5ff8d503c46768aeb78ce6333051aa6f26d9", 1016 | "shasum": "" 1017 | }, 1018 | "require": { 1019 | "php": ">=5.3.9" 1020 | }, 1021 | "require-dev": { 1022 | "psr/log": "~1.0", 1023 | "symfony/config": "~2.0,>=2.0.5", 1024 | "symfony/dependency-injection": "~2.6", 1025 | "symfony/expression-language": "~2.6", 1026 | "symfony/phpunit-bridge": "~2.7", 1027 | "symfony/stopwatch": "~2.3" 1028 | }, 1029 | "suggest": { 1030 | "symfony/dependency-injection": "", 1031 | "symfony/http-kernel": "" 1032 | }, 1033 | "type": "library", 1034 | "extra": { 1035 | "branch-alias": { 1036 | "dev-master": "2.7-dev" 1037 | } 1038 | }, 1039 | "autoload": { 1040 | "psr-4": { 1041 | "Symfony\\Component\\EventDispatcher\\": "" 1042 | } 1043 | }, 1044 | "notification-url": "https://packagist.org/downloads/", 1045 | "license": [ 1046 | "MIT" 1047 | ], 1048 | "authors": [ 1049 | { 1050 | "name": "Fabien Potencier", 1051 | "email": "fabien@symfony.com" 1052 | }, 1053 | { 1054 | "name": "Symfony Community", 1055 | "homepage": "https://symfony.com/contributors" 1056 | } 1057 | ], 1058 | "description": "Symfony EventDispatcher Component", 1059 | "homepage": "https://symfony.com", 1060 | "time": "2015-06-08 09:37:21" 1061 | }, 1062 | { 1063 | "name": "symfony/filesystem", 1064 | "version": "v2.7.1", 1065 | "source": { 1066 | "type": "git", 1067 | "url": "https://github.com/symfony/filesystem.git", 1068 | "reference": "a0d43eb3e17d4f4c6990289805a488a0482a07f3" 1069 | }, 1070 | "dist": { 1071 | "type": "zip", 1072 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/a0d43eb3e17d4f4c6990289805a488a0482a07f3", 1073 | "reference": "a0d43eb3e17d4f4c6990289805a488a0482a07f3", 1074 | "shasum": "" 1075 | }, 1076 | "require": { 1077 | "php": ">=5.3.9" 1078 | }, 1079 | "require-dev": { 1080 | "symfony/phpunit-bridge": "~2.7" 1081 | }, 1082 | "type": "library", 1083 | "extra": { 1084 | "branch-alias": { 1085 | "dev-master": "2.7-dev" 1086 | } 1087 | }, 1088 | "autoload": { 1089 | "psr-4": { 1090 | "Symfony\\Component\\Filesystem\\": "" 1091 | } 1092 | }, 1093 | "notification-url": "https://packagist.org/downloads/", 1094 | "license": [ 1095 | "MIT" 1096 | ], 1097 | "authors": [ 1098 | { 1099 | "name": "Fabien Potencier", 1100 | "email": "fabien@symfony.com" 1101 | }, 1102 | { 1103 | "name": "Symfony Community", 1104 | "homepage": "https://symfony.com/contributors" 1105 | } 1106 | ], 1107 | "description": "Symfony Filesystem Component", 1108 | "homepage": "https://symfony.com", 1109 | "time": "2015-06-08 09:37:21" 1110 | }, 1111 | { 1112 | "name": "symfony/finder", 1113 | "version": "v2.7.1", 1114 | "source": { 1115 | "type": "git", 1116 | "url": "https://github.com/symfony/Finder.git", 1117 | "reference": "c13a40d638aeede1e8400f8c956c7f9246c05f75" 1118 | }, 1119 | "dist": { 1120 | "type": "zip", 1121 | "url": "https://api.github.com/repos/symfony/Finder/zipball/c13a40d638aeede1e8400f8c956c7f9246c05f75", 1122 | "reference": "c13a40d638aeede1e8400f8c956c7f9246c05f75", 1123 | "shasum": "" 1124 | }, 1125 | "require": { 1126 | "php": ">=5.3.9" 1127 | }, 1128 | "require-dev": { 1129 | "symfony/phpunit-bridge": "~2.7" 1130 | }, 1131 | "type": "library", 1132 | "extra": { 1133 | "branch-alias": { 1134 | "dev-master": "2.7-dev" 1135 | } 1136 | }, 1137 | "autoload": { 1138 | "psr-4": { 1139 | "Symfony\\Component\\Finder\\": "" 1140 | } 1141 | }, 1142 | "notification-url": "https://packagist.org/downloads/", 1143 | "license": [ 1144 | "MIT" 1145 | ], 1146 | "authors": [ 1147 | { 1148 | "name": "Fabien Potencier", 1149 | "email": "fabien@symfony.com" 1150 | }, 1151 | { 1152 | "name": "Symfony Community", 1153 | "homepage": "https://symfony.com/contributors" 1154 | } 1155 | ], 1156 | "description": "Symfony Finder Component", 1157 | "homepage": "https://symfony.com", 1158 | "time": "2015-06-04 20:11:48" 1159 | }, 1160 | { 1161 | "name": "symfony/process", 1162 | "version": "v2.7.1", 1163 | "source": { 1164 | "type": "git", 1165 | "url": "https://github.com/symfony/Process.git", 1166 | "reference": "552d8efdc80980cbcca50b28d626ac8e36e3cdd1" 1167 | }, 1168 | "dist": { 1169 | "type": "zip", 1170 | "url": "https://api.github.com/repos/symfony/Process/zipball/552d8efdc80980cbcca50b28d626ac8e36e3cdd1", 1171 | "reference": "552d8efdc80980cbcca50b28d626ac8e36e3cdd1", 1172 | "shasum": "" 1173 | }, 1174 | "require": { 1175 | "php": ">=5.3.9" 1176 | }, 1177 | "require-dev": { 1178 | "symfony/phpunit-bridge": "~2.7" 1179 | }, 1180 | "type": "library", 1181 | "extra": { 1182 | "branch-alias": { 1183 | "dev-master": "2.7-dev" 1184 | } 1185 | }, 1186 | "autoload": { 1187 | "psr-4": { 1188 | "Symfony\\Component\\Process\\": "" 1189 | } 1190 | }, 1191 | "notification-url": "https://packagist.org/downloads/", 1192 | "license": [ 1193 | "MIT" 1194 | ], 1195 | "authors": [ 1196 | { 1197 | "name": "Fabien Potencier", 1198 | "email": "fabien@symfony.com" 1199 | }, 1200 | { 1201 | "name": "Symfony Community", 1202 | "homepage": "https://symfony.com/contributors" 1203 | } 1204 | ], 1205 | "description": "Symfony Process Component", 1206 | "homepage": "https://symfony.com", 1207 | "time": "2015-06-08 09:37:21" 1208 | }, 1209 | { 1210 | "name": "symfony/translation", 1211 | "version": "v2.7.1", 1212 | "source": { 1213 | "type": "git", 1214 | "url": "https://github.com/symfony/Translation.git", 1215 | "reference": "8349a2b0d11bd0311df9e8914408080912983a0b" 1216 | }, 1217 | "dist": { 1218 | "type": "zip", 1219 | "url": "https://api.github.com/repos/symfony/Translation/zipball/8349a2b0d11bd0311df9e8914408080912983a0b", 1220 | "reference": "8349a2b0d11bd0311df9e8914408080912983a0b", 1221 | "shasum": "" 1222 | }, 1223 | "require": { 1224 | "php": ">=5.3.9" 1225 | }, 1226 | "conflict": { 1227 | "symfony/config": "<2.7" 1228 | }, 1229 | "require-dev": { 1230 | "psr/log": "~1.0", 1231 | "symfony/config": "~2.7", 1232 | "symfony/intl": "~2.3", 1233 | "symfony/phpunit-bridge": "~2.7", 1234 | "symfony/yaml": "~2.2" 1235 | }, 1236 | "suggest": { 1237 | "psr/log": "To use logging capability in translator", 1238 | "symfony/config": "", 1239 | "symfony/yaml": "" 1240 | }, 1241 | "type": "library", 1242 | "extra": { 1243 | "branch-alias": { 1244 | "dev-master": "2.7-dev" 1245 | } 1246 | }, 1247 | "autoload": { 1248 | "psr-4": { 1249 | "Symfony\\Component\\Translation\\": "" 1250 | } 1251 | }, 1252 | "notification-url": "https://packagist.org/downloads/", 1253 | "license": [ 1254 | "MIT" 1255 | ], 1256 | "authors": [ 1257 | { 1258 | "name": "Fabien Potencier", 1259 | "email": "fabien@symfony.com" 1260 | }, 1261 | { 1262 | "name": "Symfony Community", 1263 | "homepage": "https://symfony.com/contributors" 1264 | } 1265 | ], 1266 | "description": "Symfony Translation Component", 1267 | "homepage": "https://symfony.com", 1268 | "time": "2015-06-11 17:26:34" 1269 | }, 1270 | { 1271 | "name": "symfony/yaml", 1272 | "version": "v2.7.1", 1273 | "source": { 1274 | "type": "git", 1275 | "url": "https://github.com/symfony/Yaml.git", 1276 | "reference": "9808e75c609a14f6db02f70fccf4ca4aab53c160" 1277 | }, 1278 | "dist": { 1279 | "type": "zip", 1280 | "url": "https://api.github.com/repos/symfony/Yaml/zipball/9808e75c609a14f6db02f70fccf4ca4aab53c160", 1281 | "reference": "9808e75c609a14f6db02f70fccf4ca4aab53c160", 1282 | "shasum": "" 1283 | }, 1284 | "require": { 1285 | "php": ">=5.3.9" 1286 | }, 1287 | "require-dev": { 1288 | "symfony/phpunit-bridge": "~2.7" 1289 | }, 1290 | "type": "library", 1291 | "extra": { 1292 | "branch-alias": { 1293 | "dev-master": "2.7-dev" 1294 | } 1295 | }, 1296 | "autoload": { 1297 | "psr-4": { 1298 | "Symfony\\Component\\Yaml\\": "" 1299 | } 1300 | }, 1301 | "notification-url": "https://packagist.org/downloads/", 1302 | "license": [ 1303 | "MIT" 1304 | ], 1305 | "authors": [ 1306 | { 1307 | "name": "Fabien Potencier", 1308 | "email": "fabien@symfony.com" 1309 | }, 1310 | { 1311 | "name": "Symfony Community", 1312 | "homepage": "https://symfony.com/contributors" 1313 | } 1314 | ], 1315 | "description": "Symfony Yaml Component", 1316 | "homepage": "https://symfony.com", 1317 | "time": "2015-06-10 15:30:22" 1318 | } 1319 | ], 1320 | "aliases": [], 1321 | "minimum-stability": "stable", 1322 | "stability-flags": { 1323 | "phpspec/phpspec": 20, 1324 | "behat/behat": 0 1325 | }, 1326 | "prefer-stable": false, 1327 | "prefer-lowest": false, 1328 | "platform": { 1329 | "php": ">=5.4.0" 1330 | }, 1331 | "platform-dev": [] 1332 | } 1333 | -------------------------------------------------------------------------------- /features/bootstrap/FeatureContext.php: -------------------------------------------------------------------------------- 1 | json = $json->getRaw(); 30 | } 31 | 32 | /** 33 | * @Given I get the JSON 34 | */ 35 | public function getJson() 36 | { 37 | $this->jsonHolder->setJson($this->json); 38 | } 39 | 40 | /** 41 | * @inheritdoc 42 | */ 43 | public function setJsonHolder(JsonHolder $holder) 44 | { 45 | $this->jsonHolder = $holder; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /features/common.feature: -------------------------------------------------------------------------------- 1 | Feature: Common actions 2 | 3 | Background: 4 | Given the JSON is: 5 | """ 6 | { 7 | "array": [ 8 | "json", 9 | "spec" 10 | ], 11 | "created_at": "2011-07-08 02:27:34", 12 | "empty_array": [ 13 | 14 | ], 15 | "empty_hash": { 16 | }, 17 | "false": false, 18 | "float": 10.0, 19 | "hash": { 20 | "json": "spec" 21 | }, 22 | "id": 1, 23 | "integer": 10, 24 | "negative": -10, 25 | "null": null, 26 | "string": "json_spec", 27 | "true": true, 28 | "updated_at": "2011-07-08 02:28:50" 29 | } 30 | """ 31 | 32 | Scenario: Mix formats equivalence with inclusion table 33 | When I get the JSON 34 | Then the JSON at "created_at" should be "2011-07-08 02:27:34" 35 | And the JSON should have the following: 36 | | hash/json | 37 | 38 | Scenario: Mix formats equivalence with inclusion inline 39 | When I get the JSON 40 | Then the JSON at "created_at" should be "2011-07-08 02:27:34" 41 | And the JSON should have "negative" 42 | -------------------------------------------------------------------------------- /features/equivalence.feature: -------------------------------------------------------------------------------- 1 | Feature: Equivalence 2 | 3 | Background: 4 | Given the JSON is: 5 | """ 6 | { 7 | "array": [ 8 | "json", 9 | "spec" 10 | ], 11 | "created_at": "2011-07-08 02:27:34", 12 | "empty_array": [ 13 | 14 | ], 15 | "empty_hash": { 16 | }, 17 | "false": false, 18 | "float": 10.0, 19 | "hash": { 20 | "json": "spec" 21 | }, 22 | "id": 1, 23 | "integer": 10, 24 | "negative": -10, 25 | "null": null, 26 | "string": "json_spec", 27 | "true": true, 28 | "updated_at": "2011-07-08 02:28:50" 29 | } 30 | """ 31 | 32 | Scenario: Identical JSON 33 | When I get the JSON 34 | Then the JSON should be: 35 | """ 36 | { 37 | "array": [ 38 | "json", 39 | "spec" 40 | ], 41 | "created_at": "2011-07-08 02:27:34", 42 | "empty_array": [ 43 | 44 | ], 45 | "empty_hash": { 46 | }, 47 | "false": false, 48 | "float": 10.0, 49 | "hash": { 50 | "json": "spec" 51 | }, 52 | "id": 1, 53 | "integer": 10, 54 | "negative": -10, 55 | "null": null, 56 | "string": "json_spec", 57 | "true": true, 58 | "updated_at": "2011-07-08 02:28:50" 59 | } 60 | """ 61 | 62 | Scenario: Reverse order 63 | When I get the JSON 64 | Then the JSON should be: 65 | """ 66 | { 67 | "updated_at": "2011-07-08 02:28:50", 68 | "true": true, 69 | "string": "json_spec", 70 | "null": null, 71 | "negative": -10, 72 | "integer": 10, 73 | "id": 1, 74 | "hash": { 75 | "json": "spec" 76 | }, 77 | "float": 10.0, 78 | "false": false, 79 | "empty_hash": { 80 | }, 81 | "empty_array": [ 82 | 83 | ], 84 | "created_at": "2011-07-08 02:27:34", 85 | "array": [ 86 | "json", 87 | "spec" 88 | ] 89 | } 90 | """ 91 | 92 | Scenario: Excluding keys 93 | When I get the JSON 94 | Then the JSON should be: 95 | """ 96 | { 97 | "array": [ 98 | "json", 99 | "spec" 100 | ], 101 | "empty_array": [ 102 | 103 | ], 104 | "empty_hash": { 105 | }, 106 | "false": false, 107 | "float": 10.0, 108 | "hash": { 109 | "json": "spec" 110 | }, 111 | "integer": 10, 112 | "negative": -10, 113 | "null": null, 114 | "string": "json_spec", 115 | "true": true 116 | } 117 | """ 118 | 119 | Scenario: String 120 | When I get the JSON 121 | Then the JSON at "string" should be "json_spec" 122 | And the JSON at "string" should be: 123 | """ 124 | "json_spec" 125 | """ 126 | 127 | Scenario: Integer 128 | When I get the JSON 129 | Then the JSON at "integer" should be 10 130 | And the JSON at "integer" should be: 131 | """ 132 | 10 133 | """ 134 | 135 | Scenario: Negative integer 136 | When I get the JSON 137 | Then the JSON at "negative" should be -10 138 | And the JSON at "negative" should be: 139 | """ 140 | -10 141 | """ 142 | 143 | Scenario: Float 144 | When I get the JSON 145 | Then the JSON at "float" should be 10.0 146 | And the JSON at "float" should be 10.0e0 147 | And the JSON at "float" should be 10.0e+0 148 | And the JSON at "float" should be 10.0e-0 149 | And the JSON at "float" should be 10e0 150 | And the JSON at "float" should be 10e+0 151 | And the JSON at "float" should be 10e-0 152 | And the JSON at "float" should be 1.0e1 153 | And the JSON at "float" should be 1.0e+1 154 | And the JSON at "float" should be 1e1 155 | And the JSON at "float" should be 1e+1 156 | And the JSON at "float" should be 100.0e-1 157 | And the JSON at "float" should be 100e-1 158 | And the JSON at "float" should be: 159 | """ 160 | 10.0 161 | """ 162 | 163 | Scenario: Array 164 | When I get the JSON 165 | Then the JSON at "array" should be ["json","spec"] 166 | And the JSON at "array/0" should be "json" 167 | And the JSON at "array/1" should be "spec" 168 | And the JSON at "array" should be: 169 | """ 170 | [ 171 | "json", 172 | "spec" 173 | ] 174 | """ 175 | 176 | Scenario: Empty array 177 | When I get the JSON 178 | Then the JSON at "empty_array" should be [] 179 | And the JSON at "empty_array" should be: 180 | """ 181 | [ 182 | 183 | ] 184 | """ 185 | 186 | Scenario: Hash 187 | When I get the JSON 188 | Then the JSON at "hash" should be {"json":"spec"} 189 | And the JSON at "hash/json" should be "spec" 190 | And the JSON at "hash" should be: 191 | """ 192 | { 193 | "json": "spec" 194 | } 195 | """ 196 | 197 | Scenario: Empty hash 198 | When I get the JSON 199 | Then the JSON at "empty_hash" should be {} 200 | And the JSON at "empty_hash" should be: 201 | """ 202 | { 203 | } 204 | """ 205 | 206 | Scenario: True 207 | When I get the JSON 208 | Then the JSON at "true" should be true 209 | And the JSON at "true" should be: 210 | """ 211 | true 212 | """ 213 | 214 | Scenario: False 215 | When I get the JSON 216 | Then the JSON at "false" should be false 217 | And the JSON at "false" should be: 218 | """ 219 | false 220 | """ 221 | 222 | Scenario: Null 223 | When I get the JSON 224 | Then the JSON at "null" should be null 225 | And the JSON at "null" should be: 226 | """ 227 | null 228 | """ 229 | 230 | Scenario: Excluded value 231 | When I get the JSON 232 | Then the JSON at "created_at" should be "2011-07-08 02:27:34" 233 | And the JSON at "id" should be 1 234 | And the JSON at "updated_at" should be "2011-07-08 02:28:50" 235 | 236 | Scenario: Table format 237 | When I get the JSON 238 | Then the JSON should have the following: 239 | | array | ["json","spec"] | 240 | | array/0 | "json" | 241 | | array/1 | "spec" | 242 | | created_at | "2011-07-08 02:27:34" | 243 | | empty_array | [] | 244 | | empty_hash | {} | 245 | | false | false | 246 | | float | 10.0 | 247 | | hash | {"json":"spec"} | 248 | | hash/json | "spec" | 249 | | id | 1 | 250 | | integer | 10 | 251 | | negative | -10 | 252 | | null | null | 253 | | string | "json_spec" | 254 | | true | true | 255 | | updated_at | "2011-07-08 02:28:50" | 256 | And the JSON at "array" should have the following: 257 | | 0 | "json" | 258 | | 1 | "spec" | 259 | And the JSON at "hash" should have the following: 260 | | json | "spec" | 261 | -------------------------------------------------------------------------------- /features/files.feature: -------------------------------------------------------------------------------- 1 | Feature: Files 2 | Scenario: Equivalence from a file 3 | Given the JSON is: 4 | """ 5 | { 6 | "array": [ 7 | "json", 8 | "spec" 9 | ], 10 | "false": false, 11 | "float": 10.0, 12 | "hash": { 13 | "json": "spec" 14 | }, 15 | "created_at": "2011-07-08 02:27:34", 16 | "empty_array": [ 17 | 18 | ], 19 | "empty_hash": { 20 | }, 21 | "id": 1, 22 | "integer": 10, 23 | "negative": -10, 24 | "null": null, 25 | "string": "json_spec", 26 | "true": true, 27 | "updated_at": "2011-07-08 02:28:50" 28 | } 29 | """ 30 | When I get the JSON 31 | Then the JSON should be file "two.json" 32 | 33 | Scenario: Inequivalence from a file 34 | Given the JSON is: 35 | """ 36 | { 37 | "string": "json_spec", 38 | "true": true, 39 | "updated_at": "2011-07-08 02:28:50" 40 | } 41 | """ 42 | When I get the JSON 43 | Then the JSON should not be file "two.json" 44 | 45 | 46 | Scenario: Inclusion from a file 47 | Given the JSON is: 48 | """ 49 | { 50 | "array": [ 51 | "json", 52 | "spec" 53 | ], 54 | "created_at": "2011-07-08 02:27:34", 55 | "empty_array": [ 56 | 57 | ], 58 | "empty_hash": { 59 | }, 60 | "false": false, 61 | "float": 10.0, 62 | "hash": { 63 | "json": "spec" 64 | } 65 | } 66 | """ 67 | When I get the JSON 68 | Then the JSON should include file "project/version/two.json" 69 | 70 | Scenario: Exclusion from a file 71 | Given the JSON is: 72 | """ 73 | { 74 | "array": [ 75 | "json", 76 | "spec" 77 | ], 78 | "created_at": "2011-07-08 02:27:34", 79 | "empty_array": [ 80 | 81 | ], 82 | "empty_hash": { 83 | }, 84 | "false": false, 85 | "float": 10.0 86 | } 87 | """ 88 | When I get the JSON 89 | Then the JSON should not include file "project/version/two.json" -------------------------------------------------------------------------------- /features/inclusion.feature: -------------------------------------------------------------------------------- 1 | Feature: Inclusion 2 | Background: 3 | Given the JSON is: 4 | """ 5 | { 6 | "array": [ 7 | "json", 8 | "spec" 9 | ], 10 | "created_at": "2011-07-08 02:27:34", 11 | "empty_array": [ 12 | 13 | ], 14 | "empty_hash": { 15 | }, 16 | "false": false, 17 | "float": 10.0, 18 | "hash": { 19 | "json": "spec" 20 | }, 21 | "id": 1, 22 | "integer": 10, 23 | "negative": -10, 24 | "null": null, 25 | "string": "json_spec", 26 | "true": true, 27 | "updated_at": "2011-07-08 02:28:50", 28 | "nested": { 29 | "id": 2, 30 | "key": "value" 31 | } 32 | } 33 | """ 34 | 35 | Scenario: String 36 | When I get the JSON 37 | Then the JSON should include "json_spec" 38 | And the JSON should include: 39 | """ 40 | "json_spec" 41 | """ 42 | 43 | Scenario: Integer 44 | When I get the JSON 45 | Then the JSON should include 10 46 | And the JSON should include: 47 | """ 48 | 10 49 | """ 50 | 51 | Scenario: Negative integer 52 | When I get the JSON 53 | Then the JSON should include -10 54 | And the JSON should include: 55 | """ 56 | -10 57 | """ 58 | 59 | Scenario: Float 60 | When I get the JSON 61 | Then the JSON should include 10.0 62 | And the JSON should include 10.0e0 63 | And the JSON should include 10.0e+0 64 | And the JSON should include 10.0e-0 65 | And the JSON should include 10e0 66 | And the JSON should include 10e+0 67 | And the JSON should include 10e-0 68 | And the JSON should include 1.0e1 69 | And the JSON should include 1.0e+1 70 | And the JSON should include 1e1 71 | And the JSON should include 1e+1 72 | And the JSON should include 100.0e-1 73 | And the JSON should include 100e-1 74 | And the JSON should include: 75 | """ 76 | 10.0 77 | """ 78 | 79 | Scenario: Array 80 | When I get the JSON 81 | Then the JSON should include ["json","spec"] 82 | And the JSON at "array" should include "json" 83 | And the JSON at "array" should include "spec" 84 | And the JSON should include: 85 | """ 86 | [ 87 | "json", 88 | "spec" 89 | ] 90 | """ 91 | 92 | Scenario: Empty array 93 | When I get the JSON 94 | Then the JSON should include [] 95 | And the JSON should include: 96 | """ 97 | [ 98 | 99 | ] 100 | """ 101 | 102 | Scenario: Hash 103 | When I get the JSON 104 | Then the JSON should include {"json":"spec"} 105 | And the JSON at "hash" should include "spec" 106 | And the JSON should include: 107 | """ 108 | { 109 | "json": "spec" 110 | } 111 | """ 112 | 113 | Scenario: Empty hash 114 | When I get the JSON 115 | Then the JSON should include {} 116 | And the JSON should include: 117 | """ 118 | { 119 | } 120 | """ 121 | 122 | Scenario: True 123 | When I get the JSON 124 | Then the JSON should include true 125 | And the JSON should include: 126 | """ 127 | true 128 | """ 129 | 130 | Scenario: False 131 | When I get the JSON 132 | Then the JSON should include false 133 | And the JSON should include: 134 | """ 135 | false 136 | """ 137 | 138 | Scenario: Null 139 | When I get the JSON 140 | Then the JSON should include null 141 | And the JSON should include: 142 | """ 143 | null 144 | """ 145 | 146 | Scenario: Excluded value 147 | When I get the JSON 148 | Then the JSON should not include "2011-07-08 02:27:34" 149 | And the JSON should not include 1 150 | And the JSON should not include "2011-07-08 02:28:50" 151 | 152 | Scenario: Nested exclusions 153 | When I get the JSON 154 | Then the JSON should include {"key":"value"} 155 | -------------------------------------------------------------------------------- /features/memory.feature: -------------------------------------------------------------------------------- 1 | Feature: Memory 2 | Background: 3 | Given the JSON is: 4 | """ 5 | { 6 | "array": [ 7 | 8 | ], 9 | "false": false, 10 | "float": 10.0, 11 | "hash": { 12 | }, 13 | "integer": 10, 14 | "null": null, 15 | "string": "json_spec", 16 | "true": true 17 | } 18 | """ 19 | And I get the JSON 20 | 21 | Scenario: Entire JSON 22 | When I keep the JSON as "JSON" 23 | Then the JSON should be {$JSON} 24 | And the JSON should be: 25 | """ 26 | {$JSON} 27 | """ 28 | 29 | Scenario: String 30 | When I keep the JSON at "string" as "STRING" 31 | Then the JSON at "string" should be {$STRING} 32 | And the JSON should be: 33 | """ 34 | { 35 | "array": [ 36 | 37 | ], 38 | "false": false, 39 | "float": 10.0, 40 | "hash": { 41 | }, 42 | "integer": 10, 43 | "null": null, 44 | "string": {$STRING}, 45 | "true": true 46 | } 47 | """ 48 | 49 | Scenario: Integer 50 | When I keep the JSON at "integer" as "INTEGER" 51 | Then the JSON at "integer" should be {$INTEGER} 52 | And the JSON should be: 53 | """ 54 | { 55 | "array": [ 56 | 57 | ], 58 | "false": false, 59 | "float": 10.0, 60 | "hash": { 61 | }, 62 | "integer": {$INTEGER}, 63 | "null": null, 64 | "string": "json_spec", 65 | "true": true 66 | } 67 | """ 68 | 69 | Scenario: Float 70 | When I keep the JSON at "float" as "FLOAT" 71 | Then the JSON at "float" should be {$FLOAT} 72 | And the JSON should be: 73 | """ 74 | { 75 | "array": [ 76 | 77 | ], 78 | "false": false, 79 | "float": {$FLOAT}, 80 | "hash": { 81 | }, 82 | "integer": 10, 83 | "null": null, 84 | "string": "json_spec", 85 | "true": true 86 | } 87 | """ 88 | 89 | Scenario: Array 90 | When I keep the JSON at "array" as "ARRAY" 91 | Then the JSON at "array" should be {$ARRAY} 92 | And the JSON should be: 93 | """ 94 | { 95 | "array": {$ARRAY}, 96 | "false": false, 97 | "float": 10.0, 98 | "hash": { 99 | }, 100 | "integer": 10, 101 | "null": null, 102 | "string": "json_spec", 103 | "true": true 104 | } 105 | """ 106 | 107 | Scenario: Hash 108 | When I keep the JSON at "hash" as "HASH" 109 | Then the JSON at "hash" should be {$HASH} 110 | And the JSON should be: 111 | """ 112 | { 113 | "array": [ 114 | 115 | ], 116 | "false": false, 117 | "float": 10.0, 118 | "hash": {$HASH}, 119 | "integer": 10, 120 | "null": null, 121 | "string": "json_spec", 122 | "true": true 123 | } 124 | """ 125 | 126 | Scenario: True 127 | When I keep the JSON at "true" as "TRUE" 128 | Then the JSON at "true" should be {$TRUE} 129 | And the JSON should be: 130 | """ 131 | { 132 | "array": [ 133 | 134 | ], 135 | "false": false, 136 | "float": 10.0, 137 | "hash": { 138 | }, 139 | "integer": 10, 140 | "null": null, 141 | "string": "json_spec", 142 | "true": {$TRUE} 143 | } 144 | """ 145 | 146 | Scenario: False 147 | When I keep the JSON at "false" as "FALSE" 148 | Then the JSON at "false" should be {$FALSE} 149 | And the JSON should be: 150 | """ 151 | { 152 | "array": [ 153 | 154 | ], 155 | "false": {$FALSE}, 156 | "float": 10.0, 157 | "hash": { 158 | }, 159 | "integer": 10, 160 | "null": null, 161 | "string": "json_spec", 162 | "true": true 163 | } 164 | """ 165 | 166 | Scenario: Null 167 | When I keep the JSON at "null" as "NULL" 168 | Then the JSON at "null" should be {$NULL} 169 | And the JSON should be: 170 | """ 171 | { 172 | "array": [ 173 | 174 | ], 175 | "false": false, 176 | "float": 10.0, 177 | "hash": { 178 | }, 179 | "integer": 10, 180 | "null": {$NULL}, 181 | "string": "json_spec", 182 | "true": true 183 | } 184 | """ 185 | 186 | Scenario: Table format 187 | When I keep the JSON at "string" as "STRING" 188 | And I keep the JSON at "integer" as "INTEGER" 189 | And I keep the JSON at "float" as "FLOAT" 190 | And I keep the JSON at "array" as "ARRAY" 191 | And I keep the JSON at "hash" as "HASH" 192 | And I keep the JSON at "true" as "TRUE" 193 | And I keep the JSON at "false" as "FALSE" 194 | And I keep the JSON at "null" as "NULL" 195 | Then the JSON should have the following: 196 | | string | {$STRING} | 197 | | integer | {$INTEGER} | 198 | | float | {$FLOAT} | 199 | | array | {$ARRAY} | 200 | | hash | {$HASH} | 201 | | true | {$TRUE} | 202 | | false | {$FALSE} | 203 | | null | {$NULL} | 204 | 205 | Scenario: Inclusion 206 | When I keep the JSON at "string" as "STRING" 207 | And I keep the JSON at "integer" as "INTEGER" 208 | And I keep the JSON at "float" as "FLOAT" 209 | And I keep the JSON at "array" as "ARRAY" 210 | And I keep the JSON at "hash" as "HASH" 211 | And I keep the JSON at "true" as "TRUE" 212 | And I keep the JSON at "false" as "FALSE" 213 | And I keep the JSON at "null" as "NULL" 214 | Then the JSON should include {$STRING} 215 | And the JSON should include {$INTEGER} 216 | And the JSON should include {$FLOAT} 217 | And the JSON should include {$ARRAY} 218 | And the JSON should include {$HASH} 219 | And the JSON should include {$TRUE} 220 | And the JSON should include {$FALSE} 221 | And the JSON should include {$NULL} 222 | -------------------------------------------------------------------------------- /features/paths.feature: -------------------------------------------------------------------------------- 1 | Feature: Paths 2 | Background: 3 | Given the JSON is: 4 | """ 5 | { 6 | "array": [ 7 | { 8 | "one": 1, 9 | "two": 2 10 | }, 11 | { 12 | "four": 4, 13 | "three": 3 14 | } 15 | ], 16 | "hash": { 17 | "even": [ 18 | 6, 19 | 8 20 | ], 21 | "odd": [ 22 | 5, 23 | 7 24 | ] 25 | }, 26 | "id": null 27 | } 28 | """ 29 | 30 | Scenario: Base paths 31 | When I get the JSON 32 | Then the JSON should have "array" 33 | And the JSON should have "hash" 34 | And the JSON should have "id" 35 | 36 | Scenario: Nested paths 37 | When I get the JSON 38 | Then the JSON should have "array/0" 39 | And the JSON should have "array/1" 40 | And the JSON should have "hash/even" 41 | And the JSON should have "hash/odd" 42 | 43 | Scenario: Deeply nested paths 44 | When I get the JSON 45 | Then the JSON should have "array/0/one" 46 | And the JSON should have "array/0/two" 47 | And the JSON should have "array/1/four" 48 | And the JSON should have "array/1/three" 49 | And the JSON should have "hash/even/0" 50 | And the JSON should have "hash/even/1" 51 | And the JSON should have "hash/odd/0" 52 | And the JSON should have "hash/odd/1" 53 | 54 | Scenario: Check path not exists 55 | When I get the JSON 56 | Then the JSON should not have "not_existing" 57 | 58 | Scenario: Ignored paths 59 | When I get the JSON 60 | Then the JSON should have "id" 61 | 62 | Scenario: Table format 63 | When I get the JSON 64 | Then the JSON should have the following: 65 | | array | 66 | | hash | 67 | | array/0 | 68 | | array/1 | 69 | | hash/even | 70 | | hash/odd | 71 | | array/0/one | 72 | | array/0/two | 73 | | array/1/four | 74 | | array/1/three | 75 | | hash/even/0 | 76 | | hash/even/1 | 77 | | hash/odd/0 | 78 | | hash/odd/1 | 79 | -------------------------------------------------------------------------------- /features/sizes.feature: -------------------------------------------------------------------------------- 1 | Feature: Sizes 2 | Background: 3 | Given the JSON is: 4 | """ 5 | { 6 | "id": null, 7 | "one": [ 8 | 1 9 | ], 10 | "three": [ 11 | 1, 12 | 2, 13 | 3 14 | ], 15 | "two": [ 16 | 1, 17 | 2 18 | ], 19 | "zero": [ 20 | 21 | ] 22 | } 23 | """ 24 | 25 | Scenario: Hash 26 | When I get the JSON 27 | Then the JSON should have 5 keys 28 | And the JSON should have 5 values 29 | 30 | Scenario: Empty array 31 | When I get the JSON 32 | Then the JSON at "zero" should have 0 entries 33 | 34 | Scenario: Array 35 | When I get the JSON 36 | Then the JSON at "one" should have 1 entry 37 | And the JSON at "two" should have 2 values 38 | And the JSON at "three" should have 3 numbers 39 | -------------------------------------------------------------------------------- /features/types.feature: -------------------------------------------------------------------------------- 1 | Feature: Types 2 | Scenario: All types 3 | Given the JSON is: 4 | """ 5 | { 6 | "array": [], 7 | "false": true, 8 | "float": 10.0, 9 | "hash": {}, 10 | "integer": 10, 11 | "string": "json_spec", 12 | "true": true 13 | } 14 | """ 15 | When I get the JSON 16 | Then the JSON should be an object 17 | And the JSON at "array" should be an array 18 | And the JSON at "false" should be a boolean 19 | And the JSON at "float" should be a float 20 | And the JSON at "hash" should be a object 21 | And the JSON at "integer" should be an integer 22 | And the JSON at "string" should be a string 23 | And the JSON at "true" should be a boolean 24 | -------------------------------------------------------------------------------- /phpspec.yml: -------------------------------------------------------------------------------- 1 | --- 2 | suites: 3 | default_suite: 4 | namespace: JsonSpec 5 | psr4_prefix: JsonSpec 6 | -------------------------------------------------------------------------------- /spec/JsonSpec/Behat/Helper/MemoryHelperSpec.php: -------------------------------------------------------------------------------- 1 | getRemembered()->shouldBeArray(); 13 | } 14 | 15 | public function it_memorizes_strings() 16 | { 17 | $this->memorize('key', 'value'); 18 | $this->getRemembered()->shouldBeLike(array( 19 | 'key' => 'value' 20 | )); 21 | } 22 | 23 | public function it_regurgitates_unremembered_strings() 24 | { 25 | $this->remember('json_{$key}')->shouldBe('json_{$key}'); 26 | } 27 | 28 | public function it_remembers_strings() 29 | { 30 | $this->memorize('key', 'spec'); 31 | $this->remember('json_{$key}')->shouldBe('json_spec'); 32 | } 33 | 34 | public function it_forgets() 35 | { 36 | $this->memorize('key', 'value'); 37 | $this->forget(); 38 | $this->getRemembered()->shouldBeLike(array()); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /spec/JsonSpec/JsonLoaderSpec.php: -------------------------------------------------------------------------------- 1 | path = __DIR__ . '/../../support/files'; 17 | } 18 | 19 | public function it_raises_an_error_when_no_directory_is_set() 20 | { 21 | $this->shouldThrow(new MissingDirectoryException())->duringLoadJson('one.json'); 22 | } 23 | 24 | public function it_returns_json_when_the_file_exists() 25 | { 26 | $this->setDirectory($this->path); 27 | $this->loadJson('one.json')->shouldReturn('{"value":"from_file"}'); 28 | } 29 | 30 | public function it_ignores_extra_slashes() 31 | { 32 | $this->setDirectory($this->path . '/'); 33 | $this->loadJson('one.json')->shouldReturn('{"value":"from_file"}'); 34 | } 35 | 36 | public function it_raises_an_error_when_the_file_does_not_exist() 37 | { 38 | $this->setDirectory($this->path); 39 | $this->shouldThrow(new MissingFileException($this->path . '/bogus.json'))->duringLoadJson('bogus.json'); 40 | } 41 | 42 | public function it_raises_an_error_when_the_directory_does_not_exist() 43 | { 44 | $this->setDirectory($this->path . '/bogus'); 45 | $this->shouldThrow(new MissingFileException($this->path . '/bogus/one.json'))->duringLoadJson('one.json'); 46 | } 47 | 48 | public function it_finds_nested_files() 49 | { 50 | $this->setDirectory($this->path); 51 | $this->loadJson('project/one.json')->shouldReturn('{"nested":"inside_folder"}'); 52 | $this->loadJson('project/version/one.json')->shouldReturn('{"nested":"deeply"}'); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /spec/support/files/one.json: -------------------------------------------------------------------------------- 1 | {"value":"from_file"} -------------------------------------------------------------------------------- /spec/support/files/project/one.json: -------------------------------------------------------------------------------- 1 | {"nested":"inside_folder"} -------------------------------------------------------------------------------- /spec/support/files/project/two.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": null, 3 | "one": [ 4 | 1 5 | ], 6 | "three": [ 7 | 1, 8 | 2, 9 | 3 10 | ], 11 | "two": [ 12 | 1, 13 | 2 14 | ], 15 | "zero": [ 16 | 17 | ] 18 | } -------------------------------------------------------------------------------- /spec/support/files/project/version/one.json: -------------------------------------------------------------------------------- 1 | {"nested":"deeply"} -------------------------------------------------------------------------------- /spec/support/files/project/version/two.json: -------------------------------------------------------------------------------- 1 | { 2 | "json": "spec" 3 | } -------------------------------------------------------------------------------- /spec/support/files/two.json: -------------------------------------------------------------------------------- 1 | { 2 | "array": [ 3 | "json", 4 | "spec" 5 | ], 6 | "created_at": "2011-07-08 02:27:34", 7 | "empty_array": [ 8 | 9 | ], 10 | "empty_hash": { 11 | }, 12 | "false": false, 13 | "float": 10.0, 14 | "hash": { 15 | "json": "spec" 16 | }, 17 | "id": 1, 18 | "integer": 10, 19 | "negative": -10, 20 | "null": null, 21 | "string": "json_spec", 22 | "true": true, 23 | "updated_at": "2011-07-08 02:28:50" 24 | } -------------------------------------------------------------------------------- /src/Behat/Context/ArgumentResolver/DependencyResolver.php: -------------------------------------------------------------------------------- 1 | dependencies = [ 35 | $matcherFactory, 36 | $jsonLoader, 37 | $jsonHelper 38 | ]; 39 | } 40 | 41 | /** 42 | * @inheritdoc 43 | */ 44 | public function resolveArguments(ReflectionClass $classReflection, array $arguments) 45 | { 46 | $constructor = $classReflection->getConstructor(); 47 | if ($constructor !== null) { 48 | $parameters = $constructor->getParameters(); 49 | foreach ($parameters as $parameter) { 50 | if ( 51 | null !== $parameter->getClass() && 52 | null !== ($dependency = $this->getDependency($parameter->getClass()->name)) 53 | ) { 54 | $arguments[$parameter->name] = $dependency; 55 | } 56 | } 57 | } 58 | 59 | return $arguments; 60 | } 61 | 62 | /** 63 | * @param string $className 64 | * @return object 65 | */ 66 | private function getDependency($className) 67 | { 68 | foreach ($this->dependencies as $dependency) { 69 | if (is_a($dependency, $className, true)) { 70 | return $dependency; 71 | } 72 | } 73 | 74 | return null; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/Behat/Context/ContextResolver/JsonSpecContextResolver.php: -------------------------------------------------------------------------------- 1 | jsonHolder = $jsonHolder; 30 | } 31 | 32 | /** 33 | * @inheritdoc 34 | */ 35 | public function initializeContext(Context $context) 36 | { 37 | if ($context instanceof JsonHolderAware) { 38 | $context->setJsonHolder($this->jsonHolder); 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/Behat/Context/Initializer/JsonMatcherAwareInitializer.php: -------------------------------------------------------------------------------- 1 | jsonMatcherFactory = $memoryHelper; 28 | } 29 | 30 | /** 31 | * @inheritdoc 32 | */ 33 | public function initializeContext(Context $context) 34 | { 35 | if ($context instanceof JsonMatcherAware || $this->usesTrait($context)) { 36 | $context->setJsonMatcher($this->jsonMatcherFactory); 37 | } 38 | } 39 | 40 | private function usesTrait(Context $context) 41 | { 42 | $usedTraits = class_uses($context); 43 | 44 | return is_array($usedTraits) && in_array( 45 | 'JsonSpec\\Behat\\Context\\Traits\\JsonMatcherAwareTrait', $usedTraits 46 | ); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/Behat/Context/Initializer/MemoryHelperAwareInitializer.php: -------------------------------------------------------------------------------- 1 | memoryHelper = $memoryHelper; 28 | } 29 | 30 | /** 31 | * @inheritdoc 32 | */ 33 | public function initializeContext(Context $context) 34 | { 35 | if ($context instanceof MemoryHelperAware) { 36 | $context->setMemoryHelper($this->memoryHelper); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/Behat/Context/JsonHolderAware.php: -------------------------------------------------------------------------------- 1 | jsonLoader = $jsonLoader; 54 | $this->jsonHelper = $jsonHelper; 55 | } 56 | 57 | /** 58 | * @Then /^(?:I )?keep the (?:JSON|json)(?: response)?(?: at "(.*)")? as "(.*)"$/ 59 | */ 60 | public function keepJson($path, $key) 61 | { 62 | $json = $this->jsonHolder->getJson(); 63 | $this->memoryHelper->memorize($key, $this->jsonHelper->normalize($json, $this->normalizePath($path))); 64 | } 65 | 66 | /** 67 | * @Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? be(:)$/ 68 | */ 69 | public function checkEquality($path, $isNegative, PyStringNode $json) 70 | { 71 | $this->checkEqualityInline($path, $isNegative, $json->getRaw()); 72 | } 73 | 74 | /** 75 | * @Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? be file "(.+)"/ 76 | */ 77 | public function checkEqualityWithFileContents($path = null, $isNegative = null, $jsonFile) 78 | { 79 | $this->checkEqualityInline($path, $isNegative, $this->jsonLoader->loadJson($jsonFile)); 80 | } 81 | 82 | /** 83 | * @Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? be (".*"|\-?\d+(?:\.\d+)?(?:[eE][\+\-]?\d+)?|\[.*\]|%?\{.*\}|true|false|null)$/ 84 | */ 85 | public function checkEqualityInline($path, $isNegative, $json) 86 | { 87 | $this 88 | ->json($this->rememberJson()) 89 | ->equal($this->memoryHelper->remember($json), [ 90 | 'at' => $this->normalizePath($path), 91 | JsonMatcher::OPTION_NEGATIVE => !!$isNegative 92 | ]) 93 | ; 94 | } 95 | 96 | /** 97 | * @Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? include(:)$/ 98 | */ 99 | public function checkInclusion($path, $isNegative, PyStringNode $json) 100 | { 101 | $this->checkInclusionInline($path, $isNegative, $json->getRaw()); 102 | } 103 | 104 | /** 105 | * @Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? include file "(.+)"$/ 106 | */ 107 | public function checkInclusionOfFile($path, $isNegative, $jsonFile) 108 | { 109 | $this->checkInclusionInline($path, $isNegative, $this->jsonLoader->loadJson($jsonFile)); 110 | } 111 | 112 | /** 113 | * @Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? include (".*"|\-?\d+(?:\.\d+)?(?:[eE][\+\-]?\d+)?|\[.*\]|%?\{.*\}|true|false|null)$/ 114 | */ 115 | public function checkInclusionInline($path, $isNegative, $json) 116 | { 117 | $this 118 | ->json($this->rememberJson()) 119 | ->includes($this->rememberJson($json), [ 120 | 'at' => $this->normalizePath($path), 121 | JsonMatcher::OPTION_NEGATIVE => !!$isNegative 122 | ]) 123 | ; 124 | } 125 | 126 | /** 127 | * @Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should have the following(:)$/ 128 | */ 129 | public function hasKeys($base, TableNode $table) 130 | { 131 | $matcher = $this->json($this->rememberJson()); 132 | 133 | foreach ($table->getRows() as $row) { 134 | $path = ltrim("$base/{$row[0]}"); 135 | if (count ($row) == 2) { 136 | $matcher->equal($this->rememberJson($row[1]), ['at' => $this->normalizePath($path)]); 137 | } else { 138 | $matcher->hasPath($path); 139 | } 140 | } 141 | } 142 | 143 | /** 144 | * @Then /^the (?:JSON|json)(?: response)? should( not)? have "(.*)"$/ 145 | */ 146 | public function hasKeysInline($isNegative, $path) 147 | { 148 | $this 149 | ->json($this->rememberJson()) 150 | ->hasPath($path, [ 151 | JsonMatcher::OPTION_NEGATIVE => !!$isNegative 152 | ]) 153 | ; 154 | } 155 | 156 | /** 157 | * @Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? be an? (.*)$/ 158 | */ 159 | public function haveType($path, $isNegative, $type) 160 | { 161 | $this 162 | ->json($this->rememberJson()) 163 | ->hasType($type, [ 164 | 'at' => $this->normalizePath($path), 165 | JsonMatcher::OPTION_NEGATIVE => !!$isNegative 166 | ]) 167 | ; 168 | } 169 | 170 | /** 171 | * @Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? have (\d+)/ 172 | */ 173 | public function haveSize($path, $isNegative, $size) 174 | { 175 | $this 176 | ->json($this->rememberJson()) 177 | ->hasSize(intval($size, 10), [ 178 | 'at' => $this->normalizePath($path), 179 | JsonMatcher::OPTION_NEGATIVE => !!$isNegative 180 | ]) 181 | ; 182 | } 183 | 184 | /** 185 | * @Then print last JSON response 186 | */ 187 | public function printLastJsonResponse() 188 | { 189 | echo (string) $this->jsonHolder->getJson(); 190 | } 191 | 192 | /** 193 | * @inheritdoc 194 | */ 195 | public function setJsonHolder(JsonHolder $holder) 196 | { 197 | $this->jsonHolder = $holder; 198 | } 199 | 200 | /** 201 | * @inheritdoc 202 | */ 203 | public function setMemoryHelper(MemoryHelper $memoryHelper) 204 | { 205 | $this->memoryHelper = $memoryHelper; 206 | } 207 | 208 | /** 209 | * @return string 210 | */ 211 | private function rememberJson($json = null) 212 | { 213 | if (null === $json) { 214 | $json = $this->jsonHolder->getJson(); 215 | } 216 | 217 | return $this->memoryHelper->remember($json); 218 | } 219 | 220 | private function normalizePath($path) 221 | { 222 | if (0 === strlen(ltrim($path, '/'))) { 223 | return null; 224 | } 225 | 226 | return $path; 227 | } 228 | 229 | } 230 | -------------------------------------------------------------------------------- /src/Behat/Context/MemoryHelperAware.php: -------------------------------------------------------------------------------- 1 | matcherFactory = $matcherFactory; 21 | } 22 | 23 | /** 24 | * @param $json 25 | * @return \Fesor\JsonMatcher\JsonMatcher 26 | */ 27 | protected function json($json) 28 | { 29 | return $this->matcherFactory->create($json); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/Behat/Extension.php: -------------------------------------------------------------------------------- 1 | children() 38 | ->arrayNode('excluded_keys') 39 | ->prototype('scalar') 40 | ->example(array('id', 'created_at')) 41 | ->end() 42 | ->defaultValue(array('id', 'created_at', 'updated_at')) 43 | ->end() 44 | ->scalarNode('json_directory')->defaultNull()->end() 45 | ->end(); 46 | } 47 | 48 | /** 49 | * @inheritdoc 50 | */ 51 | public function initialize(ExtensionManager $extensionManager) 52 | { 53 | $this->extensionManager = $extensionManager; 54 | } 55 | 56 | /** 57 | * @inheritdoc 58 | */ 59 | public function load(ContainerBuilder $container, array $config) 60 | { 61 | $container->setParameter('json_spec.excluded_keys', $config['excluded_keys']); 62 | $container->setParameter('json_spec.json_directory', $config['json_directory']); 63 | 64 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/Resources/services')); 65 | $loader->load('services.yml'); 66 | 67 | if (null !== $this->extensionManager->getExtension('mink')) { 68 | $loader->load('mink_integration.yml'); 69 | } 70 | } 71 | 72 | /** 73 | * @inheritdoc 74 | */ 75 | public function process(ContainerBuilder $container) {} 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/Behat/Helper/MemoryHelper.php: -------------------------------------------------------------------------------- 1 | forget(); 13 | } 14 | 15 | public function getRemembered() 16 | { 17 | return $this->memory; 18 | } 19 | 20 | public function memorize($key, $value) 21 | { 22 | $this->memory[$key] = $value; 23 | } 24 | 25 | public function remember($value) 26 | { 27 | return preg_replace_callback('/\{\$([^\}]+?)}/', function ($matched) { 28 | return isset($this->memory[$matched[1]]) ? 29 | $this->memory[$matched[1]] : $matched[0]; 30 | }, $value); 31 | } 32 | 33 | public function forget() 34 | { 35 | $this->memory = array(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Behat/JsonProvider/JsonHolder.php: -------------------------------------------------------------------------------- 1 | lastJson = $json; 20 | } 21 | 22 | /** 23 | * @return string 24 | */ 25 | public function getJson() 26 | { 27 | return $this->lastJson; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/Behat/JsonProvider/MinkJsonProvider.php: -------------------------------------------------------------------------------- 1 | jsonHolder = $jsonHolder; 31 | $this->mink = $mink; 32 | } 33 | 34 | /** 35 | * @inheritdoc 36 | */ 37 | public static function getSubscribedEvents() 38 | { 39 | return array( 40 | StepTested::AFTER => 'provideJsonResponse' 41 | ); 42 | } 43 | 44 | 45 | public function provideJsonResponse(AfterStepTested $event) 46 | { 47 | $testResult = $event->getTestResult(); 48 | 49 | if (!$testResult instanceof ExecutedStepResult) { 50 | return; 51 | } 52 | 53 | $callResult = $testResult->getCallResult()->getReturn(); 54 | if (!$callResult instanceof \Behat\Mink\Element\DocumentElement) { 55 | return; 56 | } 57 | 58 | $response = $callResult->getContent(); 59 | $this->jsonHolder->setJson($response); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/Behat/Resources/services/mink_integration.yml: -------------------------------------------------------------------------------- 1 | services: 2 | json_spec.mink_json_provider: 3 | class: JsonSpec\Behat\JsonProvider\MinkJsonProvider 4 | arguments: ["@json_spec.json_holder", "@mink"] 5 | tags: 6 | - { name: event_dispatcher.subscriber, priority: 0 } 7 | -------------------------------------------------------------------------------- /src/Behat/Resources/services/services.yml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | json_matcher.json_helper: 4 | class: Fesor\JsonMatcher\Helper\JsonHelper 5 | 6 | json_matcher.factory: 7 | class: Fesor\JsonMatcher\JsonMatcherFactory 8 | arguments: ["@json_matcher.json_helper", %json_spec.excluded_keys%] 9 | 10 | json_spec.helper.memory_helper: 11 | class: JsonSpec\Behat\Helper\MemoryHelper 12 | 13 | json_spec.helper.json_loader: 14 | class: JsonSpec\JsonLoader 15 | arguments: [%json_spec.json_directory%] 16 | 17 | json_spec.json_holder: 18 | class: JsonSpec\Behat\JsonProvider\JsonHolder 19 | 20 | json_spec.memory_helper_aware_initializer: 21 | class: JsonSpec\Behat\Context\Initializer\MemoryHelperAwareInitializer 22 | arguments: ["@json_spec.helper.memory_helper"] 23 | tags: 24 | - {name: context.initializer} 25 | 26 | json_spec.json_holder_aware_initializer: 27 | class: JsonSpec\Behat\Context\Initializer\JsonHolderAwareInitializer 28 | arguments: ["@json_spec.json_holder"] 29 | tags: 30 | - {name: context.initializer} 31 | 32 | json_spec.json_matcher_aware_initializer: 33 | class: JsonSpec\Behat\Context\Initializer\JsonMatcherAwareInitializer 34 | arguments: ["@json_matcher.factory"] 35 | tags: 36 | - {name: context.initializer} 37 | 38 | json_spec.dependency_resolver: 39 | class: JsonSpec\Behat\Context\ArgumentResolver\DependencyResolver 40 | arguments: 41 | - "@json_matcher.factory" 42 | - "@json_spec.helper.json_loader" 43 | - "@json_matcher.json_helper" 44 | tags: 45 | - {name: context.argument_resolver} 46 | 47 | json_spec.context_class_resolver: 48 | class: JsonSpec\Behat\Context\ContextResolver\JsonSpecContextResolver 49 | tags: 50 | - {name: context.class_resolver} 51 | -------------------------------------------------------------------------------- /src/Exception/JsonSpecException.php: -------------------------------------------------------------------------------- 1 | message = sprintf('Directory not defined'); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Exception/MissingFileException.php: -------------------------------------------------------------------------------- 1 | message = sprintf('File `%s` is not exists', $path); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/JsonLoader.php: -------------------------------------------------------------------------------- 1 | setDirectory($directory); 19 | } 20 | 21 | /** 22 | * @param string $directory 23 | */ 24 | public function setDirectory($directory) 25 | { 26 | if (!is_null($directory)) { 27 | $this->directory = rtrim($directory, '/').'/'; 28 | } 29 | } 30 | 31 | /** 32 | * @param string $path 33 | * @return string 34 | */ 35 | public function loadJson($path) 36 | { 37 | if (!$this->directory) { 38 | throw new MissingDirectoryException(); 39 | } 40 | 41 | $path = $this->directory . $path; 42 | if (!is_file($path)) { 43 | throw new MissingFileException($path); 44 | } 45 | 46 | return file_get_contents($path); 47 | } 48 | 49 | } 50 | --------------------------------------------------------------------------------