Merge pull request #67 from ckstettler/cuke-fmt
Added support for the cucumber json format.
Этот коммит содержится в:
		
						коммит
						f726a8ed55
					
				
					 7 изменённых файлов: 1053 добавлений и 7 удалений
				
			
		
							
								
								
									
										551
									
								
								features/formatter/cucumber.feature
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										551
									
								
								features/formatter/cucumber.feature
									
										
									
									
									
										Обычный файл
									
								
							| 
						 | 
					@ -0,0 +1,551 @@
 | 
				
			||||||
 | 
					Feature: cucumber json formatter
 | 
				
			||||||
 | 
					  In order to support tools that import cucumber json output
 | 
				
			||||||
 | 
					  I need to be able to support cucumber json formatted output
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Scenario: Support of Feature Plus Scenario Node
 | 
				
			||||||
 | 
					    Given a feature "features/simple.feature" file:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					        Feature: simple feature
 | 
				
			||||||
 | 
					            simple feature description
 | 
				
			||||||
 | 
					        Scenario: simple scenario
 | 
				
			||||||
 | 
					            simple scenario description
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    When I run feature suite with formatter "cucumber"
 | 
				
			||||||
 | 
					    Then the rendered json will be as follows:
 | 
				
			||||||
 | 
					      """ application/json
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "uri": "features/simple.feature",
 | 
				
			||||||
 | 
					            "id": "simple-feature",
 | 
				
			||||||
 | 
					            "keyword": "Feature",
 | 
				
			||||||
 | 
					            "name": "simple feature",
 | 
				
			||||||
 | 
					            "description": "        simple feature description",
 | 
				
			||||||
 | 
					            "line": 1,
 | 
				
			||||||
 | 
					            "elements": [
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                "id": "simple-feature;simple-scenario",
 | 
				
			||||||
 | 
					                "keyword": "Scenario",
 | 
				
			||||||
 | 
					                "name": "simple scenario",
 | 
				
			||||||
 | 
					                "description": "        simple scenario description",
 | 
				
			||||||
 | 
					                "line": 3,
 | 
				
			||||||
 | 
					                "type": "scenario"
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Scenario: Support of Feature Plus Scenario Node With Tags
 | 
				
			||||||
 | 
					    Given a feature "features/simple.feature" file:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					        @TAG1
 | 
				
			||||||
 | 
					        Feature: simple feature
 | 
				
			||||||
 | 
					            simple feature description
 | 
				
			||||||
 | 
					        @TAG2 @TAG3
 | 
				
			||||||
 | 
					        Scenario: simple scenario
 | 
				
			||||||
 | 
					            simple scenario description
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    When I run feature suite with formatter "cucumber"
 | 
				
			||||||
 | 
					    Then the rendered json will be as follows:
 | 
				
			||||||
 | 
					      """ application/json
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "uri": "features/simple.feature",
 | 
				
			||||||
 | 
					            "id": "simple-feature",
 | 
				
			||||||
 | 
					            "keyword": "Feature",
 | 
				
			||||||
 | 
					            "name": "simple feature",
 | 
				
			||||||
 | 
					            "description": "        simple feature description",
 | 
				
			||||||
 | 
					            "line": 2,
 | 
				
			||||||
 | 
					            "tags": [
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                "name": "@TAG1",
 | 
				
			||||||
 | 
					                "line": 1
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "elements": [
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                "id": "simple-feature;simple-scenario",
 | 
				
			||||||
 | 
					                "keyword": "Scenario",
 | 
				
			||||||
 | 
					                "name": "simple scenario",
 | 
				
			||||||
 | 
					                "description": "        simple scenario description",
 | 
				
			||||||
 | 
					                "line": 5,
 | 
				
			||||||
 | 
					                "type": "scenario",
 | 
				
			||||||
 | 
					                "tags": [
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                    "name": "@TAG1",
 | 
				
			||||||
 | 
					                    "line": 1
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                    "name": "@TAG2",
 | 
				
			||||||
 | 
					                    "line": 4
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                    "name": "@TAG3",
 | 
				
			||||||
 | 
					                    "line": 4
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					      """
 | 
				
			||||||
 | 
					  Scenario: Support of Feature Plus Scenario Outline
 | 
				
			||||||
 | 
					    Given a feature "features/simple.feature" file:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					        Feature: simple feature
 | 
				
			||||||
 | 
					            simple feature description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Scenario Outline: simple scenario
 | 
				
			||||||
 | 
					            simple scenario description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Examples: simple examples
 | 
				
			||||||
 | 
					        | status |
 | 
				
			||||||
 | 
					        | pass   |
 | 
				
			||||||
 | 
					        | fail   |
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    When I run feature suite with formatter "cucumber"
 | 
				
			||||||
 | 
					    Then the rendered json will be as follows:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					      [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "uri": "features/simple.feature",
 | 
				
			||||||
 | 
					          "id": "simple-feature",
 | 
				
			||||||
 | 
					          "keyword": "Feature",
 | 
				
			||||||
 | 
					          "name": "simple feature",
 | 
				
			||||||
 | 
					          "description": "        simple feature description",
 | 
				
			||||||
 | 
					          "line": 1,
 | 
				
			||||||
 | 
					          "elements": [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "id": "simple-feature;simple-scenario;simple-examples;2",
 | 
				
			||||||
 | 
					              "keyword": "Scenario Outline",
 | 
				
			||||||
 | 
					              "name": "simple scenario",
 | 
				
			||||||
 | 
					              "description": "        simple scenario description",
 | 
				
			||||||
 | 
					              "line": 9,
 | 
				
			||||||
 | 
					              "type": "scenario"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "id": "simple-feature;simple-scenario;simple-examples;3",
 | 
				
			||||||
 | 
					              "keyword": "Scenario Outline",
 | 
				
			||||||
 | 
					              "name": "simple scenario",
 | 
				
			||||||
 | 
					              "description": "        simple scenario description",
 | 
				
			||||||
 | 
					              "line": 10,
 | 
				
			||||||
 | 
					              "type": "scenario"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Scenario: Support of Feature Plus Scenario Outline With Tags
 | 
				
			||||||
 | 
					    Given a feature "features/simple.feature" file:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					        @TAG1
 | 
				
			||||||
 | 
					        Feature: simple feature
 | 
				
			||||||
 | 
					            simple feature description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @TAG2
 | 
				
			||||||
 | 
					        Scenario Outline: simple scenario
 | 
				
			||||||
 | 
					            simple scenario description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @TAG3
 | 
				
			||||||
 | 
					        Examples: simple examples
 | 
				
			||||||
 | 
					        | status |
 | 
				
			||||||
 | 
					        | pass   |
 | 
				
			||||||
 | 
					        | fail   |
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    When I run feature suite with formatter "cucumber"
 | 
				
			||||||
 | 
					    Then the rendered json will be as follows:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "uri": "features/simple.feature",
 | 
				
			||||||
 | 
					            "id": "simple-feature",
 | 
				
			||||||
 | 
					            "keyword": "Feature",
 | 
				
			||||||
 | 
					            "name": "simple feature",
 | 
				
			||||||
 | 
					            "description": "        simple feature description",
 | 
				
			||||||
 | 
					            "line": 2,
 | 
				
			||||||
 | 
					            "tags": [
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                "name": "@TAG1",
 | 
				
			||||||
 | 
					                "line": 1
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "elements": [
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                "id": "simple-feature;simple-scenario;simple-examples;2",
 | 
				
			||||||
 | 
					                "keyword": "Scenario Outline",
 | 
				
			||||||
 | 
					                "name": "simple scenario",
 | 
				
			||||||
 | 
					                "description": "        simple scenario description",
 | 
				
			||||||
 | 
					                "line": 12,
 | 
				
			||||||
 | 
					                "type": "scenario",
 | 
				
			||||||
 | 
					                "tags": [
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                    "name": "@TAG1",
 | 
				
			||||||
 | 
					                    "line": 1
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                    "name": "@TAG2",
 | 
				
			||||||
 | 
					                    "line": 5
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                    "name": "@TAG3",
 | 
				
			||||||
 | 
					                    "line": 9
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                "id": "simple-feature;simple-scenario;simple-examples;3",
 | 
				
			||||||
 | 
					                "keyword": "Scenario Outline",
 | 
				
			||||||
 | 
					                "name": "simple scenario",
 | 
				
			||||||
 | 
					                "description": "        simple scenario description",
 | 
				
			||||||
 | 
					                "line": 13,
 | 
				
			||||||
 | 
					                "type": "scenario",
 | 
				
			||||||
 | 
					                "tags": [
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                    "name": "@TAG1",
 | 
				
			||||||
 | 
					                    "line": 1
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                    "name": "@TAG2",
 | 
				
			||||||
 | 
					                    "line": 5
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                    "name": "@TAG3",
 | 
				
			||||||
 | 
					                    "line": 9
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					  Scenario: Support of Feature Plus Scenario With Steps
 | 
				
			||||||
 | 
					    Given a feature "features/simple.feature" file:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					        Feature: simple feature
 | 
				
			||||||
 | 
					            simple feature description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Scenario: simple scenario
 | 
				
			||||||
 | 
					            simple scenario description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Given passing step
 | 
				
			||||||
 | 
					        Then a failing step
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    When I run feature suite with formatter "cucumber"
 | 
				
			||||||
 | 
					    Then the rendered json will be as follows:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					      [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "uri": "features/simple.feature",
 | 
				
			||||||
 | 
					          "id": "simple-feature",
 | 
				
			||||||
 | 
					          "keyword": "Feature",
 | 
				
			||||||
 | 
					          "name": "simple feature",
 | 
				
			||||||
 | 
					          "description": "        simple feature description",
 | 
				
			||||||
 | 
					          "line": 1,
 | 
				
			||||||
 | 
					          "elements": [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "id": "simple-feature;simple-scenario",
 | 
				
			||||||
 | 
					              "keyword": "Scenario",
 | 
				
			||||||
 | 
					              "name": "simple scenario",
 | 
				
			||||||
 | 
					              "description": "        simple scenario description",
 | 
				
			||||||
 | 
					              "line": 4,
 | 
				
			||||||
 | 
					              "type": "scenario",
 | 
				
			||||||
 | 
					              "steps": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  "keyword": "Given ",
 | 
				
			||||||
 | 
					                  "name": "passing step",
 | 
				
			||||||
 | 
					                  "line": 7,
 | 
				
			||||||
 | 
					                  "match": {
 | 
				
			||||||
 | 
					                    "location": "STEP_ID"
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  "result": {
 | 
				
			||||||
 | 
					                    "status": "passed",
 | 
				
			||||||
 | 
					                    "duration": -1
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  "keyword": "Then ",
 | 
				
			||||||
 | 
					                  "name": "a failing step",
 | 
				
			||||||
 | 
					                  "line": 8,
 | 
				
			||||||
 | 
					                  "match": {
 | 
				
			||||||
 | 
					                    "location": "STEP_ID"
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  "result": {
 | 
				
			||||||
 | 
					                    "status": "failed",
 | 
				
			||||||
 | 
					                    "error_message": "intentional failure",
 | 
				
			||||||
 | 
					                    "duration": -1
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              ]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					  Scenario: Support of Feature Plus Scenario Outline With Steps
 | 
				
			||||||
 | 
					    Given a feature "features/simple.feature" file:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					      Feature: simple feature
 | 
				
			||||||
 | 
					        simple feature description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Scenario Outline: simple scenario
 | 
				
			||||||
 | 
					        simple scenario description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          Given <status> step
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Examples: simple examples
 | 
				
			||||||
 | 
					        | status |
 | 
				
			||||||
 | 
					        | passing |
 | 
				
			||||||
 | 
					        | failing |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    When I run feature suite with formatter "cucumber"
 | 
				
			||||||
 | 
					    Then the rendered json will be as follows:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					      [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "uri": "features/simple.feature",
 | 
				
			||||||
 | 
					          "id": "simple-feature",
 | 
				
			||||||
 | 
					          "keyword": "Feature",
 | 
				
			||||||
 | 
					          "name": "simple feature",
 | 
				
			||||||
 | 
					          "description": "    simple feature description",
 | 
				
			||||||
 | 
					          "line": 1,
 | 
				
			||||||
 | 
					          "elements": [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "id": "simple-feature;simple-scenario;simple-examples;2",
 | 
				
			||||||
 | 
					              "keyword": "Scenario Outline",
 | 
				
			||||||
 | 
					              "name": "simple scenario",
 | 
				
			||||||
 | 
					              "description": "    simple scenario description",
 | 
				
			||||||
 | 
					              "line": 11,
 | 
				
			||||||
 | 
					              "type": "scenario",
 | 
				
			||||||
 | 
					              "steps": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  "keyword": "Given ",
 | 
				
			||||||
 | 
					                  "name": "passing step",
 | 
				
			||||||
 | 
					                  "line": 11,
 | 
				
			||||||
 | 
					                  "match": {
 | 
				
			||||||
 | 
					                    "location": "STEP_ID"
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  "result": {
 | 
				
			||||||
 | 
					                    "status": "passed",
 | 
				
			||||||
 | 
					                    "duration": -1
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              ]
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "id": "simple-feature;simple-scenario;simple-examples;3",
 | 
				
			||||||
 | 
					              "keyword": "Scenario Outline",
 | 
				
			||||||
 | 
					              "name": "simple scenario",
 | 
				
			||||||
 | 
					              "description": "    simple scenario description",
 | 
				
			||||||
 | 
					              "line": 12,
 | 
				
			||||||
 | 
					              "type": "scenario",
 | 
				
			||||||
 | 
					              "steps": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  "keyword": "Given ",
 | 
				
			||||||
 | 
					                  "name": "failing step",
 | 
				
			||||||
 | 
					                  "line": 12,
 | 
				
			||||||
 | 
					                  "match": {
 | 
				
			||||||
 | 
					                    "location": "STEP_ID"
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  "result": {
 | 
				
			||||||
 | 
					                    "status": "failed",
 | 
				
			||||||
 | 
					                    "error_message": "intentional failure",
 | 
				
			||||||
 | 
					                    "duration": -1
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              ]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Currently godog only supports comments on Feature and not
 | 
				
			||||||
 | 
					  # scenario and steps.
 | 
				
			||||||
 | 
					  Scenario: Support of Comments
 | 
				
			||||||
 | 
					    Given a feature "features/simple.feature" file:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					        #Feature comment
 | 
				
			||||||
 | 
					        Feature: simple feature
 | 
				
			||||||
 | 
					          simple description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          Scenario: simple scenario
 | 
				
			||||||
 | 
					          simple feature description
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    When I run feature suite with formatter "cucumber"
 | 
				
			||||||
 | 
					    Then the rendered json will be as follows:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "uri": "features/simple.feature",
 | 
				
			||||||
 | 
					            "id": "simple-feature",
 | 
				
			||||||
 | 
					            "keyword": "Feature",
 | 
				
			||||||
 | 
					            "name": "simple feature",
 | 
				
			||||||
 | 
					            "description": "      simple description",
 | 
				
			||||||
 | 
					            "line": 2,
 | 
				
			||||||
 | 
					            "comments": [
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                "value": "#Feature comment",
 | 
				
			||||||
 | 
					                "line": 1
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "elements": [
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                "id": "simple-feature;simple-scenario",
 | 
				
			||||||
 | 
					                "keyword": "Scenario",
 | 
				
			||||||
 | 
					                "name": "simple scenario",
 | 
				
			||||||
 | 
					                "description": "      simple feature description",
 | 
				
			||||||
 | 
					                "line": 5,
 | 
				
			||||||
 | 
					                "type": "scenario"
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					  Scenario: Support of Docstrings
 | 
				
			||||||
 | 
					    Given a feature "features/simple.feature" file:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					        Feature: simple feature
 | 
				
			||||||
 | 
					          simple description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          Scenario: simple scenario
 | 
				
			||||||
 | 
					          simple feature description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          Given passing step
 | 
				
			||||||
 | 
					          \"\"\" content type
 | 
				
			||||||
 | 
					          step doc string
 | 
				
			||||||
 | 
					          \"\"\"
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    When I run feature suite with formatter "cucumber"
 | 
				
			||||||
 | 
					    Then the rendered json will be as follows:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "uri": "features/simple.feature",
 | 
				
			||||||
 | 
					        "id": "simple-feature",
 | 
				
			||||||
 | 
					        "keyword": "Feature",
 | 
				
			||||||
 | 
					        "name": "simple feature",
 | 
				
			||||||
 | 
					        "description": "      simple description",
 | 
				
			||||||
 | 
					        "line": 1,
 | 
				
			||||||
 | 
					        "elements": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "id": "simple-feature;simple-scenario",
 | 
				
			||||||
 | 
					            "keyword": "Scenario",
 | 
				
			||||||
 | 
					            "name": "simple scenario",
 | 
				
			||||||
 | 
					            "description": "      simple feature description",
 | 
				
			||||||
 | 
					            "line": 4,
 | 
				
			||||||
 | 
					            "type": "scenario",
 | 
				
			||||||
 | 
					            "steps": [
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                "keyword": "Given ",
 | 
				
			||||||
 | 
					                "name": "passing step",
 | 
				
			||||||
 | 
					                "line": 7,
 | 
				
			||||||
 | 
					                "doc_string": {
 | 
				
			||||||
 | 
					                  "value": "step doc string",
 | 
				
			||||||
 | 
					                  "content_type": "content type",
 | 
				
			||||||
 | 
					                  "line": 8
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "match": {
 | 
				
			||||||
 | 
					                  "location": "STEP_ID"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "result": {
 | 
				
			||||||
 | 
					                  "status": "passed",
 | 
				
			||||||
 | 
					                  "duration": -1
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					  Scenario: Support of Undefined, Pending and Skipped status
 | 
				
			||||||
 | 
					    Given a feature "features/simple.feature" file:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					      Feature: simple feature
 | 
				
			||||||
 | 
					      simple feature description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Scenario: simple scenario
 | 
				
			||||||
 | 
					      simple scenario description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Given passing step
 | 
				
			||||||
 | 
					        And pending step
 | 
				
			||||||
 | 
					        And undefined
 | 
				
			||||||
 | 
					        And passing step
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    When I run feature suite with formatter "cucumber"
 | 
				
			||||||
 | 
					    Then the rendered json will be as follows:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					      [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "uri": "features/simple.feature",
 | 
				
			||||||
 | 
					          "id": "simple-feature",
 | 
				
			||||||
 | 
					          "keyword": "Feature",
 | 
				
			||||||
 | 
					          "name": "simple feature",
 | 
				
			||||||
 | 
					          "description": "  simple feature description",
 | 
				
			||||||
 | 
					          "line": 1,
 | 
				
			||||||
 | 
					          "elements": [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "id": "simple-feature;simple-scenario",
 | 
				
			||||||
 | 
					              "keyword": "Scenario",
 | 
				
			||||||
 | 
					              "name": "simple scenario",
 | 
				
			||||||
 | 
					              "description": "  simple scenario description",
 | 
				
			||||||
 | 
					              "line": 4,
 | 
				
			||||||
 | 
					              "type": "scenario",
 | 
				
			||||||
 | 
					              "steps": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  "keyword": "Given ",
 | 
				
			||||||
 | 
					                  "name": "passing step",
 | 
				
			||||||
 | 
					                  "line": 7,
 | 
				
			||||||
 | 
					                  "match": {
 | 
				
			||||||
 | 
					                    "location": "STEP_ID"
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  "result": {
 | 
				
			||||||
 | 
					                    "status": "passed",
 | 
				
			||||||
 | 
					                    "duration": 397087
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  "keyword": "And ",
 | 
				
			||||||
 | 
					                  "name": "pending step",
 | 
				
			||||||
 | 
					                  "line": 8,
 | 
				
			||||||
 | 
					                  "match": {
 | 
				
			||||||
 | 
					                    "location": "FEATURE_PATH features/simple.feature:8"
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  "result": {
 | 
				
			||||||
 | 
					                    "status": "pending"
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  "keyword": "And ",
 | 
				
			||||||
 | 
					                  "name": "undefined",
 | 
				
			||||||
 | 
					                  "line": 9,
 | 
				
			||||||
 | 
					                  "match": {
 | 
				
			||||||
 | 
					                    "location": "FEATURE_PATH features/simple.feature:9"
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  "result": {
 | 
				
			||||||
 | 
					                    "status": "undefined"
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  "keyword": "And ",
 | 
				
			||||||
 | 
					                  "name": "passing step",
 | 
				
			||||||
 | 
					                  "line": 10,
 | 
				
			||||||
 | 
					                  "match": {
 | 
				
			||||||
 | 
					                    "location": "STEP_ID"
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  "result": {
 | 
				
			||||||
 | 
					                    "status": "skipped"
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              ]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ Feature: event stream formatter
 | 
				
			||||||
      """
 | 
					      """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Scenario: should process simple scenario
 | 
					  Scenario: should process simple scenario
 | 
				
			||||||
    Given a feature path "features/load.feature:21"
 | 
					    Given a feature path "features/load.feature:22"
 | 
				
			||||||
    When I run feature suite with formatter "events"
 | 
					    When I run feature suite with formatter "events"
 | 
				
			||||||
    Then the following events should be fired:
 | 
					    Then the following events should be fired:
 | 
				
			||||||
      """
 | 
					      """
 | 
				
			||||||
| 
						 | 
					@ -35,7 +35,7 @@ Feature: event stream formatter
 | 
				
			||||||
      """
 | 
					      """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Scenario: should process outline scenario
 | 
					  Scenario: should process outline scenario
 | 
				
			||||||
    Given a feature path "features/load.feature:29"
 | 
					    Given a feature path "features/load.feature:30"
 | 
				
			||||||
    When I run feature suite with formatter "events"
 | 
					    When I run feature suite with formatter "events"
 | 
				
			||||||
    Then the following events should be fired:
 | 
					    Then the following events should be fired:
 | 
				
			||||||
      """
 | 
					      """
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,10 +8,11 @@ Savybė: užkrauti savybes
 | 
				
			||||||
  Scenarijus: savybių užkrovimas iš aplanko
 | 
					  Scenarijus: savybių užkrovimas iš aplanko
 | 
				
			||||||
    Duota savybių aplankas "features"
 | 
					    Duota savybių aplankas "features"
 | 
				
			||||||
    Kai aš išskaitau savybes
 | 
					    Kai aš išskaitau savybes
 | 
				
			||||||
    Tada aš turėčiau turėti 8 savybių failus:
 | 
					    Tada aš turėčiau turėti 9 savybių failus:
 | 
				
			||||||
      """
 | 
					      """
 | 
				
			||||||
      features/background.feature
 | 
					      features/background.feature
 | 
				
			||||||
      features/events.feature
 | 
					      features/events.feature
 | 
				
			||||||
 | 
					      features/formatter/cucumber.feature
 | 
				
			||||||
      features/formatter/events.feature
 | 
					      features/formatter/events.feature
 | 
				
			||||||
      features/lang.feature
 | 
					      features/lang.feature
 | 
				
			||||||
      features/load.feature
 | 
					      features/load.feature
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,10 +6,11 @@ Feature: load features
 | 
				
			||||||
  Scenario: load features within path
 | 
					  Scenario: load features within path
 | 
				
			||||||
    Given a feature path "features"
 | 
					    Given a feature path "features"
 | 
				
			||||||
    When I parse features
 | 
					    When I parse features
 | 
				
			||||||
    Then I should have 8 feature files:
 | 
					    Then I should have 9 feature files:
 | 
				
			||||||
      """
 | 
					      """
 | 
				
			||||||
      features/background.feature
 | 
					      features/background.feature
 | 
				
			||||||
      features/events.feature
 | 
					      features/events.feature
 | 
				
			||||||
 | 
					      features/formatter/cucumber.feature
 | 
				
			||||||
      features/formatter/events.feature
 | 
					      features/formatter/events.feature
 | 
				
			||||||
      features/lang.feature
 | 
					      features/lang.feature
 | 
				
			||||||
      features/load.feature
 | 
					      features/load.feature
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										333
									
								
								fmt_cucumber.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										333
									
								
								fmt_cucumber.go
									
										
									
									
									
										Обычный файл
									
								
							| 
						 | 
					@ -0,0 +1,333 @@
 | 
				
			||||||
 | 
					package godog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   The specification for the formatting originated from https://www.relishapp.com/cucumber/cucumber/docs/formatters/json-output-formatter.
 | 
				
			||||||
 | 
					   I found that documentation was misleading or out dated.  To validate formatting I create a ruby cucumber test harness and ran the
 | 
				
			||||||
 | 
					   same feature files through godog and the ruby cucumber.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   The docstrings in the cucumber.feature represent the cucumber output for those same feature definitions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   I did note that comments in ruby could be at just about any level in particular Feature, Scenario and Step.  In godog I
 | 
				
			||||||
 | 
					   could only find comments under the Feature data structure.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/DATA-DOG/godog/gherkin"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const cukeurl = "https://www.relishapp.com/cucumber/cucumber/docs/formatters/json-output-formatter"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						Format("cucumber", fmt.Sprintf("Produces cucumber JSON stream, based on spec @: %s.", cukeurl), cucumberFunc)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func cucumberFunc(suite string, out io.Writer) Formatter {
 | 
				
			||||||
 | 
						formatter := &cukefmt{
 | 
				
			||||||
 | 
							basefmt: basefmt{
 | 
				
			||||||
 | 
								started: time.Now(),
 | 
				
			||||||
 | 
								indent:  2,
 | 
				
			||||||
 | 
								out:     out,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return formatter
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Replace spaces with - This function is used to create the "id" fields of the cucumber output.
 | 
				
			||||||
 | 
					func makeID(name string) string {
 | 
				
			||||||
 | 
						return strings.Replace(strings.ToLower(name), " ", "-", -1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The sequence of type structs are used to marshall the json object.
 | 
				
			||||||
 | 
					type cukeComment struct {
 | 
				
			||||||
 | 
						Value string `json:"value"`
 | 
				
			||||||
 | 
						Line  int    `json:"line"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type cukeDocstring struct {
 | 
				
			||||||
 | 
						Value       string `json:"value"`
 | 
				
			||||||
 | 
						ContentType string `json:"content_type"`
 | 
				
			||||||
 | 
						Line        int    `json:"line"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type cukeTag struct {
 | 
				
			||||||
 | 
						Name string `json:"name"`
 | 
				
			||||||
 | 
						Line int    `json:"line"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type cukeResult struct {
 | 
				
			||||||
 | 
						Status   string `json:"status"`
 | 
				
			||||||
 | 
						Error    string `json:"error_message,omitempty"`
 | 
				
			||||||
 | 
						Duration *int   `json:"duration,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type cukeMatch struct {
 | 
				
			||||||
 | 
						Location string `json:"location"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type cukeStep struct {
 | 
				
			||||||
 | 
						Keyword   string         `json:"keyword"`
 | 
				
			||||||
 | 
						Name      string         `json:"name"`
 | 
				
			||||||
 | 
						Line      int            `json:"line"`
 | 
				
			||||||
 | 
						Docstring *cukeDocstring `json:"doc_string,omitempty"`
 | 
				
			||||||
 | 
						Match     cukeMatch      `json:"match"`
 | 
				
			||||||
 | 
						Result    cukeResult     `json:"result"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type cukeElement struct {
 | 
				
			||||||
 | 
						ID          string     `json:"id"`
 | 
				
			||||||
 | 
						Keyword     string     `json:"keyword"`
 | 
				
			||||||
 | 
						Name        string     `json:"name"`
 | 
				
			||||||
 | 
						Description string     `json:"description"`
 | 
				
			||||||
 | 
						Line        int        `json:"line"`
 | 
				
			||||||
 | 
						Type        string     `json:"type"`
 | 
				
			||||||
 | 
						Tags        []cukeTag  `json:"tags,omitempty"`
 | 
				
			||||||
 | 
						Steps       []cukeStep `json:"steps,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type cukeFeatureJSON struct {
 | 
				
			||||||
 | 
						URI         string        `json:"uri"`
 | 
				
			||||||
 | 
						ID          string        `json:"id"`
 | 
				
			||||||
 | 
						Keyword     string        `json:"keyword"`
 | 
				
			||||||
 | 
						Name        string        `json:"name"`
 | 
				
			||||||
 | 
						Description string        `json:"description"`
 | 
				
			||||||
 | 
						Line        int           `json:"line"`
 | 
				
			||||||
 | 
						Comments    []cukeComment `json:"comments,omitempty"`
 | 
				
			||||||
 | 
						Tags        []cukeTag     `json:"tags,omitempty"`
 | 
				
			||||||
 | 
						Elements    []cukeElement `json:"elements,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type cukefmt struct {
 | 
				
			||||||
 | 
						basefmt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// currently running feature path, to be part of id.
 | 
				
			||||||
 | 
						// this is sadly not passed by gherkin nodes.
 | 
				
			||||||
 | 
						// it restricts this formatter to run only in synchronous single
 | 
				
			||||||
 | 
						// threaded execution. Unless running a copy of formatter for each feature
 | 
				
			||||||
 | 
						path         string
 | 
				
			||||||
 | 
						stat         stepType          // last step status, before skipped
 | 
				
			||||||
 | 
						outlineSteps int               // number of current outline scenario steps
 | 
				
			||||||
 | 
						ID           string            // current test id.
 | 
				
			||||||
 | 
						results      []cukeFeatureJSON // structure that represent cuke results
 | 
				
			||||||
 | 
						curStep      *cukeStep         // track the current step
 | 
				
			||||||
 | 
						curElement   *cukeElement      // track the current element
 | 
				
			||||||
 | 
						curFeature   *cukeFeatureJSON  // track the current feature
 | 
				
			||||||
 | 
						curOutline   cukeElement       // Each example show up as an outline element but the outline is parsed only once
 | 
				
			||||||
 | 
						// so I need to keep track of the current outline
 | 
				
			||||||
 | 
						curRow         int       // current row of the example table as it is being processed.
 | 
				
			||||||
 | 
						curExampleTags []cukeTag // temporary storage for tags associate with the current example table.
 | 
				
			||||||
 | 
						startTime      time.Time // used to time duration of the step execution
 | 
				
			||||||
 | 
						curExampleName string    // Due to the fact that examples are parsed once and then iterated over for each result then we need to keep track
 | 
				
			||||||
 | 
						// of the example name inorder to build id fields.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *cukefmt) Node(n interface{}) {
 | 
				
			||||||
 | 
						f.basefmt.Node(n)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch t := n.(type) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// When the example definition is seen we just need track the id and
 | 
				
			||||||
 | 
						// append the name associated with the example as part of the id.
 | 
				
			||||||
 | 
						case *gherkin.Examples:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							f.curExampleName = makeID(t.Name)
 | 
				
			||||||
 | 
							f.curRow = 2 // there can be more than one example set per outline so reset row count.
 | 
				
			||||||
 | 
							// cucumber counts the header row as an example when creating the id.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// store any example level tags in a  temp location.
 | 
				
			||||||
 | 
							f.curExampleTags = make([]cukeTag, len(t.Tags))
 | 
				
			||||||
 | 
							for idx, element := range t.Tags {
 | 
				
			||||||
 | 
								f.curExampleTags[idx].Line = element.Location.Line
 | 
				
			||||||
 | 
								f.curExampleTags[idx].Name = element.Name
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The outline node creates a placeholder and the actual element is added as each TableRow is processed.
 | 
				
			||||||
 | 
						case *gherkin.ScenarioOutline:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							f.curOutline = cukeElement{}
 | 
				
			||||||
 | 
							f.curOutline.Name = t.Name
 | 
				
			||||||
 | 
							f.curOutline.Line = t.Location.Line
 | 
				
			||||||
 | 
							f.curOutline.Description = t.Description
 | 
				
			||||||
 | 
							f.curOutline.Keyword = t.Keyword
 | 
				
			||||||
 | 
							f.curOutline.ID = f.curFeature.ID + ";" + makeID(t.Name)
 | 
				
			||||||
 | 
							f.curOutline.Type = "scenario"
 | 
				
			||||||
 | 
							f.curOutline.Tags = make([]cukeTag, len(t.Tags)+len(f.curFeature.Tags))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// apply feature level tags
 | 
				
			||||||
 | 
							if len(f.curOutline.Tags) > 0 {
 | 
				
			||||||
 | 
								copy(f.curOutline.Tags, f.curFeature.Tags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// apply outline level tags.
 | 
				
			||||||
 | 
								for idx, element := range t.Tags {
 | 
				
			||||||
 | 
									f.curOutline.Tags[idx+len(f.curFeature.Tags)].Line = element.Location.Line
 | 
				
			||||||
 | 
									f.curOutline.Tags[idx+len(f.curFeature.Tags)].Name = element.Name
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This scenario adds the element to the output immediately.
 | 
				
			||||||
 | 
						case *gherkin.Scenario:
 | 
				
			||||||
 | 
							f.curFeature.Elements = append(f.curFeature.Elements, cukeElement{})
 | 
				
			||||||
 | 
							f.curElement = &f.curFeature.Elements[len(f.curFeature.Elements)-1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							f.curElement.Name = t.Name
 | 
				
			||||||
 | 
							f.curElement.Line = t.Location.Line
 | 
				
			||||||
 | 
							f.curElement.Description = t.Description
 | 
				
			||||||
 | 
							f.curElement.Keyword = t.Keyword
 | 
				
			||||||
 | 
							f.curElement.ID = f.curFeature.ID + ";" + makeID(t.Name)
 | 
				
			||||||
 | 
							f.curElement.Type = "scenario"
 | 
				
			||||||
 | 
							f.curElement.Tags = make([]cukeTag, len(t.Tags)+len(f.curFeature.Tags))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(f.curElement.Tags) > 0 {
 | 
				
			||||||
 | 
								// apply feature level tags
 | 
				
			||||||
 | 
								copy(f.curElement.Tags, f.curFeature.Tags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// apply scenario level tags.
 | 
				
			||||||
 | 
								for idx, element := range t.Tags {
 | 
				
			||||||
 | 
									f.curElement.Tags[idx+len(f.curFeature.Tags)].Line = element.Location.Line
 | 
				
			||||||
 | 
									f.curElement.Tags[idx+len(f.curFeature.Tags)].Name = element.Name
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This is an outline scenario and the element is added to the output as
 | 
				
			||||||
 | 
						// the TableRows are encountered.
 | 
				
			||||||
 | 
						case *gherkin.TableRow:
 | 
				
			||||||
 | 
							tmpElem := f.curOutline
 | 
				
			||||||
 | 
							tmpElem.Line = t.Location.Line
 | 
				
			||||||
 | 
							tmpElem.ID = tmpElem.ID + ";" + f.curExampleName + ";" + strconv.Itoa(f.curRow)
 | 
				
			||||||
 | 
							f.curRow++
 | 
				
			||||||
 | 
							f.curFeature.Elements = append(f.curFeature.Elements, tmpElem)
 | 
				
			||||||
 | 
							f.curElement = &f.curFeature.Elements[len(f.curFeature.Elements)-1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// copy in example level tags.
 | 
				
			||||||
 | 
							f.curElement.Tags = append(f.curElement.Tags, f.curExampleTags...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *cukefmt) Feature(ft *gherkin.Feature, p string, c []byte) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f.basefmt.Feature(ft, p, c)
 | 
				
			||||||
 | 
						f.path = p
 | 
				
			||||||
 | 
						f.ID = makeID(ft.Name)
 | 
				
			||||||
 | 
						f.results = append(f.results, cukeFeatureJSON{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f.curFeature = &f.results[len(f.results)-1]
 | 
				
			||||||
 | 
						f.curFeature.URI = p
 | 
				
			||||||
 | 
						f.curFeature.Name = ft.Name
 | 
				
			||||||
 | 
						f.curFeature.Keyword = ft.Keyword
 | 
				
			||||||
 | 
						f.curFeature.Line = ft.Location.Line
 | 
				
			||||||
 | 
						f.curFeature.Description = ft.Description
 | 
				
			||||||
 | 
						f.curFeature.ID = f.ID
 | 
				
			||||||
 | 
						f.curFeature.Tags = make([]cukeTag, len(ft.Tags))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for idx, element := range ft.Tags {
 | 
				
			||||||
 | 
							f.curFeature.Tags[idx].Line = element.Location.Line
 | 
				
			||||||
 | 
							f.curFeature.Tags[idx].Name = element.Name
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f.curFeature.Comments = make([]cukeComment, len(ft.Comments))
 | 
				
			||||||
 | 
						for idx, comment := range ft.Comments {
 | 
				
			||||||
 | 
							f.curFeature.Comments[idx].Value = strings.TrimSpace(comment.Text)
 | 
				
			||||||
 | 
							f.curFeature.Comments[idx].Line = comment.Location.Line
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *cukefmt) Summary() {
 | 
				
			||||||
 | 
						dat, err := json.MarshalIndent(f.results, "", "    ")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fmt.Fprintf(f.out, "%s\n", string(dat))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *cukefmt) step(res *stepResult) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// determine if test case has finished
 | 
				
			||||||
 | 
						switch t := f.owner.(type) {
 | 
				
			||||||
 | 
						case *gherkin.TableRow:
 | 
				
			||||||
 | 
							d := int(time.Since(f.startTime).Nanoseconds())
 | 
				
			||||||
 | 
							f.curStep.Result.Duration = &d
 | 
				
			||||||
 | 
							f.curStep.Line = t.Location.Line
 | 
				
			||||||
 | 
							f.curStep.Result.Status = res.typ.String()
 | 
				
			||||||
 | 
							if res.err != nil {
 | 
				
			||||||
 | 
								f.curStep.Result.Error = res.err.Error()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case *gherkin.Scenario:
 | 
				
			||||||
 | 
							d := int(time.Since(f.startTime).Nanoseconds())
 | 
				
			||||||
 | 
							f.curStep.Result.Duration = &d
 | 
				
			||||||
 | 
							f.curStep.Result.Status = res.typ.String()
 | 
				
			||||||
 | 
							if res.err != nil {
 | 
				
			||||||
 | 
								f.curStep.Result.Error = res.err.Error()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *cukefmt) Defined(step *gherkin.Step, def *StepDef) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f.startTime = time.Now() // start timing the step
 | 
				
			||||||
 | 
						f.curElement.Steps = append(f.curElement.Steps, cukeStep{})
 | 
				
			||||||
 | 
						f.curStep = &f.curElement.Steps[len(f.curElement.Steps)-1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f.curStep.Name = step.Text
 | 
				
			||||||
 | 
						f.curStep.Line = step.Location.Line
 | 
				
			||||||
 | 
						f.curStep.Keyword = step.Keyword
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, ok := step.Argument.(*gherkin.DocString); ok {
 | 
				
			||||||
 | 
							f.curStep.Docstring = &cukeDocstring{}
 | 
				
			||||||
 | 
							f.curStep.Docstring.ContentType = strings.TrimSpace(step.Argument.(*gherkin.DocString).ContentType)
 | 
				
			||||||
 | 
							f.curStep.Docstring.Line = step.Argument.(*gherkin.DocString).Location.Line
 | 
				
			||||||
 | 
							f.curStep.Docstring.Value = step.Argument.(*gherkin.DocString).Content
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if def != nil {
 | 
				
			||||||
 | 
							f.curStep.Match.Location = strings.Split(def.definitionID(), " ")[0]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *cukefmt) Passed(step *gherkin.Step, match *StepDef) {
 | 
				
			||||||
 | 
						f.basefmt.Passed(step, match)
 | 
				
			||||||
 | 
						f.stat = passed
 | 
				
			||||||
 | 
						f.step(f.passed[len(f.passed)-1])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *cukefmt) Skipped(step *gherkin.Step) {
 | 
				
			||||||
 | 
						f.basefmt.Skipped(step)
 | 
				
			||||||
 | 
						f.step(f.skipped[len(f.skipped)-1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// no duration reported for skipped.
 | 
				
			||||||
 | 
						f.curStep.Result.Duration = nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *cukefmt) Undefined(step *gherkin.Step) {
 | 
				
			||||||
 | 
						f.basefmt.Undefined(step)
 | 
				
			||||||
 | 
						f.stat = undefined
 | 
				
			||||||
 | 
						f.step(f.undefined[len(f.undefined)-1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// the location for undefined is the feature file location not the step file.
 | 
				
			||||||
 | 
						f.curStep.Match.Location = fmt.Sprintf("%s:%d", f.path, step.Location.Line)
 | 
				
			||||||
 | 
						f.curStep.Result.Duration = nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *cukefmt) Failed(step *gherkin.Step, match *StepDef, err error) {
 | 
				
			||||||
 | 
						f.basefmt.Failed(step, match, err)
 | 
				
			||||||
 | 
						f.stat = failed
 | 
				
			||||||
 | 
						f.step(f.failed[len(f.failed)-1])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *cukefmt) Pending(step *gherkin.Step, match *StepDef) {
 | 
				
			||||||
 | 
						f.stat = pending
 | 
				
			||||||
 | 
						f.basefmt.Pending(step, match)
 | 
				
			||||||
 | 
						f.step(f.pending[len(f.pending)-1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// the location for pending is the feature file location not the step file.
 | 
				
			||||||
 | 
						f.curStep.Match.Location = fmt.Sprintf("%s:%d", f.path, step.Location.Line)
 | 
				
			||||||
 | 
						f.curStep.Result.Duration = nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								run.go
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								run.go
									
										
									
									
									
								
							| 
						 | 
					@ -157,6 +157,9 @@ func supportsConcurrency(format string) bool {
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	case "pretty":
 | 
						case "pretty":
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
 | 
						case "cucumber":
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true // all custom formatters are treated as supporting concurrency
 | 
						return true // all custom formatters are treated as supporting concurrency
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										163
									
								
								suite_test.go
									
										
									
									
									
								
							
							
						
						
									
										163
									
								
								suite_test.go
									
										
									
									
									
								
							| 
						 | 
					@ -4,13 +4,13 @@ import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/DATA-DOG/godog/gherkin"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/DATA-DOG/godog/gherkin"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestMain(m *testing.M) {
 | 
					func TestMain(m *testing.M) {
 | 
				
			||||||
| 
						 | 
					@ -66,6 +66,15 @@ func SuiteContext(s *Suite) {
 | 
				
			||||||
	s.Step(`^passing step$`, func() error {
 | 
						s.Step(`^passing step$`, func() error {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// duplicate step to 'a failing step' I added to help test cucumber.feature
 | 
				
			||||||
 | 
						// I needed to have an Scenario Outline where the status was passing or failing
 | 
				
			||||||
 | 
						// I needed the same step def language.
 | 
				
			||||||
 | 
						s.Step(`^failing step$`, c.aFailingStep)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Introduced to test formatter/cucumber.feature
 | 
				
			||||||
 | 
						s.Step(`^the rendered json will be as follows:$`, c.theRenderJSONWillBe)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type firedEvent struct {
 | 
					type firedEvent struct {
 | 
				
			||||||
| 
						 | 
					@ -185,7 +194,7 @@ func (s *suiteContext) followingStepsShouldHave(status string, steps *gherkin.Do
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(expected) > len(actual) {
 | 
						if len(expected) > len(actual) {
 | 
				
			||||||
		return fmt.Errorf("number of expected %s steps: %d is less than actual %s steps: %d", status, len(expected), status, len(actual))
 | 
							return fmt.Errorf("number of expeted %s steps: %d is less than actual %s steps: %d", status, len(expected), status, len(actual))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, a := range actual {
 | 
						for _, a := range actual {
 | 
				
			||||||
| 
						 | 
					@ -376,3 +385,151 @@ func (s *suiteContext) theseEventsHadToBeFiredForNumberOfTimes(tbl *gherkin.Data
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *suiteContext) theRenderJSONWillBe(docstring *gherkin.DocString) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var expected interface{}
 | 
				
			||||||
 | 
						if err := json.Unmarshal([]byte(docstring.Content), &expected); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var actual interface{}
 | 
				
			||||||
 | 
						if err := json.Unmarshal(s.out.Bytes(), &actual); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						expectedArr := expected.([]interface{})
 | 
				
			||||||
 | 
						actualArr := actual.([]interface{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Created to use in error reporting.
 | 
				
			||||||
 | 
						expectedCompact := &bytes.Buffer{}
 | 
				
			||||||
 | 
						actualCompact := &bytes.Buffer{}
 | 
				
			||||||
 | 
						json.Compact(expectedCompact, []byte(docstring.Content))
 | 
				
			||||||
 | 
						json.Compact(actualCompact, s.out.Bytes())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for idx, entry := range expectedArr {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Make sure all of the expected are in the actual
 | 
				
			||||||
 | 
							if err := s.mapCompareStructure(entry.(map[string]interface{}), actualArr[idx].(map[string]interface{})); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("err:%v actual result is missing fields: expected:%s actual:%s", err, expectedCompact, actualCompact)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Make sure all of actual are in expected
 | 
				
			||||||
 | 
							if err := s.mapCompareStructure(actualArr[idx].(map[string]interface{}), entry.(map[string]interface{})); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("err:%v actual result contains too many fields: expected:%s actual:%s", err, expectedCompact, actualCompact)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Make sure the values are correct
 | 
				
			||||||
 | 
							if err := s.mapCompare(entry.(map[string]interface{}), actualArr[idx].(map[string]interface{})); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("err:%v values don't match expected:%s actual:%s", err, expectedCompact, actualCompact)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					  Due to specialize matching logic to ignore exact matches on the "location" and "duration" fields.  It was
 | 
				
			||||||
 | 
					  necessary to create this compare function to validate the values of the map.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					func (s *suiteContext) mapCompare(expected map[string]interface{}, actual map[string]interface{}) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Process all keys in the map and handle them based on the type of the field.
 | 
				
			||||||
 | 
						for k, v := range expected {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if actual[k] == nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("No matching field in actual:[%s] expected value:[%v]", k, v)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Process other maps via recursion
 | 
				
			||||||
 | 
							if reflect.TypeOf(v).Kind() == reflect.Map {
 | 
				
			||||||
 | 
								if err := s.mapCompare(v.(map[string]interface{}), actual[k].(map[string]interface{})); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// This is an array of maps show as a slice
 | 
				
			||||||
 | 
							} else if reflect.TypeOf(v).Kind() == reflect.Slice {
 | 
				
			||||||
 | 
								for i, e := range v.([]interface{}) {
 | 
				
			||||||
 | 
									if err := s.mapCompare(e.(map[string]interface{}), actual[k].([]interface{})[i].(map[string]interface{})); err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// We need special rules to check location so that we are not bound to version of the code.
 | 
				
			||||||
 | 
							} else if k == "location" {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// location is tricky.  the cucumber value is either a the step def location for passed,failed, and skipped.
 | 
				
			||||||
 | 
								// it is the feature file location for undefined and skipped.
 | 
				
			||||||
 | 
								// I dont have the result context readily available so the expected input will have
 | 
				
			||||||
 | 
								// the context i need contained within its value.
 | 
				
			||||||
 | 
								// FEATURE_PATH myfile.feature:20 or
 | 
				
			||||||
 | 
								// STEP_ID
 | 
				
			||||||
 | 
								t := strings.Split(v.(string), " ")
 | 
				
			||||||
 | 
								if t[0] == "FEATURE_PATH" {
 | 
				
			||||||
 | 
									if actual[k].(string) != t[1] {
 | 
				
			||||||
 | 
										return fmt.Errorf("location has unexpected value [%s] should be [%s]",
 | 
				
			||||||
 | 
											actual[k], t[1])
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								} else if t[0] == "STEP_ID" {
 | 
				
			||||||
 | 
									if !strings.Contains(actual[k].(string), "suite_test.go:") {
 | 
				
			||||||
 | 
										return fmt.Errorf("location has unexpected filename [%s] should contain suite_test.go",
 | 
				
			||||||
 | 
											actual[k])
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									return fmt.Errorf("Bad location value [%v]", v)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// We need special rules to validate duration too.
 | 
				
			||||||
 | 
							} else if k == "duration" {
 | 
				
			||||||
 | 
								if actual[k].(float64) <= 0 {
 | 
				
			||||||
 | 
									return fmt.Errorf("duration is <= zero: actual:[%v]", actual[k])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// default numbers in json are coming as float64
 | 
				
			||||||
 | 
							} else if reflect.TypeOf(v).Kind() == reflect.Float64 {
 | 
				
			||||||
 | 
								if v.(float64) != actual[k].(float64) {
 | 
				
			||||||
 | 
									if v.(float64) != actual[k].(float64) {
 | 
				
			||||||
 | 
										return fmt.Errorf("Field:[%s] not matching expected:[%v] actual:[%v]",
 | 
				
			||||||
 | 
											k, v, actual[k])
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							} else if reflect.TypeOf(v).Kind() == reflect.String {
 | 
				
			||||||
 | 
								if v.(string) != actual[k].(string) {
 | 
				
			||||||
 | 
									return fmt.Errorf("Field:[%s] not matching expected:[%v] actual:[%v]",
 | 
				
			||||||
 | 
										k, v, actual[k])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return fmt.Errorf("Unexepcted type encountered in json at key:[%s] Type:[%v]", k, reflect.TypeOf(v).Kind())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					  Due to specialize matching logic to ignore exact matches on the "location" and "duration" fields.  It was
 | 
				
			||||||
 | 
					  necessary to create this compare function to validate the values of the map.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					func (s *suiteContext) mapCompareStructure(expected map[string]interface{}, actual map[string]interface{}) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Process all keys in the map and handle them based on the type of the field.
 | 
				
			||||||
 | 
						for k, v := range expected {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if actual[k] == nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("Structure Mismatch: no matching field:[%s] expected value:[%v]", k, v)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Process other maps via recursion
 | 
				
			||||||
 | 
							if reflect.TypeOf(v).Kind() == reflect.Map {
 | 
				
			||||||
 | 
								if err := s.mapCompareStructure(v.(map[string]interface{}), actual[k].(map[string]interface{})); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// This is an array of maps show as a slice
 | 
				
			||||||
 | 
							} else if reflect.TypeOf(v).Kind() == reflect.Slice {
 | 
				
			||||||
 | 
								for i, e := range v.([]interface{}) {
 | 
				
			||||||
 | 
									if err := s.mapCompareStructure(e.(map[string]interface{}), actual[k].([]interface{})[i].(map[string]interface{})); err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче