tms-cli 0.0.pre.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 126ea4c79dfe3ba2f0577507e6710dd6a87b54931bd88503ff73f69d87bda43a
4
+ data.tar.gz: f05c3543997e9f3f9e1452d87e99c1a742abcb6bf2fd2ff5703e93bba947e6ca
5
+ SHA512:
6
+ metadata.gz: 99d223285e06a2ec541feed40f9b2462c332deedf529944708491736741a815e6b06306cff1f152c9c818b22b5020ee4b3e480a3c189759ba26e3b2ebe6bf3b9
7
+ data.tar.gz: cf16282634835829b2fdc037c1a0377f290ddf70cf9e5339af5d675dd180f2055b39ae72b76e17aa45bc5e65ffa2f261e021dc19838a2693ea0061569f9aca45
data/bin/tms ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'lib/tms'
data/lib/.env ADDED
@@ -0,0 +1,11 @@
1
+ TR_USERNAME=core.quality@bukalapak.com
2
+ TR_SECRET=coretebukalapak
3
+ TR_API_KEY=mrVfIsQkG0yhFChoR7j/9r+4GydgMykS7FplZ6/tiXieOIzBKGGRjeI=
4
+ DB_TR_PORT=3306
5
+ DB_TR_USERNAME=usr_testrail_rw
6
+ DB_TR=testrail
7
+ DB_TR_HOST=mysql-tools-prod-testrail-main.mysql.database.azure.com
8
+ DB_TR_PASSWORD=8Z9YJfsUGmSKOnshe6vO/A==
9
+ TR_BASEURL=https://testrail.prod.tools.buka20.com/
10
+ TR_PASSWORD=/rJHEOwcShrdUA==
11
+ BASTION_HOST=bastion.prod.tools.buka20.com
data/lib/cucumber.rb ADDED
@@ -0,0 +1,369 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'testrail/testrail'
4
+ require_relative 'testrail/milestone'
5
+ require_relative 'testrail/testcase'
6
+ require_relative 'testrail/testrun'
7
+ require_relative 'testrail/dataprep'
8
+ require_relative 'testrail/testsuite'
9
+ require_relative 'testrail/testplan'
10
+ require_relative 'testrail/sections'
11
+ require_relative 'testrail/projects'
12
+ require_relative 'testrail/log'
13
+ require_relative 'testrail/testbdd'
14
+
15
+
16
+ module Tms
17
+ module Cucumber
18
+ module_function
19
+
20
+ include Dataprep
21
+ include Milestone
22
+ include Testcase
23
+ include Testrun
24
+ include Testsuite
25
+ include Testplan
26
+ include Sections
27
+ include Projects
28
+ include Log
29
+ include Testbdd
30
+
31
+ # Create test run cucumber test for micorservice project
32
+ # @param project name
33
+ # @param test type (suite)
34
+ # @param squad name
35
+ # @param custom label
36
+ # @param milestone name
37
+ def create_test_run_microservice(project_name, test_type, squad, label, milestone_name)
38
+ # getting project id
39
+ test_type = test_type
40
+ client = Dataprep.client_tr
41
+ tr_version = Dataprep.testrail_version(client, ENV['TR_BASEURL'])
42
+
43
+ project_id = search_project(client, project_name)
44
+ puts 'project_id found: ' + project_id.to_s if !project_id.nil?
45
+
46
+ milestone_id = Milestone.milestone_id(client, project_id, milestone_name, tr_version)
47
+ suite_id = search_suite(client, project_id, test_type)
48
+ case_ids = search_cases(client, project_id, suite_id, squad, label)
49
+ raise 'no cases found' if case_ids.empty?
50
+
51
+ test_run_name = "Test_Run_E2E_#{squad}_#{test_type}"
52
+ response = Testrun.testrun_id_by_milestone(client, project_id, milestone_id)
53
+ find_test_run = response.find { |run| run['name'] == test_run_name }
54
+ run_id = find_test_run&.fetch('id', nil)
55
+ test_run = run_id.nil? ? Testrun.create_testrun(client, project_id, suite_id, milestone_id, case_ids, test_run_name) : run_id
56
+
57
+ getter_testrail_clean_dependencies(test_type, milestone_id, test_run)
58
+
59
+ return test_run
60
+ end
61
+
62
+ # Create test run cucumber test for app project
63
+ # @param project name
64
+ # @param test type (suite)
65
+ # @param squad name
66
+ # @param custom label
67
+ def create_test_run_app(project_name, squad, env_name, test_type, label, pipeline_id, custom_section)
68
+ client = Dataprep.client_tr
69
+ puts project_name.split('_')[1]
70
+ project_id = app_project_id(project_name)
71
+ suite_id = search_suite(client, project_id, squad)
72
+ automation_section_ids = find_automation_section(client, project_id, suite_id, project_name.split('_')[1], env_name, custom_section)
73
+ case_ids = search_cases_by_section_ids(client, project_id, suite_id, automation_section_ids, label)
74
+
75
+ raise 'no cases found' if case_ids.empty?
76
+
77
+ test_run_name = "Test_Run_#{squad}_#{test_type}_#{env_name}_#{pipeline_id}"
78
+ response = Testrun.testrun_id_by_suite(client, project_id, milestone_id = '', suite_id)
79
+ find_test_run = response.find { |run| run['name'] == test_run_name }
80
+ run_id = find_test_run&.fetch('id', nil)
81
+ if run_id.nil?
82
+ puts "Creating #{test_run_name}..."
83
+ test_run = Testrun.create_testrun(client, project_id, suite_id, 0, case_ids, test_run_name)
84
+ else
85
+ puts "Updating #{test_run_name}..."
86
+ test_run = Testrun.update_testrun(client, run_id, milestone_id = '', case_ids)
87
+ end
88
+
89
+ getter_testrail_clean_dependencies(test_type, milestone_id = '', test_run)
90
+
91
+ return test_run
92
+ end
93
+
94
+ # Find sections of automation test
95
+ # @param client testrail
96
+ # @param project id
97
+ # @param suite id
98
+ # @param app name
99
+ # @param environment name
100
+ def find_automation_section(client, project_id, suite_id, app_name, env_name, custom_section)
101
+ sections = Sections.get_sections(client, project_id, suite_id)
102
+ app_section = sections['sections'].find { |section| section['name'].casecmp?(app_name) }
103
+ automation_section = sections['sections'].find{ |section|
104
+ section['name'].casecmp?('automation') && section['parent_id'].eql?(app_section['id'])
105
+ }
106
+
107
+ env_section = sections['sections'].find{ |section|
108
+ section['name'].casecmp?(env_name) && section['parent_id'].eql?(automation_section['id'])
109
+ }
110
+ parent_id = env_section['id']
111
+
112
+ # search for section directory hierarchy
113
+ target_section = {}
114
+ if !custom_section.eql? ""
115
+ sections_directory = custom_section.split('/')
116
+ sections_directory.each do | section_dir |
117
+ target_section = sections['sections'].find { |section|
118
+ section['name'].casecmp?(section_dir) && section['parent_id'].eql?(parent_id)
119
+ }
120
+ parent_id = target_section['id']
121
+ end
122
+ else
123
+ target_section = env_section
124
+ end
125
+
126
+ related_section_ids = find_related_sections(sections['sections'], target_section['id']) << target_section['id']
127
+ end
128
+
129
+ # Get project id
130
+ # @param client testrail
131
+ # @param project id
132
+ # @param suite id
133
+ # @param app name
134
+ # @param environment name
135
+ def app_project_id(app_name)
136
+ project = app_name.split("_")
137
+ platform = project[0]
138
+ project_ids = {
139
+ 'android' => ENV['TR_ANDROID_PROJECT_ID'],
140
+ 'ios' => ENV['TR_IOS_PROJECT_ID']
141
+ }
142
+
143
+ project_id = project_ids[platform] || (raise 'Incorrect app name!')
144
+ end
145
+
146
+ # Return all case ids from sections and filter with label
147
+ # @param client testrail
148
+ # @param project id
149
+ # @param suite id
150
+ # @param section_ids
151
+ # @param label
152
+ def search_cases_by_section_ids(client, project_id, suite_id, section_ids, label)
153
+ case_ids = []
154
+ puts label
155
+
156
+ section_ids.each do |id|
157
+ cases = Testcase.get_cases(client, project_id, suite_id, id)
158
+
159
+ # filter cases with label if any
160
+ filtered_cases =
161
+ if label.eql? ''
162
+ cases
163
+ else
164
+ cases.select { |case_data| case_data['custom_label_id']&.include?(label) }
165
+ end
166
+
167
+ case_ids.concat(filtered_cases.map { |case_data| case_data['id'] })
168
+ end
169
+
170
+ return case_ids
171
+ end
172
+
173
+ # Return project id of a project name
174
+ # @param client testrail
175
+ # @param project name
176
+ def search_project(client, project_name)
177
+ begin
178
+ projects = Projects.get_projects(client)
179
+ filtered_projects = projects.select { |project| project['name'].casecmp?(project_name) }
180
+
181
+ if filtered_projects.empty?
182
+ puts "#{project_name} is not available!"
183
+ else
184
+ return filtered_projects.first['id']
185
+ end
186
+
187
+ rescue StandardError => e
188
+ puts "An error occurred: #{e.message}"
189
+ end
190
+ end
191
+
192
+ # Return suite id of a test type
193
+ # @param client testrail
194
+ # @param project id
195
+ # @param suite name
196
+ def search_suite(client, project_id, suite_name)
197
+ begin
198
+ suites = Testsuite.suites(client, project_id)
199
+ filtered_suites = suites.select { |suite| suite['name'].casecmp?(suite_name) }
200
+
201
+ if filtered_suites.empty?
202
+ puts "#{suite_name} is not available!"
203
+ else
204
+ return filtered_suites.first['id']
205
+ end
206
+
207
+ rescue StandardError => e
208
+ puts "An error occurred: #{e.message}"
209
+ end
210
+ end
211
+
212
+ # Return array of case id from a squad section
213
+ # @param client testrail
214
+ # @param project id
215
+ # @param suite id
216
+ # @param section_name
217
+ # @param custom label
218
+ def search_cases(client, project_id, suite_id, section_name, label)
219
+ sections = Sections.get_sections(client, project_id, suite_id)
220
+ squad_section = sections['sections'].find { |section| section['name'].casecmp?(section_name) }
221
+ raise "squad section #{section_name} not found" if squad_section.eql? nil
222
+
223
+ puts 'squad section: ' + squad_section['id'].to_s
224
+ # search sections related to the squad section
225
+ related_sections_ids = find_related_sections(sections['sections'], squad_section['id']) << squad_section['id']
226
+ puts 'sections related from squad: ' + related_sections_ids.to_s
227
+
228
+ case_ids = []
229
+
230
+ related_sections_ids.each do |id|
231
+ cases = Testcase.get_cases(client, project_id, suite_id, id)
232
+ filtered_cases = []
233
+
234
+ if label.split(',').size > 1
235
+ arr_cases = []
236
+ label.split(',').each do |i|
237
+ tmp_filtered_cases = cases.select { |case_data| case_data['custom_label_id']&.include?(i) }
238
+ arr_cases.concat(tmp_filtered_cases)
239
+ end
240
+ filtered_cases = arr_cases
241
+ else
242
+ filtered_cases = cases.select { |case_data| case_data['custom_label_id']&.include?(label) }
243
+ end
244
+ case_ids.concat(filtered_cases.map { |case_data| case_data['id']})
245
+
246
+ end
247
+
248
+ return case_ids
249
+ end
250
+
251
+ # Return array of section id related to a parent id
252
+ # @param array of sections
253
+ # @param parent id
254
+ def find_related_sections(sections, parent_id)
255
+ child_ids = sections
256
+ .select { |obj| obj['parent_id'] == parent_id }
257
+ .flat_map { |obj| [obj['id']] + find_related_sections(sections, obj['id']) }
258
+
259
+ child_ids
260
+ end
261
+
262
+ # Download series of bdd cases from a test run
263
+ # @aram test run id
264
+ def feature_download(run_id)
265
+ client = Dataprep.client_tr
266
+ puts 'Get test cases for test run id: ' + run_id.to_s
267
+ num_test = 0
268
+ # Create the folder if it doesn't exist
269
+ folder_path = FileUtils.mkdir_p('scenarios')
270
+
271
+ tests = Testrun.test_ids(client, run_id)
272
+ puts "Downloading #{tests.size} test cases..."
273
+ tests.each do |test|
274
+ puts "downloading #{test['title']} feature..."
275
+ response_data = Testbdd.get_bdd(client, test['case_id'])
276
+
277
+ # handle test case with scenario outline included
278
+ response_data = Testbdd.get_case(client, test['case_id']) if response_data.include? ('Scenario Outline')
279
+
280
+ # Append test id as a feature label
281
+ # Handle if a feature already have labels
282
+ if response_data[0] == "@"
283
+ response_data = "@TESTID_#{test['id']} " + response_data
284
+ else
285
+ response_data = "@TESTID_#{test['id']}\n " + response_data
286
+ end
287
+
288
+ # Writes the data to a feature file
289
+ Dataprep.create_file("#{test['case_id']}.feature", folder_path, response_data)
290
+ end
291
+ end
292
+
293
+ # Download series of bdd cases from a test case directly (without create test run) on microservice
294
+ # @param project_name
295
+ # @param suite_name
296
+ # @param section_name
297
+ # @param label
298
+ def micr_download_feature_only(project_name, suite_name, squad, label)
299
+ client = Dataprep.client_tr
300
+
301
+ project_id = search_project(client, project_name)
302
+ suite_id = search_suite(client, project_id, suite_name)
303
+ case_ids = search_cases(client, project_id, suite_id, squad, label)
304
+ raise 'no cases found' if case_ids.empty?
305
+
306
+ download_features_to_local(client, case_ids)
307
+
308
+ getter_testrail_clean_dependencies(suite_name, '', '')
309
+ end
310
+
311
+ # Download series of bdd cases from a test case directly (without create test run) on apps
312
+ # @param project_name
313
+ # @param suite_name
314
+ # @param section_name
315
+ # @param label
316
+ def app_download_feature_only(project_name, squad, env_name, test_type, label, custom_section)
317
+ client = Dataprep.client_tr
318
+ puts project_name.split('_')[1]
319
+
320
+ project_id = app_project_id(project_name)
321
+ suite_id = search_suite(client, project_id, squad)
322
+ automation_section_ids = find_automation_section(client, project_id, suite_id, project_name.split('_')[1], env_name, custom_section)
323
+ case_ids = search_cases_by_section_ids(client, project_id, suite_id, automation_section_ids, label)
324
+ raise 'no cases found' if case_ids.empty?
325
+
326
+ download_features_to_local(client, case_ids)
327
+
328
+ getter_testrail_clean_dependencies(test_type, '', '')
329
+ end
330
+
331
+ # Download test cases to local storaged
332
+ # @param case_ids
333
+ def download_features_to_local(client, case_ids)
334
+ # Create the folder if it doesn't exist
335
+ folder_path = FileUtils.mkdir_p('scenarios')
336
+
337
+ puts "Download #{case_ids.size} test cases"
338
+ puts "This test will not create Milestone and Test Runs on Testrail"
339
+ case_ids.each do |cases|
340
+ response_data = Testbdd.get_bdd(client, cases)
341
+
342
+ # handle test case with scenario outline included
343
+ response_data = Testbdd.get_case(client, cases) if response_data.include? ('Scenario Outline')
344
+
345
+ # Writes the data to a feature file
346
+ Dataprep.create_file("#{cases}.feature", folder_path, response_data)
347
+ end
348
+ end
349
+
350
+ def getter_testrail_clean_dependencies(test_type, milestone_id, run_id)
351
+ test_type_upper = test_type.upcase
352
+ test_type_final = test_type_upper.gsub(/\s*AUTOMATION\s*/, '')
353
+ puts "Test Type: #{test_type_final}"
354
+ # Define your environment variables as a hash
355
+ env_variables = {
356
+ "TESTRAIL_TEST_TYPE" => test_type_final,
357
+ "TESTRAIL_#{test_type_final}_MILESTONE_ID" => milestone_id,
358
+ "TESTRAIL_#{test_type_final}_RUN_ID" => run_id
359
+ }
360
+
361
+ begin
362
+ Dataprep.create_file_env("testrail-data-post-test", env_variables)
363
+ rescue => e
364
+ puts "Create env failed: #{e.message}"
365
+ exit 1
366
+ end
367
+ end
368
+ end
369
+ end
data/lib/db/mysql.rb ADDED
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../util/script/generate-password'
4
+ require 'mysql2'
5
+
6
+ module Tms
7
+ module Database
8
+ module_function
9
+
10
+ include GeneratePassword
11
+
12
+ # This method will create client database
13
+ def connect_db
14
+ db_host = ENV['DB_TR_HOST']
15
+ db_port = ENV['DB_TR_PORT']
16
+ db_user = ENV['DB_TR_USERNAME']
17
+ db_pass = GeneratePassword.decrypt(ENV['DB_TR_PASSWORD'], ENV['TR_SECRET'])
18
+ db_name = ENV['DB_TR']
19
+ Mysql2::Client.new(:host => db_host, :port => db_port, :username => db_user, :password => db_pass, :database => db_name)
20
+ end
21
+
22
+ # This method will validate test runner on database
23
+ def runner_type(project_id, section_name)
24
+ test_runner = ''
25
+ connect_db.query("SELECT runner from runner_type where project_id=#{project_id} and test_type='#{section_name}'").each do |row|
26
+ test_runner = row['runner']
27
+ end
28
+ test_runner
29
+ end
30
+
31
+ # This method will insert test runner if not exist
32
+ def insert_runner_type(project_id, test_types_and_runner)
33
+ val = test_types_and_runner.split(',')
34
+ db_client = connect_db
35
+ response = db_client.query("SELECT * from runner_type where project_id=#{project_id} and test_type='#{val[0]}' and runner='#{val[1]}'")
36
+ if response.size.eql?(0)
37
+ db_client.query("INSERT INTO runner_type(project_id,test_type,runner) VALUE(#{project_id},'#{val[0]}','#{val[1]}');")
38
+ else
39
+ Log.info("Test type '#{val[0]}' with runner '#{val[1]}' already exist on project id '#{project_id}'")
40
+ end
41
+ end
42
+
43
+ # This method will generate config ssh and connect database from local via jump bastion
44
+ def jump_bastion(email_azure)
45
+ puts 'connect to vpn buka 2.0 ...'
46
+ mysql_prod_host = ENV['DB_TR_HOST']
47
+ mysql_prod_bastion = ENV['BASTION_HOST']
48
+ ENV.merge!('DB_TR_HOST' => '127.0.0.1')
49
+ ENV.merge!('DB_TR_PORT' => '3307')
50
+
51
+ `rm -rf ~/.ssh/id_rsa.pub-aadcert.pub && rm -rf azure-ssh-config`
52
+ `az ssh cert --public-key-file ~/.ssh/id_rsa.pub`
53
+ config = "
54
+ Host vault-core-dev
55
+ HostName bastion.dev.core.buka20.com
56
+ LocalForward 8200 vault.dev.core.buka20.com:8200
57
+
58
+ Host vault-core-stg
59
+ HostName bastion.stg.core.buka20.com
60
+ LocalForward 8200 vault.stg.core.buka20.com:8200
61
+
62
+ Host vault-core-prod
63
+ HostName bastion.prod.core.buka20.com
64
+ LocalForward 8200 vault.prod.core.buka20.com:8200
65
+
66
+ Host vault-o2o-dev
67
+ HostName bastion.dev.o2o.buka20.com
68
+ LocalForward 8200 vault.dev.o2o.buka20.com:8200
69
+
70
+ Host vault-o2o-stg
71
+ HostName bastion.stg.o2o.buka20.com
72
+ LocalForward 8200 vault.stg.o2o.buka20.com:8200
73
+
74
+ Host vault-o2o-prod
75
+ HostName bastion.prod.o2o.buka20.com
76
+ LocalForward 8200 vault.prod.o2o.buka20.com:8200
77
+
78
+ Host vault-tools-stg
79
+ HostName bastion.stg.tools.buka20.com
80
+ LocalForward 8200 vault.stg.tools.buka20.com:8200
81
+
82
+ Host vault-tools-prod
83
+ HostName bastion.prod.tools.buka20.com
84
+ LocalForward 8200 vault.prod.tools.buka20.com:8200
85
+
86
+ Host *
87
+ User #{email_azure}
88
+ CertificateFile ~/.ssh/id_rsa.pub-aadcert.pub
89
+ StrictHostKeyChecking no
90
+ UserKnownHostsFile /dev/null"
91
+
92
+ ssh_conf_file = File.new("azure_config", "w")
93
+ ssh_conf_file.puts(config)
94
+ ssh_conf_file.close
95
+
96
+ `ssh -F azure_config -NL 3307:#{mysql_prod_host}:3306 #{mysql_prod_bastion} & echo $! > ./pid.file & sleep 5`
97
+ end
98
+
99
+ # This method will stop jump bastion for disconnect database from local
100
+ def stop_jump_bastion
101
+ puts 'disconnect to vpn buka 2.0 ...'
102
+ pid = `cat pid.file`
103
+ `kill #{pid}`
104
+ `rm -rf pid.file`
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../testrail/testrail'
4
+ require_relative '../testrail/testcase'
5
+
6
+ module Tms
7
+ module Cucumber
8
+ module_function
9
+
10
+ include Testcase
11
+
12
+ # TODO: Will refer to import_data_bdd instead, after squad migrate test automation to Testrail completely
13
+ def import_data(client, project_id, suite_id, section_id, report_data_hash, tr_version)
14
+ # looping the feature
15
+ report_data_hash.map do |report_data_result|
16
+ # looping the test cases with get key elements
17
+ report_data_result['elements'].map do |test_result|
18
+ # looping the report when keyword is scenario and scenario outline
19
+ if(test_result['keyword'].downcase == 'scenario' || test_result['keyword'].downcase == 'scenario outline')
20
+ test_title = test_result['keyword'].downcase == 'scenario outline' ? test_result['id'] : test_result['name']
21
+ scenario = check_status_and_error_message(test_result['steps'], test_title)
22
+ test_status = scenario[0]['status']
23
+
24
+ # if status is "pending", should get status from object "after" instead
25
+ # since in cucumber report "pending" turn out as "failed" on summary result
26
+ if test_status == 'pending'
27
+ test_status = test_result['after'][0]['result']['status']
28
+ end
29
+
30
+ test_status_id = Testcase.get_status_id(test_status)
31
+ error_comment = scenario[0]['error_message']
32
+ refs = get_refs_id(test_result['tags']) || ''
33
+
34
+ Testcase.validate_testcase(client, section_id, test_title, test_status_id, refs, tr_version, error_comment)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def import_data_bdd(client, project_id, suite_id, section_id, report_data_hash, tr_version)
41
+ # looping the feature
42
+ report_data_hash.map do |report_data_result|
43
+ # looping the test cases with get key elements
44
+ report_data_result['elements'].map do |test_result|
45
+ # looping the report when keyword is scenario and scenario outline
46
+ if(test_result['keyword'].downcase == 'scenario' || test_result['keyword'].downcase == 'scenario outline')
47
+ test_title = test_result['keyword'].downcase == 'scenario outline' ? test_result['id'] : test_result['name']
48
+ scenario = check_status_and_error_message(test_result['steps'], test_title)
49
+ labels = test_result['tags'][0]['name']
50
+ test_status = scenario[0]['status']
51
+
52
+ test_status_id = Testcase.get_status_id(test_status)
53
+ error_comment = scenario[0]['error_message']
54
+ refs = get_refs_id(test_result['tags']) || ''
55
+
56
+ if labels.include?('@TESTID_')
57
+ test_id = labels.gsub('@TESTID_','')
58
+ Results.add_result(client, test_id, test_status_id, error_comment)
59
+ end
60
+ Testcase.validate_testcase_bdd(client, section_id, test_id, refs, tr_version)
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def check_status_and_error_message(steps, test_title)
67
+ result_scenario = []
68
+ isSuccess = true
69
+ error_message = ''
70
+ test_result = {}
71
+ list_status = ['passed', 'skipped', 'failed', 'undefined', 'pending']
72
+
73
+ #looping each steps in scenario
74
+ steps.map do |scenario|
75
+ status = scenario['result']['status']
76
+ # check if status failed or skipped then save the status failed and pending and not continue the looping
77
+ # save the status and error message
78
+ # if failed then get the error message from json and if skipped then the error message will be empty
79
+ if list_status.include?(status)
80
+ if status.downcase == 'failed' || status.downcase == 'undefined' || status.downcase == 'skipped'
81
+ isSuccess = false
82
+ error_message = scenario['result']['error_message'] || ''
83
+ test_result['status']= 'failed'
84
+ test_result['error_message']= error_message
85
+ break
86
+ end
87
+ else
88
+ puts "invalid status is in test case with title = #{test_title}"
89
+ puts "status = #{status}"
90
+ isSuccess = false
91
+ test_result['status'] = 'invalid status'
92
+ test_result['error_message'] = error_message
93
+ break
94
+ end
95
+ end
96
+
97
+ # after looping all the steps if there is no failed and skipeed then the status will be passed and error message will be empty
98
+ if isSuccess == true
99
+ test_result['status'] = 'passed'
100
+ test_result['error_message'] = error_message
101
+ end
102
+
103
+ return result_scenario.push(test_result)
104
+ end
105
+
106
+ def get_refs_id(elements)
107
+ jira_id = ''
108
+
109
+ # looping the tag element and find prefix @TEST or @CUCUMBER
110
+ elements.map do |element|
111
+ tag = element['name']
112
+ if tag.include?("@TEST_") || tag.include?("@CUCUMBER_")
113
+ # split the tag name with _ and save the value on index 1 that have value of JIRA ID
114
+ array_jira_id = tag.split("_")
115
+ jira_id = array_jira_id[1]
116
+ break
117
+ end
118
+ end
119
+ return jira_id
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../testrail/testrail'
3
+
4
+ module Tms
5
+ module Feature
6
+ module_function
7
+
8
+ def parsing_feature(file)
9
+ updated_bdd_scenario = ''
10
+ feature_file = File.read(file)
11
+
12
+ bdd_refs = feature_file.lines.first.scan(/@[\w-]+/).join(',').gsub('@','')
13
+
14
+ # Extract test title from local file
15
+ bdd_title = feature_file.match(/Feature:(.+)/)[1]
16
+ # Extract custom label and BDD scenario from local file
17
+ bdd_label = feature_file.match(/Feature:.+?\n\s*(.+?)\n/m)[1].scan(/@\S+/).join(' ')
18
+ bdd_scenario = bdd_label + "\n" + feature_file.match(/(?:Scenario Outline|Scenario):(.+?)(?=Scenario(?:Outline|:)|\z)/m)[0]
19
+
20
+ [bdd_refs, bdd_title, bdd_label, bdd_scenario]
21
+ end
22
+
23
+ def update_bdd(client, case_id, file)
24
+ bdd_refs, bdd_title, bdd_label, bdd_scenario = parsing_feature(file)
25
+ Testcase.update_case(client, case_id, bdd_refs, bdd_title, bdd_label, bdd_scenario)
26
+ end
27
+
28
+ def add_case(client, section_id, file)
29
+ bdd_refs, bdd_title, bdd_label, bdd_scenario = parsing_feature(file)
30
+ Testcase.add_case(client, section_id, bdd_refs, bdd_title, bdd_label, bdd_scenario)
31
+ end
32
+
33
+ end
34
+ end