1 package ginkgo 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/onsi/ginkgo/v2/internal" 8 "github.com/onsi/ginkgo/v2/internal/global" 9 "github.com/onsi/ginkgo/v2/reporters" 10 "github.com/onsi/ginkgo/v2/types" 11 ) 12 13 /* 14 Report represents the report for a Suite. 15 It is documented here: https://pkg.go.dev/github.com/onsi/ginkgo/v2/types#Report 16 */ 17 type Report = types.Report 18 19 /* 20 Report represents the report for a Spec. 21 It is documented here: https://pkg.go.dev/github.com/onsi/ginkgo/v2/types#SpecReport 22 */ 23 type SpecReport = types.SpecReport 24 25 /* 26 CurrentSpecReport returns information about the current running spec. 27 The returned object is a types.SpecReport which includes helper methods 28 to make extracting information about the spec easier. 29 30 You can learn more about SpecReport here: https://pkg.go.dev/github.com/onsi/ginkgo/types#SpecReport 31 You can learn more about CurrentSpecReport() here: https://onsi.github.io/ginkgo/#getting-a-report-for-the-current-spec 32 */ 33 func CurrentSpecReport() SpecReport { 34 return global.Suite.CurrentSpecReport() 35 } 36 37 /* 38 ReportEntryVisibility governs the visibility of ReportEntries in Ginkgo's console reporter 39 40 - ReportEntryVisibilityAlways: the default behavior - the ReportEntry is always emitted. 41 - ReportEntryVisibilityFailureOrVerbose: the ReportEntry is only emitted if the spec fails or if the tests are run with -v (similar to GinkgoWriters behavior). 42 - ReportEntryVisibilityNever: the ReportEntry is never emitted though it appears in any generated machine-readable reports (e.g. by setting `--json-report`). 43 44 You can learn more about Report Entries here: https://onsi.github.io/ginkgo/#attaching-data-to-reports 45 */ 46 type ReportEntryVisibility = types.ReportEntryVisibility 47 48 const ReportEntryVisibilityAlways, ReportEntryVisibilityFailureOrVerbose, ReportEntryVisibilityNever = types.ReportEntryVisibilityAlways, types.ReportEntryVisibilityFailureOrVerbose, types.ReportEntryVisibilityNever 49 50 /* 51 AddReportEntry generates and adds a new ReportEntry to the current spec's SpecReport. 52 It can take any of the following arguments: 53 - A single arbitrary object to attach as the Value of the ReportEntry. This object will be included in any generated reports and will be emitted to the console when the report is emitted. 54 - A ReportEntryVisibility enum to control the visibility of the ReportEntry 55 - An Offset or CodeLocation decoration to control the reported location of the ReportEntry 56 57 If the Value object implements `fmt.Stringer`, it's `String()` representation is used when emitting to the console. 58 59 AddReportEntry() must be called within a Subject or Setup node - not in a Container node. 60 61 You can learn more about Report Entries here: https://onsi.github.io/ginkgo/#attaching-data-to-reports 62 */ 63 func AddReportEntry(name string, args ...interface{}) { 64 cl := types.NewCodeLocation(1) 65 reportEntry, err := internal.NewReportEntry(name, cl, args...) 66 if err != nil { 67 Fail(fmt.Sprintf("Failed to generate Report Entry:\n%s", err.Error()), 1) 68 } 69 err = global.Suite.AddReportEntry(reportEntry) 70 if err != nil { 71 Fail(fmt.Sprintf("Failed to add Report Entry:\n%s", err.Error()), 1) 72 } 73 } 74 75 /* 76 ReportBeforeEach nodes are run for each spec, even if the spec is skipped or pending. ReportBeforeEach nodes take a function that 77 receives a SpecReport or both SpecContext and Report for interruptible behavior. They are called before the spec starts. 78 79 Example: 80 81 ReportBeforeEach(func(report SpecReport) { // process report }) 82 ReportBeforeEach(func(ctx SpecContext, report SpecReport) { 83 // process report 84 }), NodeTimeout(1 * time.Minute)) 85 86 You cannot nest any other Ginkgo nodes within a ReportBeforeEach node's closure. 87 You can learn more about ReportBeforeEach here: https://onsi.github.io/ginkgo/#generating-reports-programmatically 88 89 You can learn about interruptible nodes here: https://onsi.github.io/ginkgo/#spec-timeouts-and-interruptible-nodes 90 */ 91 func ReportBeforeEach(body any, args ...any) bool { 92 combinedArgs := []interface{}{body} 93 combinedArgs = append(combinedArgs, args...) 94 95 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportBeforeEach, "", combinedArgs...)) 96 } 97 98 /* 99 ReportAfterEach nodes are run for each spec, even if the spec is skipped or pending. 100 ReportAfterEach nodes take a function that receives a SpecReport or both SpecContext and Report for interruptible behavior. 101 They are called after the spec has completed and receive the final report for the spec. 102 103 Example: 104 105 ReportAfterEach(func(report SpecReport) { // process report }) 106 ReportAfterEach(func(ctx SpecContext, report SpecReport) { 107 // process report 108 }), NodeTimeout(1 * time.Minute)) 109 110 You cannot nest any other Ginkgo nodes within a ReportAfterEach node's closure. 111 You can learn more about ReportAfterEach here: https://onsi.github.io/ginkgo/#generating-reports-programmatically 112 113 You can learn about interruptible nodes here: https://onsi.github.io/ginkgo/#spec-timeouts-and-interruptible-nodes 114 */ 115 func ReportAfterEach(body any, args ...any) bool { 116 combinedArgs := []interface{}{body} 117 combinedArgs = append(combinedArgs, args...) 118 119 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportAfterEach, "", combinedArgs...)) 120 } 121 122 /* 123 ReportBeforeSuite nodes are run at the beginning of the suite. ReportBeforeSuite nodes take a function 124 that can either receive Report or both SpecContext and Report for interruptible behavior. 125 126 Example Usage: 127 128 ReportBeforeSuite(func(r Report) { // process report }) 129 ReportBeforeSuite(func(ctx SpecContext, r Report) { 130 // process report 131 }, NodeTimeout(1 * time.Minute)) 132 133 They are called at the beginning of the suite, before any specs have run and any BeforeSuite or SynchronizedBeforeSuite nodes, and are passed in the initial report for the suite. 134 ReportBeforeSuite nodes must be created at the top-level (i.e. not nested in a Context/Describe/When node) 135 136 # When running in parallel, Ginkgo ensures that only one of the parallel nodes runs the ReportBeforeSuite 137 138 You cannot nest any other Ginkgo nodes within a ReportAfterSuite node's closure. 139 You can learn more about ReportAfterSuite here: https://onsi.github.io/ginkgo/#generating-reports-programmatically 140 141 You can learn more about Ginkgo's reporting infrastructure, including generating reports with the CLI here: https://onsi.github.io/ginkgo/#generating-machine-readable-reports 142 143 You can learn about interruptible nodes here: https://onsi.github.io/ginkgo/#spec-timeouts-and-interruptible-nodes 144 */ 145 func ReportBeforeSuite(body any, args ...any) bool { 146 combinedArgs := []interface{}{body} 147 combinedArgs = append(combinedArgs, args...) 148 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportBeforeSuite, "", combinedArgs...)) 149 } 150 151 /* 152 ReportAfterSuite nodes are run at the end of the suite. ReportAfterSuite nodes execute at the suite's conclusion, 153 and accept a function that can either receive Report or both SpecContext and Report for interruptible behavior. 154 155 Example Usage: 156 157 ReportAfterSuite("Non-interruptible ReportAfterSuite", func(r Report) { // process report }) 158 ReportAfterSuite("Interruptible ReportAfterSuite", func(ctx SpecContext, r Report) { 159 // process report 160 }, NodeTimeout(1 * time.Minute)) 161 162 They are called at the end of the suite, after all specs have run and any AfterSuite or SynchronizedAfterSuite nodes, and are passed in the final report for the suite. 163 ReportAfterSuite nodes must be created at the top-level (i.e. not nested in a Context/Describe/When node) 164 165 When running in parallel, Ginkgo ensures that only one of the parallel nodes runs the ReportAfterSuite and that it is passed a report that is aggregated across 166 all parallel nodes 167 168 In addition to using ReportAfterSuite to programmatically generate suite reports, you can also generate JSON, JUnit, and Teamcity formatted reports using the --json-report, --junit-report, and --teamcity-report ginkgo CLI flags. 169 170 You cannot nest any other Ginkgo nodes within a ReportAfterSuite node's closure. 171 You can learn more about ReportAfterSuite here: https://onsi.github.io/ginkgo/#generating-reports-programmatically 172 173 You can learn more about Ginkgo's reporting infrastructure, including generating reports with the CLI here: https://onsi.github.io/ginkgo/#generating-machine-readable-reports 174 175 You can learn about interruptible nodes here: https://onsi.github.io/ginkgo/#spec-timeouts-and-interruptible-nodes 176 */ 177 func ReportAfterSuite(text string, body any, args ...interface{}) bool { 178 combinedArgs := []interface{}{body} 179 combinedArgs = append(combinedArgs, args...) 180 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportAfterSuite, text, combinedArgs...)) 181 } 182 183 func registerReportAfterSuiteNodeForAutogeneratedReports(reporterConfig types.ReporterConfig) { 184 body := func(report Report) { 185 if reporterConfig.JSONReport != "" { 186 err := reporters.GenerateJSONReport(report, reporterConfig.JSONReport) 187 if err != nil { 188 Fail(fmt.Sprintf("Failed to generate JSON report:\n%s", err.Error())) 189 } 190 } 191 if reporterConfig.JUnitReport != "" { 192 err := reporters.GenerateJUnitReport(report, reporterConfig.JUnitReport) 193 if err != nil { 194 Fail(fmt.Sprintf("Failed to generate JUnit report:\n%s", err.Error())) 195 } 196 } 197 if reporterConfig.TeamcityReport != "" { 198 err := reporters.GenerateTeamcityReport(report, reporterConfig.TeamcityReport) 199 if err != nil { 200 Fail(fmt.Sprintf("Failed to generate Teamcity report:\n%s", err.Error())) 201 } 202 } 203 } 204 205 flags := []string{} 206 if reporterConfig.JSONReport != "" { 207 flags = append(flags, "--json-report") 208 } 209 if reporterConfig.JUnitReport != "" { 210 flags = append(flags, "--junit-report") 211 } 212 if reporterConfig.TeamcityReport != "" { 213 flags = append(flags, "--teamcity-report") 214 } 215 pushNode(internal.NewNode( 216 deprecationTracker, types.NodeTypeReportAfterSuite, 217 fmt.Sprintf("Autogenerated ReportAfterSuite for %s", strings.Join(flags, " ")), 218 body, 219 types.NewCustomCodeLocation("autogenerated by Ginkgo"), 220 )) 221 } 222