tms-cli 0.0.pre.beta

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './testrail'
4
+ require_relative '../../util/script/generate-password'
5
+
6
+ require 'date'
7
+ require 'json'
8
+ require 'fileutils'
9
+ require 'mysql2'
10
+
11
+ module Tms
12
+ module Dataprep
13
+ module_function
14
+
15
+ include GeneratePassword
16
+ # This method will create client of testrail
17
+ def client_tr
18
+ url = ENV['TR_BASEURL']
19
+ user = ENV['TR_USERNAME']
20
+ password = GeneratePassword.decrypt(ENV['TR_API_KEY'], ENV['TR_SECRET'])
21
+
22
+ client = TestRail::APIClient.new(url)
23
+ client.user = user
24
+ client.password = password
25
+ client
26
+ end
27
+
28
+ # Creates a new file with the specified content at the given file path.
29
+ #
30
+ # @param [String] file_name The name of the file to create.
31
+ # @param [String, nil] folder_path (optional) The folder path where the file should be created.
32
+ # If provided, the file will be created in this folder; otherwise, it will be created in the current directory.
33
+ # @param [String] content The content to write to the file.
34
+ def create_file(file_name, folder_path = nil, content)
35
+ file_path = folder_path ? File.join(folder_path, file_name) : file_name
36
+
37
+ File.open(file_path, "w") do |file|
38
+ file.write(content)
39
+ end
40
+ end
41
+
42
+ # Creates a `.env` file with the specified environment variable assignments.
43
+ #
44
+ # @param [String] file_path The path to the `.env` file to be created or overwritten.
45
+ # @param [String, nil] folder_path (optional) The folder path where the file should be created.
46
+ # If provided, the file will be created in this folder; otherwise, it will be created in the current directory.
47
+ # @param [Hash] env_variables A hash containing environment variable assignments, where each
48
+ # key-value pair represents an environment variable name and its corresponding value.
49
+ #
50
+ # @example Create a `foo.env` file with custom environment variables:
51
+ # file_path = ".env"
52
+ # env_variables = {
53
+ # "DATABASE_URL" => "your_database_url",
54
+ # "API_KEY" => "your_api_key",
55
+ # "SECRET_KEY" => "your_secret_key"
56
+ # }
57
+ # create_env_file(file_path, env_variables)
58
+ def create_file_env(file_name, folder_path = nil, env_variables)
59
+ file_path = folder_path ? File.join(folder_path, file_name) : file_name
60
+
61
+ File.open("#{file_path}.env", "w") do |file|
62
+ env_variables.each do |key, value|
63
+ file.puts("#{key}=#{value}")
64
+ end
65
+ end
66
+ end
67
+
68
+ # This method will get project id based on file report
69
+ def project_id_by_report_file(client, report_file, tr_version)
70
+ project_name = nil
71
+ id = nil
72
+ response = Projects.get_projects(client)
73
+
74
+ project_name = if report_file.split('-').size.eql? 4
75
+ report_file.split('-')[2].tr('_', ' ')
76
+ else
77
+ report_file.split('-')[1].tr('_', ' ')
78
+ end
79
+ response.each do |project_detail|
80
+ if project_detail['name'].casecmp?(project_name.to_s)
81
+ id = project_detail['id']
82
+ break
83
+ end
84
+ end
85
+ id.nil? ? abort('Please input valid project name') : id
86
+ end
87
+
88
+ # This method will get project id based on project name
89
+ def project_id_by_name(client, project_name, tr_version)
90
+ id = nil
91
+ response = Projects.get_projects(client)
92
+
93
+ response.each do |project_detail|
94
+ if project_detail['name'].casecmp?(project_name.to_s)
95
+ id = project_detail['id']
96
+ break
97
+ end
98
+ end
99
+ id.nil? ? abort('Please input valid project name') : id
100
+ end
101
+
102
+ # This method will get section name based on file report
103
+ def section_name(report_file)
104
+ is_env_available = report_file.split('-').size.eql? 4
105
+ arrlocation = is_env_available ? 1 : 0
106
+ report_file.split('-')[arrlocation]
107
+ end
108
+
109
+ def platform_name(report_file)
110
+ is_env_available = report_file.split('-').size.eql? 4
111
+ arrlocation = is_env_available ? 3 : 2
112
+
113
+ report_type = report_file.split('-')[arrlocation]
114
+ report_type = report_type.split('_')[1]
115
+ platform_name = report_type.split('.')[0]
116
+ end
117
+
118
+ def test_env(report_file)
119
+ is_env_available = report_file.split('-').size.eql? 4
120
+ is_env_available ? report_file.split('-')[0] : ''
121
+ end
122
+
123
+ # This method will get testrail version on login page
124
+ # for handle response structure from api
125
+ def testrail_version(client, url)
126
+ source = URI.open(url).read
127
+ source.scan(%r{\b(?<=loginpage-version\"\>)[^"]+(?=</span>)})
128
+ end
129
+
130
+ # This method will get section id
131
+ def section_id(client, project_id, suite_id, section_name, tr_version)
132
+ tmp_sections = {}
133
+ sections = client.send_get("get_sections/#{project_id}&suite_id=#{suite_id}")
134
+ sections = sections['sections'] unless tr_version.to_s.include? '6.7.2'
135
+ sections.each do |section|
136
+ tmp_sections[section['name']] = section['id']
137
+ end
138
+ unless tmp_sections.key?(section_name)
139
+ create_section(client, project_id, suite_id, section_name)
140
+ else
141
+ tmp_sections[section_name]
142
+ end
143
+ end
144
+
145
+ # This method will create section on test suite
146
+ def create_section(client, project_id, suite_id, section_name)
147
+ payload = { description: section_name, suite_id: suite_id, name: section_name }
148
+ client.send_post("add_section/#{project_id}", payload)['id']
149
+ end
150
+
151
+ # This method will create new project
152
+ def create_project(client, project_name)
153
+ payload = { name: project_name, suite_mode: 3 }
154
+ client.send_post('add_project', payload)['id']
155
+ end
156
+
157
+ # Returns a Unix timestamp for a date that is a specified number of months before the current date.
158
+ #
159
+ # @param [Integer] month - The number of months before the current date.
160
+ # @return [Integer] - Unix timestamp for the calculated date.
161
+ #
162
+ # @example
163
+ # timestamp = get_timestamp_prev_month(3) #
164
+ # Gets the timestamp for 3 months before the current date.
165
+ def get_timestamp_prev_month(month)
166
+ Date.today.prev_month(month).to_time.to_i
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tms
4
+ module Log
5
+ module_function
6
+ def error(exception, is_local)
7
+ puts "An error of type '#{exception.class}' happened, message is '#{exception.message}'".colorize(:red)
8
+ puts "#{exception.backtrace.join("\n")}".colorize(:red)
9
+ end
10
+
11
+ def info(message)
12
+ puts "Info : #{message}".colorize(:yellow)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './testrail'
4
+ require_relative './dataprep'
5
+ require_relative './projects'
6
+ require 'json'
7
+ require 'fileutils'
8
+
9
+ module Tms
10
+ module Milestone
11
+ module_function
12
+
13
+ include Dataprep
14
+ include Projects
15
+
16
+ # This method will get milestone id
17
+ # Will create new if milestone is not exist
18
+ # Milestone name based on tag release on CI/CD pipeline
19
+ # @param client testrail
20
+ # @param project id
21
+ # @param milestone name
22
+ def milestone_id(client, project_id, milestone_name, tr_version)
23
+ milestone_id = nil
24
+ is_milestone_available = false
25
+
26
+ response = get_milestones(client, project_id)
27
+
28
+ if response.empty?
29
+ milestone_id = create_milestone(client, project_id, milestone_name)
30
+ is_milestone_available = true
31
+ else
32
+ response.map do |milestones|
33
+ next unless milestones['name'].eql? milestone_name
34
+
35
+ milestone_id = milestones['id']
36
+ is_milestone_available = true
37
+ break
38
+ end
39
+ end
40
+ is_milestone_available ? milestone_id : create_milestone(client, project_id, milestone_name)
41
+ end
42
+
43
+ # This method will create new milestone
44
+ # Will create new if milestone is not exist
45
+ # @param client testrail
46
+ # @param project id
47
+ # @param milestone name
48
+ def create_milestone(client, project_id, milestone_name)
49
+ puts "Creating new milestone ..."
50
+ # Calculate the due_on date as 14 days (2 weeks) from now
51
+ due_date = (Date.today + 14).to_time.to_i
52
+ client.send_post("add_milestone/#{project_id}", { name: milestone_name, due_on: due_date })['id']
53
+ end
54
+
55
+ # This method will update status old milestone to complete
56
+ # Only 10 recent milestones are active
57
+ # @param client testrail
58
+ # @param project id
59
+ def completed_old_milestone(client, project_id)
60
+ milestone_ids = []
61
+ client.send_get("get_milestones/#{project_id}")['milestones'].each { |detail| milestone_ids.push(detail['id'])}
62
+ 10.times { milestone_ids.delete(milestone_ids.max) }
63
+ milestone_ids.each { |id| client.send_post("update_milestone/#{id}", { is_completed: true }) }
64
+ end
65
+
66
+ def get_milestones(client, project_id)
67
+ # handle tests more than 250 entries
68
+ offset = 0
69
+ milestones = []
70
+
71
+ puts "Retrieving milestones from project id #{project_id} ..."
72
+
73
+ loop do
74
+ get_milestones = client.send_get("get_milestones/#{project_id}&offset=#{offset}&is_completed=0")
75
+ milestones.push get_milestones['milestones']
76
+ offset += 250
77
+ break if get_milestones['_links']['next'].nil?
78
+ end
79
+ milestones = milestones.flatten
80
+
81
+ return milestones
82
+ end
83
+
84
+ # This method will get milestone by name
85
+ # @param client testrail
86
+ # @param milestone name
87
+ def get_milestone_id(client, project_id, milestone_name)
88
+ response = get_milestones(client, project_id)
89
+ milestone_id = response.find { |milestone| milestone["name"] == milestone_name }&.dig("id")
90
+
91
+ return milestone_id
92
+ end
93
+
94
+ # This method will delete milestone
95
+ # @param client testrail
96
+ # @param milestone name
97
+ def delete_milestone(client, milestone_id)
98
+ client.send_post("delete_milestone/#{milestone_id}", '')
99
+ end
100
+
101
+ # This method will complete milestone
102
+ # @param client testrail
103
+ # @param test milestone id
104
+ def completed_milestone(client, milestone_id)
105
+ client.send_post("update_milestone/#{milestone_id}", { is_completed: true })
106
+ puts "Complete the Milestone with ID: #{milestone_id}"
107
+ end
108
+
109
+ # This method will get the milestone status
110
+ # @param client testrail
111
+ # @param test milestone id
112
+ def get_milestone_state(client, milestone_id)
113
+ state = client.send_get("get_milestone/#{milestone_id}")['is_completed']
114
+ puts "Milestone with ID #{milestone_id} have completed status is #{state}"
115
+ return state
116
+ end
117
+
118
+ # This method completes milestones that started a specified number of months before the current date.
119
+ # It iterates through all projects, checks each milestone's start date, and completes those that meet the criteria.
120
+ #
121
+ # @param client [Client] The client instance for making API requests.
122
+ # @param month [Integer] The number of months before the current date.
123
+ def completed_milestone_months_before(client, month)
124
+ Projects.get_list_project_ids(client).each do |project_id|
125
+ get_milestones(client, project_id).each do |milestone_detail|
126
+ milestone_id = milestone_detail['id']
127
+ started_on = milestone_detail['started_on']
128
+ if started_on && started_on <= month
129
+ completed_milestone(client, milestone_id)
130
+ puts "Completed Milestone ID: #{milestone_id} with started on date: #{started_on}"
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './testrail'
4
+ require 'json'
5
+ require 'fileutils'
6
+
7
+ module Tms
8
+ module Projects
9
+ module_function
10
+
11
+ # Returns all list of available projects and handle more than 250 entries
12
+ # @param client testrail
13
+ def get_projects(client)
14
+ offset = 0
15
+ projects = []
16
+ loop do
17
+ get_projects = client.send_get("get_projects&offset=#{offset}&is_completed=0")
18
+ projects.push get_projects['projects']
19
+ offset += 250
20
+ break if get_projects['_links']['next'].nil?
21
+ end
22
+ projects = projects.flatten
23
+ return projects
24
+ end
25
+
26
+ # Returns list of testrail project ids
27
+ # @param client testrail
28
+ def get_list_project_ids(client)
29
+ return get_projects(client).map { |project_detail| project_detail['id'] }
30
+ end
31
+
32
+ # Returns an existing testrail project
33
+ # @param client testrail
34
+ # @param project id
35
+ def get_projects_by_id(client, project_id)
36
+ response = client.send_get("get_project/#{project_id}")
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './testrail'
4
+ require 'webdrivers'
5
+ require 'openssl'
6
+ require 'base64'
7
+
8
+ module Tms
9
+ module Report
10
+ module_function
11
+
12
+ # This method will post test result from json report to test run
13
+ def post(client, test_results, run_id)
14
+ payload = {}
15
+ tmp_array = []
16
+ test_results.each do |result|
17
+ next if result['status'].eql?(3)
18
+
19
+ comment = result['comment'].nil? || result['comment'].empty? ? 'automated' : result['comment'].to_s
20
+ result['comment'] = comment
21
+ tmp_array.push(result)
22
+ end
23
+ payload['results'] = tmp_array
24
+ client.send_post("add_results_for_cases/#{run_id}", payload)
25
+ end
26
+
27
+ def results(client, run_id)
28
+ offset = 0
29
+ results = []
30
+
31
+ puts 'Retrieving test results...'
32
+
33
+ loop do
34
+ get_results = client.send_get("get_results_for_run/#{run_id}&offset=#{offset}")
35
+ results.push get_results['results']
36
+ offset += 250
37
+ break if get_results['_links']['next'].nil?
38
+ end
39
+ results = results.flatten
40
+
41
+ return results
42
+ end
43
+
44
+ def results_status(client, run_id, status_id)
45
+ offset = 0
46
+ results = []
47
+
48
+ puts 'Retrieving test results...'
49
+
50
+ loop do
51
+ get_results = client.send_get("get_results_for_run/#{run_id}/&status_id=#{status_id}&offset=#{offset}")
52
+ results.push get_results['results']
53
+ offset += 250
54
+ break if get_results['_links']['next'].nil?
55
+ end
56
+ results = results.flatten
57
+
58
+ return results
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './testrail'
4
+ require 'json'
5
+ require 'fileutils'
6
+
7
+ module Tms
8
+ module Results
9
+ module_function
10
+
11
+ # This method will adds a new test result, comment or assigns a test.
12
+ # It’s recommended to use add_results instead if you plan to add results for multiple tests.
13
+ # @param client testrail
14
+ # @param test_id
15
+ def add_result(client, test_id, status_id, comment)
16
+ payload = { status_id: status_id, comment: comment }
17
+ client.send_post("add_result/#{test_id}", payload)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './testrail'
4
+ require 'json'
5
+ require 'fileutils'
6
+
7
+ module Tms
8
+ module Sections
9
+ module_function
10
+
11
+ # Returns a list of sections for a project.
12
+ # @param client testrail
13
+ # @param project id
14
+ def get_sections(client, project_id)
15
+ client.send_get("get_sections/#{project_id}")
16
+ end
17
+
18
+ # Returns a list of sections for a project and test suite.
19
+ # @param client testrail
20
+ # @param project id
21
+ def get_sections(client, project_id, suite_id)
22
+ client.send_get("get_sections/#{project_id}&suite_id=#{suite_id}")
23
+ end
24
+
25
+ # Returns a list of sections for a project and test suite.
26
+ # @param client testrail
27
+ # @param project id
28
+ def get_section_id(client, section_id)
29
+ client.send_get("get_section/#{section_id}")
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './testrail'
4
+ require 'json'
5
+ require 'fileutils'
6
+
7
+ module Tms
8
+ module Testbdd
9
+ module_function
10
+
11
+ # Exports a BDD scenario from a test case as a .feature file.
12
+ # @param client testrail
13
+ # @param case id
14
+ def get_bdd(client, case_id)
15
+ client.send_get("get_bdd/#{case_id}")
16
+ end
17
+
18
+ def get_case(client, case_id)
19
+ response = client.send_get("get_case/#{case_id}")
20
+ response = JSON.parse(response) if response.is_a?(String)
21
+
22
+ custom_testrail_bdd_scenario = JSON.parse(response['custom_testrail_bdd_scenario'])
23
+
24
+ # handle test case that contains fews BDD scenario
25
+ content_array = []
26
+
27
+ if custom_testrail_bdd_scenario.is_a?(Array)
28
+ custom_testrail_bdd_scenario.each do |scenario|
29
+ content = scenario['content']
30
+ content_array << content if content
31
+ end
32
+ end
33
+
34
+ case_bdd = content_array.join("\n\n")
35
+
36
+ # Handle multiple refs and add "@" before each refs
37
+ refs = response['refs'].split(", ").map { |component| "@" + component }.join(" ") unless response['refs'].nil?
38
+
39
+ case_bdd = "#{refs}\nFeature: #{response['title']}\n" + case_bdd
40
+ return case_bdd
41
+ end
42
+
43
+ # Create new BDD cases by upload .feature file.
44
+ # @param client testrail
45
+ # @param case id
46
+ def add_bdd(client, section_id, file)
47
+ puts "Create test case: #{File.read(file).match(/Feature:(.+)/)[1]}"
48
+ client.send_post("add_bdd/#{section_id}", file)
49
+ end
50
+
51
+ def get_scenario(client, case_id)
52
+ response = client.send_get("get_case/#{case_id}")
53
+ response = JSON.parse(response) if response.is_a?(String)
54
+
55
+ custom_testrail_bdd_scenario_string = response['custom_testrail_bdd_scenario']
56
+ custom_testrail_bdd_scenario = JSON.parse(custom_testrail_bdd_scenario_string)
57
+
58
+ # handle test case that contains fews BDD scenario
59
+ content_array = []
60
+
61
+ if custom_testrail_bdd_scenario.is_a?(Array)
62
+ custom_testrail_bdd_scenario.each do |scenario|
63
+ content = scenario['content']
64
+ content_array << content if content
65
+ end
66
+ end
67
+
68
+ scenario_bdd = content_array.join("\n\n")
69
+ return scenario_bdd
70
+ end
71
+ end
72
+ end