15133: Move removing jobs API code+tests
[arvados.git] / apps / workbench / test / integration / pipeline_instances_test.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 require 'integration_helper'
6
7 class PipelineInstancesTest < ActionDispatch::IntegrationTest
8   setup do
9     need_javascript
10   end
11
12   def parse_browser_timestamp t
13     # Timestamps are displayed in the browser's time zone (which can
14     # differ from ours) and they come from toLocaleTimeString (which
15     # means they don't necessarily tell us which time zone they're
16     # using). In order to make sense of them, we need to ask the
17     # browser to parse them and generate a timestamp that can be
18     # parsed reliably.
19     #
20     # Note: Even with all this help, phantomjs seem to behave badly
21     # when parsing timestamps on the other side of a DST transition.
22     # See skipped tests below.
23
24     # In some locales (e.g., en_CA.UTF-8) Firefox can't parse what its
25     # own toLocaleString() puts out.
26     t.sub!(/(\d\d\d\d)-(\d\d)-(\d\d)/, '\2/\3/\1')
27
28     if /(\d+:\d+ [AP]M) (\d+\/\d+\/\d+)/ =~ t
29       # Currently dates.js renders timestamps as
30       # '{t.toLocaleTimeString()} {t.toLocaleDateString()}' which even
31       # en_US browsers can't make sense of. First we need to flip it
32       # around so it looks like what toLocaleString() would have made.
33       t = $~[2] + ', ' + $~[1]
34     end
35
36     utc = page.evaluate_script("new Date('#{t}').toUTCString()")
37     DateTime.parse(utc).to_time
38   end
39
40   if false
41     # No need to test (or mention) these all the time. If they start
42     # working (without need_selenium) then some real tests might not
43     # need_selenium any more.
44
45     test 'phantomjs DST' do
46       skip '^^'
47       t0s = '3/8/2015, 01:59 AM'
48       t1s = '3/8/2015, 03:01 AM'
49       t0 = parse_browser_timestamp t0s
50       t1 = parse_browser_timestamp t1s
51       assert_equal 120, t1-t0, "'#{t0s}' to '#{t1s}' was reported as #{t1-t0} seconds, should be 120"
52     end
53
54     test 'phantomjs DST 2' do
55       skip '^^'
56       t0s = '2015-03-08T10:43:00Z'
57       t1s = '2015-03-09T03:43:00Z'
58       t0 = parse_browser_timestamp page.evaluate_script("new Date('#{t0s}').toLocaleString()")
59       t1 = parse_browser_timestamp page.evaluate_script("new Date('#{t1s}').toLocaleString()")
60       assert_equal 17*3600, t1-t0, "'#{t0s}' to '#{t1s}' was reported as #{t1-t0} seconds, should be #{17*3600} (17 hours)"
61     end
62   end
63
64   test 'view pipeline with job and see graph' do
65     visit page_with_token('active_trustedclient', '/pipeline_instances')
66     assert page.has_text? 'pipeline_with_job'
67
68     find('a', text: 'pipeline_with_job').click
69
70     # since the pipeline component has a job, expect to see the graph
71     assert page.has_text? 'Graph'
72     click_link 'Graph'
73     page.assert_selector "#provenance_graph"
74   end
75
76   test 'pipeline description' do
77     visit page_with_token('active_trustedclient', '/pipeline_instances')
78     assert page.has_text? 'pipeline_with_job'
79
80     find('a', text: 'pipeline_with_job').click
81
82     within('.arv-description-as-subtitle') do
83       find('.fa-pencil').click
84       find('.editable-input textarea').set('*Textile description for pipeline instance*')
85       find('.editable-submit').click
86     end
87     wait_for_ajax
88
89     # verify description
90     assert page.has_no_text? '*Textile description for pipeline instance*'
91     assert page.has_text? 'Textile description for pipeline instance'
92   end
93
94   test "JSON popup available for strange components" do
95     uuid = api_fixture("pipeline_instances")["components_is_jobspec"]["uuid"]
96     visit page_with_token("active", "/pipeline_instances/#{uuid}")
97     click_on "Components"
98     assert(page.has_no_text?("script_parameters"),
99            "components JSON visible without popup")
100     click_on "Show components JSON"
101     assert(page.has_text?("script_parameters"),
102            "components JSON not found")
103   end
104
105   def create_pipeline_from(template_name, project_name="Home")
106     # Visit the named pipeline template and create a pipeline instance from it.
107     # The instance will be created under the named project.
108     template_uuid = api_fixture("pipeline_templates", template_name, "uuid")
109     visit page_with_token("active", "/pipeline_templates/#{template_uuid}")
110     click_on "Run this pipeline"
111     within(".modal-dialog") do # FIXME: source of 3 test errors
112       # Set project for the new pipeline instance
113       find(".selectable", text: project_name).click
114       click_on "Choose"
115     end
116     assert(has_text?("This pipeline was created from the template"),
117            "did not land on pipeline instance page")
118   end
119
120   [
121     ['user1_with_load', 'zzzzz-d1hrv-10pipelines0001', 0], # run time 0 minutes
122     ['user1_with_load', 'zzzzz-d1hrv-10pipelines0010', 17*60*60 + 51*60], # run time 17 hours and 51 minutes
123     ['active', 'zzzzz-d1hrv-runningpipeline', nil], # state = running
124   ].each do |user, uuid, run_time|
125     test "pipeline start and finish time display for #{uuid}" do
126       need_selenium 'to parse timestamps correctly across DST boundaries'
127       visit page_with_token(user, "/pipeline_instances/#{uuid}")
128
129       regexp = "This pipeline started at (.+?)\\. "
130       if run_time
131         regexp += "It failed after (.+?) at (.+?)\\. Check the Log"
132       else
133         regexp += "It has been active for \\d"
134       end
135       assert_match /#{regexp}/, page.text
136
137       return if !run_time
138
139       # match again to capture (.*)
140       _, started, duration, finished = *(/#{regexp}/.match(page.text))
141       assert_equal(
142         run_time,
143         parse_browser_timestamp(finished) - parse_browser_timestamp(started),
144         "expected: #{run_time}, got: started #{started}, finished #{finished}, duration #{duration}")
145     end
146   end
147
148   [
149     ['fuse', nil, 2, 20],                           # has 2 as of 11-07-2014
150     ['user1_with_load', '000025pipelines', 25, 25], # owned_by the project zzzzz-j7d0g-000025pipelines, two pages
151     ['admin', 'pipeline_20', 1, 1],
152     ['active', 'no such match', 0, 0],
153   ].each do |user, search_filter, expected_min, expected_max|
154     test "scroll pipeline instances page for #{user} with search filter #{search_filter}
155           and expect #{expected_min} <= found_items <= #{expected_max}" do
156       visit page_with_token(user, "/pipeline_instances")
157
158       if search_filter
159         find('.recent-pipeline-instances-filterable-control').set(search_filter)
160         # Wait for 250ms debounce timer (see filterable.js)
161         sleep 0.350
162         wait_for_ajax
163       end
164
165       page_scrolls = expected_max/20 + 2    # scroll num_pages+2 times to test scrolling is disabled when it should be
166       within('.arv-recent-pipeline-instances') do
167         (0..page_scrolls).each do |i|
168           page.driver.scroll_to 0, 999000
169           begin
170             wait_for_ajax
171           rescue
172           end
173         end
174       end
175
176       # Verify that expected number of pipeline instances are found
177       found_items = page.all('tr[data-kind="arvados#pipelineInstance"]')
178       found_count = found_items.count
179       if expected_min == expected_max
180         assert_equal(true, found_count == expected_min,
181           "Not found expected number of items. Expected #{expected_min} and found #{found_count}")
182         assert page.has_no_text? 'request failed'
183       else
184         assert_equal(true, found_count>=expected_min,
185           "Found too few items. Expected at least #{expected_min} and found #{found_count}")
186         assert_equal(true, found_count<=expected_max,
187           "Found too many items. Expected at most #{expected_max} and found #{found_count}")
188       end
189     end
190   end
191
192   test 'render job run time when job record is inaccessible' do
193     pi = api_fixture('pipeline_instances', 'has_component_with_completed_jobs')
194     visit page_with_token 'active', '/pipeline_instances/' + pi['uuid']
195     assert_text 'Queued for '
196   end
197
198   test "job logs linked for running pipeline" do
199     pi = api_fixture("pipeline_instances", "running_pipeline_with_complete_job")
200     visit(page_with_token("active", "/pipeline_instances/#{pi['uuid']}"))
201     find(:xpath, "//a[@href='#Log']").click
202     within "#Log" do
203       assert_text "Log for previous"
204       log_link = find("a", text: "Log for previous")
205       assert_includes(log_link[:href],
206                       "/jobs/#{pi["components"]["previous"]["job"]["uuid"]}#Log")
207       assert_selector "#event_log_div"
208     end
209   end
210
211   test "job logs linked for complete pipeline" do
212     pi = api_fixture("pipeline_instances", "complete_pipeline_with_two_jobs")
213     visit(page_with_token("active", "/pipeline_instances/#{pi['uuid']}"))
214     find(:xpath, "//a[@href='#Log']").click
215     within "#Log" do
216       assert_text "Log for previous"
217       pi["components"].each do |cname, cspec|
218         log_link = find("a", text: "Log for #{cname}")
219         assert_includes(log_link[:href], "/jobs/#{cspec["job"]["uuid"]}#Log")
220       end
221       assert_no_selector "#event_log_div"
222     end
223   end
224
225   test "job logs linked for failed pipeline" do
226     pi = api_fixture("pipeline_instances", "failed_pipeline_with_two_jobs")
227     visit(page_with_token("active", "/pipeline_instances/#{pi['uuid']}"))
228     find(:xpath, "//a[@href='#Log']").click
229     within "#Log" do
230       assert_text "Log for previous"
231       pi["components"].each do |cname, cspec|
232         log_link = find("a", text: "Log for #{cname}")
233         assert_includes(log_link[:href], "/jobs/#{cspec["job"]["uuid"]}#Log")
234       end
235       assert_no_selector "#event_log_div"
236     end
237   end
238 end