diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 24f16a7..3801a46 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,33 @@ on: - 'master' jobs: - build: + build71: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: shivammathur/setup-php@v2 + with: + php-version: '7.1.3' + - name: Install + run: composer install + - name: Install + run: composer validate + - name: Test + run: composer test + build72: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: shivammathur/setup-php@v2 + with: + php-version: '7.2' + - name: Install + run: composer install + - name: Install + run: composer validate + - name: Test + run: composer test + build73: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 @@ -20,3 +46,31 @@ jobs: run: composer install - name: Install run: composer validate + - name: Test + run: composer test + build74: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + - name: Install + run: composer install + - name: Install + run: composer validate + - name: Test + run: composer test + build80: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + - name: Install + run: composer install + - name: Install + run: composer validate + - name: Test + run: composer test diff --git a/.gitignore b/.gitignore index 7461c28..404ae0d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ vendor/* composer.phar composer.lock +/build/ +.phpunit.result.cache + diff --git a/codeception.yml b/codeception.yml new file mode 100644 index 0000000..9db5f91 --- /dev/null +++ b/codeception.yml @@ -0,0 +1,21 @@ +namespace: Yandex\Allure\Codeception +suites: + unit: + path: . + +settings: + lint: true +paths: + tests: test/codeception + output: build + support: test/codeception/_support + data: test/codeception +extensions: + enabled: + - Yandex\Allure\Codeception\AllureCodeception + config: + Yandex\Allure\Codeception\AllureCodeception: + deletePreviousResults: true + outputDirectory: allure-results + + diff --git a/composer.json b/composer.json index 39d2f8c..f49be70 100644 --- a/composer.json +++ b/composer.json @@ -16,15 +16,37 @@ "source": "https://github.com/allure-framework/allure-codeception" }, "require": { - "php": ">=5.6", - "codeception/codeception": "^2.3|^3.0|^4.0", - "allure-framework/allure-php-api": "~1.2.1", - "symfony/filesystem": ">=2.6", - "symfony/finder": ">=2.6" + "php": ">=7.1.3", + "ext-json": "*", + "codeception/codeception": "^2.5 | ^3 | ^4", + "allure-framework/allure-php-api": "^1.3", + "symfony/filesystem": "^2.7 | ^3 | ^4 | ^5", + "symfony/finder": "^2.7 | ^3 | ^4 | ^5" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^7.2 | ^8 | ^9" }, "autoload": { "psr-0": { "Yandex": "src/" } + }, + "autoload-dev": { + "psr-4": { + "Yandex\\Allure\\Codeception\\": [ + "test/report/", + "test/unit/" + ] + } + }, + "scripts": { + "test-report": [ + "vendor/bin/codecept run --no-exit --report", + "vendor/bin/phpunit --testsuite=report" + ], + "test": [ + "@test-report" + ] } } diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..dcb4000 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,12 @@ + + + + + test/report/ + + + diff --git a/test/codeception/AnnotationTest.php b/test/codeception/AnnotationTest.php new file mode 100644 index 0000000..373be55 --- /dev/null +++ b/test/codeception/AnnotationTest.php @@ -0,0 +1,68 @@ +expectNotToPerformAssertions(); + } + + /** + * @Description ("Test description with `markdown`", type = DescriptionType::MARKDOWN) + */ + public function testDescriptionAnnotation(): void + { + $this->expectNotToPerformAssertions(); + } + + /** + * @Severity (level = SeverityLevel::MINOR) + */ + public function testSeverityAnnotation(): void + { + $this->expectNotToPerformAssertions(); + } + + /** + * @Parameter (name = "foo", value = "bar", kind = ParameterKind::ARGUMENT) + */ + public function testParameterAnnotation(): void + { + $this->expectNotToPerformAssertions(); + } + + /** + * @Stories ("Story 1", "Story 2") + */ + public function testStoriesAnnotation(): void + { + $this->expectNotToPerformAssertions(); + } + + /** + * @Features ("Feature 1", "Feature 2") + */ + public function testFeaturesAnnotation(): void + { + $this->expectNotToPerformAssertions(); + } +} \ No newline at end of file diff --git a/test/codeception/StepsTest.php b/test/codeception/StepsTest.php new file mode 100644 index 0000000..88f47d1 --- /dev/null +++ b/test/codeception/StepsTest.php @@ -0,0 +1,96 @@ +expectNotToPerformAssertions(); + } + + /** + * @throws Exception + */ + public function testNoStepsError(): void + { + throw new Exception('Error'); + } + + public function testNoStepsFailure(): void + { + self::fail('Failure'); + } + + public function testNoStepsSkipped(): void + { + self::markTestSkipped('Skipped'); + } + + public function testSingleSuccessfulStepWithTitle(): void + { + $this->expectNotToPerformAssertions(); + $scenario = new Scenario($this); + $scenario->runStep(new Comment('Step 1 name')); + } + + public function testTwoSuccessfulSteps(): void + { + $this->expectNotToPerformAssertions(); + + $scenario = new Scenario($this); + $scenario->runStep(new Comment('Step 1 name')); + $scenario->runStep(new Comment('Step 2 name')); + } + + public function testTwoStepsFirstFails(): void + { + $this->expectNotToPerformAssertions(); + + $scenario = new Scenario($this); + $scenario->runStep($this->createFailingStep('Step 1 name', 'Failure')); + $scenario->runStep(new Comment('Step 2 name')); + } + + public function testTwoStepsSecondFails(): void + { + $this->expectNotToPerformAssertions(); + + $scenario = new Scenario($this); + $scenario->runStep(new Comment('Step 1 name')); + $scenario->runStep($this->createFailingStep('Step 2 name', 'Failure')); + } + + private function createFailingStep(string $name, string $failure): \Codeception\Step + { + return new class ($failure, $name) extends Meta { + + private $failure; + + public function __construct(string $failure, $action, array $arguments = []) + { + parent::__construct($action, $arguments); + $this->failure = $failure; + } + + public function run(ModuleContainer $container = null) + { + $this->setFailed(true); + Assert::fail($this->failure); + } + }; + } +} diff --git a/test/codeception/_support/.gitkeep b/test/codeception/_support/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/report/ReportTest.php b/test/report/ReportTest.php new file mode 100644 index 0000000..9e4f02f --- /dev/null +++ b/test/report/ReportTest.php @@ -0,0 +1,263 @@ +buildPath = __DIR__ . '/../../build/allure-results'; + $files = scandir($this->buildPath); + + foreach ($files as $fileName) { + $file = $this->buildPath . DIRECTORY_SEPARATOR . $fileName; + if (!is_file($file)) { + continue; + } + $extension = pathinfo($file, PATHINFO_EXTENSION); + if ('xml' == $extension) { + $dom = new DOMDocument(); + $dom->load($file); + + $path = new DOMXPath($dom); + $name = $path->query('/alr:test-suite/name')->item(0)->textContent; + if (isset($this->sources[$name])) { + throw new RuntimeException("Duplicate test suite: {$name}"); + } + $this->sources[$name] = $path; + } + } + } + + /** + * @param string $class + * @param string $xpath + * @param string $expectedValue + * @dataProvider providerSingleTextNode + */ + public function testSingleTextNode(string $class, string $xpath, string $expectedValue): void + { + self::assertArrayHasKey($class, $this->sources); + $actualValue = $this + ->sources[$class] + ->query($xpath) + ->item(0) + ->textContent; + self::assertSame($expectedValue, $actualValue); + } + + public function providerSingleTextNode(): iterable + { + return [ + 'Test case title annotation' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testTitleAnnotation', + '/title' + ), + 'Test title', + ], + 'Test case severity annotation' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testSeverityAnnotation', + '/labels/label[@name="severity" and @value="minor"]' + ), + '', + ], + 'Test case parameter annotation' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testParameterAnnotation', + '/parameters/parameter[@name="foo" and @value="bar" and @kind="argument"]' + ), + '', + ], + 'Test case stories annotation: first story' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testStoriesAnnotation', + '/labels/label[@name="story" and @value="Story 1"]' + ), + '', + ], + 'Test case stories annotation: second story' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testStoriesAnnotation', + '/labels/label[@name="story" and @value="Story 2"]' + ), + '', + ], + 'Test case features annotation: first feature' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testFeaturesAnnotation', + '/labels/label[@name="feature" and @value="Feature 1"]' + ), + '', + ], + 'Test case features annotation: second feature' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testFeaturesAnnotation', + '/labels/label[@name="feature" and @value="Feature 2"]' + ), + '', + ], + 'Successful test case without steps' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testNoStepsSuccess', + '[@status="passed"]/name' + ), + 'testNoStepsSuccess', + ], + 'Error in test case without steps' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testNoStepsError', + '[@status="broken"]/failure/message' + ), + 'Error', + ], + 'Failure in test case without steps' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testNoStepsFailure', + '[@status="failed"]/failure/message' + ), + 'Failure', + ], + 'Test case without steps skipped' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testNoStepsSkipped', + '[@status="canceled"]/failure/message' + ), + 'Skipped', + ], + 'Successful test case with single step: name' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testSingleSuccessfulStepWithTitle', + '[@status="passed"]/steps/step[1][@status="passed"]/name' + ), + 'step 1 name ', // Codeception processes action internally + ], + 'Successful test case with two successful steps: step 2 name' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testTwoSuccessfulSteps', + '[@status="passed"]/steps/step[2][@status="passed"]/name' + ), + 'step 2 name ', // Codeception processes action internally + ], + 'First step in test case with two steps fails: failure' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testTwoStepsFirstFails', + '[@status="failed"]/failure/message' + ), + 'Failure', + ], + 'First step in test case with two steps fails: step 1 name' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testTwoStepsFirstFails', + '[@status="failed"]/steps/step[1][@status="failed"]/name' + ), + 'step 1 name ', // Codeception processes action internally + ], + 'Second step in test case with two steps fails: failure' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testTwoStepsSecondFails', + '[@status="failed"]/failure/message' + ), + 'Failure', + ], + 'Second step in test case with two steps fails: step 1 name' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testTwoStepsSecondFails', + '[@status="failed"]/steps/step[1][@status="passed"]/name' + ), + 'step 1 name ', // Codeception processes action internally + ], + 'Second step in test case with two steps fails: step 2 name' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testTwoStepsSecondFails', + '[@status="failed"]/steps/step[2][@status="failed"]/name' + ), + 'step 2 name ', // Codeception processes action internally + ], + ]; + } + + /** + * @param string $class + * @param string $xpath + * @dataProvider providerNodeNotExists + */ + public function testNodeNotExists(string $class, string $xpath): void + { + $testNode = $this + ->sources[$class] + ->query($xpath) + ->item(0); + self::assertNull($testNode); + } + + public function providerNodeNotExists(): iterable + { + return [ + 'Successful test case without steps: no steps' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testNoStepsSuccess', + '/steps' + ) + ], + 'First step fails in test case with two steps: no second step' => [ + 'Yandex\Allure\Codeception.unit', + $this->buildTestXPath( + 'testTwoStepsFirstFails', + '/steps/step[2]' + ) + ], + ]; + } + + private function buildTestXPath(string $testName, string $tail): string + { + return sprintf('/alr:test-suite/test-cases/test-case[name="%s"]%s', $testName, $tail); + } +}