1
1
import linecache
2
+ import textwrap
2
3
from pathlib import Path
3
- from typing import List , Optional , Union
4
+ from typing import List , Optional
4
5
5
6
from gherkin .errors import CompositeParserException
6
7
from gherkin .parser import Parser
7
8
from gherkin .token_scanner import TokenScanner
8
- from pydantic import BaseModel
9
+ from pydantic import BaseModel , field_validator , model_validator
10
+
11
+ from . import exceptions
12
+ from .types import STEP_TYPES
9
13
10
14
11
15
class Location (BaseModel ):
@@ -41,6 +45,10 @@ class DocString(BaseModel):
41
45
delimiter : str
42
46
location : Location
43
47
48
+ @field_validator ("content" , mode = "before" )
49
+ def dedent_content (cls , value : str ) -> str :
50
+ return textwrap .dedent (value )
51
+
44
52
45
53
class Step (BaseModel ):
46
54
id : str
@@ -51,6 +59,18 @@ class Step(BaseModel):
51
59
dataTable : Optional [DataTable ] = None
52
60
docString : Optional [DocString ] = None
53
61
62
+ @field_validator ("keyword" , mode = "before" )
63
+ def normalize_keyword (cls , value : str ) -> str :
64
+ return value .lower ().strip ()
65
+
66
+ @property
67
+ def given_when_then (self ) -> str :
68
+ return self ._gwt
69
+
70
+ @given_when_then .setter
71
+ def given_when_then (self , gwt : str ) -> None :
72
+ self ._gwt = gwt
73
+
54
74
55
75
class Tag (BaseModel ):
56
76
id : str
@@ -68,6 +88,12 @@ class Scenario(BaseModel):
68
88
tags : List [Tag ]
69
89
examples : Optional [List [DataTable ]] = None
70
90
91
+ @model_validator (mode = "after" )
92
+ def process_steps (cls , instance ):
93
+ steps = instance .steps
94
+ instance .steps = _compute_given_when_then (steps )
95
+ return instance
96
+
71
97
72
98
class Rule (BaseModel ):
73
99
id : str
@@ -87,6 +113,12 @@ class Background(BaseModel):
87
113
description : str
88
114
steps : List [Step ]
89
115
116
+ @model_validator (mode = "after" )
117
+ def process_steps (cls , instance ):
118
+ steps = instance .steps
119
+ instance .steps = _compute_given_when_then (steps )
120
+ return instance
121
+
90
122
91
123
class Child (BaseModel ):
92
124
background : Optional [Background ] = None
@@ -108,6 +140,15 @@ class GherkinDocument(BaseModel):
108
140
comments : List [Comment ]
109
141
110
142
143
+ def _compute_given_when_then (steps : list [Step ]) -> list [Step ]:
144
+ last_gwt = None
145
+ for step in steps :
146
+ if step .keyword in STEP_TYPES :
147
+ last_gwt = step .keyword
148
+ step .given_when_then = last_gwt
149
+ return steps
150
+
151
+
111
152
class GherkinParser :
112
153
def __init__ (self , abs_filename : str = None , encoding : str = "utf-8" ):
113
154
self .abs_filename = Path (abs_filename ) if abs_filename else None
@@ -118,8 +159,6 @@ def __init__(self, abs_filename: str = None, encoding: str = "utf-8"):
118
159
try :
119
160
self .gherkin_data = Parser ().parse (TokenScanner (self .feature_file_text ))
120
161
except CompositeParserException as e :
121
- from src .pytest_bdd import exceptions
122
-
123
162
raise exceptions .FeatureError (
124
163
e .args [0 ],
125
164
e .errors [0 ].location ["line" ],
0 commit comments