Commit 89e345a4 by Gabriel Falcao

starting to implement background

parent 95574fd4
...@@ -88,6 +88,7 @@ class Language(object): ...@@ -88,6 +88,7 @@ class Language(object):
examples = 'Examples|Scenarios' examples = 'Examples|Scenarios'
scenario_outline = 'Scenario Outline' scenario_outline = 'Scenario Outline'
scenario_separator = 'Scenario( Outline)?' scenario_separator = 'Scenario( Outline)?'
background = "Background"
def __init__(self, code=u'en'): def __init__(self, code=u'en'):
self.code = code self.code = code
...@@ -832,6 +833,31 @@ class Scenario(object): ...@@ -832,6 +833,31 @@ class Scenario(object):
return scenario return scenario
class Background(object):
def __init__(self, lines, feature,
with_file=None,
original_string=None,
language=None):
self.steps = Step.many_from_lines(lines, with_file, original_string)
self.feature = feature
self.original_string = original_string
self.language = language
@classmethod
def from_string(new_background,
lines,
feature,
with_file=None,
original_string=None,
language=None):
return new_background(
lines,
feature,
with_file=with_file,
original_string=original_string,
language=language)
class Feature(object): class Feature(object):
""" Object that represents a feature.""" """ Object that represents a feature."""
described_at = None described_at = None
...@@ -845,7 +871,9 @@ class Feature(object): ...@@ -845,7 +871,9 @@ class Feature(object):
self.name = name self.name = name
self.language = language self.language = language
self.scenarios, self.description = self._parse_remaining_lines( (self.background,
self.scenarios,
self.description) = self._parse_remaining_lines(
remaining_lines, remaining_lines,
original_string, original_string,
with_file) with_file)
...@@ -863,10 +891,13 @@ class Feature(object): ...@@ -863,10 +891,13 @@ class Feature(object):
@property @property
def max_length(self): def max_length(self):
max_length = strings.column_width(u"%s: %s" % (self.language.first_of_feature, self.name)) max_length = strings.column_width(u"%s: %s" % (
self.language.first_of_feature, self.name))
if max_length == 0: if max_length == 0:
# in case feature has two keywords # in case feature has two keywords
max_length = strings.column_width(u"%s: %s" % (self.language.last_of_feature, self.name)) max_length = strings.column_width(u"%s: %s" % (
self.language.last_of_feature, self.name))
for line in self.description.splitlines(): for line in self.description.splitlines():
length = strings.column_width(line.strip()) + Scenario.indentation length = strings.column_width(line.strip()) + Scenario.indentation
...@@ -894,9 +925,12 @@ class Feature(object): ...@@ -894,9 +925,12 @@ class Feature(object):
filename = self.described_at.file filename = self.described_at.file
line = self.described_at.line line = self.described_at.line
head = strings.rfill(self.get_head(), length, append=u"# %s:%d\n" % (filename, line)) head = strings.rfill(self.get_head(), length,
for description, line in zip(self.description.splitlines(), self.described_at.description_at): append=u"# %s:%d\n" % (filename, line))
head += strings.rfill(u" %s" % description, length, append=u"# %s:%d\n" % (filename, line)) for description, line in zip(self.description.splitlines(),
self.described_at.description_at):
head += strings.rfill(
u" %s" % description, length, append=u"# %s:%d\n" % (filename, line))
return head return head
...@@ -949,9 +983,19 @@ class Feature(object): ...@@ -949,9 +983,19 @@ class Feature(object):
stripped = REP.tag_strip_regex.sub('', string) stripped = REP.tag_strip_regex.sub('', string)
return stripped return stripped
def _extract_desc_and_bg(self, joined):
if not re.search(self.language.background, joined):
return joined, None
parts = strings.split_wisely(joined, self.language.background)
description = parts.pop(0)
return description, parts
def _parse_remaining_lines(self, lines, original_string, with_file=None): def _parse_remaining_lines(self, lines, original_string, with_file=None):
# replacing occurrences of Scenario Outline, with just "Scenario"
joined = u"\n".join(lines[1:]) joined = u"\n".join(lines[1:])
# replacing occurrences of Scenario Outline, with just "Scenario"
scenario_prefix = u'%s:' % self.language.first_of_scenario scenario_prefix = u'%s:' % self.language.first_of_scenario
regex = re.compile( regex = re.compile(
u"%s:\s" % self.language.scenario_separator, re.U | re.I | re.DOTALL) u"%s:\s" % self.language.scenario_separator, re.U | re.I | re.DOTALL)
...@@ -961,9 +1005,17 @@ class Feature(object): ...@@ -961,9 +1005,17 @@ class Feature(object):
parts = strings.split_wisely(joined, scenario_prefix) parts = strings.split_wisely(joined, scenario_prefix)
description = u"" description = u""
background = None
if not re.search("^" + scenario_prefix, joined): if not re.search("^" + scenario_prefix, joined):
description = parts[0] description, background_lines = self._extract_desc_and_bg(parts[0])
background = background_lines and Background.from_string(
background_lines,
self,
with_file=with_file,
original_string=original_string,
language=self.language,
) or None
parts.pop(0) parts.pop(0)
prefix = self.language.first_of_scenario prefix = self.language.first_of_scenario
...@@ -994,7 +1046,7 @@ class Feature(object): ...@@ -994,7 +1046,7 @@ class Feature(object):
current_scenario = Scenario.from_string(current, **params) current_scenario = Scenario.from_string(current, **params)
scenarios.append(current_scenario) scenarios.append(current_scenario)
return scenarios, description return background, scenarios, description
def run(self, scenarios=None, ignore_case=True, tags=None, random=False): def run(self, scenarios=None, ignore_case=True, tags=None, random=False):
call_hook('before_each', 'feature', self) call_hook('before_each', 'feature', self)
......
...@@ -24,6 +24,7 @@ LANGUAGES = { ...@@ -24,6 +24,7 @@ LANGUAGES = {
'scenario': u'Scenario', 'scenario': u'Scenario',
'scenario_outline': u'Scenario Outline', 'scenario_outline': u'Scenario Outline',
'scenario_separator': u'(Scenario Outline|Scenario)', 'scenario_separator': u'(Scenario Outline|Scenario)',
'background': u'(?:Background)',
}, },
'pt-br': { 'pt-br': {
'examples': u'Exemplos|Cenários', 'examples': u'Exemplos|Cenários',
...@@ -33,6 +34,7 @@ LANGUAGES = { ...@@ -33,6 +34,7 @@ LANGUAGES = {
'scenario': u'Cenário|Cenario', 'scenario': u'Cenário|Cenario',
'scenario_outline': u'Esquema do Cenário|Esquema do Cenario', 'scenario_outline': u'Esquema do Cenário|Esquema do Cenario',
'scenario_separator': u'(Esquema do Cenário|Esquema do Cenario|Cenario|Cenário)', 'scenario_separator': u'(Esquema do Cenário|Esquema do Cenario|Cenario|Cenário)',
'background': u'(?:Contexto|Considerações)',
}, },
'pl': { 'pl': {
'examples': u'Przykład', 'examples': u'Przykład',
...@@ -42,6 +44,7 @@ LANGUAGES = { ...@@ -42,6 +44,7 @@ LANGUAGES = {
'scenario': u'Scenariusz', 'scenario': u'Scenariusz',
'scenario_outline': u'Zarys Scenariusza', 'scenario_outline': u'Zarys Scenariusza',
'scenario_separator': u'(Zarys Scenariusza|Scenariusz)', 'scenario_separator': u'(Zarys Scenariusza|Scenariusz)',
'background': u'(?:Background)',
}, },
'ca': { 'ca': {
'examples': u'Exemples', 'examples': u'Exemples',
...@@ -51,6 +54,7 @@ LANGUAGES = { ...@@ -51,6 +54,7 @@ LANGUAGES = {
'scenario': u'Escenari', 'scenario': u'Escenari',
'scenario_outline': u"Esquema d'Escenari", 'scenario_outline': u"Esquema d'Escenari",
'scenario_separator': u"(Esquema d'Escenari|Escenari)", 'scenario_separator': u"(Esquema d'Escenari|Escenari)",
'background': u'(?:Background)',
}, },
'es': { 'es': {
'examples': u'Ejemplos', 'examples': u'Ejemplos',
...@@ -60,6 +64,7 @@ LANGUAGES = { ...@@ -60,6 +64,7 @@ LANGUAGES = {
'scenario': u'Escenario', 'scenario': u'Escenario',
'scenario_outline': u'Esquema de Escenario', 'scenario_outline': u'Esquema de Escenario',
'scenario_separator': u'(Esquema de Escenario|Escenario)', 'scenario_separator': u'(Esquema de Escenario|Escenario)',
'background': u'(?:Contexto|Consideraciones)',
}, },
'hu': { 'hu': {
'examples': u'Példák', 'examples': u'Példák',
...@@ -69,6 +74,7 @@ LANGUAGES = { ...@@ -69,6 +74,7 @@ LANGUAGES = {
'scenario': u'Forgatókönyv', 'scenario': u'Forgatókönyv',
'scenario_outline': u'Forgatókönyv vázlat', 'scenario_outline': u'Forgatókönyv vázlat',
'scenario_separator': u'(Forgatókönyv|Forgatókönyv vázlat)', 'scenario_separator': u'(Forgatókönyv|Forgatókönyv vázlat)',
'background': u'(?:Background)',
}, },
'fr': { 'fr': {
'examples': u'Exemples|Scénarios', 'examples': u'Exemples|Scénarios',
...@@ -78,6 +84,7 @@ LANGUAGES = { ...@@ -78,6 +84,7 @@ LANGUAGES = {
'scenario': u'Scénario', 'scenario': u'Scénario',
'scenario_outline': u'Plan de Scénario|Plan du Scénario', 'scenario_outline': u'Plan de Scénario|Plan du Scénario',
'scenario_separator': u'(Plan de Scénario|Plan du Scénario|Scénario)', 'scenario_separator': u'(Plan de Scénario|Plan du Scénario|Scénario)',
'background': u'(?:Background)',
}, },
'de': { 'de': {
'examples': u'Beispiele|Szenarios', 'examples': u'Beispiele|Szenarios',
...@@ -87,6 +94,7 @@ LANGUAGES = { ...@@ -87,6 +94,7 @@ LANGUAGES = {
'scenario': u'Szenario', 'scenario': u'Szenario',
'scenario_outline': u'Szenario-Zusammenfassung|Zusammenfassung', 'scenario_outline': u'Szenario-Zusammenfassung|Zusammenfassung',
'scenario_separator': u'(Szenario-Zusammenfassung|Zusammenfassung)', 'scenario_separator': u'(Szenario-Zusammenfassung|Zusammenfassung)',
'background': u'(?:Background)',
}, },
'ja': { 'ja': {
'examples': u'例', 'examples': u'例',
...@@ -96,6 +104,7 @@ LANGUAGES = { ...@@ -96,6 +104,7 @@ LANGUAGES = {
'scenario': u'シナリオ', 'scenario': u'シナリオ',
'scenario_outline': u'シナリオアウトライン|シナリオテンプレート|テンプレ|シナリオテンプレ', 'scenario_outline': u'シナリオアウトライン|シナリオテンプレート|テンプレ|シナリオテンプレ',
'scenario_separator': u'(シナリオ|シナリオアウトライン|シナリオテンプレート|テンプレ|シナリオテンプレ)', 'scenario_separator': u'(シナリオ|シナリオアウトライン|シナリオテンプレート|テンプレ|シナリオテンプレ)',
'background': u'(?:Background)',
}, },
'tr': { 'tr': {
'examples': u'Örnekler', 'examples': u'Örnekler',
...@@ -105,6 +114,7 @@ LANGUAGES = { ...@@ -105,6 +114,7 @@ LANGUAGES = {
'scenario': u'Senaryo', 'scenario': u'Senaryo',
'scenario_outline': u'Senaryo taslağı|Senaryo Taslağı', 'scenario_outline': u'Senaryo taslağı|Senaryo Taslağı',
'scenario_separator': u'(Senaryo taslağı|Senaryo Taslağı|Senaryo)', 'scenario_separator': u'(Senaryo taslağı|Senaryo Taslağı|Senaryo)',
'background': u'(?:Background)',
}, },
'zh-CN': { 'zh-CN': {
'examples': u'例如|场景集', 'examples': u'例如|场景集',
...@@ -114,6 +124,7 @@ LANGUAGES = { ...@@ -114,6 +124,7 @@ LANGUAGES = {
'scenario': u'场景', 'scenario': u'场景',
'scenario_outline': u'场景模板', 'scenario_outline': u'场景模板',
'scenario_separator': u'(场景模板|场景)', 'scenario_separator': u'(场景模板|场景)',
'background': u'(?:背景)',
}, },
'zh-TW': { 'zh-TW': {
'examples': u'例如|場景集', 'examples': u'例如|場景集',
...@@ -123,6 +134,7 @@ LANGUAGES = { ...@@ -123,6 +134,7 @@ LANGUAGES = {
'scenario': u'場景', 'scenario': u'場景',
'scenario_outline': u'場景模板', 'scenario_outline': u'場景模板',
'scenario_separator': u'(場景模板|場景)', 'scenario_separator': u'(場景模板|場景)',
'background': u'(?:背景)',
}, },
'ru': { 'ru': {
'examples': u'Примеры|Сценарии', 'examples': u'Примеры|Сценарии',
...@@ -132,6 +144,7 @@ LANGUAGES = { ...@@ -132,6 +144,7 @@ LANGUAGES = {
'scenario': u'Сценарий', 'scenario': u'Сценарий',
'scenario_outline': u'Структура сценария', 'scenario_outline': u'Структура сценария',
'scenario_separator': u'(Структура сценария|Сценарий)', 'scenario_separator': u'(Структура сценария|Сценарий)',
'background': u'(?:Background)',
}, },
'uk': { 'uk': {
'examples': u'Приклади|Сценарії', 'examples': u'Приклади|Сценарії',
...@@ -141,6 +154,7 @@ LANGUAGES = { ...@@ -141,6 +154,7 @@ LANGUAGES = {
'scenario': u'Сценарій', 'scenario': u'Сценарій',
'scenario_outline': u'Структура сценарію', 'scenario_outline': u'Структура сценарію',
'scenario_separator': u'(Структура сценарію|Сценарій)', 'scenario_separator': u'(Структура сценарію|Сценарій)',
'background': u'(?:Background)',
}, },
'it': { 'it': {
'examples': u'Esempi|Scenari|Scenarii', 'examples': u'Esempi|Scenari|Scenarii',
...@@ -150,6 +164,7 @@ LANGUAGES = { ...@@ -150,6 +164,7 @@ LANGUAGES = {
'scenario': u'Scenario', 'scenario': u'Scenario',
'scenario_outline': u'Schema di Scenario|Piano di Scenario', 'scenario_outline': u'Schema di Scenario|Piano di Scenario',
'scenario_separator': u'(Schema di Scenario|Piano di Scenario|Scenario)', 'scenario_separator': u'(Schema di Scenario|Piano di Scenario|Scenario)',
'background': u'(?:Background)',
}, },
'no': { 'no': {
'examples': u'Eksempler', 'examples': u'Eksempler',
...@@ -159,5 +174,6 @@ LANGUAGES = { ...@@ -159,5 +174,6 @@ LANGUAGES = {
'scenario': u'Situasjon', 'scenario': u'Situasjon',
'scenario_outline': u'Situasjon Oversikt', 'scenario_outline': u'Situasjon Oversikt',
'scenario_separator': u'(Situasjon Oversikt|Situasjon)', 'scenario_separator': u'(Situasjon Oversikt|Situasjon)',
'background': u'(?:Background)',
} }
} }
...@@ -14,10 +14,11 @@ ...@@ -14,10 +14,11 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from sure import that from sure import that, expect
from lettuce import step from lettuce import step
from lettuce.core import Scenario from lettuce.core import Scenario
from lettuce.core import Feature from lettuce.core import Feature
from lettuce.core import Background
from nose.tools import assert_equals from nose.tools import assert_equals
FEATURE1 = """ FEATURE1 = """
...@@ -330,6 +331,31 @@ Feature: Redis database server ...@@ -330,6 +331,31 @@ Feature: Redis database server
And M2 contains database 3 And M2 contains database 3
""" """
FEATURE16 = """
Feature: Movie rental
As a rental store owner
I want to keep track of my clients
So that I can manage my business better
Background:
Given I have the following movies in my database:
| Name | Rating | New | Available |
| Matrix Revolutions | 4 stars | no | 6 |
| Iron Man 2 | 5 stars | yes | 11 |
And the following clients:
| Name |
| John Doe |
| Foo Bar |
Scenario: Renting a featured movie
Given the client 'John Doe' rents 'Iron Man 2'
Then there are 10 more left
Scenario: Renting an old movie
Given the client 'Foo Bar' rents 'Matrix Revolutions'
Then there are 5 more left
"""
def test_feature_has_repr(): def test_feature_has_repr():
"Feature implements __repr__ nicely" "Feature implements __repr__ nicely"
...@@ -569,6 +595,7 @@ def test_scenarios_with_extra_whitespace(): ...@@ -569,6 +595,7 @@ def test_scenarios_with_extra_whitespace():
def test_scenarios_parsing(): def test_scenarios_parsing():
"Tags are parsed correctly"
feature = Feature.from_string(FEATURE15) feature = Feature.from_string(FEATURE15)
scenarios_and_tags = [(s.name, s.tags) for s in feature.scenarios] scenarios_and_tags = [(s.name, s.tags) for s in feature.scenarios]
...@@ -591,3 +618,14 @@ def test_scenarios_parsing(): ...@@ -591,3 +618,14 @@ def test_scenarios_parsing():
('Slave -> Master promotion', []), ('Slave -> Master promotion', []),
('Restart farm', [u'restart_farm']), ('Restart farm', [u'restart_farm']),
]) ])
def test_background_parsing():
feature = Feature.from_string(FEATURE16)
expect(feature.description).to.equal(
"As a rental store owner\n"
"I want to keep track of my clients\n"
"So that I can manage my business better"
)
expect(feature).to.have.property('background').being.a(Background)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment