From 358b36b788cbec71d62203413e5e855431a7f1d2 Mon Sep 17 00:00:00 2001 From: Kostiantyn Goloveshko Date: Tue, 18 Jul 2023 20:59:15 +0300 Subject: [PATCH] Add messages implementation for python --- .github/workflows/test-python.yml | 59 ++ CHANGELOG.md | 1 + python/.gitignore | 55 ++ python/.pre-commit-config.yaml | 48 ++ python/README.md | 3 + python/RELEASING.md | 15 + python/pyproject.toml | 72 ++ python/pytest.ini | 2 + python/setup.cfg | 11 + python/src/messages/__init__.py | 127 +++ python/src/messages/messages.py | 797 ++++++++++++++++++ .../samples/attachments/attachments.feature | 44 + .../attachments/attachments.feature.ndjson | 87 ++ .../attachments/attachments.feature.ts | 58 ++ .../messages/samples/attachments/cucumber.png | Bin 0 -> 1739 bytes .../messages/samples/attachments/document.pdf | Bin 0 -> 10061 bytes .../src/messages/samples/cdata/cdata.feature | 6 + .../samples/cdata/cdata.feature.ndjson | 12 + .../messages/samples/cdata/cdata.feature.ts | 6 + .../samples/data-tables/data-tables.feature | 12 + .../data-tables/data-tables.feature.ndjson | 15 + .../data-tables/data-tables.feature.ts | 11 + .../examples-tables/examples-tables.feature | 28 + .../examples-tables.feature.ndjson | 68 ++ .../examples-tables.feature.ts | 14 + .../src/messages/samples/hooks/cucumber.svg | 7 + .../src/messages/samples/hooks/hooks.feature | 20 + .../samples/hooks/hooks.feature.ndjson | 77 ++ .../messages/samples/hooks/hooks.feature.ts | 30 + .../samples/markdown/markdown.feature.md | 46 + .../markdown/markdown.feature.md.ndjson | 35 + .../samples/markdown/markdown.feature.md.ts | 25 + .../messages/samples/minimal/minimal.feature | 10 + .../samples/minimal/minimal.feature.ndjson | 12 + .../samples/minimal/minimal.feature.ts | 6 + .../parameter-types/parameter-types.feature | 9 + .../parameter-types.feature.ndjson | 13 + .../parameter-types.feature.ts | 20 + .../messages/samples/pending/pending.feature | 19 + .../samples/pending/pending.feature.ndjson | 30 + .../samples/pending/pending.feature.ts | 13 + .../samples/retry/retry.arguments.txt | 1 + .../src/messages/samples/retry/retry.feature | 22 + .../samples/retry/retry.feature.ndjson | 59 ++ .../messages/samples/retry/retry.feature.ts | 25 + .../src/messages/samples/rules/rules.feature | 27 + .../samples/rules/rules.feature.ndjson | 45 + .../messages/samples/rules/rules.feature.ts | 33 + .../messages/samples/skipped/skipped.feature | 19 + .../samples/skipped/skipped.feature.ndjson | 33 + .../samples/skipped/skipped.feature.ts | 17 + .../samples/stack-traces/stack-traces.feature | 11 + .../stack-traces/stack-traces.feature.ndjson | 12 + .../stack-traces/stack-traces.feature.ts | 5 + .../samples/undefined/undefined.feature | 17 + .../undefined/undefined.feature.ndjson | 29 + .../samples/undefined/undefined.feature.ts | 9 + .../unknown-parameter-type.feature | 6 + .../unknown-parameter-type.feature.ndjson | 12 + .../unknown-parameter-type.feature.ts | 6 + python/tests/__init__.py | 0 python/tests/conftest.py | 0 python/tests/test_model_load.py | 28 + python/tox.ini | 44 + 64 files changed, 2383 insertions(+) create mode 100644 .github/workflows/test-python.yml create mode 100644 python/.gitignore create mode 100644 python/.pre-commit-config.yaml create mode 100644 python/README.md create mode 100644 python/RELEASING.md create mode 100644 python/pyproject.toml create mode 100644 python/pytest.ini create mode 100644 python/setup.cfg create mode 100644 python/src/messages/__init__.py create mode 100644 python/src/messages/messages.py create mode 100644 python/src/messages/samples/attachments/attachments.feature create mode 100644 python/src/messages/samples/attachments/attachments.feature.ndjson create mode 100644 python/src/messages/samples/attachments/attachments.feature.ts create mode 100644 python/src/messages/samples/attachments/cucumber.png create mode 100644 python/src/messages/samples/attachments/document.pdf create mode 100644 python/src/messages/samples/cdata/cdata.feature create mode 100644 python/src/messages/samples/cdata/cdata.feature.ndjson create mode 100644 python/src/messages/samples/cdata/cdata.feature.ts create mode 100644 python/src/messages/samples/data-tables/data-tables.feature create mode 100644 python/src/messages/samples/data-tables/data-tables.feature.ndjson create mode 100644 python/src/messages/samples/data-tables/data-tables.feature.ts create mode 100644 python/src/messages/samples/examples-tables/examples-tables.feature create mode 100644 python/src/messages/samples/examples-tables/examples-tables.feature.ndjson create mode 100644 python/src/messages/samples/examples-tables/examples-tables.feature.ts create mode 100644 python/src/messages/samples/hooks/cucumber.svg create mode 100644 python/src/messages/samples/hooks/hooks.feature create mode 100644 python/src/messages/samples/hooks/hooks.feature.ndjson create mode 100644 python/src/messages/samples/hooks/hooks.feature.ts create mode 100644 python/src/messages/samples/markdown/markdown.feature.md create mode 100644 python/src/messages/samples/markdown/markdown.feature.md.ndjson create mode 100644 python/src/messages/samples/markdown/markdown.feature.md.ts create mode 100644 python/src/messages/samples/minimal/minimal.feature create mode 100644 python/src/messages/samples/minimal/minimal.feature.ndjson create mode 100644 python/src/messages/samples/minimal/minimal.feature.ts create mode 100644 python/src/messages/samples/parameter-types/parameter-types.feature create mode 100644 python/src/messages/samples/parameter-types/parameter-types.feature.ndjson create mode 100644 python/src/messages/samples/parameter-types/parameter-types.feature.ts create mode 100644 python/src/messages/samples/pending/pending.feature create mode 100644 python/src/messages/samples/pending/pending.feature.ndjson create mode 100644 python/src/messages/samples/pending/pending.feature.ts create mode 100644 python/src/messages/samples/retry/retry.arguments.txt create mode 100644 python/src/messages/samples/retry/retry.feature create mode 100644 python/src/messages/samples/retry/retry.feature.ndjson create mode 100644 python/src/messages/samples/retry/retry.feature.ts create mode 100644 python/src/messages/samples/rules/rules.feature create mode 100644 python/src/messages/samples/rules/rules.feature.ndjson create mode 100644 python/src/messages/samples/rules/rules.feature.ts create mode 100644 python/src/messages/samples/skipped/skipped.feature create mode 100644 python/src/messages/samples/skipped/skipped.feature.ndjson create mode 100644 python/src/messages/samples/skipped/skipped.feature.ts create mode 100644 python/src/messages/samples/stack-traces/stack-traces.feature create mode 100644 python/src/messages/samples/stack-traces/stack-traces.feature.ndjson create mode 100644 python/src/messages/samples/stack-traces/stack-traces.feature.ts create mode 100644 python/src/messages/samples/undefined/undefined.feature create mode 100644 python/src/messages/samples/undefined/undefined.feature.ndjson create mode 100644 python/src/messages/samples/undefined/undefined.feature.ts create mode 100644 python/src/messages/samples/unknown-parameter-type/unknown-parameter-type.feature create mode 100644 python/src/messages/samples/unknown-parameter-type/unknown-parameter-type.feature.ndjson create mode 100644 python/src/messages/samples/unknown-parameter-type/unknown-parameter-type.feature.ts create mode 100644 python/tests/__init__.py create mode 100644 python/tests/conftest.py create mode 100644 python/tests/test_model_load.py create mode 100644 python/tox.ini diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml new file mode 100644 index 00000000..7f906255 --- /dev/null +++ b/.github/workflows/test-python.yml @@ -0,0 +1,59 @@ +--- +name: test-python + +on: # yamllint disable-line rule:truthy + push: + branches: + - main + - renovate/** + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + build: + + runs-on: ${{ matrix.os }} + timeout-minutes: 20 + strategy: + matrix: + python-version: + - "3.8" + - "3.9" + - "3.10" + - "3.11" + - "pypy3.8" + - "pypy3.9" + - "pypy3.10" + os: + - ubuntu-latest + - windows-latest + - macos-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U setuptools + pip install tox tox-gh-actions codecov + - name: Test with tox + working-directory: ./python + run: | + tox + codecov + - name: Build checking + working-directory: ./python + if: "matrix.python-version == '3.11'" + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + run: | + python -m pip install --upgrade build twine + python -m build + twine check dist/* diff --git a/CHANGELOG.md b/CHANGELOG.md index e22dbcdf..dc35181f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Added Python implementation ([#165](https://github.com/cucumber/messages/pull/165)) ## [24.0.1] - 2023-12-21 ### Fixed diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 00000000..563dcc08 --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,55 @@ +*.rej +*.py[cod] +/.env +*.orig +**/__pycache__ + +# C extensions +*.so + +# Packages +*.egg +*.egg-info +dist +build +_build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg +lib +lib64 + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox +nosetests.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject +.pytest_cache +.ropeproject + +# Sublime +/*.sublime-* + +#PyCharm +/.idea + +# virtualenv +/.Python +/lib +/include +/share +/local diff --git a/python/.pre-commit-config.yaml b/python/.pre-commit-config.yaml new file mode 100644 index 00000000..334019ba --- /dev/null +++ b/python/.pre-commit-config.yaml @@ -0,0 +1,48 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +--- +repos: + - repo: https://github.com/psf/black + rev: 23.3.0 + hooks: + - id: black + args: + - "python/src" + - "python/tests" + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + name: isort (python) + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - id: check-toml + - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks + rev: v2.9.0 + hooks: + - id: pretty-format-toml + args: [--autofix] + - repo: https://github.com/asottile/pyupgrade + rev: v3.7.0 + hooks: + - id: pyupgrade + args: ["--py38-plus"] + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.4.1 + hooks: + - id: mypy + additional_dependencies: [types-setuptools, types-certifi] + - repo: https://github.com/tox-dev/tox-ini-fmt + rev: "1.3.1" + hooks: + - id: tox-ini-fmt + - repo: https://github.com/adrienverge/yamllint.git + rev: v1.32.0 + hooks: + - id: yamllint + args: [--format, parsable, --strict] diff --git a/python/README.md b/python/README.md new file mode 100644 index 00000000..0a7e6d51 --- /dev/null +++ b/python/README.md @@ -0,0 +1,3 @@ +# Messages + +Cucumber Messages for Python https://github.com/cucumber/messages diff --git a/python/RELEASING.md b/python/RELEASING.md new file mode 100644 index 00000000..e0aefe25 --- /dev/null +++ b/python/RELEASING.md @@ -0,0 +1,15 @@ +# How to make a release + +```shell +python -m pip install --upgrade build twine + +# cleanup the ./dist folder +rm -rf ./dist + +# Build the distributions +python -m build + +# Upload them + +twine upload dist/* +``` diff --git a/python/pyproject.toml b/python/pyproject.toml new file mode 100644 index 00000000..0ab23d02 --- /dev/null +++ b/python/pyproject.toml @@ -0,0 +1,72 @@ +[build-system] +build-backend = "setuptools.build_meta" +requires = ["setuptools", "wheel"] + +[project] +authors = [ + {name = "Cucumber Limited", email = "cukes@googlegroups.com"} +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX", + "Operating System :: Microsoft :: Windows", + "Operating System :: MacOS :: MacOS X", + "Topic :: Software Development :: Testing", + "Topic :: Utilities", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11" +] +dependencies = [ + "importlib_resources", + "pydantic>=2.0.3" +] +description = "Cucumber Messages is a message protocol for representing results and other information from Cucumber. " +license = {text = "MIT"} +maintainers = [ + {name = "Konstantin Goloveshko", email = "kostya.goloveshko@gmail.com"} +] +name = "cuke-messages" +readme = {file = "README.md", content-type = "text/markdown"} +requires-python = ">=3.8" +urls = {Repository = "https://github.com/cucumber/messages"} +version = "0.1.0" + +[project.entry-points] + +[project.scripts] + +[tool.black] +line-length = 120 +target-version = ["py38", "py39", "py310", "py311"] +verbose = true + +[tool.isort] +line_length = 120 +multi_line_output = 3 +profile = "black" + +[tool.mypy] +files = "src/messages/**/*.py" +install_types = true +non_interactive = true +plugins = [ + "pydantic.mypy" +] +show_error_codes = true +warn_return_any = true +warn_unused_configs = true + +[[tool.mypy.overrides]] +ignore_missing_imports = true +module = [ +] + +[project.optional-dependencies] +test = [ + "tox" +] diff --git a/python/pytest.ini b/python/pytest.ini new file mode 100644 index 00000000..5ee64771 --- /dev/null +++ b/python/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +testpaths = tests diff --git a/python/setup.cfg b/python/setup.cfg new file mode 100644 index 00000000..3ef48d29 --- /dev/null +++ b/python/setup.cfg @@ -0,0 +1,11 @@ +[options] +include_package_data = True +packages = find: +package_dir = + =src + +[options.packages.find] +where = src +[options.package_data] +messages.samples = + * diff --git a/python/src/messages/__init__.py b/python/src/messages/__init__.py new file mode 100644 index 00000000..8f8fe0f9 --- /dev/null +++ b/python/src/messages/__init__.py @@ -0,0 +1,127 @@ +from .messages import ( + Attachment, + Background, + Ci, + Comment, + ContentEncoding, + DataTable, + DocString, + Duration, + Envelope, + Examples, + ExceptionMessage, + ExpressionType, + Feature, + FeatureChild, + GherkinDocument, + Git, + Group, + Hook, + JavaMethod, + JavaStackTraceElement, + KeywordType, + Location, + MediaType, + Meta, + ParameterType, + ParseError, + Pickle, + PickleDocString, + PickleStep, + PickleStepArgument, + PickleTable, + PickleTableCell, + PickleTableRow, + PickleTag, + Product, + Rule, + RuleChild, + Scenario, + Source, + SourceReference, + Status, + Step, + StepDefinition, + StepDefinitionPattern, + StepMatchArgument, + StepMatchArgumentsList, + TableCell, + TableRow, + Tag, + TestCase, + TestCaseFinished, + TestCaseStarted, + TestRunFinished, + TestRunStarted, + TestStep, + TestStepFinished, + TestStepResult, + TestStepStarted, + Timestamp, + Type, + UndefinedParameterType, +) + +__all__ = [ + "ContentEncoding", + "MediaType", + "Source", + "Location", + "Comment", + "DocString", + "KeywordType", + "TableCell", + "TableRow", + "Tag", + "JavaMethod", + "JavaStackTraceElement", + "Git", + "Product", + "PickleDocString", + "Type", + "PickleTableCell", + "PickleTableRow", + "PickleTag", + "ExpressionType", + "StepDefinitionPattern", + "Group", + "StepMatchArgument", + "StepMatchArgumentsList", + "TestStep", + "Timestamp", + "TestCaseStarted", + "ExceptionMessage", + "TestRunStarted", + "Status", + "Duration", + "TestStepStarted", + "UndefinedParameterType", + "Attachment", + "DataTable", + "Examples", + "Step", + "SourceReference", + "Ci", + "ParameterType", + "ParseError", + "PickleTable", + "StepDefinition", + "TestCase", + "TestCaseFinished", + "TestRunFinished", + "TestStepResult", + "Background", + "Scenario", + "Hook", + "Meta", + "PickleStepArgument", + "TestStepFinished", + "RuleChild", + "PickleStep", + "Rule", + "Pickle", + "FeatureChild", + "Feature", + "GherkinDocument", + "Envelope", +] diff --git a/python/src/messages/messages.py b/python/src/messages/messages.py new file mode 100644 index 00000000..d5b933bd --- /dev/null +++ b/python/src/messages/messages.py @@ -0,0 +1,797 @@ +from enum import Enum +from typing import List, Optional, Union + +from pydantic import BaseModel, ConfigDict, Field + +# mypy: disable-error-code="typeddict-unknown-key, typeddict-item" + + +class ContentEncoding(Enum): + identity = "IDENTITY" + base64 = "BASE64" + + +class MediaType(Enum): + text_x_cucumber_gherkin_plain = "text/x.cucumber.gherkin+plain" + text_x_cucumber_gherkin_markdown = "text/x.cucumber.gherkin+markdown" + + +class Source(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + use_enum_values=True, + ) + uri: str = Field( + ..., + description="*\n The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier)\n" + " of the source, typically a file path relative to the root directory", + ) + data: str = Field(..., description="The contents of the file") + media_type: Union["MediaType", str] = Field( + ..., + alias="mediaType", + description="The media type of the file. Can be used to specify custom types, such as\n" + " text/x.cucumber.gherkin+plain", + ) + + +class Location(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + line: int + column: Optional[int] = None + + +class Comment(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + location: Optional["Location"] = Field(..., description="The location of the comment") + text: str = Field(..., description="The text of the comment") + + +class DocString(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + location: Optional["Location"] + media_type: Optional[str] = Field(None, alias="mediaType") + content: str + delimiter: str + + +class KeywordType(Enum): + unknown = "Unknown" + context = "Context" + action = "Action" + outcome = "Outcome" + conjunction = "Conjunction" + + +class TableCell(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + location: Optional["Location"] = Field(..., description="The location of the cell") + value: str = Field(..., description="The value of the cell") + + +class TableRow(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + location: Optional["Location"] = Field(..., description="The location of the first cell in the row") + cells: List["TableCell"] = Field(..., description="Cells in the row") + id: str + + +class Tag(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + location: Optional["Location"] = Field(..., description="Location of the tag") + name: str = Field(..., description="The name of the tag (including the leading `@`)") + id: str = Field(..., description="Unique ID to be able to reference the Tag from PickleTag") + + +class JavaMethod(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + class_name: str = Field(..., alias="className") + method_name: str = Field(..., alias="methodName") + method_parameter_types: List[str] = Field(..., alias="methodParameterTypes") + + +class JavaStackTraceElement(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + class_name: str = Field(..., alias="className") + file_name: str = Field(..., alias="fileName") + method_name: str = Field(..., alias="methodName") + + +class Git(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + remote: str + revision: str + branch: Optional[str] = None + tag: Optional[str] = None + + +class Product(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + name: str = Field(..., description="The product name") + version: Optional[str] = Field(None, description="The product version") + + +class PickleDocString(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + media_type: Optional[str] = Field(None, alias="mediaType") + content: str + + +class Type(Enum): + unknown = "Unknown" + context = "Context" + action = "Action" + outcome = "Outcome" + + +class PickleTableCell(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + value: str + + +class PickleTableRow(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + cells: List["PickleTableCell"] = Field(..., min_length=1) + + +class PickleTag(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + name: str + ast_node_id: str = Field(..., alias="astNodeId", description="Points to the AST node this was created from") + + +class ExpressionType(Enum): + cucumber_expression = "CUCUMBER_EXPRESSION" + regular_expression = "REGULAR_EXPRESSION" + + +class StepDefinitionPattern(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + source: str + type: Union["ExpressionType", str] + + +class Group(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + children: List["Group"] + start: Optional[int] = None + value: Optional[str] = None + + +class StepMatchArgument(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + group: "Group" = Field( + ..., + description="*\n Represents the outermost capture group of an argument. This message closely matches the\n " + "`Group` class in the `cucumber-expressions` library.", + ) + parameter_type_name: Optional[str] = Field(None, alias="parameterTypeName") + + +class StepMatchArgumentsList(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + step_match_arguments: List["StepMatchArgument"] = Field(..., alias="stepMatchArguments") + + +class TestStep(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + hook_id: Optional[str] = Field(None, alias="hookId", description="Pointer to the `Hook` (if derived from a Hook)") + id: str + pickle_step_id: Optional[str] = Field( + None, alias="pickleStepId", description="Pointer to the `PickleStep` (if derived from a `PickleStep`)" + ) + step_definition_ids: Optional[List[str]] = Field( + None, + alias="stepDefinitionIds", + description="Pointer to all the matching `StepDefinition`s (if derived from a `PickleStep`)", + ) + step_match_arguments_lists: Optional[List["StepMatchArgumentsList"]] = Field( + None, + alias="stepMatchArgumentsLists", + description="A list of list of StepMatchArgument (if derived from a `PickleStep`).\n " + "Each element represents a matching step definition. A size of 0 means `UNDEFINED`,\n " + "and a size of 2+ means `AMBIGUOUS`", + ) + + +class Timestamp(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + seconds: int = Field( + ..., + description="Represents seconds of UTC time since Unix epoch\n " + "1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to\n 9999-12-31T23:59:59Z inclusive.", + ) + nanos: int = Field( + ..., + description="Non-negative fractions of a second at nanosecond resolution. Negative\n " + "second values with fractions must still have non-negative nanos values\n " + "that count forward in time. Must be from 0 to 999,999,999\n inclusive.", + ) + + +class TestCaseStarted(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + attempt: int = Field( + ..., + description="*\n The first attempt should have value 0, and for each retry the value\n should increase by 1.", + ) + id: str = Field( + ..., + description="*\n Because a `TestCase` can be run multiple times (in case of a retry),\n " + "we use this field to group messages relating to the same attempt.", + ) + test_case_id: str = Field(..., alias="testCaseId") + worker_id: Optional[str] = Field( + None, + alias="workerId", + description="An identifier for the worker process running this test case, " + "if test cases are being run in parallel. The identifier will be unique per worker, " + "but no particular format is defined - it could be an index, uuid, machine name etc - " + "and as such should be assumed that it's not human readable.", + ) + timestamp: "Timestamp" + + +class ExceptionMessage(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + type: str = Field( + ..., + description="The type of the exception that caused this result. " + 'E.g. "Error" or "org.opentest4j.AssertionFailedError"', + ) + message: Optional[str] = Field( + None, description='The message of exception that caused this result. E.g. expected: "a" but was: "b"' + ) + + +class TestRunStarted(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + timestamp: "Timestamp" + + +class Status(Enum): + unknown = "UNKNOWN" + passed = "PASSED" + skipped = "SKIPPED" + pending = "PENDING" + undefined = "UNDEFINED" + ambiguous = "AMBIGUOUS" + failed = "FAILED" + + +class Duration(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + seconds: int + nanos: int = Field( + ..., + description="Non-negative fractions of a second at nanosecond resolution. Negative\n " + "second values with fractions must still have non-negative nanos values\n " + "that count forward in time. Must be from 0 to 999,999,999\n inclusive.", + ) + + +class TestStepStarted(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + test_case_started_id: str = Field(..., alias="testCaseStartedId") + test_step_id: str = Field(..., alias="testStepId") + timestamp: "Timestamp" + + +class UndefinedParameterType(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + expression: str + name: str + + +class Attachment(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + use_enum_values=True, + ) + body: str = Field( + ..., + description="*\n The body of the attachment. If `contentEncoding` is `IDENTITY`, the attachment\n is simply " + "the string. If it's `BASE64`, the string should be Base64 decoded to\n obtain the attachment.", + ) + content_encoding: "ContentEncoding" = Field( + ..., + alias="contentEncoding", + description='*\n Whether to interpret `body` "as-is" (IDENTITY) or if it needs to be Base64-decoded (BASE64).' + "\n\n Content encoding is *not* determined by the media type, but rather by the type\n of the " + "object being attached:\n\n - string: IDENTITY\n - byte array: BASE64\n - stream: BASE64", + ) + file_name: Optional[str] = Field( + None, + alias="fileName", + description="*\n Suggested file name of the attachment. (Provided by the user as an argument to `attach`)", + ) + media_type: str = Field( + ..., + alias="mediaType", + description="*\n The media type of the data. This can be any valid\n " + "[IANA Media Type](https://www.iana.org/assignments/media-types/media-types.xhtml)\n" + " as well as Cucumber-specific media types such as `text/x.cucumber.gherkin+plain`\n " + "and `text/x.cucumber.stacktrace+plain`", + ) + source: Optional["Source"] = None + test_case_started_id: Optional[str] = Field(None, alias="testCaseStartedId") + test_step_id: Optional[str] = Field(None, alias="testStepId") + url: Optional[str] = Field( + None, + description="*\n A URL where the attachment can be retrieved. This field should not be set by Cucumber.\n " + "It should be set by a program that reads a message stream and does the following for\n " + "each Attachment message:\n\n " + "- Writes the body (after base64 decoding if necessary) to a new file.\n " + "- Sets `body` and `contentEncoding` to `null`\n " + "- Writes out the new attachment message\n\n " + "This will result in a smaller message stream, which can improve performance and\n " + "reduce bandwidth of message consumers. " + "It also makes it easier to process and download attachments\n separately from reports.", + ) + + +class DataTable(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + location: Optional["Location"] + rows: List["TableRow"] + + +class Examples(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + location: Optional["Location"] = Field(..., description="The location of the `Examples` keyword") + tags: List["Tag"] + keyword: str + name: str + description: str + table_header: Optional["TableRow"] = Field(None, alias="tableHeader") + table_body: List["TableRow"] = Field(..., alias="tableBody") + id: str + + +class Step(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + location: Optional["Location"] = Field(..., description="The location of the steps' `keyword`") + keyword: str = Field(..., description="The actual keyword as it appeared in the source.") + keyword_type: Optional["KeywordType"] = Field( + None, + alias="keywordType", + description="The test phase signalled by the keyword: Context definition (Given), Action performance (When), " + "Outcome assertion (Then). Other keywords signal Continuation (And and But) from a prior keyword. " + "Please note that all translations which a dialect maps to multiple keywords (`*` is in this " + "category for all dialects), map to 'Unknown'.", + ) + text: str + doc_string: Optional["DocString"] = Field(None, alias="docString") + data_table: Optional["DataTable"] = Field(None, alias="dataTable") + id: str = Field(..., description="Unique ID to be able to reference the Step from PickleStep") + + +class SourceReference(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + uri: Optional[str] = None + java_method: Optional["JavaMethod"] = Field(None, alias="javaMethod") + java_stack_trace_element: Optional["JavaStackTraceElement"] = Field(None, alias="javaStackTraceElement") + location: Optional["Location"] = None + + +class Ci(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + name: str = Field(..., description='Name of the CI product, e.g. "Jenkins", "CircleCI" etc.') + url: Optional[str] = Field(None, description="Link to the build") + build_number: Optional[str] = Field( + None, + alias="buildNumber", + description="The build number. Some CI servers use non-numeric build numbers, which is why this is a string", + ) + git: Optional["Git"] = None + + +class ParameterType(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + name: str = Field(..., description="The name is unique, so we don't need an id.") + regular_expressions: List[str] = Field(..., alias="regularExpressions", min_length=1) + prefer_for_regular_expression_match: bool = Field(..., alias="preferForRegularExpressionMatch") + use_for_snippets: bool = Field(..., alias="useForSnippets") + id: str + source_reference: Optional["SourceReference"] = Field(None, alias="sourceReference") + + +class ParseError(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + source: "SourceReference" + message: str + + +class PickleTable(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + rows: List["PickleTableRow"] + + +class StepDefinition(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + id: str + pattern: "StepDefinitionPattern" + source_reference: "SourceReference" = Field(..., alias="sourceReference") + + +class TestCase(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + id: str + pickle_id: str = Field(..., alias="pickleId", description="The ID of the `Pickle` this `TestCase` is derived from.") + test_steps: List["TestStep"] = Field(..., alias="testSteps") + + +class TestCaseFinished(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + test_case_started_id: str = Field(..., alias="testCaseStartedId") + timestamp: "Timestamp" + will_be_retried: bool = Field(..., alias="willBeRetried") + + +class TestRunFinished(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + message: Optional[str] = Field( + None, + description="An informative message about the test run. Typically additional information about failure, " + "but not necessarily.", + ) + success: bool = Field( + ..., + description="A test run is successful if all steps are either passed or skipped, all before/after hooks passed " + "and no other exceptions where thrown.", + ) + timestamp: "Timestamp" = Field(..., description="Timestamp when the TestRun is finished") + exception: Optional["ExceptionMessage"] = Field( + None, + description="Any exception thrown during the test run, if any. Does not include exceptions thrown while " + "executing steps.", + ) + + +class TestStepResult(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + duration: "Duration" + message: Optional[str] = Field( + None, + description="An arbitrary bit of information that explains this result. " + "This can be a stack trace of anything else.", + ) + status: "Status" + exception: Optional["ExceptionMessage"] = Field( + None, description="Exception thrown while executing this step, if any." + ) + + +class Background(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + location: Optional["Location"] = Field(..., description="The location of the `Background` keyword") + keyword: str + name: str + description: str + steps: List["Step"] + id: str + + +class Scenario(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + location: Optional["Location"] = Field(..., description="The location of the `Scenario` keyword") + tags: List["Tag"] + keyword: str + name: str + description: str + steps: List["Step"] + examples: List["Examples"] + id: str + + +class Hook(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + id: str + name: Optional[str] = None + source_reference: "SourceReference" = Field(..., alias="sourceReference") + tag_expression: Optional[str] = Field(None, alias="tagExpression") + + +class Meta(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + protocol_version: str = Field( + ..., alias="protocolVersion", description="*\n The [SEMVER](https://semver.org/) version number of the protocol" + ) + implementation: "Product" = Field(..., description="SpecFlow, Cucumber-JVM, Cucumber.js, Cucumber-Ruby, Behat etc.") + runtime: "Product" = Field(..., description="Java, Ruby, Node.js etc") + os: "Product" = Field(..., description="Windows, Linux, MacOS etc") + cpu: "Product" = Field(..., description="386, arm, amd64 etc") + ci: Optional["Ci"] = None + + +class PickleStepArgument(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + doc_string: Optional["PickleDocString"] = Field(None, alias="docString") + data_table: Optional["PickleTable"] = Field(None, alias="dataTable") + + +class TestStepFinished(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + test_case_started_id: str = Field(..., alias="testCaseStartedId") + test_step_id: str = Field(..., alias="testStepId") + test_step_result: "TestStepResult" = Field(..., alias="testStepResult") + timestamp: "Timestamp" + + +class RuleChild(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + background: Optional["Background"] = None + scenario: Optional["Scenario"] = None + + +class PickleStep(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + argument: Optional["PickleStepArgument"] = None + ast_node_ids: List[str] = Field( + ..., + alias="astNodeIds", + description="References the IDs of the source of the step. For Gherkin, this can be\n the ID of a Step, and " + "possibly also the ID of a TableRow", + ) + id: str = Field(..., description="A unique ID for the PickleStep") + type: Optional["Type"] = Field( + None, + description="The context in which the step was specified: context (Given), action (When) or outcome (Then).\n\n" + "Note that the keywords `But` and `And` inherit their meaning from prior steps and the `*` " + "'keyword' doesn't have specific meaning (hence Unknown)", + ) + text: str + + +class Rule(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + location: Optional["Location"] = Field(..., description="The location of the `Rule` keyword") + tags: List["Tag"] = Field(..., description="All the tags placed above the `Rule` keyword") + keyword: str + name: str + description: str + children: List["RuleChild"] + id: str + + +class Pickle(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + id: str = Field(..., description="*\n A unique id for the pickle") + uri: str = Field(..., description="The uri of the source file") + name: str = Field(..., description="The name of the pickle") + language: str = Field(..., description="The language of the pickle") + steps: List["PickleStep"] = Field(..., description="One or more steps") + tags: List["PickleTag"] = Field( + ..., + description="*\n One or more tags. If this pickle is constructed from a Gherkin document,\n It includes " + "inherited tags from the `Feature` as well.", + ) + ast_node_ids: List[str] = Field( + ..., + alias="astNodeIds", + description="*\n Points to the AST node locations of the pickle. The last one represents the unique\n " + "id of the pickle. A pickle constructed from `Examples` will have the first\n id originating " + "from the `Scenario` AST node, and the second from the `TableRow` AST node.", + min_length=1, + ) + + +class FeatureChild(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + rule: Optional["Rule"] = None + background: Optional["Background"] = None + scenario: Optional["Scenario"] = None + + +class Feature(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + location: Optional["Location"] = Field(..., description="The location of the `Feature` keyword") + tags: List["Tag"] = Field(..., description="All the tags placed above the `Feature` keyword") + language: str = Field( + ..., + description="The [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code of the Gherkin document", + ) + keyword: Optional[str] = Field( + None, description="The text of the `Feature` keyword (in the language specified by `language`)" + ) + name: str = Field(..., description="The name of the feature (the text following the `keyword`)") + description: str = Field( + ..., description="The line(s) underneath the line with the `keyword` that are used as description" + ) + children: List["FeatureChild"] = Field(..., description="Zero or more children") + + +class GherkinDocument(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + uri: Optional[str] = Field( + None, + description="*\n The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier)\n of the source, " + "typically a file path relative to the root directory", + ) + feature: Optional["Feature"] = None + comments: List["Comment"] = Field(..., description="All the comments in the Gherkin document") + + +class Envelope(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + attachment: Optional["Attachment"] = None + gherkin_document: Optional["GherkinDocument"] = Field(None, alias="gherkinDocument") + hook: Optional["Hook"] = None + meta: Optional["Meta"] = None + parameter_type: Optional["ParameterType"] = Field(None, alias="parameterType") + parse_error: Optional["ParseError"] = Field(None, alias="parseError") + pickle: Optional["Pickle"] = None + source: Optional["Source"] = None + step_definition: Optional["StepDefinition"] = Field(None, alias="stepDefinition") + test_case: Optional["TestCase"] = Field(None, alias="testCase") + test_case_finished: Optional["TestCaseFinished"] = Field(None, alias="testCaseFinished") + test_case_started: Optional["TestCaseStarted"] = Field(None, alias="testCaseStarted") + test_run_finished: Optional["TestRunFinished"] = Field(None, alias="testRunFinished") + test_run_started: Optional["TestRunStarted"] = Field(None, alias="testRunStarted") + test_step_finished: Optional["TestStepFinished"] = Field(None, alias="testStepFinished") + test_step_started: Optional["TestStepStarted"] = Field(None, alias="testStepStarted") + undefined_parameter_type: Optional["UndefinedParameterType"] = Field(None, alias="undefinedParameterType") + + +Group.model_rebuild() # type: ignore[attr-defined] diff --git a/python/src/messages/samples/attachments/attachments.feature b/python/src/messages/samples/attachments/attachments.feature new file mode 100644 index 00000000..17aa5bbc --- /dev/null +++ b/python/src/messages/samples/attachments/attachments.feature @@ -0,0 +1,44 @@ +Feature: Attachments + It is sometimes useful to take a screenshot while a scenario runs. + Or capture some logs. + + Cucumber lets you `attach` arbitrary files during execution, and you can + specify a content type for the contents. + + Formatters can then render these attachments in reports. + + Attachments must have a body and a content type + + Scenario: Strings can be attached with a media type + Beware that some formatters such as @cucumber/react use the media type + to determine how to display an attachment. + + When the string "hello" is attached as "application/octet-stream" + + Scenario: Log JSON + When the following string is attached as "application/json": + ``` + {"message": "The big question", "foo": "bar"} + ``` + + Scenario: Log text + When the string "hello" is logged + + Scenario: Log ANSI coloured text + When text with ANSI escapes is logged + + Scenario: Byte arrays are base64-encoded regardless of media type + When an array with 10 bytes is attached as "text/plain" + + Scenario: Streams are always base64-encoded + When a JPEG image is attached + + Scenario: Attaching images in examples + When the png is attached + + Examples: + | image | + | cucumber.png | + + Scenario: Attaching a document with a filename + When a PDF document is attached with a filename diff --git a/python/src/messages/samples/attachments/attachments.feature.ndjson b/python/src/messages/samples/attachments/attachments.feature.ndjson new file mode 100644 index 00000000..66fe2e67 --- /dev/null +++ b/python/src/messages/samples/attachments/attachments.feature.ndjson @@ -0,0 +1,87 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: Attachments\n It is sometimes useful to take a screenshot while a scenario runs.\n Or capture some logs.\n\n Cucumber lets you `attach` arbitrary files during execution, and you can\n specify a content type for the contents.\n\n Formatters can then render these attachments in reports.\n\n Attachments must have a body and a content type\n\n Scenario: Strings can be attached with a media type\n Beware that some formatters such as @cucumber/react use the media type\n to determine how to display an attachment.\n\n When the string \"hello\" is attached as \"application/octet-stream\"\n\n Scenario: Log JSON\n When the following string is attached as \"application/json\":\n ```\n {\"message\": \"The big question\", \"foo\": \"bar\"}\n ```\n\n Scenario: Log text\n When the string \"hello\" is logged\n\n Scenario: Log ANSI coloured text\n When text with ANSI escapes is logged\n\n Scenario: Byte arrays are base64-encoded regardless of media type\n When an array with 10 bytes is attached as \"text/plain\"\n\n Scenario: Streams are always base64-encoded\n When a JPEG image is attached\n\n Scenario: Attaching images in examples\n When the png is attached\n\n Examples:\n | image |\n | cucumber.png |\n\n Scenario: Attaching a document with a filename\n When a PDF document is attached with a filename\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/attachments/attachments.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":" Beware that some formatters such as @cucumber/react use the media type\n to determine how to display an attachment.","examples":[],"id":"11","keyword":"Scenario","location":{"column":3,"line":12},"name":"Strings can be attached with a media type","steps":[{"id":"10","keyword":"When ","keywordType":"Action","location":{"column":5,"line":16},"text":"the string \"hello\" is attached as \"application/octet-stream\""}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"13","keyword":"Scenario","location":{"column":3,"line":18},"name":"Log JSON","steps":[{"docString":{"content":"{\"message\": \"The big question\", \"foo\": \"bar\"}","delimiter":"```","location":{"column":8,"line":20}},"id":"12","keyword":"When ","keywordType":"Action","location":{"column":6,"line":19},"text":"the following string is attached as \"application/json\":"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"15","keyword":"Scenario","location":{"column":3,"line":24},"name":"Log text","steps":[{"id":"14","keyword":"When ","keywordType":"Action","location":{"column":5,"line":25},"text":"the string \"hello\" is logged"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"17","keyword":"Scenario","location":{"column":3,"line":27},"name":"Log ANSI coloured text","steps":[{"id":"16","keyword":"When ","keywordType":"Action","location":{"column":6,"line":28},"text":"text with ANSI escapes is logged"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"19","keyword":"Scenario","location":{"column":3,"line":30},"name":"Byte arrays are base64-encoded regardless of media type","steps":[{"id":"18","keyword":"When ","keywordType":"Action","location":{"column":5,"line":31},"text":"an array with 10 bytes is attached as \"text/plain\""}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"21","keyword":"Scenario","location":{"column":3,"line":33},"name":"Streams are always base64-encoded","steps":[{"id":"20","keyword":"When ","keywordType":"Action","location":{"column":5,"line":34},"text":"a JPEG image is attached"}],"tags":[]}},{"scenario":{"description":"","examples":[{"description":"","id":"25","keyword":"Examples","location":{"column":5,"line":39},"name":"","tableBody":[{"cells":[{"location":{"column":9,"line":41},"value":"cucumber.png"}],"id":"24","location":{"column":7,"line":41}}],"tableHeader":{"cells":[{"location":{"column":9,"line":40},"value":"image"}],"id":"23","location":{"column":7,"line":40}},"tags":[]}],"id":"26","keyword":"Scenario","location":{"column":3,"line":36},"name":"Attaching images in examples","steps":[{"id":"22","keyword":"When ","keywordType":"Action","location":{"column":5,"line":37},"text":"the png is attached"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"28","keyword":"Scenario","location":{"column":3,"line":43},"name":"Attaching a document with a filename","steps":[{"id":"27","keyword":"When ","keywordType":"Action","location":{"column":5,"line":44},"text":"a PDF document is attached with a filename"}],"tags":[]}}],"description":" It is sometimes useful to take a screenshot while a scenario runs.\n Or capture some logs.\n\n Cucumber lets you `attach` arbitrary files during execution, and you can\n specify a content type for the contents.\n\n Formatters can then render these attachments in reports.\n\n Attachments must have a body and a content type","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Attachments","tags":[]},"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["11"],"id":"30","language":"en","name":"Strings can be attached with a media type","steps":[{"astNodeIds":["10"],"id":"29","text":"the string \"hello\" is attached as \"application/octet-stream\"","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["13"],"id":"32","language":"en","name":"Log JSON","steps":[{"argument":{"docString":{"content":"{\"message\": \"The big question\", \"foo\": \"bar\"}"}},"astNodeIds":["12"],"id":"31","text":"the following string is attached as \"application/json\":","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["15"],"id":"34","language":"en","name":"Log text","steps":[{"astNodeIds":["14"],"id":"33","text":"the string \"hello\" is logged","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["17"],"id":"36","language":"en","name":"Log ANSI coloured text","steps":[{"astNodeIds":["16"],"id":"35","text":"text with ANSI escapes is logged","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["19"],"id":"38","language":"en","name":"Byte arrays are base64-encoded regardless of media type","steps":[{"astNodeIds":["18"],"id":"37","text":"an array with 10 bytes is attached as \"text/plain\"","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["21"],"id":"40","language":"en","name":"Streams are always base64-encoded","steps":[{"astNodeIds":["20"],"id":"39","text":"a JPEG image is attached","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["26","24"],"id":"42","language":"en","name":"Attaching images in examples","steps":[{"astNodeIds":["22","24"],"id":"41","text":"the cucumber.png png is attached","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["28"],"id":"44","language":"en","name":"Attaching a document with a filename","steps":[{"astNodeIds":["27"],"id":"43","text":"a PDF document is attached with a filename","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"stepDefinition":{"id":"1","pattern":{"source":"the string {string} is attached as {string}","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":8},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"2","pattern":{"source":"the string {string} is logged","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":12},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"3","pattern":{"source":"text with ANSI escapes is logged","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":16},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"4","pattern":{"source":"the following string is attached as {string}:","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":22},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"5","pattern":{"source":"an array with {int} bytes is attached as {string}","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":26},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"6","pattern":{"source":"a stream with {int} bytes are attached as {string}","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":35},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"7","pattern":{"source":"a JPEG image is attached","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":48},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"8","pattern":{"source":"the {word} png is attached","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":52},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"9","pattern":{"source":"a PDF document is attached with a filename","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":56},"uri":"samples/attachments/attachments.feature.ts"}}} +{"hook":{"id":"0","sourceReference":{"location":{"line":6},"uri":"samples/attachments/attachments.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"47","pickleId":"30","testSteps":[{"hookId":"0","id":"45"},{"id":"46","pickleStepId":"29","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[{"children":[{"children":[]}],"start":12,"value":"hello"},{"children":[{"children":[]}]}],"start":11,"value":"\"hello\""},"parameterTypeName":"string"},{"group":{"children":[{"children":[{"children":[]}],"start":35,"value":"application/octet-stream"},{"children":[{"children":[]}]}],"start":34,"value":"\"application/octet-stream\""},"parameterTypeName":"string"}]}]}]}} +{"testCase":{"id":"50","pickleId":"32","testSteps":[{"hookId":"0","id":"48"},{"id":"49","pickleStepId":"31","stepDefinitionIds":["4"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[{"children":[{"children":[]}],"start":37,"value":"application/json"},{"children":[{"children":[]}]}],"start":36,"value":"\"application/json\""},"parameterTypeName":"string"}]}]}]}} +{"testCase":{"id":"53","pickleId":"34","testSteps":[{"hookId":"0","id":"51"},{"id":"52","pickleStepId":"33","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[{"children":[{"children":[]}],"start":12,"value":"hello"},{"children":[{"children":[]}]}],"start":11,"value":"\"hello\""},"parameterTypeName":"string"}]}]}]}} +{"testCase":{"id":"56","pickleId":"36","testSteps":[{"hookId":"0","id":"54"},{"id":"55","pickleStepId":"35","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"59","pickleId":"38","testSteps":[{"hookId":"0","id":"57"},{"id":"58","pickleStepId":"37","stepDefinitionIds":["5"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":14,"value":"10"},"parameterTypeName":"int"},{"group":{"children":[{"children":[{"children":[]}],"start":39,"value":"text/plain"},{"children":[{"children":[]}]}],"start":38,"value":"\"text/plain\""},"parameterTypeName":"string"}]}]}]}} +{"testCase":{"id":"62","pickleId":"40","testSteps":[{"hookId":"0","id":"60"},{"id":"61","pickleStepId":"39","stepDefinitionIds":["7"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"65","pickleId":"42","testSteps":[{"hookId":"0","id":"63"},{"id":"64","pickleStepId":"41","stepDefinitionIds":["8"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":4,"value":"cucumber.png"},"parameterTypeName":"word"}]}]}]}} +{"testCase":{"id":"68","pickleId":"44","testSteps":[{"hookId":"0","id":"66"},{"id":"67","pickleStepId":"43","stepDefinitionIds":["9"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"69","testCaseId":"47","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"69","testStepId":"45","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"69","testStepId":"45","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"69","testStepId":"46","timestamp":{"nanos":4000000,"seconds":0}}} +{"attachment":{"body":"hello","contentEncoding":"IDENTITY","mediaType":"application/octet-stream","testCaseStartedId":"69","testStepId":"46"}} +{"testStepFinished":{"testCaseStartedId":"69","testStepId":"46","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":5000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"69","timestamp":{"nanos":6000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"70","testCaseId":"50","timestamp":{"nanos":7000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"70","testStepId":"48","timestamp":{"nanos":8000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"70","testStepId":"48","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":9000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"70","testStepId":"49","timestamp":{"nanos":10000000,"seconds":0}}} +{"attachment":{"body":"{\"message\": \"The big question\", \"foo\": \"bar\"}","contentEncoding":"IDENTITY","mediaType":"application/json","testCaseStartedId":"70","testStepId":"49"}} +{"testStepFinished":{"testCaseStartedId":"70","testStepId":"49","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":11000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"70","timestamp":{"nanos":12000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"71","testCaseId":"53","timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"71","testStepId":"51","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"71","testStepId":"51","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"71","testStepId":"52","timestamp":{"nanos":16000000,"seconds":0}}} +{"attachment":{"body":"hello","contentEncoding":"IDENTITY","mediaType":"text/x.cucumber.log+plain","testCaseStartedId":"71","testStepId":"52"}} +{"testStepFinished":{"testCaseStartedId":"71","testStepId":"52","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":17000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"71","timestamp":{"nanos":18000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"72","testCaseId":"56","timestamp":{"nanos":19000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"72","testStepId":"54","timestamp":{"nanos":20000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"72","testStepId":"54","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":21000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"72","testStepId":"55","timestamp":{"nanos":22000000,"seconds":0}}} +{"attachment":{"body":"This displays a \u001b[31mr\u001b[0m\u001b[91ma\u001b[0m\u001b[33mi\u001b[0m\u001b[32mn\u001b[0m\u001b[34mb\u001b[0m\u001b[95mo\u001b[0m\u001b[35mw\u001b[0m","contentEncoding":"IDENTITY","mediaType":"text/x.cucumber.log+plain","testCaseStartedId":"72","testStepId":"55"}} +{"testStepFinished":{"testCaseStartedId":"72","testStepId":"55","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":23000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"72","timestamp":{"nanos":24000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"73","testCaseId":"59","timestamp":{"nanos":25000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"73","testStepId":"57","timestamp":{"nanos":26000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"73","testStepId":"57","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":27000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"73","testStepId":"58","timestamp":{"nanos":28000000,"seconds":0}}} +{"attachment":{"body":"AAECAwQFBgcICQ==","contentEncoding":"BASE64","mediaType":"text/plain","testCaseStartedId":"73","testStepId":"58"}} +{"testStepFinished":{"testCaseStartedId":"73","testStepId":"58","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":29000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"73","timestamp":{"nanos":30000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"74","testCaseId":"62","timestamp":{"nanos":31000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"74","testStepId":"60","timestamp":{"nanos":32000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"74","testStepId":"60","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":33000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"74","testStepId":"61","timestamp":{"nanos":34000000,"seconds":0}}} +{"attachment":{"body":"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","contentEncoding":"BASE64","mediaType":"image/png","testCaseStartedId":"74","testStepId":"61"}} +{"testStepFinished":{"testCaseStartedId":"74","testStepId":"61","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":35000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"74","timestamp":{"nanos":36000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"75","testCaseId":"65","timestamp":{"nanos":37000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"75","testStepId":"63","timestamp":{"nanos":38000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"75","testStepId":"63","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":39000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"75","testStepId":"64","timestamp":{"nanos":40000000,"seconds":0}}} +{"attachment":{"body":"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","contentEncoding":"BASE64","mediaType":"image/png","testCaseStartedId":"75","testStepId":"64"}} +{"testStepFinished":{"testCaseStartedId":"75","testStepId":"64","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":41000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"75","timestamp":{"nanos":42000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"76","testCaseId":"68","timestamp":{"nanos":43000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"76","testStepId":"66","timestamp":{"nanos":44000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"76","testStepId":"66","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":45000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"76","testStepId":"67","timestamp":{"nanos":46000000,"seconds":0}}} +{"attachment":{"body":"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RmlsZTIgOCAwIFI+PgplbmRvYmoKMTAgMCBvYmoKPDwvVHlwZSAvRm9udAovRm9udERlc2NyaXB0b3IgOSAwIFIKL0Jhc2VGb250IC9BQUFBQUErQXJpYWxNVAovU3VidHlwZSAvQ0lERm9udFR5cGUyCi9DSURUb0dJRE1hcCAvSWRlbnRpdHkKL0NJRFN5c3RlbUluZm8gPDwvUmVnaXN0cnkgKEFkb2JlKQovT3JkZXJpbmcgKElkZW50aXR5KQovU3VwcGxlbWVudCAwPj4KL1cgWzAgWzc1MF0gNTUgWzYxMC44Mzk4NF0gNzIgWzU1Ni4xNTIzNF0gODcgWzI3Ny44MzIwM11dCi9EVyA1MDA+PgplbmRvYmoKMTEgMCBvYmoKPDwvRmlsdGVyIC9GbGF0ZURlY29kZQovTGVuZ3RoIDI1MD4+IHN0cmVhbQp4nF2Qy2rEIBSG9z7FWU4Xg0lmMtNFEMqUQha90LQPYPQkFRoVYxZ5+3pJU6ig8PP/n+dCb+1jq5UH+uaM6NDDoLR0OJvFCYQeR6VJWYFUwm8qvWLiltAAd+vscWr1YEjTAND34M7erXB4kKbHO0JfnUSn9AiHz1sXdLdY+40Tag8FYQwkDuGnZ25f+IRAE3ZsZfCVX4+B+Ut8rBahSrrM3QgjcbZcoON6RNIU4TBonsJhBLX851eZ6gfxxV1Mn64hXRT1mUV1vk/qUid2S5W/zF6ivmQos9fTls5+LBqXs08kFufCMGmDaYrYv9K4L9kaG6l4fwAdQH9hCmVuZHN0cmVhbQplbmRvYmoKNCAwIG9iago8PC9UeXBlIC9Gb250Ci9TdWJ0eXBlIC9UeXBlMAovQmFzZUZvbnQgL0FBQUFBQStBcmlhbE1UCi9FbmNvZGluZyAvSWRlbnRpdHktSAovRGVzY2VuZGFudEZvbnRzIFsxMCAwIFJdCi9Ub1VuaWNvZGUgMTEgMCBSPj4KZW5kb2JqCnhyZWYKMCAxMgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTUgMDAwMDAgbiAKMDAwMDAwMDM4MiAwMDAwMCBuIAowMDAwMDAwMTA4IDAwMDAwIG4gCjAwMDAwMDk2MDYgMDAwMDAgbiAKMDAwMDAwMDE0NSAwMDAwMCBuIAowMDAwMDAwNTkwIDAwMDAwIG4gCjAwMDAwMDA2NDUgMDAwMDAgbiAKMDAwMDAwMDY5MiAwMDAwMCBuIAowMDAwMDA4Nzg3IDAwMDAwIG4gCjAwMDAwMDkwMjEgMDAwMDAgbiAKMDAwMDAwOTI4NSAwMDAwMCBuIAp0cmFpbGVyCjw8L1NpemUgMTIKL1Jvb3QgNyAwIFIKL0luZm8gMSAwIFI+PgpzdGFydHhyZWYKOTc0NQolJUVPRgo=","contentEncoding":"BASE64","fileName":"document.pdf","mediaType":"application/pdf","testCaseStartedId":"76","testStepId":"67"}} +{"testStepFinished":{"testCaseStartedId":"76","testStepId":"67","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":47000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"76","timestamp":{"nanos":48000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":true,"timestamp":{"nanos":49000000,"seconds":0}}} diff --git a/python/src/messages/samples/attachments/attachments.feature.ts b/python/src/messages/samples/attachments/attachments.feature.ts new file mode 100644 index 00000000..c1e9c6bd --- /dev/null +++ b/python/src/messages/samples/attachments/attachments.feature.ts @@ -0,0 +1,58 @@ +import { Before, When } from '@cucumber/fake-cucumber' +import { ReadableStreamBuffer } from 'stream-buffers' +import fs from 'fs' + +// Cucumber-JVM needs to use a Before hook in order to create attachments +Before(() => undefined) + +When('the string {string} is attached as {string}', function (text: string, mediaType: string) { + this.attach(text, mediaType) +}) + +When('the string {string} is logged', function (text: string) { + this.log(text) +}) + +When('text with ANSI escapes is logged', function () { + this.log( + 'This displays a \x1b[31mr\x1b[0m\x1b[91ma\x1b[0m\x1b[33mi\x1b[0m\x1b[32mn\x1b[0m\x1b[34mb\x1b[0m\x1b[95mo\x1b[0m\x1b[35mw\x1b[0m' + ) +}) + +When('the following string is attached as {string}:', function (mediaType: string, text: string) { + this.attach(text, mediaType) +}) + +When( + 'an array with {int} bytes is attached as {string}', + function (size: number, mediaType: string) { + const data = [...Array(size).keys()] + const buffer = Buffer.from(data) + this.attach(buffer, mediaType) + } +) + +When( + 'a stream with {int} bytes are attached as {string}', + async function (size: number, mediaType: string) { + const data = [...Array(size).keys()] + const buffer = Buffer.from(data) + const stream = new ReadableStreamBuffer({ chunkSize: 1, frequency: 1 }) + stream.put(buffer) + stream.stop() + + await this.attach(stream, mediaType) + } +) + +When('a JPEG image is attached', async function () { + await this.attach(fs.createReadStream(__dirname + '/cucumber.png'), 'image/png') +}) + +When('the {word} png is attached', async function (filename) { + await this.attach(fs.createReadStream(__dirname + `/${filename}`), 'image/png') +}) + +When('a PDF document is attached with a filename', async function () { + await this.attach(fs.createReadStream(__dirname + '/document.pdf'), 'application/pdf', 'document.pdf') +}) diff --git a/python/src/messages/samples/attachments/cucumber.png b/python/src/messages/samples/attachments/cucumber.png new file mode 100644 index 0000000000000000000000000000000000000000..2760899aa0ce16aeaaea93d3118a32e7ac5bec8d GIT binary patch literal 1739 zcmV;+1~mDJP)G+r@kQ0VOJz1t=x5=pe3owlM@Ei<#*ZtpGI zgKd}Iv=WU6L@O5L5Qs#)gFvk*7OQ`VCsjOx5<>9^RV-lQA0`qL#Hi6fjMC@#y`7oe zS+={g-EDP}m+f}vz4x2%_`Ec`caU@Gwv+u9&;ebh;EPxToe6TQSS7$RIEHqf|0i z0O+ z#n5`Lu?_+l5a?76FBT_ov)oVPu)W&2iyqQk6q$E2`kQlqo37e8-)c+O?QiP<=H+=W zgC{tLKe`UI8wSZ$`}IIZS7^VP!&SQ9uqqj{=4V~1HP0i=kNxUV+pBnmxV8-q#Bxu7a+-a2h-P!vK`Neu>T>`ydm{x2F*T=U zFja;RD&fUT?=ygOHKmEIjm9$XXg4i$m^A1Td}8TX_oouO^1 zftu}jxiw*Sqct@^-zMG^t)7q>c1_O=)^$NOakew|C1vwbb(rq-=>$Sb3W6-RZHa~m zCJ-n2XtkjN1C$hf07a9cuvH0K135T-J;#nfDUID~Sv*Yz!r1hE3|a|Q4_9AFEWLHK zWz~oq!$8|OVjZ(mf6Y*Z+@G&h%4HXA<`4i@t!XO}{GYwU7nxru-K+|Xtu%RHv$V5t zB%F}kxeBctB<7atq+W_2XaHsIuS|aaB@`qli21Zmg1ge~X)cg!Z$@+@T2Kf6wH3yz z&3Yvsd2vMF!t&A=G!b@&(o#XX>H~4DCJ(w*r-TNAhSk2Rta1dYR_3O9WK; zw#R~odmNn4Rrysbu1(x67Wr<38~#FiG1`n}wO&7`@#)Xk3f`-e^xfhbRWBNCp0aZ6 zG7j-w;#H4aR;4%|7`O;-+)Hl;uEM%I>mVnijiapH>2)nQ~8L|KJMiw<@G*t1yFW9;lFYc#uS`DwjogG)sqA zR1ZXz{)UVWQ#sGl0$Aj@M%qBOs8U>G?P+&{nu~!`wccEicA`NtKbHn#g?=au3r^~) z3?!GiqlV-fJj2A0p}mr}21v220F29iQ1fLV>-W%pUD-_S!;=aJIQ+*`W9=9^HPvL{ z0;-_op8_N*ciN&xk6wP@c=5J5d#{rWB-U?ag_zePQ!sYV&o8}(nae94B=$6zwdRg$ z{h^gdI-K$AkW4|MJIzg%@h!p>_RSE#rObbMsUUTO7bj4~!21~&^MANd_d&CC2wth| hfX@)Vpvv|h{{nxzjlgvW5(5AL002ovPDHLkV1gBrLk9o= literal 0 HcmV?d00001 diff --git a/python/src/messages/samples/attachments/document.pdf b/python/src/messages/samples/attachments/document.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4647f3c9d70173713ad061f405a1aa9146615677 GIT binary patch literal 10061 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∨WEz|Va{bk-|&;_RAeM9+J zUnh0UX026ZO7^YamIZ5tFXz1xw3~EOReU=#MGVLU&?oU#=?!Q&VOGMa9g3{|}-(rTcEVg5D)DI{a z2GWDRGn4V{waEAz^x84pWqc%zrU@5vy=C+T^*opZy{sEeGZf(2bRQ7&>;3n3xehtByZm`RwV*79njVs~e!)Ed` z;YH~-tZ&NoXtbHSzJwFj7w*SIi)C%{Z8`Yu97yZ~Sa9T+txx2H)bjqO(lY!JZ9$Wd z?q>Zu#8Mn;b2b@_2~w}=Ym!RS<3n-cI9 zwjH50Myt%cFL0csJ&^^?GTKnrwi)*kO{uyhF(kR-koS?7m80R(c zIKR7oLwk9?hVqQyn)s}&`*Z`ke0eNnCvq*jB#li^lS_Wwa*V!tmKv21n> zIWRmXIB*O$9CN=2MaFzX^}(gDA^Pdq3=-`OpZdO+JS|#VtZN9tHt-cWHTu>u-?F#k zg|~2l_XW@B=IvR%<3*oH%%q4gQvvODblS~06nfp|{k(t{nm$Q>+P@j-Oauu{)-!d1p-7KZuoz;VzBBwu( zGi;S^D}Nr}uHT)sBb(kg=cqeOjYzwL|Arc%fB!xIC)99<9!MY@;SLsdNLzul+F$c0JmcRloYIBFCpvZ;0 zrxE)DdEkIRcsapfEub zk(E%lm6nxIgxR484--`O7Ra}MnAP4oqEeT&F}1xzI#dy67AUUdEr3}R#n~WGfR3^Q zioLP0F#|CFV;ah+=45AQg}6g_09<$M$Ll-jLzfTCr4Imu0lE+n7biats*w5sK5l?6 z7!2VAfw_55xBPr4WP^_nHHs>zKB^!RuK{4LyBPnd!@pod{~vUCCq~vXRzqlXO#k@lV>sE{OE+1y!pwuC+l-%Lw!84+cXP;}JYhW}G195Vmt850;vz9?0pz?(k=tBTq|bz2P^i#wnS0J__|f6=t71Hu#+ z)d1YMs6P1X06@TC9x%WZ@J|~E#Rc6R0Gof>c=%CR&wtoJT&RxucN-MKg~ECM-Npst z{qJ~SD5|^u!w>Sm_(7omwJtv&O0xek7s|zr%JkpkLAm+=WnH8L4AtZgckG**g&V3e ns1{VUwMD&4{_NjB+U+0Jazw%$kbl$)%Et@FWnhq0mcsoXXWVEI literal 0 HcmV?d00001 diff --git a/python/src/messages/samples/cdata/cdata.feature b/python/src/messages/samples/cdata/cdata.feature new file mode 100644 index 00000000..9983c41a --- /dev/null +++ b/python/src/messages/samples/cdata/cdata.feature @@ -0,0 +1,6 @@ +Feature: cdata + + Cucumber xml formatters should be able to handle xml cdata elements + + Scenario: cdata + Given I have 42 in my belly diff --git a/python/src/messages/samples/cdata/cdata.feature.ndjson b/python/src/messages/samples/cdata/cdata.feature.ndjson new file mode 100644 index 00000000..3c2a89c9 --- /dev/null +++ b/python/src/messages/samples/cdata/cdata.feature.ndjson @@ -0,0 +1,12 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: cdata\n \n Cucumber xml formatters should be able to handle xml cdata elements\n\n Scenario: cdata\n Given I have 42 in my belly\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/cdata/cdata.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"2","keyword":"Scenario","location":{"column":3,"line":5},"name":"cdata","steps":[{"id":"1","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":6},"text":"I have 42 in my belly"}],"tags":[]}}],"description":" Cucumber xml formatters should be able to handle xml cdata elements","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"cdata","tags":[]},"uri":"samples/cdata/cdata.feature"}} +{"pickle":{"astNodeIds":["2"],"id":"4","language":"en","name":"cdata","steps":[{"astNodeIds":["1"],"id":"3","text":"I have 42 in my belly","type":"Context"}],"tags":[],"uri":"samples/cdata/cdata.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"I have {int} in my belly","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":4},"uri":"samples/cdata/cdata.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"6","pickleId":"4","testSteps":[{"id":"5","pickleStepId":"3","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":7,"value":"42"},"parameterTypeName":"int"}]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"7","testCaseId":"6","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"7","testStepId":"5","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"7","testStepId":"5","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"7","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":true,"timestamp":{"nanos":5000000,"seconds":0}}} diff --git a/python/src/messages/samples/cdata/cdata.feature.ts b/python/src/messages/samples/cdata/cdata.feature.ts new file mode 100644 index 00000000..4d8af61a --- /dev/null +++ b/python/src/messages/samples/cdata/cdata.feature.ts @@ -0,0 +1,6 @@ +import assert from 'assert' +import { Given } from '@cucumber/fake-cucumber' + +Given('I have {int} in my belly', function (cukeCount: number) { + assert(cukeCount) +}) diff --git a/python/src/messages/samples/data-tables/data-tables.feature b/python/src/messages/samples/data-tables/data-tables.feature new file mode 100644 index 00000000..c5ae8059 --- /dev/null +++ b/python/src/messages/samples/data-tables/data-tables.feature @@ -0,0 +1,12 @@ +Feature: Data Tables + Data Tables can be places underneath a step and will be passed as the last + argument to the step definition. They can be used to represent richer data + structures, and can also be transformed to other types. + + Scenario: transposed table + When the following table is transposed: + | a | b | + | 1 | 2 | + Then it should be: + | a | 1 | + | b | 2 | diff --git a/python/src/messages/samples/data-tables/data-tables.feature.ndjson b/python/src/messages/samples/data-tables/data-tables.feature.ndjson new file mode 100644 index 00000000..f21c8180 --- /dev/null +++ b/python/src/messages/samples/data-tables/data-tables.feature.ndjson @@ -0,0 +1,15 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: Data Tables\n Data Tables can be places underneath a step and will be passed as the last\n argument to the step definition. They can be used to represent richer data\n structures, and can also be transformed to other types.\n\n Scenario: transposed table\n When the following table is transposed:\n | a | b |\n | 1 | 2 |\n Then it should be:\n | a | 1 |\n | b | 2 |\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/data-tables/data-tables.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"8","keyword":"Scenario","location":{"column":3,"line":6},"name":"transposed table","steps":[{"dataTable":{"location":{"column":7,"line":8},"rows":[{"cells":[{"location":{"column":9,"line":8},"value":"a"},{"location":{"column":13,"line":8},"value":"b"}],"id":"2","location":{"column":7,"line":8}},{"cells":[{"location":{"column":9,"line":9},"value":"1"},{"location":{"column":13,"line":9},"value":"2"}],"id":"3","location":{"column":7,"line":9}}]},"id":"4","keyword":"When ","keywordType":"Action","location":{"column":5,"line":7},"text":"the following table is transposed:"},{"dataTable":{"location":{"column":7,"line":11},"rows":[{"cells":[{"location":{"column":9,"line":11},"value":"a"},{"location":{"column":13,"line":11},"value":"1"}],"id":"5","location":{"column":7,"line":11}},{"cells":[{"location":{"column":9,"line":12},"value":"b"},{"location":{"column":13,"line":12},"value":"2"}],"id":"6","location":{"column":7,"line":12}}]},"id":"7","keyword":"Then ","keywordType":"Outcome","location":{"column":5,"line":10},"text":"it should be:"}],"tags":[]}}],"description":" Data Tables can be places underneath a step and will be passed as the last\n argument to the step definition. They can be used to represent richer data\n structures, and can also be transformed to other types.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Data Tables","tags":[]},"uri":"samples/data-tables/data-tables.feature"}} +{"pickle":{"astNodeIds":["8"],"id":"11","language":"en","name":"transposed table","steps":[{"argument":{"dataTable":{"rows":[{"cells":[{"value":"a"},{"value":"b"}]},{"cells":[{"value":"1"},{"value":"2"}]}]}},"astNodeIds":["4"],"id":"9","text":"the following table is transposed:","type":"Action"},{"argument":{"dataTable":{"rows":[{"cells":[{"value":"a"},{"value":"1"}]},{"cells":[{"value":"b"},{"value":"2"}]}]}},"astNodeIds":["7"],"id":"10","text":"it should be:","type":"Outcome"}],"tags":[],"uri":"samples/data-tables/data-tables.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"the following table is transposed:","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":5},"uri":"samples/data-tables/data-tables.feature.ts"}}} +{"stepDefinition":{"id":"1","pattern":{"source":"it should be:","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":9},"uri":"samples/data-tables/data-tables.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"14","pickleId":"11","testSteps":[{"id":"12","pickleStepId":"9","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"13","pickleStepId":"10","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"15","testCaseId":"14","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"15","testStepId":"12","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"15","testStepId":"12","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"15","testStepId":"13","timestamp":{"nanos":4000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"15","testStepId":"13","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":5000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"15","timestamp":{"nanos":6000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":true,"timestamp":{"nanos":7000000,"seconds":0}}} diff --git a/python/src/messages/samples/data-tables/data-tables.feature.ts b/python/src/messages/samples/data-tables/data-tables.feature.ts new file mode 100644 index 00000000..b6c71dc1 --- /dev/null +++ b/python/src/messages/samples/data-tables/data-tables.feature.ts @@ -0,0 +1,11 @@ +import { When, Then } from '@cucumber/fake-cucumber' +// TODO: Figure out a better way to export this +import DataTable from '@cucumber/fake-cucumber/dist/src/DataTable' + +When('the following table is transposed:', function (table: DataTable) { + this.transposed = table.transpose() +}) + +Then('it should be:', function (expected: DataTable) { + this.transposed.diff(expected) +}) diff --git a/python/src/messages/samples/examples-tables/examples-tables.feature b/python/src/messages/samples/examples-tables/examples-tables.feature new file mode 100644 index 00000000..ed82f0f3 --- /dev/null +++ b/python/src/messages/samples/examples-tables/examples-tables.feature @@ -0,0 +1,28 @@ +Feature: Examples Tables + Sometimes it can be desireable to run the same scenario multiple times + with different data each time. This can be done by placing an Examples + section with an Examples Table underneath a Scenario, and use + in the Scenario, matching the table headers. + + Scenario Outline: eating cucumbers + Given there are cucumbers + When I eat cucumbers + Then I should have cucumbers + + @passing + Examples: These are passing + | start | eat | left | + | 12 | 5 | 7 | + | 20 | 5 | 15 | + + @failing + Examples: These are failing + | start | eat | left | + | 12 | 20 | 0 | + | 0 | 1 | 0 | + + @undefined + Examples: These are undefined because the value is not an {int} + | start | eat | left | + | 12 | banana | 12 | + | 0 | 1 | apple | diff --git a/python/src/messages/samples/examples-tables/examples-tables.feature.ndjson b/python/src/messages/samples/examples-tables/examples-tables.feature.ndjson new file mode 100644 index 00000000..5dbd02d0 --- /dev/null +++ b/python/src/messages/samples/examples-tables/examples-tables.feature.ndjson @@ -0,0 +1,68 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: Examples Tables\n Sometimes it can be desireable to run the same scenario multiple times\n with different data each time. This can be done by placing an Examples\n section with an Examples Table underneath a Scenario, and use \n in the Scenario, matching the table headers.\n\n Scenario Outline: eating cucumbers\n Given there are cucumbers\n When I eat cucumbers\n Then I should have cucumbers\n\n @passing\n Examples: These are passing\n | start | eat | left |\n | 12 | 5 | 7 |\n | 20 | 5 | 15 |\n\n @failing\n Examples: These are failing\n | start | eat | left |\n | 12 | 20 | 0 |\n | 0 | 1 | 0 |\n\n @undefined\n Examples: These are undefined because the value is not an {int}\n | start | eat | left |\n | 12 | banana | 12 |\n | 0 | 1 | apple |\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/examples-tables/examples-tables.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[{"description":"","id":"10","keyword":"Examples","location":{"column":5,"line":13},"name":"These are passing","tableBody":[{"cells":[{"location":{"column":12,"line":15},"value":"12"},{"location":{"column":19,"line":15},"value":"5"},{"location":{"column":26,"line":15},"value":"7"}],"id":"7","location":{"column":7,"line":15}},{"cells":[{"location":{"column":12,"line":16},"value":"20"},{"location":{"column":19,"line":16},"value":"5"},{"location":{"column":25,"line":16},"value":"15"}],"id":"8","location":{"column":7,"line":16}}],"tableHeader":{"cells":[{"location":{"column":9,"line":14},"value":"start"},{"location":{"column":17,"line":14},"value":"eat"},{"location":{"column":23,"line":14},"value":"left"}],"id":"6","location":{"column":7,"line":14}},"tags":[{"id":"9","location":{"column":5,"line":12},"name":"@passing"}]},{"description":"","id":"15","keyword":"Examples","location":{"column":5,"line":19},"name":"These are failing","tableBody":[{"cells":[{"location":{"column":12,"line":21},"value":"12"},{"location":{"column":18,"line":21},"value":"20"},{"location":{"column":26,"line":21},"value":"0"}],"id":"12","location":{"column":7,"line":21}},{"cells":[{"location":{"column":13,"line":22},"value":"0"},{"location":{"column":19,"line":22},"value":"1"},{"location":{"column":26,"line":22},"value":"0"}],"id":"13","location":{"column":7,"line":22}}],"tableHeader":{"cells":[{"location":{"column":9,"line":20},"value":"start"},{"location":{"column":17,"line":20},"value":"eat"},{"location":{"column":23,"line":20},"value":"left"}],"id":"11","location":{"column":7,"line":20}},"tags":[{"id":"14","location":{"column":5,"line":18},"name":"@failing"}]},{"description":"","id":"20","keyword":"Examples","location":{"column":5,"line":25},"name":"These are undefined because the value is not an {int}","tableBody":[{"cells":[{"location":{"column":12,"line":27},"value":"12"},{"location":{"column":17,"line":27},"value":"banana"},{"location":{"column":29,"line":27},"value":"12"}],"id":"17","location":{"column":7,"line":27}},{"cells":[{"location":{"column":13,"line":28},"value":"0"},{"location":{"column":22,"line":28},"value":"1"},{"location":{"column":26,"line":28},"value":"apple"}],"id":"18","location":{"column":7,"line":28}}],"tableHeader":{"cells":[{"location":{"column":9,"line":26},"value":"start"},{"location":{"column":17,"line":26},"value":"eat"},{"location":{"column":26,"line":26},"value":"left"}],"id":"16","location":{"column":7,"line":26}},"tags":[{"id":"19","location":{"column":5,"line":24},"name":"@undefined"}]}],"id":"21","keyword":"Scenario Outline","location":{"column":3,"line":7},"name":"eating cucumbers","steps":[{"id":"3","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":8},"text":"there are cucumbers"},{"id":"4","keyword":"When ","keywordType":"Action","location":{"column":5,"line":9},"text":"I eat cucumbers"},{"id":"5","keyword":"Then ","keywordType":"Outcome","location":{"column":5,"line":10},"text":"I should have cucumbers"}],"tags":[]}}],"description":" Sometimes it can be desireable to run the same scenario multiple times\n with different data each time. This can be done by placing an Examples\n section with an Examples Table underneath a Scenario, and use \n in the Scenario, matching the table headers.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Examples Tables","tags":[]},"uri":"samples/examples-tables/examples-tables.feature"}} +{"pickle":{"astNodeIds":["21","7"],"id":"25","language":"en","name":"eating cucumbers","steps":[{"astNodeIds":["3","7"],"id":"22","text":"there are 12 cucumbers","type":"Context"},{"astNodeIds":["4","7"],"id":"23","text":"I eat 5 cucumbers","type":"Action"},{"astNodeIds":["5","7"],"id":"24","text":"I should have 7 cucumbers","type":"Outcome"}],"tags":[{"astNodeId":"9","name":"@passing"}],"uri":"samples/examples-tables/examples-tables.feature"}} +{"pickle":{"astNodeIds":["21","8"],"id":"29","language":"en","name":"eating cucumbers","steps":[{"astNodeIds":["3","8"],"id":"26","text":"there are 20 cucumbers","type":"Context"},{"astNodeIds":["4","8"],"id":"27","text":"I eat 5 cucumbers","type":"Action"},{"astNodeIds":["5","8"],"id":"28","text":"I should have 15 cucumbers","type":"Outcome"}],"tags":[{"astNodeId":"9","name":"@passing"}],"uri":"samples/examples-tables/examples-tables.feature"}} +{"pickle":{"astNodeIds":["21","12"],"id":"33","language":"en","name":"eating cucumbers","steps":[{"astNodeIds":["3","12"],"id":"30","text":"there are 12 cucumbers","type":"Context"},{"astNodeIds":["4","12"],"id":"31","text":"I eat 20 cucumbers","type":"Action"},{"astNodeIds":["5","12"],"id":"32","text":"I should have 0 cucumbers","type":"Outcome"}],"tags":[{"astNodeId":"14","name":"@failing"}],"uri":"samples/examples-tables/examples-tables.feature"}} +{"pickle":{"astNodeIds":["21","13"],"id":"37","language":"en","name":"eating cucumbers","steps":[{"astNodeIds":["3","13"],"id":"34","text":"there are 0 cucumbers","type":"Context"},{"astNodeIds":["4","13"],"id":"35","text":"I eat 1 cucumbers","type":"Action"},{"astNodeIds":["5","13"],"id":"36","text":"I should have 0 cucumbers","type":"Outcome"}],"tags":[{"astNodeId":"14","name":"@failing"}],"uri":"samples/examples-tables/examples-tables.feature"}} +{"pickle":{"astNodeIds":["21","17"],"id":"41","language":"en","name":"eating cucumbers","steps":[{"astNodeIds":["3","17"],"id":"38","text":"there are 12 cucumbers","type":"Context"},{"astNodeIds":["4","17"],"id":"39","text":"I eat banana cucumbers","type":"Action"},{"astNodeIds":["5","17"],"id":"40","text":"I should have 12 cucumbers","type":"Outcome"}],"tags":[{"astNodeId":"19","name":"@undefined"}],"uri":"samples/examples-tables/examples-tables.feature"}} +{"pickle":{"astNodeIds":["21","18"],"id":"45","language":"en","name":"eating cucumbers","steps":[{"astNodeIds":["3","18"],"id":"42","text":"there are 0 cucumbers","type":"Context"},{"astNodeIds":["4","18"],"id":"43","text":"I eat 1 cucumbers","type":"Action"},{"astNodeIds":["5","18"],"id":"44","text":"I should have apple cucumbers","type":"Outcome"}],"tags":[{"astNodeId":"19","name":"@undefined"}],"uri":"samples/examples-tables/examples-tables.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"there are {int} cucumbers","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":4},"uri":"samples/examples-tables/examples-tables.feature.ts"}}} +{"stepDefinition":{"id":"1","pattern":{"source":"I eat {int} cucumbers","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":8},"uri":"samples/examples-tables/examples-tables.feature.ts"}}} +{"stepDefinition":{"id":"2","pattern":{"source":"I should have {int} cucumbers","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":12},"uri":"samples/examples-tables/examples-tables.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"49","pickleId":"25","testSteps":[{"id":"46","pickleStepId":"22","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"12"},"parameterTypeName":"int"}]}]},{"id":"47","pickleStepId":"23","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":6,"value":"5"},"parameterTypeName":"int"}]}]},{"id":"48","pickleStepId":"24","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":14,"value":"7"},"parameterTypeName":"int"}]}]}]}} +{"testCase":{"id":"53","pickleId":"29","testSteps":[{"id":"50","pickleStepId":"26","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"20"},"parameterTypeName":"int"}]}]},{"id":"51","pickleStepId":"27","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":6,"value":"5"},"parameterTypeName":"int"}]}]},{"id":"52","pickleStepId":"28","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":14,"value":"15"},"parameterTypeName":"int"}]}]}]}} +{"testCase":{"id":"57","pickleId":"33","testSteps":[{"id":"54","pickleStepId":"30","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"12"},"parameterTypeName":"int"}]}]},{"id":"55","pickleStepId":"31","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":6,"value":"20"},"parameterTypeName":"int"}]}]},{"id":"56","pickleStepId":"32","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":14,"value":"0"},"parameterTypeName":"int"}]}]}]}} +{"testCase":{"id":"61","pickleId":"37","testSteps":[{"id":"58","pickleStepId":"34","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"0"},"parameterTypeName":"int"}]}]},{"id":"59","pickleStepId":"35","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":6,"value":"1"},"parameterTypeName":"int"}]}]},{"id":"60","pickleStepId":"36","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":14,"value":"0"},"parameterTypeName":"int"}]}]}]}} +{"testCase":{"id":"65","pickleId":"41","testSteps":[{"id":"62","pickleStepId":"38","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"12"},"parameterTypeName":"int"}]}]},{"id":"63","pickleStepId":"39","stepDefinitionIds":[],"stepMatchArgumentsLists":[]},{"id":"64","pickleStepId":"40","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":14,"value":"12"},"parameterTypeName":"int"}]}]}]}} +{"testCase":{"id":"69","pickleId":"45","testSteps":[{"id":"66","pickleStepId":"42","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"0"},"parameterTypeName":"int"}]}]},{"id":"67","pickleStepId":"43","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":6,"value":"1"},"parameterTypeName":"int"}]}]},{"id":"68","pickleStepId":"44","stepDefinitionIds":[],"stepMatchArgumentsLists":[]}]}} +{"testCaseStarted":{"attempt":0,"id":"70","testCaseId":"49","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"70","testStepId":"46","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"70","testStepId":"46","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"70","testStepId":"47","timestamp":{"nanos":4000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"70","testStepId":"47","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":5000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"70","testStepId":"48","timestamp":{"nanos":6000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"70","testStepId":"48","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":7000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"70","timestamp":{"nanos":8000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"71","testCaseId":"53","timestamp":{"nanos":9000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"71","testStepId":"50","timestamp":{"nanos":10000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"71","testStepId":"50","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":11000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"71","testStepId":"51","timestamp":{"nanos":12000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"71","testStepId":"51","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"71","testStepId":"52","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"71","testStepId":"52","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"71","timestamp":{"nanos":16000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"72","testCaseId":"57","timestamp":{"nanos":17000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"72","testStepId":"54","timestamp":{"nanos":18000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"72","testStepId":"54","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":19000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"72","testStepId":"55","timestamp":{"nanos":20000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"72","testStepId":"55","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":21000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"72","testStepId":"56","timestamp":{"nanos":22000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"72","testStepId":"56","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Expected values to be strictly equal:\n\n-8 !== 0\n","type":"AssertionError"},"message":"Expected values to be strictly equal:\n\n-8 !== 0\n\nsamples/examples-tables/examples-tables.feature:10\nsamples/examples-tables/examples-tables.feature:21","status":"FAILED"},"timestamp":{"nanos":23000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"72","timestamp":{"nanos":24000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"73","testCaseId":"61","timestamp":{"nanos":25000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"73","testStepId":"58","timestamp":{"nanos":26000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"73","testStepId":"58","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":27000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"73","testStepId":"59","timestamp":{"nanos":28000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"73","testStepId":"59","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":29000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"73","testStepId":"60","timestamp":{"nanos":30000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"73","testStepId":"60","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Expected values to be strictly equal:\n\n-1 !== 0\n","type":"AssertionError"},"message":"Expected values to be strictly equal:\n\n-1 !== 0\n\nsamples/examples-tables/examples-tables.feature:10\nsamples/examples-tables/examples-tables.feature:22","status":"FAILED"},"timestamp":{"nanos":31000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"73","timestamp":{"nanos":32000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"74","testCaseId":"65","timestamp":{"nanos":33000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"74","testStepId":"62","timestamp":{"nanos":34000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"74","testStepId":"62","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":35000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"74","testStepId":"63","timestamp":{"nanos":36000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"74","testStepId":"63","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":37000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"74","testStepId":"64","timestamp":{"nanos":38000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"74","testStepId":"64","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":39000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"74","timestamp":{"nanos":40000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"75","testCaseId":"69","timestamp":{"nanos":41000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"75","testStepId":"66","timestamp":{"nanos":42000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"75","testStepId":"66","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":43000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"75","testStepId":"67","timestamp":{"nanos":44000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"75","testStepId":"67","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":45000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"75","testStepId":"68","timestamp":{"nanos":46000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"75","testStepId":"68","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":47000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"75","timestamp":{"nanos":48000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":49000000,"seconds":0}}} diff --git a/python/src/messages/samples/examples-tables/examples-tables.feature.ts b/python/src/messages/samples/examples-tables/examples-tables.feature.ts new file mode 100644 index 00000000..336cd24b --- /dev/null +++ b/python/src/messages/samples/examples-tables/examples-tables.feature.ts @@ -0,0 +1,14 @@ +import assert from 'assert' +import { Given, When, Then } from '@cucumber/fake-cucumber' + +Given('there are {int} cucumbers', function (initialCount) { + this.count = initialCount +}) + +When('I eat {int} cucumbers', function (eatCount) { + this.count -= eatCount +}) + +Then('I should have {int} cucumbers', function (expectedCount) { + assert.strictEqual(this.count, expectedCount) +}) diff --git a/python/src/messages/samples/hooks/cucumber.svg b/python/src/messages/samples/hooks/cucumber.svg new file mode 100644 index 00000000..537ee11d --- /dev/null +++ b/python/src/messages/samples/hooks/cucumber.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/python/src/messages/samples/hooks/hooks.feature b/python/src/messages/samples/hooks/hooks.feature new file mode 100644 index 00000000..cf6a6b3c --- /dev/null +++ b/python/src/messages/samples/hooks/hooks.feature @@ -0,0 +1,20 @@ +Feature: Hooks + Hooks are special steps that run before or after each scenario's steps. + They can also conditionally target specific scenarios, using tag expressions + + Scenario: no tags, passed step + When a step passes + + Scenario: no tags, failed step + When a step throws an exception + + Scenario: no tags, undefined step + When a step throws an exception + + @some-tag + Scenario: with a tag, passed step + When a step passes + + @with-attachment + Scenario: with an attachment in the hook + When a step passes diff --git a/python/src/messages/samples/hooks/hooks.feature.ndjson b/python/src/messages/samples/hooks/hooks.feature.ndjson new file mode 100644 index 00000000..36b2db13 --- /dev/null +++ b/python/src/messages/samples/hooks/hooks.feature.ndjson @@ -0,0 +1,77 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: Hooks\n Hooks are special steps that run before or after each scenario's steps.\n They can also conditionally target specific scenarios, using tag expressions\n\n Scenario: no tags, passed step\n When a step passes\n\n Scenario: no tags, failed step\n When a step throws an exception\n\n Scenario: no tags, undefined step\n When a step throws an exception\n\n @some-tag\n Scenario: with a tag, passed step\n When a step passes\n\n @with-attachment\n Scenario: with an attachment in the hook\n When a step passes","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/hooks/hooks.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"8","keyword":"Scenario","location":{"column":3,"line":5},"name":"no tags, passed step","steps":[{"id":"7","keyword":"When ","keywordType":"Action","location":{"column":5,"line":6},"text":"a step passes"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"10","keyword":"Scenario","location":{"column":3,"line":8},"name":"no tags, failed step","steps":[{"id":"9","keyword":"When ","keywordType":"Action","location":{"column":5,"line":9},"text":"a step throws an exception"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"12","keyword":"Scenario","location":{"column":3,"line":11},"name":"no tags, undefined step","steps":[{"id":"11","keyword":"When ","keywordType":"Action","location":{"column":5,"line":12},"text":"a step throws an exception"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"15","keyword":"Scenario","location":{"column":3,"line":15},"name":"with a tag, passed step","steps":[{"id":"13","keyword":"When ","keywordType":"Action","location":{"column":5,"line":16},"text":"a step passes"}],"tags":[{"id":"14","location":{"column":3,"line":14},"name":"@some-tag"}]}},{"scenario":{"description":"","examples":[],"id":"18","keyword":"Scenario","location":{"column":3,"line":19},"name":"with an attachment in the hook","steps":[{"id":"16","keyword":"When ","keywordType":"Action","location":{"column":5,"line":20},"text":"a step passes"}],"tags":[{"id":"17","location":{"column":3,"line":18},"name":"@with-attachment"}]}}],"description":" Hooks are special steps that run before or after each scenario's steps.\n They can also conditionally target specific scenarios, using tag expressions","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Hooks","tags":[]},"uri":"samples/hooks/hooks.feature"}} +{"pickle":{"astNodeIds":["8"],"id":"20","language":"en","name":"no tags, passed step","steps":[{"astNodeIds":["7"],"id":"19","text":"a step passes","type":"Action"}],"tags":[],"uri":"samples/hooks/hooks.feature"}} +{"pickle":{"astNodeIds":["10"],"id":"22","language":"en","name":"no tags, failed step","steps":[{"astNodeIds":["9"],"id":"21","text":"a step throws an exception","type":"Action"}],"tags":[],"uri":"samples/hooks/hooks.feature"}} +{"pickle":{"astNodeIds":["12"],"id":"24","language":"en","name":"no tags, undefined step","steps":[{"astNodeIds":["11"],"id":"23","text":"a step throws an exception","type":"Action"}],"tags":[],"uri":"samples/hooks/hooks.feature"}} +{"pickle":{"astNodeIds":["15"],"id":"26","language":"en","name":"with a tag, passed step","steps":[{"astNodeIds":["13"],"id":"25","text":"a step passes","type":"Action"}],"tags":[{"astNodeId":"14","name":"@some-tag"}],"uri":"samples/hooks/hooks.feature"}} +{"pickle":{"astNodeIds":["18"],"id":"28","language":"en","name":"with an attachment in the hook","steps":[{"astNodeIds":["16"],"id":"27","text":"a step passes","type":"Action"}],"tags":[{"astNodeId":"17","name":"@with-attachment"}],"uri":"samples/hooks/hooks.feature"}} +{"stepDefinition":{"id":"2","pattern":{"source":"a step passes","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":12},"uri":"samples/hooks/hooks.feature.ts"}}} +{"stepDefinition":{"id":"3","pattern":{"source":"a step throws an exception","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":16},"uri":"samples/hooks/hooks.feature.ts"}}} +{"hook":{"id":"0","sourceReference":{"location":{"line":4},"uri":"samples/hooks/hooks.feature.ts"}}} +{"hook":{"id":"1","name":"A named hook","sourceReference":{"location":{"line":8},"uri":"samples/hooks/hooks.feature.ts"}}} +{"hook":{"id":"4","sourceReference":{"location":{"line":20},"uri":"samples/hooks/hooks.feature.ts"}}} +{"hook":{"id":"5","sourceReference":{"location":{"line":24},"uri":"samples/hooks/hooks.feature.ts"},"tagExpression":"@some-tag or @some-other-tag"}} +{"hook":{"id":"6","sourceReference":{"location":{"line":28},"uri":"samples/hooks/hooks.feature.ts"},"tagExpression":"@with-attachment"}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"33","pickleId":"20","testSteps":[{"hookId":"0","id":"29"},{"hookId":"1","id":"30"},{"id":"31","pickleStepId":"19","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"hookId":"4","id":"32"}]}} +{"testCase":{"id":"38","pickleId":"22","testSteps":[{"hookId":"0","id":"34"},{"hookId":"1","id":"35"},{"id":"36","pickleStepId":"21","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"hookId":"4","id":"37"}]}} +{"testCase":{"id":"43","pickleId":"24","testSteps":[{"hookId":"0","id":"39"},{"hookId":"1","id":"40"},{"id":"41","pickleStepId":"23","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"hookId":"4","id":"42"}]}} +{"testCase":{"id":"49","pickleId":"26","testSteps":[{"hookId":"0","id":"44"},{"hookId":"1","id":"45"},{"id":"46","pickleStepId":"25","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"hookId":"5","id":"47"},{"hookId":"4","id":"48"}]}} +{"testCase":{"id":"55","pickleId":"28","testSteps":[{"hookId":"0","id":"50"},{"hookId":"1","id":"51"},{"id":"52","pickleStepId":"27","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"hookId":"6","id":"53"},{"hookId":"4","id":"54"}]}} +{"testCaseStarted":{"attempt":0,"id":"56","testCaseId":"33","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"56","testStepId":"29","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"56","testStepId":"29","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"56","testStepId":"30","timestamp":{"nanos":4000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"56","testStepId":"30","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":5000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"56","testStepId":"31","timestamp":{"nanos":6000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"56","testStepId":"31","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":7000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"56","testStepId":"32","timestamp":{"nanos":8000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"56","testStepId":"32","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in hook","type":"Error"},"message":"Exception in hook\nsamples/hooks/hooks.feature:5","status":"FAILED"},"timestamp":{"nanos":9000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"56","timestamp":{"nanos":10000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"57","testCaseId":"38","timestamp":{"nanos":11000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"57","testStepId":"34","timestamp":{"nanos":12000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"57","testStepId":"34","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"57","testStepId":"35","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"57","testStepId":"35","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"57","testStepId":"36","timestamp":{"nanos":16000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"57","testStepId":"36","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in step","type":"Error"},"message":"Exception in step\nsamples/hooks/hooks.feature:9","status":"FAILED"},"timestamp":{"nanos":17000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"57","testStepId":"37","timestamp":{"nanos":18000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"57","testStepId":"37","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in hook","type":"Error"},"message":"Exception in hook\nsamples/hooks/hooks.feature:8","status":"FAILED"},"timestamp":{"nanos":19000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"57","timestamp":{"nanos":20000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"58","testCaseId":"43","timestamp":{"nanos":21000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"58","testStepId":"39","timestamp":{"nanos":22000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"58","testStepId":"39","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":23000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"58","testStepId":"40","timestamp":{"nanos":24000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"58","testStepId":"40","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":25000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"58","testStepId":"41","timestamp":{"nanos":26000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"58","testStepId":"41","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in step","type":"Error"},"message":"Exception in step\nsamples/hooks/hooks.feature:12","status":"FAILED"},"timestamp":{"nanos":27000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"58","testStepId":"42","timestamp":{"nanos":28000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"58","testStepId":"42","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in hook","type":"Error"},"message":"Exception in hook\nsamples/hooks/hooks.feature:11","status":"FAILED"},"timestamp":{"nanos":29000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"58","timestamp":{"nanos":30000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"59","testCaseId":"49","timestamp":{"nanos":31000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"59","testStepId":"44","timestamp":{"nanos":32000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"59","testStepId":"44","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":33000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"59","testStepId":"45","timestamp":{"nanos":34000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"59","testStepId":"45","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":35000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"59","testStepId":"46","timestamp":{"nanos":36000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"59","testStepId":"46","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":37000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"59","testStepId":"47","timestamp":{"nanos":38000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"59","testStepId":"47","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in conditional hook","type":"Error"},"message":"Exception in conditional hook\nsamples/hooks/hooks.feature:15","status":"FAILED"},"timestamp":{"nanos":39000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"59","testStepId":"48","timestamp":{"nanos":40000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"59","testStepId":"48","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in hook","type":"Error"},"message":"Exception in hook\nsamples/hooks/hooks.feature:15","status":"FAILED"},"timestamp":{"nanos":41000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"59","timestamp":{"nanos":42000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"60","testCaseId":"55","timestamp":{"nanos":43000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"60","testStepId":"50","timestamp":{"nanos":44000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"60","testStepId":"50","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":45000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"60","testStepId":"51","timestamp":{"nanos":46000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"60","testStepId":"51","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":47000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"60","testStepId":"52","timestamp":{"nanos":48000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"60","testStepId":"52","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":49000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"60","testStepId":"53","timestamp":{"nanos":50000000,"seconds":0}}} +{"attachment":{"body":"PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJtbC0zIG1sLW1kLTAiIHZpZXdCb3g9IjAgMCA0MC41OSA0Ni4zMSIgd2lkdGg9IjQwLjU5IiBoZWlnaHQ9IjQ2LjMxIj4KICAgIDxnPgogICAgICAgIDxwYXRoIGZpbGw9IiMyM2Q5NmMiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTMwLjI4MyAzLjY0NXEtLjUyOC0uMzE3LTEuMDgtLjU5M2ExNi4xNjQgMTYuMTY0IDAgMDAtMS4xNTQtLjUxOGMtLjEyNC0uMDUyLS4yNDctLjEtLjM3Mi0uMTQ5LS4zNDMtLjEyNy0uNjg5LS4yNjgtMS4wNDItLjM3MWExOS40MjcgMTkuNDI3IDAgMTAtOS43OTIgMzcuNTF2NS41NmMxMS42NzYtMS43NTMgMjIuMDE2LTEwLjk3OSAyMi43ODctMjMuMDkzLjQ1OS03LjI4OS0zLjE5My0xNC43My05LjM0Ny0xOC4zNDZ6Ii8+CiAgICAgICAgPHBhdGggZmlsbD0iIzE3MzY0NyIgZD0iTTE1Ljc4NyA0Ni4zMDd2LTUuOTM1QTIwLjQ3MiAyMC40NzIgMCAxMTI2Ljk1OSAxLjAxNWMuMjc0LjA4LjU1Ny4xODcuODMyLjI5MWwuMjQ4LjA5M2MuMTY1LjA2NC4yOTEuMTEzLjQxNy4xNjcuMzQ4LjEzNy43MzkuMzEzIDEuMjA4LjU0M3EuNTg5LjI5NSAxLjE1My42MzNjNi4zOTMgMy43NTYgMTAuMzU0IDExLjUxOCA5Ljg1NyAxOS4zMTYtLjc2MyAxMi0xMC43MjIgMjIuMTIyLTIzLjY3OSAyNC4wNjd6bTQuOC00NC4yMTRoLS4wMjZhMTguMzY2IDE4LjM2NiAwIDAwLTMuNTI0IDM2LjQwOGwuODUuMTY1djUuMThjMTEuMzkyLTIuMjI0IDIwLjAwOS0xMS4yNzIgMjAuNjg2LTIxLjkyMi40NDgtNy4wMzMtMy4xLTE0LjAxOC04LjgzLTE3LjM4M2wtLjAwOC0uMDA1QTE0LjY5MSAxNC42OTEgMCAwMDI3LjY1NCAzLjVhNS43NCA1Ljc0IDAgMDAtLjM0NC0uMTM4bC0uMjctLjFhOS40OSA5LjQ5IDAgMDAtLjcwOC0uMjQ5IDE4LjQyNSAxOC40MjUgMCAwMC01Ljc0My0uOTJ6Ii8+CiAgICAgICAgPHBhdGggZmlsbD0iIzE3MzY0NyIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTYuNjY2IDEwLjU4YTEuOCAxLjggMCAwMTEuNTgzLjYwOCA0LjE4NCA0LjE4NCAwIDAxLjcyOCAxLjEwN2MuNjQ1IDEuNDIyIDEuMDI3IDMuNDYxLjIzIDQuNjA1YTYuMzM0IDYuMzM0IDAgMDEtMy45ODEtMy4wODcgMy4yMzYgMy4yMzYgMCAwMS0uMzQ3LTEuMzM5IDEuOTU3IDEuOTU3IDAgMDExLjc4Ny0xLjg5NHptLTUuNjgzIDguMDI1YTcuNzQyIDcuNzQyIDAgMDAxLjIxOC43MzcgNS43ODkgNS43ODkgMCAwMDQuODgzLS4xMzggNi4xMTYgNi4xMTYgMCAwMC0zLjM0NS0zLjQ1IDMuNjY0IDMuNjY0IDAgMDAtMS40NDItLjMyMSAxLjg4NCAxLjg4NCAwIDAwLS4zMTkgMCAxLjc2NiAxLjc2NiAwIDAwLS45OTUgMy4xNzJ6bTYuMSAzLjQzM2MtLjc3Ny0uNTE4LTIuMzc5LS4zMDktMy4zMTItLjI5MmE0LjQxNiA0LjQxNiAwIDAwLTEuNjY2LjM1MiAzLjUgMy41IDAgMDAtMS4yMTguNzM4IDEuODE3IDEuODE3IDAgMDAxLjQwOSAzLjE3MSAzLjMgMy4zIDAgMDAxLjQ0Mi0uMzIxYzEuNDM2LS42MiAzLjE0MS0yLjMyIDMuMzQ2LTMuNjQ4em0yLjYxIDJhNi41NTYgNi41NTYgMCAwMC0zLjcyNCAzLjUwNiAzLjA5MSAzLjA5MSAwIDAwLS4zMjEgMS4zMTQgMS45MDcgMS45MDcgMCAwMDMuMyAxLjM0NiA3LjQyMiA3LjQyMiAwIDAwLjctMS4yMThjLjYyMS0xLjMzMy44NjYtMy43Mi4wNDYtNC45NDh6bTIuNTU3LTcuMTY3YTUuOTQxIDUuOTQxIDAgMDAzLjctMy4xNjcgMy4yNDMgMy4yNDMgMCAwMC4zMTktMS4zNDYgMS45MTUgMS45MTUgMCAwMC0xLjc5NC0xLjk1NCAxLjgzMiAxLjgzMiAwIDAwLTEuNi42NDEgNy4zODIgNy4zODIgMCAwMC0uNzA1IDEuMjE4Yy0uNjIgMS40MzQtLjg0MiAzLjQ4LjA4MSA0LjYwM3ptNC4yMDggMTIuMTE1YTMuMjQ0IDMuMjQ0IDAgMDAtLjMyMS0xLjM0NSA1Ljg2OSA1Ljg2OSAwIDAwLTMuNTU0LTMuMjY5IDUuMzg2IDUuMzg2IDAgMDAtLjIyNiA0LjcxMSA0LjE0NyA0LjE0NyAwIDAwLjcgMS4xMjFjMS4xMzMgMS4yMyAzLjUwNS4zMiAzLjQwMi0xLjIxOHptNC4yLTYuMjhhNy40NjYgNy40NjYgMCAwMC0xLjIxNy0uNyA0LjQyNSA0LjQyNSAwIDAwLTEuNjY2LS4zNTIgNi40IDYuNCAwIDAwLTMuMTg4LjU1NSA1Ljk1OSA1Ljk1OSAwIDAwMy4zMTYgMy4zODYgMy42NzIgMy42NzIgMCAwMDEuNDQyLjMyIDEuOCAxLjggMCAwMDEuMzEtMy4yMDl6Ii8+CiAgICA8L2c+Cjwvc3ZnPg==","contentEncoding":"BASE64","mediaType":"image/svg+xml","testCaseStartedId":"60","testStepId":"53"}} +{"testStepFinished":{"testCaseStartedId":"60","testStepId":"53","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":51000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"60","testStepId":"54","timestamp":{"nanos":52000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"60","testStepId":"54","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in hook","type":"Error"},"message":"Exception in hook\nsamples/hooks/hooks.feature:19","status":"FAILED"},"timestamp":{"nanos":53000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"60","timestamp":{"nanos":54000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":55000000,"seconds":0}}} diff --git a/python/src/messages/samples/hooks/hooks.feature.ts b/python/src/messages/samples/hooks/hooks.feature.ts new file mode 100644 index 00000000..632190cf --- /dev/null +++ b/python/src/messages/samples/hooks/hooks.feature.ts @@ -0,0 +1,30 @@ +import { When, Before, After } from '@cucumber/fake-cucumber' +import fs from 'fs' + +Before(function () { + // no-op +}) + +Before({name: 'A named hook'}, function () { + // no-op +}) + +When('a step passes', function () { + // no-op +}) + +When('a step throws an exception', function () { + throw new Error('Exception in step') +}) + +After(function () { + throw new Error('Exception in hook') +}) + +After('@some-tag or @some-other-tag', function () { + throw new Error('Exception in conditional hook') +}) + +After('@with-attachment', async function () { + await this.attach(fs.createReadStream(__dirname + '/cucumber.svg'), 'image/svg+xml') +}) diff --git a/python/src/messages/samples/markdown/markdown.feature.md b/python/src/messages/samples/markdown/markdown.feature.md new file mode 100644 index 00000000..e0cdec5b --- /dev/null +++ b/python/src/messages/samples/markdown/markdown.feature.md @@ -0,0 +1,46 @@ +# Feature: Cheese + +This table is not picked up by Gherkin (not indented 2+ spaces) + +| foo | bar | +| --- | --- | +| boz | boo | + + +## Rule: Nom nom nom + +I love cheese, especially fromage macaroni cheese. Rubber cheese ricotta caerphilly blue castello who moved my cheese queso bavarian bergkase melted cheese. + +### Scenario Outline: Ylajali! + +* Given some TypeScript code: + ```typescript + type Cheese = 'reblochon' | 'roquefort' | 'rocamadour' + ``` +* And some classic Gherkin: + ```gherkin + Given there are 24 apples in Mary's basket + ``` +* When we use a data table and attach something and then + | name | age | + | ---- | --: | + | Bill | 3 | + | Jane | 6 | + | Isla | 5 | +* Then this might or might not run + +#### Examples: because we need more tables + +This table is indented 2 spaces, so Gherkin will pick it up + + | what | + | ---- | + | fail | + | pass | + +And oh by the way, this table is also ignored by Gherkin because it doesn't have 2+ space indent: + +| cheese | +| -------- | +| gouda | +| gamalost | diff --git a/python/src/messages/samples/markdown/markdown.feature.md.ndjson b/python/src/messages/samples/markdown/markdown.feature.md.ndjson new file mode 100644 index 00000000..c4afc018 --- /dev/null +++ b/python/src/messages/samples/markdown/markdown.feature.md.ndjson @@ -0,0 +1,35 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"# Feature: Cheese\n\nThis table is not picked up by Gherkin (not indented 2+ spaces)\n\n| foo | bar |\n| --- | --- |\n| boz | boo |\n\n\n## Rule: Nom nom nom\n\nI love cheese, especially fromage macaroni cheese. Rubber cheese ricotta caerphilly blue castello who moved my cheese queso bavarian bergkase melted cheese.\n\n### Scenario Outline: Ylajali!\n\n* Given some TypeScript code:\n ```typescript\n type Cheese = 'reblochon' | 'roquefort' | 'rocamadour'\n ```\n* And some classic Gherkin:\n ```gherkin\n Given there are 24 apples in Mary's basket\n ```\n* When we use a data table and attach something and then \n | name | age |\n | ---- | --: |\n | Bill | 3 |\n | Jane | 6 |\n | Isla | 5 |\n* Then this might or might not run\n\n#### Examples: because we need more tables\n\nThis table is indented 2 spaces, so Gherkin will pick it up\n\n | what |\n | ---- |\n | fail |\n | pass |\n\nAnd oh by the way, this table is also ignored by Gherkin because it doesn't have 2+ space indent:\n\n| cheese |\n| -------- |\n| gouda |\n| gamalost |\n","mediaType":"text/x.cucumber.gherkin+markdown","uri":"samples/markdown/markdown.feature.md"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"rule":{"children":[{"scenario":{"description":"","examples":[{"description":"","id":"15","keyword":"Examples","location":{"column":6,"line":32},"name":"because we need more tables","tableBody":[{"cells":[{"location":{"column":5,"line":38},"value":"fail"}],"id":"13","location":{"column":3,"line":38}},{"cells":[{"location":{"column":5,"line":39},"value":"pass"}],"id":"14","location":{"column":3,"line":39}}],"tableHeader":{"cells":[{"location":{"column":5,"line":36},"value":"what"}],"id":"12","location":{"column":3,"line":36}},"tags":[]}],"id":"16","keyword":"Scenario Outline","location":{"column":5,"line":14},"name":"Ylajali!","steps":[{"docString":{"content":"type Cheese = 'reblochon' | 'roquefort' | 'rocamadour'","delimiter":"```","location":{"column":3,"line":17},"mediaType":"typescript"},"id":"4","keyword":"Given ","keywordType":"Context","location":{"column":3,"line":16},"text":"some TypeScript code:"},{"docString":{"content":"Given there are 24 apples in Mary's basket","delimiter":"```","location":{"column":3,"line":21},"mediaType":"gherkin"},"id":"5","keyword":"And ","keywordType":"Conjunction","location":{"column":3,"line":20},"text":"some classic Gherkin:"},{"dataTable":{"location":{"column":3,"line":25},"rows":[{"cells":[{"location":{"column":5,"line":25},"value":"name"},{"location":{"column":12,"line":25},"value":"age"}],"id":"6","location":{"column":3,"line":25}},{"cells":[{"location":{"column":5,"line":27},"value":"Bill"},{"location":{"column":14,"line":27},"value":"3"}],"id":"7","location":{"column":3,"line":27}},{"cells":[{"location":{"column":5,"line":28},"value":"Jane"},{"location":{"column":14,"line":28},"value":"6"}],"id":"8","location":{"column":3,"line":28}},{"cells":[{"location":{"column":5,"line":29},"value":"Isla"},{"location":{"column":14,"line":29},"value":"5"}],"id":"9","location":{"column":3,"line":29}}]},"id":"10","keyword":"When ","keywordType":"Action","location":{"column":3,"line":24},"text":"we use a data table and attach something and then "},{"id":"11","keyword":"Then ","keywordType":"Outcome","location":{"column":3,"line":30},"text":"this might or might not run"}],"tags":[]}}],"description":"","id":"17","keyword":"Rule","location":{"column":4,"line":10},"name":"Nom nom nom","tags":[]}}],"description":"","keyword":"Feature","language":"en","location":{"column":3,"line":1},"name":"Cheese","tags":[]},"uri":"samples/markdown/markdown.feature.md"}} +{"pickle":{"astNodeIds":["16","13"],"id":"22","language":"en","name":"Ylajali!","steps":[{"argument":{"docString":{"content":"type Cheese = 'reblochon' | 'roquefort' | 'rocamadour'","mediaType":"typescript"}},"astNodeIds":["4","13"],"id":"18","text":"some TypeScript code:","type":"Context"},{"argument":{"docString":{"content":"Given there are 24 apples in Mary's basket","mediaType":"gherkin"}},"astNodeIds":["5","13"],"id":"19","text":"some classic Gherkin:","type":"Context"},{"argument":{"dataTable":{"rows":[{"cells":[{"value":"name"},{"value":"age"}]},{"cells":[{"value":"Bill"},{"value":"3"}]},{"cells":[{"value":"Jane"},{"value":"6"}]},{"cells":[{"value":"Isla"},{"value":"5"}]}]}},"astNodeIds":["10","13"],"id":"20","text":"we use a data table and attach something and then fail","type":"Action"},{"astNodeIds":["11","13"],"id":"21","text":"this might or might not run","type":"Outcome"}],"tags":[],"uri":"samples/markdown/markdown.feature.md"}} +{"pickle":{"astNodeIds":["16","14"],"id":"27","language":"en","name":"Ylajali!","steps":[{"argument":{"docString":{"content":"type Cheese = 'reblochon' | 'roquefort' | 'rocamadour'","mediaType":"typescript"}},"astNodeIds":["4","14"],"id":"23","text":"some TypeScript code:","type":"Context"},{"argument":{"docString":{"content":"Given there are 24 apples in Mary's basket","mediaType":"gherkin"}},"astNodeIds":["5","14"],"id":"24","text":"some classic Gherkin:","type":"Context"},{"argument":{"dataTable":{"rows":[{"cells":[{"value":"name"},{"value":"age"}]},{"cells":[{"value":"Bill"},{"value":"3"}]},{"cells":[{"value":"Jane"},{"value":"6"}]},{"cells":[{"value":"Isla"},{"value":"5"}]}]}},"astNodeIds":["10","14"],"id":"25","text":"we use a data table and attach something and then pass","type":"Action"},{"astNodeIds":["11","14"],"id":"26","text":"this might or might not run","type":"Outcome"}],"tags":[],"uri":"samples/markdown/markdown.feature.md"}} +{"stepDefinition":{"id":"0","pattern":{"source":"some TypeScript code:","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":4},"uri":"samples/markdown/markdown.feature.md.ts"}}} +{"stepDefinition":{"id":"1","pattern":{"source":"some classic Gherkin:","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":8},"uri":"samples/markdown/markdown.feature.md.ts"}}} +{"stepDefinition":{"id":"2","pattern":{"source":"we use a data table and attach something and then {word}","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":12},"uri":"samples/markdown/markdown.feature.md.ts"}}} +{"stepDefinition":{"id":"3","pattern":{"source":"this might or might not run","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":23},"uri":"samples/markdown/markdown.feature.md.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"32","pickleId":"22","testSteps":[{"id":"28","pickleStepId":"18","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"29","pickleStepId":"19","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"30","pickleStepId":"20","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":50,"value":"fail"},"parameterTypeName":"word"}]}]},{"id":"31","pickleStepId":"21","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"37","pickleId":"27","testSteps":[{"id":"33","pickleStepId":"23","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"34","pickleStepId":"24","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"35","pickleStepId":"25","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":50,"value":"pass"},"parameterTypeName":"word"}]}]},{"id":"36","pickleStepId":"26","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"38","testCaseId":"32","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"38","testStepId":"28","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"38","testStepId":"28","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"38","testStepId":"29","timestamp":{"nanos":4000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"38","testStepId":"29","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":5000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"38","testStepId":"30","timestamp":{"nanos":6000000,"seconds":0}}} +{"attachment":{"body":"We are logging some plain text (fail)","contentEncoding":"IDENTITY","mediaType":"text/x.cucumber.log+plain","testCaseStartedId":"38","testStepId":"30"}} +{"testStepFinished":{"testCaseStartedId":"38","testStepId":"30","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"You asked me to fail","type":"Error"},"message":"You asked me to fail\nsamples/markdown/markdown.feature.md:24\nsamples/markdown/markdown.feature.md:38","status":"FAILED"},"timestamp":{"nanos":7000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"38","testStepId":"31","timestamp":{"nanos":8000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"38","testStepId":"31","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":9000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"38","timestamp":{"nanos":10000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"39","testCaseId":"37","timestamp":{"nanos":11000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"39","testStepId":"33","timestamp":{"nanos":12000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"39","testStepId":"33","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"39","testStepId":"34","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"39","testStepId":"34","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"39","testStepId":"35","timestamp":{"nanos":16000000,"seconds":0}}} +{"attachment":{"body":"We are logging some plain text (pass)","contentEncoding":"IDENTITY","mediaType":"text/x.cucumber.log+plain","testCaseStartedId":"39","testStepId":"35"}} +{"testStepFinished":{"testCaseStartedId":"39","testStepId":"35","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":17000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"39","testStepId":"36","timestamp":{"nanos":18000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"39","testStepId":"36","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":19000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"39","timestamp":{"nanos":20000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":21000000,"seconds":0}}} diff --git a/python/src/messages/samples/markdown/markdown.feature.md.ts b/python/src/messages/samples/markdown/markdown.feature.md.ts new file mode 100644 index 00000000..de8bbdc5 --- /dev/null +++ b/python/src/messages/samples/markdown/markdown.feature.md.ts @@ -0,0 +1,25 @@ +import assert from 'assert' +import { Given, When, Then } from '@cucumber/fake-cucumber' + +Given('some TypeScript code:', function (dataTable: string[][]) { + assert(dataTable) +}) + +Given('some classic Gherkin:', function (gherkin: string) { + assert(gherkin) +}) + +When( + 'we use a data table and attach something and then {word}', + function (word: string, dataTable: string[][]) { + assert(dataTable) + this.log(`We are logging some plain text (${word})`) + if (word === 'fail') { + throw new Error('You asked me to fail') + } + } +) + +Then('this might or might not run', function () { + // no-op +}) diff --git a/python/src/messages/samples/minimal/minimal.feature b/python/src/messages/samples/minimal/minimal.feature new file mode 100644 index 00000000..9bc37141 --- /dev/null +++ b/python/src/messages/samples/minimal/minimal.feature @@ -0,0 +1,10 @@ +Feature: minimal + + Cucumber doesn't execute this markdown, but @cucumber/react renders it + + * This is + * a bullet + * list + + Scenario: cukes + Given I have 42 cukes in my belly diff --git a/python/src/messages/samples/minimal/minimal.feature.ndjson b/python/src/messages/samples/minimal/minimal.feature.ndjson new file mode 100644 index 00000000..da9d4f22 --- /dev/null +++ b/python/src/messages/samples/minimal/minimal.feature.ndjson @@ -0,0 +1,12 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: minimal\n \n Cucumber doesn't execute this markdown, but @cucumber/react renders it\n \n * This is\n * a bullet\n * list\n \n Scenario: cukes\n Given I have 42 cukes in my belly\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/minimal/minimal.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"2","keyword":"Scenario","location":{"column":3,"line":9},"name":"cukes","steps":[{"id":"1","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":10},"text":"I have 42 cukes in my belly"}],"tags":[]}}],"description":" Cucumber doesn't execute this markdown, but @cucumber/react renders it\n \n * This is\n * a bullet\n * list","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"minimal","tags":[]},"uri":"samples/minimal/minimal.feature"}} +{"pickle":{"astNodeIds":["2"],"id":"4","language":"en","name":"cukes","steps":[{"astNodeIds":["1"],"id":"3","text":"I have 42 cukes in my belly","type":"Context"}],"tags":[],"uri":"samples/minimal/minimal.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"I have {int} cukes in my belly","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":4},"uri":"samples/minimal/minimal.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"6","pickleId":"4","testSteps":[{"id":"5","pickleStepId":"3","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":7,"value":"42"},"parameterTypeName":"int"}]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"7","testCaseId":"6","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"7","testStepId":"5","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"7","testStepId":"5","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"7","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":true,"timestamp":{"nanos":5000000,"seconds":0}}} diff --git a/python/src/messages/samples/minimal/minimal.feature.ts b/python/src/messages/samples/minimal/minimal.feature.ts new file mode 100644 index 00000000..941b5ebf --- /dev/null +++ b/python/src/messages/samples/minimal/minimal.feature.ts @@ -0,0 +1,6 @@ +import assert from 'assert' +import { Given } from '@cucumber/fake-cucumber' + +Given('I have {int} cukes in my belly', function (cukeCount: number) { + assert(cukeCount) +}) diff --git a/python/src/messages/samples/parameter-types/parameter-types.feature b/python/src/messages/samples/parameter-types/parameter-types.feature new file mode 100644 index 00000000..5b1422f4 --- /dev/null +++ b/python/src/messages/samples/parameter-types/parameter-types.feature @@ -0,0 +1,9 @@ +Feature: Parameter Types + Cucumber lets you define your own parameter types, which can be used + in Cucumber Expressions. This lets you define a precise domain-specific + vocabulary which can be used to generate a glossary with examples taken + from your scenarios. They also let you transform strings and tables into + rich types. + + Scenario: flights + Given LHR-CDG has been delayed 45 minutes diff --git a/python/src/messages/samples/parameter-types/parameter-types.feature.ndjson b/python/src/messages/samples/parameter-types/parameter-types.feature.ndjson new file mode 100644 index 00000000..61a88074 --- /dev/null +++ b/python/src/messages/samples/parameter-types/parameter-types.feature.ndjson @@ -0,0 +1,13 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: Parameter Types\n Cucumber lets you define your own parameter types, which can be used\n in Cucumber Expressions. This lets you define a precise domain-specific\n vocabulary which can be used to generate a glossary with examples taken\n from your scenarios. They also let you transform strings and tables into\n rich types.\n\n Scenario: flights\n Given LHR-CDG has been delayed 45 minutes\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/parameter-types/parameter-types.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"3","keyword":"Scenario","location":{"column":3,"line":8},"name":"flights","steps":[{"id":"2","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":9},"text":"LHR-CDG has been delayed 45 minutes"}],"tags":[]}}],"description":" Cucumber lets you define your own parameter types, which can be used\n in Cucumber Expressions. This lets you define a precise domain-specific\n vocabulary which can be used to generate a glossary with examples taken\n from your scenarios. They also let you transform strings and tables into\n rich types.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Parameter Types","tags":[]},"uri":"samples/parameter-types/parameter-types.feature"}} +{"pickle":{"astNodeIds":["3"],"id":"5","language":"en","name":"flights","steps":[{"astNodeIds":["2"],"id":"4","text":"LHR-CDG has been delayed 45 minutes","type":"Context"}],"tags":[],"uri":"samples/parameter-types/parameter-types.feature"}} +{"parameterType":{"id":"0","name":"flight","preferForRegularExpressionMatch":false,"regularExpressions":["([A-Z]{3})-([A-Z]{3})"],"sourceReference":{"location":{"line":8},"uri":"samples/parameter-types/parameter-types.feature.ts"},"useForSnippets":true}} +{"stepDefinition":{"id":"1","pattern":{"source":"{flight} has been delayed {int} minutes","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":16},"uri":"samples/parameter-types/parameter-types.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"7","pickleId":"5","testSteps":[{"id":"6","pickleStepId":"4","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[{"children":[],"start":0,"value":"LHR"},{"children":[],"start":4,"value":"CDG"}],"start":0,"value":"LHR-CDG"},"parameterTypeName":"flight"},{"group":{"children":[],"start":25,"value":"45"},"parameterTypeName":"int"}]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"8","testCaseId":"7","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"8","testStepId":"6","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"8","testStepId":"6","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"8","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":true,"timestamp":{"nanos":5000000,"seconds":0}}} diff --git a/python/src/messages/samples/parameter-types/parameter-types.feature.ts b/python/src/messages/samples/parameter-types/parameter-types.feature.ts new file mode 100644 index 00000000..ece172c3 --- /dev/null +++ b/python/src/messages/samples/parameter-types/parameter-types.feature.ts @@ -0,0 +1,20 @@ +import assert from 'assert' +import { Given, ParameterType } from '@cucumber/fake-cucumber' + +class Flight { + constructor(public readonly from: string, public readonly to: string) {} +} + +ParameterType({ + name: 'flight', + regexp: /([A-Z]{3})-([A-Z]{3})/, + transformer(from: string, to: string) { + return new Flight(from, to) + }, +}) + +Given('{flight} has been delayed {int} minutes', function (flight: Flight, delay: number) { + assert.strictEqual(flight.from, 'LHR') + assert.strictEqual(flight.to, 'CDG') + assert.strictEqual(delay, 45) +}) diff --git a/python/src/messages/samples/pending/pending.feature b/python/src/messages/samples/pending/pending.feature new file mode 100644 index 00000000..cc5ea421 --- /dev/null +++ b/python/src/messages/samples/pending/pending.feature @@ -0,0 +1,19 @@ +Feature: Pending steps + + During development, step definitions can signal at runtime that they are + not yet implemented (or "pending") by returning or throwing a particular + value. + + This causes subsequent steps in the scenario to be skipped, and the overall + result to be treated as a failure. + + Scenario: Unimplemented step signals pending status + Given a step that isnt implemented yet + + Scenario: Steps before unimplemented steps are executed + Given an implemented step + When a step that isnt implemented yet + + Scenario: Steps after unimplemented steps are skipped + Given a step that isnt implemented yet + Then a step that we expect to be skipped diff --git a/python/src/messages/samples/pending/pending.feature.ndjson b/python/src/messages/samples/pending/pending.feature.ndjson new file mode 100644 index 00000000..e95ebb87 --- /dev/null +++ b/python/src/messages/samples/pending/pending.feature.ndjson @@ -0,0 +1,30 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: Pending steps\n\n During development, step definitions can signal at runtime that they are\n not yet implemented (or \"pending\") by returning or throwing a particular\n value.\n\n This causes subsequent steps in the scenario to be skipped, and the overall\n result to be treated as a failure.\n\n Scenario: Unimplemented step signals pending status\n Given a step that isnt implemented yet\n\n Scenario: Steps before unimplemented steps are executed\n Given an implemented step\n When a step that isnt implemented yet\n\n Scenario: Steps after unimplemented steps are skipped\n Given a step that isnt implemented yet\n Then a step that we expect to be skipped\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/pending/pending.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"4","keyword":"Scenario","location":{"column":3,"line":10},"name":"Unimplemented step signals pending status","steps":[{"id":"3","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":11},"text":"a step that isnt implemented yet"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"7","keyword":"Scenario","location":{"column":3,"line":13},"name":"Steps before unimplemented steps are executed","steps":[{"id":"5","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":14},"text":"an implemented step"},{"id":"6","keyword":"When ","keywordType":"Action","location":{"column":5,"line":15},"text":"a step that isnt implemented yet"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"10","keyword":"Scenario","location":{"column":3,"line":17},"name":"Steps after unimplemented steps are skipped","steps":[{"id":"8","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":18},"text":"a step that isnt implemented yet"},{"id":"9","keyword":"Then ","keywordType":"Outcome","location":{"column":5,"line":19},"text":"a step that we expect to be skipped"}],"tags":[]}}],"description":" During development, step definitions can signal at runtime that they are\n not yet implemented (or \"pending\") by returning or throwing a particular\n value.\n\n This causes subsequent steps in the scenario to be skipped, and the overall\n result to be treated as a failure.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Pending steps","tags":[]},"uri":"samples/pending/pending.feature"}} +{"pickle":{"astNodeIds":["4"],"id":"12","language":"en","name":"Unimplemented step signals pending status","steps":[{"astNodeIds":["3"],"id":"11","text":"a step that isnt implemented yet","type":"Context"}],"tags":[],"uri":"samples/pending/pending.feature"}} +{"pickle":{"astNodeIds":["7"],"id":"15","language":"en","name":"Steps before unimplemented steps are executed","steps":[{"astNodeIds":["5"],"id":"13","text":"an implemented step","type":"Context"},{"astNodeIds":["6"],"id":"14","text":"a step that isnt implemented yet","type":"Action"}],"tags":[],"uri":"samples/pending/pending.feature"}} +{"pickle":{"astNodeIds":["10"],"id":"18","language":"en","name":"Steps after unimplemented steps are skipped","steps":[{"astNodeIds":["8"],"id":"16","text":"a step that isnt implemented yet","type":"Context"},{"astNodeIds":["9"],"id":"17","text":"a step that we expect to be skipped","type":"Outcome"}],"tags":[],"uri":"samples/pending/pending.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"an implemented step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":3},"uri":"samples/pending/pending.feature.ts"}}} +{"stepDefinition":{"id":"1","pattern":{"source":"a step that isnt implemented yet","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":7},"uri":"samples/pending/pending.feature.ts"}}} +{"stepDefinition":{"id":"2","pattern":{"source":"a step that we expect to be skipped","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":11},"uri":"samples/pending/pending.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"20","pickleId":"12","testSteps":[{"id":"19","pickleStepId":"11","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"23","pickleId":"15","testSteps":[{"id":"21","pickleStepId":"13","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"22","pickleStepId":"14","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"26","pickleId":"18","testSteps":[{"id":"24","pickleStepId":"16","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"25","pickleStepId":"17","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"27","testCaseId":"20","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"27","testStepId":"19","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"27","testStepId":"19","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PENDING"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"27","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"28","testCaseId":"23","timestamp":{"nanos":5000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"28","testStepId":"21","timestamp":{"nanos":6000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"28","testStepId":"21","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":7000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"28","testStepId":"22","timestamp":{"nanos":8000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"28","testStepId":"22","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PENDING"},"timestamp":{"nanos":9000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"28","timestamp":{"nanos":10000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"29","testCaseId":"26","timestamp":{"nanos":11000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"29","testStepId":"24","timestamp":{"nanos":12000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"29","testStepId":"24","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PENDING"},"timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"29","testStepId":"25","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"29","testStepId":"25","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"29","timestamp":{"nanos":16000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":17000000,"seconds":0}}} diff --git a/python/src/messages/samples/pending/pending.feature.ts b/python/src/messages/samples/pending/pending.feature.ts new file mode 100644 index 00000000..7f66ae22 --- /dev/null +++ b/python/src/messages/samples/pending/pending.feature.ts @@ -0,0 +1,13 @@ +import { Given } from '@cucumber/fake-cucumber' + +Given('an implemented step', function () { + // no-op +}) + +Given('a step that isnt implemented yet', function () { + return 'pending' +}) + +Given('a step that we expect to be skipped', function () { + // no-op +}) diff --git a/python/src/messages/samples/retry/retry.arguments.txt b/python/src/messages/samples/retry/retry.arguments.txt new file mode 100644 index 00000000..cf83a555 --- /dev/null +++ b/python/src/messages/samples/retry/retry.arguments.txt @@ -0,0 +1 @@ +--retry 2 diff --git a/python/src/messages/samples/retry/retry.feature b/python/src/messages/samples/retry/retry.feature new file mode 100644 index 00000000..79f9188d --- /dev/null +++ b/python/src/messages/samples/retry/retry.feature @@ -0,0 +1,22 @@ +Feature: Retry + + Some Cucumber implementations support a Retry mechanism, where test cases that fail + can be retried up to a limited number of attempts in the same test run. + + Non-passing statuses other than FAILED don't trigger a retry - they are not going to pass + however many times we attempt them. + + Scenario: test case passes on the first attempt + Given a step that always passes + + Scenario: test case passes on the second attempt + Given a step that passes the second time + + Scenario: test case passes on the final attempt + Given a step that passes the third time + + Scenario: test case fails on every attempt + Given a step that always fails + + Scenario: don't retry on UNDEFINED + Given a non-existent step diff --git a/python/src/messages/samples/retry/retry.feature.ndjson b/python/src/messages/samples/retry/retry.feature.ndjson new file mode 100644 index 00000000..3f01aaf9 --- /dev/null +++ b/python/src/messages/samples/retry/retry.feature.ndjson @@ -0,0 +1,59 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: Retry\n\n Some Cucumber implementations support a Retry mechanism, where test cases that fail\n can be retried up to a limited number of attempts in the same test run.\n\n Non-passing statuses other than FAILED don't trigger a retry - they are not going to pass\n however many times we attempt them.\n\n Scenario: test case passes on the first attempt\n Given a step that always passes\n\n Scenario: test case passes on the second attempt\n Given a step that passes the second time\n\n Scenario: test case passes on the final attempt\n Given a step that passes the third time\n\n Scenario: test case fails on every attempt\n Given a step that always fails\n\n Scenario: don't retry on UNDEFINED\n Given a non-existent step\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/retry/retry.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"5","keyword":"Scenario","location":{"column":3,"line":9},"name":"test case passes on the first attempt","steps":[{"id":"4","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":10},"text":"a step that always passes"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"7","keyword":"Scenario","location":{"column":3,"line":12},"name":"test case passes on the second attempt","steps":[{"id":"6","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":13},"text":"a step that passes the second time"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"9","keyword":"Scenario","location":{"column":3,"line":15},"name":"test case passes on the final attempt","steps":[{"id":"8","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":16},"text":"a step that passes the third time"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"11","keyword":"Scenario","location":{"column":3,"line":18},"name":"test case fails on every attempt","steps":[{"id":"10","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":19},"text":"a step that always fails"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"13","keyword":"Scenario","location":{"column":3,"line":21},"name":"don't retry on UNDEFINED","steps":[{"id":"12","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":22},"text":"a non-existent step"}],"tags":[]}}],"description":" Some Cucumber implementations support a Retry mechanism, where test cases that fail\n can be retried up to a limited number of attempts in the same test run.\n\n Non-passing statuses other than FAILED don't trigger a retry - they are not going to pass\n however many times we attempt them.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Retry","tags":[]},"uri":"samples/retry/retry.feature"}} +{"pickle":{"astNodeIds":["5"],"id":"15","language":"en","name":"test case passes on the first attempt","steps":[{"astNodeIds":["4"],"id":"14","text":"a step that always passes","type":"Context"}],"tags":[],"uri":"samples/retry/retry.feature"}} +{"pickle":{"astNodeIds":["7"],"id":"17","language":"en","name":"test case passes on the second attempt","steps":[{"astNodeIds":["6"],"id":"16","text":"a step that passes the second time","type":"Context"}],"tags":[],"uri":"samples/retry/retry.feature"}} +{"pickle":{"astNodeIds":["9"],"id":"19","language":"en","name":"test case passes on the final attempt","steps":[{"astNodeIds":["8"],"id":"18","text":"a step that passes the third time","type":"Context"}],"tags":[],"uri":"samples/retry/retry.feature"}} +{"pickle":{"astNodeIds":["11"],"id":"21","language":"en","name":"test case fails on every attempt","steps":[{"astNodeIds":["10"],"id":"20","text":"a step that always fails","type":"Context"}],"tags":[],"uri":"samples/retry/retry.feature"}} +{"pickle":{"astNodeIds":["13"],"id":"23","language":"en","name":"don't retry on UNDEFINED","steps":[{"astNodeIds":["12"],"id":"22","text":"a non-existent step","type":"Context"}],"tags":[],"uri":"samples/retry/retry.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"a step that always passes","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":3},"uri":"samples/retry/retry.feature.ts"}}} +{"stepDefinition":{"id":"1","pattern":{"source":"a step that passes the second time","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":8},"uri":"samples/retry/retry.feature.ts"}}} +{"stepDefinition":{"id":"2","pattern":{"source":"a step that passes the third time","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":16},"uri":"samples/retry/retry.feature.ts"}}} +{"stepDefinition":{"id":"3","pattern":{"source":"a step that always fails","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":23},"uri":"samples/retry/retry.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"25","pickleId":"15","testSteps":[{"id":"24","pickleStepId":"14","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"27","pickleId":"17","testSteps":[{"id":"26","pickleStepId":"16","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"29","pickleId":"19","testSteps":[{"id":"28","pickleStepId":"18","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"31","pickleId":"21","testSteps":[{"id":"30","pickleStepId":"20","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"33","pickleId":"23","testSteps":[{"id":"32","pickleStepId":"22","stepDefinitionIds":[],"stepMatchArgumentsLists":[]}]}} +{"testCaseStarted":{"attempt":0,"id":"34","testCaseId":"25","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"34","testStepId":"24","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"34","testStepId":"24","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"34","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"35","testCaseId":"27","timestamp":{"nanos":5000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"35","testStepId":"26","timestamp":{"nanos":6000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"35","testStepId":"26","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in step","type":"Error"},"message":"Exception in step\nsamples/retry/retry.feature:13","status":"FAILED"},"timestamp":{"nanos":7000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"35","timestamp":{"nanos":8000000,"seconds":0},"willBeRetried":true}} +{"testCaseStarted":{"attempt":1,"id":"36","testCaseId":"27","timestamp":{"nanos":9000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"36","testStepId":"26","timestamp":{"nanos":10000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"36","testStepId":"26","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":11000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"36","timestamp":{"nanos":12000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"37","testCaseId":"29","timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"37","testStepId":"28","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"37","testStepId":"28","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in step","type":"Error"},"message":"Exception in step\nsamples/retry/retry.feature:16","status":"FAILED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"37","timestamp":{"nanos":16000000,"seconds":0},"willBeRetried":true}} +{"testCaseStarted":{"attempt":1,"id":"38","testCaseId":"29","timestamp":{"nanos":17000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"38","testStepId":"28","timestamp":{"nanos":18000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"38","testStepId":"28","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in step","type":"Error"},"message":"Exception in step\nsamples/retry/retry.feature:16","status":"FAILED"},"timestamp":{"nanos":19000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"38","timestamp":{"nanos":20000000,"seconds":0},"willBeRetried":true}} +{"testCaseStarted":{"attempt":2,"id":"39","testCaseId":"29","timestamp":{"nanos":21000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"39","testStepId":"28","timestamp":{"nanos":22000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"39","testStepId":"28","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":23000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"39","timestamp":{"nanos":24000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"40","testCaseId":"31","timestamp":{"nanos":25000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"40","testStepId":"30","timestamp":{"nanos":26000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"40","testStepId":"30","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in step","type":"Error"},"message":"Exception in step\nsamples/retry/retry.feature:19","status":"FAILED"},"timestamp":{"nanos":27000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"40","timestamp":{"nanos":28000000,"seconds":0},"willBeRetried":true}} +{"testCaseStarted":{"attempt":1,"id":"41","testCaseId":"31","timestamp":{"nanos":29000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"41","testStepId":"30","timestamp":{"nanos":30000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"41","testStepId":"30","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in step","type":"Error"},"message":"Exception in step\nsamples/retry/retry.feature:19","status":"FAILED"},"timestamp":{"nanos":31000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"41","timestamp":{"nanos":32000000,"seconds":0},"willBeRetried":true}} +{"testCaseStarted":{"attempt":2,"id":"42","testCaseId":"31","timestamp":{"nanos":33000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"42","testStepId":"30","timestamp":{"nanos":34000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"42","testStepId":"30","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"Exception in step","type":"Error"},"message":"Exception in step\nsamples/retry/retry.feature:19","status":"FAILED"},"timestamp":{"nanos":35000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"42","timestamp":{"nanos":36000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"43","testCaseId":"33","timestamp":{"nanos":37000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"43","testStepId":"32","timestamp":{"nanos":38000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"43","testStepId":"32","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":39000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"43","timestamp":{"nanos":40000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":41000000,"seconds":0}}} diff --git a/python/src/messages/samples/retry/retry.feature.ts b/python/src/messages/samples/retry/retry.feature.ts new file mode 100644 index 00000000..077e37cd --- /dev/null +++ b/python/src/messages/samples/retry/retry.feature.ts @@ -0,0 +1,25 @@ +import { Given } from '@cucumber/fake-cucumber' + +Given('a step that always passes', function () { + // no-op +}) + +let secondTimePass = 0 +Given('a step that passes the second time', function () { + secondTimePass++ + if (secondTimePass < 2) { + throw new Error('Exception in step') + } +}) + +let thirdTimePass = 0 +Given('a step that passes the third time', function () { + thirdTimePass++ + if (thirdTimePass < 3) { + throw new Error('Exception in step') + } +}) + +Given('a step that always fails', function () { + throw new Error('Exception in step') +}) diff --git a/python/src/messages/samples/rules/rules.feature b/python/src/messages/samples/rules/rules.feature new file mode 100644 index 00000000..c45c9ed3 --- /dev/null +++ b/python/src/messages/samples/rules/rules.feature @@ -0,0 +1,27 @@ +Feature: Rules + You can place scenarios inside rules. This makes is possible to structure + Gherkin documents in the same way as [example maps](https://cucumber.io/blog/bdd/example-mapping-introduction/). + You can also use the Examples synonym for Scenario to make them even more similar. + + Rule: a sale cannot happen if change cannot be returned + # sad path + Example: no change + Given there are 5 0.20 coins inside + When the customer tries to buy a 0.85 chocolate with a 1 coin + Then the sale should not happen + + # happy path + Example: exact change + Given there are 5 0.20 coins inside + And there are 3 chocolates inside + When the customer tries to buy a 0.80 chocolate with a 1 coin + Then the customer's change should be 1 0.20 coin + + @some-tag + Rule: a sale cannot happen if we're out of stock + # sad path + Example: no chocolates left + Given there are no chocolates inside + But there are 10 0.5 coins inside + When the customer tries to buy a 0.85 chocolate with a 1 coin + Then the sale should not happen diff --git a/python/src/messages/samples/rules/rules.feature.ndjson b/python/src/messages/samples/rules/rules.feature.ndjson new file mode 100644 index 00000000..574b52b5 --- /dev/null +++ b/python/src/messages/samples/rules/rules.feature.ndjson @@ -0,0 +1,45 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: Rules\n You can place scenarios inside rules. This makes is possible to structure\n Gherkin documents in the same way as [example maps](https://cucumber.io/blog/bdd/example-mapping-introduction/).\n You can also use the Examples synonym for Scenario to make them even more similar.\n\n Rule: a sale cannot happen if change cannot be returned\n # sad path\n Example: no change\n Given there are 5 0.20 coins inside\n When the customer tries to buy a 0.85 chocolate with a 1 coin\n Then the sale should not happen\n\n # happy path\n Example: exact change\n Given there are 5 0.20 coins inside\n And there are 3 chocolates inside\n When the customer tries to buy a 0.80 chocolate with a 1 coin\n Then the customer's change should be 1 0.20 coin\n\n @some-tag\n Rule: a sale cannot happen if we're out of stock\n # sad path\n Example: no chocolates left\n Given there are no chocolates inside\n But there are 10 0.5 coins inside\n When the customer tries to buy a 0.85 chocolate with a 1 coin\n Then the sale should not happen\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/rules/rules.feature"}} +{"gherkinDocument":{"comments":[{"location":{"column":1,"line":7},"text":" # sad path"},{"location":{"column":1,"line":13},"text":" # happy path"},{"location":{"column":1,"line":22},"text":" # sad path"}],"feature":{"children":[{"rule":{"children":[{"scenario":{"description":"","examples":[],"id":"9","keyword":"Example","location":{"column":5,"line":8},"name":"no change","steps":[{"id":"6","keyword":"Given ","keywordType":"Context","location":{"column":7,"line":9},"text":"there are 5 0.20 coins inside"},{"id":"7","keyword":"When ","keywordType":"Action","location":{"column":7,"line":10},"text":"the customer tries to buy a 0.85 chocolate with a 1 coin"},{"id":"8","keyword":"Then ","keywordType":"Outcome","location":{"column":7,"line":11},"text":"the sale should not happen"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"14","keyword":"Example","location":{"column":5,"line":14},"name":"exact change","steps":[{"id":"10","keyword":"Given ","keywordType":"Context","location":{"column":7,"line":15},"text":"there are 5 0.20 coins inside"},{"id":"11","keyword":"And ","keywordType":"Conjunction","location":{"column":7,"line":16},"text":"there are 3 chocolates inside"},{"id":"12","keyword":"When ","keywordType":"Action","location":{"column":7,"line":17},"text":"the customer tries to buy a 0.80 chocolate with a 1 coin"},{"id":"13","keyword":"Then ","keywordType":"Outcome","location":{"column":7,"line":18},"text":"the customer's change should be 1 0.20 coin"}],"tags":[]}}],"description":"","id":"15","keyword":"Rule","location":{"column":3,"line":6},"name":"a sale cannot happen if change cannot be returned","tags":[]}},{"rule":{"children":[{"scenario":{"description":"","examples":[],"id":"20","keyword":"Example","location":{"column":5,"line":23},"name":"no chocolates left","steps":[{"id":"16","keyword":"Given ","keywordType":"Context","location":{"column":7,"line":24},"text":"there are no chocolates inside"},{"id":"17","keyword":"But ","keywordType":"Conjunction","location":{"column":7,"line":25},"text":"there are 10 0.5 coins inside"},{"id":"18","keyword":"When ","keywordType":"Action","location":{"column":7,"line":26},"text":"the customer tries to buy a 0.85 chocolate with a 1 coin"},{"id":"19","keyword":"Then ","keywordType":"Outcome","location":{"column":7,"line":27},"text":"the sale should not happen"}],"tags":[]}}],"description":"","id":"22","keyword":"Rule","location":{"column":3,"line":21},"name":"a sale cannot happen if we're out of stock","tags":[{"id":"21","location":{"column":3,"line":20},"name":"@some-tag"}]}}],"description":" You can place scenarios inside rules. This makes is possible to structure\n Gherkin documents in the same way as [example maps](https://cucumber.io/blog/bdd/example-mapping-introduction/).\n You can also use the Examples synonym for Scenario to make them even more similar.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Rules","tags":[]},"uri":"samples/rules/rules.feature"}} +{"pickle":{"astNodeIds":["9"],"id":"26","language":"en","name":"no change","steps":[{"astNodeIds":["6"],"id":"23","text":"there are 5 0.20 coins inside","type":"Context"},{"astNodeIds":["7"],"id":"24","text":"the customer tries to buy a 0.85 chocolate with a 1 coin","type":"Action"},{"astNodeIds":["8"],"id":"25","text":"the sale should not happen","type":"Outcome"}],"tags":[],"uri":"samples/rules/rules.feature"}} +{"pickle":{"astNodeIds":["14"],"id":"31","language":"en","name":"exact change","steps":[{"astNodeIds":["10"],"id":"27","text":"there are 5 0.20 coins inside","type":"Context"},{"astNodeIds":["11"],"id":"28","text":"there are 3 chocolates inside","type":"Context"},{"astNodeIds":["12"],"id":"29","text":"the customer tries to buy a 0.80 chocolate with a 1 coin","type":"Action"},{"astNodeIds":["13"],"id":"30","text":"the customer's change should be 1 0.20 coin","type":"Outcome"}],"tags":[],"uri":"samples/rules/rules.feature"}} +{"pickle":{"astNodeIds":["20"],"id":"36","language":"en","name":"no chocolates left","steps":[{"astNodeIds":["16"],"id":"32","text":"there are no chocolates inside","type":"Context"},{"astNodeIds":["17"],"id":"33","text":"there are 10 0.5 coins inside","type":"Context"},{"astNodeIds":["18"],"id":"34","text":"the customer tries to buy a 0.85 chocolate with a 1 coin","type":"Action"},{"astNodeIds":["19"],"id":"35","text":"the sale should not happen","type":"Outcome"}],"tags":[{"astNodeId":"21","name":"@some-tag"}],"uri":"samples/rules/rules.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"there are {int} {float} coins inside","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":4},"uri":"samples/rules/rules.feature.ts"}}} +{"stepDefinition":{"id":"1","pattern":{"source":"there are no chocolates inside","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":10},"uri":"samples/rules/rules.feature.ts"}}} +{"stepDefinition":{"id":"2","pattern":{"source":"there are {int} chocolates inside","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":14},"uri":"samples/rules/rules.feature.ts"}}} +{"stepDefinition":{"id":"3","pattern":{"source":"the customer tries to buy a {float} chocolate with a {float} coin","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":19},"uri":"samples/rules/rules.feature.ts"}}} +{"stepDefinition":{"id":"4","pattern":{"source":"the sale should not happen","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":25},"uri":"samples/rules/rules.feature.ts"}}} +{"stepDefinition":{"id":"5","pattern":{"source":"the customer's change should be {int} {float} coin(s)","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":29},"uri":"samples/rules/rules.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"40","pickleId":"26","testSteps":[{"id":"37","pickleStepId":"23","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"5"},"parameterTypeName":"int"},{"group":{"children":[],"start":12,"value":"0.20"},"parameterTypeName":"float"}]}]},{"id":"38","pickleStepId":"24","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":28,"value":"0.85"},"parameterTypeName":"float"},{"group":{"children":[],"start":50,"value":"1"},"parameterTypeName":"float"}]}]},{"id":"39","pickleStepId":"25","stepDefinitionIds":["4"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"45","pickleId":"31","testSteps":[{"id":"41","pickleStepId":"27","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"5"},"parameterTypeName":"int"},{"group":{"children":[],"start":12,"value":"0.20"},"parameterTypeName":"float"}]}]},{"id":"42","pickleStepId":"28","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"3"},"parameterTypeName":"int"}]}]},{"id":"43","pickleStepId":"29","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":28,"value":"0.80"},"parameterTypeName":"float"},{"group":{"children":[],"start":50,"value":"1"},"parameterTypeName":"float"}]}]},{"id":"44","pickleStepId":"30","stepDefinitionIds":["5"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":32,"value":"1"},"parameterTypeName":"int"},{"group":{"children":[],"start":34,"value":"0.20"},"parameterTypeName":"float"}]}]}]}} +{"testCase":{"id":"50","pickleId":"36","testSteps":[{"id":"46","pickleStepId":"32","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"47","pickleStepId":"33","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"10"},"parameterTypeName":"int"},{"group":{"children":[],"start":13,"value":"0.5"},"parameterTypeName":"float"}]}]},{"id":"48","pickleStepId":"34","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":28,"value":"0.85"},"parameterTypeName":"float"},{"group":{"children":[],"start":50,"value":"1"},"parameterTypeName":"float"}]}]},{"id":"49","pickleStepId":"35","stepDefinitionIds":["4"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"51","testCaseId":"40","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"51","testStepId":"37","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"51","testStepId":"37","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"51","testStepId":"38","timestamp":{"nanos":4000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"51","testStepId":"38","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":5000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"51","testStepId":"39","timestamp":{"nanos":6000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"51","testStepId":"39","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":7000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"51","timestamp":{"nanos":8000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"52","testCaseId":"45","timestamp":{"nanos":9000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"52","testStepId":"41","timestamp":{"nanos":10000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"52","testStepId":"41","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":11000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"52","testStepId":"42","timestamp":{"nanos":12000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"52","testStepId":"42","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"52","testStepId":"43","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"52","testStepId":"43","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"52","testStepId":"44","timestamp":{"nanos":16000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"52","testStepId":"44","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":17000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"52","timestamp":{"nanos":18000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"53","testCaseId":"50","timestamp":{"nanos":19000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"53","testStepId":"46","timestamp":{"nanos":20000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"53","testStepId":"46","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":21000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"53","testStepId":"47","timestamp":{"nanos":22000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"53","testStepId":"47","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":23000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"53","testStepId":"48","timestamp":{"nanos":24000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"53","testStepId":"48","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":25000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"53","testStepId":"49","timestamp":{"nanos":26000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"53","testStepId":"49","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":27000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"53","timestamp":{"nanos":28000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":true,"timestamp":{"nanos":29000000,"seconds":0}}} diff --git a/python/src/messages/samples/rules/rules.feature.ts b/python/src/messages/samples/rules/rules.feature.ts new file mode 100644 index 00000000..0688d16a --- /dev/null +++ b/python/src/messages/samples/rules/rules.feature.ts @@ -0,0 +1,33 @@ +import assert from 'assert' +import { Given, When, Then } from '@cucumber/fake-cucumber' + +Given('there are {int} {float} coins inside', function (count, denomination) { + // TODO: implement this + assert(count) + assert(denomination) +}) + +Given('there are no chocolates inside', function () { + // TODO: implement this +}) + +Given('there are {int} chocolates inside', function (chocolateCount) { + // TODO: implement this + assert(chocolateCount) +}) + +When('the customer tries to buy a {float} chocolate with a {float} coin', function (price, paid) { + // TODO: implement this + assert(price) + assert(paid) +}) + +Then('the sale should not happen', function () { + // TODO: implement this +}) + +Then("the customer's change should be {int} {float} coin(s)", function (count, denomination) { + // TODO: implement this + assert(count) + assert(denomination) +}) diff --git a/python/src/messages/samples/skipped/skipped.feature b/python/src/messages/samples/skipped/skipped.feature new file mode 100644 index 00000000..c9982b48 --- /dev/null +++ b/python/src/messages/samples/skipped/skipped.feature @@ -0,0 +1,19 @@ +Feature: Skipping scenarios + + Hooks and step definitions are able to signal at runtime that the scenario should + be skipped by returning or throwing a particular value. + + This can be useful when e.g. the current environment doesn't have the right conditions + for running the scenario. + + @skip + Scenario: Skipping from a Before hook + Given a step that we expect to be skipped + + Scenario: Skipping from a step doesn't affect the previous steps + Given an implemented step + When a step that skips + + Scenario: Skipping from a step causes the rest of the scenario to be skipped + Given a step that skips + When a step that we expect to be skipped diff --git a/python/src/messages/samples/skipped/skipped.feature.ndjson b/python/src/messages/samples/skipped/skipped.feature.ndjson new file mode 100644 index 00000000..6ed59723 --- /dev/null +++ b/python/src/messages/samples/skipped/skipped.feature.ndjson @@ -0,0 +1,33 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: Skipping scenarios\n\n Hooks and step definitions are able to signal at runtime that the scenario should\n be skipped by returning or throwing a particular value.\n\n This can be useful when e.g. the current environment doesn't have the right conditions\n for running the scenario.\n\n @skip\n Scenario: Skipping from a Before hook\n Given a step that we expect to be skipped\n\n Scenario: Skipping from a step doesn't affect the previous steps\n Given an implemented step\n When a step that skips\n\n Scenario: Skipping from a step causes the rest of the scenario to be skipped\n Given a step that skips\n When a step that we expect to be skipped\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/skipped/skipped.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"6","keyword":"Scenario","location":{"column":3,"line":10},"name":"Skipping from a Before hook","steps":[{"id":"4","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":11},"text":"a step that we expect to be skipped"}],"tags":[{"id":"5","location":{"column":3,"line":9},"name":"@skip"}]}},{"scenario":{"description":"","examples":[],"id":"9","keyword":"Scenario","location":{"column":3,"line":13},"name":"Skipping from a step doesn't affect the previous steps","steps":[{"id":"7","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":14},"text":"an implemented step"},{"id":"8","keyword":"When ","keywordType":"Action","location":{"column":5,"line":15},"text":"a step that skips"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"12","keyword":"Scenario","location":{"column":3,"line":17},"name":"Skipping from a step causes the rest of the scenario to be skipped","steps":[{"id":"10","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":18},"text":"a step that skips"},{"id":"11","keyword":"When ","keywordType":"Action","location":{"column":5,"line":19},"text":"a step that we expect to be skipped"}],"tags":[]}}],"description":" Hooks and step definitions are able to signal at runtime that the scenario should\n be skipped by returning or throwing a particular value.\n\n This can be useful when e.g. the current environment doesn't have the right conditions\n for running the scenario.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Skipping scenarios","tags":[]},"uri":"samples/skipped/skipped.feature"}} +{"pickle":{"astNodeIds":["6"],"id":"14","language":"en","name":"Skipping from a Before hook","steps":[{"astNodeIds":["4"],"id":"13","text":"a step that we expect to be skipped","type":"Context"}],"tags":[{"astNodeId":"5","name":"@skip"}],"uri":"samples/skipped/skipped.feature"}} +{"pickle":{"astNodeIds":["9"],"id":"17","language":"en","name":"Skipping from a step doesn't affect the previous steps","steps":[{"astNodeIds":["7"],"id":"15","text":"an implemented step","type":"Context"},{"astNodeIds":["8"],"id":"16","text":"a step that skips","type":"Action"}],"tags":[],"uri":"samples/skipped/skipped.feature"}} +{"pickle":{"astNodeIds":["12"],"id":"20","language":"en","name":"Skipping from a step causes the rest of the scenario to be skipped","steps":[{"astNodeIds":["10"],"id":"18","text":"a step that skips","type":"Context"},{"astNodeIds":["11"],"id":"19","text":"a step that we expect to be skipped","type":"Action"}],"tags":[],"uri":"samples/skipped/skipped.feature"}} +{"stepDefinition":{"id":"1","pattern":{"source":"an implemented step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":7},"uri":"samples/skipped/skipped.feature.ts"}}} +{"stepDefinition":{"id":"2","pattern":{"source":"a step that we expect to be skipped","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":11},"uri":"samples/skipped/skipped.feature.ts"}}} +{"stepDefinition":{"id":"3","pattern":{"source":"a step that skips","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":15},"uri":"samples/skipped/skipped.feature.ts"}}} +{"hook":{"id":"0","sourceReference":{"location":{"line":3},"uri":"samples/skipped/skipped.feature.ts"},"tagExpression":"@skip"}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"23","pickleId":"14","testSteps":[{"hookId":"0","id":"21"},{"id":"22","pickleStepId":"13","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"26","pickleId":"17","testSteps":[{"id":"24","pickleStepId":"15","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"25","pickleStepId":"16","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"29","pickleId":"20","testSteps":[{"id":"27","pickleStepId":"18","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"28","pickleStepId":"19","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"30","testCaseId":"23","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"30","testStepId":"21","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"30","testStepId":"21","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"30","testStepId":"22","timestamp":{"nanos":4000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"30","testStepId":"22","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":5000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"30","timestamp":{"nanos":6000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"31","testCaseId":"26","timestamp":{"nanos":7000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"31","testStepId":"24","timestamp":{"nanos":8000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"31","testStepId":"24","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":9000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"31","testStepId":"25","timestamp":{"nanos":10000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"31","testStepId":"25","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":11000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"31","timestamp":{"nanos":12000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"32","testCaseId":"29","timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"32","testStepId":"27","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"32","testStepId":"27","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"32","testStepId":"28","timestamp":{"nanos":16000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"32","testStepId":"28","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":17000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"32","timestamp":{"nanos":18000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":true,"timestamp":{"nanos":19000000,"seconds":0}}} diff --git a/python/src/messages/samples/skipped/skipped.feature.ts b/python/src/messages/samples/skipped/skipped.feature.ts new file mode 100644 index 00000000..9d0bd595 --- /dev/null +++ b/python/src/messages/samples/skipped/skipped.feature.ts @@ -0,0 +1,17 @@ +import { Before, Given } from '@cucumber/fake-cucumber' + +Before('@skip', function () { + return 'skipped' +}) + +Given('an implemented step', function () { + // no-op +}) + +Given('a step that we expect to be skipped', function () { + // no-op +}) + +Given('a step that skips', function () { + return 'skipped' +}) diff --git a/python/src/messages/samples/stack-traces/stack-traces.feature b/python/src/messages/samples/stack-traces/stack-traces.feature new file mode 100644 index 00000000..5c981df5 --- /dev/null +++ b/python/src/messages/samples/stack-traces/stack-traces.feature @@ -0,0 +1,11 @@ +Feature: Stack traces + Nothing beats stack traces when it comes to diagnosing the source of a bug. + Cucumber provides helpful stack traces that: + + - Include a stack frame from the Gherkin document + - Remove uninteresting frames by default + + The first line of the stack trace must contain the feature file. + + Scenario: A failing step + When a step throws an exception diff --git a/python/src/messages/samples/stack-traces/stack-traces.feature.ndjson b/python/src/messages/samples/stack-traces/stack-traces.feature.ndjson new file mode 100644 index 00000000..3d165072 --- /dev/null +++ b/python/src/messages/samples/stack-traces/stack-traces.feature.ndjson @@ -0,0 +1,12 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: Stack traces\n Nothing beats stack traces when it comes to diagnosing the source of a bug.\n Cucumber provides helpful stack traces that:\n \n - Include a stack frame from the Gherkin document\n - Remove uninteresting frames by default\n\n The first line of the stack trace must contain the feature file.\n\n Scenario: A failing step\n When a step throws an exception\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/stack-traces/stack-traces.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"2","keyword":"Scenario","location":{"column":3,"line":10},"name":"A failing step","steps":[{"id":"1","keyword":"When ","keywordType":"Action","location":{"column":5,"line":11},"text":"a step throws an exception"}],"tags":[]}}],"description":" Nothing beats stack traces when it comes to diagnosing the source of a bug.\n Cucumber provides helpful stack traces that:\n \n - Include a stack frame from the Gherkin document\n - Remove uninteresting frames by default\n\n The first line of the stack trace must contain the feature file.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Stack traces","tags":[]},"uri":"samples/stack-traces/stack-traces.feature"}} +{"pickle":{"astNodeIds":["2"],"id":"4","language":"en","name":"A failing step","steps":[{"astNodeIds":["1"],"id":"3","text":"a step throws an exception","type":"Action"}],"tags":[],"uri":"samples/stack-traces/stack-traces.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"a step throws an exception","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":3},"uri":"samples/stack-traces/stack-traces.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"6","pickleId":"4","testSteps":[{"id":"5","pickleStepId":"3","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"7","testCaseId":"6","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"7","testStepId":"5","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"7","testStepId":"5","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"BOOM","type":"Error"},"message":"BOOM\nsamples/stack-traces/stack-traces.feature:11","status":"FAILED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"7","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":5000000,"seconds":0}}} diff --git a/python/src/messages/samples/stack-traces/stack-traces.feature.ts b/python/src/messages/samples/stack-traces/stack-traces.feature.ts new file mode 100644 index 00000000..2ffd339b --- /dev/null +++ b/python/src/messages/samples/stack-traces/stack-traces.feature.ts @@ -0,0 +1,5 @@ +import { When } from '@cucumber/fake-cucumber' + +When('a step throws an exception', function () { + throw new Error('BOOM') +}) diff --git a/python/src/messages/samples/undefined/undefined.feature b/python/src/messages/samples/undefined/undefined.feature new file mode 100644 index 00000000..2abcf5c0 --- /dev/null +++ b/python/src/messages/samples/undefined/undefined.feature @@ -0,0 +1,17 @@ +Feature: Undefined steps + + At runtime, Cucumber may encounter a step in a scenario that it cannot match to a + step definition. In these cases, the scenario cannot run and so the step status + will be UNDEFINED, with subsequent steps being skipped and the overall result treated + as a failure. + + Scenario: Undefined step causes failure + Given a step that isnt implemented yet + + Scenario: Steps before undefined steps are executed + Given an implemented step + When a step that isnt implemented yet + + Scenario: Steps after undefined steps are skipped + Given a step that isnt implemented yet + Then a step that we expect to be skipped diff --git a/python/src/messages/samples/undefined/undefined.feature.ndjson b/python/src/messages/samples/undefined/undefined.feature.ndjson new file mode 100644 index 00000000..44d00265 --- /dev/null +++ b/python/src/messages/samples/undefined/undefined.feature.ndjson @@ -0,0 +1,29 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: Undefined steps\n\n At runtime, Cucumber may encounter a step in a scenario that it cannot match to a\n step definition. In these cases, the scenario cannot run and so the step status\n will be UNDEFINED, with subsequent steps being skipped and the overall result treated\n as a failure.\n\n Scenario: Undefined step causes failure\n Given a step that isnt implemented yet\n\n Scenario: Steps before undefined steps are executed\n Given an implemented step\n When a step that isnt implemented yet\n\n Scenario: Steps after undefined steps are skipped\n Given a step that isnt implemented yet\n Then a step that we expect to be skipped\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/undefined/undefined.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"3","keyword":"Scenario","location":{"column":3,"line":8},"name":"Undefined step causes failure","steps":[{"id":"2","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":9},"text":"a step that isnt implemented yet"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"6","keyword":"Scenario","location":{"column":3,"line":11},"name":"Steps before undefined steps are executed","steps":[{"id":"4","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":12},"text":"an implemented step"},{"id":"5","keyword":"When ","keywordType":"Action","location":{"column":5,"line":13},"text":"a step that isnt implemented yet"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"9","keyword":"Scenario","location":{"column":3,"line":15},"name":"Steps after undefined steps are skipped","steps":[{"id":"7","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":16},"text":"a step that isnt implemented yet"},{"id":"8","keyword":"Then ","keywordType":"Outcome","location":{"column":5,"line":17},"text":"a step that we expect to be skipped"}],"tags":[]}}],"description":" At runtime, Cucumber may encounter a step in a scenario that it cannot match to a\n step definition. In these cases, the scenario cannot run and so the step status\n will be UNDEFINED, with subsequent steps being skipped and the overall result treated\n as a failure.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Undefined steps","tags":[]},"uri":"samples/undefined/undefined.feature"}} +{"pickle":{"astNodeIds":["3"],"id":"11","language":"en","name":"Undefined step causes failure","steps":[{"astNodeIds":["2"],"id":"10","text":"a step that isnt implemented yet","type":"Context"}],"tags":[],"uri":"samples/undefined/undefined.feature"}} +{"pickle":{"astNodeIds":["6"],"id":"14","language":"en","name":"Steps before undefined steps are executed","steps":[{"astNodeIds":["4"],"id":"12","text":"an implemented step","type":"Context"},{"astNodeIds":["5"],"id":"13","text":"a step that isnt implemented yet","type":"Action"}],"tags":[],"uri":"samples/undefined/undefined.feature"}} +{"pickle":{"astNodeIds":["9"],"id":"17","language":"en","name":"Steps after undefined steps are skipped","steps":[{"astNodeIds":["7"],"id":"15","text":"a step that isnt implemented yet","type":"Context"},{"astNodeIds":["8"],"id":"16","text":"a step that we expect to be skipped","type":"Outcome"}],"tags":[],"uri":"samples/undefined/undefined.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"an implemented step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":3},"uri":"samples/undefined/undefined.feature.ts"}}} +{"stepDefinition":{"id":"1","pattern":{"source":"a step that we expect to be skipped","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":7},"uri":"samples/undefined/undefined.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"19","pickleId":"11","testSteps":[{"id":"18","pickleStepId":"10","stepDefinitionIds":[],"stepMatchArgumentsLists":[]}]}} +{"testCase":{"id":"22","pickleId":"14","testSteps":[{"id":"20","pickleStepId":"12","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"21","pickleStepId":"13","stepDefinitionIds":[],"stepMatchArgumentsLists":[]}]}} +{"testCase":{"id":"25","pickleId":"17","testSteps":[{"id":"23","pickleStepId":"15","stepDefinitionIds":[],"stepMatchArgumentsLists":[]},{"id":"24","pickleStepId":"16","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"26","testCaseId":"19","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"26","testStepId":"18","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"26","testStepId":"18","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"26","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"27","testCaseId":"22","timestamp":{"nanos":5000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"27","testStepId":"20","timestamp":{"nanos":6000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"27","testStepId":"20","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":7000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"27","testStepId":"21","timestamp":{"nanos":8000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"27","testStepId":"21","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":9000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"27","timestamp":{"nanos":10000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"28","testCaseId":"25","timestamp":{"nanos":11000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"28","testStepId":"23","timestamp":{"nanos":12000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"28","testStepId":"23","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"28","testStepId":"24","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"28","testStepId":"24","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"28","timestamp":{"nanos":16000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":17000000,"seconds":0}}} diff --git a/python/src/messages/samples/undefined/undefined.feature.ts b/python/src/messages/samples/undefined/undefined.feature.ts new file mode 100644 index 00000000..09d8898f --- /dev/null +++ b/python/src/messages/samples/undefined/undefined.feature.ts @@ -0,0 +1,9 @@ +import { Given } from '@cucumber/fake-cucumber' + +Given('an implemented step', function () { + // no-op +}) + +Given('a step that we expect to be skipped', function () { + // no-op +}) diff --git a/python/src/messages/samples/unknown-parameter-type/unknown-parameter-type.feature b/python/src/messages/samples/unknown-parameter-type/unknown-parameter-type.feature new file mode 100644 index 00000000..4ce05c97 --- /dev/null +++ b/python/src/messages/samples/unknown-parameter-type/unknown-parameter-type.feature @@ -0,0 +1,6 @@ +Feature: Parameter Types + Cucumber will generate an error message if a step definition registers + an unknown parameter type, but the suite will run. + + Scenario: undefined parameter type + Given CDG is closed because of a strike diff --git a/python/src/messages/samples/unknown-parameter-type/unknown-parameter-type.feature.ndjson b/python/src/messages/samples/unknown-parameter-type/unknown-parameter-type.feature.ndjson new file mode 100644 index 00000000..19efe139 --- /dev/null +++ b/python/src/messages/samples/unknown-parameter-type/unknown-parameter-type.feature.ndjson @@ -0,0 +1,12 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"darwin","version":"22.4.0"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"19.7.0"}}} +{"source":{"data":"Feature: Parameter Types\n Cucumber will generate an error message if a step definition registers\n an unknown parameter type, but the suite will run.\n\n Scenario: undefined parameter type\n Given CDG is closed because of a strike","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/unknown-parameter-type/unknown-parameter-type.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"1","keyword":"Scenario","location":{"column":3,"line":5},"name":"undefined parameter type","steps":[{"id":"0","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":6},"text":"CDG is closed because of a strike"}],"tags":[]}}],"description":" Cucumber will generate an error message if a step definition registers\n an unknown parameter type, but the suite will run.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Parameter Types","tags":[]},"uri":"samples/unknown-parameter-type/unknown-parameter-type.feature"}} +{"pickle":{"astNodeIds":["1"],"id":"3","language":"en","name":"undefined parameter type","steps":[{"astNodeIds":["0"],"id":"2","text":"CDG is closed because of a strike","type":"Context"}],"tags":[],"uri":"samples/unknown-parameter-type/unknown-parameter-type.feature"}} +{"undefinedParameterType":{"expression":"{airport} is closed because of a strike","name":"airport"}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"5","pickleId":"3","testSteps":[{"id":"4","pickleStepId":"2","stepDefinitionIds":[],"stepMatchArgumentsLists":[]}]}} +{"testCaseStarted":{"attempt":0,"id":"6","testCaseId":"5","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"6","testStepId":"4","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"6","testStepId":"4","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"6","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":5000000,"seconds":0}}} diff --git a/python/src/messages/samples/unknown-parameter-type/unknown-parameter-type.feature.ts b/python/src/messages/samples/unknown-parameter-type/unknown-parameter-type.feature.ts new file mode 100644 index 00000000..4f3e0c4d --- /dev/null +++ b/python/src/messages/samples/unknown-parameter-type/unknown-parameter-type.feature.ts @@ -0,0 +1,6 @@ +import { Given } from '@cucumber/fake-cucumber' + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +Given('{airport} is closed because of a strike', function (airport) { + throw new Error('Should not be called because airport type not defined') +}) diff --git a/python/tests/__init__.py b/python/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python/tests/conftest.py b/python/tests/conftest.py new file mode 100644 index 00000000..e69de29b diff --git a/python/tests/test_model_load.py b/python/tests/test_model_load.py new file mode 100644 index 00000000..12f99147 --- /dev/null +++ b/python/tests/test_model_load.py @@ -0,0 +1,28 @@ +import json +from itertools import chain +from pathlib import Path + +from importlib_resources import files +from pytest import mark, param + +from messages import Envelope + + +@mark.parametrize( + "ast_path", + map( + lambda file: param(file, id=file.name), # type: ignore[no-any-return] + chain.from_iterable(map(lambda p: p.glob("*.ndjson"), files("messages.samples").iterdir())), + ), +) +def test_simple_load_model(ast_path: Path): + with ast_path.open(mode="r") as ast_file: + for ast_line in ast_file: + model_datum = json.loads(ast_line) + model = Envelope.model_validate(model_datum) # type: ignore[attr-defined] + + assert isinstance(model, Envelope) + + dumped_ast_datum = json.loads(model.model_dump_json(by_alias=True, exclude_none=True)) # type: ignore[attr-defined] # migration to pydantic2 + + assert model_datum == dumped_ast_datum diff --git a/python/tox.ini b/python/tox.ini new file mode 100644 index 00000000..9ee82b15 --- /dev/null +++ b/python/tox.ini @@ -0,0 +1,44 @@ +[tox] +requires = + tox>=4.2 +env_list = + py311-pre-commit + py{311, 310, 39, 38}-mypy + py{py310, py39, py38, 311, 310, 39, 38}-pytest-coverage-{lin, win, mac} +distshare = {homedir}/.tox/distshare + +[testenv] +platform = + lin: linux + mac: darwin + win: win32 + +[testenv:py311-pre-commit] +skip_install = true +deps = + pre-commit +commands = + pre-commit run + +[testenv:py{311, 310, 39, 38}mypy] +deps = + mypy +commands = + python -m mypy + +[testenv:py{311, 310, 39, 38}-pytest-coverage] +deps = + coverage + pytest +commands = + coverage run --append -m pytest -vvl + +[gh-actions] +python = + 3.8: py38 + 3.9: py39 + 3.10: py310 + 3.11: py311 + pypy-3.8: pypy38 + pypy-3.9: pypy39 + pypy-3.10: pypy310