diff --git a/app/Http/Controllers/BuildController.php b/app/Http/Controllers/BuildController.php index 9a56c2a618..58eb1a5e7a 100644 --- a/app/Http/Controllers/BuildController.php +++ b/app/Http/Controllers/BuildController.php @@ -117,6 +117,7 @@ public function update(int $build_id): View $this->setBuildById($build_id); return $this->vue('build-update', 'Files Updated', [ + 'build-id' => $this->build->Id, 'repository-type' => $this->project->CvsViewerType, 'repository-url' => $this->project->CvsUrl, ]); diff --git a/app/cdash/tests/CMakeLists.txt b/app/cdash/tests/CMakeLists.txt index a02d066e83..66e9f47dd4 100644 --- a/app/cdash/tests/CMakeLists.txt +++ b/app/cdash/tests/CMakeLists.txt @@ -415,6 +415,8 @@ add_browser_test(/Browser/Pages/BuildDynamicAnalysisIdPageTest) add_browser_test(/Browser/Pages/BuildSummaryPageTest) +add_browser_test(/Browser/Pages/BuildSidebarComponentTest) + add_php_test(image) set_tests_properties(image PROPERTIES DEPENDS /CDash/XmlHandler/UpdateHandler) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index af4f481ce4..63cefd0747 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -26451,6 +26451,15 @@ parameters: count: 2 path: tests/Browser/Pages/BuildErrorsPageTest.php + - + rawMessage: ''' + Call to deprecated method coverageResults() of class App\Models\Build: + 07/27/2025 Use this relation only to edit the underlying coverage table. Use coverage() instead. + ''' + identifier: method.deprecated + count: 1 + path: tests/Browser/Pages/BuildSidebarComponentTest.php + - rawMessage: 'Method Tests\Browser\Pages\ProjectMembersPageTest::testFullInvitationWorkflow() throws checked exception OverflowException but it''s missing from the PHPDoc @throws tag.' identifier: missingType.checkedException diff --git a/phpstan.neon b/phpstan.neon index f201835641..3d961720e4 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -40,6 +40,7 @@ parameters: - 'Illuminate\Http\Client\ConnectionException' - 'Illuminate\Http\Exceptions\HttpResponseException' - 'Symfony\Component\Console\Exception\CommandNotFoundException' + - 'Facebook\WebDriver\Exception\TimeoutException' check: tooWideThrowType: true diff --git a/resources/css/common.css b/resources/css/common.css index 12651abd7b..c65301b10c 100644 --- a/resources/css/common.css +++ b/resources/css/common.css @@ -897,7 +897,7 @@ div#index_top { } div#main_content { - padding: 1em 2em; + padding: 1em; } #settings { diff --git a/resources/js/vue/components/BuildCommandsPage.vue b/resources/js/vue/components/BuildCommandsPage.vue index e5b23af9cb..b71e7c1d72 100644 --- a/resources/js/vue/components/BuildCommandsPage.vue +++ b/resources/js/vue/components/BuildCommandsPage.vue @@ -1,43 +1,49 @@ diff --git a/resources/js/vue/components/shared/BuildSidebarItem.vue b/resources/js/vue/components/shared/BuildSidebarItem.vue new file mode 100644 index 0000000000..c279af5499 --- /dev/null +++ b/resources/js/vue/components/shared/BuildSidebarItem.vue @@ -0,0 +1,80 @@ + + + diff --git a/tests/Browser/Pages/BuildSidebarComponentTest.php b/tests/Browser/Pages/BuildSidebarComponentTest.php new file mode 100644 index 0000000000..48fe83b2a0 --- /dev/null +++ b/tests/Browser/Pages/BuildSidebarComponentTest.php @@ -0,0 +1,382 @@ +project = $this->makePublicProject(); + + $this->site = $this->makeSite(); + $this->updateSiteInfoIfChanged($this->site, new SiteInformation([])); + } + + public function tearDown(): void + { + $this->project->delete(); + $this->site->delete(); + + parent::tearDown(); + } + + private function assertDisabled(Browser $browser, string $url, string $selector): void + { + $browser->visit($url) + ->waitFor('@sidebar-loaded') + ->assertAttributeMissing($selector, 'href'); + } + + private function assertNotDisabled(Browser $browser, string $url, string $selector, string $expectedLink): void + { + $browser->visit($url) + ->waitFor('@sidebar-loaded') + ->assertAttributeContains($selector, 'href', $expectedLink); + } + + public function testSummaryItem(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'siteid' => $this->site->id, + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + $this->browse(function (Browser $browser) use ($build): void { + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-summary', "/builds/{$build->id}"); + }); + } + + public function testUpdateItem(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'siteid' => $this->site->id, + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + $this->browse(function (Browser $browser) use ($build): void { + $this->assertDisabled($browser, "/builds/{$build->id}", '@sidebar-update'); + + /** @var BuildUpdate $update */ + $update = BuildUpdate::create([ + 'command' => Str::uuid()->toString(), + 'type' => 'GIT', + 'status' => Str::uuid()->toString(), + 'revision' => Str::uuid()->toString(), + 'priorrevision' => Str::uuid()->toString(), + 'path' => Str::uuid()->toString(), + ]); + $build->updateStep()->associate($update)->save(); + + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-update', "/builds/{$build->id}/update"); + }); + } + + public function testConfigureItem(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'siteid' => $this->site->id, + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + $this->browse(function (Browser $browser) use ($build): void { + $this->assertDisabled($browser, "/builds/{$build->id}", '@sidebar-configure'); + + $build->configurewarnings = 10; + $build->save(); + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-configure', "/builds/{$build->id}/configure"); + $browser->visit("/builds/{$build->id}") + ->waitFor('@sidebar-loaded') + ->assertSeeIn('@sidebar-configure', '10'); + + $build->configurewarnings = -1; + $build->configureerrors = 5; + $build->save(); + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-configure', "/builds/{$build->id}/configure"); + $browser->visit("/builds/{$build->id}") + ->waitFor('@sidebar-loaded') + ->assertSeeIn('@sidebar-configure', '5'); + + $build->configurewarnings = 10; + $build->save(); + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-configure', "/builds/{$build->id}/configure"); + $browser->visit("/builds/{$build->id}") + ->waitFor('@sidebar-loaded') + ->assertSeeIn('@sidebar-configure', '10') + ->assertSeeIn('@sidebar-configure', '5'); + }); + } + + public function testBuildItem(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'siteid' => $this->site->id, + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + $this->browse(function (Browser $browser) use ($build): void { + $this->assertDisabled($browser, "/builds/{$build->id}", '@sidebar-build'); + + $build->buildwarnings = 10; + $build->save(); + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-build', "/builds/{$build->id}/errors"); + $browser->visit("/builds/{$build->id}") + ->waitFor('@sidebar-loaded') + ->assertSeeIn('@sidebar-build', '10'); + + $build->buildwarnings = -1; + $build->builderrors = 5; + $build->save(); + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-build', "/builds/{$build->id}/errors"); + $browser->visit("/builds/{$build->id}") + ->waitFor('@sidebar-loaded') + ->assertSeeIn('@sidebar-build', '5'); + + $build->buildwarnings = 10; + $build->save(); + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-build', "/builds/{$build->id}/errors"); + $browser->visit("/builds/{$build->id}") + ->waitFor('@sidebar-loaded') + ->assertSeeIn('@sidebar-build', '10') + ->assertSeeIn('@sidebar-build', '5'); + }); + } + + public function testTestsItem(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'siteid' => $this->site->id, + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + $this->browse(function (Browser $browser) use ($build): void { + $this->assertDisabled($browser, "/builds/{$build->id}", '@sidebar-tests'); + + $build->testpassed = 10; + $build->save(); + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-tests', "/builds/{$build->id}/tests"); + $browser->visit("/builds/{$build->id}") + ->waitFor('@sidebar-loaded') + ->assertDontSeeIn('@sidebar-tests', '10'); + + $build->testpassed = 0; + $build->testnotrun = 7; + $build->save(); + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-tests', "/builds/{$build->id}/tests"); + $browser->visit("/builds/{$build->id}") + ->waitFor('@sidebar-loaded') + ->assertDontSeeIn('@sidebar-tests', '10'); + + $build->testnotrun = 0; + $build->testfailed = 5; + $build->save(); + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-tests', "/builds/{$build->id}/tests"); + $browser->visit("/builds/{$build->id}") + ->waitFor('@sidebar-loaded') + ->assertSeeIn('@sidebar-tests', '5'); + + $build->testpassed = 10; + $build->testnotrun = 7; + $build->testfailed = 5; + $build->save(); + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-tests', "/builds/{$build->id}/tests"); + $browser->visit("/builds/{$build->id}") + ->waitFor('@sidebar-loaded') + ->assertSeeIn('@sidebar-tests', '5'); + }); + } + + public function testCoverageItem(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'siteid' => $this->site->id, + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + $this->browse(function (Browser $browser) use ($build): void { + $this->assertDisabled($browser, "/builds/{$build->id}", '@sidebar-coverage'); + + $coverageFile = CoverageFile::firstOrCreate([ + 'fullpath' => Str::uuid()->toString(), + 'file' => Str::uuid()->toString(), + 'crc32' => 0, + ]); + $build->coverageResults()->create([ + 'fileid' => $coverageFile->id, + 'locuntested' => 10, + 'loctested' => 10, + 'branchesuntested' => 0, + 'branchestested' => 0, + ]); + + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-coverage', "/builds/{$build->id}/coverage"); + }); + } + + public function testDynamicAnalysisItem(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'siteid' => $this->site->id, + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + $this->browse(function (Browser $browser) use ($build): void { + $this->assertDisabled($browser, "/builds/{$build->id}", '@sidebar-dynamic-analysis'); + + $build->dynamicAnalyses()->save(DynamicAnalysis::factory()->make()); + + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-dynamic-analysis', "/builds/{$build->id}/dynamic_analysis"); + }); + } + + public function testFilesItem(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'siteid' => $this->site->id, + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + $this->browse(function (Browser $browser) use ($build): void { + $this->assertDisabled($browser, "/builds/{$build->id}", '@sidebar-files'); + + $build->uploadedFiles()->attach( + UploadFile::create([ + 'filename' => Str::uuid()->toString(), + 'sha1sum' => Str::uuid()->toString(), + 'filesize' => 12345, + 'isurl' => false, + ]) + ); + + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-files', "/builds/{$build->id}/files"); + + $build->uploadedFiles()->delete(); + $this->assertDisabled($browser, "/builds/{$build->id}", '@sidebar-files'); + + $build->uploadedFiles()->attach( + UploadFile::create([ + 'filename' => fake()->url(), + 'sha1sum' => Str::uuid()->toString(), + 'isurl' => true, + ]) + ); + + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-files', "/builds/{$build->id}/files"); + }); + } + + public function testNotesItem(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'siteid' => $this->site->id, + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + $this->browse(function (Browser $browser) use ($build): void { + $this->assertDisabled($browser, "/builds/{$build->id}", '@sidebar-notes'); + + $build->notes()->attach( + Note::create([ + 'name' => Str::uuid()->toString(), + 'text' => Str::uuid()->toString(), + ]) + ); + + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-notes', "/builds/{$build->id}/notes"); + }); + } + + public function testInstrumentationItem(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'siteid' => $this->site->id, + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + $this->browse(function (Browser $browser) use ($build): void { + $this->assertDisabled($browser, "/builds/{$build->id}", '@sidebar-instrumentation'); + + $build->commands()->create([ + 'type' => BuildCommandType::CUSTOM, + 'starttime' => Carbon::now(), + 'duration' => 12345, + 'command' => Str::random(10), + 'result' => Str::random(10), + 'source' => Str::random(10), + 'language' => Str::random(10), + 'config' => Str::random(10), + 'workingdirectory' => Str::uuid()->toString(), + ]); + + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-instrumentation', "/builds/{$build->id}/commands"); + }); + } + + public function testTargetsItem(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'siteid' => $this->site->id, + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + $this->browse(function (Browser $browser) use ($build): void { + $this->assertDisabled($browser, "/builds/{$build->id}", '@sidebar-targets'); + + $build->targets()->create([ + 'name' => Str::uuid()->toString(), + 'type' => TargetType::UNKNOWN, + ]); + + $this->assertNotDisabled($browser, "/builds/{$build->id}", '@sidebar-targets', "/builds/{$build->id}/targets"); + }); + } +}