1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
5 require 'integration_helper'
7 class PipelineInstancesTest < ActionDispatch::IntegrationTest
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
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.
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')
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]
36 utc = page.evaluate_script("new Date('#{t}').toUTCString()")
37 DateTime.parse(utc).to_time
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.
45 test 'phantomjs DST' do
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"
54 test 'phantomjs DST 2' do
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)"
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'
68 find('a', text: 'pipeline_with_job').click
70 # since the pipeline component has a job, expect to see the graph
71 assert page.has_text? 'Graph'
73 page.assert_selector "#provenance_graph"
76 test "JSON popup available for strange components" do
77 uuid = api_fixture("pipeline_instances")["components_is_jobspec"]["uuid"]
78 visit page_with_token("active", "/pipeline_instances/#{uuid}")
80 assert(page.has_no_text?("script_parameters"),
81 "components JSON visible without popup")
82 click_on "Show components JSON"
83 assert(page.has_text?("script_parameters"),
84 "components JSON not found")
87 def create_pipeline_from(template_name, project_name="Home")
88 # Visit the named pipeline template and create a pipeline instance from it.
89 # The instance will be created under the named project.
90 template_uuid = api_fixture("pipeline_templates", template_name, "uuid")
91 visit page_with_token("active", "/pipeline_templates/#{template_uuid}")
92 click_on "Run this pipeline"
93 within(".modal-dialog") do # FIXME: source of 3 test errors
94 # Set project for the new pipeline instance
95 find(".selectable", text: project_name).click
98 assert(has_text?("This pipeline was created from the template"),
99 "did not land on pipeline instance page")
103 ['user1_with_load', 'zzzzz-d1hrv-10pipelines0001', 0], # run time 0 minutes
104 ['user1_with_load', 'zzzzz-d1hrv-10pipelines0010', 17*60*60 + 51*60], # run time 17 hours and 51 minutes
105 ['active', 'zzzzz-d1hrv-runningpipeline', nil], # state = running
106 ].each do |user, uuid, run_time|
107 test "pipeline start and finish time display for #{uuid}" do
108 need_selenium 'to parse timestamps correctly across DST boundaries'
109 visit page_with_token(user, "/pipeline_instances/#{uuid}")
111 regexp = "This pipeline started at (.+?)\\. "
113 regexp += "It failed after (.+?) at (.+?)\\. Check the Log"
115 regexp += "It has been active for \\d"
117 assert_match /#{regexp}/, page.text
121 # match again to capture (.*)
122 _, started, duration, finished = *(/#{regexp}/.match(page.text))
125 parse_browser_timestamp(finished) - parse_browser_timestamp(started),
126 "expected: #{run_time}, got: started #{started}, finished #{finished}, duration #{duration}")
131 ['fuse', nil, 2, 20], # has 2 as of 11-07-2014
132 ['user1_with_load', '000025pipelines', 25, 25], # owned_by the project zzzzz-j7d0g-000025pipelines, two pages
133 ['admin', 'pipeline_20', 1, 1],
134 ['active', 'no such match', 0, 0],
135 ].each do |user, search_filter, expected_min, expected_max|
136 test "scroll pipeline instances page for #{user} with search filter #{search_filter}
137 and expect #{expected_min} <= found_items <= #{expected_max}" do
138 visit page_with_token(user, "/pipeline_instances")
141 find('.recent-pipeline-instances-filterable-control').set(search_filter)
142 # Wait for 250ms debounce timer (see filterable.js)
147 page_scrolls = expected_max/20 + 2 # scroll num_pages+2 times to test scrolling is disabled when it should be
148 within('.arv-recent-pipeline-instances') do
149 (0..page_scrolls).each do |i|
150 page.driver.scroll_to 0, 999000
158 # Verify that expected number of pipeline instances are found
159 found_items = page.all('tr[data-kind="arvados#pipelineInstance"]')
160 found_count = found_items.count
161 if expected_min == expected_max
162 assert_equal(true, found_count == expected_min,
163 "Not found expected number of items. Expected #{expected_min} and found #{found_count}")
164 assert page.has_no_text? 'request failed'
166 assert_equal(true, found_count>=expected_min,
167 "Found too few items. Expected at least #{expected_min} and found #{found_count}")
168 assert_equal(true, found_count<=expected_max,
169 "Found too many items. Expected at most #{expected_max} and found #{found_count}")
174 test 'render job run time when job record is inaccessible' do
175 pi = api_fixture('pipeline_instances', 'has_component_with_completed_jobs')
176 visit page_with_token 'active', '/pipeline_instances/' + pi['uuid']
177 assert_text 'Queued for '
180 test "job logs linked for running pipeline" do
181 pi = api_fixture("pipeline_instances", "running_pipeline_with_complete_job")
182 visit(page_with_token("active", "/pipeline_instances/#{pi['uuid']}"))
183 find(:xpath, "//a[@href='#Log']").click
185 assert_text "Log for previous"
186 log_link = find("a", text: "Log for previous")
187 assert_includes(log_link[:href],
188 "/jobs/#{pi["components"]["previous"]["job"]["uuid"]}#Log")
189 assert_selector "#event_log_div"
193 test "job logs linked for complete pipeline" do
194 pi = api_fixture("pipeline_instances", "complete_pipeline_with_two_jobs")
195 visit(page_with_token("active", "/pipeline_instances/#{pi['uuid']}"))
196 find(:xpath, "//a[@href='#Log']").click
198 assert_text "Log for previous"
199 pi["components"].each do |cname, cspec|
200 log_link = find("a", text: "Log for #{cname}")
201 assert_includes(log_link[:href], "/jobs/#{cspec["job"]["uuid"]}#Log")
203 assert_no_selector "#event_log_div"
207 test "job logs linked for failed pipeline" do
208 pi = api_fixture("pipeline_instances", "failed_pipeline_with_two_jobs")
209 visit(page_with_token("active", "/pipeline_instances/#{pi['uuid']}"))
210 find(:xpath, "//a[@href='#Log']").click
212 assert_text "Log for previous"
213 pi["components"].each do |cname, cspec|
214 log_link = find("a", text: "Log for #{cname}")
215 assert_includes(log_link[:href], "/jobs/#{cspec["job"]["uuid"]}#Log")
217 assert_no_selector "#event_log_div"