diff --git a/README.md b/README.md index 6b8392d..c0b6eb6 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ All configuration options are listed in the table below: | Qase test plan ID | `testops.plan.id` | `QASE_TESTOPS_PLAN_ID` | undefined | No | Any integer | | Size of batch for sending test results | `testops.batch.size` | `QASE_TESTOPS_BATCH_SIZE` | `200` | No | Any integer | | Enable defects for failed test cases | `testops.defect` | `QASE_TESTOPS_DEFECT` | `False` | No | `True`, `False` | +| Enable public report link generation after run completion | `testops.showPublicReportLink` | `QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK` | `False` | No | `True`, `False` | | Configuration values to associate with test run | `testops.configurations.values` | `QASE_TESTOPS_CONFIGURATIONS_VALUES` | `[]` | No | Comma-separated key=value pairs | | Create configuration groups and values if they don't exist | `testops.configurations.createIfNotExists` | `QASE_TESTOPS_CONFIGURATIONS_CREATE_IF_NOT_EXISTS` | `False` | No | `True`, `False` | | Status filter for test results | `testops.statusFilter` | `QASE_TESTOPS_STATUS_FILTER` | `[]` | No | Comma-separated string | @@ -94,6 +95,7 @@ All configuration options are listed in the table below: } }, "defect": false, + "showPublicReportLink": true, "project": "", "batch": { "size": 100 @@ -130,6 +132,7 @@ export QASE_TESTOPS_CONFIGURATIONS_CREATE_IF_NOT_EXISTS=true export QASE_TESTOPS_STATUS_FILTER="skipped,blocked,untested" export QASE_TESTOPS_RUN_EXTERNAL_LINK_TYPE="jiraCloud" export QASE_TESTOPS_RUN_EXTERNAL_LINK_URL="PROJ-123" +export QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK=true export QASE_STATUS_MAPPING="invalid=failed,skipped=passed" ``` @@ -138,6 +141,7 @@ The `QASE_TESTOPS_CONFIGURATIONS_VALUES` should be a comma-separated list of key ### How Configurations Work Configurations in Qase TestOps work as follows: + * **name** field represents the configuration group (e.g., "browser", "environment") * **value** field represents the configuration item within that group (e.g., "chrome", "staging") * When `createIfNotExists` is true, the system will: diff --git a/composer.json b/composer.json index 7cbe430..0ad0c5e 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ "scripts": { "test": "phpunit" }, - "version": "2.1.6", + "version": "2.1.7", "config": { "platform": { "php": "8.0" diff --git a/composer.lock b/composer.lock index 41227c1..574acb6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0db31b7e94eb7235da8ff4e6138907b2", + "content-hash": "24c08f7285b85900f049559ef8ae5d36", "packages": [ { "name": "brick/math", diff --git a/src/Client/ApiClientV1.php b/src/Client/ApiClientV1.php index 45d1731..a3df948 100644 --- a/src/Client/ApiClientV1.php +++ b/src/Client/ApiClientV1.php @@ -340,4 +340,36 @@ public function runUpdateExternalIssue(string $code, string $type, array $links) $this->logger->error('Failed to update external issue: ' . $e->getMessage()); } } + + public function enablePublicReport(string $code, int $runId): ?string + { + try { + $this->logger->debug('Enable public report for run: ' . $runId); + + // Make PATCH request to enable public report + $response = $this->client->request('PATCH', $this->clientConfig->getHost() . '/run/' . $code . '/' . $runId . '/public', [ + 'headers' => [ + 'Token' => $this->config->api->getToken(), + 'Content-Type' => 'application/json', + ], + 'json' => [ + 'status' => true, + ], + ]); + + $responseData = json_decode($response->getBody()->getContents(), true); + + if (isset($responseData['result']['hash'])) { + $publicUrl = $this->appUrl . '/public/report/' . $responseData['result']['hash']; + $this->logger->info('Public report link: ' . $publicUrl); + return $publicUrl; + } + + $this->logger->warning('Public report hash not found in response'); + return null; + } catch (Exception $e) { + $this->logger->warning('Failed to generate public report link: ' . $e->getMessage()); + return null; + } + } } diff --git a/src/Config/ConfigLoader.php b/src/Config/ConfigLoader.php index 553967b..66b4c2d 100644 --- a/src/Config/ConfigLoader.php +++ b/src/Config/ConfigLoader.php @@ -70,6 +70,7 @@ private function loadFromJsonFile(): QaseConfig if (isset($data['testops']['project'])) $config->testops->setProject($data['testops']['project']); if (isset($data['testops']['defect'])) $config->testops->setDefect($data['testops']['defect']); + if (isset($data['testops']['showPublicReportLink'])) $config->testops->setShowPublicReportLink($data['testops']['showPublicReportLink']); if (isset($data['testops']['api']['token'])) $config->testops->api->setToken($data['testops']['api']['token']); if (isset($data['testops']['api']['host'])) $config->testops->api->setHost($data['testops']['api']['host']); @@ -147,6 +148,9 @@ private function overrideWithEnvVariables(): void case "qase_testops_defect": $this->config->testops->setDefect($value); break; + case "qase_testops_show_public_report_link": + $this->config->testops->setShowPublicReportLink(filter_var($value, FILTER_VALIDATE_BOOLEAN)); + break; case "qase_testops_api_token": $this->config->testops->api->setToken($value); break; diff --git a/src/Interfaces/ClientInterface.php b/src/Interfaces/ClientInterface.php index ef61aab..a9a1e48 100644 --- a/src/Interfaces/ClientInterface.php +++ b/src/Interfaces/ClientInterface.php @@ -61,4 +61,13 @@ public function createConfigurationItem(string $code, int $groupId, string $titl * @return void */ public function runUpdateExternalIssue(string $code, string $type, array $links): void; + + /** + * Enable public report for a test run + * + * @param string $code Project code + * @param int $runId Test run ID + * @return string|null Public report link or null on failure + */ + public function enablePublicReport(string $code, int $runId): ?string; } diff --git a/src/Interfaces/LoggerInterface.php b/src/Interfaces/LoggerInterface.php index 48bddde..521fab4 100644 --- a/src/Interfaces/LoggerInterface.php +++ b/src/Interfaces/LoggerInterface.php @@ -9,4 +9,5 @@ interface LoggerInterface public function info(string $message): void; public function debug(string $message): void; public function error(string $message): void; + public function warning(string $message): void; } diff --git a/src/Loggers/Logger.php b/src/Loggers/Logger.php index 7fb6d1e..6c54d8e 100644 --- a/src/Loggers/Logger.php +++ b/src/Loggers/Logger.php @@ -13,6 +13,7 @@ class Logger implements LoggerInterface 'INFO' => "\033[32m", // Green 'DEBUG' => "\033[36m", // Blue 'ERROR' => "\033[31m", // Red + 'WARNING' => "\033[33m", // Yellow 'RESET' => "\033[0m", // Reset color ]; private bool $debug; @@ -64,6 +65,10 @@ public function error(string $message): void $this->writeLog($message, 'ERROR'); } + public function warning(string $message): void + { + $this->writeLog($message, 'WARNING'); + } private function writeLog(string $message, string $level): void { diff --git a/src/Models/Config/TestopsConfig.php b/src/Models/Config/TestopsConfig.php index 1ee53b3..bedd9d7 100644 --- a/src/Models/Config/TestopsConfig.php +++ b/src/Models/Config/TestopsConfig.php @@ -7,6 +7,7 @@ class TestopsConfig { public ?string $project = null; public bool $defect = false; + public bool $showPublicReportLink = false; public ApiConfig $api; public RunConfig $run; public PlanConfig $plan; @@ -52,4 +53,14 @@ public function setStatusFilter(array $statusFilter): void { $this->statusFilter = $statusFilter; } + + public function isShowPublicReportLink(): bool + { + return $this->showPublicReportLink; + } + + public function setShowPublicReportLink(bool $showPublicReportLink): void + { + $this->showPublicReportLink = $showPublicReportLink; + } } diff --git a/src/Reporters/TestOpsReporter.php b/src/Reporters/TestOpsReporter.php index 7329332..0f2e3e3 100644 --- a/src/Reporters/TestOpsReporter.php +++ b/src/Reporters/TestOpsReporter.php @@ -58,6 +58,11 @@ public function completeRun(): void $this->state->completeRun( function () { $this->client->completeTestRun($this->config->testops->getProject(), $this->runId); + + // Enable public report if configured + if ($this->config->testops->isShowPublicReportLink()) { + $this->enablePublicReportForRun(); + } } ); } @@ -300,4 +305,23 @@ private function updateExternalIssue(int $runId): void $this->logger->error('Failed to update external issue: ' . $e->getMessage()); } } + + /** + * Enable public report for the current test run + */ + private function enablePublicReportForRun(): void + { + try { + $publicUrl = $this->client->enablePublicReport( + $this->config->testops->getProject(), + $this->runId + ); + + // Logger already prints the link inside the client method + // No need to print again here + } catch (Exception $e) { + // Log warning through the centralized logger + $this->logger->warning('Failed to enable public report: ' . $e->getMessage()); + } + } } diff --git a/tests/PublicReportLinkTest.php b/tests/PublicReportLinkTest.php new file mode 100644 index 0000000..9efac6c --- /dev/null +++ b/tests/PublicReportLinkTest.php @@ -0,0 +1,182 @@ +logger = new Logger(); + } + + public function testShowPublicReportLinkFromEnvTrue(): void + { + // Set environment variable + putenv('QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK=true'); + + $configLoader = new ConfigLoader($this->logger); + $config = $configLoader->getConfig(); + + $this->assertTrue($config->testops->isShowPublicReportLink()); + + // Clean up + putenv('QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK'); + } + + public function testShowPublicReportLinkFromEnvFalse(): void + { + // Set environment variable + putenv('QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK=false'); + + $configLoader = new ConfigLoader($this->logger); + $config = $configLoader->getConfig(); + + $this->assertFalse($config->testops->isShowPublicReportLink()); + + // Clean up + putenv('QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK'); + } + + public function testShowPublicReportLinkFromEnv1(): void + { + // Set environment variable with numeric value + putenv('QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK=1'); + + $configLoader = new ConfigLoader($this->logger); + $config = $configLoader->getConfig(); + + $this->assertTrue($config->testops->isShowPublicReportLink()); + + // Clean up + putenv('QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK'); + } + + public function testShowPublicReportLinkFromEnv0(): void + { + // Set environment variable with numeric value + putenv('QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK=0'); + + $configLoader = new ConfigLoader($this->logger); + $config = $configLoader->getConfig(); + + $this->assertFalse($config->testops->isShowPublicReportLink()); + + // Clean up + putenv('QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK'); + } + + public function testShowPublicReportLinkDefaultValue(): void + { + // Don't set environment variable + $configLoader = new ConfigLoader($this->logger); + $config = $configLoader->getConfig(); + + // Should be false by default + $this->assertFalse($config->testops->isShowPublicReportLink()); + } + + public function testShowPublicReportLinkFromJsonFile(): void + { + // Create a temporary config file + $configFilePath = getcwd() . '/qase.config.json'; + $configData = [ + 'testops' => [ + 'showPublicReportLink' => true, + 'project' => 'TEST', + 'api' => [ + 'token' => 'test_token' + ] + ] + ]; + + file_put_contents($configFilePath, json_encode($configData)); + + // Load config + $configLoader = new ConfigLoader($this->logger); + $config = $configLoader->getConfig(); + + $this->assertTrue($config->testops->isShowPublicReportLink()); + + // Clean up + unlink($configFilePath); + } + + public function testShowPublicReportLinkFromJsonFileFalse(): void + { + // Create a temporary config file + $configFilePath = getcwd() . '/qase.config.json'; + $configData = [ + 'testops' => [ + 'showPublicReportLink' => false, + 'project' => 'TEST', + 'api' => [ + 'token' => 'test_token' + ] + ] + ]; + + file_put_contents($configFilePath, json_encode($configData)); + + // Load config + $configLoader = new ConfigLoader($this->logger); + $config = $configLoader->getConfig(); + + $this->assertFalse($config->testops->isShowPublicReportLink()); + + // Clean up + unlink($configFilePath); + } + + public function testShowPublicReportLinkEnvOverridesFile(): void + { + // Create a temporary config file with false + $configFilePath = getcwd() . '/qase.config.json'; + $configData = [ + 'testops' => [ + 'showPublicReportLink' => false, + 'project' => 'TEST', + 'api' => [ + 'token' => 'test_token' + ] + ] + ]; + + file_put_contents($configFilePath, json_encode($configData)); + + // Set environment variable to true + putenv('QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK=true'); + + // Load config + $configLoader = new ConfigLoader($this->logger); + $config = $configLoader->getConfig(); + + // Environment variable should override file config + $this->assertTrue($config->testops->isShowPublicReportLink()); + + // Clean up + unlink($configFilePath); + putenv('QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK'); + } + + public function testSetShowPublicReportLink(): void + { + $configLoader = new ConfigLoader($this->logger); + $config = $configLoader->getConfig(); + + // Test setter + $config->testops->setShowPublicReportLink(true); + $this->assertTrue($config->testops->isShowPublicReportLink()); + + $config->testops->setShowPublicReportLink(false); + $this->assertFalse($config->testops->isShowPublicReportLink()); + } +} +