Skip to content

Commit

Permalink
Implement basic build measurements UI
Browse files Browse the repository at this point in the history
  • Loading branch information
williamjallen committed Oct 14, 2024
1 parent 1e44e4f commit a1f2477
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 0 deletions.
10 changes: 10 additions & 0 deletions app/Http/Controllers/BuildController.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ public function tests(int $build_id): View
->with('filters', $filters);
}

public function measurements(int $build_id): View
{
$this->setBuildById($build_id);

$filters = json_decode(request()->get('filters')) ?? ['all' => []];

return $this->view('build.measurements', 'Build Measurements')
->with('filters', $filters);
}

protected function renderBuildPage(int $build_id, string $page_name, string $page_title = '')
{
$this->setBuildById($build_id);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('buildmeasurements', function (Blueprint $table) {
$table->unique(['buildid', 'source', 'type', 'name']);
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('buildmeasurements', function (Blueprint $table) {
$table->dropUnique(['buildid', 'source', 'type', 'name']);
});
}
};
2 changes: 2 additions & 0 deletions resources/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import ViewDynamicAnalysis from './components/ViewDynamicAnalysis.vue';
import AllProjects from './components/AllProjects.vue';
import SubProjectDependencies from './components/SubProjectDependencies.vue';
import BuildTestsPage from './components/BuildTestsPage.vue';
import BuildMeasurementsPage from './components/BuildMeasurementsPage.vue';

import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import * as FA from '@fortawesome/fontawesome-svg-core';
Expand Down Expand Up @@ -60,6 +61,7 @@ const cdash_components = {
AllProjects,
SubProjectDependencies,
BuildTestsPage,
BuildMeasurementsPage,
};

/**
Expand Down
152 changes: 152 additions & 0 deletions resources/js/components/BuildMeasurementsPage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<template>
<div class="tw-flex tw-flex-col tw-w-full tw-gap-4">
<filter-builder
filter-type="BuildMeasurementsFiltersMultiFilterInput"
primary-record-name="build measurements"
:initial-filters="initialFilters"
:execute-query-link="executeQueryLink"
@changeFilters="filters => changedFilters = filters"
/>
<loading-indicator :is-loading="!build">
<data-table
:columns="columns"
:rows="formattedMeasurementRows"
:full-width="true"
initial-sort-column="source"
/>
</loading-indicator>
</div>
</template>

<script>
import DataTable from './shared/DataTable.vue';
import gql from 'graphql-tag';
import FilterBuilder from './shared/FilterBuilder.vue';
import LoadingIndicator from './shared/LoadingIndicator.vue';
export default {
components: {
LoadingIndicator,
FilterBuilder,
DataTable,
},
props: {
buildId: {
type: Number,
required: true,
},
initialFilters: {
type: Object,
required: true,
},
},
apollo: {
build: {
query: gql`
query($buildid: ID, $filters: BuildMeasurementsFiltersMultiFilterInput, $after: String) {
build(id: $buildid) {
measurements(filters: $filters, after: $after, first: 100) {
edges {
node {
id
name
source
type
value
}
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
}
}
}
`,
variables() {
return {
buildid: this.buildId,
filters: this.initialFilters,
after: '',
};
},
result({data}) {
if (data && data.build.measurements.pageInfo.hasNextPage) {
this.$apollo.queries.build.fetchMore({
variables: {
after: data.build.measurements.pageInfo.endCursor,
},
});
}
},
},
},
data() {
return {
changedFilters: JSON.parse(JSON.stringify(this.initialFilters)),
};
},
computed: {
executeQueryLink() {
return `${window.location.origin}${window.location.pathname}?filters=${encodeURIComponent(JSON.stringify(this.changedFilters))}`;
},
columns() {
const uniqueMeasurements = [...new Set(this.build.measurements.edges.map(edge => {
return edge.node.name;
}))];
const columns = [
{
name: 'source',
displayName: 'Source',
expand: true,
},
{
name: 'type',
displayName: 'Type',
},
];
uniqueMeasurements.forEach(element => {
columns.push({
name: `measurement_${element}`,
displayName: element,
});
});
return columns;
},
formattedMeasurementRows() {
// A mapping of the form: source_type => {row object}
const source_type_pairs = {};
this.build.measurements.edges.forEach(edge => {
const key = `${edge.node.source}_${edge.node.type}`;
if (!source_type_pairs.hasOwnProperty(key)) {
source_type_pairs[key] = {
source: edge.node.source,
type: edge.node.type,
};
}
source_type_pairs[key][`measurement_${edge.node.name}`] = {
value: isNaN(edge.node.value) ? edge.node.value : Number(edge.node.value),
text: edge.node.value,
};
});
return Object.values(source_type_pairs);
},
},
};
</script>
8 changes: 8 additions & 0 deletions resources/views/build/measurements.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@extends('cdash', [
'vue' => true,
'daisyui' => true,
])

@section('main_content')
<build-measurements-page :build-id="{{ $build->Id }}" :initial-filters="@js($filters)"></build-measurements-page>
@endsection
2 changes: 2 additions & 0 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@

Route::get('/builds/{build_id}/tests', 'BuildController@tests');

Route::get('/builds/{build_id}/measurements', 'BuildController@measurements');

Route::get('/builds/{id}/update', 'BuildController@update');
Route::permanentRedirect('/build/{id}/update', url('/builds/{id}/update'));
Route::get('/viewUpdate.php', function (Request $request) {
Expand Down
3 changes: 3 additions & 0 deletions tests/cypress/e2e/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,6 @@ set_tests_properties(cypress/e2e/build-configure PROPERTIES DEPENDS cypress/e2e/

add_cypress_e2e_test(all-projects)
set_tests_properties(cypress/e2e/all-projects PROPERTIES DEPENDS cypress/e2e/build-configure)

add_cypress_e2e_test(build-measurements)
set_tests_properties(cypress/e2e/all-projects PROPERTIES DEPENDS cypress/e2e/all-project)
15 changes: 15 additions & 0 deletions tests/cypress/e2e/build-measurements.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* TODO: Fill out this test once seed data for the build measurements functionality becomes available.
* For now, we just verify that the page loads with no errors.
*/
describe('Build measurements page', () => {
it('Loads page successfully', () => {
cy.visit('/builds/372/measurements');
cy.get('#headername2').should('not.contain', '404 Not Found');
});

it('Shows 404 if build does not exist', () => {
cy.visit('/builds/12345678/measurements');
cy.get('#headername2').should('contain', '404 Not Found');
});
});

0 comments on commit a1f2477

Please sign in to comment.