Skip to content

Commit

Permalink
Load build results on dashboard via AJAX
Browse files Browse the repository at this point in the history
So it works like the dashboard on GitHub. Additionally, when submitting
the filter form the whole page doesn't need to reload anymore.

See https://progress.opensuse.org/issues/55112
  • Loading branch information
Martchus committed Sep 27, 2019
1 parent 5c37094 commit e0cb63c
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 73 deletions.
6 changes: 5 additions & 1 deletion assets/javascripts/filter_form.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function setupFilterForm() {
function setupFilterForm(options) {
// make filter form expandable
$('#filter-panel .card-header').on('click', function() {
$('#filter-panel .card-body').toggle(200);
Expand All @@ -13,6 +13,10 @@ function setupFilterForm() {
event.stopPropagation();
});

if (options && options.preventLoadingIndication) {
return;
}

$('#filter-form').on('submit', function(event) {
if($('#filter-form').serialize() !== window.location.search.substring(1)) {
// show progress indication
Expand Down
47 changes: 46 additions & 1 deletion assets/javascripts/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
function setupIndexPage() {
$('.timeago').timeago();

setupFilterForm();
setupFilterForm({preventLoadingIndication: true});
$('#filter-show-tags').prop('checked', false);
$('#filter-only-tagged').prop('checked', false);
$('#filter-fullscreen').prop('checked', false);
Expand Down Expand Up @@ -40,4 +40,49 @@ function setupIndexPage() {
return 'expanded';
}
});

setupBuildResults();
}

function setupBuildResults(queryParams) {
var buildResultsElement = $('#build-results');
var loadingElement = $('#build-results-loading');
var filterForm = $('#filter-form');
var filterFormApplyButton = $('#filter-apply-button');

loadingElement.show();
buildResultsElement.html('');
filterFormApplyButton.prop('disabled', true);
window.updatingBuildResults = true;

var showBuildResults = function(buildResults) {
loadingElement.hide();
buildResultsElement.html(buildResults);
filterFormApplyButton.prop('disabled', false);
window.updatingBuildResults = false;
};

// query build results via AJAX using parameters from filter form
$.ajax({
url: buildResultsElement.data('build-results-url'),
data: queryParams ? queryParams : window.location.search.substr(1),
success: function(response) {
showBuildResults(response);
window.buildResultStatus = 'success';
},
error: function(xhr, ajaxOptions, thrownError) {
showBuildResults('<div class="alert alert-danger" role="alert">Unable to fetch build results.</div>');
window.buildResultStatus = 'error: ' + thrownError;
}
});

// prevent page reload when submitting filter form (when we load build results via AJAX anyways)
filterForm.submit(function(event) {
if (!window.updatingBuildResults) {
var queryParams = filterForm.serialize();
setupBuildResults(queryParams);
history.replaceState({} , document.title, window.location.pathname + '?' + queryParams);
}
event.preventDefault();
});
}
5 changes: 3 additions & 2 deletions lib/OpenQA/WebAPI.pm
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,9 @@ sub startup {

# Favicon
$r->get('/favicon.ico' => sub { my $c = shift; $c->render_static('favicon.ico') });
$r->get('/index' => [format => ['html', 'json']])->to('main#index');
$r->get('/api_help' => sub { shift->render('admin/api_help') })->name('api_help');
$r->get('/index')->to('main#index');
$r->get('/dashboard_build_results')->name('dashboard_build_results')->to('main#dashboard_build_results');
$r->get('/api_help' => sub { shift->render('admin/api_help') })->name('api_help');

# Default route
$r->get('/')->name('index')->to('main#index');
Expand Down
28 changes: 19 additions & 9 deletions lib/OpenQA/WebAPI/Controller/Main.pm
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,22 @@ sub _map_tags_into_build {
return;
}

sub index {
sub dashboard_build_results {
my ($self) = @_;

log_error("In dashboard_build_results");

my $limit_builds = $self->param('limit_builds');
$limit_builds = 3 unless looks_like_number($limit_builds);
my $time_limit_days = $self->param('time_limit_days');
$time_limit_days = 14 unless looks_like_number($time_limit_days);
$self->app->log->debug("Retrieving results for up to $limit_builds builds up to $time_limit_days days old");
my $only_tagged = $self->param('only_tagged') // 0;
my $default_expanded = $self->param('default_expanded') // 0;
my $show_tags = $self->param('show_tags') // $only_tagged;
my $group_params = $self->every_param('group');
my @results;
my $groups = $self->stash('job_groups_and_parents');
my $groups = $self->stash('job_groups_and_parents');

my @results;
for my $group (@$groups) {
if (@$group_params) {
next unless grep { $_ eq '' || $group->matches_nested($_) } @$group_params;
Expand All @@ -72,13 +73,22 @@ sub index {
}
push(@results, $build_results) if @{$build_results_for_group};
}
$self->stash('limit_builds', $limit_builds);
$self->stash('time_limit_days', $time_limit_days);
$self->stash('default_expanded', $default_expanded);
$self->stash('results', \@results);

$self->stash(
default_expanded => $default_expanded,
results => \@results,
);

$self->respond_to(
json => {json => {results => \@results}},
html => {template => 'main/index'});
html => {template => 'main/dashboard_build_results'},
);
}

sub index {
my ($self) = @_;

$self->render(html => {template => 'main/index'});
}

sub group_overview {
Expand Down
6 changes: 3 additions & 3 deletions t/17-build_tagging.t
Original file line number Diff line number Diff line change
Expand Up @@ -155,17 +155,17 @@ subtest 'only_tagged=1 query parameter shows only tagged (poo#11052)' => sub {
$t->get_ok('/group_overview/1001?only_tagged=0')->status_is(200);
is(scalar @{$t->tx->res->dom->find('a[href^=/tests/]')}, 5, 'all builds shown again (on group overview)');

$t->get_ok('/?only_tagged=1')->status_is(200);
$t->get_ok('/dashboard_build_results?only_tagged=1')->status_is(200);
is(scalar @{$t->tx->res->dom->find('a[href^=/tests/]')}, 1, 'only one tagged build is shown (on index page)');
is(scalar @{$t->tx->res->dom->find('h2')}, 1, 'only one group shown anymore');
$t->get_ok('/?only_tagged=0')->status_is(200);
$t->get_ok('/dashboard_build_results?only_tagged=0')->status_is(200);
is(scalar @{$t->tx->res->dom->find('a[href^=/tests/]')}, 4, 'all builds shown again (on index page)');
is(scalar @{$t->tx->res->dom->find('h2')}, 2, 'two groups shown again');
};

subtest 'show_tags query parameter enables/disables tags on index page' => sub {
for my $enabled (0, 1) {
$t->get_ok('/?show_tags=' . $enabled)->status_is(200);
$t->get_ok('/dashboard_build_results?show_tags=' . $enabled)->status_is(200);
is(scalar @{$t->tx->res->dom->find('a[href^=/tests/]')},
4, "all builds shown on index page (show_tags=$enabled)");
is(scalar @{$t->tx->res->dom->find("i[title='important']")},
Expand Down
34 changes: 17 additions & 17 deletions t/22-dashboard.t
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2016 SUSE LLC
# Copyright (C) 2016-2019 SUSE LLC
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand All @@ -14,7 +14,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

# see also t/ui/14-dashboard.t and t/ui/14-dashboard-parents.t for PhantomJS test
# see also t/ui/14-dashboard.t and t/ui/14-dashboard-parents.t for Selenium test

use Mojo::Base -strict;

Expand All @@ -37,14 +37,14 @@ my $parent_groups = $t->app->schema->resultset('JobGroupParents');
my $jobs = $t->app->schema->resultset('Jobs');

# regular job groups shown
$t->get_ok('/')->status_is(200);
$t->get_ok('/dashboard_build_results')->status_is(200);
my @h2 = $t->tx->res->dom->find('h2 a')->map('text')->each;
is_deeply(\@h2, ['opensuse', 'opensuse test'], 'two groups shown (from fixtures)');

# create (initially) empty parent group
my $test_parent = $parent_groups->create({name => 'Test parent', sort_order => 2});

$t->get_ok('/')->status_is(200);
$t->get_ok('/dashboard_build_results')->status_is(200);
@h2 = $t->tx->res->dom->find('h2 a')->map('text')->each;
is_deeply(\@h2, ['opensuse', 'opensuse test'], 'empty parent group not shown');

Expand All @@ -60,7 +60,7 @@ like(
'parent name also shown on group overview'
);

$t->get_ok('/')->status_is(200);
$t->get_ok('/dashboard_build_results')->status_is(200);
@h2 = $t->tx->res->dom->find('h2 a')->map('text')->each;
is_deeply(\@h2, ['opensuse test', 'Test parent'], 'parent group shown and opensuse is no more on top-level');

Expand All @@ -70,7 +70,7 @@ is_deeply(\@h4, [qw(Build87.5011 Build0048@0815 Build0048)], 'builds on parent-l
is_deeply(\@h4, ['opensuse', 'opensuse', 'opensuse'], 'opensuse now shown as child group (for each build)');

# check build limit
$t->get_ok('/?limit_builds=2')->status_is(200);
$t->get_ok('/dashboard_build_results?limit_builds=2')->status_is(200);
@h4 = $t->tx->res->dom->find('div.children-collapsed .h4 a')->map('text')->each;
is_deeply(\@h4, [qw(Build87.5011 Build0048@0815)], 'builds on parent-level shown (limit builds)');
@h4 = $t->tx->res->dom->find('div.collapse .h4 a')->map('text')->each;
Expand All @@ -83,7 +83,7 @@ $opensuse_test_group->update({parent_id => $test_parent->id});
# and add review for build 0048@0815
$opensuse_group->jobs->find({BUILD => '0048@0815'})->comments->create({text => 'poo#1234', user_id => 99901});

$t->get_ok('/?limit_builds=20&show_tags=1')->status_is(200);
$t->get_ok('/dashboard_build_results?limit_builds=20&show_tags=1')->status_is(200);
@h2 = $t->tx->res->dom->find('h2 a')->map('text')->each;
is_deeply(\@h2, ['Test parent'], 'only parent shown, no more top-level job groups');

Expand Down Expand Up @@ -160,15 +160,15 @@ check_test_parent('expanded');
my $tag_for_0092_comment = $opensuse_group->comments->create({text => 'tag:0092:important:some_tag', user_id => 99901});

sub check_tags {
$t->get_ok('/?limit_builds=20&show_tags=1')->status_is(200);
$t->get_ok('/dashboard_build_results?limit_builds=20&show_tags=1')->status_is(200);
my @tags = $t->tx->res->dom->find('div.children-collapsed span i.tag')->map('text')->each;
is_deeply(\@tags, ['some_tag'], 'tag is shown on parent-level');

$t->get_ok('/parent_group_overview/' . $test_parent->id . '?limit_builds=20&show_tags=1')->status_is(200);
@tags = $t->tx->res->dom->find('div.children-expanded span i.tag')->map('text')->each;
is_deeply(\@tags, ['some_tag'], 'tag is shown on parent-level');

$t->get_ok('/?limit_builds=20&only_tagged=1')->status_is(200);
$t->get_ok('/dashboard_build_results?limit_builds=20&only_tagged=1')->status_is(200);
@tags = $t->tx->res->dom->find('div.children-collapsed span i.tag')->map('text')->each;
is_deeply(\@tags, ['some_tag'], 'tag is shown on parent-level (only tagged)');
@h4 = $t->tx->res->dom->find("div.children-collapsed .h4 a")->map('text')->each;
Expand All @@ -182,7 +182,7 @@ check_tags();

# use version-build format where version doesn't matches
$tag_for_0092_comment->update({text => 'tag:5-0092:important:some_tag', user_id => 99901});
$t->get_ok('/?limit_builds=20&only_tagged=1')->status_is(200);
$t->get_ok('/dashboard_build_results?limit_builds=20&only_tagged=1')->status_is(200);
my @tags = $t->tx->res->dom->find('div.children-collapsed .h4 span i.tag')->map('text')->each;
is_deeply(\@tags, [], 'tag is not shown on parent-level because version does not match');
@h4 = $t->tx->res->dom->find("div.children-collapsed .h4 a")->map('text')->each;
Expand All @@ -193,7 +193,7 @@ $tag_for_0092_comment->delete();
my $tag_for_0091_comment
= $opensuse_test_group->comments->create({text => 'tag:0091:important:some_tag', user_id => 99901});

$t->get_ok('/?limit_builds=20&only_tagged=1')->status_is(200);
$t->get_ok('/dashboard_build_results?limit_builds=20&only_tagged=1')->status_is(200);
@h4 = $t->tx->res->dom->find("div.children-collapsed .h4 a")->map('text')->each;
is_deeply(\@h4, ['Build0091'], 'only tagged builds on parent-level shown (common build)');
@h4 = $t->tx->res->dom->find('div#group' . $test_parent->id . '_build13_1-0091 .h4 a')->map('text')->each;
Expand Down Expand Up @@ -224,7 +224,7 @@ $t->app->schema->resultset('JobModules')->create(
});

my $review_build_id = '-Factory-0048_0815';
$t->get_ok('/?limit_builds=20')->status_is(200);
$t->get_ok('/dashboard_build_results?limit_builds=20')->status_is(200);
$t->element_count_is('#review-' . $test_parent->id . $review_build_id,
0, 'review badge NOT shown for build 0048@0815 anymore');
$t->element_count_is('#child-review-' . $test_parent->id . $review_build_id,
Expand All @@ -242,18 +242,18 @@ sub check_auto_badge {
$all_passed_count, "all passed review badge shown for build $build on child-level");
}
# all passed
$t->get_ok('/?limit_builds=20')->status_is(200);
$t->get_ok('/dashboard_build_results?limit_builds=20')->status_is(200);
check_auto_badge(1);
# all passed or softfailed
$jobs->find({id => 99947})->update({result => OpenQA::Jobs::Constants::SOFTFAILED});
$t->get_ok('/?limit_builds=20')->status_is(200);
$t->get_ok('/dashboard_build_results?limit_builds=20')->status_is(200);
check_auto_badge(1);
$jobs->find({id => 99947})->update({result => OpenQA::Jobs::Constants::PASSED});

sub check_badge {
my ($reviewed_count, $msg, $build) = @_;
$build //= 'Factory-0048';
$t->get_ok('/?limit_builds=20')->status_is(200);
$t->get_ok('/dashboard_build_results?limit_builds=20')->status_is(200);
$t->element_count_is('#review-' . $test_parent->id . '-' . $build, $reviewed_count, $msg . ' (parent-level)');
$t->element_count_is('#child-review-' . $test_parent->id . '-' . $build, $reviewed_count, $msg . ' (child-level)');
}
Expand Down Expand Up @@ -329,7 +329,7 @@ check_badge(0, 'no badge when no failed, reviewed softfailed with failing module
# change DISTRI/VERSION of test in opensuse group to test whether links are still correct then
$opensuse_group->jobs->update({VERSION => '14.2', DISTRI => 'suse'});

$t->get_ok('/?limit_builds=20&show_tags=0')->status_is(200);
$t->get_ok('/dashboard_build_results?limit_builds=20&show_tags=0')->status_is(200);
@urls = $t->tx->res->dom->find('.h4 a')->each;
is(scalar @urls, 12, 'now builds belong to different versions and are split');
is(
Expand All @@ -354,7 +354,7 @@ subtest 'build which has jobs with different DISTRIs links to overview with all
VERSION => '14.2',
MACHINE => '32bit',
});
$t->get_ok('/?limit_builds=20&show_tags=0')->status_is(200);
$t->get_ok('/dashboard_build_results?limit_builds=20&show_tags=0')->status_is(200);
my @urls = $t->tx->res->dom->find('.h4 a')->each;
is(scalar @urls, 12, 'still 12 builds shown');
my $first_url = $urls[1]->attr('href');
Expand Down
4 changes: 2 additions & 2 deletions t/api/04-jobs.t
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#! /usr/bin/perl

# Copyright (C) 2015-2017 SUSE LLC
# Copyright (C) 2015-2019 SUSE LLC
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -608,7 +608,7 @@ subtest 'json representation of group overview (actually not part of the API)' =
);
};

$t->get_ok('/index.json?limit_builds=10')->status_is(200);
$t->get_ok('/dashboard_build_results.json?limit_builds=10')->status_is(200);
my $ret = $t->tx->res->json;
is(@{$ret->{results}}, 2);
my $g1 = (shift @{$ret->{results}});
Expand Down
2 changes: 1 addition & 1 deletion t/api/10-jobgroups.t
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ subtest 'create job group' => sub() {
'list created job group'
);

$t->get_ok('/index.json')->status_is(200);
$t->get_ok('/dashboard_build_results.json')->status_is(200);
my $res = $t->tx->res->json;
is(@{$res->{results}}, 2, 'empty job groups are not shown on index page');

Expand Down
12 changes: 9 additions & 3 deletions t/ui/14-dashboard-parents.t
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#! /usr/bin/perl

# Copyright (C) 2016-2018 SUSE LLC
# Copyright (C) 2016-2019 SUSE LLC
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -98,6 +98,7 @@ my $baseurl = $driver->get_current_url();

$driver->get($baseurl . '?limit_builds=20');
disable_bootstrap_animations();
wait_for_ajax();

# test expanding/collapsing
is(scalar @{$driver->find_elements('opensuse', 'link_text')}, 0, 'link to child group collapsed (in the first place)');
Expand All @@ -115,6 +116,7 @@ ok($driver->find_element('#group1_build13_1-0091 .h4 a')->is_hidden(), 'link to
# go to parent group overview
$driver->find_element_by_link_text('Test parent')->click();
disable_bootstrap_animations();
wait_for_ajax();

ok($driver->find_element('#group1_build13_1-0091 .h4 a')->is_displayed(), 'link to child group displayed');
my @links = $driver->find_elements('.h4 a', 'css');
Expand All @@ -129,11 +131,14 @@ isnt(scalar @{$driver->find_elements('opensuse', 'link_text')}, 0, "child group
$driver->find_element_by_class('navbar-brand')->click();
$driver->find_element_by_link_text('Test parent 2')->click();
disable_bootstrap_animations();
wait_for_ajax();
isnt(scalar @{$driver->find_elements('opensuse', 'link_text')}, 0, "child group 'opensuse' in 'Test parent 2'");

# test filtering for nested groups
subtest 'filtering subgroups' => sub {
$driver->get('/');
disable_bootstrap_animations();
wait_for_ajax();
my $url = $driver->get_current_url;
$driver->find_element('#filter-panel .card-header')->click();
$driver->find_element_by_id('filter-group')->send_keys('Test parent / .* test$');
Expand All @@ -144,8 +149,9 @@ subtest 'filtering subgroups' => sub {
$ele = $driver->find_element_by_id('filter-time-limit-days');
$ele->click();
$ele->send_keys(Selenium::Remote::WDKeys->KEYS->{end}, '0'); # appended
$driver->find_element('#filter-form button')->click();
$url .= '?group=Test+parent+%2F+.*+test%24&default_expanded=1&limit_builds=30&time_limit_days=140#';
$driver->find_element('#filter-apply-button')->click();
wait_for_ajax();
$url .= '?group=Test%20parent%20%2F%20.*%20test%24&default_expanded=1&limit_builds=30&time_limit_days=140';
is($driver->get_current_url, $url, 'URL parameters for filter are correct');
is(scalar @{$driver->find_elements('opensuse', 'link_text')}, 0, "child group 'opensuse' filtered out");
isnt(scalar @{$driver->find_elements('opensuse test', 'link_text')}, 0, "child group 'opensuse test' present'");
Expand Down
Loading

0 comments on commit e0cb63c

Please sign in to comment.