rebase: bump github.com/onsi/ginkgo/v2 from 2.3.1 to 2.4.0

Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.3.1 to 2.4.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.3.1...v2.4.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2022-10-24 20:14:32 +00:00
committed by mergify[bot]
parent 49245788fc
commit 807f776132
68 changed files with 13648 additions and 402 deletions

View File

@ -1,3 +1,25 @@
## 2.4.0
### Features
- DeferCleanup supports functions with multiple-return values [5e33c75]
- Add GinkgoLogr (#1067) [bf78c28]
- Introduction of 'MustPassRepeatedly' decorator (#1051) [047c02f]
### Fixes
- correcting some typos (#1064) [1403d3c]
- fix flaky internal_integration interupt specs [2105ba3]
- Correct busted link in README [be6b5b9]
### Maintenance
- Bump actions/checkout from 2 to 3 (#1062) [8a2f483]
- Bump golang.org/x/tools from 0.1.12 to 0.2.0 (#1065) [529c4e8]
- Bump github/codeql-action from 1 to 2 (#1061) [da09146]
- Bump actions/setup-go from 2 to 3 (#1060) [918040d]
- Bump github.com/onsi/gomega from 1.22.0 to 1.22.1 (#1053) [2098e4d]
- Bump nokogiri from 1.13.8 to 1.13.9 in /docs (#1066) [1d74122]
- Add GHA to dependabot config [4442772]
## 2.3.1
## Fixes
@ -8,7 +30,7 @@ With this patch release, the ginkgo CLI can now identify a version mismatch and
- Ginkgo cli can identify version mismatches and emit a helpful error message [bc4ae2f]
- further emphasize that a version match is required when running Ginkgo on CI and/or locally [2691dd8]
## Maintenance
### Maintenance
- bump gomega to v1.22.0 [822a937]
## 2.3.0

View File

@ -90,7 +90,7 @@ If you have a question, comment, bug report, feature request, etc. please open a
## Capabilities
Whether writing basic unit specs, complex integration specs, or even performance specs - Ginkgo gives you an expressive Domain-Specific Language (DSL) that will be familiar to users coming from frameworks such as [Quick](https://github.com/Quick/Quick), [RSpec](https://rspec.info), [Jasmine](https://jasmine.github.io), and [Busted](https://olivinelabs.com/busted/). This style of testing is sometimes referred to as "Behavior-Driven Development" (BDD) though Ginkgo's utility extends beyond acceptance-level testing.
Whether writing basic unit specs, complex integration specs, or even performance specs - Ginkgo gives you an expressive Domain-Specific Language (DSL) that will be familiar to users coming from frameworks such as [Quick](https://github.com/Quick/Quick), [RSpec](https://rspec.info), [Jasmine](https://jasmine.github.io), and [Busted](https://lunarmodules.github.io/busted/). This style of testing is sometimes referred to as "Behavior-Driven Development" (BDD) though Ginkgo's utility extends beyond acceptance-level testing.
With Ginkgo's DSL you can use nestable [`Describe`, `Context` and `When` container nodes](https://onsi.github.io/ginkgo/#organizing-specs-with-container-nodes) to help you organize your specs. [`BeforeEach` and `AfterEach` setup nodes](https://onsi.github.io/ginkgo/#extracting-common-setup-beforeeach) for setup and cleanup. [`It` and `Specify` subject nodes](https://onsi.github.io/ginkgo/#spec-subjects-it) that hold your assertions. [`BeforeSuite` and `AfterSuite` nodes](https://onsi.github.io/ginkgo/#suite-setup-and-cleanup-beforesuite-and-aftersuite) to prep for and cleanup after a suite... and [much more!](https://onsi.github.io/ginkgo/#writing-specs)

View File

@ -5,7 +5,7 @@ A Ginkgo release is a tagged git sha and a GitHub release. To cut a release:
```bash
LAST_VERSION=$(git tag --sort=version:refname | tail -n1)
CHANGES=$(git log --pretty=format:'- %s [%h]' HEAD...$LAST_VERSION)
echo -e "## NEXT\n\n$CHANGES\n\n### Features\n\n## Fixes\n\n## Maintenance\n\n$(cat CHANGELOG.md)" > CHANGELOG.md
echo -e "## NEXT\n\n$CHANGES\n\n### Features\n\n### Fixes\n\n### Maintenance\n\n$(cat CHANGELOG.md)" > CHANGELOG.md
```
to update the changelog
- Categorize the changes into

View File

@ -23,6 +23,7 @@ import (
"strings"
"time"
"github.com/go-logr/logr"
"github.com/onsi/ginkgo/v2/formatter"
"github.com/onsi/ginkgo/v2/internal"
"github.com/onsi/ginkgo/v2/internal/global"
@ -46,7 +47,9 @@ func init() {
var err error
flagSet, err = types.BuildTestSuiteFlagSet(&suiteConfig, &reporterConfig)
exitIfErr(err)
GinkgoWriter = internal.NewWriter(os.Stdout)
writer := internal.NewWriter(os.Stdout)
GinkgoWriter = writer
GinkgoLogr = internal.GinkgoLogrFunc(writer)
}
func exitIfErr(err error) {
@ -90,11 +93,11 @@ type GinkgoWriterInterface interface {
}
/*
SpecContext is the context object passed into nodes that are subject to a timeout or need to be notified of an interrupt. It implements the standard context.Context interface but also contains additional helpers to provide an extensibility point for Ginkgo. (As an example, Gomega's Eventually can use the methods defined on SpecContext to provide deeper integratoin with Ginkgo).
SpecContext is the context object passed into nodes that are subject to a timeout or need to be notified of an interrupt. It implements the standard context.Context interface but also contains additional helpers to provide an extensibility point for Ginkgo. (As an example, Gomega's Eventually can use the methods defined on SpecContext to provide deeper integratoin with Ginkgo).
You can do anything with SpecContext that you do with a typical context.Context including wrapping it with any of the context.With* methods.
You can do anything with SpecContext that you do with a typical context.Context including wrapping it with any of the context.With* methods.
Ginkgo will cancel the SpecContext when a node is interrupted (e.g. by the user sending an interupt signal) or when a node has exceeded it's allowed run-time. Note, however, that even in cases where a node has a deadline, SpecContext will not return a deadline via .Deadline(). This is because Ginkgo does not use a WithDeadline() context to model node deadlines as Ginkgo needs control over the precise timing of the context cancellation to ensure it can provide an accurate progress report at the moment of cancellation.
Ginkgo will cancel the SpecContext when a node is interrupted (e.g. by the user sending an interupt signal) or when a node has exceeded it's allowed run-time. Note, however, that even in cases where a node has a deadline, SpecContext will not return a deadline via .Deadline(). This is because Ginkgo does not use a WithDeadline() context to model node deadlines as Ginkgo needs control over the precise timing of the context cancellation to ensure it can provide an accurate progress report at the moment of cancellation.
*/
type SpecContext = internal.SpecContext
@ -112,6 +115,11 @@ You can learn more at https://onsi.github.io/ginkgo/#logging-output
*/
var GinkgoWriter GinkgoWriterInterface
/*
GinkgoLogr is a logr.Logger that writes to GinkgoWriter
*/
var GinkgoLogr logr.Logger
// The interface by which Ginkgo receives *testing.T
type GinkgoTestingT interface {
Fail()
@ -686,7 +694,6 @@ Multiple BeforeAll nodes can be defined in a given Ordered container however the
BeforeAll can take a func() body, or an interruptible func(SpecContext)/func(context.Context) body.
You cannot nest any other Ginkgo nodes within a BeforeAll node's closure.
You can learn more about Ordered Containers at: https://onsi.github.io/ginkgo/#ordered-containers
And you can learn more about BeforeAll at: https://onsi.github.io/ginkgo/#setup-in-ordered-containers-beforeall-and-afterall
@ -717,10 +724,10 @@ DeferCleanup can be called within any Setup or Subject node to register a cleanu
DeferCleanup can be passed:
1. A function that takes no arguments and returns no values.
2. A function that returns an error (in which case it will assert that the returned error was nil, or it will fail the spec).
3. A function that takes a context.Context or SpecContext (and optionally returns an error). The resulting cleanup node is deemed interruptible and the passed-in context will be cancelled in the event of a timeout or interrupt.
4. A function that takes arguments (and optionally returns an error) followed by a list of arguments to pass to the function.
5. A function that takes SpecContext and a list of arguments (and optionally returns an error) followed by a list of arguments to pass to the function.
2. A function that returns multiple values. `DeferCleanup` will ignore all these return values except for the last one. If this last return value is a non-nil error `DeferCleanup` will fail the spec).
3. A function that takes a context.Context or SpecContext (and optionally returns multiple values). The resulting cleanup node is deemed interruptible and the passed-in context will be cancelled in the event of a timeout or interrupt.
4. A function that takes arguments (and optionally returns multiple values) followed by a list of arguments to pass to the function.
5. A function that takes SpecContext and a list of arguments (and optionally returns multiple values) followed by a list of arguments to pass to the function.
For example:

View File

@ -13,13 +13,21 @@ You can learn more about decorators here: https://onsi.github.io/ginkgo/#decorat
type Offset = internal.Offset
/*
FlakeAttempts(uint N) is a decorator that allows you to mark individual specs or spec containers as flaky. Ginkgo will run them up to `N` times until they pass.
FlakeAttempts(uint N) is a decorator that allows you to mark individual specs or spec containers as flaky. Ginkgo will run them up to `N` times until they pass.
You can learn more here: https://onsi.github.io/ginkgo/#repeating-spec-runs-and-managing-flaky-specs
You can learn more here: https://onsi.github.io/ginkgo/#the-flakeattempts-decorator
You can learn more about decorators here: https://onsi.github.io/ginkgo/#decorator-reference
*/
type FlakeAttempts = internal.FlakeAttempts
/*
MustPassRepeatedly(uint N) is a decorator that allows you to repeat the execution of individual specs or spec containers. Ginkgo will run them up to `N` times until they fail.
You can learn more here: https://onsi.github.io/ginkgo/#the-mustpassrepeatedly-decorator
You can learn more about decorators here: https://onsi.github.io/ginkgo/#decorator-reference
*/
type MustPassRepeatedly = internal.MustPassRepeatedly
/*
Focus is a decorator that allows you to mark a spec or container as focused. Identical to FIt and FDescribe.

View File

@ -118,6 +118,8 @@ func (g *group) initialReportForSpec(spec Spec) types.SpecReport {
ParallelProcess: g.suite.config.ParallelProcess,
IsSerial: spec.Nodes.HasNodeMarkedSerial(),
IsInOrderedContainer: !spec.Nodes.FirstNodeMarkedOrdered().IsZero(),
MaxFlakeAttempts: spec.Nodes.GetMaxFlakeAttempts(),
MaxMustPassRepeatedly: spec.Nodes.GetMaxMustPassRepeatedly(),
}
}
@ -299,16 +301,29 @@ func (g *group) run(specs Specs) {
g.suite.currentSpecReport.StartTime = time.Now()
if !skip {
maxAttempts := max(1, spec.FlakeAttempts())
if g.suite.config.FlakeAttempts > 0 {
var maxAttempts = 1
if g.suite.currentSpecReport.MaxMustPassRepeatedly > 0 {
maxAttempts = max(1, spec.MustPassRepeatedly())
} else if g.suite.config.FlakeAttempts > 0 {
maxAttempts = g.suite.config.FlakeAttempts
g.suite.currentSpecReport.MaxFlakeAttempts = maxAttempts
} else if g.suite.currentSpecReport.MaxFlakeAttempts > 0 {
maxAttempts = max(1, spec.FlakeAttempts())
}
for attempt := 0; attempt < maxAttempts; attempt++ {
g.suite.currentSpecReport.NumAttempts = attempt + 1
g.suite.writer.Truncate()
g.suite.outputInterceptor.StartInterceptingOutput()
if attempt > 0 {
fmt.Fprintf(g.suite.writer, "\nGinkgo: Attempt #%d Failed. Retrying...\n", attempt)
if g.suite.currentSpecReport.MaxMustPassRepeatedly > 0 {
fmt.Fprintf(g.suite.writer, "\nGinkgo: Attempt #%d Passed. Repeating...\n", attempt)
}
if g.suite.currentSpecReport.MaxFlakeAttempts > 0 {
fmt.Fprintf(g.suite.writer, "\nGinkgo: Attempt #%d Failed. Retrying...\n", attempt)
}
}
g.attemptSpec(attempt == maxAttempts-1, spec)
@ -318,8 +333,15 @@ func (g *group) run(specs Specs) {
g.suite.currentSpecReport.CapturedGinkgoWriterOutput += string(g.suite.writer.Bytes())
g.suite.currentSpecReport.CapturedStdOutErr += g.suite.outputInterceptor.StopInterceptingAndReturnOutput()
if g.suite.currentSpecReport.State.Is(types.SpecStatePassed | types.SpecStateSkipped | types.SpecStateAborted | types.SpecStateInterrupted) {
break
if g.suite.currentSpecReport.MaxMustPassRepeatedly > 0 {
if g.suite.currentSpecReport.State.Is(types.SpecStateFailureStates | types.SpecStateSkipped) {
break
}
}
if g.suite.currentSpecReport.MaxFlakeAttempts > 0 {
if g.suite.currentSpecReport.State.Is(types.SpecStatePassed | types.SpecStateSkipped | types.SpecStateAborted | types.SpecStateInterrupted) {
break
}
}
}
}

View File

@ -54,6 +54,7 @@ type Node struct {
MarkedOncePerOrdered bool
MarkedSuppressProgressReporting bool
FlakeAttempts int
MustPassRepeatedly int
Labels Labels
PollProgressAfter time.Duration
PollProgressInterval time.Duration
@ -80,6 +81,7 @@ const OncePerOrdered = honorsOrderedType(true)
const SuppressProgressReporting = suppressProgressReporting(true)
type FlakeAttempts uint
type MustPassRepeatedly uint
type Offset uint
type Done chan<- interface{} // Deprecated Done Channel for asynchronous testing
type Labels []string
@ -138,6 +140,8 @@ func isDecoration(arg interface{}) bool {
return true
case t == reflect.TypeOf(FlakeAttempts(0)):
return true
case t == reflect.TypeOf(MustPassRepeatedly(0)):
return true
case t == reflect.TypeOf(Labels{}):
return true
case t == reflect.TypeOf(PollProgressInterval(0)):
@ -253,6 +257,11 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
if !nodeType.Is(types.NodeTypesForContainerAndIt) {
appendError(types.GinkgoErrors.InvalidDecoratorForNodeType(node.CodeLocation, nodeType, "FlakeAttempts"))
}
case t == reflect.TypeOf(MustPassRepeatedly(0)):
node.MustPassRepeatedly = int(arg.(MustPassRepeatedly))
if !nodeType.Is(types.NodeTypesForContainerAndIt) {
appendError(types.GinkgoErrors.InvalidDecoratorForNodeType(node.CodeLocation, nodeType, "MustPassRepeatedly"))
}
case t == reflect.TypeOf(PollProgressAfter(0)):
node.PollProgressAfter = time.Duration(arg.(PollProgressAfter))
if nodeType.Is(types.NodeTypeContainer) {
@ -403,6 +412,10 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
appendError(types.GinkgoErrors.UnknownDecorator(node.CodeLocation, nodeType, arg))
}
if node.FlakeAttempts > 0 && node.MustPassRepeatedly > 0 {
appendError(types.GinkgoErrors.InvalidDeclarationOfFlakeAttemptsAndMustPassRepeatedly(node.CodeLocation, nodeType))
}
if len(errors) > 0 {
return Node{}, errors
}
@ -495,6 +508,8 @@ func extractSynchronizedBeforeSuiteAllProcsBody(arg interface{}) (func(SpecConte
}, hasContext
}
var errInterface = reflect.TypeOf((*error)(nil)).Elem()
func NewCleanupNode(deprecationTracker *types.DeprecationTracker, fail func(string, types.CodeLocation), args ...interface{}) (Node, []error) {
decorations, remainingArgs := PartitionDecorations(args...)
baseOffset := 2
@ -517,7 +532,7 @@ func NewCleanupNode(deprecationTracker *types.DeprecationTracker, fail func(stri
}
callback := reflect.ValueOf(remainingArgs[0])
if !(callback.Kind() == reflect.Func && callback.Type().NumOut() <= 1) {
if !(callback.Kind() == reflect.Func) {
return Node{}, []error{types.GinkgoErrors.DeferCleanupInvalidFunction(cl)}
}
@ -537,8 +552,12 @@ func NewCleanupNode(deprecationTracker *types.DeprecationTracker, fail func(stri
}
handleFailure := func(out []reflect.Value) {
if len(out) == 1 && !out[0].IsNil() {
fail(fmt.Sprintf("DeferCleanup callback returned error: %v", out[0]), cl)
if len(out) == 0 {
return
}
last := out[len(out)-1]
if last.Type().Implements(errInterface) && !last.IsNil() {
fail(fmt.Sprintf("DeferCleanup callback returned error: %v", last), cl)
}
}
@ -842,6 +861,26 @@ func (n Nodes) FirstNodeMarkedOrdered() Node {
return Node{}
}
func (n Nodes) GetMaxFlakeAttempts() int {
maxFlakeAttempts := 0
for i := range n {
if n[i].FlakeAttempts > 0 {
maxFlakeAttempts = n[i].FlakeAttempts
}
}
return maxFlakeAttempts
}
func (n Nodes) GetMaxMustPassRepeatedly() int {
maxMustPassRepeatedly := 0
for i := range n {
if n[i].MustPassRepeatedly > 0 {
maxMustPassRepeatedly = n[i].MustPassRepeatedly
}
}
return maxMustPassRepeatedly
}
func unrollInterfaceSlice(args interface{}) []interface{} {
v := reflect.ValueOf(args)
if v.Kind() != reflect.Slice {

View File

@ -41,6 +41,17 @@ func (s Spec) FlakeAttempts() int {
return flakeAttempts
}
func (s Spec) MustPassRepeatedly() int {
mustPassRepeatedly := 0
for i := range s.Nodes {
if s.Nodes[i].MustPassRepeatedly > 0 {
mustPassRepeatedly = s.Nodes[i].MustPassRepeatedly
}
}
return mustPassRepeatedly
}
func (s Spec) SpecTimeout() time.Duration {
return s.FirstNodeWithType(types.NodeTypeIt).SpecTimeout
}

View File

@ -5,6 +5,9 @@ import (
"fmt"
"io"
"sync"
"github.com/go-logr/logr"
"github.com/go-logr/logr/funcr"
)
type WriterMode uint
@ -101,3 +104,9 @@ func (w *Writer) Printf(format string, a ...interface{}) {
func (w *Writer) Println(a ...interface{}) {
fmt.Fprintln(w, a...)
}
func GinkgoLogrFunc(writer *Writer) logr.Logger {
return funcr.New(func(prefix, args string) {
writer.Printf("%s", args)
}, funcr.Options{})
}

View File

@ -149,7 +149,7 @@ func (r *DefaultReporter) DidRun(report types.SpecReport) {
}
} else {
header, stream = denoter, true
if report.NumAttempts > 1 {
if report.NumAttempts > 1 && report.MaxFlakeAttempts > 1 {
header, stream = fmt.Sprintf("%s [FLAKEY TEST - TOOK %d ATTEMPTS TO PASS]", r.retryDenoter, report.NumAttempts), false
}
if report.RunTime > r.conf.SlowSpecThreshold {
@ -184,6 +184,9 @@ func (r *DefaultReporter) DidRun(report types.SpecReport) {
header = fmt.Sprintf("%s! [ABORTED]", denoter)
}
if report.State.Is(types.SpecStateFailureStates) && report.MaxMustPassRepeatedly > 1 {
header, stream = fmt.Sprintf("%s DURING REPETITION #%d", header, report.NumAttempts), false
}
// Emit stream and return
if stream {
r.emit(r.f(highlightColor + header + "{{/}}"))
@ -376,6 +379,9 @@ func (r *DefaultReporter) SuiteDidEnd(report types.Report) {
if specs.CountOfFlakedSpecs() > 0 {
r.emit(r.f("{{light-yellow}}{{bold}}%d Flaked{{/}} | ", specs.CountOfFlakedSpecs()))
}
if specs.CountOfRepeatedSpecs() > 0 {
r.emit(r.f("{{light-yellow}}{{bold}}%d Repeated{{/}} | ", specs.CountOfRepeatedSpecs()))
}
r.emit(r.f("{{yellow}}{{bold}}%d Pending{{/}} | ", specs.CountWithState(types.SpecStatePending)))
r.emit(r.f("{{cyan}}{{bold}}%d Skipped{{/}}\n", specs.CountWithState(types.SpecStateSkipped)))
}

View File

@ -180,6 +180,15 @@ func (g ginkgoErrors) InvalidDeclarationOfFocusedAndPending(cl CodeLocation, nod
}
}
func (g ginkgoErrors) InvalidDeclarationOfFlakeAttemptsAndMustPassRepeatedly(cl CodeLocation, nodeType NodeType) error {
return GinkgoError{
Heading: "Invalid Combination of Decorators: FlakeAttempts and MustPassRepeatedly",
Message: formatter.F(`[%s] node was decorated with both FlakeAttempts and MustPassRepeatedly. At most one is allowed.`, nodeType),
CodeLocation: cl,
DocLink: "node-decorators-overview",
}
}
func (g ginkgoErrors) UnknownDecorator(cl CodeLocation, nodeType NodeType, decorator interface{}) error {
return GinkgoError{
Heading: "Unknown Decorator",

View File

@ -67,7 +67,7 @@ type PreRunStats struct {
SpecsThatWillRun int
}
//Add is ued by Ginkgo's parallel aggregation mechanisms to combine test run reports form individual parallel processes
//Add is used by Ginkgo's parallel aggregation mechanisms to combine test run reports form individual parallel processes
//to form a complete final report.
func (report Report) Add(other Report) Report {
report.SuiteSucceeded = report.SuiteSucceeded && other.SuiteSucceeded
@ -151,10 +151,17 @@ type SpecReport struct {
//It includes detailed information about the Failure
Failure Failure
// NumAttempts captures the number of times this Spec was run. Flakey specs can be retried with
// ginkgo --flake-attempts=N
// NumAttempts captures the number of times this Spec was run.
// Flakey specs can be retried with ginkgo --flake-attempts=N or the use of the FlakeAttempts decorator.
// Repeated specs can be retried with the use of the MustPassRepeatedly decorator
NumAttempts int
// MaxFlakeAttempts captures whether the spec has been retried with ginkgo --flake-attempts=N or the use of the FlakeAttempts decorator.
MaxFlakeAttempts int
// MaxMustPassRepeatedly captures whether the spec has the MustPassRepeatedly decorator
MaxMustPassRepeatedly int
// CapturedGinkgoWriterOutput contains text printed to the GinkgoWriter
CapturedGinkgoWriterOutput string
@ -190,6 +197,8 @@ func (report SpecReport) MarshalJSON() ([]byte, error) {
ParallelProcess int
Failure *Failure `json:",omitempty"`
NumAttempts int
MaxFlakeAttempts int
MaxMustPassRepeatedly int
CapturedGinkgoWriterOutput string `json:",omitempty"`
CapturedStdOutErr string `json:",omitempty"`
ReportEntries ReportEntries `json:",omitempty"`
@ -211,6 +220,8 @@ func (report SpecReport) MarshalJSON() ([]byte, error) {
Failure: nil,
ReportEntries: nil,
NumAttempts: report.NumAttempts,
MaxFlakeAttempts: report.MaxFlakeAttempts,
MaxMustPassRepeatedly: report.MaxMustPassRepeatedly,
CapturedGinkgoWriterOutput: report.CapturedGinkgoWriterOutput,
CapturedStdOutErr: report.CapturedStdOutErr,
}
@ -363,11 +374,22 @@ func (reports SpecReports) CountWithState(states SpecState) int {
return n
}
//CountWithState returns the number of SpecReports that passed after multiple attempts
//If the Spec passes, CountOfFlakedSpecs returns the number of SpecReports that failed after multiple attempts.
func (reports SpecReports) CountOfFlakedSpecs() int {
n := 0
for i := range reports {
if reports[i].State.Is(SpecStatePassed) && reports[i].NumAttempts > 1 {
if reports[i].MaxFlakeAttempts > 1 && reports[i].State.Is(SpecStatePassed) && reports[i].NumAttempts > 1 {
n += 1
}
}
return n
}
//If the Spec fails, CountOfRepeatedSpecs returns the number of SpecReports that passed after multiple attempts
func (reports SpecReports) CountOfRepeatedSpecs() int {
n := 0
for i := range reports {
if reports[i].MaxMustPassRepeatedly > 1 && reports[i].State.Is(SpecStateFailureStates) && reports[i].NumAttempts > 1 {
n += 1
}
}

View File

@ -1,3 +1,3 @@
package types
const VERSION = "2.3.1"
const VERSION = "2.4.0"