├── .atoum.php ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .php-cs-fixer.dist.php ├── .scrutinizer.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── UPGRADE-3.0.md ├── UPGRADE-4.0.md ├── behat.yml.dist ├── composer.json ├── i18n ├── de.xliff ├── en.xliff.dist ├── es.xliff ├── fr.xliff ├── ja.xliff ├── pt.xliff └── ru.xliff ├── phpstan.neon.dist ├── src ├── Asserter.php ├── Context │ ├── BaseContext.php │ ├── BrowserContext.php │ ├── ContextClass │ │ └── ClassResolver.php │ ├── DebugContext.php │ ├── JsonContext.php │ ├── RestContext.php │ ├── SystemContext.php │ ├── TableContext.php │ └── XmlContext.php ├── Extension.php ├── Html.php ├── HttpCall │ ├── ContextSupportedVoter.php │ ├── ContextSupportedVoters.php │ ├── FilterableHttpCallResult.php │ ├── HttpCallListener.php │ ├── HttpCallResult.php │ ├── HttpCallResultPool.php │ ├── HttpCallResultPoolResolver.php │ ├── Request.php │ ├── Request │ │ ├── BrowserKit.php │ │ └── Goutte.php │ └── RestContextVoter.php ├── Json │ ├── Json.php │ ├── JsonInspector.php │ └── JsonSchema.php ├── Resources │ ├── schemas │ │ ├── atom.xsd │ │ └── rss-2.0.xsd │ └── services │ │ └── http_call.yml └── Xml │ └── Dom.php └── tests ├── features ├── bootstrap │ └── Bootstrap.php ├── browser.feature ├── debug.feature ├── fr │ ├── browser.feature │ ├── debug.feature │ ├── json.feature │ ├── rest.feature │ ├── system.feature │ ├── table.feature │ └── xml.feature ├── ja │ ├── browser.feature │ ├── debug.feature │ ├── json.feature │ ├── rest.feature │ ├── system.feature │ ├── table.feature │ └── xml.feature ├── json.feature ├── pt │ ├── browser.feature │ ├── debug.feature │ ├── json.feature │ ├── rest.feature │ ├── system.feature │ ├── table.feature │ └── xml.feature ├── rest.feature ├── ru │ ├── browser.feature │ ├── debug.feature │ ├── json.feature │ ├── rest.feature │ ├── system.feature │ ├── table.feature │ └── xml.feature ├── system.feature ├── table.feature └── xml.feature ├── fixtures ├── files │ ├── lorem.txt │ └── schema.json └── www │ ├── browser │ ├── auth.php │ ├── elements.html │ ├── frames.html │ ├── index.html │ └── timeout.html │ ├── index.html │ ├── json │ ├── arraywithtypes.json │ ├── booking.json │ ├── definitions.json │ ├── emptyarray.json │ ├── emptyobject.json │ ├── imajson.json │ ├── imnotajson.json │ ├── notnullvalues.json │ ├── rootarray.json │ ├── schema.json │ ├── schemaref.json │ ├── swagger.json │ ├── swaggerpartial.json │ ├── withref-invalid.json │ └── withref.json │ ├── rest │ └── index.php │ ├── table │ └── index.html │ └── xml │ ├── book.xml │ ├── country.xml │ ├── feed.atom │ ├── feed.rss │ ├── feed.xml │ ├── imnotaxml.xml │ ├── needsformatting.xml │ ├── people.xml │ ├── schema.dtd │ ├── schema.ng │ └── schema.xsd └── units ├── Context └── JsonContext.php └── Json ├── Json.php ├── JsonInspector.php └── JsonSchema.php /.atoum.php: -------------------------------------------------------------------------------- 1 | addTestsFromDirectory(__DIR__ . '/tests/units'); 4 | 5 | $script->addDefaultReport(); 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | pull_request: 8 | branches: 9 | - 'main' 10 | 11 | env: 12 | COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} 13 | 14 | jobs: 15 | php-cs-fixer: 16 | name: PHP-CS-Fixer 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | php: 21 | - '8.1' 22 | - '8.2' 23 | - '8.3' 24 | fail-fast: false 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | - name: Setup PHP 29 | uses: shivammathur/setup-php@v2 30 | with: 31 | php-version: ${{ matrix.php }} 32 | extensions: intl, bcmath, curl, openssl, mbstring 33 | ini-values: memory_limit=-1 34 | tools: pecl, composer, php-cs-fixer 35 | coverage: none 36 | - name: Run PHP-CS-Fixer fix 37 | run: php-cs-fixer fix --dry-run --diff --ansi 38 | 39 | phpstan: 40 | name: PHPStan 41 | runs-on: ubuntu-latest 42 | strategy: 43 | matrix: 44 | php: 45 | - '8.1' 46 | - '8.2' 47 | - '8.3' 48 | fail-fast: false 49 | env: 50 | APP_DEBUG: '1' # https://github.com/phpstan/phpstan-symfony/issues/37 51 | steps: 52 | - name: Checkout 53 | uses: actions/checkout@v4 54 | - name: Setup PHP 55 | uses: shivammathur/setup-php@v2 56 | with: 57 | php-version: ${{ matrix.php }} 58 | extensions: intl, bcmath, curl, openssl, mbstring 59 | ini-values: memory_limit=-1 60 | tools: pecl, composer, phpstan 61 | coverage: none 62 | - name: Get composer cache directory 63 | id: composercache 64 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT 65 | - name: Cache dependencies 66 | uses: actions/cache@v4 67 | with: 68 | path: ${{ steps.composercache.outputs.dir }} 69 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} 70 | restore-keys: ${{ runner.os }}-composer- 71 | - name: Update project dependencies 72 | run: composer update --no-interaction --no-progress --ansi 73 | - name: Cache PHPStan results 74 | uses: actions/cache@v4 75 | with: 76 | path: /tmp/phpstan 77 | key: phpstan-php${{ matrix.php }}-${{ github.sha }} 78 | restore-keys: | 79 | phpstan-php${{ matrix.php }}- 80 | phpstan- 81 | continue-on-error: true 82 | - name: Run PHPStan analysis 83 | run: phpstan analyse --no-interaction --no-progress --no-interaction --ansi 84 | 85 | atoum: 86 | name: Atoum 87 | runs-on: ubuntu-latest 88 | strategy: 89 | matrix: 90 | php: 91 | - '8.1' 92 | - '8.2' 93 | - '8.3' 94 | fail-fast: false 95 | timeout-minutes: 20 96 | steps: 97 | - name: Checkout 98 | uses: actions/checkout@v4 99 | - name: Setup PHP 100 | uses: shivammathur/setup-php@v2 101 | with: 102 | php-version: ${{ matrix.php }} 103 | tools: pecl, composer 104 | extensions: intl, bcmath, curl, openssl, mbstring 105 | coverage: pcov 106 | ini-values: memory_limit=-1 107 | - name: Get composer cache directory 108 | id: composercache 109 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT 110 | - name: Cache dependencies 111 | uses: actions/cache@v4 112 | with: 113 | path: ${{ steps.composercache.outputs.dir }} 114 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} 115 | restore-keys: ${{ runner.os }}-composer- 116 | - name: Update project dependencies 117 | run: composer update --no-interaction --no-progress --ansi 118 | - name: Run tests 119 | run: ./bin/atoum 120 | # See https://github.com/SeleniumHQ/selenium/issues/9044 121 | behat: 122 | name: Behat 123 | runs-on: ubuntu-22.04 124 | strategy: 125 | matrix: 126 | php: 127 | - '8.1' 128 | profile: 129 | - 'default' 130 | - 'symfony2' 131 | fail-fast: false 132 | env: 133 | DISPLAY: ':99' 134 | steps: 135 | - uses: actions/checkout@v4 136 | - name: Setup PHP 137 | uses: shivammathur/setup-php@v2 138 | with: 139 | php-version: ${{ matrix.php }} 140 | tools: pecl, composer 141 | extensions: intl, bcmath, curl, openssl, mbstring 142 | coverage: pcov 143 | ini-values: memory_limit=-1,display_errors=1,error_reporting=-1 144 | - name: Setup Java 145 | uses: actions/setup-java@v3 146 | with: 147 | distribution: temurin 148 | java-version: '8' 149 | - name: Run Selenium 150 | run: | 151 | wget "https://selenium-release.storage.googleapis.com/3.9/selenium-server-standalone-3.9.1.jar" -O selenium.jar 152 | java -jar selenium.jar -debug &> /dev/null & 153 | - name: Run PHP fixtures server 154 | run: php -S localhost:8080 -t tests/fixtures/www &> /dev/null & 155 | - name: Get composer cache directory 156 | id: composercache 157 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT 158 | - name: Cache dependencies 159 | uses: actions/cache@v4 160 | with: 161 | path: ${{ steps.composercache.outputs.dir }} 162 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} 163 | restore-keys: ${{ runner.os }}-composer- 164 | - name: Update project dependencies 165 | run: composer update --no-interaction --no-progress --ansi 166 | - name: Wait for browser & PHP to start 167 | run: | 168 | while ! nc -z localhost 4444 in(__DIR__) 7 | ->exclude([ 8 | 'src/Core/Bridge/Symfony/Maker/Resources/skeleton', 9 | 'tests/Fixtures/app/var', 10 | 'tests/Fixtures/Symfony/Maker', 11 | ]) 12 | ->notPath('src/Symfony/Bundle/DependencyInjection/Configuration.php') 13 | ->notPath('src/Annotation/ApiFilter.php') // temporary 14 | ->notPath('src/Annotation/ApiProperty.php') // temporary 15 | ->notPath('src/Annotation/ApiResource.php') // temporary 16 | ->notPath('src/Annotation/ApiSubresource.php') // temporary 17 | ->notPath('tests/Fixtures/TestBundle/Entity/DummyPhp8.php') // temporary 18 | ->append([ 19 | 'tests/Fixtures/app/console', 20 | ]); 21 | 22 | $defaultIgnoreTags = (new PhpCsFixer\Fixer\DoctrineAnnotation\DoctrineAnnotationSpacesFixer()) 23 | ->getConfigurationDefinition() 24 | ->resolve([])['ignored_tags'] ?? [] 25 | ; 26 | 27 | return (new PhpCsFixer\Config()) 28 | ->setRiskyAllowed(true) 29 | ->setRules([ 30 | '@DoctrineAnnotation' => true, 31 | '@PHP71Migration' => true, 32 | '@PHP71Migration:risky' => true, 33 | '@PHPUnit60Migration:risky' => true, 34 | '@Symfony' => true, 35 | '@Symfony:risky' => true, 36 | 'align_multiline_comment' => [ 37 | 'comment_type' => 'phpdocs_like', 38 | ], 39 | 'array_indentation' => true, 40 | 'compact_nullable_typehint' => true, 41 | 'doctrine_annotation_array_assignment' => [ 42 | 'operator' => '=', 43 | ], 44 | 'doctrine_annotation_spaces' => [ 45 | 'after_array_assignments_equals' => false, 46 | 'before_array_assignments_equals' => false, 47 | 'ignored_tags' => array_merge($defaultIgnoreTags, [ 48 | // Behat step tags 49 | 'Given', 50 | 'When', 51 | 'Then', 52 | ]), 53 | ], 54 | 'explicit_indirect_variable' => true, 55 | 'fully_qualified_strict_types' => true, 56 | 'logical_operators' => true, 57 | 'multiline_comment_opening_closing' => true, 58 | 'multiline_whitespace_before_semicolons' => [ 59 | 'strategy' => 'no_multi_line', 60 | ], 61 | 'no_alternative_syntax' => true, 62 | 'no_extra_blank_lines' => [ 63 | 'tokens' => [ 64 | 'break', 65 | 'continue', 66 | 'curly_brace_block', 67 | 'extra', 68 | 'parenthesis_brace_block', 69 | 'return', 70 | 'square_brace_block', 71 | 'throw', 72 | 'use', 73 | ], 74 | ], 75 | 'no_superfluous_elseif' => true, 76 | 'no_superfluous_phpdoc_tags' => [ 77 | 'allow_mixed' => false, 78 | ], 79 | 'no_unset_cast' => true, 80 | 'no_unset_on_property' => true, 81 | 'no_useless_else' => true, 82 | 'no_useless_return' => true, 83 | 'ordered_imports' => [ 84 | 'imports_order' => [ 85 | 'class', 86 | 'function', 87 | 'const', 88 | ], 89 | 'sort_algorithm' => 'alpha', 90 | ], 91 | 'php_unit_method_casing' => [ 92 | 'case' => 'camel_case', 93 | ], 94 | 'php_unit_set_up_tear_down_visibility' => true, 95 | 'php_unit_test_annotation' => [ 96 | 'style' => 'prefix', 97 | ], 98 | 'phpdoc_add_missing_param_annotation' => [ 99 | 'only_untyped' => true, 100 | ], 101 | 'phpdoc_no_alias_tag' => true, 102 | 'phpdoc_order' => true, 103 | 'phpdoc_trim_consecutive_blank_line_separation' => true, 104 | 'phpdoc_var_annotation_correct_order' => true, 105 | 'return_assignment' => true, 106 | 'strict_param' => true, 107 | 'visibility_required' => [ 108 | 'elements' => [ 109 | 'const', 110 | 'method', 111 | 'property', 112 | ], 113 | ], 114 | 'declare_strict_types' => false, 115 | ]) 116 | ->setFinder($finder); 117 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | tools: 2 | php_sim: true 3 | php_pdepend: true 4 | php_analyzer: true 5 | 6 | filter: 7 | excluded_paths: 8 | - 'tests/*' 9 | - 'bin/*' 10 | checks: 11 | php: 12 | excluded_dependencies: 13 | - atoum/atoum 14 | security_vulnerabilities: true 15 | use_self_instead_of_fqcn: true 16 | uppercase_constants: true 17 | simplify_boolean_return: true 18 | remove_extra_empty_lines: true 19 | properties_in_camelcaps: true 20 | prefer_while_loop_over_for_loop: true 21 | parameters_in_camelcaps: true 22 | optional_parameters_at_the_end: true 23 | no_goto: true 24 | newline_at_end_of_file: true 25 | classes_in_camel_caps: true 26 | avoid_todo_comments: true 27 | avoid_multiple_statements_on_same_line: true 28 | avoid_fixme_comments: true 29 | fix_doc_comments: false 30 | 31 | build: 32 | tests: 33 | override: 34 | - 35 | command: './bin/atoum' 36 | coverage: 37 | file: 'atoum.xunit.xml' 38 | format: 'php-clover' 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Behatch is an open source, community-driven project. If you'd like to 4 | contribute, feel free to do this, but remember to follow this few simple rules: 5 | 6 | * Make your feature addition or bug fix, 7 | * __Always__ as base for your changes use `master` branch (all new development 8 | happens here), 9 | * Add `*.features` for those changes (please look into `features/` folder for 10 | some examples). This is important so we don't break it in a future version 11 | unintentionally, 12 | * __Remember__: when you create Pull Request, always select `master` branch as 13 | target, otherwise it will be closed (this is selected by default). 14 | 15 | # Contributing to Formatter Translations 16 | 17 | Almost step provide by Behatch could be translated into your language with 18 | `--lang` option. In order to fix/add translation, edit the appropriate file in 19 | the `i18n` directory. 20 | 21 | # Running tests 22 | 23 | Make sure that you don't break anything with your changes by running the test 24 | suites: 25 | 26 | ```bash 27 | $> ./bin/atoum 28 | $> ./bin/behat 29 | ``` 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | "THE BEER-WARE LICENSE" (Revision 42): 2 | As long as you retain this notice you can do whatever you want with this stuff. 3 | If we meet some day, and you think this stuff is worth it, you can buy me a beer 4 | in return. 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Behatch contexts 2 | ================ 3 | 4 | [![CI](https://github.com/soyuka/contexts/actions/workflows/ci.yml/badge.svg)](https://github.com/soyuka/contexts/actions/workflows/ci.yml) 5 | 6 | Behatch contexts provide most common Behat tests. 7 | 8 | Installation 9 | ------------ 10 | 11 | This extension requires: 12 | 13 | * Behat 3+ 14 | * Mink 15 | * Mink extension 16 | 17 | ### Project dependency 18 | 19 | 1. [Install Composer](https://getcomposer.org/download/) 20 | 2. Require the package with Composer: 21 | 22 | ``` 23 | $ composer require --dev soyuka/contexts 24 | ``` 25 | 26 | 3. Activate extension by specifying its class in your `behat.yml`: 27 | 28 | ```yaml 29 | # behat.yml 30 | default: 31 | # ... 32 | extensions: 33 | Behatch\Extension: ~ 34 | ``` 35 | 36 | ### Project bootstraping 37 | 38 | 1. Download the Behatch skeleton with composer: 39 | 40 | ``` 41 | $ php composer.phar create-project behatch/skeleton 42 | ``` 43 | 44 | Browser, json, table and rest step need a mink configuration, see [Mink 45 | extension](https://github.com/FriendsOfBehat/MinkExtension) for more information. 46 | 47 | Usage 48 | ----- 49 | 50 | In `behat.yml`, enable desired contexts: 51 | 52 | ```yaml 53 | default: 54 | suites: 55 | default: 56 | contexts: 57 | - behatch:context:browser 58 | - behatch:context:debug 59 | - behatch:context:system 60 | - behatch:context:json 61 | - behatch:context:table 62 | - behatch:context:rest 63 | - behatch:context:xml 64 | ``` 65 | 66 | ### Examples 67 | 68 | This project is self-tested, you can explore the [features 69 | directory](./tests/features) to find some examples. 70 | 71 | Configuration 72 | ------------- 73 | 74 | * `browser` - more browser related steps (like mink) 75 | * `timeout` - default timeout 76 | * `debug` - helper steps for debugging 77 | * `screenshotDir` - the directory where store screenshots 78 | * `system` - shell related steps 79 | * `root` - the root directory of the filesystem 80 | * `json` - JSON related steps 81 | * `evaluationMode` - javascript "foo.bar" or php "foo->bar" 82 | * `table` - play with HTML the tables 83 | * `rest` - send GET, POST, ... requests and test the HTTP headers 84 | * `xml` - XML related steps 85 | 86 | ### Configuration Example 87 | 88 | For example, if you want to change default directory to screenshots - you can do it this way: 89 | 90 | ```yaml 91 | default: 92 | suites: 93 | default: 94 | contexts: 95 | - behatch:context:debug: 96 | screenshotDir: "var" 97 | ``` 98 | -------------------------------------------------------------------------------- /UPGRADE-3.0.md: -------------------------------------------------------------------------------- 1 | UPGRADE FROM 2.x to 3.0 2 | ======================= 3 | 4 | * All classes have moved to `Sanpi\Behatch` namespace to `Behatch`. 5 | 6 | * The contexts aliases start with `behatch:context:` instead of `behatch:` 7 | prefix. 8 | 9 | * Fixed miss spelling methods: 10 | * `JsonContext::theJsonNodesShoudBeEqualTo` => `JsonContext::theJsonNodesShouldBeEqualTo` 11 | * `JsonContext::theJsonNodesShoudContain` => `JsonContext::theJsonNodesShouldContain` 12 | * `JsonContext::theJsonNodesShoudNotContain` => `JsonContext::theJsonNodesShouldNotContain` 13 | * `SystemContext::ouputShouldNotContain` => `SystemContext::outputShouldNotContain` 14 | -------------------------------------------------------------------------------- /UPGRADE-4.0.md: -------------------------------------------------------------------------------- 1 | [Behat Deprecation 2 | Extension](https://github.com/caciobanu/behat-deprecation-extension) can help 3 | you to find and fix deprecation notices. 4 | 5 | # Upgrade from 3.x to 4.0 6 | 7 | * PHP 5 is not supported, please upgrade to PHP 7 8 | -------------------------------------------------------------------------------- /behat.yml.dist: -------------------------------------------------------------------------------- 1 | default: 2 | suites: 3 | default: 4 | paths: [ '%paths.base%/tests/features' ] 5 | contexts: 6 | - Behat\MinkExtension\Context\MinkContext 7 | - behatch:context:browser: 8 | timeout: 1 9 | - behatch:context:debug: 10 | screenshotDir: '.' 11 | - behatch:context:json: 12 | evaluationMode: javascript 13 | - behatch:context:rest 14 | - behatch:context:system: 15 | root: '.' 16 | - behatch:context:table 17 | - behatch:context:xml 18 | filters: 19 | tags: '~@user' 20 | extensions: 21 | Behat\MinkExtension: 22 | base_url: 'http://localhost:8080' 23 | files_path: '%paths.base%/tests/fixtures/files' 24 | sessions: 25 | default: 26 | goutte: ~ 27 | symfony2: 28 | selenium2: 29 | browser: 'chrome' 30 | capabilities: 31 | browserName: 'chrome' 32 | chrome: 33 | switches: ['--headless', '--disable-gpu', '--no-sandbox' ] 34 | extra_capabilities: 35 | "goog:chromeOptions": 36 | w3c: false 37 | Behatch\Extension: ~ 38 | 39 | symfony2: 40 | suites: 41 | default: 42 | filters: 43 | # Ignore @statusCode and @rest tags because Selenium2Driver does not support headers or status code (https://github.com/php-webdriver/php-webdriver/issues/811) 44 | # Ignore @json and @xml tags because response is wrapped inside html tags 45 | tags: '~@user&&~@statusCode&&~@rest&&~@json&&~@xml' 46 | extensions: 47 | Behat\MinkExtension: 48 | default_session: 'symfony2' 49 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "soyuka/contexts", 3 | "description": "Behatch contexts", 4 | "keywords": ["BDD", "Behat", "Symfony2", "Context"], 5 | "type": "library", 6 | "license": "beerware", 7 | 8 | "require": { 9 | "php": ">=8.0", 10 | "behat/behat": "^3.0.13", 11 | "friends-of-behat/mink-extension": "^2.3.1", 12 | "justinrainbow/json-schema": "^5.0|^6.0", 13 | "symfony/property-access": "^2.3|^3.0|^4.0|^5.0|^6.0|^7.0", 14 | "symfony/http-foundation": "^2.3|^3.0|^4.0|^5.0|^6.0|^7.0", 15 | "symfony/dom-crawler": "^2.4|^3.0|^4.0|^5.0|^6.0|^7.0" 16 | }, 17 | 18 | "require-dev": { 19 | "behat/mink-goutte-driver": "^1.1", 20 | "guzzlehttp/guzzle": "^6.3", 21 | "behat/mink-selenium2-driver": "^1.6", 22 | "atoum/atoum": "^4.0", 23 | "fabpot/goutte": "^3.2", 24 | "phpunit/phpunit": "^9.5", 25 | "atoum/stubs": "^2.6" 26 | }, 27 | 28 | "autoload": { 29 | "psr-4": { 30 | "Behatch\\": "src/", 31 | "Behatch\\Tests\\Units\\": "tests/units/" 32 | } 33 | }, 34 | 35 | "config": { 36 | "bin-dir": "bin/" 37 | }, 38 | 39 | "replace": { 40 | "sanpi/behatch-contexts": "self.version" 41 | }, 42 | 43 | "extra": { 44 | "branch-alias": { 45 | "dev-master": "3.0.x-dev" 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 5 3 | paths: 4 | - src 5 | - tests 6 | scanDirectories: 7 | - vendor/atoum/stubs/classes 8 | -------------------------------------------------------------------------------- /src/Asserter.php: -------------------------------------------------------------------------------- 1 | getSession()->getDriver()); 18 | } 19 | 20 | protected function assert($test, $message): void 21 | { 22 | if (false === $test) { 23 | throw new ExpectationException($message, $this->getSession()->getDriver()); 24 | } 25 | } 26 | 27 | protected function assertContains($expected, $actual, $message = null): void 28 | { 29 | $regex = '/'.preg_quote($expected, '/').'/ui'; 30 | 31 | $this->assert( 32 | preg_match($regex, $actual) > 0, 33 | $message ?: "The string '$expected' was not found." 34 | ); 35 | } 36 | 37 | protected function assertNotContains($expected, $actual, $message = null): void 38 | { 39 | $message = $message ?: "The string '$expected' was found."; 40 | 41 | $this->not(function () use ($expected, $actual): void { 42 | $this->assertContains($expected, $actual); 43 | }, $message); 44 | } 45 | 46 | protected function assertCount($expected, array $elements, $message = null): void 47 | { 48 | $this->assert( 49 | (int) $expected === \count($elements), 50 | $message ?: \sprintf('%d elements found, but should be %d.', \count($elements), $expected) 51 | ); 52 | } 53 | 54 | protected function assertEquals($expected, $actual, $message = null): void 55 | { 56 | $this->assert( 57 | $expected == $actual, 58 | $message ?: "The element '$actual' is not equal to '$expected'" 59 | ); 60 | } 61 | 62 | protected function assertSame($expected, $actual, $message = null): void 63 | { 64 | $this->assert( 65 | $expected === $actual, 66 | $message ?: "The element '$actual' is not equal to '$expected'" 67 | ); 68 | } 69 | 70 | protected function assertArrayHasKey($key, $array, $message = null): void 71 | { 72 | $this->assert( 73 | isset($array[$key]), 74 | $message ?: "The array has no key '$key'" 75 | ); 76 | } 77 | 78 | protected function assertArrayNotHasKey($key, $array, $message = null): void 79 | { 80 | $message = $message ?: "The array has key '$key'"; 81 | 82 | $this->not(function () use ($key, $array): void { 83 | $this->assertArrayHasKey($key, $array); 84 | }, $message); 85 | } 86 | 87 | protected function assertTrue($value, $message = 'The value is false'): void 88 | { 89 | $this->assert($value, $message); 90 | } 91 | 92 | protected function assertFalse($value, $message = 'The value is true'): void 93 | { 94 | $this->not(function () use ($value): void { 95 | $this->assertTrue($value); 96 | }, $message); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Context/BaseContext.php: -------------------------------------------------------------------------------- 1 | setMink($this->getMink()); 45 | $context->setMinkParameters($this->getMinkParameters()); 46 | 47 | return $context; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Context/ContextClass/ClassResolver.php: -------------------------------------------------------------------------------- 1 | screenshotDir = $screenshotDir; 19 | } 20 | 21 | /** 22 | * Pauses the scenario until the user presses a key. Useful when debugging a scenario. 23 | * 24 | * @Then (I )put a breakpoint 25 | */ 26 | public function iPutABreakpoint(): void 27 | { 28 | fwrite(\STDOUT, "\033[s \033[93m[Breakpoint] Press \033[1;93m[RETURN]\033[0;93m to continue...\033[0m"); 29 | while ('' == fgets(\STDIN, 1024)) { 30 | } 31 | fwrite(\STDOUT, "\033[u"); 32 | } 33 | 34 | /** 35 | * Saving a screenshot. 36 | * 37 | * @When (I )save a screenshot in :filename 38 | */ 39 | public function iSaveAScreenshotIn($filename): void 40 | { 41 | sleep(1); 42 | $this->saveScreenshot($filename, $this->screenshotDir); 43 | } 44 | 45 | /** 46 | * @AfterStep 47 | */ 48 | public function failScreenshots(AfterStepScope $scope): void 49 | { 50 | if ($scope->getTestResult()->isPassed()) { 51 | return; 52 | } 53 | 54 | $this->displayProfilerLink(); 55 | 56 | $suiteName = urlencode(str_replace(' ', '_', $scope->getSuite()->getName())); 57 | $featureName = urlencode(str_replace(' ', '_', $scope->getFeature()->getTitle() ?? '')); 58 | 59 | if ($this->getBackground($scope)) { 60 | $scenarioName = 'background'; 61 | } else { 62 | $scenario = $this->getScenario($scope); 63 | $scenarioName = urlencode(str_replace(' ', '_', $scenario->getTitle() ?? '')); 64 | } 65 | 66 | $filename = \sprintf('fail_%s_%s_%s_%s.png', time(), $suiteName, $featureName, $scenarioName); 67 | $this->saveScreenshot($filename, $this->screenshotDir); 68 | } 69 | 70 | private function displayProfilerLink(): void 71 | { 72 | try { 73 | $headers = $this->getMink()->getSession()->getResponseHeaders(); 74 | echo "The debug profile URL {$headers['X-Debug-Token-Link'][0]}"; 75 | } catch (\Exception $e) { 76 | /* Intentionally leave blank */ 77 | } 78 | } 79 | 80 | /** 81 | * @return \Behat\Gherkin\Node\ScenarioInterface 82 | */ 83 | private function getScenario(AfterStepScope $scope) 84 | { 85 | $scenarios = $scope->getFeature()->getScenarios(); 86 | foreach ($scenarios as $scenario) { 87 | $stepLinesInScenario = array_map( 88 | function (StepNode $step) { 89 | return $step->getLine(); 90 | }, 91 | $scenario->getSteps() 92 | ); 93 | if (\in_array($scope->getStep()->getLine(), $stepLinesInScenario, true)) { 94 | return $scenario; 95 | } 96 | } 97 | 98 | throw new \LogicException('Unable to find the scenario'); 99 | } 100 | 101 | /** 102 | * @return \Behat\Gherkin\Node\BackgroundNode|bool 103 | */ 104 | private function getBackground(AfterStepScope $scope) 105 | { 106 | $background = $scope->getFeature()->getBackground(); 107 | if (!$background) { 108 | return false; 109 | } 110 | $stepLinesInBackground = array_map( 111 | function (StepNode $step) { 112 | return $step->getLine(); 113 | }, 114 | $background->getSteps() 115 | ); 116 | if (\in_array($scope->getStep()->getLine(), $stepLinesInBackground, true)) { 117 | return $background; 118 | } 119 | 120 | return false; 121 | } 122 | 123 | public function saveScreenshot($filename = null, $filepath = null): void 124 | { 125 | try { 126 | parent::saveScreenshot($filename, $filepath); 127 | } catch (UnsupportedDriverActionException $e) { 128 | return; 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/Context/SystemContext.php: -------------------------------------------------------------------------------- 1 | root = $root; 19 | } 20 | 21 | public static function getTranslationResources() 22 | { 23 | return glob(__DIR__.'/../../i18n/*.xliff'); 24 | } 25 | 26 | /** 27 | * Execute a command. 28 | * 29 | * @Given (I )execute :command 30 | */ 31 | public function iExecute($cmd): void 32 | { 33 | $start = microtime(true); 34 | 35 | exec($cmd, $this->output, $this->lastReturnCode); 36 | 37 | $this->lastExecutionTime = microtime(true) - $start; 38 | } 39 | 40 | /** 41 | * Execute a command from project root. 42 | * 43 | * @Given (I )execute :command from project root 44 | */ 45 | public function iExecuteFromProjectRoot($cmd): void 46 | { 47 | $cmd = $this->root.\DIRECTORY_SEPARATOR.$cmd; 48 | $this->iExecute($cmd); 49 | } 50 | 51 | /** 52 | * Display the last command output. 53 | * 54 | * @Then (I )display the last command output 55 | */ 56 | public function iDumpCommandOutput(): void 57 | { 58 | echo implode(\PHP_EOL, $this->output); 59 | } 60 | 61 | /** 62 | * Command should succeed. 63 | * 64 | * @Then command should succeed 65 | */ 66 | public function commandShouldSucceed(): void 67 | { 68 | if (0 !== $this->lastReturnCode) { 69 | throw new \Exception(\sprintf('Command should succeed %b', $this->lastReturnCode)); 70 | } 71 | } 72 | 73 | /** 74 | * Command should fail. 75 | * 76 | * @Then command should fail 77 | */ 78 | public function commandShouldFail(): void 79 | { 80 | if (0 === $this->lastReturnCode) { 81 | throw new \Exception(\sprintf('Command should fail %b', $this->lastReturnCode)); 82 | } 83 | } 84 | 85 | /** 86 | * Command should last less than. 87 | * 88 | * @Then command should last less than :seconds seconds 89 | */ 90 | public function commandShouldLastLessThan($seconds): void 91 | { 92 | if ($this->lastExecutionTime > $seconds) { 93 | throw new \Exception(\sprintf('Last command last %s which is more than %s seconds', $this->lastExecutionTime, $seconds)); 94 | } 95 | } 96 | 97 | /** 98 | * Command should last more than. 99 | * 100 | * @Then command should last more than :seconds seconds 101 | */ 102 | public function commandShouldMoreLessThan($seconds): void 103 | { 104 | if ($this->lastExecutionTime < $seconds) { 105 | throw new \Exception(\sprintf('Last command last %s which is less than %s seconds', $this->lastExecutionTime, $seconds)); 106 | } 107 | } 108 | 109 | /** 110 | * Checks, that output contains specified text. 111 | * 112 | * @Then output should contain :text 113 | */ 114 | public function outputShouldContain($text): void 115 | { 116 | $regex = '~'.$text.'~ui'; 117 | 118 | $check = false; 119 | foreach ($this->output as $line) { 120 | if (1 === preg_match($regex, $line)) { 121 | $check = true; 122 | break; 123 | } 124 | } 125 | 126 | if (false === $check) { 127 | throw new \Exception(\sprintf("The text '%s' was not found anywhere on output of command.\n%s", $text, implode("\n", $this->output))); 128 | } 129 | } 130 | 131 | /** 132 | * Checks, that output not contains specified text. 133 | * 134 | * @Then output should not contain :text 135 | */ 136 | public function outputShouldNotContain($text): void 137 | { 138 | $regex = '~'.$text.'~ui'; 139 | 140 | foreach ($this->output as $line) { 141 | if (1 === preg_match($regex, $line)) { 142 | throw new \Exception(\sprintf("The text '%s' was found somewhere on output of command.\n%s", $text, implode("\n", $this->output))); 143 | } 144 | } 145 | } 146 | 147 | /** 148 | * @Given output should be: 149 | */ 150 | public function outputShouldBe(PyStringNode $string): void 151 | { 152 | $expected = $string->getStrings(); 153 | foreach ($this->output as $index => $line) { 154 | if ($line !== $expected[$index]) { 155 | throw new \Exception(\sprintf("instead of\n%s", implode("\n", $this->output))); 156 | } 157 | } 158 | } 159 | 160 | /** 161 | * @Given output should not be: 162 | */ 163 | public function outputShouldNotBe(PyStringNode $string): void 164 | { 165 | $expected = $string->getStrings(); 166 | 167 | $check = false; 168 | foreach ($this->output as $index => $line) { 169 | if ($line !== $expected[$index]) { 170 | $check = true; 171 | break; 172 | } 173 | } 174 | 175 | if (false === $check) { 176 | throw new \Exception('Output should not be'); 177 | } 178 | } 179 | 180 | /** 181 | * @Given (I )create the file :filename containing: 182 | * @Given (I )create the file :filename contening: 183 | */ 184 | public function iCreateTheFileContaining($filename, PyStringNode $string): void 185 | { 186 | if (!is_file($filename)) { 187 | file_put_contents($filename, $string); 188 | $this->createdFiles[] = $filename; 189 | } else { 190 | throw new \RuntimeException("'$filename' already exists."); 191 | } 192 | } 193 | 194 | /** 195 | * @Then print the content of :filename file 196 | */ 197 | public function printTheContentOfFile($filename): void 198 | { 199 | if (is_file($filename)) { 200 | echo file_get_contents($filename); 201 | } else { 202 | throw new \RuntimeException("'$filename' doesn't exists."); 203 | } 204 | } 205 | 206 | /** 207 | * @AfterScenario 208 | */ 209 | public function after(): void 210 | { 211 | foreach ($this->createdFiles as $filename) { 212 | unlink($filename); 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/Context/TableContext.php: -------------------------------------------------------------------------------- 1 | getSession()->getPage()->findAll('css', $columnsSelector); 18 | 19 | $this->iShouldSeeColumnsInTheTable(\count($text->getHash()), $table); 20 | 21 | foreach ($text->getHash() as $key => $column) { 22 | $this->assertEquals($column['columns'], $columns[$key]->getText()); 23 | } 24 | } 25 | 26 | /** 27 | * Checks that the specified table contains the given number of columns. 28 | * 29 | * @Then (I )should see :count column(s) in the :table table 30 | */ 31 | public function iShouldSeeColumnsInTheTable(int $count, string $table): void 32 | { 33 | $columnsSelector = "$table thead tr th"; 34 | $columns = $this->getSession()->getPage()->findAll('css', $columnsSelector); 35 | 36 | $this->assertEquals($count, \count($columns)); 37 | } 38 | 39 | /** 40 | * Checks that the specified table contains the specified number of rows in its body. 41 | * 42 | * @Then (I )should see :count rows in the :index :table table 43 | */ 44 | public function iShouldSeeRowsInTheNthTable(int $count, int $index, string $table): void 45 | { 46 | $actual = $this->countElements('tbody tr', $index, $table); 47 | $this->assertEquals($count, $actual); 48 | } 49 | 50 | /** 51 | * Checks that the specified table contains the specified number of rows in its body. 52 | * 53 | * @Then (I )should see :count row(s) in the :table table 54 | */ 55 | public function iShouldSeeRowsInTheTable(int $count, string $table): void 56 | { 57 | $this->iShouldSeeRowsInTheNthTable($count, 1, $table); 58 | } 59 | 60 | /** 61 | * Checks that the data of the specified row matches the given schema. 62 | * 63 | * @Then the data in the :index row of the :table table should match: 64 | */ 65 | public function theDataOfTheRowShouldMatch(int $index, string $table, TableNode $text): void 66 | { 67 | $rowsSelector = "$table tbody tr"; 68 | $rows = $this->getSession()->getPage()->findAll('css', $rowsSelector); 69 | 70 | if (!isset($rows[$index - 1])) { 71 | throw new \Exception("The row $index was not found in the '$table' table"); 72 | } 73 | 74 | $cells = (array) $rows[$index - 1]->findAll('css', 'td'); 75 | $cells = array_merge((array) $rows[$index - 1]->findAll('css', 'th'), $cells); 76 | 77 | $hash = current($text->getHash()); 78 | 79 | foreach (array_keys($hash) as $columnName) { 80 | // Extract index from column. ex "col2" -> 2 81 | preg_match('/^col(?P\d+)$/', $columnName, $matches); 82 | $index = (int) $matches['index'] - 1; 83 | 84 | $this->assertEquals($hash[$columnName], $cells[$index]->getText()); 85 | } 86 | } 87 | 88 | /** 89 | * Checks that the specified cell (column/row) of the table's body contains the specified text. 90 | * 91 | * @Then the :colIndex column of the :rowIndex row in the :table table should contain :text 92 | */ 93 | public function theStColumnOfTheStRowInTheTableShouldContain(int $colIndex, int $rowIndex, string $table, string $text): void 94 | { 95 | $rowSelector = "$table tbody tr"; 96 | $rows = $this->getSession()->getPage()->findAll('css', $rowSelector); 97 | 98 | if (!isset($rows[$rowIndex - 1])) { 99 | throw new \Exception("The row $rowIndex was not found in the '$table' table"); 100 | } 101 | 102 | $row = $rows[$rowIndex - 1]; 103 | $cols = $row->findAll('css', 'td'); 104 | 105 | if (!isset($cols[$colIndex - 1])) { 106 | throw new \Exception("The column $colIndex was not found in the row $rowIndex of the '$table' table"); 107 | } 108 | 109 | $actual = $cols[$colIndex - 1]->getText(); 110 | 111 | $this->assertContains($text, $actual); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Extension.php: -------------------------------------------------------------------------------- 1 | load('http_call.yml'); 36 | 37 | $this->loadClassResolver($container); 38 | $this->loadHttpCallListener($container); 39 | } 40 | 41 | public function configure(ArrayNodeDefinition $builder): void 42 | { 43 | } 44 | 45 | private function loadClassResolver(ContainerBuilder $container): void 46 | { 47 | $definition = new Definition('Behatch\Context\ContextClass\ClassResolver'); 48 | $definition->addTag(ContextExtension::CLASS_RESOLVER_TAG); 49 | $container->setDefinition('behatch.class_resolver', $definition); 50 | } 51 | 52 | private function loadHttpCallListener(ContainerBuilder $container): void 53 | { 54 | $processor = new \Behat\Testwork\ServiceContainer\ServiceProcessor(); 55 | $references = $processor->findAndSortTaggedServices($container, 'behatch.context_voter'); 56 | $definition = $container->getDefinition('behatch.context_supported.voter'); 57 | 58 | foreach ($references as $reference) { 59 | $definition->addMethodCall('register', [$reference]); 60 | } 61 | } 62 | 63 | public function getCompilerPasses() 64 | { 65 | return []; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Html.php: -------------------------------------------------------------------------------- 1 | getSession()->getPage(); 12 | 13 | $parents = $page->findAll('css', $parent); 14 | if (!isset($parents[$index - 1])) { 15 | throw new \Exception("The $index element '$parent' was not found anywhere in the page"); 16 | } 17 | 18 | $elements = $parents[$index - 1]->findAll('css', $element); 19 | 20 | return \count($elements); 21 | } 22 | 23 | protected function findElement($selector, $locator, $index) 24 | { 25 | $page = $this->getSession()->getPage(); 26 | 27 | $nodes = $page->findAll($selector, $locator); 28 | 29 | if (!isset($nodes[$index - 1])) { 30 | throw new \Exception("The $index $selector '$locator' was not found anywhere in the page"); 31 | } 32 | 33 | return $nodes[$index - 1]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/HttpCall/ContextSupportedVoter.php: -------------------------------------------------------------------------------- 1 | register($voter); 13 | } 14 | } 15 | 16 | public function register(ContextSupportedVoter $voter): void 17 | { 18 | $this->voters[] = $voter; 19 | } 20 | 21 | public function vote(HttpCallResult $httpCallResult) 22 | { 23 | foreach ($this->voters as $voter) { 24 | if ($voter->vote($httpCallResult)) { 25 | if ($voter instanceof FilterableHttpCallResult) { 26 | $httpCallResult->update($voter->filter($httpCallResult)); 27 | } 28 | 29 | return true; 30 | } 31 | } 32 | 33 | return false; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/HttpCall/FilterableHttpCallResult.php: -------------------------------------------------------------------------------- 1 | contextSupportedVoter = $contextSupportedVoter; 22 | $this->httpCallResultPool = $httpCallResultPool; 23 | $this->mink = $mink; 24 | } 25 | 26 | public static function getSubscribedEvents() 27 | { 28 | return [ 29 | StepTested::AFTER => 'afterStep', 30 | ]; 31 | } 32 | 33 | public function afterStep(AfterStepTested $event) 34 | { 35 | $testResult = $event->getTestResult(); 36 | 37 | if (!$testResult instanceof ExecutedStepResult) { 38 | return; 39 | } 40 | 41 | $httpCallResult = new HttpCallResult( 42 | $testResult->getCallResult()->getReturn() 43 | ); 44 | 45 | if ($this->contextSupportedVoter->vote($httpCallResult)) { 46 | $this->httpCallResultPool->store($httpCallResult); 47 | 48 | return true; 49 | } 50 | 51 | // For now to avoid modification on MinkContext 52 | // We add fallback on Mink 53 | try { 54 | $this->httpCallResultPool->store( 55 | new HttpCallResult($this->mink->getSession()->getPage()->getContent()) 56 | ); 57 | } catch (\LogicException $e) { 58 | // Mink has no response 59 | } catch (\Behat\Mink\Exception\DriverException $e) { 60 | // No Mink 61 | } catch (\WebDriver\Exception\NoSuchDriver $e) { 62 | // A session is either terminated or not started 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/HttpCall/HttpCallResult.php: -------------------------------------------------------------------------------- 1 | value = $value; 12 | } 13 | 14 | public function update($value): void 15 | { 16 | $this->value = $value; 17 | } 18 | 19 | public function getValue() 20 | { 21 | return $this->value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/HttpCall/HttpCallResultPool.php: -------------------------------------------------------------------------------- 1 | result = $result; 15 | } 16 | 17 | /** 18 | * @return HttpCallResult|null 19 | */ 20 | public function getResult() 21 | { 22 | return $this->result; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/HttpCall/HttpCallResultPoolResolver.php: -------------------------------------------------------------------------------- 1 | dependencies = []; 14 | 15 | foreach (\func_get_args() as $param) { 16 | $this->dependencies[$param::class] = $param; 17 | } 18 | } 19 | 20 | public function resolveArguments(\ReflectionClass $classReflection, array $arguments) 21 | { 22 | if (null !== ($constructor = $classReflection->getConstructor())) { 23 | foreach ($constructor->getParameters() as $parameter) { 24 | if (!($type = $parameter->getType()) instanceof \ReflectionNamedType) { 25 | continue; 26 | } 27 | 28 | if ($type->isBuiltin()) { 29 | continue; 30 | } 31 | 32 | $class = new \ReflectionClass($type->getName()); 33 | 34 | if (!isset($this->dependencies[$class->name])) { 35 | continue; 36 | } 37 | 38 | $arguments[$parameter->name] = $this->dependencies[$class->name]; 39 | } 40 | } 41 | 42 | return $arguments; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/HttpCall/Request.php: -------------------------------------------------------------------------------- 1 | mink = $mink; 34 | } 35 | 36 | /** 37 | * @param string $name 38 | */ 39 | public function __call($name, $arguments) 40 | { 41 | return \call_user_func_array([$this->getClient(), $name], $arguments); 42 | } 43 | 44 | /** 45 | * @return Request\BrowserKit 46 | */ 47 | private function getClient() 48 | { 49 | if (null === $this->client) { 50 | if ('symfony2' === $this->mink->getDefaultSessionName()) { 51 | $this->client = new Request\Goutte($this->mink); 52 | } else { 53 | $this->client = new Request\BrowserKit($this->mink); 54 | } 55 | } 56 | 57 | return $this->client; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/HttpCall/Request/BrowserKit.php: -------------------------------------------------------------------------------- 1 | mink = $mink; 17 | } 18 | 19 | public function getMethod() 20 | { 21 | return $this->getRequest() 22 | ->getMethod(); 23 | } 24 | 25 | public function getUri() 26 | { 27 | return $this->getRequest() 28 | ->getUri(); 29 | } 30 | 31 | public function getServer() 32 | { 33 | return $this->getRequest() 34 | ->getServer(); 35 | } 36 | 37 | public function getParameters() 38 | { 39 | return $this->getRequest() 40 | ->getParameters(); 41 | } 42 | 43 | protected function getRequest() 44 | { 45 | $client = $this->mink->getSession()->getDriver()->getClient(); 46 | // BC layer for BrowserKit 2.2.x and older 47 | if (method_exists($client, 'getInternalRequest')) { 48 | $request = $client->getInternalRequest(); 49 | } else { 50 | $request = $client->getRequest(); 51 | } 52 | 53 | return $request; 54 | } 55 | 56 | public function getContent() 57 | { 58 | return $this->mink->getSession()->getPage()->getContent(); 59 | } 60 | 61 | public function send($method, $url, $parameters = [], $files = [], $content = null, $headers = []) 62 | { 63 | foreach ($files as $originalName => &$file) { 64 | if (\is_string($file)) { 65 | $file = new UploadedFile($file, (string) $originalName); 66 | } 67 | } 68 | 69 | $client = $this->mink->getSession()->getDriver()->getClient(); 70 | 71 | $client->followRedirects(false); 72 | 73 | // Workaround for https://github.com/symfony/symfony/issues/33393: prevent a default Accept header to be set 74 | if (!isset($headers['HTTP_ACCEPT']) && '' === $client->getServerParameter('HTTP_ACCEPT')) { 75 | $headers['HTTP_ACCEPT'] = null; 76 | } 77 | 78 | $client->request($method, $url, $parameters, $files, $headers, $content); 79 | $client->followRedirects(true); 80 | $this->resetHttpHeaders(); 81 | 82 | return $this->mink->getSession()->getPage(); 83 | } 84 | 85 | public function setHttpHeader($name, $value): void 86 | { 87 | $client = $this->mink->getSession()->getDriver()->getClient(); 88 | if (method_exists($client, 'setHeader')) { 89 | /* 90 | * @var \Goutte\Client $client 91 | */ 92 | $client->setHeader($name, $value); 93 | } else { 94 | /** 95 | * @var \Symfony\Component\BrowserKit\HttpBrowser $client 96 | */ 97 | 98 | /* taken from Behat\Mink\Driver\BrowserKitDriver::setRequestHeader */ 99 | $contentHeaders = ['CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true]; 100 | $name = str_replace('-', '_', strtoupper($name)); 101 | 102 | // CONTENT_* are not prefixed with HTTP_ in PHP when building $_SERVER 103 | if (!isset($contentHeaders[$name])) { 104 | $name = 'HTTP_'.$name; 105 | } 106 | /* taken from Behat\Mink\Driver\BrowserKitDriver::setRequestHeader */ 107 | 108 | $client->setServerParameter($name, $value); 109 | } 110 | } 111 | 112 | public function getHttpHeaders() 113 | { 114 | return array_change_key_case( 115 | $this->mink->getSession()->getResponseHeaders(), 116 | \CASE_LOWER 117 | ); 118 | } 119 | 120 | public function getHttpHeader($name) 121 | { 122 | $values = $this->getHttpRawHeader($name); 123 | 124 | return implode(', ', $values); 125 | } 126 | 127 | public function getHttpRawHeader($name) 128 | { 129 | $name = strtolower($name); 130 | $headers = $this->getHttpHeaders(); 131 | 132 | if (isset($headers[$name])) { 133 | $value = $headers[$name]; 134 | if (!\is_array($headers[$name])) { 135 | $value = [$headers[$name]]; 136 | } 137 | } else { 138 | throw new \OutOfBoundsException("The header '$name' doesn't exist"); 139 | } 140 | 141 | return $value; 142 | } 143 | 144 | protected function resetHttpHeaders(): void 145 | { 146 | /** @var GoutteClient|BrowserKitClient $client */ 147 | $client = $this->mink->getSession()->getDriver()->getClient(); 148 | 149 | $client->setServerParameters([]); 150 | if ($client instanceof GoutteClient) { 151 | $client->restart(); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/HttpCall/Request/Goutte.php: -------------------------------------------------------------------------------- 1 | requestHeaders)); 18 | $this->resetHttpHeaders(); 19 | 20 | return $page; 21 | } 22 | 23 | public function setHttpHeader($name, $value): void 24 | { 25 | /* taken from Behat\Mink\Driver\BrowserKitDriver::setRequestHeader */ 26 | $contentHeaders = ['CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true]; 27 | $name = str_replace('-', '_', strtoupper($name)); 28 | 29 | // CONTENT_* are not prefixed with HTTP_ in PHP when building $_SERVER 30 | if (!isset($contentHeaders[$name])) { 31 | $name = 'HTTP_'.$name; 32 | } 33 | /* taken from Behat\Mink\Driver\BrowserKitDriver::setRequestHeader */ 34 | 35 | $this->requestHeaders[$name] = $value; 36 | } 37 | 38 | protected function resetHttpHeaders(): void 39 | { 40 | $this->requestHeaders = []; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/HttpCall/RestContextVoter.php: -------------------------------------------------------------------------------- 1 | getValue() instanceof \Behat\Mink\Element\DocumentElement; 10 | } 11 | 12 | public function filter(HttpCallResult $httpCallResult) 13 | { 14 | return $httpCallResult->getValue()->getContent(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Json/Json.php: -------------------------------------------------------------------------------- 1 | content = $this->decode((string) $content); 14 | } 15 | 16 | public function getContent() 17 | { 18 | return $this->content; 19 | } 20 | 21 | public function read($expression, PropertyAccessor $accessor) 22 | { 23 | if (\is_array($this->content)) { 24 | $expression = preg_replace('/^root/', '', $expression); 25 | } else { 26 | $expression = preg_replace('/^root./', '', $expression); 27 | } 28 | 29 | // If root asked, we return the entire content 30 | if ('' === trim($expression)) { 31 | return $this->content; 32 | } 33 | 34 | return $accessor->getValue($this->content, $expression); 35 | } 36 | 37 | public function encode($pretty = true) 38 | { 39 | $flags = \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE; 40 | 41 | if (true === $pretty && \defined('JSON_PRETTY_PRINT')) { 42 | $flags |= \JSON_PRETTY_PRINT; 43 | } 44 | 45 | return json_encode($this->content, $flags); 46 | } 47 | 48 | public function __toString() 49 | { 50 | return $this->encode(false); 51 | } 52 | 53 | private function decode($content) 54 | { 55 | $result = json_decode($content); 56 | 57 | if (\JSON_ERROR_NONE !== json_last_error()) { 58 | throw new \Exception("The string '$content' is not valid json"); 59 | } 60 | 61 | return $result; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Json/JsonInspector.php: -------------------------------------------------------------------------------- 1 | evaluationMode = $evaluationMode; 23 | $this->accessor = new PropertyAccessor($magicMethods, $throwException); 24 | } 25 | 26 | public function evaluate(Json $json, $expression) 27 | { 28 | if ('javascript' === $this->evaluationMode) { 29 | $expression = str_replace('->', '.', $expression); 30 | } 31 | 32 | try { 33 | return $json->read($expression, $this->accessor); 34 | } catch (\Exception $e) { 35 | throw new \Exception("Failed to evaluate expression '$expression'"); 36 | } 37 | } 38 | 39 | public function validate(Json $json, JsonSchema $schema) 40 | { 41 | $validator = new \JsonSchema\Validator(); 42 | 43 | $resolver = new \JsonSchema\SchemaStorage(new \JsonSchema\Uri\UriRetriever(), new \JsonSchema\Uri\UriResolver()); 44 | $schema->resolve($resolver); 45 | 46 | return $schema->validate($json, $validator); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Json/JsonSchema.php: -------------------------------------------------------------------------------- 1 | uri = $uri; 15 | 16 | parent::__construct($content); 17 | } 18 | 19 | public function resolve(SchemaStorage $resolver) 20 | { 21 | if (!$this->hasUri()) { 22 | return $this; 23 | } 24 | 25 | $this->content = $resolver->resolveRef($this->uri); 26 | 27 | return $this; 28 | } 29 | 30 | public function validate(Json $json, Validator $validator) 31 | { 32 | $validator->check($json->getContent(), $this->getContent()); 33 | 34 | if (!$validator->isValid()) { 35 | $msg = 'JSON does not validate. Violations:'.\PHP_EOL; 36 | foreach ($validator->getErrors() as $error) { 37 | $msg .= \sprintf(' - [%s] %s'.\PHP_EOL, $error['property'], $error['message']); 38 | } 39 | throw new \Exception($msg); 40 | } 41 | 42 | return true; 43 | } 44 | 45 | private function hasUri() 46 | { 47 | return null !== $this->uri; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Resources/services/http_call.yml: -------------------------------------------------------------------------------- 1 | services: 2 | behatch.http_call.listener: 3 | class: Behatch\HttpCall\HttpCallListener 4 | arguments: ["@behatch.context_supported.voter", "@behatch.http_call.result_pool", "@mink"] 5 | public: false 6 | tags: 7 | - { name: event_dispatcher.subscriber, priority: 0 } 8 | 9 | behatch.http_call.result_pool: 10 | class: Behatch\HttpCall\HttpCallResultPool 11 | public: false 12 | 13 | behatch.context_supported.voter: 14 | class: Behatch\HttpCall\ContextSupportedVoters 15 | public: false 16 | 17 | behatch.rest_context_supported.voter: 18 | class: Behatch\HttpCall\RestContextVoter 19 | public: false 20 | tags: 21 | - { name: behatch.context_voter } 22 | 23 | behatch.http_call.request: 24 | class: Behatch\HttpCall\Request 25 | arguments: ["@mink"] 26 | public: false 27 | 28 | behatch.http_call.argument_resolver: 29 | class: Behatch\HttpCall\HttpCallResultPoolResolver 30 | arguments: ["@behatch.http_call.result_pool", "@behatch.http_call.request"] 31 | public: false 32 | tags: 33 | - { name: context.argument_resolver } 34 | -------------------------------------------------------------------------------- /src/Xml/Dom.php: -------------------------------------------------------------------------------- 1 | dom = new \DOMDocument(); 12 | $this->dom->strictErrorChecking = false; 13 | $this->dom->validateOnParse = false; 14 | $this->dom->preserveWhiteSpace = true; 15 | $this->dom->loadXML($content, \LIBXML_PARSEHUGE); 16 | $this->throwError(); 17 | } 18 | 19 | public function __toString() 20 | { 21 | $this->dom->formatOutput = true; 22 | 23 | return $this->dom->saveXML(); 24 | } 25 | 26 | public function validate(): void 27 | { 28 | $this->dom->validate(); 29 | $this->throwError(); 30 | } 31 | 32 | public function validateXsd($xsd): void 33 | { 34 | $this->dom->schemaValidateSource($xsd); 35 | $this->throwError(); 36 | } 37 | 38 | public function validateNg($ng): void 39 | { 40 | try { 41 | $this->dom->relaxNGValidateSource($ng); 42 | $this->throwError(); 43 | } catch (\DOMException $e) { 44 | throw new \RuntimeException($e->getMessage()); 45 | } 46 | } 47 | 48 | public function xpath($element) 49 | { 50 | $xpath = new \DOMXPath($this->dom); 51 | $this->registerNamespace($xpath); 52 | 53 | $element = $this->fixNamespace($element); 54 | $elements = $xpath->query($element); 55 | 56 | return (false === $elements) ? new \DOMNodeList() : $elements; 57 | } 58 | 59 | private function registerNamespace(\DOMXPath $xpath): void 60 | { 61 | $namespaces = $this->getNamespaces(); 62 | 63 | foreach ($namespaces as $prefix => $namespace) { 64 | if (empty($prefix) && $this->hasDefaultNamespace()) { 65 | $prefix = 'rootns'; 66 | } 67 | $xpath->registerNamespace($prefix, $namespace); 68 | } 69 | } 70 | 71 | /** 72 | * "fix" queries to the default namespace if any namespaces are defined. 73 | */ 74 | private function fixNamespace($element) 75 | { 76 | $namespaces = $this->getNamespaces(); 77 | 78 | if (!empty($namespaces) && $this->hasDefaultNamespace()) { 79 | for ($i = 0; $i < 2; ++$i) { 80 | $element = preg_replace('/\/(\w+)(\[[^]]+\])?\//', '/rootns:$1$2/', $element); 81 | } 82 | $element = preg_replace('/\/(\w+)(\[[^]]+\])?$/', '/rootns:$1$2', $element); 83 | } 84 | 85 | return $element; 86 | } 87 | 88 | private function hasDefaultNamespace() 89 | { 90 | $defaultNamespaceUri = $this->dom->lookupNamespaceURI(null); 91 | $defaultNamespacePrefix = $defaultNamespaceUri ? $this->dom->lookupPrefix($defaultNamespaceUri) : null; 92 | 93 | return empty($defaultNamespacePrefix) && !empty($defaultNamespaceUri); 94 | } 95 | 96 | public function getNamespaces() 97 | { 98 | $xml = simplexml_import_dom($this->dom); 99 | 100 | return $xml->getNamespaces(true); 101 | } 102 | 103 | private function throwError(): void 104 | { 105 | $error = libxml_get_last_error(); 106 | if (!empty($error)) { 107 | // https://bugs.php.net/bug.php?id=46465 108 | if ('Validation failed: no DTD found !' != $error->message) { 109 | throw new \DOMException($error->message.' at line '.$error->line); 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /tests/features/bootstrap/Bootstrap.php: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | """ 30 | 31 | Scénario: 32 | Alors le flux XML devrait être valide avec le schéma relax NG "tests/fixtures/www/xml/schema.ng" 33 | 34 | Scénario: 35 | Alors le flux XML devrait être valide avec ce schéma relax NG : 36 | """ 37 | 38 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | """ 55 | 56 | Scénario: 57 | Étant donné je suis sur "/xml/feed.atom" 58 | Alors le flux atom devrait être valide 59 | 60 | Scénario: 61 | Étant donné je suis sur "/xml/feed.rss" 62 | Alors le flux RSS2 devrait être valide 63 | -------------------------------------------------------------------------------- /tests/features/ja/browser.feature: -------------------------------------------------------------------------------- 1 | #language: ja 2 | @japanese @browser 3 | フィーチャ: Browser Feature 4 | 5 | もしテストに失敗した場合は 6 | セットアップがうまくいっていない可能性があります。 7 | README.mdをご一読ください 8 | 9 | @javascript 10 | シナリオ: Testing simple web access 11 | 前提 "/index.html" を表示している 12 | ならば 画面に "Congratulations, you've correctly set up your apache environment." と表示されていること 13 | 14 | @statusCode 15 | シナリオ: Basic authentication 16 | 前提 "/browser/auth.php" を表示している 17 | ならば レスポンスコードが 401 であること 18 | かつ 画面に "NONE SHALL PASS" と表示されていること 19 | 20 | もし Basic認証を"something"と"wrong"で設定する 21 | かつ "/browser/auth.php" へ移動する 22 | ならば レスポンスコードが 401 であること 23 | かつ 画面に "NONE SHALL PASS" と表示されていること 24 | 25 | もし Basic認証を"gabriel"と"30091984"で設定する 26 | かつ "/browser/auth.php" へ移動する 27 | ならば レスポンスコードが 200 であること 28 | かつ 画面に "Successfuly logged in" と表示されていること 29 | 30 | もし "/browser/auth.php?logout" へ移動する 31 | かつ 画面に "Logged out" と表示されていること 32 | 33 | かつ "/browser/auth.php" へ移動する 34 | ならば レスポンスコードが 401 であること 35 | かつ 画面に "NONE SHALL PASS" と表示されていること 36 | 37 | @javascript 38 | シナリオ: Elements testing 39 | 前提 下記から構成されるURLに遷移する: 40 | | parameters | 41 | | /browser | 42 | | /elements.html | 43 | ならば 1番目の"body"要素が4個の"div"要素を持つこと 44 | ならば 1番目の"body"要素が6個以下の"div"要素を持つこと 45 | ならば 1番目の"body"要素が2個以上の"div"要素を持つこと 46 | かつ セレクトボックス"months_selector"は"january"を含むこと 47 | かつ セレクトボックス"months_selector"は"december"を含まないこと 48 | もし 私が 1 番目の "ul li" 要素をクリックする 49 | ならば 画面に "You clicked First" と表示されていること 50 | 51 | @javascript 52 | シナリオ: Frames testing 53 | 前提 "/browser/frames.html" を表示している 54 | もし 私が "index" iframeにフォーカスする 55 | ならば 画面に "Visible" と表示されていること 56 | 57 | もし 私が メインフレームにフォーカスする 58 | 59 | もし 私が "elements" iframeにフォーカスする 60 | ならば セレクトボックス"months_selector"は"january"を含むこと 61 | 62 | @javascript 63 | シナリオ: Wait before seeing 64 | 前提 "/browser/timeout.html" を表示している 65 | ならば 私が"timeout"を見るまで3秒間待つ 66 | かつ 私が1秒間待つ 67 | かつ 私が"#iframe"要素を見るまで待つ 68 | かつ 私が "#iframe" 要素を見るまで 5 秒間待つ 69 | かつ 私が "#iframe" 要素を見るまで 5 秒待つ 70 | かつ "#iframe" 要素を見るまで 5 秒待つ 71 | 72 | @javascript 73 | シナリオ: Check element visibility 74 | 前提 "/browser/index.html" を表示している 75 | ならば 要素"#visible-element"は可視であること 76 | かつ 要素"#hidden-element"は不可視であること 77 | 78 | @javascript 79 | シナリオ: 80 | 前提 "/browser/elements.html" を表示している 81 | ならば 私が"today"に現在の日付を入力する 82 | かつ 私が"today"に現在の日付を"-1 day"で入力する 83 | -------------------------------------------------------------------------------- /tests/features/ja/debug.feature: -------------------------------------------------------------------------------- 1 | #language: ja 2 | @japanese @debug 3 | 機能: Debug Feature 4 | 5 | @user 6 | シナリオ: Testing a break point 7 | 前提 "/index.html" を表示している 8 | ならば 私がブレークポイントを設置する 9 | ならば 画面に "Congratulations, you've correctly set up your apache environment." と表示されていること 10 | ならば ブレークポイントを設置する 11 | 12 | @javascript 13 | シナリオ: Taking a screenshot 14 | 前提 "/index.html" を表示している 15 | ならば スクリーンショットを"./index.png"に保存する 16 | -------------------------------------------------------------------------------- /tests/features/ja/json.feature: -------------------------------------------------------------------------------- 1 | #language: ja 2 | @japanese @json 3 | 機能: Testing JSONContext 4 | 5 | シナリオ: Am I a JSON ? 6 | 前提 "/json/imajson.json" を表示している 7 | ならば レスポンスがJSONであること 8 | もし "/json/emptyarray.json" を表示している 9 | ならば レスポンスがJSON形式であること 10 | もし "/json/emptyobject.json" を表示している 11 | ならば レスポンスがJSONであること 12 | もし "/json/imnotajson.json" を表示している 13 | ならば レスポンスがJSONでないこと 14 | 15 | シナリオ: Count JSON elements 16 | 前提 "/json/imajson.json" を表示している 17 | ならば JSONのノード"numbers"が4個の要素を持つこと 18 | 19 | シナリオ: Checking JSON evaluation 20 | 前提 "/json/imajson.json" を表示している 21 | 22 | ならば JSONにノード"foo"が存在すること 23 | かつ JSONにノード"root.foo"が存在すること 24 | かつ JSONのノード"foo"が"bar"を含むこと 25 | かつ JSONのノード"foo"が"something else"を含まないこと 26 | 27 | かつ JSONのノード"numbers[0]"が"öne"を含むこと 28 | かつ JSONのノード"numbers[1]"が"two"を含むこと 29 | かつ JSONのノード"numbers[2]"が"three"を含むこと 30 | かつ JSONのノード"numbers[3].complexeshizzle"が"true"と等しいこと 31 | かつ JSONのノード"numbers[3].so[0]"が"very"と等しいこと 32 | かつ JSONのノード"numbers[3].so[1].complicated"が"indeed"と等しいこと 33 | 34 | かつ JSONにノード"bar"が存在しないこと 35 | # かつ ブレークポイントを設置する 36 | 37 | シナリオ: Json validation with schema 38 | 前提 "/json/imajson.json" を表示している 39 | ならば JSONがスキーマファイル"tests/fixtures/www/json/schema.json"に従っていること 40 | # かつ ブレークポイントを設置する 41 | 42 | シナリオ: Json validation with schema containing ref 43 | 前提 "/json/withref.json" を表示している 44 | ならば JSONがスキーマファイル"tests/fixtures/www/json/schemaref.json"に従っていること 45 | # かつ ブレークポイントを設置する 46 | 47 | シナリオ: Json validation 48 | 前提 "/json/imajson.json" を表示している 49 | ならば JSONが下記のスキーマに従っていること: 50 | """ 51 | { 52 | "type": "object", 53 | "$schema": "http://json-schema.org/draft-03/schema", 54 | "required":true, 55 | "properties": { 56 | "foo": { 57 | "type": "string", 58 | "required":true 59 | }, 60 | "numbers": { 61 | "type": "array", 62 | "required":true, 63 | "öne": { 64 | "type": "string", 65 | "required":true 66 | }, 67 | "two": { 68 | "type": "string", 69 | "required":true 70 | }, 71 | "three": { 72 | "type": "string", 73 | "required":true 74 | } 75 | } 76 | } 77 | } 78 | """ 79 | # かつ ブレークポイントを設置する 80 | 81 | シナリオ: Json contents validation 82 | 前提 "/json/imajson.json" を表示している 83 | ならば JSONが下記と一致すること: 84 | """ 85 | { 86 | "foo": "bar", 87 | "numbers": [ 88 | "öne", 89 | "two", 90 | "three", 91 | { 92 | "complexeshizzle": true, 93 | "so": [ 94 | "very", 95 | { 96 | "complicated": "indeed" 97 | } 98 | ] 99 | } 100 | ] 101 | } 102 | """ 103 | かつ 最後のJSONレスポンスを表示する 104 | # かつ ブレークポイントを設置する 105 | 106 | シナリオ: Check json root node 107 | 前提 "/json/rootarray.json" を表示している 108 | ならば レスポンスがJSON形式であること 109 | かつ JSONにノード"root[0].name"が存在すること 110 | かつ JSONのノード"root"が2個の要素を持つこと 111 | -------------------------------------------------------------------------------- /tests/features/ja/rest.feature: -------------------------------------------------------------------------------- 1 | #language: ja 2 | @japanese @rest 3 | フィーチャ: Testing RESTContext 4 | 5 | シナリオ: Testing headers 6 | もし 私がGETメソッドで"rest/index.php"へリクエストを送る 7 | かつ "Content-Type"ヘッダが"text"を含むこと 8 | かつ "Content-Type"ヘッダが"text/html; charset=UTF-8"と一致すること 9 | かつ "Content-Type"ヘッダが"text/json"を含まないこと 10 | かつ "xxx"ヘッダが存在しないこと 11 | かつ レスポンスが将来期限切れになること 12 | かつ レスポンスが"UTF-8"でエンコードされていること 13 | # ならば ブレークポイントを設置する 14 | 15 | シナリオ: Testing request methods. 16 | 前提 私がGETメソッドで"/rest/index.php"へリクエストを送る 17 | ならば 画面に "You have sent a GET request. " と表示されていること 18 | かつ 画面に "No parameter received" と表示されていること 19 | 20 | もし 私がGETメソッドで"/rest/index.php?first=foo&second=bar"へリクエストを送る 21 | ならば 画面に "You have sent a GET request. " と表示されていること 22 | かつ 画面に "2 parameter(s)" と表示されていること 23 | かつ 画面に "first : foo" と表示されていること 24 | かつ 画面に "second : bar" と表示されていること 25 | 26 | # ならば ブレークポイントを設置する 27 | もし POSTメソッドで"/rest/index.php"へ下記のパラメーターを伴ったリクエストを送る: 28 | | key | value | 29 | | foo | bar | 30 | | foofile | @lorem.txt | 31 | 32 | ならば 最後のレスポンスを表示 33 | ならば 画面に "You have sent a POST request. " と表示されていること 34 | かつ 画面に "1 parameter(s)" と表示されていること 35 | かつ 画面に "1 file(s)" と表示されていること 36 | かつ 画面に "foo : bar" と表示されていること 37 | かつ 画面に "foofile - name : lorem.txt" と表示されていること 38 | かつ 画面に "foofile - error : 0" と表示されていること 39 | かつ 画面に "foofile - size : 39" と表示されていること 40 | 41 | もし 私がPUTメソッドで"rest/index.php"へリクエストを送る 42 | ならば 画面に "You have sent a PUT request. " と表示されていること 43 | 44 | もし 私がDELETEメソッドで"rest/index.php"へリクエストを送る 45 | ならば 画面に "You have sent a DELETE request. " と表示されていること 46 | 47 | もし POSTメソッドで"/rest/index.php"へ下記のボディを持ったリクエストを送る: 48 | """ 49 | This is a body. 50 | """ 51 | ならば 3秒間待つ 52 | ならば 最後のレスポンスを表示 53 | ならば 画面に "Body : This is a body." と表示されていること 54 | 55 | もし PUTメソッドで"/rest/index.php"へ下記のボディを持ったリクエストを送る: 56 | """ 57 | {"this is":"some json"} 58 | """ 59 | ならば レスポンスが空であること 60 | # ならば ブレークポイントを設置する 61 | 62 | シナリオ: Add header 63 | 前提 "xxx"ヘッダに"yyy"を追加する 64 | もし 私がGETメソッドで"/rest/index.php"へリクエストを送る 65 | ならば 画面に "HTTP_XXX : yyy" と表示されていること 66 | # ならば ブレークポイントを設置する 67 | 68 | シナリオ: Case-insensitive header name 69 | Like describe in the rfc2614 §4.2 70 | https://tools.ietf.org/html/rfc2616#section-4.2 71 | 72 | もし 私がGETメソッドで"/rest/index.php"へリクエストを送る 73 | かつ "Content-Type"ヘッダが"text"を含むこと 74 | # ならば ブレークポイントを設置する 75 | 76 | シナリオ: Debug 77 | 前提 "xxx"ヘッダに"yyy"を追加する 78 | もし POSTメソッドで"/rest/index.php"へ下記のパラメーターを伴ったリクエストを送る: 79 | | key | value | 80 | | foo | bar | 81 | ならば 最後のレスポンスヘッダを表示する 82 | かつ curlコマンドを表示する 83 | -------------------------------------------------------------------------------- /tests/features/ja/system.feature: -------------------------------------------------------------------------------- 1 | #language: ja 2 | @japanese @system 3 | フィーチャ: System feature 4 | 5 | シナリオ: Testing execution 6 | 前提 "ls"を実行する 7 | 8 | シナリオ: Testing execution from the project root 9 | 前提 "bin/behat --help"を実行する 10 | 11 | シナリオ: File creation 12 | もし "tests/fixtures/test"というファイルを下記のテキストで作成する: 13 | """ 14 | A new file 15 | """ 16 | ならば "tests/fixtures/test"というファイルのテキストを表示する 17 | -------------------------------------------------------------------------------- /tests/features/ja/table.feature: -------------------------------------------------------------------------------- 1 | #language: ja 2 | @japanese @table 3 | フィーチャ: Table Feature 4 | 5 | シナリオ: Testing access to /table/index.html 6 | 前提 "/table/index.html" を表示している 7 | ならば 画面に "You are about to test table." と表示されていること 8 | 9 | シナリオ: Testing columns 10 | 前提 "/table/index.html" を表示している 11 | 12 | ならば テーブル"table"が3個のカラムを持つこと 13 | 14 | かつ テーブル"table"のカラムスキーマが下記と一致すること: 15 | | columns | 16 | | Lorem | 17 | | Ipsum | 18 | | Integer | 19 | # ならば ブレークポイントを設置する 20 | 21 | シナリオ: Testing rows 22 | 前提 "/table/index.html" を表示している 23 | 24 | ならば テーブル"table"が2行持つこと 25 | かつ 1番目のテーブル"table"が2行持つこと 26 | 27 | かつ テーブル"table"の1行目のデータが下記と一致すること: 28 | | col1 | col2 | 29 | | Lorem | Ipsum | 30 | 31 | かつ テーブル"table"の2行目のデータが下記と一致すること: 32 | | col1 | col2 | 33 | | Dolor | Sit | 34 | # ならば ブレークポイントを設置する 35 | 36 | シナリオ: Partial Testing rows 37 | 前提 "/table/index.html" を表示している 38 | 39 | ならば テーブル"table"が2行持つこと 40 | かつ 1番目のテーブル"table"が2行持つこと 41 | 42 | かつ テーブル"table"の1行目のデータが下記と一致すること: 43 | | col2 | 44 | | Ipsum | 45 | 46 | かつ テーブル"table"の2行目のデータが下記と一致すること: 47 | | col1 | 48 | | Dolor | 49 | # ならば ブレークポイントを設置する 50 | 51 | シナリオ: Testing cell content 52 | 前提 "/table/index.html" を表示している 53 | ならば テーブル"table"の1行目1列が"Lorem"を含むこと 54 | 55 | かつ テーブル"table"の1行目2列が"Ipsum"を含むこと 56 | -------------------------------------------------------------------------------- /tests/features/ja/xml.feature: -------------------------------------------------------------------------------- 1 | #language: ja 2 | @japanese @xml 3 | フィーチャ: Testing XmlContext 4 | 5 | 背景: 6 | 前提 "/xml/feed.xml" を表示している 7 | 8 | シナリオ: Am I a XML ? 9 | ならば レスポンスがXML形式であること 10 | もし "/xml/feed.atom" を表示している 11 | ならば レスポンスがXML形式であること 12 | もし "/xml/feed.rss" を表示している 13 | ならば レスポンスがXML形式であること 14 | もし "/xml/book.xml" を表示している 15 | ならば レスポンスがXML形式であること 16 | もし "/xml/people.xml" を表示している 17 | ならば レスポンスがXML形式であること 18 | もし "/xml/country.xml" を表示している 19 | ならば レスポンスがXML形式であること 20 | もし "/xml/needsformatting.xml" を表示している 21 | ならば レスポンスがXMLであること 22 | もし "/xml/imnotaxml.xml" を表示している 23 | ならば レスポンスがXML形式でないこと 24 | もし "/xml/notfound.xml" を表示している 25 | ならば レスポンスがXMLでないこと 26 | # ならば ブレークポイントを設置する 27 | 28 | シナリオ: Validation with DTD 29 | ならば XMLフィードが自身のDTDに従っていること 30 | 31 | シナリオ: Validation with XSD file 32 | ならば XMLフィードがXSDファイル"tests/fixtures/www/xml/schema.xsd"に従っていること 33 | 34 | # ならば ブレークポイントを設置する 35 | シナリオ: Validation with inline XSD 36 | ならば XMLフィードが下記のXSDに従っていること: 37 | """ 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | """ 51 | 52 | シナリオ: Validation with relax NG file 53 | ならば XMLフィードがrelax NG schemaファイル"tests/fixtures/www/xml/schema.ng"に従っていること 54 | 55 | # ならば ブレークポイントを設置する 56 | シナリオ: Validation with inline relax NG 57 | ならば XMLフィードが下記のrelax NG schemaに従っていること: 58 | """ 59 | 60 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | """ 77 | 78 | シナリオ: Atom feed validation 79 | 前提 "/xml/feed.atom" を表示している 80 | ならば atomフィードが妥当であること 81 | 82 | シナリオ: RSS feed validation 83 | 前提 "/xml/feed.rss" を表示している 84 | ならば RSS2フィードが妥当であること 85 | 86 | # ならば ブレークポイントを設置する 87 | シナリオ: Check XML evaluation 88 | 前提 "/xml/book.xml" を表示している 89 | ならば XMLには "//book/chapter/title" 要素が存在していること 90 | かつ XMLには "//book/chapter/index" 要素が存在していないこと 91 | かつ XMLの "//book/chapter/title" 要素は "My books" と一致していること 92 | かつ XMLの "//book/title" 要素は "My wonderful lists" と一致していないこと 93 | かつ XMLの "//book/chapter/para/informaltable/tgroup" 要素には "cols" 属性が存在していること 94 | かつ XMLの "//book/chapter/title" 要素には "color" 属性が存在していないこと 95 | かつ XMLの "//book/chapter" 要素の "id" 属性は "books" と一致していること 96 | かつ XMLの "//book" 要素の "id" 属性は "choices" と一致していないこと 97 | かつ XMLには "//book/chapter/para/informaltable/tgroup/tbody" 要素を 3 個含んでいること 98 | かつ XMLの "//book/title" 要素は "is" を含んでいること 99 | かつ XMLの "//book/chapter/title" 要素は "if" を含んでいないこと 100 | # ならば ブレークポイントを設置する 101 | 102 | シナリオ: Check XML evaluation with namespaces and a default namespace 103 | 前提 "/xml/country.xml" を表示している 104 | ならば XMLは名前空間 "http://example.org/xsd/country" を使っていること 105 | かつ XMLには "//country/airports" 要素が存在していること 106 | かつ XMLには "//country/cities/city:city/city:park" 要素が存在していること 107 | かつ XMLには "//country/treasure" 要素が存在していないこと 108 | かつ XMLの "//city:city[@id=1]/city:park" 要素の "opened" 属性は "1873" と一致していること 109 | かつ XMLの "//city:city[@id=2]/city:park" 要素の "attraction" 属性は "Fireworks" と一致していないこと 110 | かつ XMLの "//country" 要素には "version" 属性が存在していること 111 | かつ XMLの "//country/airports/city:airport" 要素には "typo" 属性が存在していないこと 112 | かつ XMLには "//country/cities" 要素を 2 個含んでいること 113 | かつ XMLには "//country/cities/city:city[@id=2]" 要素を 1 個含んでいること 114 | 115 | シナリオ: Check XML evaluation with namespaces but no default namespace 116 | 前提 "/xml/people.xml" を表示している 117 | ならば XMLは名前空間 "http://example.org/ns" を使っていること 118 | かつ XMLは名前空間 "http://example.org/test" を使っていないこと 119 | かつ XMLには "//people" 要素が存在していること 120 | かつ XMLには "//people/p:person" 要素が存在していること 121 | かつ XMLには "//people/description" 要素が存在していないこと 122 | かつ XMLの "//people/p:person[@id=1]/items/item[@id=1]" 要素は "Rubber Ducky" と一致していること 123 | かつ XMLには "//people" 要素を 3 個含んでいること 124 | かつ XMLの "//people/p:person[@id=1]" 要素の "name" 属性は "Bert" と一致していること 125 | かつ XMLの "//people/p:person[@id=2]" 要素の "id" 属性は "4" と一致していないこと 126 | かつ XMLの "//people/p:person[@id=3]" 要素には "name" 属性が存在していること 127 | かつ XMLの "//people/p:person[@id=1]/items/item" 要素には "size" 属性が存在していないこと 128 | 129 | シナリオ: Pretty print xml 130 | 前提 "/xml/needsformatting.xml" を表示している 131 | かつ 最後のXMLレスポンスを表示する 132 | -------------------------------------------------------------------------------- /tests/features/json.feature: -------------------------------------------------------------------------------- 1 | @json 2 | Feature: Testing JSONContext 3 | 4 | Scenario: Am I a JSON ? 5 | Given I am on "/json/imajson.json" 6 | Then the response should be in JSON 7 | When I am on "/json/emptyarray.json" 8 | Then the response should be in JSON 9 | When I am on "/json/emptyobject.json" 10 | Then the response should be in JSON 11 | When I am on "/json/imnotajson.json" 12 | Then the response should not be in JSON 13 | 14 | Scenario: Count JSON elements 15 | Given I am on "/json/imajson.json" 16 | Then the JSON node "numbers" should have 4 elements 17 | 18 | Scenario: Checking JSON evaluation 19 | Given I am on "/json/imajson.json" 20 | 21 | Then the JSON node "foo" should exist 22 | And the JSON node "root.foo" should exist 23 | And the JSON node "foo" should contain "bar" 24 | And the JSON node "foo" should not contain "something else" 25 | 26 | And the JSON node "numbers[0]" should contain "öne" 27 | And the JSON node "numbers[1]" should contain "two" 28 | And the JSON node "numbers[2]" should contain "three" 29 | And the JSON node "numbers[3].complexeshizzle" should be equal to "true" 30 | And the JSON node "numbers[3].so[0]" should be equal to "very" 31 | And the JSON node "numbers[3].so[1].complicated" should be equal to "indeed" 32 | And the JSON node "numbers[0]" should match "/ö.{1}e/" 33 | And the JSON node "numbers[1]" should match "/.{2}o/" 34 | And the JSON node "numbers[2]" should match "/[a-z]{3}e.+/" 35 | 36 | And the JSON nodes should be equal to: 37 | | foo | bar | 38 | | numbers[0] | öne | 39 | | numbers[1] | two | 40 | | numbers[2] | three | 41 | 42 | And the JSON nodes should contain: 43 | | foo | bar | 44 | | numbers[0] | öne | 45 | | numbers[1] | two | 46 | | numbers[2] | three | 47 | 48 | And the JSON nodes should not contain: 49 | | foo | something else | 50 | 51 | And the JSON node "bar" should not exist 52 | 53 | Scenario: Json validation with schema 54 | Given I am on "/json/imajson.json" 55 | Then the JSON should be valid according to the schema "tests/fixtures/www/json/schema.json" 56 | 57 | Scenario: Json validation with schema containing ref (invalid case) 58 | Given I am on "/json/withref-invalid.json" 59 | Then the JSON should be invalid according to the schema "tests/fixtures/www/json/schemaref.json" 60 | 61 | Scenario: Json validation with schema containing ref 62 | Given I am on "/json/withref.json" 63 | Then the JSON should be valid according to the schema "tests/fixtures/www/json/schemaref.json" 64 | 65 | Scenario: Json validation 66 | Given I am on "/json/imajson.json" 67 | Then the JSON should be valid according to this schema: 68 | """ 69 | { 70 | "type": "object", 71 | "$schema": "http://json-schema.org/draft-03/schema", 72 | "required":true, 73 | "properties": { 74 | "foo": { 75 | "type": "string", 76 | "required":true 77 | }, 78 | "numbers": { 79 | "type": "array", 80 | "required":true, 81 | "one": { 82 | "type": "string", 83 | "required":true 84 | }, 85 | "two": { 86 | "type": "string", 87 | "required":true 88 | }, 89 | "three": { 90 | "type": "string", 91 | "required":true 92 | } 93 | } 94 | } 95 | } 96 | """ 97 | 98 | Scenario: Json validation deep 99 | Given I am on "/json/booking.json" 100 | Then the JSON should be invalid according to this schema: 101 | """ 102 | { 103 | "type":"object", 104 | "$schema": "http://json-schema.org/draft-03/schema", 105 | "required":false, 106 | "properties":{ 107 | "Booking": { 108 | "type":"object", 109 | "required":false 110 | }, 111 | "Metadata": { 112 | "type":"object", 113 | "required":false, 114 | "properties":{ 115 | "First": { 116 | "type":"object", 117 | "required":false, 118 | "properties":{ 119 | "default_value": { 120 | "type":"boolean", 121 | "required":false 122 | }, 123 | "enabled": { 124 | "type":"boolean", 125 | "required":true 126 | } 127 | } 128 | } 129 | } 130 | } 131 | } 132 | } 133 | """ 134 | 135 | Scenario: Json contents validation 136 | Given I am on "/json/imajson.json" 137 | Then the JSON should be equal to: 138 | """ 139 | { 140 | "foo": "bar", 141 | "numbers": [ 142 | "öne", 143 | "two", 144 | "three", 145 | { 146 | "complexeshizzle": true, 147 | "so": [ 148 | "very", 149 | { 150 | "complicated": "indeed" 151 | } 152 | ] 153 | } 154 | ] 155 | } 156 | """ 157 | And print last JSON response 158 | 159 | Scenario: Check json root node 160 | Given I am on "/json/rootarray.json" 161 | Then the response should be in JSON 162 | And the JSON node "root[0].name" should exist 163 | And the JSON node "root" should have 2 elements 164 | 165 | Scenario: Check with type comparison 166 | Given I am on "/json/arraywithtypes.json" 167 | Then the response should be in JSON 168 | And the JSON node "root[0]" should be null 169 | And the JSON node "root[1]" should be true 170 | And the JSON node "root[2]" should be false 171 | And the JSON node "root[3]" should be equal to the string "dunglas.fr" 172 | And the JSON node "root[4]" should be equal to the number 1312 173 | And the JSON node "root[4]" should be equal to the number 1312.0 174 | And the JSON node "root[5]" should be equal to the number 1936.2 175 | 176 | Scenario: Check not null values 177 | Given I am on "/json/notnullvalues.json" 178 | Then the response should be in JSON 179 | And the JSON node '' should have 5 elements 180 | And the JSON node "one" should not be null 181 | And the JSON node "one" should be false 182 | And the JSON node "two" should not be null 183 | And the JSON node "two" should be true 184 | And the JSON node "three" should not be null 185 | And the JSON node "three" should be equal to the string "" 186 | And the JSON node "four" should not be null 187 | And the JSON node "four" should be equal to the string "foo" 188 | And the JSON node "five" should not be null 189 | And the JSON node "five" should be equal to the number 5 190 | 191 | Scenario: Json validation against swagger dump file 192 | Given I am on "/json/swaggerpartial.json" 193 | Then the response should be in JSON 194 | And the JSON should be valid according to swagger "tests/fixtures/www/json/swagger.json" dump schema "sample-definition" 195 | 196 | Scenario: Json validation against swagger dump file 197 | Given I am on "/json/swaggerpartial.json" 198 | Then the response should be in JSON 199 | And the JSON should not be valid according to swagger "tests/fixtures/www/json/swagger.json" dump schema "sample-invalid-definition" 200 | -------------------------------------------------------------------------------- /tests/features/pt/browser.feature: -------------------------------------------------------------------------------- 1 | #language: pt 2 | Funcionalidade: Browser 3 | 4 | # Se este cenário falhar 5 | # Seu ambiente não deve estar configurado corretamente 6 | # Você pode encontrar a ajuda necessária no README.md 7 | @javascript 8 | Cenário: Testando um acesso web simples 9 | Quando estou em "/index.html" 10 | Então devo ver "Congratulations, you've correctly set up your apache environment." 11 | 12 | @statusCode 13 | Cenário: Basic Authentication 14 | Quando Eu estou em "/browser/auth.php" 15 | Então o código de status da resposta deve ser 401 16 | E devo ver "NONE SHALL PASS" 17 | 18 | Quando eu preencho a autenticação com "something" e "wrong" 19 | E vou para "/browser/auth.php" 20 | Então o código de status da resposta deve ser 401 21 | E devo ver "NONE SHALL PASS" 22 | 23 | Quando eu preencho a autenticação com "gabriel" e "30091984" 24 | E vou para "/browser/auth.php" 25 | Então o código de status da resposta deve ser 200 26 | E devo ver "Successfuly logged in" 27 | 28 | Quando Eu vou para "/browser/auth.php?logout" 29 | Então devo ver "Logged out" 30 | 31 | Quando Eu vou para "/browser/auth.php" 32 | Então o código de status da resposta deve ser 401 33 | E devo ver "NONE SHALL PASS" 34 | 35 | @javascript 36 | Cenário: Testando elementos 37 | Quando Eu estou em uma url composta por: 38 | | parameters | 39 | | /browser | 40 | | /elements.html | 41 | Então devo ver 4 "div" no 1º "body" 42 | E devo ver menos que 6 "div" no 1º "body" 43 | E devo ver mais que 2 "div" no 1º "body" 44 | E o select "months_selector" não deve conter "december" 45 | E o select "months_selector" deve conter "january" 46 | Quando Eu clico no 1º elemento "ul li" 47 | Então Eu devo ver "You clicked First" 48 | 49 | @javascript 50 | Cenário: Testando frames 51 | Quando Eu estou em "/browser/frames.html" 52 | E mudo para o iframe "index" 53 | Então devo ver "Visible" 54 | 55 | Quando eu mudo para o frame principal 56 | 57 | Quando mudo para o iframe "elements" 58 | Então o select "months_selector" deve conter "january" 59 | 60 | @javascript 61 | Cenário: Esperar antes de ver 62 | Quando Eu estou em "/browser/timeout.html" 63 | Então espero 3 segundos até ver "timeout" 64 | E espero 1 segundo 65 | E espero pelo elemento "#iframe" 66 | E espero 5 segundos pelo elemento "#iframe" 67 | 68 | @javascript 69 | Cenário: Verificar visibilidade do elemento 70 | Quando Eu estou em "/browser/index.html" 71 | Então o elemento "#visible-element" deve estar visível 72 | E o elemento "#hidden-element" não deve estar visível 73 | 74 | @javascript 75 | Cenário: 76 | Quando Eu estou em "/browser/elements.html" 77 | Então Eu preencho "today" com a data atual 78 | E Eu preencho "today" com a data atual e o modificador "-1 day" 79 | 80 | 81 | Cenário: 82 | Quando Eu estou em "/browser/elements.html" 83 | Então Eu salvo o valor de "today" no parâmetro "today" 84 | -------------------------------------------------------------------------------- /tests/features/pt/debug.feature: -------------------------------------------------------------------------------- 1 | #language: pt 2 | Funcionalidade: Debug 3 | 4 | @user 5 | Cenário: Testando um breakpoint 6 | Quando Eu estou em "index.html" 7 | E coloco um breakpoint 8 | Então devo ver "Congratulations, you've correctly set up your apache environment." 9 | E coloco um breakpoint 10 | 11 | @javascript 12 | Cenário: Capturando uma screenshot 13 | Quando Eu estou em "index.html" 14 | E salvo uma screenshot em "index.png" 15 | -------------------------------------------------------------------------------- /tests/features/pt/json.feature: -------------------------------------------------------------------------------- 1 | #language: pt 2 | @json 3 | Funcionalidade: Testando o JSONContext 4 | 5 | Cenário: Eu sou um JSON? 6 | Quando Eu estou em "/json/imajson.json" 7 | Então a resposta deve estar em JSON 8 | Quando Eu estou em "/json/emptyarray.json" 9 | Então a resposta deve estar em JSON 10 | Quando Eu estou em "/json/emptyobject.json" 11 | Então a resposta deve estar em JSON 12 | Quando Eu estou em "/json/imnotajson.json" 13 | Então a resposta não deve estar em JSON 14 | 15 | Cenário: Contar os elementos JSON 16 | Quando Eu estou em "/json/imajson.json" 17 | Então o nó JSON "numbers" deve ter 4 elementos 18 | 19 | Cenário: Verificar a interpretação do JSON 20 | Quando Eu estou em "/json/imajson.json" 21 | 22 | Então o nó JSON "foo" deve existir 23 | E o nó JSON "root.foo" deve existir 24 | E o nó JSON "foo" deve conter "bar" 25 | E o nó JSON "foo" não deve conter "something else" 26 | 27 | E o nó JSON "numbers[0]" deve conter "öne" 28 | E o nó JSON "numbers[1]" deve conter "two" 29 | E o nó JSON "numbers[2]" deve conter "three" 30 | E o nó JSON "numbers[3].complexeshizzle" deve ser igual a "true" 31 | E o nó JSON "numbers[3].so[0]" deve ser igual a "very" 32 | E o nó JSON "numbers[3].so[1].complicated" deve ser igual a "indeed" 33 | 34 | E os nós JSON devem ser iguais a: 35 | | foo | bar | 36 | | numbers[0] | öne | 37 | | numbers[1] | two | 38 | | numbers[2] | three | 39 | 40 | E os nós JSON devem conter: 41 | | foo | bar | 42 | | numbers[0] | öne | 43 | | numbers[1] | two | 44 | | numbers[2] | three | 45 | 46 | E os nós JSON não devem conter: 47 | | foo | something else | 48 | 49 | E o nó JSON "bar" não deve existir 50 | 51 | Cenário: Validação do JSON com schema 52 | Quando Eu estou em "/json/imajson.json" 53 | Então o JSON deve ser válido de acordo com o schema "tests/fixtures/www/json/schema.json" 54 | 55 | Cenário: Validação do JSON com schema contendo ref (caso inválido) 56 | Quando Eu estou em "/json/withref-invalid.json" 57 | Então o JSON deve ser inválido de acordo com o schema "tests/fixtures/www/json/schemaref.json" 58 | 59 | Cenário: Validação do JSON com schema contendo ref 60 | Quando Eu estou em "/json/withref.json" 61 | Então o JSON deve ser válido de acordo com o schema "tests/fixtures/www/json/schemaref.json" 62 | 63 | Cenário: Validação do JSON 64 | Quando Eu estou em "/json/imajson.json" 65 | Então o JSON deve ser válido de acordo com esse schema: 66 | """ 67 | { 68 | "type": "object", 69 | "$schema": "http://json-schema.org/draft-03/schema", 70 | "required":true, 71 | "properties": { 72 | "foo": { 73 | "type": "string", 74 | "required":true 75 | }, 76 | "numbers": { 77 | "type": "array", 78 | "required":true, 79 | "öne": { 80 | "type": "string", 81 | "required":true 82 | }, 83 | "two": { 84 | "type": "string", 85 | "required":true 86 | }, 87 | "three": { 88 | "type": "string", 89 | "required":true 90 | } 91 | } 92 | } 93 | } 94 | """ 95 | 96 | Cenário: Validação do conteúdo do JSON 97 | Quando Eu estou em "/json/imajson.json" 98 | Então o JSON deve ser igual a: 99 | """ 100 | { 101 | "foo": "bar", 102 | "numbers": [ 103 | "öne", 104 | "two", 105 | "three", 106 | { 107 | "complexeshizzle": true, 108 | "so": [ 109 | "very", 110 | { 111 | "complicated": "indeed" 112 | } 113 | ] 114 | } 115 | ] 116 | } 117 | """ 118 | E exiba a última resposta JSON 119 | 120 | Cenário: Verificar o nó raiz do JSON 121 | Quando Eu estou em "/json/rootarray.json" 122 | Então a resposta deve estar em JSON 123 | E o nó JSON "root[0].name" deve existir 124 | E o nó JSON "root" deve ter 2 elementos 125 | 126 | Cenário: Verificação com comparação de tipos 127 | Quando Eu estou em "/json/arraywithtypes.json" 128 | Então a resposta deve estar em JSON 129 | E o nó JSON "root[0]" deve ser null 130 | E o nó JSON "root[1]" deve ser true 131 | E o nó JSON "root[2]" deve ser false 132 | E o nó JSON "root[3]" deve ser igual a string "dunglas.fr" 133 | E o nó JSON "root[4]" deve ser igual ao número 1312 134 | E o nó JSON "root[4]" deve ser igual ao número 1312.0 135 | E o nó JSON "root[5]" deve ser igual ao número 1936.2 136 | -------------------------------------------------------------------------------- /tests/features/pt/rest.feature: -------------------------------------------------------------------------------- 1 | #language: pt 2 | @rest 3 | Funcionalidade: Testando o RESTContext 4 | 5 | Cenário: Testando headers 6 | Quando envio uma requisição GET para "rest/index.php" 7 | Então o header "Content-Type" deve conter "text" 8 | E o header "Content-Type" deve ser igual a "text/html; charset=UTF-8" 9 | E o header "Content-Type" não deve conter "text/json" 10 | E o header "xxx" não deve existir 11 | E a resposta deve expirar no futuro 12 | E a resposta deve estar codificada em "UTF-8" 13 | 14 | Cenário: Testando métodos de requisição 15 | Quando envio uma requisição GET para "/rest/index.php" 16 | Então devo ver "You have sent a GET request. " 17 | E devo ver "No parameter received" 18 | 19 | Quando envio uma requisição GET para "/rest/index.php?first=foo&second=bar" 20 | Então devo ver "You have sent a GET request. " 21 | E devo ver "2 parameter(s)" 22 | E devo ver "first : foo" 23 | E devo ver "second : bar" 24 | 25 | Quando envio uma requisição POST para "/rest/index.php" com os parâmetros: 26 | | key | value | 27 | | foo | bar | 28 | | foofile | @lorem.txt | 29 | Então devo ver "You have sent a POST request. " 30 | E devo ver "1 parameter(s)" 31 | E devo ver "1 file(s)" 32 | E devo ver "foo : bar" 33 | E devo ver "foofile - name : lorem.txt" 34 | E devo ver "foofile - error : 0" 35 | E devo ver "foofile - size : 39" 36 | 37 | Quando envio uma requisição PUT para "/rest/index.php" 38 | Então devo ver "You have sent a PUT request. " 39 | 40 | Quando envio uma requisição DELETE para "/rest/index.php" 41 | Então devo ver "You have sent a DELETE request. " 42 | 43 | Quando envio uma requisição POST para "/rest/index.php" com o corpo: 44 | """ 45 | This is a body. 46 | """ 47 | Então devo ver "Body : This is a body." 48 | 49 | Quando envio uma requisição PUT para "/rest/index.php" com o corpo: 50 | """ 51 | {"this is":"some json"} 52 | """ 53 | Então a resposta deve estar vazia 54 | 55 | Cenário: Adicionar um header 56 | Quando adiciono o header "xxx" com o valor "yyy" 57 | E envio uma requisição GET para "/rest/index.php" 58 | Então devo ver "HTTP_XXX : yyy" 59 | 60 | Cenário: Nome do header case-insensitive 61 | Como descrito na rfc2614 §4.2 62 | https://tools.ietf.org/html/rfc2616#section-4.2 63 | 64 | Quando envio uma requisição GET para "rest/index.php" 65 | Então o header "content-type" deve conter "text" 66 | 67 | Cenário: Debug 68 | Quando adiciono o header "xxx" com o valor "yyy" 69 | E envio uma requisição POST para "/rest/index.php" com os parâmetros: 70 | | key | value | 71 | | foo | bar | 72 | Então exiba os headers da última resposta 73 | E exiba o comando curl correspondente 74 | -------------------------------------------------------------------------------- /tests/features/pt/system.feature: -------------------------------------------------------------------------------- 1 | #language: pt 2 | Funcionalidade: System 3 | 4 | Cenário: Testando a execução 5 | Quando Eu executo "true" 6 | Então o comando deve ser executado com sucesso 7 | Quando Eu executo "false" 8 | Então o comando deve falhar 9 | 10 | Cenário: Testando o tempo de execução 11 | Quando Eu executo "sleep 1" 12 | Então o comando deve demorar menos que 2 segundos 13 | 14 | Quando Eu executo "sleep 2" 15 | Então o comando deve demorar mais que 1 segundos 16 | 17 | Cenário: Testando a saída da execução 18 | Quando Eu executo "echo 'Hello world'" 19 | Então a saída deve conter "Hello world" 20 | E a saída deve conter "Hel.*ld" 21 | E a saída não deve conter "Hello John" 22 | E a saída não deve conter "Hel.*hn" 23 | 24 | Cenário: Testando a saída da execução com múltiplas linhas 25 | Quando Eu executo "echo 'Hello world\nHow are you?'" 26 | Então a saída deve ser: 27 | """ 28 | Hello world 29 | How are you? 30 | """ 31 | E a saída não deve ser: 32 | """ 33 | Hello John 34 | How are you? 35 | """ 36 | 37 | Cenário: Testando a execução de comandos a partir da raiz do projeto 38 | Quando executo "bin/behat --help" 39 | 40 | Cenário: Criação de arquivo 41 | Quando crio o arquivo "tests/fixtures/test" contendo: 42 | """ 43 | A new file 44 | """ 45 | Então exiba o conteúdo do arquivo "tests/fixtures/test" 46 | -------------------------------------------------------------------------------- /tests/features/pt/table.feature: -------------------------------------------------------------------------------- 1 | #language: pt 2 | Funcionalidade: Table 3 | 4 | Cenário: Testando o acesso a /table/index.html 5 | Quando estou em "/table/index.html" 6 | Então devo ver "You are about to test table." 7 | 8 | Cenário: Testando colunas 9 | Quando estou em "/table/index.html" 10 | 11 | Então devo ver 3 colunas na tabela "table" 12 | 13 | E as colunas da tabela "table" devem ser: 14 | | columns | 15 | | Lorem | 16 | | Ipsum | 17 | | Integer | 18 | 19 | Cenário: Testando linhas 20 | Quando estou em "/table/index.html" 21 | 22 | Então devo ver 2 linhas na tabela "table" 23 | E devo ver 2 linhas na 1ª tabela "table" 24 | 25 | E os dados na 1ª linha da tabela "table" devem ser iguais a: 26 | | col1 | col2 | 27 | | Lorem | Ipsum | 28 | 29 | E os dados na 2ª linha da tabela "table" devem ser iguais a: 30 | | col1 | col2 | 31 | | Dolor | Sit | 32 | 33 | Cenário: Teste parcial de linhas 34 | Quando estou em "/table/index.html" 35 | 36 | Então devo ver 2 linhas na tabela "table" 37 | E devo ver 2 linhas na 1ª tabela "table" 38 | 39 | E os dados na 1ª linha da tabela "table" devem ser iguais a: 40 | | col2 | 41 | | Ipsum | 42 | 43 | E os dados na 2ª linha da tabela "table" devem ser iguais a: 44 | | col1 | 45 | | Dolor | 46 | 47 | Cenário: Testando o conteúdo das células 48 | Quando estou em "/table/index.html" 49 | Então a 1ª coluna da 1ª linha da tabela "table" deve conter "Lorem" 50 | E a 2ª coluna da 1ª linha da tabela "table" deve conter "Ipsum" 51 | -------------------------------------------------------------------------------- /tests/features/pt/xml.feature: -------------------------------------------------------------------------------- 1 | #language: pt 2 | @xml 3 | Funcionalidade: Testando o XmlContext 4 | 5 | Contexto: 6 | Quando Eu estou em "/xml/feed.xml" 7 | 8 | Cenário: Eu sou um XML? 9 | Então a resposta deve estar em XML 10 | Quando Eu estou em "/xml/feed.atom" 11 | Então a resposta deve estar em XML 12 | Quando Eu estou em "/xml/feed.rss" 13 | Então a resposta deve estar em XML 14 | Quando Eu estou em "/xml/book.xml" 15 | Então a resposta deve estar em XML 16 | Quando Eu estou em "/xml/people.xml" 17 | Então a resposta deve estar em XML 18 | Quando Eu estou em "/xml/country.xml" 19 | Então a resposta deve estar em XML 20 | Quando Eu estou em "/xml/needsformatting.xml" 21 | Então a resposta deve estar em XML 22 | Quando Eu estou em "/xml/imnotaxml.xml" 23 | Então a resposta não deve estar em XML 24 | Quando Eu estou em "/xml/notfound.xml" 25 | Então a resposta não deve estar em XML 26 | 27 | Cenário: Validação com DTD 28 | Então o XML deve ser válido de acordo com o seu DTD 29 | 30 | Cenário: Validação com um arquivo XSD 31 | Então o XML deve ser válido de acordo com o XSD "tests/fixtures/www/xml/schema.xsd" 32 | 33 | Cenário: Validação com um XSD inline 34 | Então o XML deve ser válido de acordo com esse XSD: 35 | """ 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | """ 49 | 50 | Cenário: Validação com um arquivo relax NG 51 | Então o XML deve ser válido de acordo com o schema relax NG "tests/fixtures/www/xml/schema.ng" 52 | 53 | Cenário: Validação com relax NG inline 54 | Então o XML deve ser válido de acordo com esse schema relax NG: 55 | """ 56 | 57 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | """ 74 | 75 | Cenário: Validação de feed Atom 76 | Quando Eu estou em "/xml/feed.atom" 77 | Então o feed atom deve ser válido 78 | 79 | Cenário: Validação de feed RSS 80 | Quando Eu estou em "/xml/feed.rss" 81 | Então o feed RSS2 deve ser válido 82 | 83 | Cenário: Verifica a interpretação do XML 84 | Quando Eu estou em "/xml/book.xml" 85 | Então o elemento XML "//book/chapter/title" deve existir 86 | E o elemento XML "//book/chapter/index" não deve existir 87 | E o elemento XML "//book/chapter/title" deve ser igual a "My books" 88 | E o elemento XML "//book/title" não deve ser igual a "My wonderful lists" 89 | E o elemento "//book/chapter/para/informaltable/tgroup" deve possuir o atributo XML "cols" 90 | E o elemento "//book/chapter/title" não deve possuir o atributo XML "color" 91 | E o atributo XML "id" no elemento "//book/chapter" deve ser igual a "books" 92 | E o atributo XML "id" no elemento "//book" não deve ser igual a "choices" 93 | E o elemento XML "//book/chapter/para/informaltable/tgroup/tbody" deve ter 3 elementos 94 | E o elemento XML "//book/title" deve conter "is" 95 | E o elemento XML "//book/chapter/title" não deve conter "if" 96 | 97 | Cenário: Verifica a interpretação do XML com namespaces e namespace default 98 | Quando Eu estou em "/xml/country.xml" 99 | Então o XML deve utilizar o namespace "http://example.org/xsd/country" 100 | E o elemento XML "//country/airports" deve existir 101 | E o elemento XML "//country/cities/city:city/city:park" deve existir 102 | E o elemento XML "//country/treasure" não deve existir 103 | E o atributo XML "opened" no elemento "//city:city[@id=1]/city:park" deve ser igual a "1873" 104 | E o atributo XML "attraction" no elemento "//city:city[@id=2]/city:park" não deve ser igual a "Fireworks" 105 | E o elemento "//country" deve possuir o atributo XML "version" 106 | E o elemento "//country/airports/city:airport" não deve possuir o atributo XML "typo" 107 | E o elemento XML "//country/cities" deve ter 2 elementos 108 | E o elemento XML "//country/cities/city:city[@id=2]" deve ter 1 elemento 109 | 110 | Cenário: Verifica a interpretação do XML com namespaces, mas sem namespace default 111 | Quando Eu estou em "/xml/people.xml" 112 | Então o XML deve utilizar o namespace "http://example.org/ns" 113 | E o XML não deve utilizar o namespace "http://example.org/test" 114 | E o elemento XML "//people" deve existir 115 | E o elemento XML "//people/p:person" deve existir 116 | E o elemento XML "//people/description" não deve existir 117 | E o elemento XML "//people/p:person[@id=1]/items/item[@id=1]" deve ser igual a "Rubber Ducky" 118 | E o elemento XML "//people" deve ter 3 elementos 119 | E o atributo XML "name" no elemento "//people/p:person[@id=1]" deve ser igual a "Bert" 120 | E o atributo XML "id" no elemento "//people/p:person[@id=2]" não deve ser igual a "4" 121 | E o elemento "//people/p:person[@id=3]" deve possuir o atributo XML "name" 122 | E o elemento "//people/p:person[@id=1]/items/item" não deve possuir o atributo XML "size" 123 | 124 | Cenário: Exibe o XML formatado 125 | Quando Eu estou em "/xml/needsformatting.xml" 126 | E exiba a última resposta XML 127 | -------------------------------------------------------------------------------- /tests/features/rest.feature: -------------------------------------------------------------------------------- 1 | @rest 2 | Feature: Testing RESTContext 3 | 4 | Scenario: Testing headers 5 | When I send a GET request to "rest/index.php" 6 | And the header "Content-Type" should contain "text" 7 | And the header "Content-Type" should be equal to "text/html; charset=UTF-8" 8 | And the header "Content-Type" should not be equal to "x-test/no-such-type" 9 | And the header "Content-Type" should not contain "text/json" 10 | And the header "Content-Type" should match "@^text/html; [a-zA-Z=-]+@" 11 | And the header "Content-Type" should not match "/^no-such-type$/" 12 | And the header "xxx" should not exist 13 | And the response should expire in the future 14 | And the response should be encoded in "UTF-8" 15 | 16 | Scenario: Testing request methods. 17 | Given I send a GET request to "/rest/index.php" 18 | Then I should see "You have sent a GET request. " 19 | And I should see "No parameter received" 20 | 21 | When I send a GET request to "/rest/index.php?first=foo&second=bar" 22 | Then I should see "You have sent a GET request. " 23 | And I should see "2 parameter(s)" 24 | And I should see "first : foo" 25 | And I should see "second : bar" 26 | 27 | When I send a POST request to "/rest/index.php" with parameters: 28 | | key | value | 29 | | foo | bar | 30 | | foofile | @lorem.txt | 31 | Then I should see "You have sent a POST request. " 32 | And I should see "1 parameter(s)" 33 | And I should see "1 file(s)" 34 | And I should see "foo : bar" 35 | And I should see "foofile - name : lorem.txt" 36 | And I should see "foofile - error : 0" 37 | And I should see "foofile - size : 39" 38 | 39 | When I send a PUT request to "/rest/index.php" 40 | Then I should see "You have sent a PUT request. " 41 | 42 | When I send a DELETE request to "/rest/index.php" 43 | Then I should see "You have sent a DELETE request. " 44 | 45 | When I send a POST request to "/rest/index.php" with body: 46 | """ 47 | This is a body. 48 | """ 49 | Then I should see "Body : This is a body." 50 | 51 | When I send a PUT request to "/rest/index.php" with body: 52 | """ 53 | {"this is":"some json"} 54 | """ 55 | Then the response should be empty 56 | 57 | Scenario: request parameter with dot 58 | https://github.com/Behatch/contexts/issues/256 59 | When I send a POST request to "/rest/index.php" with parameters: 60 | | key | value | 61 | | item.id | 1 | 62 | Then I should see "item.id=1" 63 | 64 | Scenario: Add header 65 | Given I add "xxx" header equal to "yyy" 66 | When I send a GET request to "/rest/index.php" 67 | Then I should see "HTTP_XXX : yyy" 68 | 69 | Scenario: Add header with large numeric value 70 | Given I add "xxx-large-numeric" header equal to "92233720368547758070" 71 | When I send a GET request to "/rest/index.php" 72 | Then I should see "HTTP_XXX_LARGE_NUMERIC : 92233720368547758070" 73 | 74 | Scenario: Header should not be cross-scenarios persistent 75 | When I send a GET request to "/rest/index.php" 76 | Then I should not see "HTTP_XXX : yyy" 77 | Then I should not see "HTTP_XXX_LARGE_NUMERIC" 78 | 79 | Scenario: Case-insensitive header name 80 | Like describe in the rfc2614 §4.2 81 | https://tools.ietf.org/html/rfc2616#section-4.2 82 | 83 | When I send a GET request to "rest/index.php" 84 | Then the header "content-type" should contain "text" 85 | 86 | Scenario: Debug 87 | Given I add "xxx" header equal to "yyy" 88 | When I send a POST request to "/rest/index.php" with parameters: 89 | | key | value | 90 | | foo | bar | 91 | Then print last response headers 92 | And print the corresponding curl command 93 | 94 | Scenario: Response body 95 | Given I send a GET request to "/" 96 | Then the response should be equal to: 97 | """ 98 | Congratulations, you've correctly set up your apache environment. 99 | """ 100 | 101 | Scenario: Accept header should not be set by dfault 102 | When I send a GET request to "/rest/index.php" 103 | Then I should not see "HTTP_ACCEPT" 104 | 105 | @>php5.5 106 | Scenario: Set content headers in POST request 107 | When I add "Content-Type" header equal to "xxx" 108 | When I send a "POST" request to "rest/index.php" with body: 109 | """ 110 | {"name": "test"} 111 | """ 112 | Then the response should contain ">CONTENT_TYPE : xxx" 113 | Then the response should contain ">HTTP_CONTENT_TYPE : xxx" 114 | 115 | Scenario: Content header is clear in different scenario 116 | When I send a "POST" request to "rest/index.php" with body: 117 | """ 118 | {"name": "test"} 119 | """ 120 | Then the response should not contain ">CONTENT_TYPE : xxx" 121 | Then the response should not contain ">HTTP_CONTENT_TYPE : xxx" 122 | -------------------------------------------------------------------------------- /tests/features/ru/browser.feature: -------------------------------------------------------------------------------- 1 | #language: ru 2 | Функционал: Браузер 3 | 4 | # Если этот сценарий проваливается 5 | # Возможно, это из-за того, что Ваше web-окружение неправильно настроено 6 | # Вы найдёте необходимую помощь в README.md 7 | @javascript 8 | Сценарий: Тестирование простого веб-доступа 9 | Пусть я на странице "/index.html" 10 | Тогда я должен видеть "Congratulations, you've correctly set up your apache environment." 11 | 12 | @statusCode 13 | Сценарий: Basic-аутентификация 14 | Пусть я на странице "/browser/auth.php" 15 | Тогда код ответа сервера должен быть 401 16 | И я должен видеть "NONE SHALL PASS" 17 | 18 | Когда я устанавливаю Basic-аутентификацию с "something" и "wrong" 19 | И я перехожу на "/browser/auth.php" 20 | Тогда код ответа сервера должен быть 401 21 | И я должен видеть "NONE SHALL PASS" 22 | 23 | Когда я устанавливаю Basic-аутентификацию с "gabriel" и "30091984" 24 | И я перехожу на "/browser/auth.php" 25 | Тогда код ответа сервера должен быть 200 26 | И я должен видеть "Successfuly logged in" 27 | 28 | Когда я перехожу на "/browser/auth.php?logout" 29 | Тогда я должен видеть "Logged out" 30 | 31 | Когда я перехожу на "/browser/auth.php" 32 | Тогда код ответа сервера должен быть 401 33 | И я должен видеть "NONE SHALL PASS" 34 | 35 | @javascript 36 | Сценарий: Тестирование элементов 37 | Пусть я на адресе из: 38 | | parameters | 39 | | /browser | 40 | | /elements.html | 41 | Тогда я должен видеть 4 "div" в 1ом "body" 42 | И я должен видеть менее 6 "div" в 1 "body" 43 | И я должен видеть более 2 "div" в 1 "body" 44 | И выпадающий список "months_selector" не должен содержать "december" 45 | И выпадающий список "months_selector" должен содержать "january" 46 | Когда я кликаю на 1 элемент "ul li" 47 | Тогда я должен видеть "You clicked First LI" 48 | Когда я нажимаю на 2 кнопку "Submit" 49 | Тогда я должен видеть "You clicked Second BUTTON" 50 | Когда я кликаю по 1 ссылке "Second" 51 | Тогда я должен видеть "You clicked Second A" 52 | 53 | @javascript 54 | Сценарий: Тестирование фреймов 55 | Пусть я на странице "/browser/frames.html" 56 | Когда я переключаюсь на iframe "index" 57 | Тогда я должен видеть "Visible" 58 | 59 | Когда я переключаюсь на главный фрейм 60 | 61 | Когда переключаюсь на iframe "elements" 62 | Тогда выпадающий список "months_selector" должен содержать "january" 63 | 64 | @javascript 65 | Сценарий: Ожидание перед проверкой 66 | Пусть я на странице "/browser/timeout.html" 67 | Когда я жду 3 секунды пока не увижу "timeout" 68 | И я жду 1 секунду 69 | И я жду элемент "#iframe" 70 | И я жду 5 секунд элемент "#iframe" 71 | Тогда общее время должно быть more чем 1 секунды 72 | 73 | @javascript 74 | Сценарий: Ожидание пока текст не станет видимым 75 | Пусть я на странице "/browser/timeout.html" 76 | Тогда не должен видеть "timeout" 77 | Когда я жду 3 секунды пока не увижу "timeout" 78 | Тогда я должен видеть "timeout" 79 | 80 | Сценарий: Ожидание пока текст не станет видимым 81 | Пусть я на странице "/browser/index.html" 82 | Тогда я не должен видеть "foobar" в течение 1 секунды 83 | 84 | @javascript 85 | Сценарий: Проверка видимости элемента 86 | Пусть я на странице "/browser/index.html" 87 | Тогда элемент "#visible-element" должен быть видимым 88 | И элемент "#hidden-element" не должен быть видимым 89 | 90 | @javascript 91 | Сценарий: 92 | Пусть я на странице "/browser/elements.html" 93 | Тогда я заполняю поле "today" текущей датой 94 | И я заполняю поле "today" текущей датой с поправкой "-1 day" 95 | 96 | Сценарий: 97 | Пусть я на странице "/browser/elements.html" 98 | Тогда я сохраняю значение поля "today" в параметре "today" 99 | 100 | Сценарий: Ожидание долей секунды 101 | Пусть я на странице "/browser/index.html" 102 | И я жду 1.9 секунды 103 | И я жду 1.9 секунды 104 | И я жду 1.9 секунды 105 | Тогда общее время должно быть more чем 4 секунды 106 | -------------------------------------------------------------------------------- /tests/features/ru/debug.feature: -------------------------------------------------------------------------------- 1 | #language: ru 2 | Функционал: Отладка 3 | 4 | @user 5 | Сценарий: Тестирование паузы 6 | Пусть я на странице "index.html" 7 | Тогда я ставлю паузу 8 | Тогда я должен видеть "Congratulations, you've correctly set up your apache environment." 9 | Тогда я ставлю паузу 10 | 11 | @javascript 12 | Сценарий: Снятие скриншота 13 | Пусть я на странице "index.html" 14 | И я сохраняю скриншот в "index.png" 15 | -------------------------------------------------------------------------------- /tests/features/ru/json.feature: -------------------------------------------------------------------------------- 1 | #language: ru 2 | @json 3 | Функционал: Тестирование JSONContext 4 | 5 | Сценарий: Я JSON ? 6 | Пусть я на странице "/json/imajson.json" 7 | Тогда ответ должен быть в JSON 8 | Когда я на странице "/json/emptyarray.json" 9 | Тогда ответ должен быть в JSON 10 | Когда я на странице "/json/emptyobject.json" 11 | Тогда ответ должен быть в JSON 12 | Когда я на странице "/json/imnotajson.json" 13 | Тогда ответ не должен быть в JSON 14 | 15 | Сценарий: Подсчёт элементов JSON 16 | Пусть я на странице "/json/imajson.json" 17 | Тогда узел JSON "numbers" должен содержать 4 элемента 18 | 19 | Сценарий: Тестирование разбора JSON 20 | Пусть я на странице "/json/imajson.json" 21 | 22 | Тогда узел JSON "foo" должен существовать 23 | И узел JSON "root.foo" должен существовать 24 | И узел JSON "foo" должен содержать "bar" 25 | И узел JSON "foo" не должен содержать "something else" 26 | 27 | И узел JSON "numbers[0]" должен содержать "öne" 28 | И узел JSON "numbers[1]" должен содержать "two" 29 | И узел JSON "numbers[2]" должен содержать "three" 30 | И узел JSON "numbers[3].complexeshizzle" должен быть равен "true" 31 | И узел JSON "numbers[3].so[0]" должен быть равен "very" 32 | И узел JSON "numbers[3].so[1].complicated" должен быть равен "indeed" 33 | И узел JSON "numbers[0]" должен соответствовать "/ö.{1}e/" 34 | И узел JSON "numbers[1]" должен соответствовать "/.{2}o/" 35 | И узел JSON "numbers[2]" должен соответствовать "/[a-z]{3}e.+/" 36 | 37 | И узлы JSON должны быть равны: 38 | | foo | bar | 39 | | numbers[0] | öne | 40 | | numbers[1] | two | 41 | | numbers[2] | three | 42 | 43 | И узлы JSON должны содержать: 44 | | foo | bar | 45 | | numbers[0] | öne | 46 | | numbers[1] | two | 47 | | numbers[2] | three | 48 | 49 | И узлы JSON не должны содержать: 50 | | foo | something else | 51 | 52 | И узел JSON "bar" не должен существовать 53 | 54 | Сценарий: Валидация Json схемой 55 | Пусть я на странице "/json/imajson.json" 56 | Тогда JSON должен соответствовать схеме "tests/fixtures/www/json/schema.json" 57 | 58 | Сценарий: Валидация Json схемой со ссылкой (случай невалидного JSON) 59 | Пусть я на странице "/json/withref-invalid.json" 60 | Тогда JSON не должен соответствовать схеме "tests/fixtures/www/json/schemaref.json" 61 | 62 | Сценарий: Валидация Json схемой со ссылкой 63 | Пусть я на странице "/json/withref.json" 64 | Тогда JSON должен соответствовать схеме "tests/fixtures/www/json/schemaref.json" 65 | 66 | Сценарий: Валидация Json 67 | Пусть я на странице "/json/imajson.json" 68 | Тогда JSON должен соответствовать следующей схеме: 69 | """ 70 | { 71 | "type": "object", 72 | "$schema": "http://json-schema.org/draft-03/schema", 73 | "required":true, 74 | "properties": { 75 | "foo": { 76 | "type": "string", 77 | "required":true 78 | }, 79 | "numbers": { 80 | "type": "array", 81 | "required":true, 82 | "öne": { 83 | "type": "string", 84 | "required":true 85 | }, 86 | "two": { 87 | "type": "string", 88 | "required":true 89 | }, 90 | "three": { 91 | "type": "string", 92 | "required":true 93 | } 94 | } 95 | } 96 | } 97 | """ 98 | 99 | Сценарий: Глубокая валидация Json 100 | Пусть я на странице "/json/booking.json" 101 | Тогда JSON не должен соответствовать следующей схеме: 102 | """ 103 | { 104 | "type":"object", 105 | "$schema": "http://json-schema.org/draft-03/schema", 106 | "required":false, 107 | "properties":{ 108 | "Booking": { 109 | "type":"object", 110 | "required":false 111 | }, 112 | "Metadata": { 113 | "type":"object", 114 | "required":false, 115 | "properties":{ 116 | "First": { 117 | "type":"object", 118 | "required":false, 119 | "properties":{ 120 | "default_value": { 121 | "type":"boolean", 122 | "required":false 123 | }, 124 | "enabled": { 125 | "type":"boolean", 126 | "required":true 127 | } 128 | } 129 | } 130 | } 131 | } 132 | } 133 | } 134 | """ 135 | 136 | Сценарий: Валидация содержимого Json 137 | Пусть я на странице "/json/imajson.json" 138 | Тогда JSON должен быть равен: 139 | """ 140 | { 141 | "foo": "bar", 142 | "numbers": [ 143 | "öne", 144 | "two", 145 | "three", 146 | { 147 | "complexeshizzle": true, 148 | "so": [ 149 | "very", 150 | { 151 | "complicated": "indeed" 152 | } 153 | ] 154 | } 155 | ] 156 | } 157 | """ 158 | И выведи последний JSON ответ 159 | 160 | Сценарий: Проверка корневого узла JSON 161 | Пусть я на странице "/json/rootarray.json" 162 | Тогда ответ должен быть в JSON 163 | И узел JSON "root[0].name" должен существовать 164 | И узел JSON "root" должен содержать 2 элемента 165 | 166 | Сценарий: Тестирование сравнения типов 167 | Пусть я на странице "/json/arraywithtypes.json" 168 | Тогда ответ должен быть в JSON 169 | И узел JSON "root[0]" должен быть null 170 | И узел JSON "root[1]" должен быть истиной 171 | И узел JSON "root[2]" должен быть ложью 172 | И узел JSON "root[3]" должен быть равен строке "dunglas.fr" 173 | И узел JSON "root[4]" должен быть равен числу 1312 174 | И узел JSON "root[4]" должен быть равен числу 1312.0 175 | И узел JSON "root[5]" должен быть равен числу 1936.2 176 | 177 | Сценарий: Тестирование не-null значений 178 | Пусть я на странице "/json/notnullvalues.json" 179 | Тогда ответ должен быть в JSON 180 | И узел JSON '' должен содержать 5 элементов 181 | И узел JSON "one" не должен быть null 182 | И узел JSON "one" должен быть ложью 183 | И узел JSON "two" не должен быть null 184 | И узел JSON "two" должен быть истиной 185 | И узел JSON "three" не должен быть null 186 | И узел JSON "three" должен быть равен строке "" 187 | И узел JSON "four" не должен быть null 188 | И узел JSON "four" должен быть равен строке "foo" 189 | И узел JSON "five" не должен быть null 190 | И узел JSON "five" должен быть равен числу 5 191 | -------------------------------------------------------------------------------- /tests/features/ru/rest.feature: -------------------------------------------------------------------------------- 1 | #language: ru 2 | @rest 3 | Функционал: Тестирование RESTContext 4 | 5 | Сценарий: Тестирование заголовков 6 | Когда я отправляю GET запрос на "rest/index.php" 7 | И заголовок "Content-Type" должен содержать "text" 8 | И заголовок "Content-Type" должен быть равен "text/html; charset=UTF-8" 9 | И заголовок "Content-Type" не должен быть равен "x-test/no-such-type" 10 | И заголовок "Content-Type" не должен содержать "text/json" 11 | И заголовок "xxx" не должен существовать 12 | И ответ должен истекать в будущем 13 | И ответ должен быть закодирован "UTF-8" 14 | 15 | Сценарий: Тестирование методов запросов. 16 | Пусть я отправляю GET запрос на "/rest/index.php" 17 | Тогда я должен видеть "You have sent a GET request. " 18 | И я должен видеть "No parameter received" 19 | 20 | Когда я отправляю GET запрос на "/rest/index.php?first=foo&second=bar" 21 | Тогда я должен видеть "You have sent a GET request. " 22 | И я должен видеть "2 parameter(s)" 23 | И я должен видеть "first : foo" 24 | И я должен видеть "second : bar" 25 | 26 | Когда я отправляю POST запрос на "/rest/index.php" с параметрами: 27 | | key | value | 28 | | foo | bar | 29 | | foofile | @lorem.txt | 30 | Тогда я должен видеть "You have sent a POST request. " 31 | И я должен видеть "1 parameter(s)" 32 | И я должен видеть "1 file(s)" 33 | И я должен видеть "foo : bar" 34 | И я должен видеть "foofile - name : lorem.txt" 35 | И я должен видеть "foofile - error : 0" 36 | И я должен видеть "foofile - size : 39" 37 | 38 | Когда я отправляю PUT запрос на "/rest/index.php" 39 | Тогда я должен видеть "You have sent a PUT request. " 40 | 41 | Когда я отправляю DELETE запрос на "/rest/index.php" 42 | Тогда я должен видеть "You have sent a DELETE request. " 43 | 44 | Когда я отправляю POST запрос на "/rest/index.php" с телом: 45 | """ 46 | This is a body. 47 | """ 48 | Тогда я должен видеть "Body : This is a body." 49 | 50 | Когда я отправляю PUT запрос на "/rest/index.php" с телом: 51 | """ 52 | {"this is":"some json"} 53 | """ 54 | Тогда ответ должен быть пустым 55 | 56 | Сценарий: Добавление заголовка 57 | Пусть я добавляю заголовок "xxx" со значением "yyy" 58 | Когда я отправляю GET запрос на "/rest/index.php" 59 | Тогда я должен видеть "HTTP_XXX : yyy" 60 | 61 | Сценарий: Добавление заголовка с огромным числовым значением 62 | Пусть я добавляю заголовок "xxx-large-numeric" со значением "92233720368547758070" 63 | Когда я отправляю GET запрос на "/rest/index.php" 64 | Тогда я должен видеть "HTTP_XXX_LARGE_NUMERIC : 92233720368547758070" 65 | 66 | Сценарий: Заголовок не должен сохраняться между сценариями 67 | Когда я отправляю GET запрос на "/rest/index.php" 68 | Тогда я не должен видеть "HTTP_XXX : yyy" 69 | Тогда я не должен видеть "HTTP_XXX_LARGE_NUMERIC" 70 | 71 | Сценарий: Регистронезависимость имён заголовков 72 | Как описано в rfc2614 §4.2 73 | https://tools.ietf.org/html/rfc2616#section-4.2 74 | 75 | Когда я отправляю GET запрос на "rest/index.php" 76 | Тогда заголовок "content-type" должен содержать "text" 77 | 78 | Сценарий: Отладка 79 | Пусть я добавляю заголовок "xxx" со значением "yyy" 80 | Когда я отправляю POST запрос на "/rest/index.php" с параметрами: 81 | | key | value | 82 | | foo | bar | 83 | Тогда выведи заголовки последнего ответа 84 | И выведи соответствующую команду curl 85 | 86 | Сценарий: Тело ответа 87 | Пусть я отправляю GET запрос на "/" 88 | Тогда ответ должен быть 89 | """ 90 | Congratulations, you've correctly set up your apache environment. 91 | """ 92 | 93 | @>php5.5 94 | Сценарий: Установка content-заголовка в POST запросе 95 | Когда я добавляю заголовок "Content-Type" со значением "xxx" 96 | Когда я отправляю "POST" запрос на "rest/index.php" с телом: 97 | """ 98 | {"name": "test"} 99 | """ 100 | Тогда тело ответа должно содержать ">CONTENT_TYPE : xxx" 101 | Тогда тело ответа должно содержать ">HTTP_CONTENT_TYPE : xxx" 102 | 103 | Сценарий: Content-заголовок очищается между сценариями 104 | Когда я отправляю "POST" запрос на "rest/index.php" с телом: 105 | """ 106 | {"name": "test"} 107 | """ 108 | Тогда тело ответа не должно содержать ">CONTENT_TYPE : xxx" 109 | Тогда тело ответа не должно содержать ">HTTP_CONTENT_TYPE : xxx" 110 | -------------------------------------------------------------------------------- /tests/features/ru/system.feature: -------------------------------------------------------------------------------- 1 | #language: ru 2 | Функционал: Системные команды 3 | 4 | Сценарий: Тестирование выполнения 5 | Пусть я выполняю "true" 6 | Тогда команда должна выполниться успешно 7 | Пусть я выполняю "false" 8 | Тогда команда должна выполниться неуспешно 9 | 10 | Сценарий: Тестирование времени выполнения 11 | Пусть я выполняю "sleep 1" 12 | Тогда команда должна выполняться менее чем 2 секунды 13 | 14 | Пусть я выполняю "sleep 2" 15 | Тогда команда должна выполняться более чем 1 секунду 16 | 17 | Сценарий: Тестирование вывода 18 | Пусть я выполняю "echo 'Hello world'" 19 | Тогда вывод должен содержать "Hello world" 20 | И вывод должен содержать "Hel.*ld" 21 | И вывод не должен содержать "Hello John" 22 | И вывод не должен содержать "Hel.*hn" 23 | 24 | Сценарий: Тестирование полного вывода 25 | Пусть я выполняю "echo 'Hello world\nHow are you?'" 26 | Тогда вывод должен быть: 27 | """ 28 | Hello world 29 | How are you? 30 | """ 31 | И вывод не должен быть: 32 | """ 33 | Hello John 34 | How are you? 35 | """ 36 | 37 | Сценарий: Тестирование выполнения из корня проекта 38 | Пусть я выполняю "bin/behat --help" 39 | 40 | Сценарий: Создание файлов 41 | Когда я создаю файл "tests/fixtures/test" с содержимым: 42 | """ 43 | A new file 44 | """ 45 | Тогда выведи содержимое файла "tests/fixtures/test" 46 | -------------------------------------------------------------------------------- /tests/features/ru/table.feature: -------------------------------------------------------------------------------- 1 | #language: ru 2 | Функционал: Таблицы 3 | 4 | Сценарий: Тестирование доступа к /table/index.html 5 | Пусть я на странице "/table/index.html" 6 | Тогда я должен видеть "You are about to test table." 7 | 8 | Сценарий: Тестирование столбцов 9 | Пусть я на странице "/table/index.html" 10 | 11 | Тогда я должен видеть 3 столбца в таблице "table" 12 | 13 | И схема столбцов таблицы "table" должна соответствовать: 14 | | columns | 15 | | Lorem | 16 | | Ipsum | 17 | | Integer | 18 | 19 | Сценарий: Тестирование строк 20 | Пусть я на странице "/table/index.html" 21 | 22 | Тогда я должен видеть 2 строки в таблице "table" 23 | И я должен видеть 2 строки в 1 таблице "table" 24 | 25 | И данные в 1 строке таблицы "table" должны соответствовать: 26 | | col1 | col2 | 27 | | Lorem | Ipsum | 28 | 29 | И данные во 2 строке таблицы "table" должны соответствовать: 30 | | col1 | col2 | 31 | | Dolor | Sit | 32 | 33 | Сценарий: Частичное тестирование строк 34 | Пусть я на странице "/table/index.html" 35 | 36 | Тогда я должен видеть 2 строки в таблице "table" 37 | И я должен видеть 2 строки в 1 таблице "table" 38 | 39 | И данные в 1 строке таблицы "table" должны соответствовать: 40 | | col2 | 41 | | Ipsum | 42 | 43 | И данные во 2 строке таблицы "table" должны соответствовать: 44 | | col1 | 45 | | Dolor | 46 | 47 | Сценарий: Тестирование содержимого ячеек 48 | Пусть я на странице "/table/index.html" 49 | Тогда 1 столбец 1 строки в таблице "table" должен содержать "Lorem" 50 | И 2 столбец 1 строки в таблице "table" должен содержать "Ipsum" 51 | -------------------------------------------------------------------------------- /tests/features/ru/xml.feature: -------------------------------------------------------------------------------- 1 | #language: ru 2 | @xml 3 | Функционал: Тестирование XmlContext 4 | 5 | Контекст: 6 | Пусть я на странице "/xml/feed.xml" 7 | 8 | Сценарий: Я XML ? 9 | Тогда ответ должен быть в XML 10 | Когда я на странице "/xml/feed.atom" 11 | Тогда ответ должен быть в XML 12 | Когда я на странице "/xml/feed.rss" 13 | Тогда ответ должен быть в XML 14 | Когда я на странице "/xml/book.xml" 15 | Тогда ответ должен быть в XML 16 | Когда я на странице "/xml/people.xml" 17 | Тогда ответ должен быть в XML 18 | Когда я на странице "/xml/country.xml" 19 | Тогда ответ должен быть в XML 20 | Когда я на странице "/xml/needsformatting.xml" 21 | Тогда ответ должен быть в XML 22 | Когда я на странице "/xml/imnotaxml.xml" 23 | Тогда ответ не должен быть в XML 24 | Когда я на странице "/xml/notfound.xml" 25 | Тогда ответ не должен быть в XML 26 | 27 | Сценарий: Валидация с DTD 28 | Тогда XML должен соответствовать его DTD 29 | 30 | Сценарий: Валидация с XSD файлом 31 | Тогда XML должен соответствовать XSD "tests/fixtures/www/xml/schema.xsd" 32 | 33 | Сценарий: Валидация с XSD 34 | Тогда XML должен соответствовать следующему XSD: 35 | """ 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | """ 49 | 50 | Сценарий: Валидация с relax NG файлом 51 | Тогда XML должен соответствовать relax NG схеме "tests/fixtures/www/xml/schema.ng" 52 | 53 | Сценарий: Валидация с relax NG 54 | Тогда XML должен соответствовать следующей relax NG схеме: 55 | """ 56 | 57 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | """ 74 | 75 | Сценарий: Валидация Atom 76 | Пусть я на странице "/xml/feed.atom" 77 | Тогда atom должен быть валидным 78 | 79 | Сценарий: Валидация RSS 80 | Пусть я на странице "/xml/feed.rss" 81 | Тогда RSS2 должен быть валидным 82 | 83 | Сценарий: Тестирование разбора XML 84 | Пусть я на странице "/xml/book.xml" 85 | Тогда XML элемент "//book/chapter/title" должен существовать 86 | И XML элемент "//book/chapter/index" не должен существовать 87 | И XML элемент "//book/chapter/title" должен быть равен "My books" 88 | И XML элемент "//book/title" не должен быть равен "My wonderful lists" 89 | И XML атрибут "cols" у элемента "//book/chapter/para/informaltable/tgroup" должен существовать 90 | И XML атрибут "color" у элемента "//book/chapter/title" не должен существовать 91 | И XML атрибут "id" у элемента "//book/chapter" должен быть равен "books" 92 | И XML атрибут "id" у элемента "//book" не должен быть равен "choices" 93 | И XML элемент "//book/chapter/para/informaltable/tgroup/tbody" должен содержать 3 элемента 94 | И XML элемент "//book/title" должен содержать "is" 95 | И XML элемент "//book/chapter/title" не должен содержать "if" 96 | 97 | Сценарий: Тестирование разбора XML с пространствами имён и пространством имён по умолчанию 98 | Пусть я на странице "/xml/country.xml" 99 | Тогда XML должен использовать пространство имён "http://example.org/xsd/country" 100 | И XML элемент "//country/airports" должен существовать 101 | И XML элемент "//country/cities/city:city/city:park" должен существовать 102 | И XML элемент "//country/treasure" не должен существовать 103 | И XML атрибут "opened" у элемента "//city:city[@id=1]/city:park" должен быть равен "1873" 104 | И XML атрибут "attraction" у элемента "//city:city[@id=2]/city:park" не должен быть равен "Fireworks" 105 | И XML атрибут "version" у элемента "//country" должен существовать 106 | И XML атрибут "typo" у элемента "//country/airports/city:airport" не должен существовать 107 | И XML элемент "//country/cities" должен содержать 2 элемента 108 | И XML элемент "//country/cities/city:city[@id=2]" должен содержать 1 элемент 109 | 110 | Сценарий: Тестирование разбора XML с пространствами имён, но без пространства имён по умолчанию 111 | Пусть я на странице "/xml/people.xml" 112 | Тогда XML должен использовать пространство имён "http://example.org/ns" 113 | И XML не должен использовать пространство имён "http://example.org/test" 114 | И XML элемент "//people" должен существовать 115 | И XML элемент "//people/p:person" должен существовать 116 | И XML элемент "//people/description" не должен существовать 117 | И XML элемент "//people/p:person[@id=1]/items/item[@id=1]" должен быть равен "Rubber Ducky" 118 | И XML элемент "//people" должен содержать 3 элемента 119 | И XML атрибут "name" у элемента "//people/p:person[@id=1]" должен быть равен "Bert" 120 | И XML атрибут "id" у элемента "//people/p:person[@id=2]" не должен быть равен "4" 121 | И XML атрибут "name" у элемента "//people/p:person[@id=3]" должен существовать 122 | И XML атрибут "size" у элемента "//people/p:person[@id=1]/items/item" не должен существовать 123 | 124 | Сценарий: Красивый вывод XML 125 | Пусть я на странице "/xml/needsformatting.xml" 126 | И выведи последний XML ответ 127 | -------------------------------------------------------------------------------- /tests/features/system.feature: -------------------------------------------------------------------------------- 1 | Feature: System feature 2 | 3 | Scenario: Testing execution 4 | Given I execute "true" 5 | Then command should succeed 6 | Given I execute "false" 7 | Then command should fail 8 | 9 | Scenario: Testing execution time 10 | Given I execute "sleep 1" 11 | Then Command should last less than 2 seconds 12 | 13 | Given I execute "sleep 2" 14 | Then Command should last more than 1 seconds 15 | 16 | Scenario: Testing displaying output 17 | Given I execute "echo 'Hello world'" 18 | Then display the last command output 19 | 20 | Scenario: Testing execution output 21 | Given I execute "echo 'Hello world'" 22 | Then output should contain "Hello world" 23 | And output should contain "Hel.*ld" 24 | And output should not contain "Hello John" 25 | And output should not contain "Hel.*hn" 26 | 27 | Scenario: Testing execution output wall output 28 | Given I execute "echo 'Hello world\nHow are you?'" 29 | Then output should be: 30 | """ 31 | Hello world 32 | How are you? 33 | """ 34 | And output should not be: 35 | """ 36 | Hello John 37 | How are you? 38 | """ 39 | 40 | Scenario: Testing execution from the project root 41 | Given I execute "bin/behat --help" 42 | 43 | Scenario: File creation 44 | When I create the file "tests/fixtures/test" containing: 45 | """ 46 | A new file 47 | """ 48 | Then print the content of "tests/fixtures/test" file 49 | -------------------------------------------------------------------------------- /tests/features/table.feature: -------------------------------------------------------------------------------- 1 | Feature: Browser Feature 2 | 3 | Scenario: Testing access to /table/index.html 4 | Given I am on "/table/index.html" 5 | Then I should see "You are about to test table." 6 | 7 | Scenario: Testing columns 8 | Given I am on "/table/index.html" 9 | 10 | Then I should see 3 columns in the "table" table 11 | 12 | And the columns schema of the "table" table should match: 13 | | columns | 14 | | Lorem | 15 | | Ipsum | 16 | | Integer | 17 | 18 | Scenario: Testing rows 19 | Given I am on "/table/index.html" 20 | 21 | Then I should see 2 rows in the "table" table 22 | And I should see 2 rows in the 1st "table" table 23 | 24 | And the data in the 1st row of the "table" table should match: 25 | | col1 | col2 | 26 | | Lorem | Ipsum | 27 | 28 | And the data in the 2nd row of the "table" table should match: 29 | | col1 | col2 | 30 | | Dolor | Sit | 31 | 32 | Scenario: Partial Testing rows 33 | Given I am on "/table/index.html" 34 | 35 | Then I should see 2 rows in the "table" table 36 | And I should see 2 rows in the 1st "table" table 37 | 38 | And the data in the 1st row of the "table" table should match: 39 | | col2 | 40 | | Ipsum | 41 | 42 | And the data in the 2nd row of the "table" table should match: 43 | | col1 | 44 | | Dolor | 45 | 46 | Scenario: Testing cell content 47 | Given I am on "/table/index.html" 48 | Then the 1st column of the 1st row in the "table" table should contain "Lorem" 49 | And the 2nd column of the 1st row in the "table" table should contain "Ipsum" 50 | And the 3rd column of the 1st row in the "table" table should contain "42" 51 | -------------------------------------------------------------------------------- /tests/features/xml.feature: -------------------------------------------------------------------------------- 1 | @xml 2 | Feature: Testing XmlContext 3 | 4 | Background: 5 | Given I am on "/xml/feed.xml" 6 | 7 | Scenario: Am I a XML ? 8 | Then the response should be in XML 9 | When I am on "/xml/feed.atom" 10 | Then the response should be in XML 11 | When I am on "/xml/feed.rss" 12 | Then the response should be in XML 13 | When I am on "/xml/book.xml" 14 | Then the response should be in XML 15 | When I am on "/xml/people.xml" 16 | Then the response should be in XML 17 | When I am on "/xml/country.xml" 18 | Then the response should be in XML 19 | When I am on "/xml/needsformatting.xml" 20 | Then the response should be in XML 21 | When I am on "/xml/imnotaxml.xml" 22 | Then the response should not be in XML 23 | When I am on "/xml/notfound.xml" 24 | Then the response should not be in XML 25 | 26 | Scenario: Validation with DTD 27 | Then the XML feed should be valid according to its DTD 28 | 29 | Scenario: Validation with XSD file 30 | Then the XML feed should be valid according to the XSD "tests/fixtures/www/xml/schema.xsd" 31 | 32 | Scenario: Validation with inline XSD 33 | Then the XML feed should be valid according to this XSD: 34 | """ 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | """ 48 | 49 | Scenario: Validation with relax NG file 50 | Then the XML feed should be valid according to the relax NG schema "tests/fixtures/www/xml/schema.ng" 51 | 52 | Scenario: Validation with inline relax NG 53 | Then the XML feed should be valid according to this relax NG schema: 54 | """ 55 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | """ 73 | 74 | Scenario: Atom feed validation 75 | Given I am on "/xml/feed.atom" 76 | Then the atom feed should be valid 77 | 78 | Scenario: RSS feed validation 79 | Given I am on "/xml/feed.rss" 80 | Then the RSS2 feed should be valid 81 | 82 | Scenario: Check XML evaluation 83 | Given I am on "/xml/book.xml" 84 | Then the XML element "//book/chapter/title" should exist 85 | And the XML element "//book/chapter/index" should not exist 86 | And the XML element "//book/chapter/title" should be equal to "My books" 87 | And the XML element "//book/title" should not be equal to "My wonderful lists" 88 | And the XML attribute "cols" on element "//book/chapter/para/informaltable/tgroup" should exist 89 | And the XML attribute "color" on element "//book/chapter/title" should not exist 90 | And the XML attribute "id" on element "//book/chapter" should be equal to "books" 91 | And the XML attribute "id" on element "//book" should not be equal to "choices" 92 | And the XML element "//book/chapter/para/informaltable/tgroup/tbody" should have 3 elements 93 | And the XML element "//book/title" should contain "is" 94 | And the XML element "//book/chapter/title" should not contain "if" 95 | 96 | Scenario: Check XML evaluation with namespaces and a default namespace 97 | Given I am on "/xml/country.xml" 98 | Then the XML should use the namespace "http://example.org/xsd/country" 99 | And the XML element "//country/airports" should exist 100 | And the XML element "//country/cities/city:city/city:park" should exist 101 | And the XML element "//country/treasure" should not exist 102 | And the XML attribute "opened" on element "//city:city[@id=1]/city:park" should be equal to "1873" 103 | And the XML attribute "attraction" on element "//city:city[@id=2]/city:park" should not be equal to "Fireworks" 104 | And the XML attribute "version" on element "//country" should exist 105 | And the XML attribute "typo" on element "//country/airports/city:airport" should not exist 106 | And the XML element "//country/cities" should have 2 elements 107 | And the XML element "//country/cities/city:city[@id=2]" should have 1 element 108 | 109 | Scenario: Check XML evaluation with namespaces but no default namespace 110 | Given I am on "/xml/people.xml" 111 | Then the XML should use the namespace "http://example.org/ns" 112 | And the XML should not use the namespace "http://example.org/test" 113 | And the XML element "//people" should exist 114 | And the XML element "//people/p:person" should exist 115 | And the XML element "//people/description" should not exist 116 | And the XML element "//people/p:person[@id=1]/items/item[@id=1]" should be equal to "Rubber Ducky" 117 | And the XML element "//people" should have 3 elements 118 | And the XML attribute "name" on element "//people/p:person[@id=1]" should be equal to "Bert" 119 | And the XML attribute "id" on element "//people/p:person[@id=2]" should not be equal to "4" 120 | And the XML attribute "name" on element "//people/p:person[@id=3]" should exist 121 | And the XML attribute "size" on element "//people/p:person[@id=1]/items/item" should not exist 122 | 123 | Scenario: Pretty print xml 124 | Given I am on "/xml/needsformatting.xml" 125 | And print last XML response 126 | -------------------------------------------------------------------------------- /tests/fixtures/files/lorem.txt: -------------------------------------------------------------------------------- 1 | Tristique pellentesque? Urna enim sit! 2 | -------------------------------------------------------------------------------- /tests/fixtures/files/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /tests/fixtures/www/browser/auth.php: -------------------------------------------------------------------------------- 1 | Login"; 11 | } elseif ( 12 | !isset($_SERVER['PHP_AUTH_USER']) 13 | || !isset($_SERVER['PHP_AUTH_PW']) 14 | || !isset($_SESSION['login']) 15 | ) { 16 | header('WWW-Authenticate: Basic realm="Test"'); 17 | header('HTTP/1.0 401 Unauthorized'); 18 | $_SESSION['login'] = true; 19 | echo 'NONE SHALL PASS !'; 20 | } else { 21 | if ( 22 | $_SERVER['PHP_AUTH_USER'] == $username 23 | && $_SERVER['PHP_AUTH_PW'] == $password 24 | ) { 25 | echo 'Successfuly logged in'; 26 | } else { 27 | unset($_SESSION['login']); 28 | header('Location: '.$_SERVER['PHP_SELF']); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/fixtures/www/browser/elements.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
1
4 |
1
5 |
6 | 11 |
12 |
13 | 17 | 18 |
19 | First 20 | Second 21 |
22 | 23 | 24 | 25 | 26 |
27 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /tests/fixtures/www/browser/frames.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/fixtures/www/browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Browser fixtures 4 | 9 | 10 | 11 |
Visible
12 |
Hidden
13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/fixtures/www/browser/timeout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Timeout test 4 | 5 | 6 | 17 | 18 | -------------------------------------------------------------------------------- /tests/fixtures/www/index.html: -------------------------------------------------------------------------------- 1 | Congratulations, you've correctly set up your apache environment. -------------------------------------------------------------------------------- /tests/fixtures/www/json/arraywithtypes.json: -------------------------------------------------------------------------------- 1 | [ 2 | null, 3 | true, 4 | false, 5 | "dunglas.fr", 6 | 1312, 7 | 1936.2 8 | ] 9 | -------------------------------------------------------------------------------- /tests/fixtures/www/json/booking.json: -------------------------------------------------------------------------------- 1 | { 2 | "Booking": { 3 | "id": "1", 4 | "price": "77.21" 5 | }, "Metadata": 6 | { 7 | "First": { 8 | "bad_property_name": true, 9 | "default_value": true 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/fixtures/www/json/definitions.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-03/schema", 4 | "required": true, 5 | "definitions": { 6 | "https": { 7 | "type": "string", 8 | "pattern": "^https?\\:\\/\\/.+\\.[a-zA-Z]{2,4}$" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/fixtures/www/json/emptyarray.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /tests/fixtures/www/json/emptyobject.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /tests/fixtures/www/json/imajson.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "bar", 3 | "numbers": [ 4 | "öne", 5 | "two", 6 | "three", 7 | { 8 | "complexeshizzle": true, 9 | "so": ["very", {"complicated": "indeed"}] 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tests/fixtures/www/json/imnotajson.json: -------------------------------------------------------------------------------- 1 | No I'm not. -------------------------------------------------------------------------------- /tests/fixtures/www/json/notnullvalues.json: -------------------------------------------------------------------------------- 1 | { 2 | "one": false, 3 | "two": true, 4 | "three": "", 5 | "four": "foo", 6 | "five": 5 7 | } 8 | -------------------------------------------------------------------------------- /tests/fixtures/www/json/rootarray.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name":"first" 4 | }, 5 | { 6 | "name":"second" 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /tests/fixtures/www/json/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-03/schema", 4 | "required":true, 5 | "properties": { 6 | "foo": { 7 | "type": "string", 8 | "required":true 9 | }, 10 | "numbers": { 11 | "type": "array", 12 | "required":true, 13 | "one": { 14 | "type": "string", 15 | "required":true 16 | }, 17 | "two": { 18 | "type": "string", 19 | "required":true 20 | }, 21 | "three": { 22 | "type": "string", 23 | "required":true 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/fixtures/www/json/schemaref.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-03/schema", 4 | "required": true, 5 | "definitions": { 6 | "url": { 7 | "type": "string", 8 | "pattern": "^https?\\:\\/\\/.+\\.[a-zA-Z]{2,4}$" 9 | } 10 | }, 11 | "properties": { 12 | "foo": { "$ref": "#/definitions/url"}, 13 | "bar": { "$ref": "definitions.json#/definitions/https"} 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/fixtures/www/json/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "basePath": "\/", 4 | "info": { 5 | "title": "Sample API", 6 | "version": "2.0.0", 7 | "description": "Sample API" 8 | }, 9 | "paths": { 10 | "\/a\/path": { 11 | "get": { 12 | "tags": [ 13 | "sample" 14 | ], 15 | "operationId": "sampleId", 16 | "produces": [ 17 | "application\/json", 18 | "text\/html" 19 | ], 20 | "summary": "Just a fixture sample endpoint", 21 | "responses": {}, 22 | "parameters": [] 23 | } 24 | } 25 | }, 26 | "definitions": { 27 | "sample-invalid-definition": { 28 | "type": "object", 29 | "description": "", 30 | "properties": { 31 | "stringValue": { 32 | "readOnly": true, 33 | "type": "integer" 34 | }, 35 | "intValue": { 36 | "readOnly": true, 37 | "type": "integer" 38 | } 39 | } 40 | }, 41 | "sample-definition": { 42 | "type": "object", 43 | "description": "", 44 | "properties": { 45 | "stringValue": { 46 | "readOnly": true, 47 | "type": "string" 48 | }, 49 | "intValue": { 50 | "readOnly": true, 51 | "type": "integer" 52 | } 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/fixtures/www/json/swaggerpartial.json: -------------------------------------------------------------------------------- 1 | { 2 | "stringValue": "value", 3 | "intValue": 7 4 | } 5 | -------------------------------------------------------------------------------- /tests/fixtures/www/json/withref-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "not a uri", 3 | "bar": "not a uri" 4 | } 5 | -------------------------------------------------------------------------------- /tests/fixtures/www/json/withref.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "https://perdu.com", 3 | "bar": "https://woot.com" 4 | } 5 | -------------------------------------------------------------------------------- /tests/fixtures/www/rest/index.php: -------------------------------------------------------------------------------- 1 | 17 | 18 | You have sent a request. 19 | 20 | header(s) received. 21 | $value) { ?> 22 |
: 23 | 24 | 25 | 26 |
No parameter received. 27 | 28 |
parameter(s) received. 29 | $value) { ?> 30 |
: 31 | 32 | 33 | 34 | 35 |
No files received. 36 | 37 |
file(s) received. 38 | $value) { ?> 39 |
- name : 40 |
- error : 41 |
- size : 42 | 43 | 44 | 45 | 46 |
No body received. 47 | 48 |
Body : 49 | 50 | -------------------------------------------------------------------------------- /tests/fixtures/www/table/index.html: -------------------------------------------------------------------------------- 1 | You are about to test table. 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
LoremIpsumInteger
Lorem ipsum dolor sit amet
LoremIpsum42
DolorSit24
29 | -------------------------------------------------------------------------------- /tests/fixtures/www/xml/book.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | My lists 7 | 8 | My books 9 | 10 | 11 | 12 | 13 | 14 | Title 15 | Author 16 | Language 17 | ISBN 18 | 19 | 20 | 21 | 22 | The Grapes of Wrath 23 | John Steinbeck 24 | en 25 | 0140186409 26 | 27 | 28 | The Pearl 29 | John Steinbeck 30 | en 31 | 014017737X 32 | 33 | 34 | Samarcande 35 | Amine Maalouf 36 | fr 37 | 2253051209 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /tests/fixtures/www/xml/country.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Shiba Park 10 | 11 | 12 | Maruyama Park 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/fixtures/www/xml/feed.atom: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example Feed 6 | A subtitle. 7 | 8 | 9 | urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6 10 | 2003-12-13T18:30:02Z 11 | 12 | 13 | 14 | Atom-Powered Robots Run Amok 15 | 16 | 17 | 18 | urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a 19 | 2003-12-13T18:30:02Z 20 | Some text. 21 | 22 | John Doe 23 | johndoe@example.com 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /tests/fixtures/www/xml/feed.rss: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RSS Title 5 | This is an example of an RSS feed 6 | http://www.someexamplerssdomain.com/main.html 7 | Mon, 06 Sep 2010 00:01:00 +0000 8 | Mon, 06 Sep 2009 16:45:00 +0000 9 | 1800 10 | 11 | Example entry 12 | Here is some text containing an interesting description. 13 | http://www.wikipedia.org/ 14 | unique string per item 15 | Mon, 06 Sep 2009 16:45:00 +0000 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/fixtures/www/xml/feed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Title 5 | Content 6 | Comment 7 | 8 | -------------------------------------------------------------------------------- /tests/fixtures/www/xml/imnotaxml.xml: -------------------------------------------------------------------------------- 1 | Nope, definitely not XML. 2 | -------------------------------------------------------------------------------- /tests/fixtures/www/xml/needsformatting.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /tests/fixtures/www/xml/people.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Rubber Ducky 6 | 7 | 8 | 9 | 10 | Car 11 | 12 | 13 | 14 | 15 | Bowling ball 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/fixtures/www/xml/schema.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /tests/fixtures/www/xml/schema.ng: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/fixtures/www/xml/schema.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/units/Context/JsonContext.php: -------------------------------------------------------------------------------- 1 | httpCallResultPool = new HttpCallResultPool(); 16 | $this->httpCallResultPool->store( 17 | new HttpCallResult(json_encode([ 18 | 'a string node' => 'some string', 19 | 'another string node' => 'some other string', 20 | 'a null node' => null, 21 | 'a true node' => true, 22 | 'a false node' => false, 23 | 'a number node' => 3, 24 | 'an array node' => [ 25 | 'one', 26 | 'two', 27 | 'three', 28 | ], 29 | ], \JSON_THROW_ON_ERROR)) 30 | ); 31 | } 32 | 33 | public function testTheJsonNodeShouldBeEqualTo(): void 34 | { 35 | $this 36 | ->given($this->newTestedInstance($this->httpCallResultPool)) 37 | ->then 38 | 39 | ->if($this->testedInstance->theJsonNodeShouldBeEqualTo('a string node', 'some string')) 40 | 41 | ->exception(function (): void { 42 | $this->testedInstance->theJsonNodeShouldBeEqualTo('a string node', 'expectedstring'); 43 | }) 44 | ->hasMessage("The node 'a string node' value is '\"some string\"', 'expectedstring' expected"); 45 | } 46 | 47 | public function testTheJsonNodesShouldBeEqualTo(): void 48 | { 49 | $this 50 | ->given($this->newTestedInstance($this->httpCallResultPool)) 51 | ->and($validTableNode = new TableNode([ 52 | 1 => ['a string node', 'some string'], 53 | 2 => ['another string node', 'some other string'], 54 | ])) 55 | ->then 56 | 57 | ->if($this->testedInstance->theJsonNodesShouldBeEqualTo($validTableNode)) 58 | 59 | ->exception(function (): void { 60 | $invalidTableNode = new TableNode([ 61 | 1 => ['a string node', 'may the force'], 62 | 2 => ['another string node', 'be with you'], 63 | ]); 64 | $this->testedInstance->theJsonNodesShouldBeEqualTo($invalidTableNode); 65 | }) 66 | ->hasMessage("The node 'a string node' value is '\"some string\"', 'may the force' expected\nThe node 'another string node' value is '\"some other string\"', 'be with you' expected"); 67 | } 68 | 69 | public function testTheJsonNodeShouldMatch(): void 70 | { 71 | $this 72 | ->given($this->newTestedInstance($this->httpCallResultPool)) 73 | ->then 74 | 75 | ->if($this->testedInstance->theJsonNodeShouldMatch('a string node', '/some/')) 76 | 77 | ->exception(function (): void { 78 | $this->testedInstance->theJsonNodeShouldMatch('a string node', '/nomatch/'); 79 | }) 80 | ->hasMessage("The node 'a string node' value is '\"some string\"', '/nomatch/' pattern expected"); 81 | } 82 | 83 | public function testTheJsonNodeShouldBeNull(): void 84 | { 85 | $this 86 | ->given($this->newTestedInstance($this->httpCallResultPool)) 87 | ->then 88 | 89 | ->if($this->testedInstance->theJsonNodeShouldBeNull('a null node')) 90 | 91 | ->exception(function (): void { 92 | $this->testedInstance->theJsonNodeShouldBeNull('a string node'); 93 | }) 94 | ->hasMessage("The node 'a string node' value is '\"some string\"', null expected"); 95 | } 96 | 97 | public function testTheJsonNodeShouldNotBeNull(): void 98 | { 99 | $this 100 | ->given($this->newTestedInstance($this->httpCallResultPool)) 101 | ->then 102 | 103 | ->if($this->testedInstance->theJsonNodeShouldNotBeNull('a string node')) 104 | 105 | ->exception(function (): void { 106 | $this->testedInstance->theJsonNodeShouldNotBeNull('a null node'); 107 | }) 108 | ->hasMessage("The node 'a null node' value is null, non-null value expected"); 109 | } 110 | 111 | public function testTheJsonNodeShouldBeTrue(): void 112 | { 113 | $this 114 | ->given($this->newTestedInstance($this->httpCallResultPool)) 115 | ->then 116 | 117 | ->if($this->testedInstance->theJsonNodeShouldBeTrue('a true node')) 118 | 119 | ->exception(function (): void { 120 | $this->testedInstance->theJsonNodeShouldBeTrue('a false node'); 121 | }) 122 | ->hasMessage("The node 'a false node' value is 'false', 'true' expected"); 123 | } 124 | 125 | public function testTheJsonNodeShouldBeFalse(): void 126 | { 127 | $this 128 | ->given($this->newTestedInstance($this->httpCallResultPool)) 129 | ->then 130 | 131 | ->if($this->testedInstance->theJsonNodeShouldBeFalse('a false node')) 132 | 133 | ->exception(function (): void { 134 | $this->testedInstance->theJsonNodeShouldBeFalse('a true node'); 135 | }) 136 | ->hasMessage("The node 'a true node' value is 'true', 'false' expected"); 137 | } 138 | 139 | public function testTheJsonNodeShouldBeEqualToTheString(): void 140 | { 141 | $this 142 | ->given($this->newTestedInstance($this->httpCallResultPool)) 143 | ->then 144 | 145 | ->if($this->testedInstance->theJsonNodeShouldBeEqualToTheString('a string node', 'some string')) 146 | 147 | ->exception(function (): void { 148 | $this->testedInstance->theJsonNodeShouldBeEqualToTheString('a string node', 'expected'); 149 | }) 150 | ->hasMessage("The node 'a string node' value is '\"some string\"', string 'expected' expected"); 151 | } 152 | 153 | public function testTheJsonNodeShouldBeEqualToTheNumber(): void 154 | { 155 | $this 156 | ->given($this->newTestedInstance($this->httpCallResultPool)) 157 | ->then 158 | 159 | ->if($this->testedInstance->theJsonNodeShouldBeEqualToTheNumber('a number node', 3)) 160 | 161 | ->exception(function (): void { 162 | $this->testedInstance->theJsonNodeShouldBeEqualToTheNumber('a number node', 2); 163 | }) 164 | ->hasMessage("The node 'a number node' value is '3', number '2' expected"); 165 | } 166 | 167 | public function testTheJsonNodeShouldExist(): void 168 | { 169 | $this 170 | ->given($this->newTestedInstance($this->httpCallResultPool)) 171 | ->then 172 | 173 | ->if($this->testedInstance->theJsonNodeShouldExist('a string node')) 174 | 175 | ->exception(function (): void { 176 | $this->testedInstance->theJsonNodeShouldExist('invalid key'); 177 | }) 178 | ->hasMessage("The node 'invalid key' does not exist."); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /tests/units/Json/Json.php: -------------------------------------------------------------------------------- 1 | newTestedInstance('{"foo": "bar"}'); 12 | $this->object($json) 13 | ->isInstanceOf('Behatch\Json\Json'); 14 | } 15 | 16 | public function test_construct_invalid_json(): void 17 | { 18 | $this->exception(function (): void { 19 | $json = $this->newTestedInstance('{{json'); 20 | }) 21 | ->hasMessage("The string '{{json' is not valid json"); 22 | } 23 | 24 | public function test_to_string(): void 25 | { 26 | $content = '{"foo":"bar"}'; 27 | $json = $this->newTestedInstance($content); 28 | 29 | $this->castToString($json) 30 | ->isEqualTo($content); 31 | } 32 | 33 | public function test_read(): void 34 | { 35 | $accessor = PropertyAccess::createPropertyAccessor(); 36 | $json = $this->newTestedInstance('{"foo":"bar"}'); 37 | $result = $json->read('foo', $accessor); 38 | 39 | $this->string($result) 40 | ->isEqualTo('bar'); 41 | } 42 | 43 | public function test_read_invalid_expression(): void 44 | { 45 | $accessor = PropertyAccess::createPropertyAccessor(); 46 | $json = $this->newTestedInstance('{"foo":"bar"}'); 47 | 48 | $this->exception(function () use ($json, $accessor): void { 49 | $json->read('jeanmarc', $accessor); 50 | }) 51 | ->isInstanceOf('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException'); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/units/Json/JsonInspector.php: -------------------------------------------------------------------------------- 1 | newTestedInstance('php'); 11 | $result = $inspector->evaluate($json, 'foo.bar'); 12 | 13 | $this->string($result) 14 | ->isEqualTo('foobar'); 15 | } 16 | 17 | public function test_evaluate_invalid(): void 18 | { 19 | $json = new \Behatch\Json\Json('{}'); 20 | $inspector = $this->newTestedInstance('php'); 21 | 22 | $this->exception(function () use ($json, $inspector): void { 23 | $inspector->evaluate($json, 'foo.bar'); 24 | }) 25 | ->hasMessage("Failed to evaluate expression 'foo.bar'"); 26 | } 27 | 28 | public function test_evaluate_javascript_mode(): void 29 | { 30 | $json = new \Behatch\Json\Json('{ "foo": { "bar": "foobar" } }'); 31 | $inspector = $this->newTestedInstance('javascript'); 32 | $result = $inspector->evaluate($json, 'foo->bar'); 33 | 34 | $this->string($result) 35 | ->isEqualTo('foobar'); 36 | } 37 | 38 | public function test_evaluate_php_mode(): void 39 | { 40 | $json = new \Behatch\Json\Json('{ "foo": { "bar": "foobar" } }'); 41 | $inspector = $this->newTestedInstance('php'); 42 | $result = $inspector->evaluate($json, 'foo.bar'); 43 | 44 | $this->string($result) 45 | ->isEqualTo('foobar'); 46 | } 47 | 48 | public function test_validate(): void 49 | { 50 | $json = new \Behatch\Json\Json('{ "foo": { "bar": "foobar" } }'); 51 | $inspector = $this->newTestedInstance('php'); 52 | $schema = new \Behatch\Json\JsonSchema('{}'); 53 | 54 | $result = $inspector->validate($json, $schema); 55 | 56 | $this->boolean($result) 57 | ->isEqualTo(true); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/units/Json/JsonSchema.php: -------------------------------------------------------------------------------- 1 | newTestedInstance('{}'); 10 | $resolver = new \JsonSchema\SchemaStorage(new \JsonSchema\Uri\UriRetriever(), new \JsonSchema\Uri\UriResolver()); 11 | $schema->resolve($resolver); 12 | } 13 | 14 | public function test_resolve_with_uri(): void 15 | { 16 | $file = 'file://'.__DIR__.'/../../fixtures/files/schema.json'; 17 | $schema = (object) ['id' => $file]; 18 | $resolver = new \JsonSchema\SchemaStorage(new \JsonSchema\Uri\UriRetriever(), new \JsonSchema\Uri\UriResolver()); 19 | $result = $resolver->resolveRef($file); 20 | 21 | $this->object($result) 22 | ->isEqualTo($schema); 23 | } 24 | 25 | public function test_validate(): void 26 | { 27 | $schema = $this->newTestedInstance('{}'); 28 | $json = new \Behatch\Json\Json('{}'); 29 | $validator = new \JsonSchema\Validator(); 30 | $result = $schema->validate($json, $validator); 31 | 32 | $this->boolean($result) 33 | ->isTrue(); 34 | } 35 | 36 | public function test_validate_invalid(): void 37 | { 38 | $schema = $this->newTestedInstance('{ "type": "object", "properties": {}, "additionalProperties": false }'); 39 | $json = new \Behatch\Json\Json('{ "foo": { "bar": "foobar" } }'); 40 | $validator = new \JsonSchema\Validator(); 41 | $this->exception(function () use ($schema, $json, $validator): void { 42 | $schema->validate($json, $validator); 43 | }) 44 | ->hasMessage(<<