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.
@@ -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