diff --git a/.env.example b/.env.example index 8844ac4245..d5848b55d2 100755 --- a/.env.example +++ b/.env.example @@ -63,10 +63,6 @@ DB_PASSWORD=secret # This option is useful if you don't want the CDash's GitHub check to block merging. #GITHUB_ALWAYS_PASS=false -# API key for maps.google.com. -# Set to a valid value to display maps of sites. -#GOOGLE_MAP_API_KEY=null - # Maximum size of large text fields, in bytes. 0 for unlimited #LARGE_TEXT_LIMIT=0 diff --git a/app/Http/Controllers/SiteController.php b/app/Http/Controllers/SiteController.php index ca5085cf98..2d112c291c 100644 --- a/app/Http/Controllers/SiteController.php +++ b/app/Http/Controllers/SiteController.php @@ -4,14 +4,11 @@ use App\Models\Site; use App\Models\User; -use App\Utils\TestingDay; use CDash\Database; use CDash\Model\Project; use Illuminate\Http\RedirectResponse; -use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Gate; use Illuminate\View\View; final class SiteController extends AbstractController @@ -320,231 +317,10 @@ public function editSite(): View|RedirectResponse ->with('xsl_content', generate_XSLT($xml, base_path() . '/app/cdash/public/editSite', true)); } - public function viewSite(int $siteid): View + public function viewSite(Site $site): View { - $db = Database::getInstance(); - - $site = Site::findOrFail($siteid); - - $currenttime = $_GET['currenttime'] ?? null; - if ($currenttime !== null) { - $currenttime = (int) $currenttime; - - // Current timestamp is the beginning of the dashboard and we want the end - $currenttimestamp = Carbon::createFromTimestamp($currenttime + 3600 * 24); - } else { - $currenttimestamp = Carbon::maxValue(); - } - - $siteinformation = $site->mostRecentInformation($currenttimestamp)->first(); - - $xml = begin_XML_for_XSLT(); - - $projectid = (int) ($_GET['project'] ?? 0); - - if ($projectid > 0) { - $project = new Project(); - $project->Id = $projectid; - $project->Fill(); - $xml .= 'index.php?project=' . urlencode($project->Name); - $date = TestingDay::get($project, gmdate(FMT_DATETIME, $currenttime)); - $xml .= '&date=' . $date; - $xml .= ''; - } else { - $project = null; - $xml .= 'index.php'; - } - $xml .= "CDash - {$site->name}"; - $xml .= "{$site->name}"; - - $xml .= ''; - $xml .= 'CDash'; - - $xml .= add_XML_value('baseURL', config('app.url')); - $apikey = config('cdash.google_map_api_key'); - - $MB = 1048576; - - $total_virtual_memory = $siteinformation?->totalvirtualmemory; - if ($total_virtual_memory !== null) { - $total_virtual_memory = getByteValueWithExtension($total_virtual_memory * $MB) . 'iB'; - } - - $total_physical_memory = $siteinformation?->totalphysicalmemory; - if ($total_physical_memory !== null) { - $total_physical_memory = getByteValueWithExtension($total_physical_memory * $MB) . 'iB'; - } - - $processor_clock_frequency = $siteinformation?->processorclockfrequency; - if ($processor_clock_frequency !== null) { - $processor_clock_frequency = getByteValueWithExtension($processor_clock_frequency * 10 ** 6, 1000) . 'Hz'; - } - - $xml .= add_XML_value('googlemapkey', $apikey); - $xml .= ''; - $xml .= ''; - $xml .= add_XML_value('id', $site->id); - $xml .= add_XML_value('name', $site->name); - $xml .= add_XML_value('description', stripslashes($siteinformation->description ?? '')); - $xml .= add_XML_value('processoris64bits', $siteinformation?->processoris64bits); - $xml .= add_XML_value('processorvendor', $siteinformation?->processorvendor); - $xml .= add_XML_value('processorvendorid', $siteinformation?->processorvendorid); - $xml .= add_XML_value('processorfamilyid', $siteinformation?->processorfamilyid); - $xml .= add_XML_value('processormodelid', $siteinformation?->processormodelid); - $xml .= add_XML_value('processorcachesize', $siteinformation?->processorcachesize); - $xml .= add_XML_value('numberlogicalcpus', $siteinformation?->numberlogicalcpus); - $xml .= add_XML_value('numberphysicalcpus', $siteinformation?->numberphysicalcpus); - $xml .= add_XML_value('totalvirtualmemory', $total_virtual_memory); - $xml .= add_XML_value('totalphysicalmemory', $total_physical_memory); - $xml .= add_XML_value('logicalprocessorsperphysical', $siteinformation?->logicalprocessorsperphysical); - $xml .= add_XML_value('processorclockfrequency', $processor_clock_frequency); - $xml .= add_XML_value('outoforder', $site->outoforder); - if ($project !== null && $project->ShowIPAddresses) { - $xml .= add_XML_value('ip', $site->ip); - $xml .= add_XML_value('latitude', $site->latitude); - $xml .= add_XML_value('longitude', $site->longitude); - } - $xml .= ''; - - // List the claimers of the site - $siteclaimer = $db->executePrepared(' - SELECT u.firstname, u.lastname, u.email - FROM users as u, site2user - WHERE - u.id=site2user.userid - AND site2user.siteid=? - ORDER BY firstname - ', [$siteid]); - foreach ($siteclaimer as $sc) { - $xml .= ''; - $xml .= add_XML_value('firstname', $sc['firstname']); - $xml .= add_XML_value('lastname', $sc['lastname']); - if (isset($_SESSION['cdash'])) { - $xml .= add_XML_value('email', $sc['email']); - } - $xml .= ''; - } - - // Select projects that belong to this site - $displayPage = 0; - $projects = []; - $site2project = $db->executePrepared(' - SELECT projectid, max(submittime) AS maxtime - FROM build - WHERE - siteid=? - AND projectid>0 - GROUP BY projectid - ', [$siteid]); - - foreach ($site2project as $project_site) { - $projectid = $project_site['projectid']; - - $project = new Project(); - $project->Id = $projectid; - $project->Fill(); - if (Gate::allows('view-project', $project)) { - $xml .= ''; - $xml .= add_XML_value('id', $projectid); - $xml .= add_XML_value('submittime', $project_site['maxtime']); - $xml .= add_XML_value('name', $project->Name); - $xml .= add_XML_value('name_encoded', urlencode($project->Name)); - $xml .= ''; - $displayPage = 1; // if we have at least a valid project we display the page - $projects[] = $projectid; - } - } - - // If the current site as only private projects we check that we have the right - // to view the page - if (!$displayPage) { - abort(403, 'You cannot access this page'); - } - - // Compute the time for all the projects (faster than individually) average of the week - if (config('database.default') == 'pgsql') { - $timediff = 'EXTRACT(EPOCH FROM (build.submittime - buildupdate.starttime))'; - $timestampadd = "NOW()-INTERVAL'167 hours'"; - } else { - $timediff = 'TIME_TO_SEC(TIMEDIFF(build.submittime, buildupdate.starttime))'; - $timestampadd = 'TIMESTAMPADD(' . qiv('HOUR') . ', -167, NOW())'; - } - - $testtime = $db->executePrepared(" - SELECT projectid, build.name AS buildname, build.type AS buildtype, SUM({$timediff}) AS elapsed - FROM build, buildupdate, build2update - WHERE - build.submittime > {$timestampadd} - AND build2update.buildid = build.id - AND buildupdate.id = build2update.updateid - AND build.siteid = ? - GROUP BY projectid,buildname,buildtype - ORDER BY elapsed - ", [$siteid]); - - $xml .= ''; - - echo pdo_error(); - $totalload = 0; - foreach ($testtime as $tt) { - $projectid = $tt['projectid']; - $project = new Project(); - $project->Id = $projectid; - $project->Fill(); - if (Gate::allows('view-project', $project)) { - $timespent = round($tt['elapsed'] / 7.0); // average over 7 days - $xml .= ''; - $xml .= add_XML_value('name', $tt['buildname']); - $xml .= add_XML_value('project', $project->Name); - $xml .= add_XML_value('type', $tt['buildtype']); - $xml .= add_XML_value('time', $timespent); - $totalload += $timespent; - $xml .= ''; - } - } - - // Compute the idle time - $idletime = 24 * 3600 - $totalload; - if ($idletime < 0) { - $idletime = 0; - } - $xml .= '' . $idletime . ''; - $xml .= ''; - - if (isset($_SESSION['cdash'])) { - $xml .= ''; - $userid = Auth::id(); - - // Check if the current user as a role in this project - foreach ($projects as $projectid) { - // TODO: (williamjallen) Optimize this loop to execute a constant number of queries - - $user2project = $db->executePrepared('SELECT role FROM user2project WHERE projectid=? and role>0', [$projectid]); - if (count($user2project) > 0) { - $xml .= add_XML_value('sitemanager', '1'); - - $user2site = $db->executePrepared('SELECT * FROM site2user WHERE siteid=? and userid=?', - [$siteid, $userid]); - if (count($user2site) == 0) { - $xml .= add_XML_value('siteclaimed', '0'); - } else { - $xml .= add_XML_value('siteclaimed', '1'); - } - break; - } - } - - $user = User::where('id', '=', $userid)->first(); - $xml .= add_XML_value('id', $userid); - $xml .= add_XML_value('admin', $user->admin); - $xml .= ''; - } - - $xml .= ''; - - return $this->view('cdash', $site->name) - ->with('xsl', true) - ->with('xsl_content', generate_XSLT($xml, base_path() . '/app/cdash/public/viewSite', true)); + return $this->view('site.view-site', $site->name) + ->with('site', $site); } /** diff --git a/app/cdash/public/viewSite.xsl b/app/cdash/public/viewSite.xsl deleted file mode 100644 index 8964236cb5..0000000000 --- a/app/cdash/public/viewSite.xsl +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - - - /editSite.php?siteid= - Are you maintaining this site? [claim this site][edit site description] -
-
- - - -
This site has been marked as temporarly out of order by its maintainer. -
-
- - - No system information available at this time. - - -
Processor Speed: - -
Processor Vendor: - - () - -
- -
Number of CPUs: -
- -
Number of Cores: -
- -
Total Physical Memory: -
- -
- -
Description:
-
-
- - - - - Claimed by: - - &nbsp; - - - mailto: - - - - -

-
- - - - - IP address:
- Map:
- - - <script type="text/javascript"> - // Creates a marker whose info window displays the letter corresponding - // to the given index. - function createMarker(point,title) - { - var marker = new GMarker(point); - GEvent.addListener(marker, "click", function() - { - marker.openInfoWindowHtml(title); - }); - return marker; - } - - function load() { - if (GBrowserIsCompatible()) { - var map = new GMap2(document.getElementById("map")); - - - map.setCenter(new GLatLng(,),5); - map.addControl(new GLargeMapControl()); - var point = new GLatLng(,); - map.addOverlay(createMarker(point,'')); - - - - map.setCenter(new GLatLng(0,0),1); - - - } - } - </script> - - -
- -
-
-
- - - This site belongs to the following projects:
- - - /index.php?project= - - - ()
-
-
- - - Time spent per project (computed from average data over one week):

- -
- -
-
diff --git a/app/cdash/tests/CMakeLists.txt b/app/cdash/tests/CMakeLists.txt index c84f0e1912..2b9039f186 100644 --- a/app/cdash/tests/CMakeLists.txt +++ b/app/cdash/tests/CMakeLists.txt @@ -227,8 +227,12 @@ set_tests_properties(/Feature/GraphQL/BuildMeasurementTypeTest PROPERTIES DEPEND add_laravel_test(/Feature/GraphQL/CoverageTypeTest) set_tests_properties(/Feature/GraphQL/CoverageTypeTest PROPERTIES DEPENDS /Feature/GraphQL/BuildTypeTest) +add_laravel_test(/Browser/Pages/SitesIdPageTest) +set_tests_properties(/Browser/Pages/SitesIdPageTest PROPERTIES DEPENDS /Feature/GraphQL/BuildTypeTest) +set_tests_properties(/Browser/Pages/SitesIdPageTest PROPERTIES RUN_SERIAL TRUE) + add_laravel_test(/Feature/PurgeUnusedProjectsCommand) -set_tests_properties(/Feature/PurgeUnusedProjectsCommand PROPERTIES DEPENDS "/Feature/GraphQL/TestTypeTest;/Feature/GraphQL/TestMeasurementTypeTest;/Feature/GraphQL/NoteTypeTest;/Feature/GraphQL/BuildMeasurementTypeTest;/Feature/GraphQL/CoverageTypeTest") +set_tests_properties(/Feature/PurgeUnusedProjectsCommand PROPERTIES DEPENDS "/Feature/GraphQL/TestTypeTest;/Feature/GraphQL/TestMeasurementTypeTest;/Feature/GraphQL/NoteTypeTest;/Feature/GraphQL/BuildMeasurementTypeTest;/Feature/GraphQL/CoverageTypeTest;/Browser/Pages/SitesIdPageTest") add_laravel_test(/Browser/Pages/ProjectSitesPageTest) set_tests_properties(/Browser/Pages/ProjectSitesPageTest PROPERTIES DEPENDS /Feature/PurgeUnusedProjectsCommand) diff --git a/app/cdash/tests/test_projectwebpage.php b/app/cdash/tests/test_projectwebpage.php index 13e797890c..a68d591771 100644 --- a/app/cdash/tests/test_projectwebpage.php +++ b/app/cdash/tests/test_projectwebpage.php @@ -4,7 +4,6 @@ // After including cdash_test_case.php, subsequent require_once calls are // relative to the top of the CDash source tree // -use Illuminate\Support\Carbon; require_once dirname(__FILE__) . '/cdash_test_case.php'; @@ -181,27 +180,6 @@ public function testSubmissionInDb() $this->assertEqual($result[0], $expected); } - public function testProjectExperimentalLinkMachineName() - { - $content = $this->connect($this->url . '/api/v1/index.php?project=BatchmakeExample'); - $jsonobj = json_decode($content, true); - if (count($jsonobj['buildgroups']) < 1) { - $this->fail('No build found when expected'); - return; - } - $buildgroup = array_pop($jsonobj['buildgroups']); - $siteid = $buildgroup['builds'][0]['siteid']; - - $content = $this->connect($this->url . "/sites/$siteid?project=4¤ttime=" . Carbon::now()->getTimestamp()); - if (!$content) { - return; - } elseif (!$this->findString($content, 'Total Physical Memory: 15MiB
')) { - $this->assertTrue(false, 'The webpage does not match the expected content'); - return; - } - $this->assertTrue(true, 'The webpage matches the expected content'); - } - public function testProjectExperimentalLinkBuildSummary() { $content = $this->connect($this->url . '/api/v1/index.php?project=BatchmakeExample'); diff --git a/config/cdash.php b/config/cdash.php index 3d6eda8a7a..05a919788f 100755 --- a/config/cdash.php +++ b/config/cdash.php @@ -54,7 +54,6 @@ 'default_git_dir' => env('DEFAULT_GIT_DIRECTORY', '/cdash/_build'), 'default_project' => env('DEFAULT_PROJECT', null), 'delete_old_subprojects' => env('DELETE_OLD_SUBPROJECTS', true), - 'google_map_api_key' => env('GOOGLE_MAP_API_KEY', null), 'github_always_pass' => env('GITHUB_ALWAYS_PASS', false), 'github_app_id' => env('GITHUB_APP_ID', null), 'github_private_key' => env('GITHUB_PRIVATE_KEY', null), diff --git a/graphql/schema.graphql b/graphql/schema.graphql index fd0f49580a..ce1d6343e0 100644 --- a/graphql/schema.graphql +++ b/graphql/schema.graphql @@ -426,7 +426,7 @@ type Site { longitude: String! "Every edit of this site's information." - information: [SiteInformation!]! @hasMany(type: CONNECTION) @orderBy(column: "timestamp") + information: [SiteInformation!]! @hasMany(type: CONNECTION) @orderBy(column: "timestamp", direction: DESC) "The most recent information we have about this site." # TODO: Figure out how to support the date parameter. Perhaps it would be better to use a scope instead. diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 2077faf1fb..5dcdbe3762 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2412,7 +2412,7 @@ parameters: - message: "#^Argument of an invalid type array\\|false supplied for foreach, only iterables are supported\\.$#" - count: 4 + count: 1 path: app/Http/Controllers/SiteController.php - @@ -2425,7 +2425,7 @@ parameters: #^Call to deprecated function pdo_error\\(\\)\\: 04/01/2023$# """ - count: 2 + count: 1 path: app/Http/Controllers/SiteController.php - @@ -2433,7 +2433,7 @@ parameters: #^Call to deprecated method executePrepared\\(\\) of class CDash\\\\Database\\: 04/22/2023 Use Laravel query builder or Eloquent instead$# """ - count: 7 + count: 2 path: app/Http/Controllers/SiteController.php - @@ -2454,33 +2454,13 @@ parameters: count: 1 path: app/Http/Controllers/SiteController.php - - - message: "#^Cannot access property \\$admin on App\\\\Models\\\\User\\|null\\.$#" - count: 1 - path: app/Http/Controllers/SiteController.php - - message: "#^Cannot cast mixed to int\\.$#" - count: 4 - path: app/Http/Controllers/SiteController.php - - - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 2 path: app/Http/Controllers/SiteController.php - - message: "#^Dynamic call to static method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasOne\\\\:\\:first\\(\\)\\.$#" - count: 1 - path: app/Http/Controllers/SiteController.php - - - - message: "#^Foreach overwrites \\$projectid with its value variable\\.$#" - count: 1 - path: app/Http/Controllers/SiteController.php - - - - message: "#^Loose comparison via \"\\=\\=\" is not allowed\\.$#" + message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 2 path: app/Http/Controllers/SiteController.php @@ -2504,11 +2484,6 @@ parameters: count: 1 path: app/Http/Controllers/SiteController.php - - - message: "#^Only booleans are allowed in a negated boolean, int given\\.$#" - count: 1 - path: app/Http/Controllers/SiteController.php - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" count: 4 @@ -2529,11 +2504,6 @@ parameters: count: 1 path: app/Http/Controllers/SiteController.php - - - message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, array\\|false given\\.$#" - count: 2 - path: app/Http/Controllers/SiteController.php - - message: "#^Parameter \\#1 \\$value of function intval expects array\\|bool\\|float\\|int\\|resource\\|string\\|null, mixed given\\.$#" count: 4 @@ -25898,7 +25868,7 @@ parameters: - message: "#^Cannot access offset 'buildgroups' on mixed\\.$#" - count: 4 + count: 2 path: app/cdash/tests/test_projectwebpage.php - @@ -25908,7 +25878,7 @@ parameters: - message: "#^Cannot access offset 'builds' on mixed\\.$#" - count: 2 + count: 1 path: app/cdash/tests/test_projectwebpage.php - @@ -25921,14 +25891,9 @@ parameters: count: 1 path: app/cdash/tests/test_projectwebpage.php - - - message: "#^Cannot access offset 'siteid' on mixed\\.$#" - count: 1 - path: app/cdash/tests/test_projectwebpage.php - - message: "#^Cannot access offset 0 on mixed\\.$#" - count: 5 + count: 4 path: app/cdash/tests/test_projectwebpage.php - @@ -25941,11 +25906,6 @@ parameters: count: 1 path: app/cdash/tests/test_projectwebpage.php - - - message: "#^Method ProjectWebPageTestCase\\:\\:testProjectExperimentalLinkMachineName\\(\\) has no return type specified\\.$#" - count: 1 - path: app/cdash/tests/test_projectwebpage.php - - message: "#^Method ProjectWebPageTestCase\\:\\:testSubmissionBatchmakeBuild\\(\\) has no return type specified\\.$#" count: 1 @@ -26013,7 +25973,7 @@ parameters: - message: "#^Parameter \\#1 \\$array of function array_pop expects array, mixed given\\.$#" - count: 2 + count: 1 path: app/cdash/tests/test_projectwebpage.php - @@ -26028,7 +25988,7 @@ parameters: - message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#" - count: 3 + count: 2 path: app/cdash/tests/test_projectwebpage.php - @@ -26041,11 +26001,6 @@ parameters: count: 1 path: app/cdash/tests/test_projectwebpage.php - - - message: "#^Part \\$siteid \\(mixed\\) of encapsed string cannot be cast to string\\.$#" - count: 1 - path: app/cdash/tests/test_projectwebpage.php - - message: """ #^Call to deprecated method submission\\(\\) of class KWWebTestCase\\: @@ -31888,6 +31843,11 @@ parameters: count: 1 path: server.php + - + message: "#^Cannot access property \\$description on App\\\\Models\\\\SiteInformation\\|null\\.$#" + count: 1 + path: tests/Browser/Pages/SitesIdPageTest.php + - message: "#^Access to an undefined property CDash\\\\Model\\\\Build\\:\\:\\$Endime\\.$#" count: 2 diff --git a/resources/js/vue/app.js b/resources/js/vue/app.js index c470539c44..99ea2c7bf2 100755 --- a/resources/js/vue/app.js +++ b/resources/js/vue/app.js @@ -32,6 +32,7 @@ import AllProjects from './components/AllProjects.vue'; import SubProjectDependencies from './components/SubProjectDependencies.vue'; import BuildTestsPage from './components/BuildTestsPage.vue'; import ProjectSitesPage from './components/ProjectSitesPage.vue'; +import SitesIdPage from './components/SitesIdPage.vue'; import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; import * as FA from '@fortawesome/fontawesome-svg-core'; @@ -62,6 +63,7 @@ const cdash_components = { SubProjectDependencies, BuildTestsPage, ProjectSitesPage, + SitesIdPage, }; /** @@ -106,6 +108,9 @@ const apolloClient = new ApolloClient({ tests: relayStylePagination(), }, }, + Site: { + information: relayStylePagination(), + }, }, }), uri: `${app.config.globalProperties.$baseURL}/graphql`, diff --git a/resources/js/vue/components/SitesIdPage.vue b/resources/js/vue/components/SitesIdPage.vue new file mode 100644 index 0000000000..7c4790a2a4 --- /dev/null +++ b/resources/js/vue/components/SitesIdPage.vue @@ -0,0 +1,427 @@ + + + diff --git a/resources/views/site/view-site.blade.php b/resources/views/site/view-site.blade.php new file mode 100644 index 0000000000..6b1501356e --- /dev/null +++ b/resources/views/site/view-site.blade.php @@ -0,0 +1,8 @@ +@extends('cdash', [ + 'vue' => true, + 'daisyui' => true, +]) + +@section('main_content') + +@endsection diff --git a/routes/web.php b/routes/web.php index 1511272d8e..45ab4cc553 100755 --- a/routes/web.php +++ b/routes/web.php @@ -200,7 +200,7 @@ return redirect("/projects/{$project}/subprojects/dependencies", 301); }); -Route::match(['get', 'post'], '/sites/{siteid}', 'SiteController@viewSite')->whereNumber('siteid'); +Route::match(['get', 'post'], '/sites/{site}', 'SiteController@viewSite'); Route::get('/viewSite.php', function (Request $request) { $siteid = $request->query('siteid'); return redirect("/sites/$siteid", 301); diff --git a/tests/Browser/Pages/ProjectSitesPageTest.php b/tests/Browser/Pages/ProjectSitesPageTest.php index e5b210310d..d5727376d6 100644 --- a/tests/Browser/Pages/ProjectSitesPageTest.php +++ b/tests/Browser/Pages/ProjectSitesPageTest.php @@ -2,6 +2,7 @@ namespace Tests\Browser\Pages; +use Illuminate\Foundation\Testing\DatabaseTruncation; use Illuminate\Support\Str; use Laravel\Dusk\Browser; use Tests\BrowserTestCase; @@ -12,6 +13,7 @@ class ProjectSitesPageTest extends BrowserTestCase { use CreatesProjects; use CreatesSites; + use DatabaseTruncation; public function testSiteDisplaysLatestInformation(): void { diff --git a/tests/Browser/Pages/SitesIdPageTest.php b/tests/Browser/Pages/SitesIdPageTest.php new file mode 100644 index 0000000000..0ad161fc1c --- /dev/null +++ b/tests/Browser/Pages/SitesIdPageTest.php @@ -0,0 +1,241 @@ + + */ + private array $projects = []; + + /** + * @var array + */ + private array $sites = []; + + public function tearDown(): void + { + parent::tearDown(); + + foreach ($this->projects as $project) { + $project->delete(); + } + $this->projects = []; + + foreach ($this->sites as $site) { + $site->delete(); + } + $this->sites = []; + } + + public function testMostRecentSiteDetails(): void + { + $this->sites['site1'] = $this->makeSite(); + $this->sites['site1']->information()->createMany([ + [ + 'totalphysicalmemory' => 5678, + 'numberphysicalcpus' => 2, + ], + [ + 'totalphysicalmemory' => 8765, + 'numberphysicalcpus' => 4, + ], + ]); + + $this->projects['public'] = $this->makePublicProject(); + $this->projects['public']->builds()->create([ + 'name' => 'build1', + 'uuid' => Str::uuid()->toString(), + 'siteid' => $this->sites['site1']->id, + ]); + + $this->browse(function (Browser $browser) { + $browser->visit("/sites/{$this->sites['site1']->id}") + ->whenAvailable('@site-details @site-details-table', function (Browser $browser) { + // Just spot check a couple fields + self::assertEquals('4', $browser->elements('@site-details-table-cell')[6]->getText()); + self::assertEquals('8.56 GiB', $browser->elements('@site-details-table-cell')[8]->getText()); + }) + ->whenAvailable('@site-details @site-description', function (Browser $browser) { + $browser->assertSee('No description provided...'); + }); + }); + + $this->sites['site1']->mostRecentInformation->description = 'abc'; + $this->sites['site1']->mostRecentInformation?->save(); + + $this->browse(function (Browser $browser) { + $browser->visit("/sites/{$this->sites['site1']->id}") + ->whenAvailable('@site-details @site-description', function (Browser $browser) { + $browser->assertSee('abc'); + }); + }); + } + + public function testProjectsList(): void + { + $this->sites['site1'] = $this->makeSite(); + + $this->projects['public1'] = $this->makePublicProject(); + $this->projects['public1']->description = Str::uuid()->toString(); + $this->projects['public1']->save(); + $this->projects['public1']->builds()->create([ + 'name' => 'build1', + 'uuid' => Str::uuid()->toString(), + 'siteid' => $this->sites['site1']->id, + ]); + + $this->projects['public2'] = $this->makePublicProject(); + $this->projects['public2']->description = Str::uuid()->toString(); + $this->projects['public2']->save(); + $this->projects['public2']->builds()->create([ + 'name' => 'build1', + 'uuid' => Str::uuid()->toString(), + 'siteid' => $this->sites['site1']->id, + ]); + + $this->browse(function (Browser $browser) { + $browser->visit("/sites/{$this->sites['site1']->id}") + ->whenAvailable('@site-projects-table', function (Browser $browser) { + $browser->assertSee($this->projects['public1']->name); + $browser->assertSee($this->projects['public2']->name); + $browser->assertSee($this->projects['public1']->description); + $browser->assertSee($this->projects['public2']->description); + }); + }); + } + + public function testHistoryList(): void + { + $this->sites['site1'] = $this->makeSite(); + $this->sites['site1']->information()->createMany([ + [ + 'timestamp' => Carbon::now()->subMinutes(5), + 'totalphysicalmemory' => 5678, + 'numberphysicalcpus' => 2, + ], + [ + 'timestamp' => Carbon::now()->subMinutes(4), + 'totalphysicalmemory' => 8765, + 'numberphysicalcpus' => 4, + ], + [ + 'timestamp' => Carbon::now()->subMinutes(3), + 'totalphysicalmemory' => 8765, + 'numberphysicalcpus' => 4, + 'description' => 'description1', + ], + [ + 'timestamp' => Carbon::now()->subMinutes(2), + 'totalphysicalmemory' => 8765, + 'numberphysicalcpus' => 4, + 'description' => 'description2', + ], + [ + 'timestamp' => Carbon::now(), + 'totalphysicalmemory' => 10765, + 'numberphysicalcpus' => 8, + 'description' => 'description2', + ], + ]); + + $this->browse(function (Browser $browser) { + $browser->visit("/sites/{$this->sites['site1']->id}") + ->whenAvailable('@site-history', function (Browser $browser) { + // Can't use nth child with @ selector unfortunately + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(1)', 'System Update'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(1)', '10.51 GiB'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(1)', '8'); + + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(2)', 'Description Changed'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(2) [data-test="old-description"]', 'description1'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(2) [data-test="new-description"]', 'description2'); + + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(3)', 'Description Changed'); + $browser->assertMissing('[data-test="site-history-item"]:nth-child(3) [data-test="old-description"]'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(3) [data-test="new-description"]', 'description1'); + + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(4)', 'System Update'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(4)', '8.56 GiB'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(4)', '4'); + + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(5)', 'Site Created'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(5)', '5.54 GiB'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(5)', '2'); + }); + }); + } + + /** + * Deduplication is needed until duplicate site information is resolved. + */ + public function testHistoryDeduplication(): void + { + $this->sites['site1'] = $this->makeSite(); + $this->sites['site1']->information()->createMany([ + [ + 'timestamp' => Carbon::now()->subMinutes(5), + 'totalphysicalmemory' => 5678, + 'numberphysicalcpus' => 2, + ], + [ + 'timestamp' => Carbon::now()->subMinutes(4), + 'totalphysicalmemory' => 8765, + 'numberphysicalcpus' => 4, + ], + [ + 'timestamp' => Carbon::now()->subMinutes(3), + 'totalphysicalmemory' => 8765, + 'numberphysicalcpus' => 4, + ], + [ + 'timestamp' => Carbon::now()->subMinutes(2), + 'totalphysicalmemory' => 8765, + 'numberphysicalcpus' => 6, + ], + ]); + + $this->browse(function (Browser $browser) { + $browser->visit("/sites/{$this->sites['site1']->id}") + ->whenAvailable('@site-history', function (Browser $browser) { + // Can't use nth child with @ selector unfortunately + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(1)', 'System Update'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(1)', '8.56 GiB'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(1)', '6'); + + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(2)', 'System Update'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(2)', '8.56 GiB'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(2)', '4'); + + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(3)', 'Site Created'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(3)', '5.54 GiB'); + $browser->assertSeeIn('[data-test="site-history-item"]:nth-child(3)', '2'); + }); + }); + } + + public function testSiteWithNoInformation(): void + { + $this->sites['site1'] = $this->makeSite(); + + $this->browse(function (Browser $browser) { + $browser->visit("/sites/{$this->sites['site1']->id}") + ->whenAvailable('@site-details', function (Browser $browser) { + $browser->assertSee('No information available for this site.'); + }); + }); + } +} diff --git a/tests/BrowserTestCase.php b/tests/BrowserTestCase.php index da77dec12d..79c4bde028 100644 --- a/tests/BrowserTestCase.php +++ b/tests/BrowserTestCase.php @@ -6,18 +6,14 @@ use Facebook\WebDriver\Chrome\ChromeOptions; use Facebook\WebDriver\Remote\DesiredCapabilities; use Facebook\WebDriver\Remote\RemoteWebDriver; -use Illuminate\Foundation\Testing\DatabaseTruncation; use Illuminate\Support\Collection; use Laravel\Dusk\Browser; use Laravel\Dusk\Dusk; use Laravel\Dusk\TestCase as BaseTestCase; -use Nuwave\Lighthouse\Testing\MakesGraphQLRequests; abstract class BrowserTestCase extends BaseTestCase { use CreatesApplication; - use MakesGraphQLRequests; - use DatabaseTruncation; private string $original_env_contents = ''; diff --git a/tests/Feature/GraphQL/SiteTypeTest.php b/tests/Feature/GraphQL/SiteTypeTest.php index 50fdf746c0..51356591dd 100644 --- a/tests/Feature/GraphQL/SiteTypeTest.php +++ b/tests/Feature/GraphQL/SiteTypeTest.php @@ -778,7 +778,7 @@ public function testMultipleSiteInformation(): void 'edges' => [ [ 'node' => [ - 'description' => 'site 1 information 1', + 'description' => 'site 1 information 3', ], ], [ @@ -788,7 +788,7 @@ public function testMultipleSiteInformation(): void ], [ 'node' => [ - 'description' => 'site 1 information 3', + 'description' => 'site 1 information 1', ], ], ], diff --git a/tests/cypress/e2e/sites.cy.js b/tests/cypress/e2e/sites.cy.js index 45e0a84df7..18d75ade08 100644 --- a/tests/cypress/e2e/sites.cy.js +++ b/tests/cypress/e2e/sites.cy.js @@ -24,45 +24,3 @@ describe('site statistics', () => { }); }); }); - -describe('site page', () => { - it('can navigated to from the index page', () => { - cy.visit('index.php?subproject=Teuchos&project=Trilinos'); - cy.get('tbody').contains('a', 'hut11.kitware').click(); - cy.url().should('contain', 'sites/'); - }); - - it('loads the expected info for the site', () => { - cy.visit('index.php?subproject=Teuchos&project=Trilinos'); - cy.get('tbody').contains('a', 'hut11.kitware').click(); - - cy.get('#subheadername').should('contain', 'hut11.kitware'); - - // Since the site created timestamp isn't the same time as the submission, no site information exists. - // We clear the timestamp to get the latest information. TODO: Find a better way to do this... - cy.url().then(url => { - cy.visit(url.slice(0, -23)); - }); - - cy.get('#subheadername').should('contain', 'hut11.kitware'); - cy.get('#main_content') - .should('contain', 'Processor Speed:') - .and('contain', 'Processor Vendor:') - .and('contain', 'Number of CPUs:') - .and('contain', 'Number of Cores:') - .and('contain', 'Total Physical Memory:') - .and('contain', 'This site belongs to the following projects:'); - cy.get('#main_content').find('a').each(project_url => { - cy.wrap(project_url).should('have.attr', 'href').and('contain', 'index.php?project='); - }); - }); - - it('loads the "Time spent" graph', () => { - cy.visit('index.php?project=BatchmakeExample'); - cy.get('tbody').contains('a', 'Dash20.kitware').click(); - cy.get('#main_content').should('contain', 'Time spent per project'); - - // TODO: (sbelsk) test this graph more thoroughly once it's in d3 - cy.get('#placeholder').find('canvas').should('exist'); - }); -});