├── .editorconfig ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .scrutinizer.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── RoboFile.php ├── changelog ├── changelog.config.example.php ├── codeception.dist.yml ├── compose.yaml ├── composer.json ├── package └── stub.php ├── src ├── AbstractIO.php ├── ChangeLog.php ├── Console │ ├── AbstractCommand.php │ ├── Add.php │ ├── ConfigNotFoundException.php │ ├── Convert.php │ ├── InvalidArgumentException.php │ ├── Merge.php │ ├── Release.php │ ├── ReleaseExistsException.php │ └── ReleaseNotFoundException.php ├── GenericFactory.php ├── IO │ ├── AbstractGitHubIO.php │ ├── File.php │ ├── Flysystem.php │ ├── GitHub.php │ ├── GitHubReleases.php │ └── Native.php ├── IOInterface.php ├── Log.php ├── Parser │ └── KeepAChangeLog.php ├── ParserInterface.php ├── Release.php ├── RenderInterface.php ├── Renderer │ ├── Json.php │ ├── KeepAChangeLog.php │ └── Xml.php └── Version.php └── tests ├── _bootstrap.php ├── _output └── .gitkeep ├── _support ├── UnitHelper.php ├── UnitTester.php └── _generated │ └── UnitTesterActions.php ├── resources ├── Parser-Json-testRender.json ├── Parser-Xml-testRender.xml ├── changelog.config.merge.php ├── changelog.config.php ├── changelog.config_missing_unlreleased.php ├── changelog.md ├── changelog.no_unreleased.md ├── partial-changelog-1.md └── partial-changelog-2.md ├── stubs ├── AbstractCommandStub.php ├── AbstractIOStub.php ├── GetableConstructorStub.php ├── GitHubReleasesStub.php └── GitHubStub.php ├── unit.suite.yml └── unit ├── AbstractIOTest.php ├── ChangeLogTest.php ├── Console ├── AbstractCommandTest.php ├── AddTest.php ├── ConvertTest.php ├── MergeTest.php └── ReleaseTest.php ├── GenericFactoryTest.php ├── IO ├── FileTest.php ├── FlysystemTest.php ├── GitHubReleasesTest.php ├── GitHubTest.php └── NativeTest.php ├── LogTest.php ├── Parser └── KeepAChangeLogTest.php ├── ReleaseTest.php ├── Renderer ├── JsonTest.php ├── KeepAChangeLogTest.php └── XmlTest.php ├── UnitTester.php └── _bootstrap.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | end_of_line = lf 4 | charset = utf-8 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | indent_style = tab 8 | indent_size = 4 9 | [*.yml] 10 | indent_style = space 11 | indent_size = 2 12 | [*.json] 13 | indent_style = space 14 | indent_size = 4 15 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: PHP Composer 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | run: 12 | runs-on: ${{ matrix.operating-system }} 13 | strategy: 14 | matrix: 15 | operating-system: ['ubuntu-latest'] 16 | php-versions: ['8.2', '8.3'] 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 10 21 | 22 | - name: Setup PHP 23 | uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: ${{ matrix.php-versions }} 26 | extensions: fileinfo 27 | tools: composer 28 | coverage: xdebug 29 | 30 | - name: Validate composer 31 | run: composer validate --strict 32 | 33 | - name: Install 34 | run: composer install --prefer-dist --no-progress 35 | 36 | - name: Unit Test 37 | run: vendor/bin/codecept run unit --coverage-xml 38 | 39 | - name: Upload Scrutinizer coverage 40 | uses: sudo-bot/action-scrutinizer@latest 41 | with: 42 | cli-args: "--format=php-clover tests/_output/coverage.xml" 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | composer.lock 3 | .idea 4 | codeception.yml 5 | tests/_output/* 6 | package/bin 7 | package/changelog.phar 8 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - php 3 | tools: 4 | external_code_coverage: true 5 | build: 6 | image: default-bionic 7 | nodes: 8 | analysis: 9 | tests: 10 | override: 11 | - php-scrutinizer-run 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [Unreleased] 4 | ### Changed 5 | - Changed my mind and bumped min PHP version to 8.2 [#28](https://github.com/emlynwest/changelog/issues/28) 6 | - Converted CI to use GitHub actions [#29](https://github.com/emlynwest/changelog/issues/29) 7 | - Raised minimum php version to 8.1 + bare minimum code updates [#28](https://github.com/emlynwest/changelog/issues/28) 8 | - Updated composer packages to run with 8.1+ versions of packages [#28](https://github.com/emlynwest/changelog/issues/28) 9 | - Updated composer packages and fixed tests to run on 8.1 [#28](https://github.com/emlynwest/changelog/issues/28) 10 | 11 | ## [1.3.0] 12 | ### Added 13 | - Introduce command line merge command [#25](https://github.com/emlynwest/changelog/issues/25) 14 | - Build targets for PHP 7.1 and 7.2 15 | 16 | ### Removed 17 | - Build target for HHVM 18 | 19 | ## [1.2.0] 2016-04-23 20 | ### Added 21 | - Create release command line utility [#6](https://github.com/emlynwest/changelog/issues/6). 22 | - Create convert command line utility [#6](https://github.com/emlynwest/changelog/issues/6). 23 | - Create add command line utility [#6](https://github.com/emlynwest/changelog/issues/6). 24 | - Task to create .phar distributable. 25 | 26 | ### Fixed 27 | - Links are no longer parsed as a description. 28 | - Links are now parsed correctly. 29 | - No longer require `dev-master` for the `naneau/semver` package [#21](https://github.com/emlynwest/changelog/issues/21). 30 | 31 | ## [1.1.0] 32 | ### Added 33 | - Flysystem IO adaptor [#3](https://github.com/emlynwest/changelog/issues/3). 34 | - GitHub release reading as part of [#17](https://github.com/emlynwest/changelog/issues/17). 35 | 36 | ### Fixed 37 | - Fixes `KeepAChangeLog` behaving strangely with extra new lines [#19](https://github.com/emlynwest/changelog/issues/19). 38 | - Changed `IO\String` to `IO\Native` 39 | 40 | ## [1.0.0] 41 | ### Removed 42 | - `IO\File` no longer does an `is_file()` check to allow for remote url fetching [#13](https://github.com/emlynwest/changelog/issues/13). 43 | 44 | ## [0.4.0] - 2015-01-29 45 | ### Added 46 | - Remove a `Release` from a `Log` [#14](https://github.com/emlynwest/changelog/issues/14). 47 | - Added date field to `Release` and date parsing to `KeepAChangeLog` parser [#11](https://github.com/emlynwest/changelog/issues/11). 48 | - Added support to `KeepAChangeLog` to look for yanked releases [#10](https://github.com/emlynwest/changelog/issues/10). 49 | - Added messy link handling to `KeepAChangeLog` [#9](https://github.com/emlynwest/changelog/issues/9). 50 | - Json and XML renderer [#7](https://github.com/emlynwest/changelog/issues/7). 51 | 52 | ### Fixed 53 | - Fixed sorting releases with a release of the title "unreleased" [#15](https://github.com/emlynwest/changelog/issues/15). 54 | - Named links are now parsed and rendered correctly. 55 | 56 | ### Changed 57 | - Splits `Parser` into `Renderer`s and `Parser`s to better separate the functionality. 58 | 59 | ## [0.3.0] - 2015-01-25 60 | ### Added 61 | - `Release`s are now sorted when being added to a `Log` [#12](https://github.com/emlynwest/changelog/issues/12). 62 | - `Log`s can now be merged [#5](https://github.com/emlynwest/changelog/issues/5). 63 | - GitHub IO adaptor implemented [#4](https://github.com/emlynwest/changelog/issues/4). 64 | 65 | ### Changed 66 | - `ChangeLog` now throws exceptions if input/output are not set when calling `parse()` and `write()`. 67 | 68 | ## [0.2.0] - 2015-01-21 69 | ### Added 70 | - Implements array access for `Log` [#1](https://github.com/emlynwest/changelog/issues/1). 71 | - Added name param to Release constructor. 72 | - Added ability to write out logs to file [#2](https://github.com/emlynwest/changelog/issues/2). 73 | 74 | ### Changed 75 | - Renamed "providers" to "IO". 76 | 77 | ## [0.1.0] - 2015-01-18 78 | ### Added 79 | - Initial version. 80 | 81 | [Unreleased]: https://github.com/emlynwest/changelog 82 | [1.3.0]: https://github.com/emlynwest/changelog/releases/tag/1.3.0 83 | [1.2.0]: https://github.com/emlynwest/changelog/releases/tag/1.2.0 84 | [1.1.0]: https://github.com/emlynwest/changelog/releases/tag/1.1.0 85 | [1.0.0]: https://github.com/emlynwest/changelog/releases/tag/1.0.0 86 | [0.4.0]: https://github.com/emlynwest/changelog/releases/tag/0.4.0 87 | [0.3.0]: https://github.com/emlynwest/changelog/releases/tag/0.3.0 88 | [0.2.0]: https://github.com/emlynwest/changelog/releases/tag/0.2.0 89 | [0.1.0]: https://github.com/emlynwest/changelog/releases/tag/0.1.0 90 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are awesome but to help things along please take a look at the following. 4 | 5 | - [Issues](#-issues) 6 | - [Feature Requests](#-feature-requests) 7 | - [Pull Requests](#-pull-requests) 8 | 9 | ## Issues 10 | 11 | When opening an issue please make sure to include as much information as possible such as: 12 | 13 | - **things you have already tried** 14 | - Have you tried the latest package version? (`dev-main`) 15 | - Is there already a similar issue? 16 | - PHP version 17 | - Stack trace 18 | - Sample code or data 19 | 20 | ## Feature requests 21 | 22 | Feature requests can be either opened as an issue or you can contact me and ask for something. 23 | 24 | When you do think about what you are asking for, will others use it? Is it specific to your problem or not? 25 | 26 | ## Pull requests 27 | 28 | These are always welcome, it's what open source software is all about. But please keep a few things in mind: 29 | 30 | ### Follow the coding standard 31 | 32 | This package uses [PSR-2] with the exception that indentation should be `tab` characters, not `spaces` and that all `{` 33 | should be on a new line. 34 | 35 | ### Unit tests 36 | 37 | Any changes to the code should include updated or additional tests. 38 | Please make sure that if a test relates to an issue there is an `@link` to show that. 39 | 40 | Any new features should have related tests in the same pull request. 41 | 42 | All tests must pass, nothing will be merged in if there are tests failing. 43 | 44 | ### Code Quality 45 | 46 | While mostly a secondary concern code quality is still important. As a general rule of thumb make sure methods are not 47 | too long or needlessly complex when they can be broken up, use a bit of common sense. 48 | 49 | [PSR-2]: http://www.php-fig.org/psr/psr-2/ 50 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/devgine/composer-php:latest 2 | 3 | ADD . /code 4 | 5 | WORKDIR /code 6 | 7 | RUN apk add --no-cache bash 8 | 9 | RUN composer install 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Emlyn West 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | 3 | [![Build Status](https://github.com/emlynwest/changelog/actions/workflows/main.yml/badge.svg)](https://github.com/emlynwest/changelog/actions) 4 | [![Code Coverage](https://img.shields.io/scrutinizer/g/emlynwest/changelog.svg?style=flat-square)](https://scrutinizer-ci.com/g/emlynwest/changelog/) 5 | [![Code Quality](https://img.shields.io/scrutinizer/coverage/g/emlynwest/changelog.svg?style=flat-square)](https://scrutinizer-ci.com/g/emlynwest/changelog/) 6 | [![Packagist](https://img.shields.io/packagist/v/emlynwest/changelog.svg?style=flat-square)](https://packagist.org/packages/emlynwest/changelog) 7 | 8 | Quickly and easily modify change logs from a variety of sources. 9 | 10 | Currently the package only supports the [KeepAChangeLog] format of change logs. 11 | 12 | It is possible to read and write logs from/to: 13 | 14 | - File 15 | - Url (no support for output) 16 | - Native string 17 | - [Flysystem][flysystem] 18 | - GitHub repo via GitHub API (currently not for output as I've yet to find a sensible way to do this) 19 | 20 | Logs can be formatted into the [KeepAChangeLog] format, xml and json through the use of various render classes. 21 | 22 | ## Quick Examples 23 | 24 | ### Creating a log 25 | 26 | ```php 27 | setTitle('My Project Change Log'); 32 | $log->setDescription('This is my project\'s change log. Any crazy stuff that happens will appear here.'); 33 | 34 | // Create and add a new release. 35 | $release1 = new \ChangeLog\Release('1.0.0'); 36 | $release1->addChange('Added', 'Awesome feature needed for release'); 37 | $log->addRelease($release1); 38 | 39 | $release2 = new \ChangeLog\Release('0.3.0'); 40 | $release2->addChange('Added', 'Finally added a change log'); 41 | $release2->setChanges('Fixed', [ 42 | 'Bug 1', 43 | 'Bug 2', 44 | 'Bug 3', 45 | ]); 46 | $log->addRelease($release2); 47 | ``` 48 | 49 | **Note** releases are sorted in accordance to [Semantic Versioning](http://semver.org) automatically with the latest release at the top. 50 | It is expected that all release names follow this and the only exception to this is `unreleased` which will always be at 51 | the top of the release list. 52 | 53 | ### Parsing a log 54 | 55 | ```php 56 | 'path/to/changelog.md' 60 | ]); 61 | 62 | $parser = new \ChangeLog\Parser\KeepAChangeLog(); 63 | 64 | $cl = new \ChangeLog\ChangeLog; 65 | $cl->setParser($parser); 66 | $cl->setInput($input); 67 | 68 | $log = $cl->parse(); 69 | 70 | // Instance of ChangeLog\Log 71 | var_dump($log); 72 | ``` 73 | 74 | ### Writing a log 75 | 76 | ```php 77 | 'path/to/changelog.md' 81 | ]); 82 | 83 | $renderer = new \ChangeLog\Renderer\KeepAChangeLog(); 84 | 85 | $cl = new \ChangeLog\ChangeLog; 86 | $cl->setRenderer($renderer); 87 | $cl->setOutput($output); 88 | 89 | $log = new Log; 90 | // Build up the log file information here 91 | 92 | $cl->write($log); 93 | ``` 94 | 95 | ### Merging Logs 96 | 97 | Logs can be merged together to create a single change log. This includes releases and their changes. 98 | 99 | ```php 100 | $log1 = new Log; 101 | // Add some releases or something 102 | 103 | $log2 = new Log; 104 | // Add some releases to this too 105 | 106 | $log1->mergeLog($log2); 107 | // $log1 now contains all releases and changes from $log2 108 | ``` 109 | 110 | Depending on your use case it might be useful to create an empty log first and merge other logs into that. 111 | 112 | ## Command line utility 113 | 114 | Common actions can be performed from the command line using the `./vendor/bin/changelog` command or via the `changelog.phar` 115 | at [the releases page][releases]. 116 | 117 | The command line utility expects a config file called `changelog.config.php` to exist in the working directory, or it 118 | can be specified with the global `--config` option. An example config file can be found in `changelog.config.example.php` 119 | 120 | All commands use the same four options to read, parse, render and finally output a change log. These all default to the 121 | "default" entry in their respective config arrays. 122 | 123 | ``` 124 | --input[=INPUT] Config to use for input processor [default: "default"] 125 | --parser[=PARSER] Config to use for parser processor [default: "default"] 126 | --renderer[=RENDERER] Config to use for renderer processor [default: "default"] 127 | --output[=OUTPUT] Config to use for output processor [default: "default"] 128 | ``` 129 | 130 | Eg: `changelog.phar --renderer=json` would use the `json` entry from the `renderer` entry of the config file to construct 131 | a `ChangeLog\Renderer\Json` object to use to create the end content. 132 | 133 | The current commands are: 134 | - Add: Adds a change to a release 135 | - Convert: Converts a release between formats. Simply runs the read, parse, render, write sequence. 136 | - Release: Converts the "unreleased" release into a real release. Can take names such as `major`, `minor`, `patch` to 137 | automatically create release numbers. 138 | - Merge: Merge multiple changelog into one. 139 | 140 | Check `changelog.phar help` for more information. 141 | 142 | ## Development 143 | 144 | Current plans for development can be found in the repo's [issue tracker][issues]. 145 | If you wish to request extra functionality then open an issue or pull request. 146 | 147 | Feel free to report any issues on the [issue tracker][issues]. 148 | 149 | ## Author 150 | 151 | ### Emlyn West 152 | 153 | - [GitHub] 154 | - Email: emlyn.west@gmail.com 155 | 156 | [KeepAChangeLog]: http://keepachangelog.com/ 157 | [flysystem]: http://flysystem.thephpleague.com/ 158 | [issues]: https://github.com/emlynwest/changelog/issues 159 | [GitHub]: https://github.com/emlynwest 160 | [releases]: https://github.com/emlynwest/changelog/releases 161 | -------------------------------------------------------------------------------- /RoboFile.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT http://opensource.org/licenses/MIT 8 | * @link https://github.com/emlynwest/changelog 9 | */ 10 | 11 | use Robo\Tasks; 12 | use Symfony\Component\Finder\Finder; 13 | 14 | class RoboFile extends Tasks 15 | { 16 | public function createPhar() 17 | { 18 | $collection = $this->collection(); 19 | 20 | $this->taskComposerInstall() 21 | ->noDev() 22 | ->optimizeAutoloader() 23 | ->printed(false) 24 | ->addToCollection($collection); 25 | 26 | $packer = $this->taskPackPhar('package/changelog.phar') 27 | ->compress(true) 28 | ->stub('package/stub.php'); 29 | 30 | $files = Finder::create() 31 | ->ignoreVCS(true) 32 | ->files() 33 | ->name('*.php') 34 | ->path('src') 35 | ->path('vendor') 36 | ->in(__DIR__); 37 | 38 | foreach ($files as $file) { 39 | $packer->addFile($file->getRelativePathname(), $file->getRealPath()); 40 | } 41 | 42 | @unlink('package/bin'); 43 | @unlink('package/changelog.phar'); 44 | copy('changelog', 'package/bin'); 45 | 46 | $packer->addFile('changelog', 'package/bin') 47 | ->addToCollection($collection); 48 | 49 | $this->taskComposerInstall() 50 | ->printed(false) 51 | ->addToCollection($collection); 52 | 53 | $collection->run(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /changelog: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | add(new Release('release')); 29 | $app->add(new Convert('convert')); 30 | $app->add(new Add('add')); 31 | $app->add(new Merge('merge')); 32 | 33 | $app->run(); 34 | -------------------------------------------------------------------------------- /changelog.config.example.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT http://opensource.org/licenses/MIT 8 | * @link https://github.com/emlynwest/changelog 9 | * 10 | * Sample config. 11 | */ 12 | 13 | /** 14 | * Sample config for the command line utility 15 | * TODO: expand this out to cover all the various IO, parser and renderer options 16 | */ 17 | 18 | return [ 19 | 'input' => [ 20 | 'default' => [ 21 | 'strategy' => 'File', 22 | 'config' => [ 23 | 'file' => 'CHANGELOG.md', 24 | ], 25 | ], 26 | ], 27 | 'parser' => [ 28 | 'default' => [ 29 | 'strategy' => 'KeepAChangeLog', 30 | ], 31 | ], 32 | 'renderer' => [ 33 | 'default' => [ 34 | 'strategy' => 'KeepAChangeLog', 35 | ], 36 | 'json' => [ 37 | 'strategy' => 'Json', 38 | ], 39 | ], 40 | 'output' => [ 41 | 'default' => [ 42 | 'strategy' => 'File', 43 | 'config' => [ 44 | 'file' => 'CHANGELOG.updated.md', 45 | ], 46 | ], 47 | ], 48 | ]; 49 | -------------------------------------------------------------------------------- /codeception.dist.yml: -------------------------------------------------------------------------------- 1 | actor: Tester 2 | paths: 3 | tests: tests 4 | log: tests/_output 5 | data: tests/_data 6 | helpers: tests/_support 7 | output: tests/_output 8 | settings: 9 | bootstrap: _bootstrap.php 10 | colors: true 11 | memory_limit: 1024M 12 | coverage: 13 | enabled: true 14 | include: 15 | - src/* 16 | -------------------------------------------------------------------------------- /compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | php: 3 | build: . 4 | volumes: 5 | - .:/code 6 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "emlynwest/changelog", 3 | "description": "Package to enable change logs to be parsed", 4 | "type": "library", 5 | "keywords": ["change", "log", "changelog", "parser"], 6 | "homepage": "https://github.com/emlynwest/changelog", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Emlyn West", 11 | "email": "emlyn.west@gmail.com" 12 | } 13 | ], 14 | "require": { 15 | "symfony/console": "^7.0", 16 | "naneau/semver": "^0.0.7" 17 | }, 18 | "require-dev": { 19 | "php": ">=8.2", 20 | "ext-json": "*", 21 | "codeception/codeception": "~5.1", 22 | "codeception/mockery-module": "~0.5", 23 | "milo/github-api": "dev-master", 24 | "league/flysystem": ">=3.29", 25 | "consolidation/robo": "^5.1", 26 | "codeception/module-asserts": "^3.0", 27 | "codeception/module-filesystem": "^3.0" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "ChangeLog\\": "src/" 32 | } 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { 36 | "ChangeLog\\Stub\\": "tests/stubs" 37 | } 38 | }, 39 | "suggest": { 40 | "milo/github-api": "Allows change logs to be loaded and committed to/from a github repo.", 41 | "league/flysystem": "Allows change logs to be read and written to pretty much everywhere." 42 | }, 43 | "bin":["changelog"] 44 | } 45 | -------------------------------------------------------------------------------- /package/stub.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | setConfig($config); 34 | } 35 | 36 | /** 37 | * {@inheritdoc} 38 | */ 39 | public function setConfig($config) 40 | { 41 | $this->config = $config; 42 | } 43 | 44 | /** 45 | * Gets the given config key, will load from config defaults if not set or return 46 | * null if there is no default. 47 | * 48 | * @param string|int $key 49 | * 50 | * @return string|array 51 | */ 52 | public function getConfig($key = null) 53 | { 54 | if ($key === null) 55 | { 56 | return array_merge($this->configDefaults, $this->config); 57 | } 58 | elseif (isset($this->config[$key])) 59 | { 60 | return $this->config[$key]; 61 | } 62 | elseif (isset($this->configDefaults[$key])) 63 | { 64 | return $this->configDefaults[$key]; 65 | } 66 | 67 | return null; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/ChangeLog.php: -------------------------------------------------------------------------------- 1 | input === null) 48 | { 49 | throw new LogicException('You must specify an IOInterface for input first.'); 50 | } 51 | 52 | return $this->parser->parse( 53 | $this->input->getContent() 54 | ); 55 | } 56 | 57 | /** 58 | * Writes out the given Log to the chosen output. 59 | * 60 | * @param Log $log 61 | * 62 | * @throws LogicException 63 | */ 64 | public function write(Log $log) 65 | { 66 | if ($this->output === null) 67 | { 68 | throw new LogicException('You must specify an IOInterface for output first.'); 69 | } 70 | 71 | $this->output->setContent( 72 | $this->renderer->render($log) 73 | ); 74 | } 75 | 76 | /** 77 | * Sets the adaptor to use for reading change logs. 78 | * 79 | * @param IOInterface $input 80 | */ 81 | public function setInput(IOInterface $input) 82 | { 83 | $this->input = $input; 84 | } 85 | 86 | /** 87 | * Sets the adaptor to use for writing change logs. 88 | * 89 | * @param IOInterface $output 90 | */ 91 | public function setOutput(IOInterface $output) 92 | { 93 | $this->output = $output; 94 | } 95 | 96 | /** 97 | * Sets the adaptor to render with. 98 | * 99 | * @param RenderInterface $renderer 100 | */ 101 | public function setRenderer(RenderInterface $renderer) 102 | { 103 | $this->renderer = $renderer; 104 | } 105 | 106 | /** 107 | * Sets the adaptor to parse with. 108 | * 109 | * @param ParserInterface $parser 110 | */ 111 | public function setParser(ParserInterface $parser) 112 | { 113 | $this->parser = $parser; 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/Console/AbstractCommand.php: -------------------------------------------------------------------------------- 1 | addOption( 37 | 'config', 38 | null, 39 | InputOption::VALUE_OPTIONAL, 40 | 'Location of config file.', 41 | 'changelog.config.php' 42 | ); 43 | $this->addOption( 44 | 'input', 45 | null, 46 | InputOption::VALUE_OPTIONAL, 47 | 'Config to use for input processor', 48 | 'default' 49 | ); 50 | $this->addOption( 51 | 'parser', 52 | null, 53 | InputOption::VALUE_OPTIONAL, 54 | 'Config to use for parser processor', 55 | 'default' 56 | ); 57 | $this->addOption( 58 | 'renderer', 59 | null, 60 | InputOption::VALUE_OPTIONAL, 61 | 'Config to use for renderer processor', 62 | 'default' 63 | ); 64 | $this->addOption( 65 | 'output', 66 | null, 67 | InputOption::VALUE_OPTIONAL, 68 | 'Config to use for output processor', 69 | 'default' 70 | ); 71 | } 72 | 73 | public function execute(InputInterface $input, OutputInterface $output): int 74 | { 75 | $configLocation = $input->getOption('config'); 76 | 77 | if ( ! is_file($configLocation) && ! is_readable($configLocation)) 78 | { 79 | throw new ConfigNotFoundException('Unable to open config file: ' . $configLocation); 80 | } 81 | 82 | $this->config = require $configLocation; 83 | 84 | // Construct a changelog object to manipulate 85 | $this->changeLog = new ChangeLog(); 86 | 87 | $this->setInput($input->getOption('input')); 88 | $this->setParser($input->getOption('parser')); 89 | $this->setRenderer($input->getOption('renderer')); 90 | $this->setOutput($input->getOption('output')); 91 | 92 | return Command::SUCCESS; 93 | } 94 | 95 | protected function setInput($factoryName) 96 | { 97 | $factory = new GenericFactory('\ChangeLog\IO\\'); 98 | $instance = $factory->getInstance( 99 | $this->config['input'][$factoryName]['strategy'], 100 | $this->config['input'][$factoryName]['config'] 101 | ); 102 | $this->changeLog->setInput($instance); 103 | } 104 | 105 | protected function setParser($factoryName) 106 | { 107 | $factory = new GenericFactory('\ChangeLog\Parser\\'); 108 | $instance = $factory->getInstance($this->config['parser'][$factoryName]['strategy']); 109 | $this->changeLog->setParser($instance); 110 | } 111 | 112 | protected function setRenderer($factoryName) 113 | { 114 | $factory = new GenericFactory('\ChangeLog\Renderer\\'); 115 | $instance = $factory->getInstance($this->config['renderer'][$factoryName]['strategy']); 116 | $this->changeLog->setRenderer($instance); 117 | } 118 | 119 | protected function setOutput($factoryName) 120 | { 121 | $factory = new GenericFactory('\ChangeLog\IO\\'); 122 | $instance = $factory->getInstance( 123 | $this->config['output'][$factoryName]['strategy'], 124 | $this->config['output'][$factoryName]['config'] 125 | ); 126 | $this->changeLog->setOutput($instance); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/Console/Add.php: -------------------------------------------------------------------------------- 1 | addArgument( 35 | 'release', 36 | InputOption::VALUE_REQUIRED, 37 | 'Release to add the change to, will be created if it does not exist.' 38 | ); 39 | 40 | $this->addArgument( 41 | 'type', 42 | InputOption::VALUE_REQUIRED, 43 | 'Added, fixed, changed, etc' 44 | ); 45 | 46 | $this->addArgument( 47 | 'change', 48 | InputOption::VALUE_REQUIRED, 49 | 'Change message, eg "fixed issue #123"' 50 | ); 51 | } 52 | 53 | public function execute(InputInterface $input, OutputInterface $output): int 54 | { 55 | parent::execute($input, $output); 56 | 57 | $log = $this->changeLog->parse(); 58 | 59 | $releaseName = $input->getArgument('release'); 60 | 61 | // Create the release_name if needed 62 | if ( ! $log->hasRelease($releaseName)) { 63 | $newRelease = new LogRelease($releaseName); 64 | $log->addRelease($newRelease); 65 | } 66 | 67 | $release = $log->getRelease($releaseName); 68 | 69 | $release->addChange($input->getArgument('type'), $input->getArgument('change')); 70 | 71 | $this->changeLog->write($log); 72 | 73 | return Command::SUCCESS; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/Console/ConfigNotFoundException.php: -------------------------------------------------------------------------------- 1 | changeLog->parse(); 40 | $this->changeLog->write($log); 41 | 42 | return Command::SUCCESS; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/Console/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | addArgument( 40 | 'files', 41 | InputArgument::IS_ARRAY, 42 | 'Changelog to merge.' 43 | ); 44 | } 45 | 46 | public function execute(InputInterface $input, OutputInterface $output): int 47 | { 48 | parent::execute($input, $output); 49 | 50 | // Parse existing CHANGELOG 51 | $logs = $this->changeLog->parse(); 52 | 53 | $files = $input->getArgument('files'); 54 | foreach ($files as $f) { 55 | // Merge each file 56 | $this->changeLog->setInput(new File(['file' => $f])); 57 | $logs->mergeLog($this->changeLog->parse()); 58 | } 59 | 60 | // Write updated CHANGELOG 61 | $this->changeLog->write($logs); 62 | 63 | return Command::SUCCESS; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Console/Release.php: -------------------------------------------------------------------------------- 1 | addArgument( 34 | 'release', 35 | InputOption::VALUE_REQUIRED, 36 | 'New release number, can be any valid semver or [major|minor|patch]' 37 | ); 38 | $this->addOption( 39 | 'link', 40 | null, 41 | InputOption::VALUE_REQUIRED, 42 | 'Optional link to the release\'s info page' 43 | ); 44 | $this->addOption( 45 | 'linkName', 46 | null, 47 | InputOption::VALUE_REQUIRED, 48 | 'Optional link name, release name will be used if not specified' 49 | ); 50 | } 51 | 52 | public function execute(InputInterface $input, OutputInterface $output): int 53 | { 54 | parent::execute($input, $output); 55 | 56 | $releaseName = $input->getArgument('release'); 57 | 58 | if ($releaseName === null) { 59 | throw new InvalidArgumentException('A release name is required'); 60 | } 61 | 62 | $log = $this->changeLog->parse(); 63 | 64 | // Don't continue if there is no unreleased version 65 | if ( ! $log->hasRelease('unreleased')) { 66 | throw new ReleaseNotFoundException('Unable to find an unreleased version.'); 67 | } 68 | 69 | // Work out what version number we actually want 70 | $newReleaseName = $log->getNextVersion($releaseName); 71 | 72 | // Don't continue if the release already exists 73 | if ($log->hasRelease($newReleaseName)) { 74 | throw new ReleaseExistsException('A release with the name "' . $newReleaseName . '" already exists."'); 75 | } 76 | 77 | $release = $log->getRelease('unreleased'); 78 | $release->setName($newReleaseName); 79 | 80 | $newLink = $input->getOption('link'); 81 | 82 | if ($newLink === null) { 83 | $release->setLink(null); 84 | $release->setLinkName(null); 85 | } else { 86 | $newLinkName = $input->getOption('linkName'); 87 | $newLinkName = $newLinkName === null ? $newReleaseName : $newLinkName; 88 | 89 | $release->setLink($newLink); 90 | $release->setLinkName($newLinkName); 91 | } 92 | 93 | // Remove and re-add the release to trigger the log internals 94 | $log->addRelease($release); 95 | $log->removeRelease('unreleased'); 96 | 97 | $this->changeLog->write($log); 98 | 99 | return Command::SUCCESS; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Console/ReleaseExistsException.php: -------------------------------------------------------------------------------- 1 | baseNamespace = $baseNamespace; 41 | } 42 | 43 | /** 44 | * Returns a constructed instance of the named class. 45 | * 46 | * @param string $name 47 | * @param array $parameters 48 | * 49 | * @return mixed 50 | * 51 | * @throws InvalidArgumentException If the class cannot be found. 52 | */ 53 | public function getInstance($name, $parameters = []) 54 | { 55 | $class = $this->baseNamespace . ucfirst($name); 56 | 57 | // If we have a custom class, use that instead. 58 | if ( ! empty($this->addedClasses[$name])) 59 | { 60 | $class = $this->addedClasses[$name]; 61 | } 62 | 63 | // Ensure our class actually exists 64 | if ( ! class_exists($class)) 65 | { 66 | throw new InvalidArgumentException("$name is not a known class ($class)"); 67 | } 68 | 69 | return new $class($parameters); 70 | } 71 | 72 | /** 73 | * Adds a new named class to the factory. 74 | * 75 | * @param string $name 76 | * @param string $class 77 | */ 78 | public function addClass($name, $class) 79 | { 80 | $this->addedClasses[$name] = $class; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/IO/AbstractGitHubIO.php: -------------------------------------------------------------------------------- 1 | api === null) 31 | { 32 | $this->createApiInstance(); 33 | } 34 | 35 | return $this->api; 36 | } 37 | 38 | /** 39 | * Creates a new instance of the API library to use later. 40 | * 41 | * @throws InvalidArgumentException 42 | */ 43 | protected function createApiInstance() 44 | { 45 | $configToken = $this->getConfig('token'); 46 | 47 | if ($configToken === null) 48 | { 49 | throw new InvalidArgumentException('API token has not been set in the config.'); 50 | } 51 | 52 | $token = new Token($configToken); 53 | $this->api = new Api(); 54 | $this->api->setToken($token); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/IO/File.php: -------------------------------------------------------------------------------- 1 | "\n", 23 | ]; 24 | 25 | /** 26 | * {@inheritdoc} 27 | * 28 | * @throws InvalidArgumentException 29 | */ 30 | public function getContent() 31 | { 32 | $file = $this->getFileLocation(); 33 | 34 | $content = file_get_contents($file); 35 | 36 | return explode( 37 | $this->getConfig('line_separator'), 38 | $content 39 | ); 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function setContent($content) 46 | { 47 | $file = $this->getFileLocation(); 48 | file_put_contents($file, $content); 49 | } 50 | 51 | /** 52 | * Gets the file location from the config. 53 | * 54 | * @return string 55 | */ 56 | protected function getFileLocation() 57 | { 58 | $file = $this->getConfig('file'); 59 | 60 | if ( ! $file) 61 | { 62 | throw new InvalidArgumentException('File not specified.'); 63 | } 64 | 65 | return $file; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/IO/Flysystem.php: -------------------------------------------------------------------------------- 1 | "\n", 24 | ]; 25 | 26 | /** 27 | * {@inheritdoc} 28 | * 29 | * @throws InvalidArgumentException 30 | */ 31 | public function getContent() 32 | { 33 | $file = $this->getFileLocation(); 34 | $filesystem = $this->getFilesystem(); 35 | 36 | $content = $filesystem->read($file); 37 | 38 | return explode( 39 | $this->getConfig('line_separator'), 40 | $content 41 | ); 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public function setContent($content) 48 | { 49 | $file = $this->getFileLocation(); 50 | $filesystem = $this->getFilesystem(); 51 | 52 | $filesystem->put($file, $content); 53 | } 54 | 55 | /** 56 | * Gets the file location from the config. 57 | * 58 | * @return string 59 | */ 60 | protected function getFileLocation() 61 | { 62 | $file = $this->getConfig('file'); 63 | 64 | if ( ! $file) 65 | { 66 | throw new InvalidArgumentException('File not specified.'); 67 | } 68 | 69 | return $file; 70 | } 71 | 72 | /** 73 | * @return Filesystem 74 | */ 75 | protected function getFilesystem() 76 | { 77 | $adaptor = $this->getConfig('filesystem'); 78 | 79 | if ( ! $adaptor) 80 | { 81 | throw new InvalidArgumentException('Filesystem object not specified.'); 82 | } 83 | 84 | return $adaptor; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/IO/GitHub.php: -------------------------------------------------------------------------------- 1 | /" The full username and repo name to use. 17 | * - file: Path to the file from the root of the repo 18 | * - token: GitHub API token, can be generated under account -> applications 19 | * - commit_message: Optional commit message, used when setContent() is called. 20 | * 21 | * The token must have the "repo" or "public_repo" permission depending on the visibility of the repo. 22 | */ 23 | class GitHub extends AbstractGitHubIO 24 | { 25 | 26 | protected $configDefaults = [ 27 | 'commit_message' => 'Updates change log.', 28 | 'line_separator' => "\n", 29 | ]; 30 | 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function getContent() 35 | { 36 | $content = $this->requestFile(); 37 | 38 | return explode( 39 | $this->getConfig('line_separator'), 40 | base64_decode($content->content) 41 | ); 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public function setContent($content) 48 | { 49 | // Request the file so we can get its sha 50 | $fileInfo = $this->requestFile(); 51 | 52 | $encodedContent = base64_encode($content); 53 | $data = [ 54 | 'message' => $this->getConfig('commit_message'), 55 | 'content' => $encodedContent, 56 | 'sha' => $fileInfo->sha, 57 | ]; 58 | 59 | $api = $this->getApi(); 60 | $response = $api->put($this->getApiUrl(), $data); 61 | // Parse the response so an exception is thrown if anything goes funky 62 | $api->decode($response); 63 | } 64 | 65 | /** 66 | * Gets a URL for the GitHub api for our file. 67 | * 68 | * @return string 69 | */ 70 | protected function getApiUrl() 71 | { 72 | $repo = $this->getConfig('repo'); 73 | $file = $this->getConfig('file'); 74 | 75 | $url = "/repos/$repo/contents/$file"; 76 | return $url; 77 | } 78 | 79 | /** 80 | * @return stdClass 81 | */ 82 | protected function requestFile() 83 | { 84 | $api = $this->getApi(); 85 | $response = $api->get($this->getApiUrl()); 86 | $content = $api->decode($response); 87 | return $content; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/IO/GitHubReleases.php: -------------------------------------------------------------------------------- 1 | /" The full username and repo name to use. 15 | * - token: GitHub API token, can be generated under account -> applications. 16 | * - log_title: Optional name of the change log. 17 | * 18 | * The token must have the "repo" or "public_repo" permission depending on the visibility of the repo. 19 | */ 20 | class GitHubReleases extends AbstractGitHubIO 21 | { 22 | 23 | protected $configDefaults = [ 24 | 'log_title' => 'GitHub Releases', 25 | ]; 26 | 27 | /** 28 | * Returns the content of the change log to be parsed. 29 | * The returned data should be an array of strings, one entry for each file line. 30 | * 31 | * @return array 32 | */ 33 | public function getContent() 34 | { 35 | $api = $this->getApi(); 36 | $response = $api->get($this->getApiUrl()); 37 | $content = $api->decode($response); 38 | 39 | $links = []; 40 | 41 | $title = $this->getConfig('log_title'); 42 | $log = "# $title\n\n"; 43 | 44 | foreach($content as $release) 45 | { 46 | // published_at for release date 47 | $date = substr($release->published_at, 0, 10); 48 | 49 | // tag_name for the release title 50 | $log .= "## [{$release->tag_name}] - $date\n"; 51 | 52 | // body for the release changes 53 | $log .= $release->body."\n"; 54 | 55 | // html_url for links added at the end 56 | $links[] = "[{$release->tag_name}]: {$release->html_url}"; 57 | } 58 | 59 | return explode("\n", $log . "\n" . implode("\n", $links)); 60 | } 61 | 62 | /** 63 | * Writes out the given content, 64 | * 65 | * @param string $content 66 | */ 67 | public function setContent($content) 68 | { 69 | // TODO: Implement setContent() method. 70 | throw new \Exception('This has yet to be implemented.'); 71 | } 72 | 73 | /** 74 | * Gets a URL for the GitHub api for our file. 75 | * 76 | * @return string 77 | */ 78 | protected function getApiUrl() 79 | { 80 | $repo = $this->getConfig('repo'); 81 | 82 | $url = "/repos/$repo/releases"; 83 | return $url; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/IO/Native.php: -------------------------------------------------------------------------------- 1 | "\n", 25 | ]; 26 | 27 | /** 28 | * @param string $content 29 | */ 30 | public function __construct($content = '') 31 | { 32 | $this->setContent($content); 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function setContent($content) 39 | { 40 | $this->content = $content; 41 | } 42 | 43 | /** 44 | * {@inheritdoc} 45 | */ 46 | public function getContent() 47 | { 48 | if (is_string($this->content)) 49 | { 50 | return explode( 51 | $this->getConfig('line_separator'), 52 | $this->content 53 | ); 54 | } 55 | 56 | return $this->content; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/IOInterface.php: -------------------------------------------------------------------------------- 1 | releases; 50 | } 51 | 52 | /** 53 | * Gets the named release. 54 | * 55 | * @param string $name 56 | * 57 | * @return Release|null 58 | */ 59 | public function getRelease($name) 60 | { 61 | $key = strtolower($name); 62 | if ( ! $this->hasRelease($key)) 63 | { 64 | return null; 65 | } 66 | 67 | return $this->releases[$key]; 68 | } 69 | 70 | /** 71 | * Adds a release to the Log. 72 | * Can be used to replace existing releases too. 73 | * 74 | * @param Release $release 75 | */ 76 | public function addRelease(Release $release) 77 | { 78 | $name = strtolower($release->getName()); 79 | $this->releases[$name] = $release; 80 | $this->sortReleases(); 81 | } 82 | 83 | /** 84 | * Removes a release from the Log. 85 | * 86 | * @param string $name 87 | */ 88 | public function removeRelease($name) 89 | { 90 | $key = strtolower($name); 91 | unset($this->releases[$key]); 92 | } 93 | 94 | /** 95 | * Checks if the Log has the named Release. 96 | * 97 | * @param string $name 98 | * 99 | * @return bool 100 | */ 101 | public function hasRelease($name) 102 | { 103 | $key = strtolower($name); 104 | return isset($this->releases[$key]); 105 | } 106 | 107 | /** 108 | * @return string 109 | */ 110 | public function getDescription() 111 | { 112 | return $this->description; 113 | } 114 | 115 | /** 116 | * @param string $description 117 | */ 118 | public function setDescription($description) 119 | { 120 | $this->description = $description; 121 | } 122 | 123 | /** 124 | * @return string 125 | */ 126 | public function getTitle() 127 | { 128 | return $this->title; 129 | } 130 | 131 | /** 132 | * @param string $title 133 | */ 134 | public function setTitle($title) 135 | { 136 | $this->title = $title; 137 | } 138 | 139 | /** 140 | * {@inheritdoc} 141 | */ 142 | public function getIterator() 143 | { 144 | return new ArrayIterator($this->releases); 145 | } 146 | 147 | /** 148 | * {@inheritdoc} 149 | */ 150 | public function count() 151 | { 152 | return count($this->releases); 153 | } 154 | 155 | /** 156 | * Sorts the releases inside this log in accordance with semantic versioning, latest release first. 157 | */ 158 | public function sortReleases() 159 | { 160 | // If there is an unreleased release pull that out and sort the rest 161 | $unreleased = null; 162 | if (isset($this->releases['unreleased'])) 163 | { 164 | $unreleased = $this->releases['unreleased']; 165 | unset($this->releases['unreleased']); 166 | } 167 | 168 | $order = Sort::sort(array_keys($this->releases)); 169 | $order = array_reverse($order); 170 | 171 | $newOrder = []; 172 | /** @var Version $version */ 173 | foreach ($order as $version) 174 | { 175 | $index = $version->__toString(); 176 | $newOrder[$index] = $this->releases[$index]; 177 | } 178 | 179 | if ($unreleased !== null) 180 | { 181 | $newOrder = ['unreleased' => $unreleased] + $newOrder; 182 | } 183 | 184 | $this->releases = $newOrder; 185 | } 186 | 187 | /** 188 | * Merges another Log's releases with this log. 189 | * 190 | * @param Log $log 191 | */ 192 | public function mergeLog(Log $log) 193 | { 194 | /** @var Release $release */ 195 | foreach ($log as $release) 196 | { 197 | $name = $release->getName(); 198 | if ($this->hasRelease($name)) 199 | { 200 | // if it does exist then merge the changes 201 | $this->mergeRelease($log, $name); 202 | } 203 | else 204 | { 205 | // If the release does not exist add it 206 | $this->addRelease($release); 207 | } 208 | } 209 | } 210 | 211 | /** 212 | * Combines all changes of the name of the given release from the given log into this log. 213 | * 214 | * @param Log $log 215 | * @param string $name 216 | */ 217 | protected function mergeRelease(Log $log, $name) 218 | { 219 | $myRelease = $this->getRelease($name); 220 | $theirRelease = $log->getRelease($name); 221 | 222 | $changes = $this->mergeChangesArrays( 223 | $theirRelease->getAllChanges(), 224 | $myRelease->getAllChanges() 225 | ); 226 | $myRelease->setAllChanges($changes); 227 | } 228 | 229 | /** 230 | * Merges two sets of changes. 231 | * 232 | * @param array $left 233 | * @param array $right 234 | * 235 | * @return array 236 | */ 237 | protected function mergeChangesArrays($left, $right) 238 | { 239 | $return = $left; 240 | 241 | foreach ($right as $type => $changes) 242 | { 243 | if (isset($left[$type])) 244 | { 245 | $return[$type] = array_merge($right[$type], $left[$type]); 246 | } 247 | else 248 | { 249 | $return[$type] = $changes; 250 | } 251 | } 252 | 253 | return $return; 254 | } 255 | 256 | /** 257 | * @return Release 258 | */ 259 | public function getLatestRelease() 260 | { 261 | $releases = $this->releases; 262 | 263 | $release = array_shift($releases); 264 | 265 | if (count($this->releases) > 1 && strtolower($release->getName()) === 'unreleased') 266 | { 267 | $release = array_shift($releases); 268 | } 269 | 270 | return $release; 271 | } 272 | 273 | public function getNextVersion($type) 274 | { 275 | if (! in_array($type, [static::VERSION_MAJOR, static::VERSION_MINOR, static::VERSION_PATCH])) 276 | { 277 | return $type; 278 | } 279 | 280 | $latestRelease = $this->getLatestRelease(); 281 | 282 | $version = $latestRelease->getName() === 'unreleased' ? '0.0.0' : $latestRelease->getName() ; 283 | 284 | $semver = Parser::parse($version); 285 | $patch = $semver->getPatch(); 286 | $minor = $semver->getMinor(); 287 | $major = $semver->getMajor(); 288 | 289 | switch ($type) 290 | { 291 | case Log::VERSION_PATCH: 292 | $patch++; 293 | break; 294 | case Log::VERSION_MINOR: 295 | $minor++; 296 | break; 297 | case Log::VERSION_MAJOR: 298 | $major++; 299 | break; 300 | } 301 | 302 | return "$major.$minor.$patch"; 303 | } 304 | 305 | } 306 | -------------------------------------------------------------------------------- /src/Parser/KeepAChangeLog.php: -------------------------------------------------------------------------------- 1 | setTitle($this->trimHashes($line)); 44 | } 45 | elseif (preg_match('/^##(?!#).+/', $line) === 1) 46 | { 47 | $release = $this->parseRelease($content); 48 | $log->addRelease($release); 49 | $bodyEnded = true; 50 | } 51 | elseif (preg_match('/^\[(.+)\]: (.+)/', $line, $matches)) 52 | { 53 | if (count($matches) >= 3) 54 | { 55 | $links[$matches[1]] = $matches[2]; 56 | } 57 | $bodyEnded = true; 58 | } 59 | elseif ( ! $bodyEnded) 60 | { 61 | $description[] = $line; 62 | } 63 | 64 | $line = next($content); 65 | } 66 | 67 | $log->setDescription(implode("\n", $description)); 68 | 69 | // Assign the releases their real links 70 | $this->assignLinks($log, $links); 71 | 72 | return $log; 73 | } 74 | 75 | /** 76 | * Trims off whitespace and excess hashes from the start of a string. 77 | * 78 | * @param string $line 79 | * 80 | * @return string 81 | */ 82 | public function trimHashes($line) 83 | { 84 | return ltrim($line, "\t\n\r\0\x0B# "); 85 | } 86 | 87 | /** 88 | * Returns true if $haystack starts with $needle. 89 | * 90 | * @param $haystack 91 | * @param $needle 92 | * 93 | * @return bool 94 | */ 95 | public function startsWith($haystack, $needle) 96 | { 97 | return (substr($haystack, 0, strlen($needle)) === $needle); 98 | } 99 | 100 | /** 101 | * Builds a release. 102 | * 103 | * @param string[] $content 104 | * 105 | * @return Release 106 | */ 107 | public function parseRelease(&$content) 108 | { 109 | $release = new Release; 110 | $types = []; 111 | $lastType = ''; 112 | $nameSet = false; 113 | 114 | $line = current($content); 115 | while ($line !== false) 116 | { 117 | $line = ltrim($line); 118 | 119 | if ($this->startsWith($line, '###')) 120 | { 121 | $type = $this->trimHashes($line); 122 | $types[$type] = []; 123 | $lastType = $type; 124 | } 125 | elseif ($nameSet && 126 | $this->startsWith($line, '##') || 127 | $this->startsWith($line, '[') 128 | ) 129 | { 130 | prev($content); 131 | break; 132 | } 133 | elseif ($this->startsWith($line, '##')) 134 | { 135 | $this->handleName($release, $line); 136 | $nameSet = true; 137 | } 138 | else 139 | { 140 | $change = ltrim($line, "\t\n\r\0\x0B -"); 141 | 142 | if ($change !== '') 143 | { 144 | $types[$lastType][] = $change; 145 | } 146 | } 147 | 148 | $line = next($content); 149 | } 150 | 151 | $release->setAllChanges($types); 152 | return $release; 153 | } 154 | 155 | /** 156 | * Pulls out the needed information from a Release title and assigns that to the 157 | * given release. 158 | * 159 | * @param Release $release 160 | * @param string $line 161 | */ 162 | protected function handleName(Release $release, $line) 163 | { 164 | $this->setName($release, $line); 165 | $this->setDate($release, $line); 166 | $this->setLink($release, $line); 167 | $this->setYanked($release, $line); 168 | } 169 | 170 | /** 171 | * Extracts and sets the name of the link if there is one. 172 | * 173 | * @param Release $release 174 | * @param string $line 175 | */ 176 | protected function setLink(Release $release, $line) 177 | { 178 | $matches = []; 179 | 180 | if (preg_match('/^## \[([\w\.-\.]+)\](?:\[(\w+)\])?/', $line, $matches)) 181 | { 182 | if (count($matches) >= 3) 183 | { 184 | $release->setLink($matches[2]); 185 | $release->setLinkName($matches[2]); 186 | } 187 | else 188 | { 189 | $release->setLink($matches[1]); 190 | } 191 | } 192 | } 193 | 194 | /** 195 | * Extracts and sets the yanked flag. 196 | * 197 | * @param Release $release 198 | * @param string $line 199 | */ 200 | protected function setYanked(Release $release, $line) 201 | { 202 | if (preg_match('/\[YANKED\]$/i', $line)) 203 | { 204 | $release->setYanked(true); 205 | } 206 | } 207 | 208 | /** 209 | * Extracts and sets the release Date. 210 | * 211 | * @param Release $release 212 | * @param string $line 213 | */ 214 | protected function setDate(Release $release, $line) 215 | { 216 | $matches = []; 217 | if (preg_match('/[0-9]{4,}-[0-9]{2,}-[0-9]{2,}/', $line, $matches)) 218 | { 219 | $date = DateTime::createFromFormat('Y-m-d', $matches[0]); 220 | if ($date) 221 | { 222 | $release->setDate($date); 223 | } 224 | } 225 | } 226 | 227 | /** 228 | * Extracts and sets the Release name. 229 | * 230 | * @param Release $release 231 | * @param string $line 232 | */ 233 | protected function setName(Release $release, $line) 234 | { 235 | $matches = []; 236 | if (preg_match('/([\w\.-]{1,})/', $line, $matches)) 237 | { 238 | $release->setName($matches[0]); 239 | } 240 | } 241 | 242 | /** 243 | * @param $log 244 | * @param $links 245 | * 246 | * @since 247 | */ 248 | protected function assignLinks($log, $links) 249 | { 250 | /** @var Release $release */ 251 | foreach ($log as $release) 252 | { 253 | $link = null; 254 | $linkName = $release->getLink(); 255 | if (isset($links[$linkName])) 256 | { 257 | $link = $links[$linkName]; 258 | } 259 | $release->setLink($link); 260 | } 261 | } 262 | 263 | } 264 | -------------------------------------------------------------------------------- /src/ParserInterface.php: -------------------------------------------------------------------------------- 1 | setName($name); 58 | } 59 | 60 | /** 61 | * @return string 62 | */ 63 | public function getName() 64 | { 65 | return $this->name; 66 | } 67 | 68 | /** 69 | * @param string $name 70 | */ 71 | public function setName($name) 72 | { 73 | $this->name = $name; 74 | } 75 | 76 | /** 77 | * @return boolean 78 | */ 79 | public function isYanked() 80 | { 81 | return $this->yanked; 82 | } 83 | 84 | /** 85 | * @param boolean $yanked 86 | */ 87 | public function setYanked($yanked) 88 | { 89 | $this->yanked = $yanked; 90 | } 91 | 92 | /** 93 | * @return string 94 | */ 95 | public function getLink() 96 | { 97 | return $this->link; 98 | } 99 | 100 | /** 101 | * @param string $link 102 | */ 103 | public function setLink($link) 104 | { 105 | $this->link = $link; 106 | } 107 | 108 | /** 109 | * Adds a change to the release. 110 | * 111 | * @param string $type 112 | * @param string $message 113 | */ 114 | public function addChange($type, $message) 115 | { 116 | $this->changes[$type][] = $message; 117 | } 118 | 119 | /** 120 | * Sets all the changes for the given type 121 | * 122 | * @param string $type 123 | * @param string[] $changes 124 | */ 125 | public function setChanges($type, $changes) 126 | { 127 | $this->changes[$type] = $changes; 128 | } 129 | 130 | /** 131 | * Returns all changes for the given type or null if there are no changes. 132 | * 133 | * @param string $type 134 | * 135 | * @return string[]|null 136 | */ 137 | public function getChanges($type) 138 | { 139 | if ( ! isset($this->changes[$type])) 140 | { 141 | return null; 142 | } 143 | 144 | return $this->changes[$type]; 145 | } 146 | 147 | /** 148 | * Returns all changes in the release indexed by type 149 | * 150 | * @return array 151 | */ 152 | public function getAllChanges() 153 | { 154 | return $this->changes; 155 | } 156 | 157 | /** 158 | * Sets all the changes for the release, should be indexed by change type. 159 | * 160 | * @param string[] $changes 161 | */ 162 | public function setAllChanges($changes) 163 | { 164 | $this->changes = $changes; 165 | } 166 | 167 | /** 168 | * @return DateTime 169 | */ 170 | public function getDate() 171 | { 172 | return $this->date; 173 | } 174 | 175 | /** 176 | * @param DateTime $date 177 | */ 178 | public function setDate(DateTime $date) 179 | { 180 | $this->date = $date; 181 | } 182 | 183 | /** 184 | * @return string 185 | */ 186 | public function getLinkName() 187 | { 188 | return $this->linkName; 189 | } 190 | 191 | /** 192 | * @param string $linkName 193 | */ 194 | public function setLinkName($linkName) 195 | { 196 | $this->linkName = $linkName; 197 | } 198 | 199 | } 200 | -------------------------------------------------------------------------------- /src/RenderInterface.php: -------------------------------------------------------------------------------- 1 | $log->getTitle(), 27 | 'description' => $log->getDescription(), 28 | 'releases' => [], 29 | ]; 30 | 31 | /** 32 | * @var string $name 33 | * @var Release $release 34 | */ 35 | foreach ($log as $name => $release) 36 | { 37 | $content['releases'][$name] = $this->renderRelease($release); 38 | } 39 | 40 | return json_encode($content); 41 | } 42 | 43 | /** 44 | * @param Release $release 45 | * 46 | * @return array 47 | */ 48 | protected function renderRelease(Release $release) 49 | { 50 | $date = null; 51 | 52 | if ($release->getDate() !== null) 53 | { 54 | $date = $release->getDate()->format('Y-m-d'); 55 | } 56 | 57 | return [ 58 | 'name' => $release->getName(), 59 | 'link' => $release->getLink(), 60 | 'linkName' => $release->getLinkName(), 61 | 'date' => $date, 62 | 'changes' => $release->getAllChanges(), 63 | ]; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/Renderer/KeepAChangeLog.php: -------------------------------------------------------------------------------- 1 | getTitle()}\n" . 26 | "{$log->getDescription()}\n"; 27 | 28 | $links = ''; 29 | 30 | /** @var Release $release */ 31 | foreach ($log as $release) 32 | { 33 | $content .= $this->renderRelease($release); 34 | $links .= $this->createLink($release); 35 | } 36 | 37 | if ($links !== '') 38 | { 39 | $content .= "\n\n" . $links; 40 | } 41 | 42 | return $content; 43 | } 44 | 45 | /** 46 | * Creates the needed link text for a Release. 47 | * 48 | * @param Release $release 49 | * 50 | * @return string 51 | */ 52 | protected function createLink(Release $release) 53 | { 54 | $line = ''; 55 | 56 | $link = $release->getLink(); 57 | 58 | if ($link !== null) 59 | { 60 | $linkName = $release->getLinkName(); 61 | $name = $release->getName(); 62 | 63 | $reference = ($linkName === null) ? $name : $linkName; 64 | 65 | $line = "[$reference] $link\n"; 66 | } 67 | 68 | return $line; 69 | } 70 | 71 | /** 72 | * Converts a Release into its text representation. 73 | * 74 | * @param Release $release 75 | * 76 | * @return string 77 | */ 78 | public function renderRelease(Release $release) 79 | { 80 | $name = $release->getName(); 81 | 82 | if ($release->getLink() !== null) 83 | { 84 | $name = "[$name]"; 85 | } 86 | 87 | if ($release->getLinkName() !== null) 88 | { 89 | $name .= "[{$release->getLinkName()}]"; 90 | } 91 | 92 | $content = "\n## $name"; 93 | 94 | $content .= $this->addDate($release); 95 | $content .= $this->addYanked($release); 96 | 97 | $content .= "\n"; 98 | 99 | foreach ($release->getAllChanges() as $type => $changes) 100 | { 101 | $content .= $this->renderType($type, $changes); 102 | } 103 | 104 | return substr($content, 0, strlen($content)-1); 105 | } 106 | 107 | /** 108 | * Converts a list of changes with a given type back into text. 109 | * 110 | * @param string $type 111 | * @param array $changes 112 | * 113 | * @return string 114 | */ 115 | public function renderType($type, $changes) 116 | { 117 | $content = ''; 118 | 119 | if (count($changes) > 0) 120 | { 121 | $content = "### $type\n" . 122 | '- ' . implode("\n- ", $changes) . "\n"; 123 | } 124 | 125 | return $content . "\n"; 126 | } 127 | 128 | /** 129 | * Adds the date to a Release title for rendering if the Release has a date. 130 | * 131 | * @param Release $release 132 | * 133 | * @return string 134 | */ 135 | protected function addDate(Release $release) 136 | { 137 | $content = ''; 138 | $date = $release->getDate(); 139 | if ($date !== null) 140 | { 141 | $content = ' - ' . $date->format('Y-m-d'); 142 | } 143 | return $content; 144 | } 145 | /** 146 | * Returns the YANKED tag if needed 147 | * 148 | * @param Release $release 149 | * 150 | * @return string 151 | */ 152 | protected function addYanked(Release $release) 153 | { 154 | $content = ''; 155 | if ($release->isYanked()) 156 | { 157 | $content = ' [YANKED]'; 158 | } 159 | return $content; 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /src/Renderer/Xml.php: -------------------------------------------------------------------------------- 1 | '); 27 | 28 | $xml->addChild('title', $log->getTitle()); 29 | $xml->addChild('description', $log->getDescription()); 30 | 31 | $releases = $xml->addChild('releases'); 32 | 33 | /** @var Release $release */ 34 | foreach ($log as $release) 35 | { 36 | $this->renderRelease($releases, $release); 37 | } 38 | 39 | $xml = $xml->asXML(); 40 | return ($xml !== false) ? $xml : '' ; 41 | } 42 | 43 | /** 44 | * Adds the release's changes to the given xml release node. 45 | * 46 | * @param SimpleXMLElement $releaseNode 47 | * @param Release $release 48 | */ 49 | protected function addChanges(SimpleXMLElement $releaseNode, Release $release) 50 | { 51 | $changesNode = $releaseNode->addChild('changes'); 52 | 53 | foreach ($release->getAllChanges() as $type => $changes) 54 | { 55 | $typeNode = $changesNode->addChild('type'); 56 | $typeNode->addAttribute('name', $type); 57 | 58 | foreach ($changes as $change) 59 | { 60 | $typeNode->addChild('change', $change); 61 | } 62 | } 63 | } 64 | 65 | /** 66 | * @param SimpleXMLElement $releases 67 | * @param Release $release 68 | */ 69 | protected function renderRelease($releases, $release) 70 | { 71 | $releaseNode = $releases->addChild('release'); 72 | $releaseNode->addChild('name', $release->getName()); 73 | $releaseNode->addChild('link', $release->getLink()); 74 | 75 | if ($release->getLinkName() !== null) 76 | { 77 | $releaseNode->addChild('linkName', $release->getLinkName()); 78 | } 79 | 80 | if ($release->getDate() !== null) 81 | { 82 | $releaseNode->addChild('date', 83 | $release->getDate() 84 | ->format('Y-m-d') 85 | ); 86 | } 87 | 88 | $this->addChanges($releaseNode, $release); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/Version.php: -------------------------------------------------------------------------------- 1 | assertEquals(5, $element->getChildrenCount()); 27 | * ``` 28 | * 29 | * Floating-point example: 30 | * ```php 31 | * assertEquals(0.3, $calculator->add(0.1, 0.2), 'Calculator should add the two numbers correctly.', 0.01); 33 | * ``` 34 | * 35 | * @param $expected 36 | * @param $actual 37 | * @param string $message 38 | * @param float $delta 39 | * @see \Codeception\Module\Asserts::assertEquals() 40 | */ 41 | public function assertEquals($expected, $actual, $message = null, $delta = null) { 42 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEquals', func_get_args())); 43 | } 44 | 45 | 46 | /** 47 | * [!] Method is generated. Documentation taken from corresponding module. 48 | * 49 | * Checks that two variables are not equal. If you're comparing floating-point values, 50 | * you can specify the optional "delta" parameter which dictates how great of a precision 51 | * error are you willing to tolerate in order to consider the two values not equal. 52 | * 53 | * Regular example: 54 | * ```php 55 | * assertNotEquals(0, $element->getChildrenCount()); 57 | * ``` 58 | * 59 | * Floating-point example: 60 | * ```php 61 | * assertNotEquals(0.4, $calculator->add(0.1, 0.2), 'Calculator should add the two numbers correctly.', 0.01); 63 | * ``` 64 | * 65 | * @param $expected 66 | * @param $actual 67 | * @param string $message 68 | * @param float $delta 69 | * @see \Codeception\Module\Asserts::assertNotEquals() 70 | */ 71 | public function assertNotEquals($expected, $actual, $message = null, $delta = null) { 72 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotEquals', func_get_args())); 73 | } 74 | 75 | 76 | /** 77 | * [!] Method is generated. Documentation taken from corresponding module. 78 | * 79 | * Checks that two variables are same 80 | * 81 | * @param $expected 82 | * @param $actual 83 | * @param string $message 84 | * @see \Codeception\Module\Asserts::assertSame() 85 | */ 86 | public function assertSame($expected, $actual, $message = null) { 87 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertSame', func_get_args())); 88 | } 89 | 90 | 91 | /** 92 | * [!] Method is generated. Documentation taken from corresponding module. 93 | * 94 | * Checks that two variables are not same 95 | * 96 | * @param $expected 97 | * @param $actual 98 | * @param string $message 99 | * @see \Codeception\Module\Asserts::assertNotSame() 100 | */ 101 | public function assertNotSame($expected, $actual, $message = null) { 102 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotSame', func_get_args())); 103 | } 104 | 105 | 106 | /** 107 | * [!] Method is generated. Documentation taken from corresponding module. 108 | * 109 | * Checks that actual is greater than expected 110 | * 111 | * @param $expected 112 | * @param $actual 113 | * @param string $message 114 | * @see \Codeception\Module\Asserts::assertGreaterThan() 115 | */ 116 | public function assertGreaterThan($expected, $actual, $message = null) { 117 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertGreaterThan', func_get_args())); 118 | } 119 | 120 | 121 | /** 122 | * [!] Method is generated. Documentation taken from corresponding module. 123 | * 124 | * Checks that actual is greater or equal than expected 125 | * 126 | * @param $expected 127 | * @param $actual 128 | * @param string $message 129 | * @see \Codeception\Module\Asserts::assertGreaterThanOrEqual() 130 | */ 131 | public function assertGreaterThanOrEqual($expected, $actual, $message = null) { 132 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertGreaterThanOrEqual', func_get_args())); 133 | } 134 | 135 | 136 | /** 137 | * [!] Method is generated. Documentation taken from corresponding module. 138 | * 139 | * Checks that actual is less than expected 140 | * 141 | * @param $expected 142 | * @param $actual 143 | * @param string $message 144 | * @see \Codeception\Module\Asserts::assertLessThan() 145 | */ 146 | public function assertLessThan($expected, $actual, $message = null) { 147 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertLessThan', func_get_args())); 148 | } 149 | 150 | 151 | /** 152 | * [!] Method is generated. Documentation taken from corresponding module. 153 | * 154 | * Checks that actual is less or equal than expected 155 | * 156 | * @param $expected 157 | * @param $actual 158 | * @param string $message 159 | * @see \Codeception\Module\Asserts::assertLessThanOrEqual() 160 | */ 161 | public function assertLessThanOrEqual($expected, $actual, $message = null) { 162 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertLessThanOrEqual', func_get_args())); 163 | } 164 | 165 | 166 | /** 167 | * [!] Method is generated. Documentation taken from corresponding module. 168 | * 169 | * Checks that haystack contains needle 170 | * 171 | * @param $needle 172 | * @param $haystack 173 | * @param string $message 174 | * @see \Codeception\Module\Asserts::assertContains() 175 | */ 176 | public function assertContains($needle, $haystack, $message = null) { 177 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertContains', func_get_args())); 178 | } 179 | 180 | 181 | /** 182 | * [!] Method is generated. Documentation taken from corresponding module. 183 | * 184 | * Checks that haystack doesn't contain needle. 185 | * 186 | * @param $needle 187 | * @param $haystack 188 | * @param string $message 189 | * @see \Codeception\Module\Asserts::assertNotContains() 190 | */ 191 | public function assertNotContains($needle, $haystack, $message = null) { 192 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotContains', func_get_args())); 193 | } 194 | 195 | 196 | /** 197 | * [!] Method is generated. Documentation taken from corresponding module. 198 | * 199 | * Checks that string match with pattern 200 | * 201 | * @param string $pattern 202 | * @param string $string 203 | * @param string $message 204 | * @see \Codeception\Module\Asserts::assertRegExp() 205 | */ 206 | public function assertRegExp($pattern, $string, $message = null) { 207 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertRegExp', func_get_args())); 208 | } 209 | 210 | 211 | /** 212 | * [!] Method is generated. Documentation taken from corresponding module. 213 | * 214 | * Checks that string not match with pattern 215 | * 216 | * @param string $pattern 217 | * @param string $string 218 | * @param string $message 219 | * @see \Codeception\Module\Asserts::assertNotRegExp() 220 | */ 221 | public function assertNotRegExp($pattern, $string, $message = null) { 222 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotRegExp', func_get_args())); 223 | } 224 | 225 | 226 | /** 227 | * [!] Method is generated. Documentation taken from corresponding module. 228 | * 229 | * Checks that a string starts with the given prefix. 230 | * 231 | * @param string $prefix 232 | * @param string $string 233 | * @param string $message 234 | * @see \Codeception\Module\Asserts::assertStringStartsWith() 235 | */ 236 | public function assertStringStartsWith($prefix, $string, $message = null) { 237 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringStartsWith', func_get_args())); 238 | } 239 | 240 | 241 | /** 242 | * [!] Method is generated. Documentation taken from corresponding module. 243 | * 244 | * Checks that a string doesn't start with the given prefix. 245 | * 246 | * @param string $prefix 247 | * @param string $string 248 | * @param string $message 249 | * @see \Codeception\Module\Asserts::assertStringStartsNotWith() 250 | */ 251 | public function assertStringStartsNotWith($prefix, $string, $message = null) { 252 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringStartsNotWith', func_get_args())); 253 | } 254 | 255 | 256 | /** 257 | * [!] Method is generated. Documentation taken from corresponding module. 258 | * 259 | * Checks that variable is empty. 260 | * 261 | * @param $actual 262 | * @param string $message 263 | * @see \Codeception\Module\Asserts::assertEmpty() 264 | */ 265 | public function assertEmpty($actual, $message = null) { 266 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEmpty', func_get_args())); 267 | } 268 | 269 | 270 | /** 271 | * [!] Method is generated. Documentation taken from corresponding module. 272 | * 273 | * Checks that variable is not empty. 274 | * 275 | * @param $actual 276 | * @param string $message 277 | * @see \Codeception\Module\Asserts::assertNotEmpty() 278 | */ 279 | public function assertNotEmpty($actual, $message = null) { 280 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotEmpty', func_get_args())); 281 | } 282 | 283 | 284 | /** 285 | * [!] Method is generated. Documentation taken from corresponding module. 286 | * 287 | * Checks that variable is NULL 288 | * 289 | * @param $actual 290 | * @param string $message 291 | * @see \Codeception\Module\Asserts::assertNull() 292 | */ 293 | public function assertNull($actual, $message = null) { 294 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNull', func_get_args())); 295 | } 296 | 297 | 298 | /** 299 | * [!] Method is generated. Documentation taken from corresponding module. 300 | * 301 | * Checks that variable is not NULL 302 | * 303 | * @param $actual 304 | * @param string $message 305 | * @see \Codeception\Module\Asserts::assertNotNull() 306 | */ 307 | public function assertNotNull($actual, $message = null) { 308 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotNull', func_get_args())); 309 | } 310 | 311 | 312 | /** 313 | * [!] Method is generated. Documentation taken from corresponding module. 314 | * 315 | * Checks that condition is positive. 316 | * 317 | * @param $condition 318 | * @param string $message 319 | * @see \Codeception\Module\Asserts::assertTrue() 320 | */ 321 | public function assertTrue($condition, $message = null) { 322 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertTrue', func_get_args())); 323 | } 324 | 325 | 326 | /** 327 | * [!] Method is generated. Documentation taken from corresponding module. 328 | * 329 | * Checks that the condition is NOT true (everything but true) 330 | * 331 | * @param $condition 332 | * @param string $message 333 | * @see \Codeception\Module\Asserts::assertNotTrue() 334 | */ 335 | public function assertNotTrue($condition, $message = null) { 336 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotTrue', func_get_args())); 337 | } 338 | 339 | 340 | /** 341 | * [!] Method is generated. Documentation taken from corresponding module. 342 | * 343 | * Checks that condition is negative. 344 | * 345 | * @param $condition 346 | * @param string $message 347 | * @see \Codeception\Module\Asserts::assertFalse() 348 | */ 349 | public function assertFalse($condition, $message = null) { 350 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFalse', func_get_args())); 351 | } 352 | 353 | 354 | /** 355 | * [!] Method is generated. Documentation taken from corresponding module. 356 | * 357 | * Checks that the condition is NOT false (everything but false) 358 | * 359 | * @param $condition 360 | * @param string $message 361 | * @see \Codeception\Module\Asserts::assertNotFalse() 362 | */ 363 | public function assertNotFalse($condition, $message = null) { 364 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotFalse', func_get_args())); 365 | } 366 | 367 | 368 | /** 369 | * [!] Method is generated. Documentation taken from corresponding module. 370 | * 371 | * Checks if file exists 372 | * 373 | * @param string $filename 374 | * @param string $message 375 | * @see \Codeception\Module\Asserts::assertFileExists() 376 | */ 377 | public function assertFileExists($filename, $message = null) { 378 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileExists', func_get_args())); 379 | } 380 | 381 | 382 | /** 383 | * [!] Method is generated. Documentation taken from corresponding module. 384 | * 385 | * Checks if file doesn't exist 386 | * 387 | * @param string $filename 388 | * @param string $message 389 | * @see \Codeception\Module\Asserts::assertFileNotExists() 390 | */ 391 | public function assertFileNotExists($filename, $message = null) { 392 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileNotExists', func_get_args())); 393 | } 394 | 395 | 396 | /** 397 | * [!] Method is generated. Documentation taken from corresponding module. 398 | * 399 | * @param $expected 400 | * @param $actual 401 | * @param $description 402 | * @see \Codeception\Module\Asserts::assertGreaterOrEquals() 403 | */ 404 | public function assertGreaterOrEquals($expected, $actual, $description = null) { 405 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertGreaterOrEquals', func_get_args())); 406 | } 407 | 408 | 409 | /** 410 | * [!] Method is generated. Documentation taken from corresponding module. 411 | * 412 | * @param $expected 413 | * @param $actual 414 | * @param $description 415 | * @see \Codeception\Module\Asserts::assertLessOrEquals() 416 | */ 417 | public function assertLessOrEquals($expected, $actual, $description = null) { 418 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertLessOrEquals', func_get_args())); 419 | } 420 | 421 | 422 | /** 423 | * [!] Method is generated. Documentation taken from corresponding module. 424 | * 425 | * @param $actual 426 | * @param $description 427 | * @see \Codeception\Module\Asserts::assertIsEmpty() 428 | */ 429 | public function assertIsEmpty($actual, $description = null) { 430 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsEmpty', func_get_args())); 431 | } 432 | 433 | 434 | /** 435 | * [!] Method is generated. Documentation taken from corresponding module. 436 | * 437 | * @param $key 438 | * @param $actual 439 | * @param $description 440 | * @see \Codeception\Module\Asserts::assertArrayHasKey() 441 | */ 442 | public function assertArrayHasKey($key, $actual, $description = null) { 443 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertArrayHasKey', func_get_args())); 444 | } 445 | 446 | 447 | /** 448 | * [!] Method is generated. Documentation taken from corresponding module. 449 | * 450 | * @param $key 451 | * @param $actual 452 | * @param $description 453 | * @see \Codeception\Module\Asserts::assertArrayNotHasKey() 454 | */ 455 | public function assertArrayNotHasKey($key, $actual, $description = null) { 456 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertArrayNotHasKey', func_get_args())); 457 | } 458 | 459 | 460 | /** 461 | * [!] Method is generated. Documentation taken from corresponding module. 462 | * 463 | * Checks that array contains subset. 464 | * 465 | * @param array $subset 466 | * @param array $array 467 | * @param bool $strict 468 | * @param string $message 469 | * @see \Codeception\Module\Asserts::assertArraySubset() 470 | */ 471 | public function assertArraySubset($subset, $array, $strict = null, $message = null) { 472 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertArraySubset', func_get_args())); 473 | } 474 | 475 | 476 | /** 477 | * [!] Method is generated. Documentation taken from corresponding module. 478 | * 479 | * @param $expectedCount 480 | * @param $actual 481 | * @param $description 482 | * @see \Codeception\Module\Asserts::assertCount() 483 | */ 484 | public function assertCount($expectedCount, $actual, $description = null) { 485 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertCount', func_get_args())); 486 | } 487 | 488 | 489 | /** 490 | * [!] Method is generated. Documentation taken from corresponding module. 491 | * 492 | * @param $class 493 | * @param $actual 494 | * @param $description 495 | * @see \Codeception\Module\Asserts::assertInstanceOf() 496 | */ 497 | public function assertInstanceOf($class, $actual, $description = null) { 498 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertInstanceOf', func_get_args())); 499 | } 500 | 501 | 502 | /** 503 | * [!] Method is generated. Documentation taken from corresponding module. 504 | * 505 | * @param $class 506 | * @param $actual 507 | * @param $description 508 | * @see \Codeception\Module\Asserts::assertNotInstanceOf() 509 | */ 510 | public function assertNotInstanceOf($class, $actual, $description = null) { 511 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotInstanceOf', func_get_args())); 512 | } 513 | 514 | 515 | /** 516 | * [!] Method is generated. Documentation taken from corresponding module. 517 | * 518 | * @param $type 519 | * @param $actual 520 | * @param $description 521 | * @see \Codeception\Module\Asserts::assertInternalType() 522 | */ 523 | public function assertInternalType($type, $actual, $description = null) { 524 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertInternalType', func_get_args())); 525 | } 526 | 527 | 528 | /** 529 | * [!] Method is generated. Documentation taken from corresponding module. 530 | * 531 | * Fails the test with message. 532 | * 533 | * @param $message 534 | * @see \Codeception\Module\Asserts::fail() 535 | */ 536 | public function fail($message) { 537 | return $this->getScenario()->runStep(new \Codeception\Step\Action('fail', func_get_args())); 538 | } 539 | 540 | 541 | /** 542 | * [!] Method is generated. Documentation taken from corresponding module. 543 | * 544 | * Handles and checks exception called inside callback function. 545 | * Either exception class name or exception instance should be provided. 546 | * 547 | * ```php 548 | * expectException(MyException::class, function() { 550 | * $this->doSomethingBad(); 551 | * }); 552 | * 553 | * $I->expectException(new MyException(), function() { 554 | * $this->doSomethingBad(); 555 | * }); 556 | * ``` 557 | * If you want to check message or exception code, you can pass them with exception instance: 558 | * ```php 559 | * expectException(new MyException("Don't do bad things"), function() { 562 | * $this->doSomethingBad(); 563 | * }); 564 | * ``` 565 | * 566 | * @param $exception string or \Exception 567 | * @param $callback 568 | * 569 | * @deprecated Use expectThrowable instead 570 | * @see \Codeception\Module\Asserts::expectException() 571 | */ 572 | public function expectException($exception, $callback) { 573 | return $this->getScenario()->runStep(new \Codeception\Step\Action('expectException', func_get_args())); 574 | } 575 | 576 | 577 | /** 578 | * [!] Method is generated. Documentation taken from corresponding module. 579 | * 580 | * Handles and checks throwables (Exceptions/Errors) called inside the callback function. 581 | * Either throwable class name or throwable instance should be provided. 582 | * 583 | * ```php 584 | * expectThrowable(MyThrowable::class, function() { 586 | * $this->doSomethingBad(); 587 | * }); 588 | * 589 | * $I->expectThrowable(new MyException(), function() { 590 | * $this->doSomethingBad(); 591 | * }); 592 | * ``` 593 | * If you want to check message or throwable code, you can pass them with throwable instance: 594 | * ```php 595 | * expectThrowable(new MyError("Don't do bad things"), function() { 598 | * $this->doSomethingBad(); 599 | * }); 600 | * ``` 601 | * 602 | * @param $throwable string or \Throwable 603 | * @param $callback 604 | * @see \Codeception\Module\Asserts::expectThrowable() 605 | */ 606 | public function expectThrowable($throwable, $callback) { 607 | return $this->getScenario()->runStep(new \Codeception\Step\Action('expectThrowable', func_get_args())); 608 | } 609 | 610 | 611 | /** 612 | * [!] Method is generated. Documentation taken from corresponding module. 613 | * 614 | * Enters a directory In local filesystem. 615 | * Project root directory is used by default 616 | * 617 | * @param string $path 618 | * @see \Codeception\Module\Filesystem::amInPath() 619 | */ 620 | public function amInPath($path) { 621 | return $this->getScenario()->runStep(new \Codeception\Step\Condition('amInPath', func_get_args())); 622 | } 623 | 624 | 625 | /** 626 | * [!] Method is generated. Documentation taken from corresponding module. 627 | * 628 | * Opens a file and stores it's content. 629 | * 630 | * Usage: 631 | * 632 | * ``` php 633 | * openFile('composer.json'); 635 | * $I->seeInThisFile('codeception/codeception'); 636 | * ?> 637 | * ``` 638 | * 639 | * @param string $filename 640 | * @see \Codeception\Module\Filesystem::openFile() 641 | */ 642 | public function openFile($filename) { 643 | return $this->getScenario()->runStep(new \Codeception\Step\Action('openFile', func_get_args())); 644 | } 645 | 646 | 647 | /** 648 | * [!] Method is generated. Documentation taken from corresponding module. 649 | * 650 | * Deletes a file 651 | * 652 | * ``` php 653 | * deleteFile('composer.lock'); 655 | * ?> 656 | * ``` 657 | * 658 | * @param string $filename 659 | * @see \Codeception\Module\Filesystem::deleteFile() 660 | */ 661 | public function deleteFile($filename) { 662 | return $this->getScenario()->runStep(new \Codeception\Step\Action('deleteFile', func_get_args())); 663 | } 664 | 665 | 666 | /** 667 | * [!] Method is generated. Documentation taken from corresponding module. 668 | * 669 | * Deletes directory with all subdirectories 670 | * 671 | * ``` php 672 | * deleteDir('vendor'); 674 | * ?> 675 | * ``` 676 | * 677 | * @param string $dirname 678 | * @see \Codeception\Module\Filesystem::deleteDir() 679 | */ 680 | public function deleteDir($dirname) { 681 | return $this->getScenario()->runStep(new \Codeception\Step\Action('deleteDir', func_get_args())); 682 | } 683 | 684 | 685 | /** 686 | * [!] Method is generated. Documentation taken from corresponding module. 687 | * 688 | * Copies directory with all contents 689 | * 690 | * ``` php 691 | * copyDir('vendor','old_vendor'); 693 | * ?> 694 | * ``` 695 | * 696 | * @param string $src 697 | * @param string $dst 698 | * @see \Codeception\Module\Filesystem::copyDir() 699 | */ 700 | public function copyDir($src, $dst) { 701 | return $this->getScenario()->runStep(new \Codeception\Step\Action('copyDir', func_get_args())); 702 | } 703 | 704 | 705 | /** 706 | * [!] Method is generated. Documentation taken from corresponding module. 707 | * 708 | * Checks If opened file has `text` in it. 709 | * 710 | * Usage: 711 | * 712 | * ``` php 713 | * openFile('composer.json'); 715 | * $I->seeInThisFile('codeception/codeception'); 716 | * ?> 717 | * ``` 718 | * 719 | * @param string $text 720 | * Conditional Assertion: Test won't be stopped on fail 721 | * @see \Codeception\Module\Filesystem::seeInThisFile() 722 | */ 723 | public function canSeeInThisFile($text) { 724 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInThisFile', func_get_args())); 725 | } 726 | /** 727 | * [!] Method is generated. Documentation taken from corresponding module. 728 | * 729 | * Checks If opened file has `text` in it. 730 | * 731 | * Usage: 732 | * 733 | * ``` php 734 | * openFile('composer.json'); 736 | * $I->seeInThisFile('codeception/codeception'); 737 | * ?> 738 | * ``` 739 | * 740 | * @param string $text 741 | * @see \Codeception\Module\Filesystem::seeInThisFile() 742 | */ 743 | public function seeInThisFile($text) { 744 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInThisFile', func_get_args())); 745 | } 746 | 747 | 748 | /** 749 | * [!] Method is generated. Documentation taken from corresponding module. 750 | * 751 | * Checks If opened file has the `number` of new lines. 752 | * 753 | * Usage: 754 | * 755 | * ``` php 756 | * openFile('composer.json'); 758 | * $I->seeNumberNewLines(5); 759 | * ?> 760 | * ``` 761 | * 762 | * @param int $number New lines 763 | * Conditional Assertion: Test won't be stopped on fail 764 | * @see \Codeception\Module\Filesystem::seeNumberNewLines() 765 | */ 766 | public function canSeeNumberNewLines($number) { 767 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeNumberNewLines', func_get_args())); 768 | } 769 | /** 770 | * [!] Method is generated. Documentation taken from corresponding module. 771 | * 772 | * Checks If opened file has the `number` of new lines. 773 | * 774 | * Usage: 775 | * 776 | * ``` php 777 | * openFile('composer.json'); 779 | * $I->seeNumberNewLines(5); 780 | * ?> 781 | * ``` 782 | * 783 | * @param int $number New lines 784 | * @see \Codeception\Module\Filesystem::seeNumberNewLines() 785 | */ 786 | public function seeNumberNewLines($number) { 787 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeNumberNewLines', func_get_args())); 788 | } 789 | 790 | 791 | /** 792 | * [!] Method is generated. Documentation taken from corresponding module. 793 | * 794 | * Checks that contents of currently opened file matches $regex 795 | * 796 | * @param string $regex 797 | * Conditional Assertion: Test won't be stopped on fail 798 | * @see \Codeception\Module\Filesystem::seeThisFileMatches() 799 | */ 800 | public function canSeeThisFileMatches($regex) { 801 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeThisFileMatches', func_get_args())); 802 | } 803 | /** 804 | * [!] Method is generated. Documentation taken from corresponding module. 805 | * 806 | * Checks that contents of currently opened file matches $regex 807 | * 808 | * @param string $regex 809 | * @see \Codeception\Module\Filesystem::seeThisFileMatches() 810 | */ 811 | public function seeThisFileMatches($regex) { 812 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeThisFileMatches', func_get_args())); 813 | } 814 | 815 | 816 | /** 817 | * [!] Method is generated. Documentation taken from corresponding module. 818 | * 819 | * Checks the strict matching of file contents. 820 | * Unlike `seeInThisFile` will fail if file has something more than expected lines. 821 | * Better to use with HEREDOC strings. 822 | * Matching is done after removing "\r" chars from file content. 823 | * 824 | * ``` php 825 | * openFile('process.pid'); 827 | * $I->seeFileContentsEqual('3192'); 828 | * ?> 829 | * ``` 830 | * 831 | * @param string $text 832 | * Conditional Assertion: Test won't be stopped on fail 833 | * @see \Codeception\Module\Filesystem::seeFileContentsEqual() 834 | */ 835 | public function canSeeFileContentsEqual($text) { 836 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeFileContentsEqual', func_get_args())); 837 | } 838 | /** 839 | * [!] Method is generated. Documentation taken from corresponding module. 840 | * 841 | * Checks the strict matching of file contents. 842 | * Unlike `seeInThisFile` will fail if file has something more than expected lines. 843 | * Better to use with HEREDOC strings. 844 | * Matching is done after removing "\r" chars from file content. 845 | * 846 | * ``` php 847 | * openFile('process.pid'); 849 | * $I->seeFileContentsEqual('3192'); 850 | * ?> 851 | * ``` 852 | * 853 | * @param string $text 854 | * @see \Codeception\Module\Filesystem::seeFileContentsEqual() 855 | */ 856 | public function seeFileContentsEqual($text) { 857 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeFileContentsEqual', func_get_args())); 858 | } 859 | 860 | 861 | /** 862 | * [!] Method is generated. Documentation taken from corresponding module. 863 | * 864 | * Checks If opened file doesn't contain `text` in it 865 | * 866 | * ``` php 867 | * openFile('composer.json'); 869 | * $I->dontSeeInThisFile('codeception/codeception'); 870 | * ?> 871 | * ``` 872 | * 873 | * @param string $text 874 | * Conditional Assertion: Test won't be stopped on fail 875 | * @see \Codeception\Module\Filesystem::dontSeeInThisFile() 876 | */ 877 | public function cantSeeInThisFile($text) { 878 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInThisFile', func_get_args())); 879 | } 880 | /** 881 | * [!] Method is generated. Documentation taken from corresponding module. 882 | * 883 | * Checks If opened file doesn't contain `text` in it 884 | * 885 | * ``` php 886 | * openFile('composer.json'); 888 | * $I->dontSeeInThisFile('codeception/codeception'); 889 | * ?> 890 | * ``` 891 | * 892 | * @param string $text 893 | * @see \Codeception\Module\Filesystem::dontSeeInThisFile() 894 | */ 895 | public function dontSeeInThisFile($text) { 896 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInThisFile', func_get_args())); 897 | } 898 | 899 | 900 | /** 901 | * [!] Method is generated. Documentation taken from corresponding module. 902 | * 903 | * Deletes a file 904 | * @see \Codeception\Module\Filesystem::deleteThisFile() 905 | */ 906 | public function deleteThisFile() { 907 | return $this->getScenario()->runStep(new \Codeception\Step\Action('deleteThisFile', func_get_args())); 908 | } 909 | 910 | 911 | /** 912 | * [!] Method is generated. Documentation taken from corresponding module. 913 | * 914 | * Checks if file exists in path. 915 | * Opens a file when it's exists 916 | * 917 | * ``` php 918 | * seeFileFound('UserModel.php','app/models'); 920 | * ?> 921 | * ``` 922 | * 923 | * @param string $filename 924 | * @param string $path 925 | * Conditional Assertion: Test won't be stopped on fail 926 | * @see \Codeception\Module\Filesystem::seeFileFound() 927 | */ 928 | public function canSeeFileFound($filename, $path = null) { 929 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeFileFound', func_get_args())); 930 | } 931 | /** 932 | * [!] Method is generated. Documentation taken from corresponding module. 933 | * 934 | * Checks if file exists in path. 935 | * Opens a file when it's exists 936 | * 937 | * ``` php 938 | * seeFileFound('UserModel.php','app/models'); 940 | * ?> 941 | * ``` 942 | * 943 | * @param string $filename 944 | * @param string $path 945 | * @see \Codeception\Module\Filesystem::seeFileFound() 946 | */ 947 | public function seeFileFound($filename, $path = null) { 948 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeFileFound', func_get_args())); 949 | } 950 | 951 | 952 | /** 953 | * [!] Method is generated. Documentation taken from corresponding module. 954 | * 955 | * Checks if file does not exist in path 956 | * 957 | * @param string $filename 958 | * @param string $path 959 | * Conditional Assertion: Test won't be stopped on fail 960 | * @see \Codeception\Module\Filesystem::dontSeeFileFound() 961 | */ 962 | public function cantSeeFileFound($filename, $path = null) { 963 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFileFound', func_get_args())); 964 | } 965 | /** 966 | * [!] Method is generated. Documentation taken from corresponding module. 967 | * 968 | * Checks if file does not exist in path 969 | * 970 | * @param string $filename 971 | * @param string $path 972 | * @see \Codeception\Module\Filesystem::dontSeeFileFound() 973 | */ 974 | public function dontSeeFileFound($filename, $path = null) { 975 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeFileFound', func_get_args())); 976 | } 977 | 978 | 979 | /** 980 | * [!] Method is generated. Documentation taken from corresponding module. 981 | * 982 | * Erases directory contents 983 | * 984 | * ``` php 985 | * cleanDir('logs'); 987 | * ?> 988 | * ``` 989 | * 990 | * @param string $dirname 991 | * @see \Codeception\Module\Filesystem::cleanDir() 992 | */ 993 | public function cleanDir($dirname) { 994 | return $this->getScenario()->runStep(new \Codeception\Step\Action('cleanDir', func_get_args())); 995 | } 996 | 997 | 998 | /** 999 | * [!] Method is generated. Documentation taken from corresponding module. 1000 | * 1001 | * Saves contents to file 1002 | * 1003 | * @param string $filename 1004 | * @param string $contents 1005 | * @see \Codeception\Module\Filesystem::writeToFile() 1006 | */ 1007 | public function writeToFile($filename, $contents) { 1008 | return $this->getScenario()->runStep(new \Codeception\Step\Action('writeToFile', func_get_args())); 1009 | } 1010 | } 1011 | -------------------------------------------------------------------------------- /tests/resources/Parser-Json-testRender.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Change Log", 3 | "description": "A log for changes!", 4 | "releases": { 5 | "1.0.0": { 6 | "name": "1.0.0", 7 | "link": "http://fuelphp.com", 8 | "linkName": null, 9 | "date": "2015-01-29", 10 | "changes": { 11 | "Fixed": [ 12 | "fixed 1", 13 | "fixed 2" 14 | ], 15 | "Changed": [ 16 | "changed 1" 17 | ] 18 | } 19 | }, 20 | "0.1.0": { 21 | "name": "0.1.0", 22 | "link": "http://google.com", 23 | "linkName": "foobar", 24 | "date": "2015-01-20", 25 | "changes": { 26 | "Changed": [ 27 | "changed 2" 28 | ] 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/resources/Parser-Xml-testRender.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Change Log 4 | A log for changes! 5 | 6 | 7 | 1.0.0 8 | http://fuelphp.com 9 | 2015-01-29 10 | 11 | 12 | fixed 1 13 | fixed 2 14 | 15 | 16 | changed 1 17 | 18 | 19 | 20 | 21 | 0.1.0 22 | http://google.com 23 | foobar 24 | 2015-01-20 25 | 26 | 27 | changed 2 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /tests/resources/changelog.config.merge.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'default' => [ 13 | 'strategy' => 'File', 14 | 'config' => [ 15 | 'file' => __DIR__.'/changelog.no_unreleased.md', 16 | ], 17 | ], 18 | ], 19 | 'parser' => [ 20 | 'default' => [ 21 | 'strategy' => 'KeepAChangeLog', 22 | ], 23 | ], 24 | 'renderer' => [ 25 | 'default' => [ 26 | 'strategy' => 'json', 27 | ], 28 | ], 29 | 'output' => [ 30 | 'default' => [ 31 | 'strategy' => 'File', 32 | 'config' => [ 33 | 'file' => __DIR__.'/../_output/changelog.json', 34 | ], 35 | ], 36 | ], 37 | ]; 38 | -------------------------------------------------------------------------------- /tests/resources/changelog.config.php: -------------------------------------------------------------------------------- 1 | [ 13 | 'default' => [ 14 | 'strategy' => 'File', 15 | 'config' => [ 16 | 'file' => __DIR__.'/changelog.md', 17 | ], 18 | ], 19 | ], 20 | 'parser' => [ 21 | 'default' => [ 22 | 'strategy' => 'KeepAChangeLog', 23 | ], 24 | ], 25 | 'renderer' => [ 26 | 'default' => [ 27 | 'strategy' => 'Json', 28 | ], 29 | ], 30 | 'output' => [ 31 | 'default' => [ 32 | 'strategy' => 'File', 33 | 'config' => [ 34 | 'file' => __DIR__.'/../_output/changelog.json', 35 | ], 36 | ], 37 | ], 38 | ]; 39 | -------------------------------------------------------------------------------- /tests/resources/changelog.config_missing_unlreleased.php: -------------------------------------------------------------------------------- 1 | [ 13 | 'default' => [ 14 | 'strategy' => 'File', 15 | 'config' => [ 16 | 'file' => __DIR__.'/changelog.no_unreleased.md', 17 | ], 18 | ], 19 | ], 20 | 'parser' => [ 21 | 'default' => [ 22 | 'strategy' => 'KeepAChangeLog', 23 | ], 24 | ], 25 | 'renderer' => [ 26 | 'default' => [ 27 | 'strategy' => 'Json', 28 | ], 29 | ], 30 | 'output' => [ 31 | 'default' => [ 32 | 'strategy' => 'File', 33 | 'config' => [ 34 | 'file' => __DIR__.'/../_output/changelog.json', 35 | ], 36 | ], 37 | ], 38 | ]; 39 | -------------------------------------------------------------------------------- /tests/resources/changelog.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | This is a change test change log 3 | 4 | ## [Unreleased][unreleased] 5 | ### Fixed 6 | - Unreleased fixed issue 7 | 8 | ## [0.0.6] - 2015-01-12 9 | ### Added 10 | - something was added 11 | 12 | ### Changed 13 | - something was changed 14 | 15 | ### Deprecated 16 | - something was deprecated 17 | ### Removed 18 | - something was removed 19 | 20 | ### Fixed 21 | -something was fixed 22 | 23 | ### Security 24 | - security fix 25 | 26 | ## [0.0.5] - 2015-01-11 27 | ### Added 28 | - First add 29 | - Second add 30 | 31 | [unreleased]: http://server.dev/unreleased 32 | [0.0.5]: http://server.dev/0.0.6 33 | [0.0.5]: http://server.dev/0.0.5 34 | -------------------------------------------------------------------------------- /tests/resources/changelog.no_unreleased.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | This is a change test change log 3 | 4 | ## [0.0.6] - 2015-01-12 5 | ### Added 6 | - something was added 7 | 8 | ### Changed 9 | - something was changed 10 | 11 | ### Deprecated 12 | - something was deprecated 13 | ### Removed 14 | - something was removed 15 | 16 | ### Fixed 17 | -something was fixed 18 | 19 | ### Security 20 | - security fix 21 | 22 | ## [0.0.5] - 2015-01-11 23 | ### Added 24 | - First add 25 | - Second add 26 | 27 | [unreleased]: http://server.dev/unreleased 28 | [0.0.5]: http://server.dev/0.0.6 29 | [0.0.5]: http://server.dev/0.0.5 30 | -------------------------------------------------------------------------------- /tests/resources/partial-changelog-1.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | This is a change test change log 3 | 4 | ## [Unreleased][unreleased] 5 | ### Fixed 6 | 7 | - Unreleased fixed issue 1 8 | -------------------------------------------------------------------------------- /tests/resources/partial-changelog-2.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | This is a change test change log 3 | 4 | ## [Unreleased][unreleased] 5 | ### Fixed 6 | 7 | - Unreleased fixed issue 2 8 | -------------------------------------------------------------------------------- /tests/stubs/AbstractCommandStub.php: -------------------------------------------------------------------------------- 1 | 'value', 20 | ]; 21 | 22 | /** 23 | * {@inheritdoc} 24 | */ 25 | public function getContent() 26 | { 27 | return null; 28 | } 29 | 30 | /** 31 | *{@inheritdoc} 32 | */ 33 | public function setContent($content) 34 | { 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /tests/stubs/GetableConstructorStub.php: -------------------------------------------------------------------------------- 1 | one = $config[0]; 20 | $this->two = $config[1]; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /tests/stubs/GitHubReleasesStub.php: -------------------------------------------------------------------------------- 1 | api = $api; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /tests/stubs/GitHubStub.php: -------------------------------------------------------------------------------- 1 | api = $api; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /tests/unit.suite.yml: -------------------------------------------------------------------------------- 1 | # Codeception Test Suite Configuration 2 | 3 | # suite for unit (internal) tests. 4 | class_name: UnitTester 5 | modules: 6 | enabled: [Asserts, UnitHelper, Mockery, Filesystem] 7 | -------------------------------------------------------------------------------- /tests/unit/AbstractIOTest.php: -------------------------------------------------------------------------------- 1 | provider = new AbstractIOStub; 27 | } 28 | 29 | public function testGetConfig() 30 | { 31 | $config = ['foo' => 'bar']; 32 | 33 | $this->provider->setConfig($config); 34 | 35 | $this->assertEquals( 36 | ['foo' => 'bar', 'default' => 'value'], 37 | $this->provider->getConfig() 38 | ); 39 | 40 | $this->assertEquals( 41 | 'bar', 42 | $this->provider->getConfig('foo') 43 | ); 44 | 45 | $this->assertEquals( 46 | 'value', 47 | $this->provider->getConfig('default') 48 | ); 49 | 50 | $this->assertNull( 51 | $this->provider->getConfig('not here') 52 | ); 53 | } 54 | 55 | public function testOverrideDefault() 56 | { 57 | $config = ['default' => 'new value']; 58 | 59 | $this->provider->setConfig($config); 60 | 61 | $this->assertEquals( 62 | $config, 63 | $this->provider->getConfig() 64 | ); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /tests/unit/ChangeLogTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('getContent') 25 | ->once() 26 | ->andReturn($content); 27 | 28 | $parser = Mockery::mock('ChangeLog\ParserInterface'); 29 | $parser->shouldReceive('parse') 30 | ->once() 31 | ->with($content); 32 | 33 | $changeLog = new ChangeLog; 34 | $changeLog->setParser($parser); 35 | $changeLog->setInput($input); 36 | $changeLog->parse(); 37 | } 38 | 39 | public function testWrite() 40 | { 41 | $log = new Log; 42 | $parsed = 'foobar'; 43 | 44 | $renderer = Mockery::mock('ChangeLog\RenderInterface'); 45 | $renderer->shouldReceive('render') 46 | ->once() 47 | ->with($log) 48 | ->andReturn($parsed); 49 | 50 | $output = Mockery::mock('ChangeLog\IOInterface'); 51 | $output->shouldReceive('setContent') 52 | ->once() 53 | ->with($parsed); 54 | 55 | $changeLog = new ChangeLog; 56 | $changeLog->setRenderer($renderer); 57 | $changeLog->setOutput($output); 58 | $changeLog->write($log); 59 | } 60 | 61 | public function testParseWithoutInput() 62 | { 63 | $this->expectException(LogicException::class); 64 | 65 | $parser = Mockery::mock('ChangeLog\ParserInterface'); 66 | $changeLog = new ChangeLog($parser); 67 | $changeLog->parse(); 68 | } 69 | 70 | public function testWriteWithoutInput() 71 | { 72 | $this->expectException(LogicException::class); 73 | 74 | $parser = Mockery::mock('ChangeLog\RenderInterface'); 75 | $changeLog = new ChangeLog(); 76 | $changeLog->setRenderer($parser); 77 | $changeLog->write(new Log); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /tests/unit/Console/AbstractCommandTest.php: -------------------------------------------------------------------------------- 1 | add(new AbstractCommandStub('abstract_command')); 28 | 29 | $command = $application->find('abstract_command'); 30 | $this->commandTester = new CommandTester($command); 31 | } 32 | 33 | public function testExecuteWithNoConfig() 34 | { 35 | $this->expectException(ConfigNotFoundException::class); 36 | 37 | $this->commandTester->execute([ 38 | '--config' => 'this should not exist', 39 | ]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/unit/Console/AddTest.php: -------------------------------------------------------------------------------- 1 | add(new Add('add')); 27 | 28 | $command = $application->find('add'); 29 | $this->commandTester = new CommandTester($command); 30 | } 31 | 32 | public function testAddToExistingRelease() 33 | { 34 | $outputPath = __DIR__ . '/../../_output/changelog.json'; 35 | @unlink($outputPath); 36 | 37 | $this->commandTester->execute([ 38 | 'release' => '0.0.6', 39 | 'type' => 'foobar', 40 | 'change' => 'something', 41 | '--config' => __DIR__.'/../../resources/changelog.config.php', 42 | ]); 43 | 44 | $this->assertFileExists($outputPath); 45 | $jsonContent = json_decode(file_get_contents($outputPath), true); 46 | 47 | $this->assertFalse(empty($jsonContent['releases']['0.0.6']['changes']['foobar'])); 48 | $this->assertTrue(in_array('something', $jsonContent['releases']['0.0.6']['changes']['foobar'])); 49 | } 50 | 51 | public function testAddToNewRelease() 52 | { 53 | $outputPath = __DIR__ . '/../../_output/changelog.json'; 54 | @unlink($outputPath); 55 | 56 | $this->commandTester->execute([ 57 | 'release' => '1.0.0', 58 | 'type' => 'flip-flopped', 59 | 'change' => 'something', 60 | '--config' => __DIR__.'/../../resources/changelog.config.php', 61 | ]); 62 | 63 | $this->assertFileExists($outputPath); 64 | $jsonContent = json_decode(file_get_contents($outputPath), true); 65 | 66 | $this->assertFalse(empty($jsonContent['releases']['1.0.0']['changes']['flip-flopped'])); 67 | $this->assertTrue(in_array('something', $jsonContent['releases']['1.0.0']['changes']['flip-flopped'])); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/unit/Console/ConvertTest.php: -------------------------------------------------------------------------------- 1 | add(new Convert('convert')); 23 | 24 | $command = $application->find('convert'); 25 | $commandTester = new CommandTester($command); 26 | $commandTester->execute(['--config' => __DIR__.'/../../resources/changelog.config.php']); 27 | 28 | $this->assertFileExists($outputPath); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/unit/Console/MergeTest.php: -------------------------------------------------------------------------------- 1 | add(new Merge('merge')); 27 | 28 | $command = $application->find('merge'); 29 | $this->commandTester = new CommandTester($command); 30 | } 31 | 32 | public function testMergeMultipleChangelog() 33 | { 34 | $outputPath = __DIR__.'/../../_output/changelog.json'; 35 | @unlink($outputPath); 36 | 37 | $this->commandTester->execute([ 38 | 'files' => [ 39 | __DIR__.'/../../resources/partial-changelog-1.md', 40 | __DIR__.'/../../resources/partial-changelog-2.md', 41 | ], 42 | '--config' => __DIR__.'/../../resources/changelog.config.merge.php', 43 | ]); 44 | 45 | $this->assertFileExists($outputPath); 46 | $jsonContent = json_decode(file_get_contents($outputPath), true); 47 | 48 | $this->assertCount(2, $jsonContent['releases']['unreleased']['changes']['Fixed']); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/unit/Console/ReleaseTest.php: -------------------------------------------------------------------------------- 1 | add(new Release('release')); 27 | 28 | $command = $application->find('release'); 29 | $this->commandTester = new CommandTester($command); 30 | } 31 | 32 | public function testMissingRelease() 33 | { 34 | $this->expectException(InvalidArgumentException::class); 35 | 36 | $this->commandTester->execute([ 37 | '--config' => __DIR__.'/../../resources/changelog.config.php', 38 | ]); 39 | } 40 | 41 | public function testNoUnreleased() 42 | { 43 | $this->expectException(ReleaseNotFoundException::class); 44 | 45 | $this->commandTester->execute([ 46 | 'release' => 'foobar', 47 | '--config' => __DIR__.'/../../resources/changelog.config_missing_unlreleased.php', 48 | ]); 49 | } 50 | 51 | public function testExistingRelease() 52 | { 53 | $this->expectException(ReleaseExistsException::class); 54 | 55 | $this->commandTester->execute([ 56 | 'release' => '0.0.6', 57 | '--config' => __DIR__.'/../../resources/changelog.config.php', 58 | ]); 59 | } 60 | 61 | public function testRelease() 62 | { 63 | $outputPath = __DIR__ . '/../../_output/changelog.json'; 64 | @unlink($outputPath); 65 | 66 | $this->commandTester->execute([ 67 | 'release' => '1.0.0', 68 | '--config' => __DIR__.'/../../resources/changelog.config.php', 69 | ]); 70 | 71 | $this->assertFileExists($outputPath); 72 | $jsonContent = json_decode(file_get_contents($outputPath), true); 73 | 74 | $this->assertEquals('1.0.0', $jsonContent['releases']['1.0.0']['name']); 75 | $this->assertEquals(null, $jsonContent['releases']['1.0.0']['link']); 76 | $this->assertEquals(null, $jsonContent['releases']['1.0.0']['linkName']); 77 | } 78 | 79 | public function testReleaseWithLink() 80 | { 81 | $outputPath = __DIR__ . '/../../_output/changelog.json'; 82 | @unlink($outputPath); 83 | 84 | $this->commandTester->execute([ 85 | 'release' => '1.0.0', 86 | '--link' => 'http://foobar.com/release/1.0.0', 87 | '--config' => __DIR__.'/../../resources/changelog.config.php', 88 | ]); 89 | 90 | $this->assertFileExists($outputPath); 91 | $jsonContent = json_decode(file_get_contents($outputPath), true); 92 | 93 | $this->assertEquals('1.0.0', $jsonContent['releases']['1.0.0']['name']); 94 | $this->assertEquals('http://foobar.com/release/1.0.0', $jsonContent['releases']['1.0.0']['link']); 95 | $this->assertEquals('1.0.0', $jsonContent['releases']['1.0.0']['linkName']); 96 | } 97 | 98 | public function testReleaseWithName() 99 | { 100 | $outputPath = __DIR__ . '/../../_output/changelog.json'; 101 | @unlink($outputPath); 102 | 103 | $this->commandTester->execute([ 104 | 'release' => '1.0.0', 105 | '--link' => 'http://foobar.com/release/1.0.0', 106 | '--linkName' => 'hello', 107 | '--config' => __DIR__.'/../../resources/changelog.config.php', 108 | ]); 109 | 110 | $this->assertFileExists($outputPath); 111 | $jsonContent = json_decode(file_get_contents($outputPath), true); 112 | 113 | $this->assertEquals('1.0.0', $jsonContent['releases']['1.0.0']['name']); 114 | $this->assertEquals('http://foobar.com/release/1.0.0', $jsonContent['releases']['1.0.0']['link']); 115 | $this->assertEquals('hello', $jsonContent['releases']['1.0.0']['linkName']); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /tests/unit/GenericFactoryTest.php: -------------------------------------------------------------------------------- 1 | factory = new GenericFactory('ChangeLog\IO\\'); 29 | } 30 | 31 | public function testGetInstance() 32 | { 33 | $this->assertInstanceOf( 34 | 'ChangeLog\IO\Native', 35 | $this->factory->getInstance('native') 36 | ); 37 | } 38 | 39 | public function testGetInvalidClass() 40 | { 41 | $this->expectException(InvalidArgumentException::class); 42 | 43 | $this->factory->getInstance('If this works there is something seriously wrong!'); 44 | } 45 | 46 | public function testCustomClass() 47 | { 48 | $class = 'ChangeLog\Stub\AbstractIOStub'; 49 | $name = 'foobar'; 50 | 51 | $this->factory->addClass($name, $class); 52 | 53 | $this->assertInstanceOf( 54 | $class, 55 | $this->factory->getInstance($name) 56 | ); 57 | } 58 | 59 | public function testGetInstanceWithParameters() 60 | { 61 | $one = 'foo'; 62 | $two = 'bar'; 63 | 64 | $class = 'ChangeLog\Stub\GetableConstructorStub'; 65 | $this->factory->addClass('bazbat', $class); 66 | 67 | /** @var GetableConstructorStub $instance */ 68 | $instance = $this->factory->getInstance('bazbat', [$one, $two]); 69 | 70 | $this->assertInstanceOf( 71 | $class, 72 | $instance 73 | ); 74 | 75 | $this->assertEquals( 76 | $one, 77 | $instance->one 78 | ); 79 | 80 | $this->assertEquals( 81 | $two, 82 | $instance->two 83 | ); 84 | } 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /tests/unit/IO/FileTest.php: -------------------------------------------------------------------------------- 1 | file = new File; 28 | } 29 | 30 | public function testLoadFile() 31 | { 32 | $fileLocation = __DIR__ . '/../../_output/hello'; 33 | @unlink ($fileLocation); 34 | 35 | file_put_contents($fileLocation, "hello\n"); 36 | 37 | $this->file->setConfig([ 38 | 'file' => $fileLocation, 39 | ]); 40 | 41 | $this->assertEquals( 42 | ['hello', ''], 43 | $this->file->getContent() 44 | ); 45 | } 46 | 47 | public function testFileNotSet() 48 | { 49 | $this->expectException(InvalidArgumentException::class); 50 | $this->file->getContent(); 51 | } 52 | 53 | public function testSetContent() 54 | { 55 | $content = 'foobar'; 56 | 57 | $file = __DIR__.'/../../_output/IO-File-testSetContent.txt'; 58 | touch($file); 59 | 60 | $this->file->setConfig(['file' => $file]); 61 | $this->file->setContent($content); 62 | 63 | /** @var Filesystem $filesystem */ 64 | $filesystem = $this->getModule('Filesystem'); 65 | $filesystem->openFile($file); 66 | $filesystem->seeInThisFile($content); 67 | $filesystem->deleteFile($file); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/unit/IO/FlysystemTest.php: -------------------------------------------------------------------------------- 1 | file = new Flysystem; 27 | } 28 | 29 | public function testLoadFile() 30 | { 31 | $filesystem = \Mockery::mock('League\Flysystem\Filesystem'); 32 | $filesystem->shouldReceive('read') 33 | ->with('hello.txt') 34 | ->once() 35 | ->andReturn("hello\n"); 36 | 37 | $this->file->setConfig([ 38 | 'file' => 'hello.txt', 39 | 'filesystem' => $filesystem, 40 | ]); 41 | 42 | $this->assertEquals( 43 | ['hello', ''], 44 | $this->file->getContent() 45 | ); 46 | } 47 | 48 | public function testFileNotSet() 49 | { 50 | $this->expectException(InvalidArgumentException::class); 51 | $this->expectExceptionMessage('File not specified.'); 52 | 53 | $filesystem = \Mockery::mock('League\Flysystem\Filesystem'); 54 | $this->file->setConfig([ 55 | 'filesystem' => $filesystem, 56 | ]); 57 | $this->file->getContent(); 58 | } 59 | 60 | public function testFilesystemNotSet() 61 | { 62 | $this->expectException(InvalidArgumentException::class); 63 | $this->expectExceptionMessage('Filesystem object not specified.'); 64 | 65 | $this->file->setConfig([ 66 | 'file' => 'foobar', 67 | ]); 68 | $this->file->getContent(); 69 | } 70 | 71 | public function testSetContent() 72 | { 73 | $content = 'foobar'; 74 | $file = 'hello.txt'; 75 | 76 | $filesystem = \Mockery::mock('League\Flysystem\Filesystem'); 77 | $filesystem->shouldReceive('put') 78 | ->with('hello.txt', $content) 79 | ->once(); 80 | 81 | $this->file->setConfig([ 82 | 'file' => $file, 83 | 'filesystem' => $filesystem, 84 | ]); 85 | 86 | $this->file->setContent($content); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /tests/unit/IO/GitHubReleasesTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('get') 28 | ->with("/repos/$repo/releases") 29 | ->andReturn(true) 30 | ->once(); 31 | 32 | $release1 = new stdClass; 33 | $release1->published_at = '2015-02-26'; 34 | $release1->tag_name = '0.2.0'; 35 | $release1->body = 'content 2'; 36 | $release1->html_url = 'http://release.com/2'; 37 | 38 | $release2 = new stdClass; 39 | $release2->published_at = '2015-02-25'; 40 | $release2->tag_name = '0.1.0'; 41 | $release2->body = 'content 1'; 42 | $release2->html_url = 'http://release.com/1'; 43 | 44 | $releases = [$release1, $release2]; 45 | 46 | $apiMock->shouldReceive('decode') 47 | ->with(true) 48 | ->andReturn($releases) 49 | ->once(); 50 | 51 | $gitHub = new GitHubReleasesStub; 52 | $gitHub->setApi($apiMock); 53 | $gitHub->setConfig([ 54 | 'repo' => $repo, 55 | ]); 56 | 57 | $this->assertEquals( 58 | [ 59 | '# GitHub Releases', 60 | '', 61 | '## [0.2.0] - 2015-02-26', 62 | 'content 2', 63 | '## [0.1.0] - 2015-02-25', 64 | 'content 1', 65 | '', 66 | '[0.2.0]: http://release.com/2', 67 | '[0.1.0]: http://release.com/1', 68 | ], 69 | $gitHub->getContent() 70 | ); 71 | } 72 | 73 | public function testSetContent() 74 | { 75 | $this->expectException(Exception::class); 76 | $this->expectExceptionMessage('This has yet to be implemented.'); 77 | 78 | $gitHub = new GitHubReleasesStub; 79 | $gitHub->setContent('foobar'); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /tests/unit/IO/GitHubTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('get') 29 | ->with("/repos/$repo/contents/$file") 30 | ->andReturn(true) 31 | ->once(); 32 | 33 | $expected = new stdClass; 34 | $content = 'foobar'; 35 | $expected->content = base64_encode($content); 36 | 37 | $apiMock->shouldReceive('decode') 38 | ->with(true) 39 | ->andReturn($expected) 40 | ->once(); 41 | 42 | $gitHub = new GitHubStub; 43 | $gitHub->setApi($apiMock); 44 | $gitHub->setConfig([ 45 | 'repo' => $repo, 46 | 'file' => $file, 47 | ]); 48 | 49 | $this->assertEquals( 50 | [$content], 51 | $gitHub->getContent() 52 | ); 53 | } 54 | 55 | public function testCreateApiWithNoToken() 56 | { 57 | $this->expectException(InvalidArgumentException::class); 58 | 59 | $gitHub = new GitHubStub; 60 | $gitHub->getContent(); 61 | } 62 | 63 | public function testSetContent() 64 | { 65 | $repo = 'foo/bar'; 66 | $file = 'changelog.md'; 67 | $path = "/repos/$repo/contents/$file"; 68 | $message = 'I updated the changelog!'; 69 | $newContent = 'foobarmazbat'; 70 | 71 | $apiMock = Mockery::mock(); 72 | $apiMock->shouldReceive('get') 73 | ->with($path) 74 | ->andReturn(true) 75 | ->once(); 76 | 77 | $expected = new stdClass; 78 | $content = 'foobar'; 79 | $sha = 'asdasd'; 80 | $expected->sha = $sha; 81 | $expected->content = base64_encode($content); 82 | 83 | $apiMock->shouldReceive('decode') 84 | ->with(true) 85 | ->andReturn($expected) 86 | ->twice(); 87 | 88 | $apiMock->shouldReceive('put') 89 | ->with($path, [ 90 | 'message' => $message, 91 | 'content' => base64_encode($newContent), 92 | 'sha' => $sha, 93 | ]) 94 | ->andReturn(true) 95 | ->once(); 96 | 97 | $gitHub = new GitHubStub; 98 | $gitHub->setApi($apiMock); 99 | $gitHub->setConfig([ 100 | 'repo' => $repo, 101 | 'file' => $file, 102 | 'commit_message' => $message, 103 | ]); 104 | 105 | $gitHub->setContent($newContent); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /tests/unit/IO/NativeTest.php: -------------------------------------------------------------------------------- 1 | assertEquals( 24 | $content, 25 | $string->getContent() 26 | ); 27 | 28 | $string = new Native; 29 | $string->setContent("baz\nbat"); 30 | $this->assertEquals( 31 | ['baz', 'bat'], 32 | $string->getContent() 33 | ); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /tests/unit/LogTest.php: -------------------------------------------------------------------------------- 1 | log = new Log; 26 | } 27 | 28 | public function testGetSetDescription() 29 | { 30 | $description = 'A log file that shows changes to the project.'; 31 | $this->log->setDescription($description); 32 | $this->assertEquals( 33 | $description, 34 | $this->log->getDescription() 35 | ); 36 | } 37 | 38 | public function testGetSetTitle() 39 | { 40 | $title = 'My Change Log'; 41 | $this->log->setTitle($title); 42 | $this->assertEquals( 43 | $title, 44 | $this->log->getTitle() 45 | ); 46 | } 47 | 48 | public function testGetSetRelease() 49 | { 50 | $name = '1.0.0'; 51 | $release = new Release; 52 | $release->setName($name); 53 | 54 | $this->assertFalse( 55 | $this->log->hasRelease($name) 56 | ); 57 | $this->assertNull( 58 | $this->log->getRelease($name) 59 | ); 60 | 61 | $this->log->addRelease($release); 62 | $this->assertTrue( 63 | $this->log->hasRelease($name) 64 | ); 65 | $this->assertEquals( 66 | $release, 67 | $this->log->getRelease($name) 68 | ); 69 | $this->assertEquals( 70 | [$name => $release], 71 | $this->log->getReleases() 72 | ); 73 | } 74 | 75 | public function testArrayAccess() 76 | { 77 | $release1 = new Release; 78 | $release1->setName('0.1.0'); 79 | $release2 = new Release; 80 | $release2->setName('0.2.0'); 81 | 82 | $log = new Log; 83 | $log->addRelease($release1); 84 | $log->addRelease($release2); 85 | 86 | $this->assertEquals( 87 | 2, 88 | count($log) 89 | ); 90 | 91 | $releases = []; 92 | /** @var Release $release */ 93 | foreach ($log as $release) 94 | { 95 | $releases[] = $release->getName(); 96 | } 97 | 98 | $this->assertEquals( 99 | ['0.2.0', '0.1.0'], 100 | $releases 101 | ); 102 | } 103 | 104 | public function testRevisionsAreStoredInOrder() 105 | { 106 | $releases = ['2.0.0', '2.1.0', '1.0.0-rc.1', '3.0.0-beta.1']; 107 | $expected = ['3.0.0-beta.1', '2.1.0', '2.0.0', '1.0.0-rc.1']; 108 | 109 | foreach ($releases as $releaseName) 110 | { 111 | $this->log->addRelease(new Release($releaseName)); 112 | } 113 | 114 | $this->assertEquals( 115 | $expected, 116 | array_keys($this->log->getReleases()) 117 | ); 118 | } 119 | 120 | public function testMerge() 121 | { 122 | $thisRelease1 = new Release('1.0.0'); 123 | $thisRelease1->setAllChanges([ 124 | 'Added' => ['Added 1', 'Added 2'], 125 | 'Changed' => ['Changed 1'], 126 | ]); 127 | $this->log->addRelease($thisRelease1); 128 | 129 | $thisRelease1 = new Release('2.0.0'); 130 | $this->log->addRelease($thisRelease1); 131 | 132 | $otherLog = new Log; 133 | 134 | $thisRelease3 = new Release('1.0.0'); 135 | $thisRelease3->setAllChanges([ 136 | 'Added' => ['Added 3', 'Added 4'], 137 | 'Removed' => ['Removed 1'], 138 | ]); 139 | $otherLog->addRelease($thisRelease3); 140 | 141 | $thisRelease4 = new Release('3.0.0'); 142 | $otherLog->addRelease($thisRelease4); 143 | 144 | $this->log->mergeLog($otherLog); 145 | 146 | $this->assertTrue($this->log->hasRelease('1.0.0')); 147 | $this->assertTrue($this->log->hasRelease('2.0.0')); 148 | $this->assertTrue($this->log->hasRelease('3.0.0')); 149 | 150 | $mergedRelease = $this->log->getRelease('1.0.0'); 151 | $this->assertEquals( 152 | [ 153 | 'Added' => ['Added 1', 'Added 2', 'Added 3', 'Added 4'], 154 | 'Changed' => ['Changed 1'], 155 | 'Removed' => ['Removed 1'], 156 | ], 157 | $mergedRelease->getAllChanges() 158 | ); 159 | } 160 | 161 | public function testRemoveRelease() 162 | { 163 | $name = '1.0.0'; 164 | $release = new Release($name); 165 | $this->log->addRelease($release); 166 | 167 | $this->assertTrue( 168 | $this->log->hasRelease($name) 169 | ); 170 | 171 | $this->log->removeRelease($name); 172 | 173 | $this->assertFalse( 174 | $this->log->hasRelease($name) 175 | ); 176 | } 177 | 178 | /** 179 | * @link https://github.com/emlynwest/changelog/issues/15 180 | */ 181 | public function testSortWithUnreleased() 182 | { 183 | $release1 = new Release('0.1.0'); 184 | $release2 = new Release('Unreleased'); 185 | 186 | $this->log->addRelease($release1); 187 | $this->log->addRelease($release2); 188 | 189 | $this->assertEquals( 190 | ['unreleased' => $release2, '0.1.0' => $release1], 191 | $this->log->getReleases() 192 | ); 193 | } 194 | 195 | public function testGetLatestRelease() 196 | { 197 | $release1 = new Release('0.1.0'); 198 | $release2 = new Release('Unreleased'); 199 | 200 | $this->log->addRelease($release1); 201 | $this->log->addRelease($release2); 202 | 203 | $this->assertEquals( 204 | $release1, 205 | $this->log->getLatestRelease() 206 | ); 207 | } 208 | 209 | public function testGetNextVersion() 210 | { 211 | $release1 = new Release('0.1.0'); 212 | 213 | $this->log->addRelease($release1); 214 | 215 | $this->assertEquals( 216 | '0.1.1', 217 | $this->log->getNextVersion(Log::VERSION_PATCH) 218 | ); 219 | 220 | $this->assertEquals( 221 | '0.2.0', 222 | $this->log->getNextVersion(Log::VERSION_MINOR) 223 | ); 224 | 225 | $this->assertEquals( 226 | '1.1.0', 227 | $this->log->getNextVersion(Log::VERSION_MAJOR) 228 | ); 229 | 230 | $this->assertEquals( 231 | 'foobar', 232 | $this->log->getNextVersion('foobar') 233 | ); 234 | } 235 | 236 | } 237 | -------------------------------------------------------------------------------- /tests/unit/Parser/KeepAChangeLogTest.php: -------------------------------------------------------------------------------- 1 | parser = new KeepAChangeLog; 26 | } 27 | 28 | public function testBuildLog() 29 | { 30 | $desc1 = 'This is my description.'; 31 | $desc2 = 'It has multiple lines.'; 32 | $content = [ 33 | '# Change log', 34 | $desc1, 35 | $desc2, 36 | ]; 37 | 38 | $log = $this->parser->parse($content); 39 | 40 | $this->assertInstanceOf( 41 | 'ChangeLog\Log', 42 | $log 43 | ); 44 | 45 | $this->assertEquals( 46 | 'Change log', 47 | $log->getTitle() 48 | ); 49 | 50 | $this->assertEquals( 51 | $desc1."\n".$desc2, 52 | $log->getDescription() 53 | ); 54 | } 55 | 56 | public function testBuildRelease() 57 | { 58 | $content = [ 59 | '## 1.0.0', 60 | '### Fixes', 61 | ' - 1', 62 | '- 2', 63 | '-3', 64 | '', 65 | '### Added', 66 | '- a', 67 | ]; 68 | 69 | $release = $this->parser->parseRelease($content); 70 | 71 | $this->assertInstanceOf( 72 | 'ChangeLog\Release', 73 | $release 74 | ); 75 | 76 | $this->assertEquals( 77 | '1.0.0', 78 | $release->getName() 79 | ); 80 | 81 | $changes = $release->getAllChanges(); 82 | 83 | $this->assertArrayHasKey( 84 | 'Fixes', 85 | $changes 86 | ); 87 | 88 | $this->assertArrayHasKey( 89 | 'Added', 90 | $changes 91 | ); 92 | 93 | $this->assertEquals( 94 | ['1', '2', '3'], 95 | $changes['Fixes'] 96 | ); 97 | 98 | $this->assertEquals( 99 | ['a'], 100 | $changes['Added'] 101 | ); 102 | } 103 | 104 | public function testFullParse() 105 | { 106 | $content = [ 107 | '# Change log', 108 | '', 109 | '## 1.0.0', 110 | '### Fixes', 111 | '- Issue 1', 112 | '- Issue 2', 113 | '', 114 | '## 0.4.0', 115 | '### Adds', 116 | '- Awesome new feature', 117 | ]; 118 | 119 | $log = $this->parser->parse($content); 120 | $this->assertEquals( 121 | 'Change log', 122 | $log->getTitle() 123 | ); 124 | 125 | $this->assertTrue( 126 | $log->hasRelease('1.0.0') 127 | ); 128 | $this->assertTrue( 129 | $log->hasRelease('0.4.0') 130 | ); 131 | } 132 | 133 | public function testDate() 134 | { 135 | $releaseName = '## 1.0.0 - 2015-01-25'; 136 | $content = [$releaseName]; 137 | 138 | $release = $this->parser->parseRelease($content); 139 | 140 | $this->assertEquals( 141 | '2015-01-25', 142 | $release->getDate()->format('Y-m-d') 143 | ); 144 | } 145 | 146 | public function testYanked() 147 | { 148 | $releaseName = '## 1.0.0 - 2015-01-25 [YANKED]'; 149 | $content = [$releaseName]; 150 | 151 | $release = $this->parser->parseRelease($content); 152 | 153 | $this->assertTrue( 154 | $release->isYanked() 155 | ); 156 | 157 | $this->assertEquals( 158 | '2015-01-25', 159 | $release->getDate()->format('Y-m-d') 160 | ); 161 | 162 | $this->assertEquals( 163 | '1.0.0', 164 | $release->getName() 165 | ); 166 | 167 | $releaseName = '## 1.0.0 - 2015-01-25 [yanked]'; 168 | $content = [$releaseName]; 169 | 170 | $release = $this->parser->parseRelease($content); 171 | 172 | $this->assertTrue( 173 | $release->isYanked() 174 | ); 175 | } 176 | 177 | public function testParseLinks() 178 | { 179 | $content = [ 180 | '# Change log', 181 | '', 182 | '## [1.1.0][a]', 183 | '', 184 | '## [1.0.0]', 185 | '', 186 | '[a]: http://google.com', 187 | '[1.0.0]: http://fuelphp.com', 188 | ]; 189 | 190 | $log = $this->parser->parse($content); 191 | 192 | $this->assertEquals( 193 | 'http://google.com', 194 | $log->getRelease('1.1.0')->getLink() 195 | ); 196 | 197 | $this->assertEquals( 198 | 'a', 199 | $log->getRelease('1.1.0')->getLinkName() 200 | ); 201 | 202 | $this->assertEquals( 203 | 'http://fuelphp.com', 204 | $log->getRelease('1.0.0')->getLink() 205 | ); 206 | 207 | $this->assertEmpty($log->getDescription()); 208 | } 209 | 210 | } 211 | -------------------------------------------------------------------------------- /tests/unit/ReleaseTest.php: -------------------------------------------------------------------------------- 1 | release = new Release; 27 | } 28 | 29 | public function testGetSetName() 30 | { 31 | $name = '1.0.0'; 32 | $this->release->setName($name); 33 | $this->assertEquals( 34 | $name, 35 | $this->release->getName() 36 | ); 37 | } 38 | 39 | public function testGetSetYanked() 40 | { 41 | $this->assertFalse( 42 | $this->release->isYanked() 43 | ); 44 | 45 | $this->release->setYanked(true); 46 | 47 | $this->assertTrue( 48 | $this->release->isYanked() 49 | ); 50 | } 51 | 52 | public function testGetSetLink() 53 | { 54 | $link = 'http://google.com'; 55 | $this->release->setLink($link); 56 | $this->assertEquals( 57 | $link, 58 | $this->release->getLink() 59 | ); 60 | } 61 | 62 | public function testGetSetChanges() 63 | { 64 | $fixes = ['#1', '#2', '#3']; 65 | $changes = [ 66 | 'fixed' => $fixes, 67 | 'added' => ['Super awesome feature'] 68 | ]; 69 | 70 | $this->release->setAllChanges($changes); 71 | $this->assertEquals( 72 | $changes, 73 | $this->release->getAllChanges() 74 | ); 75 | 76 | $this->assertEquals( 77 | $fixes, 78 | $this->release->getChanges('fixed') 79 | ); 80 | 81 | $this->assertNull( 82 | $this->release->getChanges('foobar') 83 | ); 84 | 85 | $this->release->addChange('fixed', '#4'); 86 | $this->assertEquals( 87 | array_merge($fixes, ['#4']), 88 | $this->release->getChanges('fixed') 89 | ); 90 | 91 | $this->release->addChange('new', 'foobar'); 92 | $this->assertEquals( 93 | ['foobar'], 94 | $this->release->getChanges('new') 95 | ); 96 | 97 | $newFixes = ['a']; 98 | $this->release->setChanges('fixed', $newFixes); 99 | $this->assertEquals( 100 | $newFixes, 101 | $this->release->getChanges('fixed') 102 | ); 103 | } 104 | 105 | public function testGetSetDate() 106 | { 107 | $date = new DateTime; 108 | 109 | $this->release->setDate($date); 110 | $this->assertEquals( 111 | $date, 112 | $this->release->getDate() 113 | ); 114 | } 115 | 116 | public function testGetSetLinkName() 117 | { 118 | $name = 'abc'; 119 | 120 | $this->release->setLinkName($name); 121 | $this->assertEquals( 122 | $name, 123 | $this->release->getLinkName() 124 | ); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /tests/unit/Renderer/JsonTest.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT http://opensource.org/licenses/MIT 6 | * @link https://github.com/emlynwest/changelog 7 | */ 8 | 9 | namespace ChangeLog\Renderer; 10 | 11 | use ChangeLog\Log; 12 | use ChangeLog\Release; 13 | use Codeception\Test\Unit; 14 | use DateTime; 15 | 16 | /** 17 | * Tests for Json 18 | */ 19 | class JsonTest extends Unit 20 | { 21 | 22 | /** 23 | * @var Json 24 | */ 25 | protected $renderer; 26 | 27 | protected function _before() 28 | { 29 | $this->renderer = new Json; 30 | } 31 | 32 | public function testRender() 33 | { 34 | $log = new Log; 35 | $log->setTitle('Change Log'); 36 | $log->setDescription('A log for changes!'); 37 | 38 | $release1 = new Release('1.0.0'); 39 | $release1->setLink('http://fuelphp.com'); 40 | $release1->setAllChanges([ 41 | 'Fixed' => ['fixed 1', 'fixed 2'], 42 | 'Changed' => ['changed 1'], 43 | ]); 44 | $release1->setDate(DateTime::createFromFormat('Y-m-d', '2015-01-29')); 45 | $log->addRelease($release1); 46 | 47 | $release2 = new Release('0.1.0'); 48 | $release2->setLink('http://google.com'); 49 | $release2->setLinkName('foobar'); 50 | $release2->setDate(DateTime::createFromFormat('Y-m-d', '2015-01-20')); 51 | $release2->setAllChanges([ 52 | 'Changed' => ['changed 2'], 53 | ]); 54 | $log->addRelease($release2); 55 | 56 | $result = $this->renderer->render($log); 57 | $this->assertJsonStringEqualsJsonFile( 58 | __DIR__.'/../../resources/Parser-Json-testRender.json', 59 | $result 60 | ); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /tests/unit/Renderer/KeepAChangeLogTest.php: -------------------------------------------------------------------------------- 1 | renderer = new KeepAChangeLog; 29 | } 30 | 31 | public function testRender() 32 | { 33 | $log = new Log; 34 | $log->setDescription('This is my change log, it will have lots of changes in it!'); 35 | $log->setTitle('My change log'); 36 | 37 | $release1 = new Release('1.0.0'); 38 | $release1->setAllChanges([ 39 | 'Added' => ['Thing 1', 'Thing 2'], 40 | 'Changed' => ['Some change'] 41 | ]); 42 | $log->addRelease($release1); 43 | 44 | $release2 = new Release('0.1.0'); 45 | $release2->setAllChanges([ 46 | 'Added' => ['Initial release'], 47 | ]); 48 | $log->addRelease($release2); 49 | 50 | $expected = implode("\n", [ 51 | '# My change log', 52 | 'This is my change log, it will have lots of changes in it!', 53 | '', 54 | '## 1.0.0', 55 | '### Added', 56 | '- Thing 1', 57 | '- Thing 2', 58 | '', 59 | '### Changed', 60 | '- Some change', 61 | '', 62 | '## 0.1.0', 63 | '### Added', 64 | '- Initial release', 65 | '', 66 | ]); 67 | 68 | $actual = $this->renderer->render($log); 69 | $this->assertEquals( 70 | $expected, 71 | $actual 72 | ); 73 | 74 | } 75 | 76 | public function testDateRender() 77 | { 78 | $release = new Release('1.0.0'); 79 | $release->setDate(DateTime::createFromFormat('Y-m-d', '2015-01-25')); 80 | 81 | $result = $this->renderer->renderRelease($release); 82 | $this->assertEquals( 83 | "\n## 1.0.0 - 2015-01-25", 84 | $result 85 | ); 86 | } 87 | 88 | public function testYankedRender() 89 | { 90 | $release = new Release('1.0.0'); 91 | $release->setYanked(true); 92 | 93 | $result = $this->renderer->renderRelease($release); 94 | $this->assertEquals( 95 | "\n## 1.0.0 [YANKED]", 96 | $result 97 | ); 98 | } 99 | 100 | public function testRenderLink() 101 | { 102 | $log = new Log(); 103 | $log->setTitle('Change log'); 104 | $log->setDescription('My change log'); 105 | 106 | $release1 = new Release('1.0.0'); 107 | $release1->setLink('http://fuelphp.com'); 108 | $log->addRelease($release1); 109 | 110 | $release1 = new Release('0.1.0'); 111 | $release1->setLink('http://google.com'); 112 | $release1->setLinkName('a'); 113 | $log->addRelease($release1); 114 | 115 | $expected = implode("\n", [ 116 | '# Change log', 117 | 'My change log', 118 | '', 119 | '## [1.0.0]', 120 | '## [0.1.0][a]', 121 | '', 122 | '[1.0.0] http://fuelphp.com', 123 | '[a] http://google.com', 124 | '', 125 | ]); 126 | 127 | $result = $this->renderer->render($log); 128 | 129 | $this->assertEquals( 130 | $expected, 131 | $result 132 | ); 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /tests/unit/Renderer/XmlTest.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT http://opensource.org/licenses/MIT 6 | * @link https://github.com/emlynwest/changelog 7 | */ 8 | 9 | namespace ChangeLog\Renderer; 10 | 11 | use ChangeLog\Log; 12 | use ChangeLog\Release; 13 | use Codeception\Test\Unit; 14 | use DateTime; 15 | 16 | /** 17 | * Tests for Xml 18 | */ 19 | class XmlTest extends Unit 20 | { 21 | 22 | /** 23 | * @var Json 24 | */ 25 | protected $renderer; 26 | 27 | protected function _before() 28 | { 29 | $this->renderer = new Xml; 30 | } 31 | 32 | public function testRender() 33 | { 34 | $log = new Log; 35 | $log->setTitle('Change Log'); 36 | $log->setDescription('A log for changes!'); 37 | 38 | $release1 = new Release('1.0.0'); 39 | $release1->setLink('http://fuelphp.com'); 40 | $release1->setAllChanges([ 41 | 'Fixed' => ['fixed 1', 'fixed 2'], 42 | 'Changed' => ['changed 1'], 43 | ]); 44 | $release1->setDate(DateTime::createFromFormat('Y-m-d', '2015-01-29')); 45 | $log->addRelease($release1); 46 | 47 | $release2 = new Release('0.1.0'); 48 | $release2->setLink('http://google.com'); 49 | $release2->setLinkName('foobar'); 50 | $release2->setDate(DateTime::createFromFormat('Y-m-d', '2015-01-20')); 51 | $release2->setAllChanges([ 52 | 'Changed' => ['changed 2'], 53 | ]); 54 | $log->addRelease($release2); 55 | 56 | $result = $this->renderer->render($log); 57 | $this->assertXmlStringEqualsXmlFile( 58 | __DIR__.'/../../resources/Parser-Xml-testRender.xml', 59 | $result 60 | ); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /tests/unit/UnitTester.php: -------------------------------------------------------------------------------- 1 | scenario->runStep(new \Codeception\Step\Action('assertEquals', func_get_args())); 45 | } 46 | 47 | 48 | /** 49 | * [!] Method is generated. Documentation taken from corresponding module. 50 | * 51 | * Checks that two variables are not equal 52 | * 53 | * @param $expected 54 | * @param $actual 55 | * @param string $message 56 | * @see \Codeception\Module\Asserts::assertNotEquals() 57 | */ 58 | public function assertNotEquals($expected, $actual, $message = null) { 59 | return $this->scenario->runStep(new \Codeception\Step\Action('assertNotEquals', func_get_args())); 60 | } 61 | 62 | 63 | /** 64 | * [!] Method is generated. Documentation taken from corresponding module. 65 | * 66 | * Checks that expected is greater than actual 67 | * 68 | * @param $expected 69 | * @param $actual 70 | * @param string $message 71 | * @see \Codeception\Module\Asserts::assertGreaterThan() 72 | */ 73 | public function assertGreaterThan($expected, $actual, $message = null) { 74 | return $this->scenario->runStep(new \Codeception\Step\Action('assertGreaterThan', func_get_args())); 75 | } 76 | 77 | 78 | /** 79 | * [!] Method is generated. Documentation taken from corresponding module. 80 | * 81 | * @deprecated 82 | * @see \Codeception\Module\Asserts::assertGreaterThen() 83 | */ 84 | public function assertGreaterThen($expected, $actual, $message = null) { 85 | return $this->scenario->runStep(new \Codeception\Step\Action('assertGreaterThen', func_get_args())); 86 | } 87 | 88 | 89 | /** 90 | * [!] Method is generated. Documentation taken from corresponding module. 91 | * 92 | * Checks that expected is greater or equal than actual 93 | * 94 | * @param $expected 95 | * @param $actual 96 | * @param string $message 97 | * @see \Codeception\Module\Asserts::assertGreaterThanOrEqual() 98 | */ 99 | public function assertGreaterThanOrEqual($expected, $actual, $message = null) { 100 | return $this->scenario->runStep(new \Codeception\Step\Action('assertGreaterThanOrEqual', func_get_args())); 101 | } 102 | 103 | 104 | /** 105 | * [!] Method is generated. Documentation taken from corresponding module. 106 | * 107 | * @deprecated 108 | * @see \Codeception\Module\Asserts::assertGreaterThenOrEqual() 109 | */ 110 | public function assertGreaterThenOrEqual($expected, $actual, $message = null) { 111 | return $this->scenario->runStep(new \Codeception\Step\Action('assertGreaterThenOrEqual', func_get_args())); 112 | } 113 | 114 | 115 | /** 116 | * [!] Method is generated. Documentation taken from corresponding module. 117 | * 118 | * Checks that expected is less than actual 119 | * 120 | * @param $expected 121 | * @param $actual 122 | * @param string $message 123 | * @see \Codeception\Module\Asserts::assertLessThan() 124 | */ 125 | public function assertLessThan($expected, $actual, $message = null) { 126 | return $this->scenario->runStep(new \Codeception\Step\Action('assertLessThan', func_get_args())); 127 | } 128 | 129 | 130 | /** 131 | * [!] Method is generated. Documentation taken from corresponding module. 132 | * 133 | * Checks that expected is less or equal than actual 134 | * 135 | * @param $expected 136 | * @param $actual 137 | * @param string $message 138 | * @see \Codeception\Module\Asserts::assertLessThanOrEqual() 139 | */ 140 | public function assertLessThanOrEqual($expected, $actual, $message = null) { 141 | return $this->scenario->runStep(new \Codeception\Step\Action('assertLessThanOrEqual', func_get_args())); 142 | } 143 | 144 | 145 | /** 146 | * [!] Method is generated. Documentation taken from corresponding module. 147 | * 148 | * Checks that haystack contains needle 149 | * 150 | * @param $needle 151 | * @param $haystack 152 | * @param string $message 153 | * @see \Codeception\Module\Asserts::assertContains() 154 | */ 155 | public function assertContains($needle, $haystack, $message = null) { 156 | return $this->scenario->runStep(new \Codeception\Step\Action('assertContains', func_get_args())); 157 | } 158 | 159 | 160 | /** 161 | * [!] Method is generated. Documentation taken from corresponding module. 162 | * 163 | * Checks that haystack doesn't contain needle. 164 | * 165 | * @param $needle 166 | * @param $haystack 167 | * @param string $message 168 | * @see \Codeception\Module\Asserts::assertNotContains() 169 | */ 170 | public function assertNotContains($needle, $haystack, $message = null) { 171 | return $this->scenario->runStep(new \Codeception\Step\Action('assertNotContains', func_get_args())); 172 | } 173 | 174 | 175 | /** 176 | * [!] Method is generated. Documentation taken from corresponding module. 177 | * 178 | * Checks that variable is empty. 179 | * 180 | * @param $actual 181 | * @param string $message 182 | * @see \Codeception\Module\Asserts::assertEmpty() 183 | */ 184 | public function assertEmpty($actual, $message = null) { 185 | return $this->scenario->runStep(new \Codeception\Step\Action('assertEmpty', func_get_args())); 186 | } 187 | 188 | 189 | /** 190 | * [!] Method is generated. Documentation taken from corresponding module. 191 | * 192 | * Checks that variable is not empty. 193 | * 194 | * @param $actual 195 | * @param string $message 196 | * @see \Codeception\Module\Asserts::assertNotEmpty() 197 | */ 198 | public function assertNotEmpty($actual, $message = null) { 199 | return $this->scenario->runStep(new \Codeception\Step\Action('assertNotEmpty', func_get_args())); 200 | } 201 | 202 | 203 | /** 204 | * [!] Method is generated. Documentation taken from corresponding module. 205 | * 206 | * Checks that variable is NULL 207 | * 208 | * @param $actual 209 | * @param string $message 210 | * @see \Codeception\Module\Asserts::assertNull() 211 | */ 212 | public function assertNull($actual, $message = null) { 213 | return $this->scenario->runStep(new \Codeception\Step\Action('assertNull', func_get_args())); 214 | } 215 | 216 | 217 | /** 218 | * [!] Method is generated. Documentation taken from corresponding module. 219 | * 220 | * Checks that variable is not NULL 221 | * 222 | * @param $actual 223 | * @param string $message 224 | * @see \Codeception\Module\Asserts::assertNotNull() 225 | */ 226 | public function assertNotNull($actual, $message = null) { 227 | return $this->scenario->runStep(new \Codeception\Step\Action('assertNotNull', func_get_args())); 228 | } 229 | 230 | 231 | /** 232 | * [!] Method is generated. Documentation taken from corresponding module. 233 | * 234 | * Checks that condition is positive. 235 | * 236 | * @param $condition 237 | * @param string $message 238 | * @see \Codeception\Module\Asserts::assertTrue() 239 | */ 240 | public function assertTrue($condition, $message = null) { 241 | return $this->scenario->runStep(new \Codeception\Step\Action('assertTrue', func_get_args())); 242 | } 243 | 244 | 245 | /** 246 | * [!] Method is generated. Documentation taken from corresponding module. 247 | * 248 | * Checks that condition is negative. 249 | * 250 | * @param $condition 251 | * @param string $message 252 | * @see \Codeception\Module\Asserts::assertFalse() 253 | */ 254 | public function assertFalse($condition, $message = null) { 255 | return $this->scenario->runStep(new \Codeception\Step\Action('assertFalse', func_get_args())); 256 | } 257 | 258 | 259 | /** 260 | * [!] Method is generated. Documentation taken from corresponding module. 261 | * 262 | * Fails the test with message. 263 | * 264 | * @param $message 265 | * @see \Codeception\Module\Asserts::fail() 266 | */ 267 | public function fail($message) { 268 | return $this->scenario->runStep(new \Codeception\Step\Action('fail', func_get_args())); 269 | } 270 | 271 | 272 | /** 273 | * [!] Method is generated. Documentation taken from corresponding module. 274 | * 275 | * Enters a directory In local filesystem. 276 | * Project root directory is used by default 277 | * 278 | * @param $path 279 | * @see \Codeception\Module\Filesystem::amInPath() 280 | */ 281 | public function amInPath($path) { 282 | return $this->scenario->runStep(new \Codeception\Step\Condition('amInPath', func_get_args())); 283 | } 284 | 285 | 286 | /** 287 | * [!] Method is generated. Documentation taken from corresponding module. 288 | * 289 | * Opens a file and stores it's content. 290 | * 291 | * Usage: 292 | * 293 | * ``` php 294 | * openFile('composer.json'); 296 | * $I->seeInThisFile('codeception/codeception'); 297 | * ?> 298 | * ``` 299 | * 300 | * @param $filename 301 | * @see \Codeception\Module\Filesystem::openFile() 302 | */ 303 | public function openFile($filename) { 304 | return $this->scenario->runStep(new \Codeception\Step\Action('openFile', func_get_args())); 305 | } 306 | 307 | 308 | /** 309 | * [!] Method is generated. Documentation taken from corresponding module. 310 | * 311 | * Deletes a file 312 | * 313 | * ``` php 314 | * deleteFile('composer.lock'); 316 | * ?> 317 | * ``` 318 | * 319 | * @param $filename 320 | * @see \Codeception\Module\Filesystem::deleteFile() 321 | */ 322 | public function deleteFile($filename) { 323 | return $this->scenario->runStep(new \Codeception\Step\Action('deleteFile', func_get_args())); 324 | } 325 | 326 | 327 | /** 328 | * [!] Method is generated. Documentation taken from corresponding module. 329 | * 330 | * Deletes directory with all subdirectories 331 | * 332 | * ``` php 333 | * deleteDir('vendor'); 335 | * ?> 336 | * ``` 337 | * 338 | * @param $dirname 339 | * @see \Codeception\Module\Filesystem::deleteDir() 340 | */ 341 | public function deleteDir($dirname) { 342 | return $this->scenario->runStep(new \Codeception\Step\Action('deleteDir', func_get_args())); 343 | } 344 | 345 | 346 | /** 347 | * [!] Method is generated. Documentation taken from corresponding module. 348 | * 349 | * Copies directory with all contents 350 | * 351 | * ``` php 352 | * copyDir('vendor','old_vendor'); 354 | * ?> 355 | * ``` 356 | * 357 | * @param $src 358 | * @param $dst 359 | * @see \Codeception\Module\Filesystem::copyDir() 360 | */ 361 | public function copyDir($src, $dst) { 362 | return $this->scenario->runStep(new \Codeception\Step\Action('copyDir', func_get_args())); 363 | } 364 | 365 | 366 | /** 367 | * [!] Method is generated. Documentation taken from corresponding module. 368 | * 369 | * Checks If opened file has `text` in it. 370 | * 371 | * Usage: 372 | * 373 | * ``` php 374 | * openFile('composer.json'); 376 | * $I->seeInThisFile('codeception/codeception'); 377 | * ?> 378 | * ``` 379 | * 380 | * @param $text 381 | * Conditional Assertion: Test won't be stopped on fail 382 | * @see \Codeception\Module\Filesystem::seeInThisFile() 383 | */ 384 | public function canSeeInThisFile($text) { 385 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInThisFile', func_get_args())); 386 | } 387 | /** 388 | * [!] Method is generated. Documentation taken from corresponding module. 389 | * 390 | * Checks If opened file has `text` in it. 391 | * 392 | * Usage: 393 | * 394 | * ``` php 395 | * openFile('composer.json'); 397 | * $I->seeInThisFile('codeception/codeception'); 398 | * ?> 399 | * ``` 400 | * 401 | * @param $text 402 | * @see \Codeception\Module\Filesystem::seeInThisFile() 403 | */ 404 | public function seeInThisFile($text) { 405 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInThisFile', func_get_args())); 406 | } 407 | 408 | 409 | /** 410 | * [!] Method is generated. Documentation taken from corresponding module. 411 | * 412 | * Checks the strict matching of file contents. 413 | * Unlike `seeInThisFile` will fail if file has something more than expected lines. 414 | * Better to use with HEREDOC strings. 415 | * Matching is done after removing "\r" chars from file content. 416 | * 417 | * ``` php 418 | * openFile('process.pid'); 420 | * $I->seeFileContentsEqual('3192'); 421 | * ?> 422 | * ``` 423 | * 424 | * @param $text 425 | * Conditional Assertion: Test won't be stopped on fail 426 | * @see \Codeception\Module\Filesystem::seeFileContentsEqual() 427 | */ 428 | public function canSeeFileContentsEqual($text) { 429 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeFileContentsEqual', func_get_args())); 430 | } 431 | /** 432 | * [!] Method is generated. Documentation taken from corresponding module. 433 | * 434 | * Checks the strict matching of file contents. 435 | * Unlike `seeInThisFile` will fail if file has something more than expected lines. 436 | * Better to use with HEREDOC strings. 437 | * Matching is done after removing "\r" chars from file content. 438 | * 439 | * ``` php 440 | * openFile('process.pid'); 442 | * $I->seeFileContentsEqual('3192'); 443 | * ?> 444 | * ``` 445 | * 446 | * @param $text 447 | * @see \Codeception\Module\Filesystem::seeFileContentsEqual() 448 | */ 449 | public function seeFileContentsEqual($text) { 450 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeFileContentsEqual', func_get_args())); 451 | } 452 | 453 | 454 | /** 455 | * [!] Method is generated. Documentation taken from corresponding module. 456 | * 457 | * Checks If opened file doesn't contain `text` in it 458 | * 459 | * ``` php 460 | * openFile('composer.json'); 462 | * $I->dontSeeInThisFile('codeception/codeception'); 463 | * ?> 464 | * ``` 465 | * 466 | * @param $text 467 | * Conditional Assertion: Test won't be stopped on fail 468 | * @see \Codeception\Module\Filesystem::dontSeeInThisFile() 469 | */ 470 | public function cantSeeInThisFile($text) { 471 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInThisFile', func_get_args())); 472 | } 473 | /** 474 | * [!] Method is generated. Documentation taken from corresponding module. 475 | * 476 | * Checks If opened file doesn't contain `text` in it 477 | * 478 | * ``` php 479 | * openFile('composer.json'); 481 | * $I->dontSeeInThisFile('codeception/codeception'); 482 | * ?> 483 | * ``` 484 | * 485 | * @param $text 486 | * @see \Codeception\Module\Filesystem::dontSeeInThisFile() 487 | */ 488 | public function dontSeeInThisFile($text) { 489 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInThisFile', func_get_args())); 490 | } 491 | 492 | 493 | /** 494 | * [!] Method is generated. Documentation taken from corresponding module. 495 | * 496 | * Deletes a file 497 | * @see \Codeception\Module\Filesystem::deleteThisFile() 498 | */ 499 | public function deleteThisFile() { 500 | return $this->scenario->runStep(new \Codeception\Step\Action('deleteThisFile', func_get_args())); 501 | } 502 | 503 | 504 | /** 505 | * [!] Method is generated. Documentation taken from corresponding module. 506 | * 507 | * Checks if file exists in path. 508 | * Opens a file when it's exists 509 | * 510 | * ``` php 511 | * seeFileFound('UserModel.php','app/models'); 513 | * ?> 514 | * ``` 515 | * 516 | * @param $filename 517 | * @param string $path 518 | * Conditional Assertion: Test won't be stopped on fail 519 | * @see \Codeception\Module\Filesystem::seeFileFound() 520 | */ 521 | public function canSeeFileFound($filename, $path = null) { 522 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeFileFound', func_get_args())); 523 | } 524 | /** 525 | * [!] Method is generated. Documentation taken from corresponding module. 526 | * 527 | * Checks if file exists in path. 528 | * Opens a file when it's exists 529 | * 530 | * ``` php 531 | * seeFileFound('UserModel.php','app/models'); 533 | * ?> 534 | * ``` 535 | * 536 | * @param $filename 537 | * @param string $path 538 | * @see \Codeception\Module\Filesystem::seeFileFound() 539 | */ 540 | public function seeFileFound($filename, $path = null) { 541 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeFileFound', func_get_args())); 542 | } 543 | 544 | 545 | /** 546 | * [!] Method is generated. Documentation taken from corresponding module. 547 | * 548 | * Checks if file does not exists in path 549 | * 550 | * @param $filename 551 | * @param string $path 552 | * Conditional Assertion: Test won't be stopped on fail 553 | * @see \Codeception\Module\Filesystem::dontSeeFileFound() 554 | */ 555 | public function cantSeeFileFound($filename, $path = null) { 556 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFileFound', func_get_args())); 557 | } 558 | /** 559 | * [!] Method is generated. Documentation taken from corresponding module. 560 | * 561 | * Checks if file does not exists in path 562 | * 563 | * @param $filename 564 | * @param string $path 565 | * @see \Codeception\Module\Filesystem::dontSeeFileFound() 566 | */ 567 | public function dontSeeFileFound($filename, $path = null) { 568 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeFileFound', func_get_args())); 569 | } 570 | 571 | 572 | /** 573 | * [!] Method is generated. Documentation taken from corresponding module. 574 | * 575 | * Erases directory contents 576 | * 577 | * ``` php 578 | * cleanDir('logs'); 580 | * ?> 581 | * ``` 582 | * 583 | * @param $dirname 584 | * @see \Codeception\Module\Filesystem::cleanDir() 585 | */ 586 | public function cleanDir($dirname) { 587 | return $this->scenario->runStep(new \Codeception\Step\Action('cleanDir', func_get_args())); 588 | } 589 | 590 | 591 | /** 592 | * [!] Method is generated. Documentation taken from corresponding module. 593 | * 594 | * Saves contents to file 595 | * 596 | * @param $filename 597 | * @param $contents 598 | * @see \Codeception\Module\Filesystem::writeToFile() 599 | */ 600 | public function writeToFile($filename, $contents) { 601 | return $this->scenario->runStep(new \Codeception\Step\Action('writeToFile', func_get_args())); 602 | } 603 | } 604 | -------------------------------------------------------------------------------- /tests/unit/_bootstrap.php: -------------------------------------------------------------------------------- 1 |