golden_rose 0.1.0 → 1.0.0.pre
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 +4 -4
- data/.gitignore +1 -0
- data/README.md +10 -5
- data/golden_rose.gemspec +3 -1
- data/lib/golden_rose/build_log/build_section.rb +56 -0
- data/lib/golden_rose/build_log/build_target.rb +21 -0
- data/lib/golden_rose/build_log/parser.rb +40 -0
- data/lib/golden_rose/build_log.rb +3 -0
- data/lib/golden_rose/child_item.rb +91 -9
- data/lib/golden_rose/class_configurator.rb +49 -0
- data/lib/golden_rose/cli/app.rb +12 -3
- data/lib/golden_rose/execution_details.rb +22 -42
- data/lib/golden_rose/file_resource.rb +98 -0
- data/lib/golden_rose/generators/html_format.rb +70 -7
- data/lib/golden_rose/info.rb +11 -0
- data/lib/golden_rose/parser.rb +28 -30
- data/lib/golden_rose/results_filterer.rb +4 -54
- data/lib/golden_rose/templates/_build_logs.haml +24 -0
- data/lib/golden_rose/templates/_footer.haml +5 -0
- data/lib/golden_rose/templates/_test_logs.haml +9 -0
- data/lib/golden_rose/templates/_tests.haml +52 -0
- data/lib/golden_rose/templates/assets/javascript/main.js.haml +48 -0
- data/lib/golden_rose/templates/assets/styles/main.css.haml +203 -0
- data/lib/golden_rose/templates/assets/styles/test_logs.css.haml +101 -0
- data/lib/golden_rose/templates/index.haml +23 -116
- data/lib/golden_rose/templates/test_logs/index.haml +38 -0
- data/lib/golden_rose/testable_summary.rb +90 -0
- data/lib/golden_rose/version.rb +1 -1
- data/lib/golden_rose/xcactivitylog_reader.rb +54 -0
- data/lib/golden_rose.rb +20 -5
- metadata +51 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87af3920c30c7c90ea498eaea2646802c77f4024
|
4
|
+
data.tar.gz: 645e890496f6343dba1f30041a4f2757303d224e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e284409105b20bac7246a24ca1b81e2633ba05659fbcbd918d8f74342154797197a5dd0eeef4df011baeeba24ff8daf7fa655954f847c8eac550c9724289b650
|
7
|
+
data.tar.gz: 2d9368a84c7255be44b7803079a0b6e100e16b3c7eab4a45afbb8be63fbb694984387deafc5e044035fbb7decd2bd8f4e5e0175f35224408d32f210896f52439
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# GoldenRose
|
2
2
|
|
3
|
-
[](https://rubygems.org/gems/golden_rose)
|
4
|
+
[](https://github.com/PGSSoft/GoldenRose/blob/master/LICENSE.txt)
|
4
5
|
|
5
6
|
A tool for generating reports from Xcode results bundle.
|
6
7
|
|
@@ -28,17 +29,21 @@ xcodebuild -workspace MyProject.xcworkspace \
|
|
28
29
|
- Generate reports with GoldenRose:
|
29
30
|
|
30
31
|
```bash
|
31
|
-
golden_rose generate MyApp.test_result
|
32
|
+
golden_rose generate MyApp.test_result [options]
|
32
33
|
```
|
33
34
|
|
34
|
-
The tool will generate a report in html format in the current directory.
|
35
|
+
The tool will generate a report in html format in the `goldenrose` directory under the current directory.
|
36
|
+
|
37
|
+
### golden_rose command options
|
38
|
+
* -o --output - specifies output directory, `goldenrose` by default
|
39
|
+
* -f --force-save - if typed then the whole destination directory will be overwritten
|
35
40
|
|
36
41
|
## Features
|
37
42
|
|
38
43
|
- [x] UI Automation tests
|
39
44
|
- [x] Reading from ZIP files
|
40
|
-
- [
|
41
|
-
- [
|
45
|
+
- [x] Unit tests reports
|
46
|
+
- [x] Build logs
|
42
47
|
- [ ] Code coverage
|
43
48
|
- [ ] Fastlane integration
|
44
49
|
- [ ] JUnit reports
|
data/golden_rose.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.summary = %q{A tool for generating reports from Xcode results bundle.}
|
13
13
|
spec.description = %q{A tool for generating reports from Xcode results bundle.
|
14
14
|
Analyze results bundle directory created by Xcode during building, testing, running and generates HTML report with results.}
|
15
|
-
spec.homepage = "https://github.com/PGSSoft/
|
15
|
+
spec.homepage = "https://github.com/PGSSoft/GoldenRose"
|
16
16
|
spec.license = "MIT"
|
17
17
|
|
18
18
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
@@ -25,8 +25,10 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_dependency "rubyzip"
|
26
26
|
spec.add_dependency "thor"
|
27
27
|
spec.add_dependency "tilt"
|
28
|
+
spec.add_dependency "rubytree", "~> 0.9.7"
|
28
29
|
|
29
30
|
spec.add_development_dependency "bundler", "~> 1.10"
|
30
31
|
spec.add_development_dependency "rake", "~> 10.0"
|
31
32
|
spec.add_development_dependency "rspec"
|
33
|
+
spec.add_development_dependency "pry"
|
32
34
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module GoldenRose
|
2
|
+
module BuildLog
|
3
|
+
class BuildSection
|
4
|
+
attr_reader :name, :path, :errors
|
5
|
+
|
6
|
+
def initialize(section_logs)
|
7
|
+
@name = section_logs.match(/([a-zA-Z]+(\s[a-zA-Z]+)*)/).to_s
|
8
|
+
@path = match_path(@name, section_logs)
|
9
|
+
@errors = extract_errors(section_logs)
|
10
|
+
end
|
11
|
+
|
12
|
+
def file
|
13
|
+
@file ||= if path?
|
14
|
+
last_element = @path.split('/').last
|
15
|
+
last_element if last_element.include?('.')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def directory
|
20
|
+
@directory ||= file? ? @path.gsub(file, '') : @path
|
21
|
+
end
|
22
|
+
|
23
|
+
def file?
|
24
|
+
!(file.nil? || file.empty?)
|
25
|
+
end
|
26
|
+
|
27
|
+
def path?
|
28
|
+
!(@path.nil? || @path.empty?)
|
29
|
+
end
|
30
|
+
|
31
|
+
def errors?
|
32
|
+
!success?
|
33
|
+
end
|
34
|
+
|
35
|
+
def success?
|
36
|
+
@errors.nil? || @errors.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def match_path(name, section_logs)
|
42
|
+
path_patern = /(\/\w([[[\w|-]|\s]\+]*\/?)*(\.[a-zA-Z]+)?)/
|
43
|
+
matched = section_logs.match(/#{name} #{path_patern}/)
|
44
|
+
matched[1] if matched
|
45
|
+
end
|
46
|
+
|
47
|
+
def extract_errors(section_logs)
|
48
|
+
error_strings = section_logs.scan(/error:.[^\r]*/)
|
49
|
+
return if error_strings.empty?
|
50
|
+
error_strings.map do |err|
|
51
|
+
err.gsub(/error:\s*/, '')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GoldenRose
|
2
|
+
module BuildLog
|
3
|
+
class BuildTarget
|
4
|
+
attr_reader :head, :sections, :target_name, :project_name
|
5
|
+
|
6
|
+
def initialize(target_logs)
|
7
|
+
@head, *sections = target_logs.scan(/.*?BuildLogSection\d+/i)
|
8
|
+
@target_name = @head.split.first
|
9
|
+
@sections = sections.map { |section| BuildSection.new(section) }
|
10
|
+
@project_name = match_project_name(@target_name, @head)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def match_project_name(target_name, head)
|
16
|
+
matched = head.match(/#{target_name} OF PROJECT ([^\/.]*) WITH/)
|
17
|
+
matched[1] if matched
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module GoldenRose
|
2
|
+
module BuildLog
|
3
|
+
class Parser
|
4
|
+
def initialize(log_path)
|
5
|
+
@log_path = log_path
|
6
|
+
end
|
7
|
+
|
8
|
+
def parse!
|
9
|
+
@build_targets = build_targets_logs.map do |target|
|
10
|
+
BuildTarget.new(target)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def logs
|
15
|
+
@logs ||= read_unzip_xcactivitylog
|
16
|
+
end
|
17
|
+
|
18
|
+
def read_unzip_xcactivitylog
|
19
|
+
XcactivitylogReader.read(@log_path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_targets_logs
|
23
|
+
@build_targets_logs ||= parse_build_target_array
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse_build_target_array
|
27
|
+
_, *build_targets_array = logs.split('=== BUILD TARGET')
|
28
|
+
ommit_short_build_target(build_targets_array)
|
29
|
+
end
|
30
|
+
|
31
|
+
def ommit_short_build_target(build_targets_array)
|
32
|
+
build_targets_array.map.with_index do |item, idx|
|
33
|
+
patern = item[/.*===/]
|
34
|
+
next_item = build_targets_array[idx + 1]
|
35
|
+
next_item && next_item[0..patern.size - 1] == patern ? nil : item
|
36
|
+
end.compact
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,28 +1,87 @@
|
|
1
1
|
# Used for setting single subtest details
|
2
2
|
# It represents last level of tests
|
3
|
+
require 'tree'
|
4
|
+
require 'securerandom'
|
3
5
|
|
4
6
|
module GoldenRose
|
5
7
|
class ChildItem < SubtestItem
|
6
|
-
|
8
|
+
ActivitySummary = Struct.new(
|
9
|
+
:uuid, :finish_time_interval, :start_time_interval, :title,
|
10
|
+
:has_screenshot_data, :has_snapshot
|
11
|
+
) do
|
12
|
+
def started_at
|
13
|
+
start_time_interval ? Time.at(start_time_interval) : nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def finished_at
|
17
|
+
finish_time_interval ? Time.at(finish_time_interval) : nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def start_time
|
21
|
+
started_at ? started_at.strftime("%H:%M:%S.%L") : nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def finish_time
|
25
|
+
finished_at ? finished_at.strftime("%H:%M:%S.%L") : nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :status, :time, :failures, :activity_summaries, :uuid
|
7
30
|
|
8
31
|
def initialize(source_subtest)
|
9
32
|
@status = source_subtest["TestStatus"].downcase
|
10
33
|
set_time(source_subtest)
|
11
34
|
set_failures(source_subtest)
|
12
|
-
|
35
|
+
@activity_summaries = if source_subtest["ActivitySummaries"]
|
36
|
+
build_activity_summaries(source_subtest["ActivitySummaries"])
|
37
|
+
end
|
38
|
+
@uuid = ::SecureRandom.uuid
|
13
39
|
super
|
14
40
|
end
|
15
41
|
|
42
|
+
def has_activity_summaries
|
43
|
+
@activity_summaries && !(@activity_summaries.children.nil? || @activity_summaries.children.empty?)
|
44
|
+
end
|
45
|
+
|
46
|
+
def instance
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def last_activity_with_screenshot
|
51
|
+
arr = [];
|
52
|
+
return unless @activity_summaries
|
53
|
+
@activity_summaries.each do |node|
|
54
|
+
next if node.is_root? || !node.content.has_screenshot_data
|
55
|
+
arr << { uuid: node.content.uuid, timestamp: node.content.start_time_interval}
|
56
|
+
end
|
57
|
+
arr.max_by { |node| node[:timestamp] }
|
58
|
+
end
|
59
|
+
|
16
60
|
private
|
17
61
|
|
18
62
|
def set_time(source_subtest)
|
19
|
-
|
20
|
-
|
63
|
+
counted_time = source_subtest['Duration'] || count_time_by_interval(source_subtest)
|
64
|
+
return unless counted_time
|
65
|
+
@time = "#{signif_round(counted_time)}s"
|
66
|
+
end
|
21
67
|
|
22
|
-
|
68
|
+
# Rounds to 2 significant digits after comma
|
69
|
+
def signif_round(time)
|
70
|
+
return 0.0 if time == 0
|
71
|
+
return time.round(2) if time >= 0.005
|
72
|
+
time_str = "%f" % time
|
73
|
+
first_decimal_digit_idx = time_str.index(time_str[/[1-9]/])
|
74
|
+
rounded = time.round(first_decimal_digit_idx)
|
75
|
+
rounded_str = "%f" % rounded
|
76
|
+
rounded_str[/\d.0*[1-9]+/]
|
77
|
+
end
|
23
78
|
|
24
|
-
|
25
|
-
|
79
|
+
def count_time_by_interval(source_subtest)
|
80
|
+
sum_node = source_subtest['ActivitySummaries']
|
81
|
+
last_time = sum_node ? sum_node.last['FinishTimeInterval'] : nil
|
82
|
+
first_time = sum_node ? sum_node.first['StartTimeInterval'] : nil
|
83
|
+
return unless last_time && first_time
|
84
|
+
last_time - first_time
|
26
85
|
end
|
27
86
|
|
28
87
|
def set_failures(source_subtest)
|
@@ -36,8 +95,31 @@ module GoldenRose
|
|
36
95
|
end
|
37
96
|
end
|
38
97
|
|
98
|
+
def build_activity_summaries(activity_summaries, node = nil)
|
99
|
+
return unless activity_summaries
|
100
|
+
node ||= Tree::TreeNode.new("ROOT", "Root Content")
|
101
|
+
|
102
|
+
activity_summaries.map do |activity_summary|
|
103
|
+
content = ActivitySummary.new(
|
104
|
+
activity_summary['UUID'],
|
105
|
+
activity_summary['FinishTimeInterval'],
|
106
|
+
activity_summary['StartTimeInterval'],
|
107
|
+
activity_summary['Title'],
|
108
|
+
activity_summary['HasScreenshotData'],
|
109
|
+
activity_summary['HasSnapshot']
|
110
|
+
)
|
111
|
+
child = if activity_summary['SubActivities']
|
112
|
+
build_activity_summaries(activity_summary['SubActivities'], Tree::TreeNode.new(content.uuid, content))
|
113
|
+
else
|
114
|
+
Tree::TreeNode.new(content.uuid, content)
|
115
|
+
end
|
116
|
+
node << child if child
|
117
|
+
end
|
118
|
+
node
|
119
|
+
end
|
120
|
+
|
39
121
|
def hashable_attributes
|
40
|
-
%i(name status time failures)
|
122
|
+
%i(name status time failures uuid has_activity_summaries activity_summaries instance)
|
41
123
|
end
|
42
124
|
end
|
43
|
-
end
|
125
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module GoldenRose
|
2
|
+
class ClassConfigurator
|
3
|
+
|
4
|
+
def initialize(source_type, folder_path, output_path)
|
5
|
+
@source_type = source_type
|
6
|
+
@folder_path = folder_path
|
7
|
+
@output_path = output_path
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.configure(source_type, folder_path, output_path)
|
11
|
+
new(source_type, folder_path, output_path).configure
|
12
|
+
end
|
13
|
+
|
14
|
+
def configure
|
15
|
+
add_screenshot_to_each_test
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def add_screenshot_to_each_test
|
21
|
+
source_type = @source_type
|
22
|
+
folder_path = @folder_path
|
23
|
+
output_path = @output_path
|
24
|
+
|
25
|
+
FileUtils.remove_dir(@output_path + "/screenshots", true)
|
26
|
+
|
27
|
+
GoldenRose::ChildItem.class_eval do
|
28
|
+
@@source_type = source_type
|
29
|
+
@@folder_path = folder_path
|
30
|
+
@@output_path = output_path
|
31
|
+
|
32
|
+
def screenshot
|
33
|
+
return @screenshot if @screenshot
|
34
|
+
screenshot_item = last_activity_with_screenshot
|
35
|
+
return unless screenshot_item
|
36
|
+
dest_path = @@output_path + "/screenshots"
|
37
|
+
FileUtils.mkdir_p dest_path
|
38
|
+
source_path = "Attachments/Screenshot_#{screenshot_item[:uuid]}.png"
|
39
|
+
|
40
|
+
screen = FileResource.new(:screenshoot, @@source_type, @@folder_path, source_path,
|
41
|
+
"#{dest_path}/Screenshot_#{screenshot_item[:uuid]}.png")
|
42
|
+
@screenshot = screen.path
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
data/lib/golden_rose/cli/app.rb
CHANGED
@@ -3,18 +3,27 @@ require "thor"
|
|
3
3
|
module GoldenRose
|
4
4
|
module CLI
|
5
5
|
class App < Thor
|
6
|
+
DEFAULT_OUTPUT_DIR = 'goldenrose'
|
7
|
+
|
6
8
|
desc "generate <path_to_folder_or_zip>", "Generate report from a folder"
|
7
9
|
method_option :output, type: :string, aliases: "-o"
|
10
|
+
method_option "force-save", type: :boolean, aliases: "-f"
|
8
11
|
|
9
12
|
def generate(folder_path)
|
10
13
|
say("Started formatting report...")
|
11
|
-
GoldenRose::generate(folder_path, options[
|
12
|
-
say("Report generated in #{options[:output] || Dir.pwd}/index.html", :green)
|
14
|
+
GoldenRose::generate(folder_path, output, options['force-save'])
|
15
|
+
say("Report generated in #{options[:output] || Dir.pwd + '/' + DEFAULT_OUTPUT_DIR}/index.html", :green)
|
13
16
|
rescue GoldenRose::GeneratingError => e
|
14
17
|
say(e.message, :yellow)
|
15
18
|
rescue StandardError => e
|
16
19
|
say("Error during running generator.", :red)
|
17
20
|
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def output
|
25
|
+
options[:output] || DEFAULT_OUTPUT_DIR
|
26
|
+
end
|
18
27
|
end
|
19
28
|
end
|
20
|
-
end
|
29
|
+
end
|
@@ -1,57 +1,37 @@
|
|
1
1
|
module GoldenRose
|
2
2
|
class ExecutionDetails
|
3
|
-
|
4
|
-
@parsed_plist = parsed_plist
|
5
|
-
@items = items
|
6
|
-
end
|
7
|
-
|
8
|
-
def name
|
9
|
-
@parsed_plist["TestableSummaries"].first['TestName']
|
10
|
-
end
|
11
|
-
|
12
|
-
def formatted_time
|
13
|
-
hours = total_time / (60 * 60)
|
14
|
-
minutes = (total_time / 60) % 60
|
15
|
-
seconds = total_time % 60
|
16
|
-
|
17
|
-
"".tap do |time|
|
18
|
-
time << "#{pluralized(hours, 'hour')}, " if hours > 0
|
19
|
-
time << "#{pluralized(minutes, 'minute')} and " if minutes > 0
|
20
|
-
time << "#{pluralized(seconds, 'second')}"
|
21
|
-
end
|
22
|
-
end
|
3
|
+
attr_reader :testable_summaries
|
23
4
|
|
24
|
-
def
|
25
|
-
@
|
26
|
-
|
27
|
-
|
28
|
-
def total_tests_count
|
29
|
-
@items.reduce(0) { |count, item| count + item[:subtests].count }
|
30
|
-
end
|
31
|
-
|
32
|
-
def passing_count
|
33
|
-
total_tests_count - failures_count
|
5
|
+
def initialize(parsed_plist)
|
6
|
+
@parsed_plist = parsed_plist
|
7
|
+
@testable_summaries = create_testable_summaries
|
34
8
|
end
|
35
9
|
|
36
10
|
def model_name
|
37
|
-
@parsed_plist[
|
11
|
+
@parsed_plist['RunDestination']['TargetDevice']['ModelName']
|
12
|
+
rescue
|
13
|
+
'Undefined'
|
38
14
|
end
|
39
15
|
|
40
16
|
def os_version
|
41
|
-
@parsed_plist[
|
17
|
+
@parsed_plist['RunDestination']['TargetDevice']['OperatingSystemVersion']
|
18
|
+
rescue
|
19
|
+
'Undefined'
|
42
20
|
end
|
43
21
|
|
44
|
-
def
|
45
|
-
@
|
46
|
-
|
47
|
-
|
48
|
-
end
|
49
|
-
time + subtests_time
|
50
|
-
end.round
|
22
|
+
def project_name
|
23
|
+
@parsed_plist['TestableSummaries'].first['ProjectPath'].split('/').first.sub(/\..+/, '')
|
24
|
+
rescue
|
25
|
+
nil
|
51
26
|
end
|
52
27
|
|
53
|
-
|
54
|
-
|
28
|
+
private
|
29
|
+
|
30
|
+
def create_testable_summaries
|
31
|
+
raise GeneratingError, 'Testable summaries not present.' unless @parsed_plist['TestableSummaries']
|
32
|
+
@parsed_plist['TestableSummaries'].map do |testable_summary|
|
33
|
+
TestableSummary.new(testable_summary)
|
34
|
+
end
|
55
35
|
end
|
56
36
|
end
|
57
|
-
end
|
37
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'zip'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
module GoldenRose
|
5
|
+
class FileResource
|
6
|
+
attr_reader :path, :file_name, :folder_path, :source_type, :resource_type
|
7
|
+
|
8
|
+
TEST_SUMMARIES_FILE_NAME = 'action_TestSummaries.plist'
|
9
|
+
INFO_FILE_NAME = 'Info.plist'
|
10
|
+
ALLOWED_RESOURCE_TYPES = [
|
11
|
+
:test_summaries_plist,
|
12
|
+
:info_plist,
|
13
|
+
:build_log,
|
14
|
+
:screenshoot
|
15
|
+
]
|
16
|
+
|
17
|
+
def initialize(type, source_type, folder_path, file_name = nil, destination = nil)
|
18
|
+
@resource_type = validate_resource_type(type)
|
19
|
+
@source_type = source_type
|
20
|
+
@folder_path = folder_path
|
21
|
+
@destination = destination
|
22
|
+
initialize_filename(file_name)
|
23
|
+
open_source
|
24
|
+
rescue Zip::Error, Errno::ENOENT
|
25
|
+
raise GeneratingError, "Could not open the folder #{folder_path}."
|
26
|
+
end
|
27
|
+
|
28
|
+
def extension
|
29
|
+
@file_name.split('.').last
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete_extracted_file
|
33
|
+
return unless @path
|
34
|
+
File.delete(@path) if source_type == :zip
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def initialize_filename(file_name)
|
40
|
+
@file_name = case @resource_type
|
41
|
+
when :test_summaries_plist then TEST_SUMMARIES_FILE_NAME
|
42
|
+
when :info_plist then INFO_FILE_NAME
|
43
|
+
else file_name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate_resource_type(type)
|
48
|
+
return type if ALLOWED_RESOURCE_TYPES.include? type
|
49
|
+
fail ArgumentError, "FileResource of #{type} type is disallowed!"
|
50
|
+
end
|
51
|
+
|
52
|
+
def open_source
|
53
|
+
case @source_type
|
54
|
+
when :zip then open_zip
|
55
|
+
when :dir then open_directory(@file_name)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def compare_file_path(expected_path, file_path)
|
60
|
+
path_array = file_path.split('/').reverse
|
61
|
+
expected_path.split('/').reverse.map.with_index do |val, idx|
|
62
|
+
path_array[idx] == val
|
63
|
+
end.all?
|
64
|
+
end
|
65
|
+
|
66
|
+
def open_zip
|
67
|
+
Zip::File.open(folder_path) do |zip_file|
|
68
|
+
entry = zip_file.find do |entry|
|
69
|
+
compare_file_path(@file_name, entry.name)
|
70
|
+
end
|
71
|
+
if entry
|
72
|
+
uuid = ::SecureRandom.uuid
|
73
|
+
@path = if @destination
|
74
|
+
File.join(GoldenRose.root, "/#{@destination}")
|
75
|
+
else
|
76
|
+
File.join(GoldenRose.root, "/source_#{uuid}.#{extension}")
|
77
|
+
end
|
78
|
+
entry.extract(path)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def open_directory(file_name)
|
84
|
+
unless @destination
|
85
|
+
@path = Dir.glob("#{folder_path}/**/#{file_name}").first
|
86
|
+
return
|
87
|
+
end
|
88
|
+
source_path = Dir.glob("#{folder_path}/**/#{file_name}").first
|
89
|
+
cp_file(source_path, @destination)
|
90
|
+
dest_arr = @destination.split('/')
|
91
|
+
@path = dest_arr[1..dest_arr.length].join('/')
|
92
|
+
end
|
93
|
+
|
94
|
+
def cp_file(source, destination)
|
95
|
+
FileUtils.cp(source, destination)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -3,24 +3,87 @@
|
|
3
3
|
require "haml"
|
4
4
|
require "tilt/haml"
|
5
5
|
require "pathname"
|
6
|
+
require 'fileutils'
|
6
7
|
|
7
8
|
module GoldenRose
|
8
9
|
module Generators
|
9
10
|
class HtmlFormat
|
10
|
-
def initialize(results, output_path = nil)
|
11
|
-
@details = results
|
12
|
-
@
|
11
|
+
def initialize(results, build_logs, output_path = nil, force_save = false)
|
12
|
+
@details = results
|
13
|
+
@build_logs = build_logs
|
13
14
|
@output_path = output_path || ""
|
15
|
+
@force_save = force_save || false
|
14
16
|
end
|
15
17
|
|
16
18
|
def output
|
17
|
-
|
19
|
+
prepare_output_paths
|
20
|
+
make_output_html("index.html", "index.haml")
|
21
|
+
generate_test_logs if @details
|
22
|
+
end
|
23
|
+
|
24
|
+
def render_partial(path)
|
25
|
+
full_path = "#{GoldenRose.root}/lib/golden_rose/templates/#{path}"
|
26
|
+
render_haml_partial(full_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def render_asset(file_name, type: :js)
|
30
|
+
asset_path =
|
31
|
+
case type.to_sym
|
32
|
+
when :js then "#{GoldenRose.root}/lib/golden_rose/templates/assets/javascript/"
|
33
|
+
when :css then "#{GoldenRose.root}/lib/golden_rose/templates/assets/styles/"
|
34
|
+
end
|
35
|
+
render_haml_partial(asset_path + file_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def render_subactivities(subactivities)
|
40
|
+
return unless subactivities
|
41
|
+
full_path = "#{GoldenRose.root}/lib/golden_rose/templates/_test_logs.haml"
|
42
|
+
partial = Tilt::HamlTemplate.new(full_path)
|
43
|
+
return unless subactivities.children
|
44
|
+
partial.render(self, activity_summaries: subactivities.children)
|
45
|
+
end
|
46
|
+
|
47
|
+
def generate_test_logs
|
48
|
+
return unless @details.testable_summaries
|
49
|
+
@details.testable_summaries.each do |testable_summary|
|
50
|
+
testable_summary_name = testable_summary.name
|
51
|
+
next unless testable_summary.items
|
52
|
+
testable_summary.items.each do |item|
|
53
|
+
next unless item[:subtests]
|
54
|
+
item[:subtests].each do |subtest|
|
55
|
+
next unless subtest[:activity_summaries]
|
56
|
+
locals = {
|
57
|
+
testable_summary_name: testable_summary_name, item: item, subtest: subtest,
|
58
|
+
activity_summaries: subtest[:activity_summaries].children
|
59
|
+
}
|
60
|
+
make_output_html("test_logs/#{subtest[:uuid]}.html", "test_logs/index.haml", locals)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def render_haml_partial(path)
|
69
|
+
partial = Tilt::HamlTemplate.new(path)
|
70
|
+
partial.render(self)
|
71
|
+
end
|
72
|
+
|
73
|
+
def prepare_output_paths
|
74
|
+
FileUtils.remove_dir(@output_path, true) if @force_save
|
75
|
+
FileUtils.remove_dir(@output_path + "/test_logs", true)
|
76
|
+
FileUtils.mkdir_p @output_path + "/test_logs"
|
77
|
+
end
|
78
|
+
|
79
|
+
def make_output_html(relative_file_path, relative_template, *locals)
|
80
|
+
file_path = Pathname(@output_path) + relative_file_path
|
18
81
|
file = File.new(file_path, "w+")
|
19
|
-
template_path = File.join(GoldenRose::root, "/lib/golden_rose/templates
|
82
|
+
template_path = File.join(GoldenRose::root, "/lib/golden_rose/templates/#{relative_template}")
|
20
83
|
template = Tilt::HamlTemplate.new(template_path)
|
21
|
-
file.puts template.render(self)
|
84
|
+
file.puts template.render(self, *locals)
|
22
85
|
file.close
|
23
86
|
end
|
24
87
|
end
|
25
88
|
end
|
26
|
-
end
|
89
|
+
end
|