golden_rose 0.1.0 → 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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