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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 760134cb39e5e84e51797d064d8fc4b3d0e772c6
4
- data.tar.gz: 8c7386d6714e8070b30dc08d171dc4b8ed0fecf0
3
+ metadata.gz: 87af3920c30c7c90ea498eaea2646802c77f4024
4
+ data.tar.gz: 645e890496f6343dba1f30041a4f2757303d224e
5
5
  SHA512:
6
- metadata.gz: 7911d91360192b462d5bb6fb84caa94ec511325279cdea31b8510a4a0bc7a84ed0b155ccec786a567edf97be6c06100d10c39920d13b470efd1f91a9a57805bb
7
- data.tar.gz: 14035a0968b6809fad19fd6845227cae7407cc46cf6f281144590f75afd813247ae0141ca8133e0201f31a8023150afe6da9092a7217a2ddd6dad3ee51794065
6
+ metadata.gz: e284409105b20bac7246a24ca1b81e2633ba05659fbcbd918d8f74342154797197a5dd0eeef4df011baeeba24ff8daf7fa655954f847c8eac550c9724289b650
7
+ data.tar.gz: 2d9368a84c7255be44b7803079a0b6e100e16b3c7eab4a45afbb8be63fbb694984387deafc5e044035fbb7decd2bd8f4e5e0175f35224408d32f210896f52439
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ /goldenrose
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # GoldenRose
2
2
 
3
- [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/PGSSoft/GoldenRose/blob/master/LICENSE)
3
+ [![Gem](https://img.shields.io/gem/v/golden_rose.svg)](https://rubygems.org/gems/golden_rose)
4
+ [![License](https://img.shields.io/github/license/PGSSoft/GoldenRose.svg)](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
- - [ ] Unit tests reports
41
- - [ ] Build logs
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/golden_rose"
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
@@ -0,0 +1,3 @@
1
+ require "golden_rose/build_log/build_target"
2
+ require "golden_rose/build_log/parser"
3
+ require "golden_rose/build_log/build_section"
@@ -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
- attr_reader :status, :time, :failures
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
- last_time = source_subtest["ActivitySummaries"].last["FinishTimeInterval"]
20
- first_time = source_subtest["ActivitySummaries"].first["StartTimeInterval"]
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
- return unless last_time && first_time
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
- counted_time = last_time - first_time
25
- @time = "#{counted_time.round(2)}s"
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
@@ -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[:output])
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
- def initialize(parsed_plist, items)
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 failures_count
25
- @items.reduce(0) { |count, item| count + item[:failures_count].to_i }
26
- end
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["RunDestination"]["TargetDevice"]["ModelName"]
11
+ @parsed_plist['RunDestination']['TargetDevice']['ModelName']
12
+ rescue
13
+ 'Undefined'
38
14
  end
39
15
 
40
16
  def os_version
41
- @parsed_plist["RunDestination"]["TargetDevice"]["OperatingSystemVersion"]
17
+ @parsed_plist['RunDestination']['TargetDevice']['OperatingSystemVersion']
18
+ rescue
19
+ 'Undefined'
42
20
  end
43
21
 
44
- def total_time
45
- @items.reduce(0) do |time, item|
46
- subtests_time = item[:subtests].reduce(0) do |time, item|
47
- time + item[:time].to_f
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
- def pluralized(number, noun)
54
- "#{number} #{noun}#{'s' if number != 1}"
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.details
12
- @items = results.items
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
- file_path = Pathname(@output_path) + "index.html"
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/index.haml")
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