├── test ├── ecukes-test.el ├── fixtures │ ├── feature │ │ ├── empty.feature │ │ ├── tags-none.feature │ │ ├── tags-single.feature │ │ ├── same-name.feature │ │ ├── tags-comment.feature │ │ ├── tags-multiple-tags.feature │ │ ├── tags-whitespace.feature │ │ ├── simple.feature │ │ ├── tags-multiple-scenarios.feature │ │ └── tags-multiple-mixed.feature │ ├── intro │ │ ├── no-intro.feature │ │ ├── fewer-description-lines.feature │ │ ├── all-good.feature │ │ ├── comments.feature │ │ ├── no-space-in-header.feature │ │ ├── wrong-indentation.feature │ │ ├── line-breaks.feature │ │ ├── extra-space-in-header.feature │ │ ├── more-description-lines.feature │ │ ├── spaces-above.feature │ │ ├── section-background.feature │ │ ├── section-scenario.feature │ │ ├── scenario-tags.feature │ │ └── comments-above.feature │ ├── scenarios │ │ ├── none.feature │ │ ├── empty.feature │ │ ├── all-good.feature │ │ ├── with-background.feature │ │ └── with-intro.feature │ ├── scenario │ │ ├── no-scenario.feature │ │ ├── tags-none.feature │ │ ├── tags-single.feature │ │ ├── same-name.feature │ │ ├── tags-comment.feature │ │ ├── tags-multiple.feature │ │ ├── same-step-names.feature │ │ ├── tags-whitespace.feature │ │ ├── missing-steps.feature │ │ ├── all-good.feature │ │ ├── no-space-in-header.feature │ │ ├── whitespace.feature │ │ ├── wrong-indentation.feature │ │ ├── comments.feature │ │ ├── line-breaks.feature │ │ ├── extra-space-in-header.feature │ │ ├── comment-breaks.feature │ │ ├── mix-py-string-table-regular.feature │ │ ├── mix-regular-py-string-table.feature │ │ └── mix-table-regular-py-string.feature │ ├── step │ │ ├── and.feature │ │ ├── but.feature │ │ ├── given.feature │ │ ├── then.feature │ │ ├── when.feature │ │ ├── table-single-row.feature │ │ ├── py-string-same-lines.feature │ │ ├── py-string-two-empty-lines.feature │ │ ├── table-same-row.feature │ │ ├── table-empty-columns.feature │ │ ├── table-multiple-rows.feature │ │ ├── table-wrong-indentation.feature │ │ ├── py-string-all-good.feature │ │ ├── py-string-line-break.feature │ │ └── py-string-wrong-indentation.feature │ ├── background │ │ ├── all-good.feature │ │ ├── comments.feature │ │ ├── line-breaks.feature │ │ ├── wrong-indentation.feature │ │ ├── comment-breaks.feature │ │ └── with-intro.feature │ ├── outlines │ │ ├── no-examples.feature │ │ ├── one-example.feature │ │ ├── wrong-column-names.feature │ │ ├── bad-indents.feature │ │ ├── tags.feature │ │ ├── background.feature │ │ ├── multiple-examples.feature │ │ └── substitution.feature │ └── block │ │ ├── all-good.feature │ │ ├── with-blank-lines.feature │ │ ├── mix.feature │ │ └── with-comments.feature ├── ecukes-helpers-test.el ├── ecukes-parse-feature-test.el ├── ecukes-parse-new-section-test.el ├── ecukes-template-test.el ├── ecukes-test ├── ecukes-project-test.el ├── ecukes-hooks-test.el ├── ecukes-parse-scenarios-test.el ├── ecukes-parse-line-test.el ├── ecukes-parse-scenario-tags-test.el ├── ecukes-parse-regular-step-test.el ├── ecukes-parse-table-step-test.el ├── ecukes-parse-py-string-step-test.el ├── ecukes-parse-background-test.el ├── ecukes-parse-scenario-test.el ├── ecukes-new-test.el ├── ecukes-parse-feature-tags-test.el ├── ecukes-parse-scenario-outline-test.el ├── test-helper.el ├── ecukes-parse-intro-test.el ├── ecukes-parse-step-test.el ├── ecukes-setup-test.el ├── ecukes-steps-test.el ├── ecukes-stats-test.el ├── ecukes-run-test.el └── ecukes-print-test.el ├── .gitignore ├── templates ├── missing-step.tpl ├── feature.tpl ├── step-definition.tpl ├── usage.tpl └── env.tpl ├── ecukes-pkg.el ├── Carton ├── Makefile ├── ecukes-term.el ├── ecukes-cli.el ├── ecukes-helpers.el ├── README.markdown ├── ecukes-project.el ├── ecukes-template.el ├── ecukes-hooks.el ├── ecukes-def.el ├── ecukes.el ├── ecukes ├── ecukes-new.el ├── ecukes-stats.el ├── ecukes-steps.el ├── ecukes-setup.el ├── ecukes-run.el ├── ecukes-print.el └── ecukes-parse.el /test/ecukes-test.el: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /elpa 2 | *.elc 3 | -------------------------------------------------------------------------------- /test/fixtures/feature/empty.feature: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/intro/no-intro.feature: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/scenarios/none.feature: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/scenario/no-scenario.feature: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/step/and.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | And another key action 3 | -------------------------------------------------------------------------------- /test/fixtures/step/but.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | But observe outcomes 3 | -------------------------------------------------------------------------------- /test/fixtures/step/given.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given a known state 3 | -------------------------------------------------------------------------------- /test/fixtures/step/then.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Then observe outcomes 3 | -------------------------------------------------------------------------------- /test/fixtures/step/when.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | When the key action 3 | -------------------------------------------------------------------------------- /templates/missing-step.tpl: -------------------------------------------------------------------------------- 1 | ({{head}} "^{{body}}$" 2 | (lambda ({{args}}) 3 | 4 | )) -------------------------------------------------------------------------------- /test/fixtures/scenario/tags-none.feature: -------------------------------------------------------------------------------- 1 | 2 | Scenario: Some scenario 3 | Given a known state 4 | -------------------------------------------------------------------------------- /test/fixtures/scenario/tags-single.feature: -------------------------------------------------------------------------------- 1 | @debug 2 | Scenario: Some scenario 3 | Given a known state 4 | -------------------------------------------------------------------------------- /test/fixtures/scenario/same-name.feature: -------------------------------------------------------------------------------- 1 | @debug @debug 2 | Scenario: Some scenario 3 | Given a known state 4 | -------------------------------------------------------------------------------- /test/fixtures/scenario/tags-comment.feature: -------------------------------------------------------------------------------- 1 | # @debug 2 | Scenario: Some scenario 3 | Given a known state 4 | -------------------------------------------------------------------------------- /test/fixtures/scenario/tags-multiple.feature: -------------------------------------------------------------------------------- 1 | @debug @verbose 2 | Scenario: Some scenario 3 | Given a known state 4 | -------------------------------------------------------------------------------- /test/fixtures/scenarios/empty.feature: -------------------------------------------------------------------------------- 1 | Scenario: Add two positive numbers 2 | Scenario: Add two negative numbers 3 | -------------------------------------------------------------------------------- /test/fixtures/scenario/same-step-names.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given a known state 3 | Given a known state 4 | -------------------------------------------------------------------------------- /test/fixtures/scenario/tags-whitespace.feature: -------------------------------------------------------------------------------- 1 | @debug @verbose 2 | Scenario: Some scenario 3 | Given a known state 4 | -------------------------------------------------------------------------------- /ecukes-pkg.el: -------------------------------------------------------------------------------- 1 | (define-package "ecukes" "0.4.3" 2 | "Cucumber for Emacs." 3 | '((s "1.3.0") (dash "1.0.1") (ansi "0.0.2"))) 4 | -------------------------------------------------------------------------------- /test/fixtures/background/all-good.feature: -------------------------------------------------------------------------------- 1 | Background: 2 | Given a known state 3 | When the key action 4 | Then observe outcomes 5 | -------------------------------------------------------------------------------- /test/fixtures/outlines/no-examples.feature: -------------------------------------------------------------------------------- 1 | Feature: scenario outlines 2 | 3 | Scenario Outline: no examples 4 | Given is 5 | -------------------------------------------------------------------------------- /test/fixtures/background/comments.feature: -------------------------------------------------------------------------------- 1 | # Background: 2 | # Given a known state 3 | # When the key action 4 | # Then observe outcomes 5 | -------------------------------------------------------------------------------- /test/fixtures/background/line-breaks.feature: -------------------------------------------------------------------------------- 1 | Background: 2 | Given a known state 3 | 4 | When the key action 5 | 6 | Then observe outcomes 7 | -------------------------------------------------------------------------------- /test/fixtures/background/wrong-indentation.feature: -------------------------------------------------------------------------------- 1 | Background: 2 | Given a known state 3 | When the key action 4 | Then observe outcomes 5 | -------------------------------------------------------------------------------- /test/fixtures/step/table-single-row.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given these meals: 3 | | meal | price | 4 | | Hamburger | $4.50 | 5 | -------------------------------------------------------------------------------- /test/fixtures/step/py-string-same-lines.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given this text: 3 | """ 4 | Lorem ipsum 5 | Lorem ipsum 6 | """ 7 | -------------------------------------------------------------------------------- /test/fixtures/step/py-string-two-empty-lines.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given this text: 3 | """ 4 | abc 5 | 6 | def 7 | 8 | """ 9 | -------------------------------------------------------------------------------- /test/fixtures/feature/tags-none.feature: -------------------------------------------------------------------------------- 1 | 2 | Feature: Some Feature 3 | As a programmer 4 | I love debugging 5 | 6 | Scenario: Some Scenario 7 | Given a known state 8 | -------------------------------------------------------------------------------- /test/fixtures/intro/fewer-description-lines.feature: -------------------------------------------------------------------------------- 1 | Feature: Addition of two numbers 2 | As a math idiot I want to aviod silly mistakes and be told the sum of two numbers 3 | -------------------------------------------------------------------------------- /test/fixtures/background/comment-breaks.feature: -------------------------------------------------------------------------------- 1 | Background: 2 | # foo 3 | Given a known state 4 | # bar 5 | When the key action 6 | # baz 7 | Then observe outcomes 8 | -------------------------------------------------------------------------------- /test/fixtures/intro/all-good.feature: -------------------------------------------------------------------------------- 1 | Feature: Addition of two numbers 2 | In order to aviod silly mistakes 3 | As a math idiot 4 | I want to be told the sum of two numbers 5 | -------------------------------------------------------------------------------- /test/fixtures/feature/tags-single.feature: -------------------------------------------------------------------------------- 1 | @debug 2 | Feature: Some Feature 3 | As a programmer 4 | I love debugging 5 | 6 | Scenario: Some Scenario 7 | Given a known state 8 | -------------------------------------------------------------------------------- /test/fixtures/scenario/missing-steps.feature: -------------------------------------------------------------------------------- 1 | Background: 2 | Given a step without definition 3 | 4 | Scenario: Missing steps 5 | Given a step that does not have a definition 6 | -------------------------------------------------------------------------------- /test/fixtures/step/table-same-row.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given these meals: 3 | | meal | price | 4 | | Hamburger | $4.50 | 5 | | Hamburger | $4.50 | 6 | -------------------------------------------------------------------------------- /test/fixtures/feature/same-name.feature: -------------------------------------------------------------------------------- 1 | @debug @debug 2 | Feature: Some Feature 3 | As a programmer 4 | I love debugging 5 | 6 | Scenario: Some Scenario 7 | Given a known state 8 | -------------------------------------------------------------------------------- /test/fixtures/feature/tags-comment.feature: -------------------------------------------------------------------------------- 1 | # @debug 2 | Feature: Some Feature 3 | As a programmer 4 | I love debugging 5 | 6 | Scenario: Some Scenario 7 | Given a known state 8 | -------------------------------------------------------------------------------- /test/fixtures/intro/comments.feature: -------------------------------------------------------------------------------- 1 | # Feature: Addition of two numbers 2 | # In order to aviod silly mistakes 3 | # As a math idiot 4 | # I want to be told the sum of two numbers 5 | -------------------------------------------------------------------------------- /test/fixtures/intro/no-space-in-header.feature: -------------------------------------------------------------------------------- 1 | Feature:Addition of two numbers 2 | In order to aviod silly mistakes 3 | As a math idiot 4 | I want to be told the sum of two numbers 5 | -------------------------------------------------------------------------------- /test/fixtures/intro/wrong-indentation.feature: -------------------------------------------------------------------------------- 1 | Feature: Addition of two numbers 2 | In order to aviod silly mistakes 3 | As a math idiot 4 | I want to be told the sum of two numbers 5 | -------------------------------------------------------------------------------- /test/fixtures/step/table-empty-columns.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given these meals: 3 | | meal | price | 4 | | Hamburger | | 5 | | Pizza | $5.30 | 6 | -------------------------------------------------------------------------------- /test/fixtures/step/table-multiple-rows.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given these meals: 3 | | meal | price | 4 | | Hamburger | $4.50 | 5 | | Pizza | $5.30 | 6 | -------------------------------------------------------------------------------- /test/fixtures/step/table-wrong-indentation.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given these meals: 3 | | meal | price | 4 | | Hamburger | $4.50 | 5 | | Pizza | $5.30 | 6 | -------------------------------------------------------------------------------- /test/fixtures/intro/line-breaks.feature: -------------------------------------------------------------------------------- 1 | Feature: Addition of two numbers 2 | In order to aviod silly mistakes 3 | As a math idiot 4 | 5 | 6 | I want to be told the sum of two numbers 7 | -------------------------------------------------------------------------------- /test/fixtures/feature/tags-multiple-tags.feature: -------------------------------------------------------------------------------- 1 | @debug @verbose 2 | Feature: Some Feature 3 | As a programmer 4 | I love debugging 5 | 6 | Scenario: Some Scenario 7 | Given a known state 8 | -------------------------------------------------------------------------------- /test/fixtures/step/py-string-all-good.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given this text: 3 | """ 4 | Lorem ipsum dolor sit amet. 5 | Curabitur pellentesque iaculis eros. 6 | """ 7 | -------------------------------------------------------------------------------- /test/fixtures/feature/tags-whitespace.feature: -------------------------------------------------------------------------------- 1 | @debug @verbose 2 | Feature: Some Feature 3 | As a programmer 4 | I love debugging 5 | 6 | Scenario: Some Scenario 7 | Given a known state 8 | -------------------------------------------------------------------------------- /test/fixtures/step/py-string-line-break.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given this text: 3 | """ 4 | Lorem ipsum dolor sit amet. 5 | 6 | Curabitur pellentesque iaculis eros. 7 | """ 8 | -------------------------------------------------------------------------------- /test/fixtures/intro/extra-space-in-header.feature: -------------------------------------------------------------------------------- 1 | Feature: Addition of two numbers 2 | In order to aviod silly mistakes 3 | As a math idiot 4 | I want to be told the sum of two numbers 5 | -------------------------------------------------------------------------------- /test/fixtures/step/py-string-wrong-indentation.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given this text: 3 | """ 4 | Lorem ipsum dolor sit amet. 5 | Curabitur pellentesque iaculis eros. 6 | """ 7 | -------------------------------------------------------------------------------- /test/fixtures/feature/simple.feature: -------------------------------------------------------------------------------- 1 | Feature: Simple 2 | This 3 | Is 4 | Simple 5 | 6 | Background: 7 | Given a known state 8 | 9 | Scenario: Scenario 10 | Given an unknown state 11 | -------------------------------------------------------------------------------- /test/fixtures/outlines/one-example.feature: -------------------------------------------------------------------------------- 1 | Feature: scenario outlines 2 | 3 | Scenario Outline: single example given 4 | Given is 5 | 6 | Examples: 7 | | foo | bar | 8 | | 1 | 2 | 9 | -------------------------------------------------------------------------------- /test/fixtures/intro/more-description-lines.feature: -------------------------------------------------------------------------------- 1 | Feature: Addition of two numbers 2 | In order to aviod silly mistakes 3 | As a math idiot 4 | And as an idiot in general 5 | I want to be told the sum of two numbers 6 | -------------------------------------------------------------------------------- /test/fixtures/intro/spaces-above.feature: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Feature: Addition of two numbers 8 | In order to aviod silly mistakes 9 | As a math idiot 10 | I want to be told the sum of two numbers 11 | -------------------------------------------------------------------------------- /test/fixtures/outlines/wrong-column-names.feature: -------------------------------------------------------------------------------- 1 | Feature: scenario outlines 2 | 3 | Scenario Outline: wrong column names 4 | Given is 5 | 6 | Examples: 7 | | baz | quux | 8 | | 1 | 2 | 9 | -------------------------------------------------------------------------------- /test/fixtures/intro/section-background.feature: -------------------------------------------------------------------------------- 1 | Feature: Addition of two numbers 2 | In order to aviod silly mistakes 3 | As a math idiot 4 | I want to be told the sum of two numbers 5 | 6 | Background: 7 | Given a known state 8 | -------------------------------------------------------------------------------- /test/fixtures/outlines/bad-indents.feature: -------------------------------------------------------------------------------- 1 | Feature: scenario outlines 2 | 3 | Scenario Outline: weird indents 4 | Given is 5 | 6 | Examples: 7 | | foo | bar | 8 | | 1 | 2 | 9 | | 3 | 4| 10 | -------------------------------------------------------------------------------- /test/fixtures/scenario/all-good.feature: -------------------------------------------------------------------------------- 1 | Scenario: Add two numbers 2 | Given I have entered 50 into the calculator 3 | And I have entered 70 into the calculator 4 | When I press add 5 | Then the result should be 120 on the screen 6 | -------------------------------------------------------------------------------- /test/fixtures/scenario/no-space-in-header.feature: -------------------------------------------------------------------------------- 1 | Scenario:Add two numbers 2 | Given I have entered 50 into the calculator 3 | And I have entered 70 into the calculator 4 | When I press add 5 | Then the result should be 120 on the screen 6 | -------------------------------------------------------------------------------- /test/fixtures/scenario/whitespace.feature: -------------------------------------------------------------------------------- 1 | Scenario: Add two numbers 2 | 3 | Given I have entered 50 into the calculator 4 | And I have entered 70 into the calculator 5 | When I press add 6 | Then the result should be 120 on the screen 7 | -------------------------------------------------------------------------------- /test/fixtures/scenario/wrong-indentation.feature: -------------------------------------------------------------------------------- 1 | Scenario: Add two numbers 2 | Given I have entered 50 into the calculator 3 | And I have entered 70 into the calculator 4 | When I press add 5 | Then the result should be 120 on the screen 6 | -------------------------------------------------------------------------------- /test/fixtures/intro/section-scenario.feature: -------------------------------------------------------------------------------- 1 | Feature: Addition of two numbers 2 | In order to aviod silly mistakes 3 | As a math idiot 4 | I want to be told the sum of two numbers 5 | 6 | Scenario: Positive numbers 7 | Given a known state 8 | -------------------------------------------------------------------------------- /test/fixtures/scenario/comments.feature: -------------------------------------------------------------------------------- 1 | # Scenario: Add two numbers 2 | # Given I have entered 50 into the calculator 3 | # And I have entered 70 into the calculator 4 | # When I press add 5 | # Then the result should be 120 on the screen 6 | -------------------------------------------------------------------------------- /test/fixtures/outlines/tags.feature: -------------------------------------------------------------------------------- 1 | Feature: scenario outlines 2 | 3 | @regression @ui 4 | Scenario Outline: with tags 5 | Given is 6 | 7 | Examples: 8 | | foo | bar | 9 | | 1 | 2 | 10 | | 3 | 4 | 11 | -------------------------------------------------------------------------------- /test/fixtures/scenario/line-breaks.feature: -------------------------------------------------------------------------------- 1 | Scenario: Add two numbers 2 | Given I have entered 50 into the calculator 3 | 4 | And I have entered 70 into the calculator 5 | 6 | When I press add 7 | 8 | Then the result should be 120 on the screen 9 | -------------------------------------------------------------------------------- /test/fixtures/block/all-good.feature: -------------------------------------------------------------------------------- 1 | Background: 2 | Given I see something on the screen 3 | And I see something else on the screen 4 | But I dont see something on the screen 5 | When I see something on the screen 6 | Then I should see something on the screen 7 | -------------------------------------------------------------------------------- /test/fixtures/scenario/extra-space-in-header.feature: -------------------------------------------------------------------------------- 1 | Scenario: Add two numbers 2 | Given I have entered 50 into the calculator 3 | And I have entered 70 into the calculator 4 | When I press add 5 | Then the result should be 120 on the screen 6 | -------------------------------------------------------------------------------- /Carton: -------------------------------------------------------------------------------- 1 | (source "melpa" "http://melpa.milkbox.net/packages/") 2 | 3 | (package "ecukes" "0.4.3" "Cucumber for Emacs.") 4 | 5 | (depends-on "s" "1.3.0") 6 | (depends-on "dash" "1.0.1") 7 | (depends-on "ansi" "0.0.2") 8 | 9 | (development 10 | (depends-on "el-mock")) 11 | -------------------------------------------------------------------------------- /test/fixtures/outlines/background.feature: -------------------------------------------------------------------------------- 1 | Feature: scenario outlines 2 | 3 | Background: 4 | Given I do something 5 | 6 | Scenario Outline: with background 7 | Given is 8 | 9 | Examples: 10 | | foo | bar | 11 | | 1 | 2 | 12 | -------------------------------------------------------------------------------- /test/fixtures/feature/tags-multiple-scenarios.feature: -------------------------------------------------------------------------------- 1 | @debug @verbose 2 | Feature: Some Feature 3 | As a programmer 4 | I love debugging 5 | 6 | Scenario: Some Scenario 7 | Given a known state 8 | 9 | Scenario: Some other Scenario 10 | Given a known state 11 | -------------------------------------------------------------------------------- /test/fixtures/outlines/multiple-examples.feature: -------------------------------------------------------------------------------- 1 | Feature: scenario outline 2 | 3 | Scenario Outline: multiple examples 4 | Given is 5 | 6 | Examples: 7 | | foo | bar | 8 | | 1 | 2 | 9 | | 3 | 4 | 10 | | 5 | 6 | 11 | -------------------------------------------------------------------------------- /test/ecukes-helpers-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-helpers) 2 | 3 | (ert-deftest run-feature-steps () 4 | "Should return all steps." 5 | (with-parse-feature 6 | "simple" 7 | (lambda (feature intro scenarios background steps) 8 | (should (equal (ecukes-feature-steps (list feature)) steps))))) 9 | -------------------------------------------------------------------------------- /test/ecukes-parse-feature-test.el: -------------------------------------------------------------------------------- 1 | (ert-deftest parse-feature-empty () 2 | "Should parse empty feature" 3 | (with-parse-feature 4 | "empty" 5 | (lambda (feature intro scenarios background steps) 6 | (should (equal feature (make-ecukes-feature :intro nil :background nil :scenarios nil)))))) 7 | -------------------------------------------------------------------------------- /test/fixtures/block/with-blank-lines.feature: -------------------------------------------------------------------------------- 1 | Background: 2 | Given I see something on the screen 3 | And I see something else on the screen 4 | 5 | But I dont see something on the screen 6 | 7 | 8 | When I see something on the screen 9 | Then I should see something on the screen 10 | -------------------------------------------------------------------------------- /test/fixtures/scenario/comment-breaks.feature: -------------------------------------------------------------------------------- 1 | Scenario: Add two numbers 2 | # foo 3 | Given I have entered 50 into the calculator 4 | # bar 5 | And I have entered 70 into the calculator 6 | # baz 7 | When I press add 8 | # qux 9 | Then the result should be 120 on the screen 10 | -------------------------------------------------------------------------------- /test/fixtures/background/with-intro.feature: -------------------------------------------------------------------------------- 1 | Feature: Addition of two numbers 2 | In order to aviod silly mistakes 3 | As a math idiot 4 | I want to be told the sum of two numbers 5 | 6 | Background: 7 | Given a known state 8 | When the key action 9 | Then observe outcomes 10 | -------------------------------------------------------------------------------- /test/fixtures/block/mix.feature: -------------------------------------------------------------------------------- 1 | Background: 2 | Given I see this on the screen: 3 | """ 4 | Some awesome text... 5 | """ 6 | And I see something else on the screen 7 | Then I should have these elements: 8 | | element | 9 | | h1 | 10 | | p | 11 | | div | 12 | And I should be happy 13 | -------------------------------------------------------------------------------- /test/fixtures/scenario/mix-py-string-table-regular.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given this text: 3 | """ 4 | Lorem ipsum dolor sit amet. 5 | Curabitur pellentesque iaculis eros. 6 | """ 7 | Given these meals: 8 | | meal | price | 9 | | Hamburger | $4.50 | 10 | Given a known state 11 | -------------------------------------------------------------------------------- /test/fixtures/scenario/mix-regular-py-string-table.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given a known state 3 | Given this text: 4 | """ 5 | Lorem ipsum dolor sit amet. 6 | Curabitur pellentesque iaculis eros. 7 | """ 8 | Given these meals: 9 | | meal | price | 10 | | Hamburger | $4.50 | 11 | -------------------------------------------------------------------------------- /test/fixtures/scenario/mix-table-regular-py-string.feature: -------------------------------------------------------------------------------- 1 | Scenario: Some scenario 2 | Given these meals: 3 | | meal | price | 4 | | Hamburger | $4.50 | 5 | Given a known state 6 | Given this text: 7 | """ 8 | Lorem ipsum dolor sit amet. 9 | Curabitur pellentesque iaculis eros. 10 | """ 11 | -------------------------------------------------------------------------------- /templates/feature.tpl: -------------------------------------------------------------------------------- 1 | Feature: Do Some things 2 | In order to do something 3 | As a user 4 | I want to do something 5 | 6 | Scenario: Do Something 7 | Given I have "something" 8 | When I have "something" 9 | Then I should have "something" 10 | And I should have "something" 11 | But I should not have "something" 12 | -------------------------------------------------------------------------------- /test/fixtures/block/with-comments.feature: -------------------------------------------------------------------------------- 1 | Background: 2 | Given I see something on the screen 3 | # Here I see something else on the screen 4 | And I see something else on the screen 5 | # But here I don't 6 | # see something on the screen 7 | But I dont see something on the screen 8 | When I see something on the screen 9 | Then I should see something on the screen 10 | -------------------------------------------------------------------------------- /test/fixtures/feature/tags-multiple-mixed.feature: -------------------------------------------------------------------------------- 1 | @debug @verbose 2 | Feature: Some Feature 3 | As a programmer 4 | I love debugging 5 | 6 | @super 7 | Scenario: Some Scenario 8 | Given a known state 9 | 10 | @duper 11 | Scenario Outline: Some Scenario 12 | Given state 13 | 14 | Examples: 15 | | state | 16 | | known | 17 | | unknown | 18 | -------------------------------------------------------------------------------- /test/fixtures/intro/scenario-tags.feature: -------------------------------------------------------------------------------- 1 | Feature: Addition of two numbers 2 | In order to aviod silly mistakes 3 | As a math idiot 4 | I want to be told the sum of two numbers 5 | 6 | @foo @bar 7 | Scenario: Add two numbers 8 | Given I have entered 50 into the calculator 9 | And I have entered 70 into the calculator 10 | When I press add 11 | Then the result should be 120 on the screen 12 | -------------------------------------------------------------------------------- /test/fixtures/scenarios/all-good.feature: -------------------------------------------------------------------------------- 1 | Scenario: Add two positive numbers 2 | Given I have entered 50 into the calculator 3 | And I have entered 70 into the calculator 4 | When I press add 5 | Then the result should be 120 on the screen 6 | 7 | Scenario: Add two negative numbers 8 | Given I have entered -50 into the calculator 9 | And I have entered -70 into the calculator 10 | When I press add 11 | Then the result should be -120 on the screen 12 | -------------------------------------------------------------------------------- /test/fixtures/intro/comments-above.feature: -------------------------------------------------------------------------------- 1 | # Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Pellentesque 2 | # in tellus. In pharetra consequat augue. In congue. Curabitur 3 | # pellentesque iaculis eros. 4 | 5 | # Proin magna odio, posuere sed, commodo nec, varius nec, 6 | # tortor. Fusce ante. Curabitur tincidunt. Duis posuere eleifend 7 | # justo. 8 | 9 | Feature: Addition of two numbers 10 | In order to aviod silly mistakes 11 | As a math idiot 12 | I want to be told the sum of two numbers 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY : all test clean 2 | EMACS ?= emacs 3 | SRC = $(filter-out %-pkg.el, $(wildcard *.el)) 4 | ELC = $(SRC:.el=.elc) 5 | 6 | all: test 7 | 8 | test: clean-elc 9 | $(MAKE) quick-test 10 | $(MAKE) compile 11 | 12 | quick-test: elpa 13 | carton exec ./test/ecukes-test 14 | 15 | elpa: 16 | carton install 17 | 18 | compile: $(ELC) 19 | %.elc: %.el 20 | carton exec $(EMACS) -Q -batch -L . -f batch-byte-compile $< 21 | 22 | clean: clean-elc 23 | rm -rf elpa 24 | 25 | clean-elc: 26 | rm -rf *.elc test/*.elc 27 | -------------------------------------------------------------------------------- /test/fixtures/scenarios/with-background.feature: -------------------------------------------------------------------------------- 1 | Background: 2 | Given a known state 3 | When the key action 4 | Then observe outcomes 5 | 6 | Scenario: Add two positive numbers 7 | Given I have entered 50 into the calculator 8 | And I have entered 70 into the calculator 9 | When I press add 10 | Then the result should be 120 on the screen 11 | 12 | Scenario: Add two negative numbers 13 | Given I have entered -50 into the calculator 14 | And I have entered -70 into the calculator 15 | When I press add 16 | Then the result should be -120 on the screen 17 | -------------------------------------------------------------------------------- /test/fixtures/outlines/substitution.feature: -------------------------------------------------------------------------------- 1 | Feature: scenario outlines 2 | 3 | Scenario Outline: py-string and table substitution 4 | Given I want to 5 | When I say: 6 | """ 7 | You are ! I want to you. 8 | """ 9 | Then the results are: 10 | | response | desired | 11 | | | | 12 | 13 | Examples: 14 | | activity | compliment | response | desired | 15 | | marry | great | positive | true | 16 | | marry | not bad | negative | false | 17 | | divorce | an idiot | negative | true | 18 | -------------------------------------------------------------------------------- /test/ecukes-parse-new-section-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (ert-deftest parse-new-section-end-of-buffer () 4 | (with-mock 5 | (stub eobp => t) 6 | (stub ecukes-parse-line) 7 | (should (ecukes-parse-new-section-p)))) 8 | 9 | (ert-deftest parse-new-section-background () 10 | (with-mock 11 | (stub eobp => nil) 12 | (stub ecukes-parse-line => "Background:") 13 | (should (ecukes-parse-new-section-p)))) 14 | 15 | (ert-deftest parse-new-section-scenario () 16 | (with-mock 17 | (stub eobp => t) 18 | (stub ecukes-parse-line => "Scenario: Positive numbers") 19 | (should (ecukes-parse-new-section-p)))) 20 | -------------------------------------------------------------------------------- /test/fixtures/scenarios/with-intro.feature: -------------------------------------------------------------------------------- 1 | Feature: Addition of two numbers 2 | In order to aviod silly mistakes 3 | As a math idiot 4 | I want to be told the sum of two numbers 5 | 6 | Scenario: Add two positive numbers 7 | Given I have entered 50 into the calculator 8 | And I have entered 70 into the calculator 9 | When I press add 10 | Then the result should be 120 on the screen 11 | 12 | Scenario: Add two negative numbers 13 | Given I have entered -50 into the calculator 14 | And I have entered -70 into the calculator 15 | When I press add 16 | Then the result should be -120 on the screen 17 | -------------------------------------------------------------------------------- /ecukes-term.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-term.el --- Cucumber for Emacs 2 | 3 | (defvar ecukes-path 4 | (file-name-directory load-file-name) 5 | "Path to ecukes.") 6 | 7 | (add-to-list 'load-path ecukes-path) 8 | 9 | (require 'ecukes-run) 10 | (require 'ecukes-stats) 11 | (require 'ecukes-setup) 12 | 13 | (ecukes-setup) 14 | 15 | (let ((feature-files 16 | (progn 17 | (or argv (setq argv (list "features"))) 18 | (if (file-directory-p (car argv)) 19 | (directory-files (ecukes-project-features-path) t "\\.feature$") 20 | argv)))) 21 | (ecukes-run feature-files)) 22 | 23 | (ecukes-quit 24 | (if (> ecukes-stats-steps-failed 0) 1 0)) 25 | 26 | ;;; ecukes-term.el ends here 27 | -------------------------------------------------------------------------------- /templates/step-definition.tpl: -------------------------------------------------------------------------------- 1 | ;; This file contains your project specific step definitions. All 2 | ;; files in this directory whose names end with "-steps.el" will be 3 | ;; loaded automatically by Ecukes. 4 | 5 | (Given "^I have \"\\(.+\\)\"$" 6 | (lambda (something) 7 | ;; Do something 8 | )) 9 | 10 | (When "^I have \"\\(.+\\)\"$" 11 | (lambda (something) 12 | ;; Do something 13 | )) 14 | 15 | (Then "^I should have \"\\(.+\\)\"$" 16 | (lambda (something) 17 | ;; Do something 18 | )) 19 | 20 | (And "^I have \"\\(.+\\)\"$" 21 | (lambda (something) 22 | ;; Do something 23 | )) 24 | 25 | (But "^I should not have \"\\(.+\\)\"$" 26 | (lambda (something) 27 | ;; Do something 28 | )) 29 | -------------------------------------------------------------------------------- /ecukes-cli.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-cli.el --- Cucumber for Emacs 2 | 3 | (defvar ecukes-path 4 | (file-name-directory load-file-name) 5 | "Path to ecukes.") 6 | 7 | (add-to-list 'load-path ecukes-path) 8 | 9 | (require 'ecukes-helpers) 10 | (require 'ecukes-setup) 11 | (require 'ecukes-project) 12 | (require 'ecukes-print) 13 | (require 'ecukes-new) 14 | 15 | (defun ecukes-cli-print-steps () 16 | (ecukes-setup) 17 | (let ((has (lambda (flag) 18 | (when (member flag command-line-args-left) 19 | (setq command-line-args-left 20 | (delete flag command-line-args-left)) 21 | t)))) 22 | (ecukes-print-steps 23 | (funcall has "--with-doc") 24 | (funcall has "--with-file")))) 25 | 26 | ;;; ecukes-cli.el ends here 27 | -------------------------------------------------------------------------------- /templates/usage.tpl: -------------------------------------------------------------------------------- 1 | USAGE: ecukes [--script|--win] [file|dir] [options] 2 | 3 | OPTIONS: 4 | -h, --help Display this help message 5 | --script Run Ecukes as a script/batch job 6 | --win Run Ecukes with full GUI window 7 | --new Create new Ecukes setup for project 8 | --list-steps Print all available steps defined for this project. 9 | --with-doc Include docstring when printing steps with --list-steps. 10 | --with-file Include file name when printing steps with --list-steps. 11 | --verbose Show `message' output 12 | --dbg Run in debug mode (enable as much debug options in Emacs as possible) 13 | --tags TAG_EXPRESSION Only execute the scenarios with tags matching TAG_EXPRESSION. 14 | TAG_EXPRESSION Examples: --tags @dev, --tags @dev,~@local 15 | A tag starting with ~ excluded from the scenarios. 16 | -------------------------------------------------------------------------------- /test/ecukes-template-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-template) 2 | 3 | (ert-deftest template-substitute-with-single-substitution () 4 | "Should substitute single substitution." 5 | (should 6 | (equal 7 | (ecukes-template-substitute 8 | "replace {{foo}} in string" 9 | '(("foo" . "bar"))) 10 | "replace bar in string"))) 11 | 12 | (ert-deftest template-substitute-with-multiple-substitutions () 13 | "Should substitute multiple substitutions." 14 | (should 15 | (equal 16 | (ecukes-template-substitute 17 | "replace {{foo}} and {{baz}} in string" 18 | '(("foo" . "bar") 19 | ("baz" . "qux"))) 20 | "replace bar and qux in string"))) 21 | 22 | (ert-deftest template-dont-substitute-when-no-substitutions () 23 | "Should return same string when no substitutions." 24 | (should 25 | (equal 26 | (ecukes-template-substitute 27 | "do not replace anything" 28 | '(("not" . "NOT"))) 29 | "do not replace anything"))) 30 | -------------------------------------------------------------------------------- /ecukes-helpers.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-helpers.el --- Misc helpers 2 | 3 | (require 'dash) 4 | (require 'ecukes-def) 5 | 6 | (defun ecukes-feature-steps (features) 7 | "Return all steps in all FEATURES." 8 | (let* ((scenarios 9 | (-flatten 10 | (-map 11 | (lambda (feature) 12 | (ecukes-feature-scenarios feature)) features))) 13 | (backgrounds 14 | (-reject 15 | 'null 16 | (-map 17 | (lambda (feature) 18 | (ecukes-feature-background feature)) features))) 19 | (scenario-steps 20 | (-map 21 | (lambda (scenario) 22 | (ecukes-scenario-steps scenario)) scenarios)) 23 | (background-steps 24 | (-map 25 | (lambda (background) 26 | (ecukes-background-steps background)) backgrounds))) 27 | (-flatten (-concat background-steps scenario-steps)))) 28 | 29 | (provide 'ecukes-helpers) 30 | 31 | ;;; ecukes-helpers.el ends here 32 | -------------------------------------------------------------------------------- /test/ecukes-test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ":"; exec emacs "$@" --script "$0" # -*-emacs-lisp-*- 3 | 4 | (let ((current-directory (file-name-directory load-file-name))) 5 | (setq ecukes-test-path (expand-file-name "." current-directory)) 6 | (setq ecukes-root-path (expand-file-name ".." current-directory))) 7 | 8 | (add-to-list 'load-path ecukes-root-path) 9 | (add-to-list 'load-path ecukes-test-path) 10 | 11 | (setq ecukes-path (expand-file-name ".." ecukes-test-path)) 12 | (setq ecukes-fixtures-path (expand-file-name "fixtures" ecukes-test-path)) 13 | 14 | (require 'cl) 15 | (require 'ert) 16 | (require 'el-mock) 17 | (require 's) 18 | (require 'dash) 19 | (require 'ansi) 20 | (require 'ecukes-def) 21 | 22 | (setq debug-on-entry t) 23 | (setq debug-on-error t) 24 | (setq ecukes-include-tags nil) 25 | 26 | (load (expand-file-name "test-helper.el" ecukes-test-path) nil t) 27 | (dolist (test-file (or argv (directory-files ecukes-test-path t "-test.el$"))) 28 | (load test-file nil t)) 29 | 30 | (let ((ecukes-verbose t)) 31 | (ert-run-tests-batch t)) 32 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Ecukes - Cucumber for Emacs 2 | 3 | [](https://www.youtube.com/watch?v=4VcH4uAQJZI) 4 | 5 | There are plenty of unit/regression testing tools for Emacs, and even 6 | some for functional testing. What Emacs is missing though is a really 7 | good testing framework for integration testing. This is where 8 | [Ecukes](http://github.com/rejeep/ecukes) comes in. 9 | 10 | [Cucumber](http://cukes.info/) is a great integration testing tool, 11 | used mostly for testing web applications. Ecukes is Cucumber for 12 | Emacs. No, it's **not** a major mode to edit feature files. It is a 13 | package that makes it possible to write Cucumber like tests for your 14 | Emacs packages. 15 | 16 | Ecukes is not a complete clone of Cucumber and is not intended to 17 | be. If however Ecukes is missing some feature that you feel really 18 | should be included, please make a bug report. 19 | 20 | If you don't know anything about Cucumber I suggest you read up a bit 21 | about it before continuing with Ecukes. 22 | 23 | See for more information! 24 | -------------------------------------------------------------------------------- /test/ecukes-project-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-project) 2 | 3 | (ert-deftest project-path () 4 | "Should return correct project path." 5 | (with-mock 6 | (stub file-directory-p => t) 7 | (let ((default-directory "/path/to/project/")) 8 | (should (equal "/path/to/project" (ecukes-project-path)))))) 9 | 10 | (ert-deftest project-name () 11 | "Should return correct project name." 12 | (with-project 13 | (should (equal "project" (ecukes-project-name))))) 14 | 15 | (ert-deftest project-features-path () 16 | "Should return correct project features path." 17 | (with-project 18 | (should (equal "/path/to/project/features" (ecukes-project-features-path))))) 19 | 20 | (ert-deftest project-support-path () 21 | "Should return correct project support path." 22 | (with-project 23 | (should (equal "/path/to/project/features/support" (ecukes-project-support-path))))) 24 | 25 | (ert-deftest project-step-definition-path () 26 | "Should return correct project step definition path." 27 | (with-project 28 | (should (equal "/path/to/project/features/step-definitions" (ecukes-project-step-definitions-path))))) 29 | -------------------------------------------------------------------------------- /ecukes-project.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-project.el --- Project helpers 2 | 3 | (defun ecukes-project-path (&optional dir) 4 | "Path to project." 5 | (or dir (setq dir default-directory)) 6 | (if (file-directory-p (expand-file-name "features" dir)) 7 | (directory-file-name dir) 8 | (let ((new-dir (expand-file-name (file-name-as-directory "..") dir))) 9 | (unless (equal dir "/") 10 | (ecukes-project-path new-dir))))) 11 | 12 | (defun ecukes-project-name () 13 | "Name of the project." 14 | (file-name-nondirectory (ecukes-project-path))) 15 | 16 | (defun ecukes-project-features-path () 17 | "Path to project features dir." 18 | (directory-file-name (expand-file-name "features" (ecukes-project-path)))) 19 | 20 | (defun ecukes-project-support-path () 21 | "Path to project features dir." 22 | (directory-file-name (expand-file-name "support" (ecukes-project-features-path)))) 23 | 24 | (defun ecukes-project-step-definitions-path () 25 | "Path to project step definitions dir." 26 | (directory-file-name (expand-file-name "step-definitions" (ecukes-project-features-path)))) 27 | 28 | (provide 'ecukes-project) 29 | 30 | ;;; ecukes-project.el ends here 31 | -------------------------------------------------------------------------------- /templates/env.tpl: -------------------------------------------------------------------------------- 1 | ;; This is an example of how you could set up this file. This setup 2 | ;; requires a directory called util in the project root and that the 3 | ;; util directory contains the testing tools ert and espuds. 4 | 5 | (let* ((features-directory 6 | (file-name-directory 7 | (directory-file-name (file-name-directory load-file-name)))) 8 | (project-directory 9 | (file-name-directory 10 | (directory-file-name features-directory)))) 11 | (setq {{project-name}}-root-path project-directory) 12 | (setq {{project-name}}-util-path (expand-file-name "util" {{project-name}}-root-path))) 13 | 14 | (add-to-list 'load-path {{project-name}}-root-path) 15 | (add-to-list 'load-path (expand-file-name "espuds" {{project-name}}-util-path)) 16 | (add-to-list 'load-path (expand-file-name "ert" {{project-name}}-util-path)) 17 | 18 | (require '{{project-name}}) 19 | (require 'espuds) 20 | (require 'ert) 21 | 22 | 23 | (Setup 24 | ;; Before anything has run 25 | ) 26 | 27 | (Before 28 | ;; Before each scenario is run 29 | ) 30 | 31 | (After 32 | ;; After each scenario is run 33 | ) 34 | 35 | (Teardown 36 | ;; After when everything has been run 37 | ) 38 | -------------------------------------------------------------------------------- /test/ecukes-hooks-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-hooks) 2 | 3 | (ert-deftest hooks-before () 4 | "Should run before hooks." 5 | (with-mock 6 | (mock (before-mock) :times 1) 7 | (with-hooks 8 | (Before 9 | (before-mock)) 10 | (ecukes-hooks-run-before)))) 11 | 12 | (ert-deftest hooks-after () 13 | "Should run after hooks." 14 | (with-mock 15 | (mock (after-mock) :times 1) 16 | (with-hooks 17 | (After 18 | (after-mock)) 19 | (ecukes-hooks-run-after)))) 20 | 21 | (ert-deftest hooks-setup () 22 | "Should run setup hooks." 23 | (with-mock 24 | (mock (setup-mock) :times 1) 25 | (with-hooks 26 | (Setup 27 | (setup-mock)) 28 | (ecukes-hooks-run-setup)))) 29 | 30 | (ert-deftest hooks-teardown () 31 | "Should run teardown hooks." 32 | (with-mock 33 | (mock (teardown-mock) :times 1) 34 | (with-hooks 35 | (Teardown 36 | (teardown-mock)) 37 | (ecukes-hooks-run-teardown)))) 38 | 39 | (ert-deftest hooks-append () 40 | "Should append hooks." 41 | (with-hooks 42 | (Before "1") 43 | (Before "2") 44 | (should (equal "1" (funcall (nth 0 ecukes-hooks-before)))) 45 | (should (equal "2" (funcall (nth 1 ecukes-hooks-before)))))) 46 | -------------------------------------------------------------------------------- /test/ecukes-parse-scenarios-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (defun with-parse-scenarios (name fn) 4 | (let* ((feature-file (fixture-file-path "scenarios" name)) 5 | (feature (ecukes-parse-feature feature-file)) 6 | (scenarios (ecukes-feature-scenarios feature))) 7 | (funcall fn (mapcar 'ecukes-scenario-name scenarios)))) 8 | 9 | (defun should-parse-scenarios (name) 10 | (with-parse-scenarios 11 | name 12 | (lambda (scenario-names) 13 | (should 14 | (equal 15 | scenario-names 16 | '("Add two positive numbers" "Add two negative numbers")))))) 17 | 18 | (ert-deftest parse-scenarios-no-scenarios () 19 | "Should not parse scenarios when none." 20 | (with-parse-scenarios 21 | "none" 22 | (lambda (scenario-names) 23 | (should-not scenario-names)))) 24 | 25 | (ert-deftest parse-scenarios-all-good () 26 | "Should parse scenarios when all good." 27 | (should-parse-scenarios "all-good")) 28 | 29 | (ert-deftest parse-scenarios-with-intro () 30 | "Should parse scenarios with intro." 31 | (should-parse-scenarios "with-intro")) 32 | 33 | (ert-deftest parse-scenarios-with-background () 34 | "Should parse scenarios with background." 35 | (should-parse-scenarios "with-background")) 36 | 37 | (ert-deftest parse-scenarios-empty () 38 | "Should parse empty scenarios." 39 | (should-parse-scenarios "empty")) 40 | -------------------------------------------------------------------------------- /test/ecukes-parse-line-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (defmacro with-line (line &rest body) 4 | `(with-mock 5 | (stub buffer-substring => ,line) 6 | ,@body)) 7 | 8 | (defun should-get-exact-line-when-no-strip (line) 9 | "Parsing a line when no strip should return that line exactly as it is." 10 | (with-line line (should (equal (ecukes-parse-line) line)))) 11 | 12 | (ert-deftest parse-line-only-text () 13 | (should-get-exact-line-when-no-strip "Given a known state")) 14 | 15 | (ert-deftest parse-line-empty () 16 | (should-get-exact-line-when-no-strip "")) 17 | 18 | (ert-deftest parse-line-with-whitespace () 19 | (should-get-exact-line-when-no-strip " Given a known state ")) 20 | 21 | (ert-deftest parse-line-only-whitespace () 22 | (with-line 23 | " " 24 | (should (equal (ecukes-parse-line) " ")))) 25 | 26 | (ert-deftest parse-line-strip-only-text () 27 | (with-line 28 | "Given a known state" 29 | (should (equal (ecukes-parse-line t) "Given a known state")))) 30 | 31 | (ert-deftest parse-line-strip-empty () 32 | (with-line 33 | "" 34 | (should-not (ecukes-parse-line t)))) 35 | 36 | (ert-deftest parse-line-strip-with-whitespace () 37 | (with-line 38 | " Given a known state " 39 | (should (equal (ecukes-parse-line t) "Given a known state")))) 40 | 41 | (ert-deftest parse-line-strip-only-whitespace () 42 | (with-line 43 | " " 44 | (should-not (ecukes-parse-line t)))) 45 | -------------------------------------------------------------------------------- /test/ecukes-parse-scenario-tags-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (ert-deftest parse-scenario-tags-single-tag () 4 | "Should parse single tag." 5 | (with-parse-scenario 6 | "tags-single" 7 | (lambda (scenario name step-names tags) 8 | (should (equal tags '("debug")))))) 9 | 10 | (ert-deftest parse-scenario-tags-multiple-tags () 11 | "Should parse multiple tags." 12 | (with-parse-scenario 13 | "tags-multiple" 14 | (lambda (scenario name step-names tags) 15 | (should (equal tags '("debug" "verbose")))))) 16 | 17 | (ert-deftest parse-scenario-tags-tags-with-whitespace () 18 | "Should parse multiple tags despite whitespace." 19 | (with-parse-scenario 20 | "tags-whitespace" 21 | (lambda (scenario name step-names tags) 22 | (should (equal tags '("debug" "verbose")))))) 23 | 24 | (ert-deftest parse-scenario-tags-tags-with-same-name () 25 | "Should parse tag with same name only once." 26 | (with-parse-scenario 27 | "same-name" 28 | (lambda (scenario name step-names tags) 29 | (should (equal tags '("debug")))))) 30 | 31 | (ert-deftest parse-scenario-tags-tags-comment () 32 | "Should not parse when comment." 33 | (with-parse-scenario 34 | "tags-comment" 35 | (lambda (scenario name step-names tags) 36 | (should-not tags)))) 37 | 38 | (ert-deftest parse-scenario-tags-tags-none () 39 | "Should parse when none." 40 | (with-parse-scenario 41 | "tags-none" 42 | (lambda (scenario name step-names tags) 43 | (should-not tags)))) 44 | -------------------------------------------------------------------------------- /ecukes-template.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-template.el --- Template helpers 2 | 3 | (require 'dash) 4 | (require 's) 5 | 6 | (defvar ecukes-path 7 | (file-name-directory load-file-name) 8 | "Path to ecukes.") 9 | 10 | (defvar ecukes-template-path 11 | (expand-file-name "templates" ecukes-path) 12 | "Path to templates directory.") 13 | 14 | 15 | (defun ecukes-template-get (template &optional substitutions) 16 | "Return TEMPLATE with SUBSTITUTIONS as a string." 17 | (let ((template-file 18 | (expand-file-name (format "%s.tpl" (symbol-name template)) ecukes-template-path))) 19 | (if (file-exists-p template-file) 20 | (with-temp-buffer 21 | (insert-file-contents-literally template-file) 22 | (ecukes-template-substitute (buffer-string) substitutions)) 23 | (error "Missing template file %s" template-file)))) 24 | 25 | (defun ecukes-template-substitute (string substitutions) 26 | "Substitute all SUBSTITUTIONS in STRING." 27 | (-each 28 | substitutions 29 | (lambda (substitution) 30 | (let ((old (car substitution)) 31 | (new (cdr substitution))) 32 | (setq string (s-replace (format "{{%s}}" old) new string))))) 33 | string) 34 | 35 | (defun ecukes-template-write (write-to template &optional substitutions) 36 | "Write TEMPLATE to WRITE-TO with SUBSTITUTIONS." 37 | (let ((contents (ecukes-template-get template substitutions))) 38 | (with-temp-file write-to 39 | (insert contents)))) 40 | 41 | 42 | (provide 'ecukes-template) 43 | 44 | ;;; ecukes-template.el ends here 45 | -------------------------------------------------------------------------------- /test/ecukes-parse-regular-step-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (ert-deftest parse-regular-step-given () 4 | "Should parse given step." 5 | (with-parse-step 6 | "given" 7 | (lambda (name head body type arg) 8 | (should (eq type 'regular)) 9 | (should (equal name "Given a known state")) 10 | (should (equal body "a known state"))))) 11 | 12 | (ert-deftest parse-regular-step-when () 13 | "Should parse when step." 14 | (with-parse-step 15 | "when" 16 | (lambda (name head body type arg) 17 | (should (eq type 'regular)) 18 | (should (equal name "When the key action")) 19 | (should (equal body "the key action"))))) 20 | 21 | (ert-deftest parse-regular-step-then () 22 | "Should parse then step." 23 | (with-parse-step 24 | "then" 25 | (lambda (name head body type arg) 26 | (should (eq type 'regular)) 27 | (should (equal name "Then observe outcomes")) 28 | (should (equal body "observe outcomes"))))) 29 | 30 | (ert-deftest parse-regular-step-and () 31 | "Should parse and step." 32 | (with-parse-step 33 | "and" 34 | (lambda (name head body type arg) 35 | (should (eq type 'regular)) 36 | (should (equal name "And another key action")) 37 | (should (equal body "another key action"))))) 38 | 39 | (ert-deftest parse-regular-step-but () 40 | "Should parse but step." 41 | (with-parse-step 42 | "but" 43 | (lambda (name head body type arg) 44 | (should (eq type 'regular)) 45 | (should (eq type 'regular)) 46 | (should (equal name "But observe outcomes"))))) 47 | -------------------------------------------------------------------------------- /test/ecukes-parse-table-step-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (ert-deftest parse-table-step-single-row () 4 | "Should parse table with single row." 5 | (with-parse-step 6 | "table-single-row" 7 | (lambda (name head body type arg) 8 | (should (eq type 'table)) 9 | (should (equal arg '(("meal" "price") ("Hamburger" "$4.50"))))))) 10 | 11 | (ert-deftest parse-table-step-multiple-rows () 12 | "Should parse table with multiple rows." 13 | (with-parse-step 14 | "table-multiple-rows" 15 | (lambda (name head body type arg) 16 | (should (eq type 'table)) 17 | (should (equal arg '(("meal" "price") ("Hamburger" "$4.50") ("Pizza" "$5.30"))))))) 18 | 19 | (ert-deftest parse-table-step-wrong-indentation () 20 | "Should parse table with wrong indentation." 21 | (with-parse-step 22 | "table-wrong-indentation" 23 | (lambda (name head body type arg) 24 | (should (eq type 'table)) 25 | (should (equal arg '(("meal" "price") ("Hamburger" "$4.50") ("Pizza" "$5.30"))))))) 26 | 27 | (ert-deftest parse-table-step-same-row () 28 | "Should parse table with same row twice." 29 | (with-parse-step 30 | "table-same-row" 31 | (lambda (name head body type arg) 32 | (should (eq type 'table)) 33 | (should (equal arg '(("meal" "price") ("Hamburger" "$4.50") ("Hamburger" "$4.50"))))))) 34 | 35 | (ert-deftest parse-table-step-empty-columns () 36 | "Should parse table with empty columns." 37 | (with-parse-step 38 | "table-empty-columns" 39 | (lambda (name head body type arg) 40 | (should (eq type 'table)) 41 | (should (equal arg '(("meal" "price") ("Hamburger" "") ("Pizza" "$5.30"))))))) 42 | -------------------------------------------------------------------------------- /ecukes-hooks.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-hooks.el --- A number of hooks that allow us to run code at various points in the test cycle 2 | 3 | (require 'dash) 4 | 5 | (defvar ecukes-hooks-before () 6 | "List of before hooks.") 7 | 8 | (defvar ecukes-hooks-after () 9 | "List of after hooks.") 10 | 11 | (defvar ecukes-hooks-setup () 12 | "List of setup hooks.") 13 | 14 | (defvar ecukes-hooks-teardown () 15 | "List of teardown hooks.") 16 | 17 | 18 | (defmacro define-hook (list body) 19 | `(add-to-list ,list (lambda () ,@body) t)) 20 | 21 | (defmacro Before (&rest body) 22 | "Run BODY in before hook." 23 | `(define-hook 'ecukes-hooks-before ,body)) 24 | 25 | (defmacro After (&rest body) 26 | "Run BODY in after hook." 27 | `(define-hook 'ecukes-hooks-after ,body)) 28 | 29 | (defmacro Setup (&rest body) 30 | "Run BODY in setup hook." 31 | `(define-hook 'ecukes-hooks-setup ,body)) 32 | 33 | (defmacro Teardown (&rest body) 34 | "Run BODY in teardown hook." 35 | `(define-hook 'ecukes-hooks-teardown ,body)) 36 | 37 | 38 | (defun ecukes-hooks-run-before () 39 | "Run all before hooks." 40 | (ecukes-hooks-run ecukes-hooks-before)) 41 | 42 | (defun ecukes-hooks-run-after () 43 | "Run all after hooks." 44 | (ecukes-hooks-run ecukes-hooks-after)) 45 | 46 | (defun ecukes-hooks-run-setup () 47 | "Run all setup hooks." 48 | (ecukes-hooks-run ecukes-hooks-setup)) 49 | 50 | (defun ecukes-hooks-run-teardown () 51 | "Run all teardown hooks." 52 | (ecukes-hooks-run ecukes-hooks-teardown)) 53 | 54 | (defun ecukes-hooks-run (hooks) 55 | "Run HOOKS." 56 | (-each hooks 'funcall)) 57 | 58 | (provide 'ecukes-hooks) 59 | 60 | ;;; ecukes-hooks.el ends here 61 | -------------------------------------------------------------------------------- /ecukes-def.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-def.el --- Data structure definitions 2 | 3 | (eval-when-compile (require 'cl)) 4 | 5 | (defstruct ecukes-feature 6 | "A feature is the top level structure for a feature file." 7 | intro background outlines scenarios) 8 | 9 | (defstruct ecukes-intro 10 | "A feature introduction is a description of a feature. It is 11 | optional, but is conventionally included." 12 | header description) 13 | 14 | (defstruct ecukes-background 15 | "A feature background is a few steps that are run before each scenario." 16 | steps) 17 | 18 | (defstruct ecukes-outline 19 | "A scenario outline contains examples that are used to generate concrete scenarios." 20 | name steps tags table) 21 | 22 | (defstruct ecukes-scenario 23 | "A feature scenario is a scenario that is built up by steps." 24 | name steps tags) 25 | 26 | (defstruct ecukes-step 27 | "A step is some kind of action." 28 | name head body arg type err) 29 | 30 | (defstruct ecukes-step-def 31 | "A step definition." 32 | regex fn doc file) 33 | 34 | (defun ecukes-step-file-name (step &optional relative) 35 | "Return the file in which STEP is defined. 36 | File name is converted to \".el\" if it exists, otherwise 37 | \".elc\" file may be returned. When the second argument RELATIVE 38 | is given, return relative path." 39 | (let* ((file (ecukes-step-def-file step)) 40 | (el (when file 41 | (replace-regexp-in-string "\\.elc\\'" ".el" file)))) 42 | (when (and el (file-exists-p el)) 43 | (setq file el)) 44 | (if (and file relative) 45 | (file-relative-name file) 46 | file))) 47 | 48 | (provide 'ecukes-def) 49 | 50 | ;;; ecukes-def.el ends here 51 | -------------------------------------------------------------------------------- /test/ecukes-parse-py-string-step-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (ert-deftest parse-py-string-step-all-good () 4 | "Should parse py string step when all good." 5 | (with-parse-step 6 | "py-string-all-good" 7 | (lambda (name head body type arg) 8 | (should (eq type 'py-string)) 9 | (should (equal arg "Lorem ipsum dolor sit amet.\nCurabitur pellentesque iaculis eros."))))) 10 | 11 | (ert-deftest parse-py-string-step-line-break () 12 | "Should parse py string step with line break." 13 | (with-parse-step 14 | "py-string-line-break" 15 | (lambda (name head body type arg) 16 | (should (eq type 'py-string)) 17 | (should (equal arg "Lorem ipsum dolor sit amet.\n\nCurabitur pellentesque iaculis eros."))))) 18 | 19 | (ert-deftest parse-py-string-step-wrong-indentation () 20 | "Should parse py string when wrong indentation." 21 | (with-parse-step 22 | "py-string-wrong-indentation" 23 | (lambda (name head body type arg) 24 | (should (eq type 'py-string)) 25 | (should (equal arg " Lorem ipsum dolor sit amet.\nrabitur pellentesque iaculis eros."))))) 26 | 27 | (ert-deftest parse-py-string-step-same-lines () 28 | "Should parse py string when same lines." 29 | (with-parse-step 30 | "py-string-same-lines" 31 | (lambda (name head body type arg) 32 | (should (eq type 'py-string)) 33 | (should (equal arg "Lorem ipsum\nLorem ipsum"))))) 34 | 35 | (ert-deftest parse-py-string-step-two-empty-lines () 36 | "Should parse py string with several empty lines." 37 | (with-parse-step 38 | "py-string-two-empty-lines" 39 | (lambda (name head body type arg) 40 | (should (eq type 'py-string)) 41 | (should (equal arg "abc\n\ndef\n"))))) 42 | -------------------------------------------------------------------------------- /test/ecukes-parse-background-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (defun with-parse-background (name fn) 4 | (let* ((feature-file (fixture-file-path "background" name)) 5 | (feature (ecukes-parse-feature feature-file)) 6 | (background (ecukes-feature-background feature)) 7 | (steps (if background (ecukes-background-steps background) ()))) 8 | (funcall fn background (mapcar 'ecukes-step-name steps)))) 9 | 10 | (defun should-parse-background (name) 11 | "Parsing NAME feature should parse background correctly." 12 | (with-parse-background 13 | name 14 | (lambda (background step-names) 15 | (should 16 | (equal 17 | step-names 18 | '("Given a known state" 19 | "When the key action" 20 | "Then observe outcomes")))))) 21 | 22 | (ert-deftest parse-background-all-good () 23 | "Should parse background when all good." 24 | (should-parse-background "all-good")) 25 | 26 | (ert-deftest parse-background-with-intro () 27 | "Should parse background when intro." 28 | (should-parse-background "with-intro")) 29 | 30 | (ert-deftest parse-background-wrong-indentation () 31 | "Should parse background when wrong indentation." 32 | (should-parse-background "wrong-indentation")) 33 | 34 | (ert-deftest parse-background-line-breaks () 35 | "Should parse background when line breaks." 36 | (should-parse-background "line-breaks")) 37 | 38 | (ert-deftest parse-background-comment-breaks () 39 | "Should parse background when comment breaks." 40 | (should-parse-background "comment-breaks")) 41 | 42 | (ert-deftest parse-background-comments () 43 | "Should not parse background when comments." 44 | (with-parse-background 45 | "comments" 46 | (lambda (background step-names) 47 | (should-not background)))) 48 | -------------------------------------------------------------------------------- /test/ecukes-parse-scenario-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (defun should-parse-scenario (name) 4 | (with-parse-scenario 5 | name 6 | (lambda (scenario name step-names tags) 7 | (should (equal name "Add two numbers")) 8 | (should 9 | (equal 10 | step-names 11 | '("Given I have entered 50 into the calculator" 12 | "And I have entered 70 into the calculator" 13 | "When I press add" 14 | "Then the result should be 120 on the screen")))))) 15 | 16 | 17 | (ert-deftest parse-scenario-all-good () 18 | "Should parse when all good." 19 | (should-parse-scenario "all-good")) 20 | 21 | (ert-deftest parse-scenario-extra-space-in-header () 22 | "Should parse when extra space in header." 23 | (should-parse-scenario "extra-space-in-header")) 24 | 25 | (ert-deftest parse-scenario-no-space-in-header () 26 | "Should parse when no space in header." 27 | (should-parse-scenario "no-space-in-header")) 28 | 29 | (ert-deftest parse-scenario-line-breaks () 30 | "Should parse when line breaks." 31 | (should-parse-scenario "line-breaks")) 32 | 33 | (ert-deftest parse-scenario-wrong-indentation () 34 | "Should parse when wrong indentation." 35 | (should-parse-scenario "wrong-indentation")) 36 | 37 | (ert-deftest parse-scenario-comment-breaks () 38 | "Should parse when comment breaks." 39 | (should-parse-scenario "comment-breaks")) 40 | 41 | (ert-deftest parse-scenario-comments () 42 | "Should not parse when comments." 43 | (with-parse-scenario 44 | "comments" 45 | (lambda (scenario name step-names tags) 46 | (should-not scenario)))) 47 | 48 | (ert-deftest parse-scenario-same-step-names () 49 | "Should parse when same step names." 50 | (with-parse-scenario 51 | "same-step-names" 52 | (lambda (scenario name step-names tags) 53 | (should (equal (length step-names) 2))))) 54 | 55 | (ert-deftest parse-scenario-whitespace () 56 | "Should parse when whitespace under header." 57 | (should-parse-scenario "whitespace")) 58 | -------------------------------------------------------------------------------- /ecukes.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes.el --- Cucumber for Emacs 2 | 3 | (defvar ecukes-path 4 | (file-name-directory load-file-name) 5 | "Path to ecukes.") 6 | 7 | (add-to-list 'load-path ecukes-path) 8 | 9 | (require 'ecukes-run) 10 | (require 'ecukes-stats) 11 | (require 'ecukes-setup) 12 | (require 'ansi-color) 13 | 14 | (defun ecukes (&optional ask-for-tags) 15 | (interactive "P") 16 | (unless (ecukes-project-path) 17 | (error "You are not visiting an Ecukes project.")) 18 | (ecukes-setup) 19 | (if ask-for-tags 20 | (ecukes-setup-tags (read-string "Run tags: "))) 21 | (let ((feature-files 22 | (if (s-matches? "\.feature$" (buffer-file-name)) 23 | (list (buffer-file-name)) 24 | (directory-files (ecukes-project-features-path) t "\\.feature$")))) 25 | (let ((ecukes-buffer (get-buffer-create "*ecukes*")) 26 | (buffers (buffer-list)) 27 | (ecukes-internal-message-log) 28 | (ecukes-stats-steps 0) 29 | (ecukes-stats-steps-passed 0) 30 | (ecukes-stats-steps-failed 0) 31 | (ecukes-stats-steps-skipped 0) 32 | (ecukes-stats-scenarios 0) 33 | (ecukes-stats-scenarios-passed 0) 34 | (ecukes-stats-scenarios-failed 0)) 35 | (ecukes-run feature-files) 36 | (save-excursion 37 | (set-buffer ecukes-buffer) 38 | (erase-buffer) 39 | (-map 40 | (lambda (log) 41 | (let ((type (car log)) 42 | (message (cdr log))) 43 | (if (eq type 'message) 44 | (insert (ansi-color-apply message) "\n")))) 45 | ecukes-internal-message-log) 46 | (font-lock-mode t) 47 | (toggle-read-only 1)) 48 | (display-buffer ecukes-buffer) 49 | (-each 50 | (buffer-list) 51 | (lambda (buffer) 52 | (unless (-contains? buffers buffer) 53 | (let ((buffer-modified-p nil)) 54 | (kill-buffer buffer)))))))) 55 | 56 | (provide 'ecukes) 57 | 58 | ;;; ecukes.el ends here 59 | -------------------------------------------------------------------------------- /ecukes: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # ecukes --- Cucumber for Emacs 4 | 5 | # Copyright (C) 2010-2013 Johan Andersson 6 | # Copyright (C) 2012 Joel McCracken 7 | # Copyright (C) 2012 Dave Paroulek 8 | # Copyright (C) 2013 Kyle Hargraves 9 | # Copyright (C) 2013 Takafumi Arakaki 10 | 11 | # Author: Johan Andersson 12 | # Maintainer: Johan Andersson 13 | # Version: 0.4.3 14 | # Keywords: testing, cucumber 15 | # URL: http://ecukes.info 16 | 17 | # This file is NOT part of GNU Emacs. 18 | 19 | # License: 20 | 21 | # This program is free software; you can redistribute it and/or modify 22 | # it under the terms of the GNU General Public License as published by 23 | # the Free Software Foundation; either version 3, or (at your option) 24 | # any later version. 25 | 26 | # This program is distributed in the hope that it will be useful, 27 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 | # GNU General Public License for more details. 30 | 31 | # You should have received a copy of the GNU General Public License 32 | # along with GNU Emacs; see the file COPYING. If not, write to the 33 | # Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 34 | # Boston, MA 02110-1301, USA. 35 | 36 | ECUKES_HOME=$(dirname $BASH_SOURCE) 37 | ECUKES_CLI=$ECUKES_HOME/ecukes-cli.el 38 | ECUKES_TERM=$ECUKES_HOME/ecukes-term.el 39 | 40 | if [ -z "$ECUKES_EMACS" ] ; then 41 | if [ -z "$EMACS" ] ; then 42 | ECUKES_EMACS="emacs" 43 | else 44 | ECUKES_EMACS="$EMACS" 45 | fi 46 | fi 47 | 48 | if [ "$1" == "-h" -o "$1" == "--help" ] ; then 49 | cat $ECUKES_HOME/templates/usage.tpl 50 | elif [ "$1" == "--list-steps" ] ; then 51 | "$ECUKES_EMACS" --script "$ECUKES_CLI" -f ecukes-cli-print-steps "${@:2}" 52 | elif [ "$1" == "--new" ] ; then 53 | "$ECUKES_EMACS" --script "$ECUKES_CLI" -f ecukes-new 54 | elif [ "$1" == "--script" ] ; then 55 | "$ECUKES_EMACS" --script "$ECUKES_TERM" "${@:2}" 56 | else 57 | export ECUKES_OUTFILE=$(mktemp /tmp/ecukes.XXX) 58 | 59 | if [[ "$1" == "--win" ]]; then 60 | "$ECUKES_EMACS" --load "$ECUKES_TERM" -Q "$@" 61 | else 62 | "$ECUKES_EMACS" -nw --load "$ECUKES_TERM" -Q "$@" 63 | fi 64 | 65 | STATUS=$? 66 | 67 | cat $ECUKES_OUTFILE 68 | rm -f $ECUKES_OUTFILE 69 | 70 | exit $STATUS 71 | fi 72 | -------------------------------------------------------------------------------- /ecukes-new.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-new.el --- Setup up Ecukes for a project 2 | 3 | (require 's) 4 | (require 'dash) 5 | (require 'ansi) 6 | (require 'ecukes-project) 7 | (require 'ecukes-template) 8 | 9 | (defun ecukes-new () 10 | "Create new Ecukes setup for project." 11 | (if (ecukes-new-exists-p) 12 | (error 13 | (ansi-red "Ecukes already exists for this project")) 14 | (ecukes-new-create-root) 15 | (ecukes-new-create-step-definitions) 16 | (ecukes-new-create-support) 17 | (ecukes-new-create-feature))) 18 | 19 | (defun ecukes-new-create-root () 20 | "Create features directory." 21 | (make-directory (ecukes-project-features-path)) 22 | (ecukes-new-message 0 "features")) 23 | 24 | (defun ecukes-new-create-step-definitions () 25 | "Create step-definitions directory and step definition." 26 | (let ((step-definitions-path (expand-file-name "step-definitions" (ecukes-project-features-path)))) 27 | (make-directory step-definitions-path) 28 | (ecukes-new-message 2 "step-definition") 29 | (let ((step-definition 30 | (expand-file-name (format "%s-steps.el" (ecukes-project-name)) step-definitions-path))) 31 | (ecukes-template-write step-definition 'step-definition))) 32 | (ecukes-new-message 4 (ecukes-project-name) "-steps.el")) 33 | 34 | (defun ecukes-new-create-support () 35 | "Create support directory." 36 | (let ((support (expand-file-name "support" (ecukes-project-features-path)))) 37 | (make-directory support) 38 | (ecukes-new-message 2 "support") 39 | (let ((env (expand-file-name "env.el" support))) 40 | (ecukes-template-write env 'env `(("project-name" . ,(ecukes-project-name)))))) 41 | (ecukes-new-message 4 "env.el")) 42 | 43 | (defun ecukes-new-create-feature () 44 | "Create feature file." 45 | (let ((feature 46 | (expand-file-name 47 | (format "%s.feature" (ecukes-project-name)) (ecukes-project-features-path)))) 48 | (ecukes-template-write feature 'feature)) 49 | (ecukes-new-message 2 (format "%s.feature" (ecukes-project-name)))) 50 | 51 | (defun ecukes-new-exists-p () 52 | "Check if there already exist an Ecukes setup." 53 | (file-directory-p (ecukes-project-features-path))) 54 | 55 | (defun ecukes-new-message (indent &rest objects) 56 | "Print message about created file." 57 | (message 58 | "create%s%s" 59 | (s-repeat (1+ indent) " ") 60 | (ansi-green (apply 's-concat objects)))) 61 | 62 | (provide 'ecukes-new) 63 | 64 | ;;; ecukes-new.el ends here 65 | -------------------------------------------------------------------------------- /test/ecukes-new-test.el: -------------------------------------------------------------------------------- 1 | (require 'ansi) 2 | (require 'ecukes-new) 3 | 4 | (ert-deftest new-features-does-exist () 5 | "Should exist when there is a features directory." 6 | (with-mock 7 | (stub file-directory-p => t) 8 | (should (ecukes-new-exists-p)))) 9 | 10 | (ert-deftest new-features-does-not-exist () 11 | "Should not exist when there is no features directory." 12 | (with-mock 13 | (stub file-directory-p => nil) 14 | (should-not (ecukes-new-exists-p)))) 15 | 16 | (ert-deftest new-should-print-error-message-when-already-exists () 17 | "Should print error message when already exists." 18 | (with-mock 19 | (stub ecukes-new-exists-p => t) 20 | (mock (error (ansi-red "Ecukes already exists for this project") :times 1)) 21 | (ecukes-new))) 22 | 23 | (ert-deftest new-should-create-setup-when-not-exists () 24 | "Should create setup when not exist." 25 | (with-mock 26 | (stub ecukes-new-exists-p => nil) 27 | (stub ecukes-new-message) 28 | (mock (ecukes-new-create-root) :times 1) 29 | (mock (ecukes-new-create-step-definitions) :times 1) 30 | (mock (ecukes-new-create-support) :times 1) 31 | (mock (ecukes-new-create-feature) :times 1) 32 | (ecukes-new))) 33 | 34 | (ert-deftest new-should-create-root () 35 | "Should create features directory." 36 | (with-mock 37 | (with-project 38 | (mock (make-directory "/path/to/project/features") :times 1) 39 | (mock (ecukes-new-message) :times 1) 40 | (ecukes-new-create-root)))) 41 | 42 | (ert-deftest new-should-create-step-definitions () 43 | "Should create step definitions directory and step definition." 44 | (with-mock 45 | (with-project 46 | (mock (make-directory "/path/to/project/features/step-definitions") :times 1) 47 | (mock (ecukes-template-write) :times 1) 48 | (mock (ecukes-new-message) :times 2) 49 | (ecukes-new-create-step-definitions)))) 50 | 51 | (ert-deftest new-should-create-support () 52 | "Should create support directory with env file." 53 | (with-mock 54 | (with-project 55 | (mock (make-directory "/path/to/project/features/support") :times 1) 56 | (mock (ecukes-template-write "/path/to/project/features/support/env.el") :times 1) 57 | (mock (ecukes-new-message) :times 2) 58 | (ecukes-new-create-support)))) 59 | 60 | (ert-deftest new-should-create-feature () 61 | "Should create feature file." 62 | (with-mock 63 | (with-project 64 | (mock (ecukes-template-write "/path/to/project/features/project.feature") :times 1) 65 | (mock (ecukes-new-message) :times 1) 66 | (ecukes-new-create-feature)))) 67 | -------------------------------------------------------------------------------- /test/ecukes-parse-feature-tags-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (ert-deftest parse-feature-tags-single-tag () 4 | "Should parse single tag." 5 | (with-parse-feature 6 | "tags-single" 7 | (lambda (feature intro scenarios background steps) 8 | (should (equal (ecukes-scenario-tags (car scenarios)) '("debug")))))) 9 | 10 | (ert-deftest parse-feature-tags-multiple-tags () 11 | "Should parse multiple tags." 12 | (with-parse-feature 13 | "tags-multiple-tags" 14 | (lambda (feature intro scenarios background steps) 15 | (should (equal (ecukes-scenario-tags (car scenarios)) '("debug" "verbose")))))) 16 | 17 | (ert-deftest parse-feature-tags-multiple-scenarios () 18 | "Should parse multiple tags." 19 | (with-parse-feature 20 | "tags-multiple-scenarios" 21 | (lambda (feature intro scenarios background steps) 22 | (should (equal (ecukes-scenario-tags (nth 0 scenarios)) '("debug" "verbose"))) 23 | (should (equal (ecukes-scenario-tags (nth 1 scenarios)) '("debug" "verbose")))))) 24 | 25 | (ert-deftest parse-feature-tags-mixed () 26 | "Should parse multiple tags." 27 | (with-parse-feature 28 | "tags-multiple-mixed" 29 | (lambda (feature intro scenarios background steps) 30 | (should (equal (ecukes-scenario-tags (nth 0 scenarios)) '("debug" "verbose" "super"))) 31 | (should (equal (ecukes-scenario-tags (nth 1 scenarios)) '("debug" "verbose" "duper"))) 32 | (should (equal (ecukes-scenario-tags (nth 2 scenarios)) '("debug" "verbose" "duper")))))) 33 | 34 | (ert-deftest parse-feature-tags-tags-with-whitespace () 35 | "Should parse multiple tags despite whitespace." 36 | (with-parse-feature 37 | "tags-whitespace" 38 | (lambda (feature intro scenarios background steps) 39 | (should (equal (ecukes-scenario-tags (car scenarios)) '("debug" "verbose")))))) 40 | 41 | (ert-deftest parse-feature-tags-tags-with-same-name () 42 | "Should parse tag with same name only once." 43 | (with-parse-feature 44 | "same-name" 45 | (lambda (feature intro scenarios background steps) 46 | (should (equal (ecukes-scenario-tags (car scenarios)) '("debug")))))) 47 | 48 | (ert-deftest parse-feature-tags-tags-comment () 49 | "Should not parse when comment." 50 | (with-parse-feature 51 | "tags-comment" 52 | (lambda (feature intro scenarios background steps) 53 | (should-not (ecukes-scenario-tags (car scenarios)))))) 54 | 55 | (ert-deftest parse-feature-tags-tags-none () 56 | "Should parse when none." 57 | (with-parse-feature 58 | "tags-none" 59 | (lambda (feature intro scenarios background steps) 60 | (should-not (ecukes-scenario-tags (car scenarios)))))) 61 | -------------------------------------------------------------------------------- /ecukes-stats.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-stats.el --- Statistics about the passed and failed scenarios and steps 2 | 3 | (require 'ansi) 4 | (require 's) 5 | 6 | (defvar ecukes-stats-steps 0 7 | "Number of steps that have be runned.") 8 | 9 | (defvar ecukes-stats-steps-passed 0 10 | "Number of steps that have passed.") 11 | 12 | (defvar ecukes-stats-steps-failed 0 13 | "Number of steps that have failed.") 14 | 15 | (defvar ecukes-stats-steps-skipped 0 16 | "Number of steps that were skipped.") 17 | 18 | (defvar ecukes-stats-scenarios 0 19 | "Number of scenarios that have been runned.") 20 | 21 | (defvar ecukes-stats-scenarios-passed 0 22 | "Number of scenarios that have passed.") 23 | 24 | (defvar ecukes-stats-scenarios-failed 0 25 | "Number of scenarios that have failed.") 26 | 27 | (defun ecukes-stats-reset () 28 | "Reset stats." 29 | (setq ecukes-stats-steps 0) 30 | (setq ecukes-stats-steps-passed 0) 31 | (setq ecukes-stats-steps-failed 0) 32 | (setq ecukes-stats-steps-skipped 0) 33 | (setq ecukes-stats-scenarios 0) 34 | (setq ecukes-stats-scenarios-passed 0) 35 | (setq ecukes-stats-scenarios-failed 0)) 36 | 37 | (defmacro ecukes-stats-step (&rest body) 38 | `(progn 39 | (setq ecukes-stats-steps (1+ ecukes-stats-steps)) 40 | ,@body)) 41 | 42 | (defmacro ecukes-stats-scenario (&rest body) 43 | `(progn 44 | (setq ecukes-stats-scenarios (1+ ecukes-stats-scenarios)) 45 | ,@body)) 46 | 47 | 48 | (defun ecukes-stats-step-pass () 49 | "Step passed." 50 | (ecukes-stats-step 51 | (setq ecukes-stats-steps-passed (1+ ecukes-stats-steps-passed)))) 52 | 53 | (defun ecukes-stats-step-fail () 54 | "Step failed." 55 | (ecukes-stats-step 56 | (setq ecukes-stats-steps-failed (1+ ecukes-stats-steps-failed)))) 57 | 58 | (defun ecukes-stats-step-skip () 59 | "Step skipped." 60 | (ecukes-stats-step 61 | (setq ecukes-stats-steps-skipped (1+ ecukes-stats-steps-skipped)))) 62 | 63 | (defun ecukes-stats-scenario-pass () 64 | "Scenario passed." 65 | (ecukes-stats-scenario 66 | (setq ecukes-stats-scenarios-passed (1+ ecukes-stats-scenarios-passed)))) 67 | 68 | (defun ecukes-stats-scenario-fail () 69 | "Scenario failed." 70 | (ecukes-stats-scenario 71 | (setq ecukes-stats-scenarios-failed (1+ ecukes-stats-scenarios-failed)))) 72 | 73 | (defun ecukes-stats-step-summary () 74 | "Return step summary as a string." 75 | (if (zerop ecukes-stats-steps) 76 | "0 steps" 77 | (format 78 | "%d steps (%s, %s, %s)" 79 | ecukes-stats-steps 80 | (ansi-red "%d failed" ecukes-stats-steps-failed) 81 | (ansi-cyan "%d skipped" ecukes-stats-steps-skipped) 82 | (ansi-green "%d passed" ecukes-stats-steps-passed)))) 83 | 84 | (defun ecukes-stats-scenario-summary () 85 | "Return scenario summary as a string." 86 | (if (zerop ecukes-stats-scenarios) 87 | "0 scenarios" 88 | (format 89 | "%d scenarios (%s, %s)" 90 | ecukes-stats-scenarios 91 | (ansi-red "%d failed" ecukes-stats-scenarios-failed) 92 | (ansi-green "%d passed" ecukes-stats-scenarios-passed)))) 93 | 94 | (defun ecukes-stats-summary () 95 | "Return scenario and step summary." 96 | (s-join 97 | "\n" 98 | (list 99 | (ecukes-stats-scenario-summary) 100 | (ecukes-stats-step-summary)))) 101 | 102 | 103 | (provide 'ecukes-stats) 104 | 105 | ;;; ecukes-stats.el ends here 106 | -------------------------------------------------------------------------------- /test/ecukes-parse-scenario-outline-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (defun with-parse-outline (name fn) 4 | (let* ((feature-file (fixture-file-path "outlines" name)) 5 | (feature (ecukes-parse-feature feature-file)) 6 | (outlines (ecukes-feature-outlines feature)) 7 | (scenarios (ecukes-feature-scenarios feature))) 8 | (funcall fn outlines scenarios))) 9 | 10 | (defun should-parse-outline (name expected-examples &optional tests) 11 | (with-parse-outline 12 | name 13 | (lambda (outlines scenarios) 14 | (should (= 1 (length outlines))) 15 | (let* ((outline (car outlines)) 16 | (tags (ecukes-outline-tags outline)) 17 | (table (ecukes-outline-table outline))) 18 | (should (equal expected-examples (1- (length table)))) 19 | (should (equal expected-examples (length scenarios))) 20 | (should (-all? (lambda (scenario) (equal tags (ecukes-scenario-tags scenario))) scenarios)) 21 | (when (functionp tests) (funcall tests (car outlines) scenarios)))))) 22 | 23 | (ert-deftest parse-outline-no-examples () 24 | "Should parse scenario outlines with no examples." 25 | (with-parse-outline 26 | "no-examples" 27 | (lambda (outlines scenarios) 28 | (should (= 1 (length outlines))) 29 | (should-not (ecukes-outline-table (car outlines))) 30 | (should (= 0 (length scenarios)))))) 31 | 32 | (ert-deftest parse-outline-one-example () 33 | "Should parse scenario outline with one example." 34 | (should-parse-outline 35 | "one-example" 1 36 | (lambda (outline scenarios) 37 | (should (ecukes-outline-table outline)) 38 | (should (= 1 (length scenarios)))))) 39 | 40 | (ert-deftest parse-outline-multiple-examples () 41 | "Should parse scenario outlines with multiple examples." 42 | (should-parse-outline "multiple-examples" 3)) 43 | 44 | (ert-deftest parse-outline-tags () 45 | "Should parse scenario outlines with tags." 46 | (should-parse-outline "tags" 2)) 47 | 48 | (ert-deftest parse-outline-background () 49 | "Should parse scenario outlines with backgrounds." 50 | (should-parse-outline "background" 1)) 51 | 52 | (ert-deftest parse-outline-bad-indents () 53 | "Should parse scenario outlines with bad indentation." 54 | (should-parse-outline "bad-indents" 2)) 55 | 56 | (ert-deftest parse-outline-substitution () 57 | "Should substitute the values from examples into the generated scenarios." 58 | (should-parse-outline 59 | "substitution" 3 60 | (lambda (outline scenarios) 61 | (let* ((scenario (car scenarios)) 62 | (simple-step (car (ecukes-scenario-steps scenario))) 63 | (py-step (nth 1 (ecukes-scenario-steps scenario))) 64 | (table-step (nth 2 (ecukes-scenario-steps scenario)))) 65 | (should (string= "Given I want to marry" (ecukes-step-name simple-step))) 66 | (should (string= "I want to marry" (ecukes-step-body simple-step))) 67 | (should-not (ecukes-step-arg simple-step)) 68 | 69 | (should (string= "You are great! I want to marry you." (ecukes-step-arg py-step))) 70 | 71 | (should (equal '(("response" "desired") ("positive" "true")) 72 | (ecukes-step-arg table-step))))))) 73 | 74 | (ert-deftest parse-outline-wrong-column-names () 75 | "Generates scenarios without any substitutions if your column names are wrong." 76 | (should-parse-outline 77 | "wrong-column-names" 1 78 | (lambda (outline scenarios) 79 | (let ((step (car (ecukes-scenario-steps (car scenarios))))) 80 | (should (string= "Given is " (ecukes-step-name step))))))) 81 | -------------------------------------------------------------------------------- /ecukes-steps.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-steps.el --- Functions to define and call step definitions 2 | 3 | (eval-when-compile (require 'cl)) 4 | 5 | (require 'ansi) 6 | 7 | (require 'ecukes-parse) 8 | 9 | (defvar ecukes-steps-definitions nil 10 | "All defined step definitions.") 11 | 12 | 13 | ;;;###autoload 14 | (defalias 'Given 'ecukes-steps-define-or-call-step 15 | "Put the system in a known state.") 16 | 17 | ;;;###autoload 18 | (defalias 'When 'ecukes-steps-define-or-call-step 19 | "Describe the key action.") 20 | 21 | ;;;###autoload 22 | (defalias 'Then 'ecukes-steps-define-or-call-step 23 | "Observe outcomes.") 24 | 25 | ;;;###autoload 26 | (defalias 'And 'ecukes-steps-define-or-call-step 27 | "Make Given/When/Then read more fluently.") 28 | 29 | ;;;###autoload 30 | (defalias 'But 'ecukes-steps-define-or-call-step 31 | "Make Given/When/Then read more fluently.") 32 | 33 | ;;;###autoload 34 | (defun ecukes-steps-define-or-call-step (name &rest args) 35 | "Define or call step. 36 | 37 | When *defining* a step, argument takes the following form: 38 | (STEP-REGEXP [DOC] FUNCTION) 39 | where STEP-REGEXP is a regular expression defining a step and 40 | FUNCTION is the definition of the step. You can optionally 41 | give a docstring DOC as the second argument. 42 | 43 | When *calling* a step, argument takes the following form: 44 | (STEP-NAME [ARG [ARG ..]]) 45 | 46 | \(fn STEP-REGEXP [DOC] FUNCTION | STEP-NAME &optional ARGS)" 47 | (let ((fn (car (last args))) 48 | (doc (when (= (length args) 2) (car args)))) 49 | (if (functionp fn) 50 | ;; `buffer-file-name' is for the case evaluated interactively. 51 | (ecukes-steps-define name fn doc 52 | (or load-file-name buffer-file-name)) 53 | (ecukes-steps-call name args)))) 54 | 55 | ;;;###autoload 56 | (put 'ecukes-steps-define-or-call-step 'lisp-indent-function 'defun) 57 | ;;;###autoload 58 | (put 'ecukes-steps-define-or-call-step 'doc-string-elt 2) 59 | 60 | (defun ecukes-steps-define (regex fn &optional doc file) 61 | "Define step." 62 | (unless (-any? 63 | (lambda (step-def) 64 | (equal regex step-def)) ecukes-steps-definitions) 65 | (add-to-list 66 | 'ecukes-steps-definitions 67 | (make-ecukes-step-def :regex regex :fn fn :doc doc :file file)))) 68 | 69 | (defun ecukes-steps-call (name args) 70 | "Call step" 71 | (let* ((query (apply 'format (cons name args))) 72 | (step-def (ecukes-steps-find query))) 73 | (if step-def 74 | (apply (ecukes-step-def-fn step-def) 75 | (or args 76 | (ecukes-steps-args 77 | (make-ecukes-step :body name)))) 78 | (error (ansi-red "Step not defined: `%s`" query))))) 79 | 80 | (defun ecukes-steps-missing-definition (steps) 81 | "Return from STEPS those who have not been defined." 82 | (-reject 83 | (lambda (step) 84 | (ecukes-steps-find (ecukes-step-body step))) steps)) 85 | 86 | (defun ecukes-steps-find (name) 87 | "Find step by name." 88 | (-first 89 | (lambda (step-def) 90 | (s-matches? (ecukes-step-def-regex step-def) name)) 91 | ecukes-steps-definitions)) 92 | 93 | (defun ecukes-steps-args (step) 94 | "Return args from step BODY." 95 | (let* ((body (ecukes-step-body step)) 96 | (step-def (ecukes-steps-find body))) 97 | (if step-def 98 | (cdr (s-match (ecukes-step-def-regex step-def) body)) 99 | (loop for sub on (cdr (split-string body "\"")) 100 | by (function cddr) 101 | collect (car sub))))) 102 | 103 | (provide 'ecukes-steps) 104 | 105 | ;;; ecukes-steps.el ends here 106 | -------------------------------------------------------------------------------- /test/test-helper.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (defmacro with-steps (&rest body) 4 | `(let ((ecukes-steps-definitions)) 5 | ,@body)) 6 | 7 | (defmacro with-hooks (&rest body) 8 | `(let ((ecukes-hooks-before) 9 | (ecukes-hooks-after) 10 | (ecukes-hooks-setup) 11 | (ecukes-hooks-teardown)) 12 | ,@body)) 13 | 14 | (defun mock-step (name &rest overrides) 15 | (let* ((matches (s-match ecukes-parse-step-re name)) 16 | (name (or (plist-get overrides :name) name)) 17 | (head (or (plist-get overrides :head) (nth 1 matches))) 18 | (body (or (plist-get overrides :body) (nth 2 matches))) 19 | (type (or (plist-get overrides :type) 'regular)) 20 | (arg (plist-get overrides :arg)) 21 | (err (plist-get overrides :err)) 22 | (properties 23 | (list :name name :head head :body body :arg arg :type type :err err))) 24 | (apply 'make-ecukes-step properties))) 25 | 26 | (defun with-parse-step (name fn) 27 | (let* ((feature-file (fixture-file-path "step" name)) 28 | (feature (ecukes-parse-feature feature-file)) 29 | (scenarios (ecukes-feature-scenarios feature)) 30 | (scenario (car scenarios)) 31 | (steps (ecukes-scenario-steps scenario)) 32 | (step (car steps)) 33 | (name (ecukes-step-name step)) 34 | (head (ecukes-step-head step)) 35 | (body (ecukes-step-body step)) 36 | (type (ecukes-step-type step)) 37 | (arg (ecukes-step-arg step))) 38 | (funcall fn name head body type arg))) 39 | 40 | (defun with-parse-scenario (name fn) 41 | (let* ((feature-file (fixture-file-path "scenario" name)) 42 | (feature (ecukes-parse-feature feature-file)) 43 | (scenarios (ecukes-feature-scenarios feature)) 44 | (scenario (car scenarios)) 45 | (name) (step-names) (tags)) 46 | (condition-case err 47 | (progn 48 | (setq name (ecukes-scenario-name scenario)) 49 | (setq step-names (mapcar 'ecukes-step-name (ecukes-scenario-steps scenario))) 50 | (setq tags (ecukes-scenario-tags scenario))) 51 | (error)) 52 | (funcall fn scenario name step-names tags))) 53 | 54 | (defun with-parse-feature (name fn) 55 | (let* ((feature-file (fixture-file-path "feature" name)) 56 | (feature (ecukes-parse-feature feature-file)) 57 | (intro (ecukes-feature-intro feature)) 58 | (scenarios (ecukes-feature-scenarios feature)) 59 | (background (ecukes-feature-background feature)) 60 | (steps 61 | (-concat 62 | (if background 63 | (ecukes-background-steps background)) 64 | (if scenarios 65 | (-flatten 66 | (-map 67 | (lambda (scenario) 68 | (ecukes-scenario-steps scenario)) scenarios)))))) 69 | (funcall fn feature intro scenarios background steps))) 70 | 71 | (defun with-messages (callback) 72 | (let ((messages)) 73 | (flet ((ecukes-print-message 74 | (format-string &rest args) 75 | (add-to-list 'messages (apply 'ecukes-print-format (cons format-string args)) t 'eq))) 76 | (funcall callback messages)))) 77 | 78 | (defmacro with-stats (&rest body) 79 | `(let ((ecukes-stats-steps 0) 80 | (ecukes-stats-steps-passed 0) 81 | (ecukes-stats-steps-failed 0) 82 | (ecukes-stats-steps-skipped 0) 83 | (ecukes-stats-scenarios 0) 84 | (ecukes-stats-scenarios-passed 0) 85 | (ecukes-stats-scenarios-failed 0)) 86 | ,@body)) 87 | 88 | (defmacro with-project (&rest body) 89 | `(with-mock 90 | (stub ecukes-project-path => "/path/to/project") 91 | ,@body)) 92 | 93 | (defun fixture-file-path (category name) 94 | (let ((category-path (expand-file-name category ecukes-fixtures-path))) 95 | (expand-file-name (format "%s.feature" name) category-path))) 96 | -------------------------------------------------------------------------------- /test/ecukes-parse-intro-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (defun with-parse-intro (name fn) 4 | (let* ((feature-file (fixture-file-path "intro" name)) 5 | (feature (ecukes-parse-feature feature-file)) 6 | (intro (ecukes-feature-intro feature)) 7 | (header) (description)) 8 | (condition-case err 9 | (progn 10 | (setq header (ecukes-intro-header intro)) 11 | (setq description (ecukes-intro-description intro))) 12 | (error)) 13 | (funcall fn intro header description))) 14 | 15 | (defun should-parse-number-addition (name) 16 | "Parsing NAME feature should parse number addition correctly." 17 | (with-parse-intro 18 | name 19 | (lambda (intro header description) 20 | (should (equal header "Addition of two numbers")) 21 | (should 22 | (equal 23 | description 24 | '("In order to aviod silly mistakes" 25 | "As a math idiot" 26 | "I want to be told the sum of two numbers")))))) 27 | 28 | (ert-deftest parse-intro-no-intro () 29 | "Should not parse intro none." 30 | (with-parse-intro 31 | "no-intro" 32 | (lambda (intro header description) 33 | (should-not intro)))) 34 | 35 | (ert-deftest parse-intro-comments () 36 | "Should not parse when comments." 37 | (with-parse-intro 38 | "comments" 39 | (lambda (intro header description) 40 | (should-not intro)))) 41 | 42 | (ert-deftest parse-intro-all-good-new () 43 | "Should parse when all good." 44 | (should-parse-number-addition "all-good")) 45 | 46 | (ert-deftest parse-intro-spaces-above () 47 | "Should parse when spaces above." 48 | (should-parse-number-addition "spaces-above")) 49 | 50 | (ert-deftest parse-intro-comments-above () 51 | "Should parse when commants above." 52 | (should-parse-number-addition "comments-above")) 53 | 54 | (ert-deftest parse-intro-no-space-in-header () 55 | "Should parse when no space in header." 56 | (should-parse-number-addition "no-space-in-header")) 57 | 58 | (ert-deftest parse-intro-extra-space-in-header () 59 | "Should parse when extra space in header." 60 | (should-parse-number-addition "extra-space-in-header")) 61 | 62 | (ert-deftest parse-intro-wrong-indentation () 63 | "Should parse when wrong indentation." 64 | (should-parse-number-addition "wrong-indentation")) 65 | 66 | (ert-deftest parse-intro-line-breaks () 67 | "Should parse when line breaks." 68 | (should-parse-number-addition "line-breaks")) 69 | 70 | (ert-deftest parse-intro-new-section-background () 71 | "Should stop when entering background." 72 | (should-parse-number-addition "section-background")) 73 | 74 | (ert-deftest parse-intro-new-section-scenario () 75 | "Should stop when entering scenario." 76 | (should-parse-number-addition "section-scenario")) 77 | 78 | (ert-deftest parse-intro-fewer-description-lines () 79 | "Should parse when fewer description lines." 80 | (with-parse-intro 81 | "fewer-description-lines" 82 | (lambda (intro header description) 83 | (should (equal header "Addition of two numbers")) 84 | (should 85 | (equal 86 | description 87 | '("As a math idiot I want to aviod silly mistakes and be told the sum of two numbers")))))) 88 | 89 | (ert-deftest parse-intro-more-description-lines () 90 | "Should parse when more description lines." 91 | (with-parse-intro 92 | "more-description-lines" 93 | (lambda (intro header description) 94 | (should (equal header "Addition of two numbers")) 95 | (should 96 | (equal 97 | description 98 | '("In order to aviod silly mistakes" 99 | "As a math idiot" 100 | "And as an idiot in general" 101 | "I want to be told the sum of two numbers")))))) 102 | 103 | (ert-deftest parse-intro-scenario-tags () 104 | "Should not consider scenario tags part of intro." 105 | (with-parse-intro 106 | "scenario-tags" 107 | (lambda (intro header description) 108 | (should (equal header "Addition of two numbers")) 109 | (should 110 | (equal 111 | description 112 | '("In order to aviod silly mistakes" 113 | "As a math idiot" 114 | "I want to be told the sum of two numbers")))))) 115 | -------------------------------------------------------------------------------- /ecukes-setup.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-setup.el --- Common setup for drivers 2 | 3 | (eval-when-compile 4 | (require 'cl) 5 | (defvar debug-on-entry)) 6 | (require 's) 7 | (require 'dash) 8 | (require 'ansi) 9 | (require 'ecukes-def) 10 | (require 'ecukes-steps) 11 | (require 'ecukes-hooks) 12 | (require 'ecukes-project) 13 | (require 'ecukes-template) 14 | (require 'ecukes-helpers) 15 | 16 | 17 | (defvar ecukes-include-tags nil 18 | "List of tags to test.") 19 | 20 | (defvar ecukes-exclude-tags nil 21 | "List of tags not to test.") 22 | 23 | (defvar ecukes-verbose nil 24 | "Show `message' output if true.") 25 | 26 | (defvar ecukes-message nil 27 | "Tells if current message if from Ecukes or external.") 28 | 29 | (defvar ecukes-internal-message-log nil 30 | "List with `message' output.") 31 | 32 | (defvar ecukes-message-log nil 33 | "List with `message' output (only from external code).") 34 | 35 | (defadvice message (around message-around activate) 36 | (let ((message 37 | (if (car (ad-get-args 0)) 38 | (apply 'format (ad-get-args 0)) 39 | ""))) 40 | (unless ecukes-message 41 | (add-to-list 'ecukes-message-log message t 'eq)) 42 | (when (or ecukes-message ecukes-verbose) 43 | (add-to-list 'ecukes-internal-message-log `(message . ,message) t 'eq) 44 | ad-do-it))) 45 | 46 | (defadvice print (around print-around activate) 47 | (add-to-list 'ecukes-internal-message-log `(print . ,ad-do-it) t 'eq)) 48 | 49 | (defun ecukes-quit (&optional exit-code) 50 | "Quit Emacs with EXIT-CODE and write to file if in graphical mode." 51 | (or exit-code (setq exit-code 1)) 52 | (let ((outfile (getenv "ECUKES_OUTFILE")) 53 | (output 54 | (-map 55 | (lambda (log) 56 | (let ((type (car log)) 57 | (message (cdr log))) 58 | (if (eq type 'print) 59 | (prin1-to-string message) 60 | message))) 61 | ecukes-internal-message-log))) 62 | (when outfile 63 | (with-temp-buffer 64 | (insert (s-join "\n" output) "\n") 65 | (write-file outfile nil)))) 66 | (kill-emacs exit-code)) 67 | 68 | (defun usage () 69 | "Show usage information and quit." 70 | (let ((ecukes-message t)) 71 | (message 72 | (ecukes-template-get 'usage)) 73 | (ecukes-quit))) 74 | 75 | (defun ecukes-setup () 76 | "Validate and load." 77 | (ecukes-setup-argv) 78 | (ecukes-setup-features-dir-exist) 79 | (ecukes-setup-load)) 80 | 81 | (defun ecukes-setup-argv () 82 | "Setup options from `argv'." 83 | (let ((options)) 84 | (when (-contains? argv "--dbg") 85 | (setq debug-on-error t) 86 | (setq debug-on-entry t) 87 | (setq ecukes-verbose t) 88 | (push "--dbg" options)) 89 | (when (-contains? argv "--verbose") 90 | (setq ecukes-verbose t) 91 | (push "--verbose" options)) 92 | (when (-contains? argv "--win") 93 | (push "--win" options)) 94 | (let ((is-tag)) 95 | (-each 96 | argv 97 | (lambda (arg) 98 | (cond ((equal arg "--tags") 99 | (push "--tags" options) 100 | (setq is-tag t)) 101 | (t 102 | (when is-tag 103 | (push arg options) 104 | (ecukes-setup-tags arg) 105 | (setq is-tag nil))))))) 106 | (setq argv (-difference argv options)))) 107 | 108 | (defun ecukes-setup-tags (tags) 109 | "Parse comma separated TAGS and set `ecukes-include-tags'." 110 | (-map 111 | (lambda (tag) 112 | (if (s-prefix-p "~" tag) 113 | (add-to-list 'ecukes-exclude-tags (substring tag 2) t) 114 | (add-to-list 'ecukes-include-tags (substring tag 1) t))) 115 | (split-string tags ","))) 116 | 117 | (defun ecukes-setup-features-dir-exist () 118 | "Print usage and quit if there's no features directory." 119 | (unless (file-directory-p (ecukes-project-features-path)) 120 | (let ((ecukes-message t)) 121 | (message 122 | (ansi-red "Missing `features` directory."))) 123 | (usage))) 124 | 125 | (defun ecukes-setup-load () 126 | "Load support and step definitions." 127 | (ecukes-setup-load-support) 128 | (ecukes-setup-load-step-definitions)) 129 | 130 | (defun ecukes-setup-load-support () 131 | "Load project support files." 132 | (let* ((env-file (expand-file-name "env.el" (ecukes-project-support-path))) 133 | (support-files 134 | (-reject 135 | (lambda (support-file) 136 | (s-equals? support-file env-file)) 137 | (directory-files (ecukes-project-support-path) t "\\.el$")))) 138 | (load env-file nil t) 139 | (-map 140 | (lambda (support-file) 141 | (load support-file nil t)) 142 | support-files))) 143 | 144 | (defun ecukes-setup-load-step-definitions () 145 | "Load project step definition files." 146 | (let ((step-definition-files (directory-files (ecukes-project-step-definitions-path) t "-steps\\.el$"))) 147 | (-map 148 | (lambda (step-definition-file) 149 | (load step-definition-file nil t)) 150 | step-definition-files))) 151 | 152 | 153 | (provide 'ecukes-setup) 154 | -------------------------------------------------------------------------------- /ecukes-run.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-run.el --- Run features, scenarios, steps etc... 2 | 3 | (eval-when-compile (defvar ecukes-include-tags) 4 | (defvar ecukes-exclude-tags)) 5 | (require 'dash) 6 | 7 | (require 'ecukes-parse) 8 | (require 'ecukes-steps) 9 | (require 'ecukes-print) 10 | (require 'ecukes-stats) 11 | (require 'ecukes-helpers) 12 | (require 'ecukes-hooks) 13 | 14 | (defun ecukes-run (feature-files) 15 | "Parse and run FEATURE-FILES if no steps are missing." 16 | (let* ((features 17 | (-map 18 | (lambda (feature-file) 19 | (ecukes-parse-feature feature-file)) feature-files)) 20 | (steps 21 | (ecukes-feature-steps features)) 22 | (steps-missing 23 | (ecukes-steps-missing-definition steps))) 24 | (if steps-missing 25 | (ecukes-print-missing-steps steps-missing) 26 | (ecukes-run-features features)))) 27 | 28 | (defun ecukes-run-features (features) 29 | "Run FEATURES." 30 | (ecukes-hooks-run-setup) 31 | (-each features 'ecukes-run-feature) 32 | (ecukes-hooks-run-teardown) 33 | (ecukes-print-stats-summary)) 34 | 35 | (defun ecukes-run-feature (feature) 36 | "Run FEATURE." 37 | (let ((intro (ecukes-feature-intro feature)) 38 | (background (ecukes-feature-background feature)) 39 | (scenarios (ecukes-feature-scenarios feature))) 40 | (if intro 41 | (ecukes-print-intro intro)) 42 | (let ((background-success t) (background-should-run (not background))) 43 | (when background 44 | (ecukes-hooks-run-before) 45 | (setq background-success (ecukes-run-background background))) 46 | (-each 47 | scenarios 48 | (lambda (scenario) 49 | (let ((tags (ecukes-scenario-tags scenario))) 50 | (when (and (or (not ecukes-include-tags) 51 | (-intersection ecukes-include-tags tags)) 52 | (not (-intersection ecukes-exclude-tags tags))) 53 | (if background-should-run 54 | (ecukes-hooks-run-before)) 55 | (when (and background background-success background-should-run) 56 | (ecukes-run-background-steps background)) 57 | (ecukes-run-scenario scenario background-success) 58 | (ecukes-hooks-run-after) 59 | (setq background-should-run t)))))))) 60 | 61 | (defun ecukes-run-background-steps (background) 62 | "Run BACKGROUND steps." 63 | (let ((steps (ecukes-background-steps background))) 64 | (-each steps (lambda (step) (ecukes-run-step step))))) 65 | 66 | (defun ecukes-run-background (background) 67 | "Run BACKGROUND." 68 | (ecukes-print-background-header) 69 | (let* ((steps (ecukes-background-steps background)) 70 | (success (ecukes-run-steps steps t))) 71 | (ecukes-print-newline) 72 | success)) 73 | 74 | (defun ecukes-run-scenario (scenario background-success) 75 | "Run SCENARIO." 76 | (ecukes-print-scenario-header scenario) 77 | (let* ((steps (ecukes-scenario-steps scenario)) 78 | (success (ecukes-run-steps steps background-success))) 79 | (if success 80 | (ecukes-stats-scenario-pass) 81 | (ecukes-stats-scenario-fail))) 82 | (ecukes-print-newline)) 83 | 84 | (defun ecukes-run-steps (steps success) 85 | "Run and print STEPS and return `t' if all was successful, `nil' otherwise." 86 | (let ((status (if success 'success 'skipped))) 87 | (-each 88 | steps 89 | (lambda (step) 90 | (if success 91 | (progn 92 | (setq success (ecukes-run-step step)) 93 | (unless success 94 | (setq status 'failure))) 95 | (setq status 'skipped)) 96 | (cond ((eq status 'success) 97 | (ecukes-stats-step-pass)) 98 | ((eq status 'failure) 99 | (ecukes-stats-step-fail)) 100 | ((eq status 'skipped) 101 | (ecukes-stats-step-skip))) 102 | (ecukes-print-step step status))) 103 | success)) 104 | 105 | (defun ecukes-run-step (step) 106 | "Run STEP. Return `t' if success and `nil' otherwise." 107 | (let ((success)) 108 | (condition-case err 109 | (progn 110 | (let* ((body (ecukes-step-body step)) 111 | (arg (ecukes-step-arg step)) 112 | (args (ecukes-steps-args step)) 113 | (args (if arg (cons arg args) args)) 114 | (step-def (ecukes-steps-find body)) 115 | (fn (ecukes-step-def-fn step-def)) 116 | (fn-args-count 117 | (length 118 | (if (byte-code-function-p fn) 119 | (aref fn 0) 120 | (if (listp fn) 121 | (cadr fn)))))) 122 | (if (and (not (symbolp fn)) (> fn-args-count (length args))) 123 | (progn 124 | (let ((wait t)) 125 | (add-to-list 'args (lambda (&rest args) (setq wait nil)) t) 126 | (apply fn args) 127 | (while wait 128 | (accept-process-output nil 0.005)))) 129 | (apply fn args))) 130 | (setq success t)) 131 | (error 132 | (setf (ecukes-step-err step) (error-message-string err))) 133 | (quit)) 134 | success)) 135 | 136 | (provide 'ecukes-run) 137 | 138 | ;;; ecukes-run.el ends here 139 | -------------------------------------------------------------------------------- /test/ecukes-parse-step-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-parse) 2 | 3 | (ert-deftest parse-step-mix-regular-py-string-table () 4 | "Should parse a mix of steps in order: regular, py-string, table." 5 | (with-parse-scenario 6 | "mix-regular-py-string-table" 7 | (lambda (scenario name step-names tags) 8 | (let ((steps (ecukes-scenario-steps scenario))) 9 | (let* ((step (nth 0 steps)) 10 | (name (ecukes-step-name step)) 11 | (head (ecukes-step-head step)) 12 | (body (ecukes-step-body step)) 13 | (type (ecukes-step-type step)) 14 | (arg (ecukes-step-arg step))) 15 | (should (equal name "Given a known state")) 16 | (should (equal head "Given")) 17 | (should (equal body "a known state")) 18 | (should (equal type 'regular)) 19 | (should (equal arg nil))) 20 | (let* ((step (nth 1 steps)) 21 | (name (ecukes-step-name step)) 22 | (head (ecukes-step-head step)) 23 | (body (ecukes-step-body step)) 24 | (type (ecukes-step-type step)) 25 | (arg (ecukes-step-arg step))) 26 | (should (equal name "Given this text:")) 27 | (should (equal head "Given")) 28 | (should (equal body "this text:")) 29 | (should (equal type 'py-string)) 30 | (should (equal arg "Lorem ipsum dolor sit amet.\nCurabitur pellentesque iaculis eros."))) 31 | (let* ((step (nth 2 steps)) 32 | (name (ecukes-step-name step)) 33 | (head (ecukes-step-head step)) 34 | (body (ecukes-step-body step)) 35 | (type (ecukes-step-type step)) 36 | (arg (ecukes-step-arg step))) 37 | (should (equal name "Given these meals:")) 38 | (should (equal head "Given")) 39 | (should (equal body "these meals:")) 40 | (should (equal type 'table)) 41 | (should (equal arg '(("meal" "price") ("Hamburger" "$4.50"))))))))) 42 | 43 | (ert-deftest parse-step-mix-py-string-table-regular () 44 | "Should parse a mix of steps in order: py-string, table, regular." 45 | (with-parse-scenario 46 | "mix-py-string-table-regular" 47 | (lambda (scenario name step-names tags) 48 | (let ((steps (ecukes-scenario-steps scenario))) 49 | (let* ((step (nth 0 steps)) 50 | (name (ecukes-step-name step)) 51 | (head (ecukes-step-head step)) 52 | (body (ecukes-step-body step)) 53 | (type (ecukes-step-type step)) 54 | (arg (ecukes-step-arg step))) 55 | (should (equal name "Given this text:")) 56 | (should (equal head "Given")) 57 | (should (equal body "this text:")) 58 | (should (equal type 'py-string)) 59 | (should (equal arg "Lorem ipsum dolor sit amet.\nCurabitur pellentesque iaculis eros."))) 60 | (let* ((step (nth 1 steps)) 61 | (name (ecukes-step-name step)) 62 | (head (ecukes-step-head step)) 63 | (body (ecukes-step-body step)) 64 | (type (ecukes-step-type step)) 65 | (arg (ecukes-step-arg step))) 66 | (should (equal name "Given these meals:")) 67 | (should (equal head "Given")) 68 | (should (equal body "these meals:")) 69 | (should (equal type 'table)) 70 | (should (equal arg '(("meal" "price") ("Hamburger" "$4.50"))))) 71 | (let* ((step (nth 2 steps)) 72 | (name (ecukes-step-name step)) 73 | (body (ecukes-step-body step)) 74 | (head (ecukes-step-head step)) 75 | (type (ecukes-step-type step)) 76 | (arg (ecukes-step-arg step))) 77 | (should (equal name "Given a known state")) 78 | (should (equal head "Given")) 79 | (should (equal body "a known state")) 80 | (should (equal type 'regular)) 81 | (should (equal arg nil))))))) 82 | 83 | (ert-deftest parse-step-mix-table-regular-py-string () 84 | "Should parse a mix of steps in order: table, regular, py-string." 85 | (with-parse-scenario 86 | "mix-table-regular-py-string" 87 | (lambda (scenario name step-names tags) 88 | (let ((steps (ecukes-scenario-steps scenario))) 89 | (let* ((step (nth 0 steps)) 90 | (name (ecukes-step-name step)) 91 | (head (ecukes-step-head step)) 92 | (body (ecukes-step-body step)) 93 | (type (ecukes-step-type step)) 94 | (arg (ecukes-step-arg step))) 95 | (should (equal name "Given these meals:")) 96 | (should (equal head "Given")) 97 | (should (equal body "these meals:")) 98 | (should (equal type 'table)) 99 | (should (equal arg '(("meal" "price") ("Hamburger" "$4.50"))))) 100 | (let* ((step (nth 1 steps)) 101 | (name (ecukes-step-name step)) 102 | (head (ecukes-step-head step)) 103 | (body (ecukes-step-body step)) 104 | (type (ecukes-step-type step)) 105 | (arg (ecukes-step-arg step))) 106 | (should (equal name "Given a known state")) 107 | (should (equal head "Given")) 108 | (should (equal body "a known state")) 109 | (should (equal type 'regular)) 110 | (should (equal arg nil))) 111 | (let* ((step (nth 2 steps)) 112 | (name (ecukes-step-name step)) 113 | (head (ecukes-step-head step)) 114 | (body (ecukes-step-body step)) 115 | (type (ecukes-step-type step)) 116 | (arg (ecukes-step-arg step))) 117 | (should (equal name "Given this text:")) 118 | (should (equal head "Given")) 119 | (should (equal body "this text:")) 120 | (should (equal type 'py-string)) 121 | (should (equal arg "Lorem ipsum dolor sit amet.\nCurabitur pellentesque iaculis eros."))))))) 122 | -------------------------------------------------------------------------------- /test/ecukes-setup-test.el: -------------------------------------------------------------------------------- 1 | (require 'ansi) 2 | (require 'ecukes-setup) 3 | 4 | (ert-deftest setup-ecukes-quit-failure () 5 | "Should quit with exit code 1 by default." 6 | (with-mock 7 | (stub getenv => nil) 8 | (mock (kill-emacs 1) :times 1) 9 | (ecukes-quit))) 10 | 11 | (ert-deftest setup-ecukes-quit-success () 12 | "Should quit with exit code 0 on success." 13 | (with-mock 14 | (stub getenv => nil) 15 | (mock (kill-emacs 0) :times 1) 16 | (ecukes-quit 0))) 17 | 18 | (ert-deftest setup-ecukes-quit-graphical () 19 | "Should write to file before quit when graphical." 20 | (with-mock 21 | (stub getenv => "/tmp/ecukes.XYZ") 22 | (mock (kill-emacs 1) :times 1) 23 | (mock (write-file "/tmp/ecukes.XYZ") :times 1) 24 | (ecukes-quit))) 25 | 26 | (ert-deftest setup () 27 | "Should validate and setup." 28 | (with-mock 29 | (mock (ecukes-setup-features-dir-exist) :times 1) 30 | (mock (ecukes-setup-load) :times 1) 31 | (mock (ecukes-setup-argv) :times 1) 32 | (ecukes-setup))) 33 | 34 | (ert-deftest setup-argv-empty () 35 | "Should leave as is when empty." 36 | (let ((argv)) 37 | (ecukes-setup-argv) 38 | (should (equal argv nil)))) 39 | 40 | (ert-deftest setup-argv-non-options () 41 | "Should leave as is when non options." 42 | (let* ((argv (list "features")) 43 | (orig argv)) 44 | (ecukes-setup-argv) 45 | (should (equal argv orig))) 46 | (let* ((argv (list "features/a.feature" "features/b.feature")) 47 | (orig argv)) 48 | (ecukes-setup-argv) 49 | (should (equal argv orig)))) 50 | 51 | (ert-deftest setup-argv-dbg () 52 | "Should enable debug (and verbose)." 53 | (let ((debug-on-entry nil) 54 | (debug-on-error nil) 55 | (argv (list "--dbg" "features"))) 56 | (ecukes-setup-argv) 57 | (should (equal argv (list "features"))) 58 | (should (equal debug-on-entry t)) 59 | (should (equal debug-on-error t)) 60 | (should (equal ecukes-verbose t)))) 61 | 62 | (ert-deftest setup-argv-verbose () 63 | "Should enable verbose." 64 | (let ((ecukes-verbose nil) 65 | (argv (list "features/a.feature" "--verbose"))) 66 | (ecukes-setup-argv) 67 | (should (equal ecukes-verbose t)) 68 | (should (equal argv (list "features/a.feature"))))) 69 | 70 | (ert-deftest setup-argv-tags-single-include () 71 | "Should set tags when single." 72 | (let ((ecukes-include-tags nil) 73 | (ecukes-exclude-tags nil) 74 | (argv (list "features/a.feature" "--tags" "@foo"))) 75 | (ecukes-setup-argv) 76 | (should (equal ecukes-include-tags (list "foo"))) 77 | (should (equal ecukes-exclude-tags nil)) 78 | (should (equal argv (list "features/a.feature"))))) 79 | 80 | (ert-deftest setup-argv-tags-single-exclude () 81 | "Should set tags when single." 82 | (let ((ecukes-include-tags nil) 83 | (ecukes-exclude-tags nil) 84 | (argv (list "features/a.feature" "--tags" "~@foo"))) 85 | (ecukes-setup-argv) 86 | (should (equal ecukes-include-tags nil)) 87 | (should (equal ecukes-exclude-tags (list "foo"))) 88 | (should (equal argv (list "features/a.feature"))))) 89 | 90 | (ert-deftest setup-argv-tags-multiple () 91 | "Should set tags when multiple." 92 | (let ((ecukes-include-tags nil) 93 | (ecukes-exclude-tags nil) 94 | (argv (list "features/a.feature" "--tags" "@foo,@bar"))) 95 | (ecukes-setup-argv) 96 | (should (equal ecukes-include-tags (list "foo" "bar"))) 97 | (should (equal ecukes-exclude-tags nil)) 98 | (should (equal argv (list "features/a.feature"))))) 99 | 100 | (ert-deftest setup-argv-tags-complex-multiple () 101 | "Should set tags when multiple." 102 | (let ((ecukes-include-tags nil) 103 | (ecukes-exclude-tags nil) 104 | (argv (list "features/a.feature" "--tags" "@foo,~@bar"))) 105 | (ecukes-setup-argv) 106 | (should (equal ecukes-include-tags (list "foo"))) 107 | (should (equal ecukes-exclude-tags (list "bar"))) 108 | (should (equal argv (list "features/a.feature"))))) 109 | 110 | (ert-deftest setup-argv-tags-double-multiple () 111 | "Should set tags when double multiple." 112 | (let ((ecukes-include-tags nil) 113 | (ecukes-exclude-tags nil) 114 | (argv (list "features/a.feature" "--tags" "@foo,@bar" "--verbose" "--tags" "@baz"))) 115 | (ecukes-setup-argv) 116 | (should (equal ecukes-include-tags (list "foo" "bar" "baz"))) 117 | (should (equal ecukes-exclude-tags nil)) 118 | (should (equal argv (list "features/a.feature"))))) 119 | 120 | (ert-deftest setup-argv-win () 121 | "Should run with win." 122 | (let ((argv (list "--win" "features/a.feature"))) 123 | (ecukes-setup-argv) 124 | (should (equal argv (list "features/a.feature"))))) 125 | 126 | (ert-deftest setup-features-directory-exist-when-no-features-dir () 127 | "Should print message when no features dir exist." 128 | (with-mock 129 | (stub file-directory-p => nil) 130 | (stub message) 131 | (mock (usage) :times 1) 132 | (mock (ansi-red "Missing `features` directory.") :times 1) 133 | (ecukes-setup-features-dir-exist))) 134 | 135 | (ert-deftest setup-load () 136 | "Should load support and step definitions" 137 | (with-mock 138 | (mock (ecukes-setup-load-support) :times 1) 139 | (mock (ecukes-setup-load-step-definitions) :times 1) 140 | (ecukes-setup-load))) 141 | 142 | (ert-deftest setup-load-support () 143 | "Should load support first and only once, then the rest." 144 | (with-mock 145 | (with-project 146 | (stub expand-file-name => "/path/to/project/features/support/env.el") 147 | (stub 148 | directory-files => 149 | '("/path/to/project/features/support/env.el" 150 | "/path/to/project/features/support/foo.el" 151 | "/path/to/project/features/support/bar.el")) 152 | (mock (load) :times 3) 153 | (ecukes-setup-load-support)))) 154 | 155 | (ert-deftest setup-load-step-definitions () 156 | "Should load all step definitions." 157 | (with-mock 158 | (stub 159 | directory-files => 160 | '("/path/to/project/features/step-definitions/project-steps.el" 161 | "/path/to/project/features/step-definitions/misc-steps.el")) 162 | (mock (load) :times 2) 163 | (ecukes-setup-load-step-definitions))) 164 | 165 | (ert-deftest setup-message-nil () 166 | "Should handle printing nil message." 167 | (let ((ecukes-internal-message-log)) 168 | (message nil) 169 | (should (equal (car (car ecukes-internal-message-log)) 'message)) 170 | (should (equal (cdr (car ecukes-internal-message-log)) "")))) 171 | -------------------------------------------------------------------------------- /test/ecukes-steps-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-steps) 2 | 3 | (ert-deftest steps-define-step () 4 | "Should define step." 5 | (with-steps 6 | (let ((load-file-name "DUMMY/FILE/NAME.el")) 7 | (Given "^a known state$" 'ignore)) 8 | (should 9 | (equal 10 | (make-ecukes-step-def :regex "^a known state$" :fn 'ignore 11 | :file "DUMMY/FILE/NAME.el") 12 | (car ecukes-steps-definitions))))) 13 | 14 | (ert-deftest steps-defined-with-doc () 15 | "Should record docstring if given." 16 | (with-steps 17 | (Given "^a known state$" 18 | "This step does what." 19 | 'ignore) 20 | (should 21 | (equal (ecukes-step-def-doc (car ecukes-steps-definitions)) 22 | "This step does what.")))) 23 | 24 | (ert-deftest steps-define-same-step-twice () 25 | "Should not define same step twice." 26 | (with-steps 27 | (Given "^a known state$" 'ignore) 28 | (Given "^a known state$" 'ignore) 29 | (should 30 | (equal (length ecukes-steps-definitions) 1)))) 31 | 32 | (ert-deftest steps-call-step-no-arguments () 33 | "Should call step with no arguments." 34 | (with-steps 35 | (Given "^a known state$" (lambda () "x")) 36 | (should (equal (Given "a known state") "x")))) 37 | 38 | (ert-deftest steps-call-step-single-argument () 39 | "Should call step with single argument." 40 | (with-steps 41 | (Given "^a \\(.+\\) state$" 'identity) 42 | (should (equal (Given "a %s state" "known") "known")))) 43 | 44 | (ert-deftest steps-call-step-multiple-arguments () 45 | "Should call step with multiple arguments." 46 | (with-steps 47 | (Given "^state \\(.+\\) and \\(.+\\)$" 48 | (lambda (state-1 state-2) 49 | (format "%s-%s" state-1 state-2))) 50 | (should (equal (Given "state %s and %s" "known" "unknown") "known-unknown")))) 51 | 52 | (ert-deftest steps-call-step-line-arguments () 53 | "Should call step with inline arguments." 54 | (with-steps 55 | (Given "^state \\(.+\\) and \\(.+\\)$" 56 | (lambda (state-1 state-2) 57 | (format "%s-%s" state-1 state-2))) 58 | (should (equal (Given "state known and unknown") "known-unknown")))) 59 | 60 | (ert-deftest steps-undefined-no-arguments () 61 | "Should error when not defined, no arguments." 62 | (with-steps 63 | (with-mock 64 | (mock (error (ansi-red "Step not defined: `a known state`")) :times 1) 65 | (Given "a known state")))) 66 | 67 | (ert-deftest steps-undefined-single-argument () 68 | "Should error when not defined, single argument." 69 | (with-steps 70 | (with-mock 71 | (mock (error (ansi-red "Step not defined: `a known state`")) :times 1) 72 | (Given "a %s state" "known")))) 73 | 74 | (ert-deftest steps-undefined-multiple-arguments () 75 | "Should error when not defined, multiple arguments." 76 | (with-steps 77 | (with-mock 78 | (mock (error (ansi-red "Step not defined: `state known and unknown`")) :times 1) 79 | (Given "state %s and %s" "known" "unknown")))) 80 | 81 | (ert-deftest steps-missing-definition-no-steps () 82 | "Should return nil when no steps." 83 | (should-not (ecukes-steps-missing-definition nil))) 84 | 85 | (ert-deftest steps-missing-definition-no-definitions () 86 | "Should return all steps when all missing." 87 | (let ((steps (list (mock-step "Given a known state")))) 88 | (should (equal (ecukes-steps-missing-definition steps) steps)))) 89 | 90 | (ert-deftest steps-missing-definition-have-definitions () 91 | "Should return nil when no steps missing." 92 | (with-steps 93 | (let ((steps (list (mock-step "Given a known state")))) 94 | (Given "^a known state$" 'ignore) 95 | (should-not (ecukes-steps-missing-definition steps))))) 96 | 97 | (ert-deftest steps-missing-definition-have-definitions-with-argument () 98 | "Should return nil when no steps missing with argument." 99 | (with-steps 100 | (let ((steps (list (mock-step "Given state \"known\"")))) 101 | (Given "^state \"known\"$" 'ignore) 102 | (should-not (ecukes-steps-missing-definition steps))))) 103 | 104 | (ert-deftest steps-missing-definition-some-missing () 105 | "Should return missing steps when some missing." 106 | (with-steps 107 | (let* ((known 108 | (mock-step "Given a known state")) 109 | (unknown 110 | (mock-step "Given an unknown state")) 111 | (steps (list known unknown))) 112 | (Given "^a known state$" 'ignore) 113 | (should (equal (list unknown) (ecukes-steps-missing-definition steps)))))) 114 | 115 | (ert-deftest steps-missing-definition-same-steps () 116 | "Should return uniq steps when same steps." 117 | (with-steps 118 | (let* ((step 119 | (mock-step "Given a known state")) 120 | (steps (list step step))) 121 | (should (equal steps (ecukes-steps-missing-definition steps)))))) 122 | 123 | (ert-deftest steps-args-no-args () 124 | "Should return empty list when no args." 125 | (with-steps 126 | (Given "^a known state$" 'ignore) 127 | (let ((step (mock-step "Given a known state"))) 128 | (should (equal (ecukes-steps-args step) nil))))) 129 | 130 | (ert-deftest steps-args-single-arg () 131 | "Should return args when single arg." 132 | (with-steps 133 | (Given "^state \"\\(.+\\)\"$" 'ignore) 134 | (let ((step (mock-step "Given state \"known\""))) 135 | (should (equal (ecukes-steps-args step) (list "known")))))) 136 | 137 | (ert-deftest steps-args-multiple-args () 138 | "Should return args when multiple args." 139 | (with-steps 140 | (Given "^state \"\\(.+\\)\" and \"\\(.+\\)\"$" 'ignore) 141 | (let ((step (mock-step "Given state \"known\" and \"unknown\""))) 142 | (should (equal (ecukes-steps-args step) (list "known" "unknown")))))) 143 | 144 | (ert-deftest steps-args-without-quotes () 145 | "Should return args when multiple (unquoted) args." 146 | (with-steps 147 | (Given "^state \\(.+\\) and \\(.+\\)$" 'ignore) 148 | (let ((step (mock-step "Given state known and unknown"))) 149 | (should (equal (ecukes-steps-args step) (list "known" "unknown")))))) 150 | 151 | (ert-deftest steps-args-not-defined-no-arg () 152 | "Should return quoted args when not defined when no args." 153 | (with-steps 154 | (let ((step (mock-step "Given state known"))) 155 | (should (equal (ecukes-steps-args step) nil))))) 156 | 157 | (ert-deftest steps-args-not-defined-args () 158 | "Should return quoted args when not defined when args." 159 | (with-steps 160 | (let ((step (mock-step "Given state \"known\" and \"unknown\""))) 161 | (should (equal (ecukes-steps-args step) (list "known" "unknown")))))) 162 | -------------------------------------------------------------------------------- /test/ecukes-stats-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-stats) 2 | 3 | (ert-deftest stats-reset () 4 | "Should reset stats." 5 | (with-stats 6 | (setq ecukes-stats-steps 1) 7 | (setq ecukes-stats-steps-passed 2) 8 | (setq ecukes-stats-steps-failed 3) 9 | (setq ecukes-stats-steps-skipped 4) 10 | (setq ecukes-stats-scenarios 5) 11 | (setq ecukes-stats-scenarios-passed 6) 12 | (setq ecukes-stats-scenarios-failed 7) 13 | 14 | (ecukes-stats-reset) 15 | 16 | (should (equal ecukes-stats-steps 0)) 17 | (should (equal ecukes-stats-steps-passed 0)) 18 | (should (equal ecukes-stats-steps-failed 0)) 19 | (should (equal ecukes-stats-steps-skipped 0)) 20 | (should (equal ecukes-stats-scenarios 0)) 21 | (should (equal ecukes-stats-scenarios-passed 0)) 22 | (should (equal ecukes-stats-scenarios-failed 0)))) 23 | 24 | (ert-deftest stats-update-num-steps () 25 | "Should update number of steps." 26 | (with-stats 27 | (ecukes-stats-step-pass) 28 | (ecukes-stats-step-fail) 29 | (ecukes-stats-step-skip) 30 | (should (equal ecukes-stats-steps 3)))) 31 | 32 | (ert-deftest stats-update-passed-steps () 33 | "Should update number of passed steps." 34 | (with-stats 35 | (ecukes-stats-step-pass) 36 | (should (equal ecukes-stats-steps-passed 1)) 37 | (should (equal ecukes-stats-steps 1)))) 38 | 39 | (ert-deftest stats-update-failed-steps () 40 | "Should update number of failed steps." 41 | (with-stats 42 | (ecukes-stats-step-fail) 43 | (should (equal ecukes-stats-steps-failed 1)) 44 | (should (equal ecukes-stats-steps 1)))) 45 | 46 | (ert-deftest stats-update-skipped-steps () 47 | "Should update number of skipped steps." 48 | (with-stats 49 | (ecukes-stats-step-skip) 50 | (should (equal ecukes-stats-steps-skipped 1)) 51 | (should (equal ecukes-stats-steps 1)))) 52 | 53 | (ert-deftest stats-update-num-scenarios () 54 | "Should update number of scenarios." 55 | (with-stats 56 | (ecukes-stats-scenario-pass) 57 | (ecukes-stats-scenario-fail) 58 | (should (equal ecukes-stats-scenarios 2)))) 59 | 60 | (ert-deftest stats-update-passed-scenarios () 61 | "Should update number of passed scenarios." 62 | (with-stats 63 | (ecukes-stats-scenario-pass) 64 | (should (equal ecukes-stats-scenarios-passed 1)) 65 | (should (equal ecukes-stats-scenarios 1)))) 66 | 67 | (ert-deftest stats-update-failed-scenarios () 68 | "Should update number of failed scenarios." 69 | (with-stats 70 | (ecukes-stats-scenario-fail) 71 | (should (equal ecukes-stats-scenarios-failed 1)) 72 | (should (equal ecukes-stats-scenarios 1)))) 73 | 74 | (ert-deftest stats-step-summary-no-steps () 75 | "Should show only total (zero) when no steps." 76 | (with-stats 77 | (should (equal (ecukes-stats-step-summary) "0 steps")))) 78 | 79 | (ert-deftest stats-step-summary-only-passed () 80 | "Should show only passed steps." 81 | (with-stats 82 | (ecukes-stats-step-pass) 83 | (ecukes-stats-step-pass) 84 | (should 85 | (equal 86 | (ecukes-stats-step-summary) 87 | (format 88 | "2 steps (%s, %s, %s)" 89 | (ansi-red "0 failed") 90 | (ansi-cyan "0 skipped") 91 | (ansi-green "2 passed")))))) 92 | 93 | (ert-deftest stats-step-summary-only-failed () 94 | "Should show only failed steps." 95 | (with-stats 96 | (ecukes-stats-step-fail) 97 | (ecukes-stats-step-fail) 98 | (should 99 | (equal 100 | (ecukes-stats-step-summary) 101 | (format 102 | "2 steps (%s, %s, %s)" 103 | (ansi-red "2 failed") 104 | (ansi-cyan "0 skipped") 105 | (ansi-green "0 passed")))))) 106 | 107 | (ert-deftest stats-step-summary-passed-and-failed () 108 | "Should show passed and failed steps." 109 | (with-stats 110 | (ecukes-stats-step-pass) 111 | (ecukes-stats-step-fail) 112 | (should 113 | (equal 114 | (ecukes-stats-step-summary) 115 | (format 116 | "2 steps (%s, %s, %s)" 117 | (ansi-red "1 failed") 118 | (ansi-cyan "0 skipped") 119 | (ansi-green "1 passed")))))) 120 | 121 | (ert-deftest stats-step-summary-passed-failed-and-skipped () 122 | "Should show only passed, faild and skipped steps." 123 | (with-stats 124 | (ecukes-stats-step-pass) 125 | (ecukes-stats-step-fail) 126 | (ecukes-stats-step-skip) 127 | (should 128 | (equal 129 | (ecukes-stats-step-summary) 130 | (format 131 | "3 steps (%s, %s, %s)" 132 | (ansi-red "1 failed") 133 | (ansi-cyan "1 skipped") 134 | (ansi-green "1 passed")))))) 135 | 136 | (ert-deftest stats-scenario-summary-no-scenarios () 137 | "Should show only total (zero) when no scenarios." 138 | (with-stats 139 | (should (equal (ecukes-stats-scenario-summary) "0 scenarios")))) 140 | 141 | (ert-deftest stats-scenario-summary-only-passed () 142 | "Should show only passed scenarios." 143 | (with-stats 144 | (ecukes-stats-scenario-pass) 145 | (ecukes-stats-scenario-pass) 146 | (should 147 | (equal 148 | (ecukes-stats-scenario-summary) 149 | (format 150 | "2 scenarios (%s, %s)" 151 | (ansi-red "0 failed") 152 | (ansi-green "2 passed")))))) 153 | 154 | (ert-deftest stats-scenario-summary-only-failed () 155 | "Should show only failed scenarios." 156 | (with-stats 157 | (ecukes-stats-scenario-fail) 158 | (ecukes-stats-scenario-fail) 159 | (should 160 | (equal 161 | (ecukes-stats-scenario-summary) 162 | (format 163 | "2 scenarios (%s, %s)" 164 | (ansi-red "2 failed") 165 | (ansi-green "0 passed")))))) 166 | 167 | (ert-deftest stats-scenario-summary-passed-and-failed () 168 | "Should show passed and failed scenarios." 169 | (with-stats 170 | (ecukes-stats-scenario-pass) 171 | (ecukes-stats-scenario-fail) 172 | (should 173 | (equal 174 | (ecukes-stats-scenario-summary) 175 | (format 176 | "2 scenarios (%s, %s)" 177 | (ansi-red "1 failed") 178 | (ansi-green "1 passed")))))) 179 | 180 | (ert-deftest stats-summary-no-scenarios-or-steps () 181 | "Should show both scenarios and steps when no scenarios or steps." 182 | (with-stats 183 | (should (equal (ecukes-stats-summary) "0 scenarios\n0 steps")))) 184 | 185 | (ert-deftest stats-summary-with-scenarios-or-steps () 186 | "Should show both scenarios and steps when both scenarios and steps." 187 | (with-stats 188 | (ecukes-stats-scenario-pass) 189 | (ecukes-stats-scenario-fail) 190 | (ecukes-stats-step-pass) 191 | (ecukes-stats-step-fail) 192 | (ecukes-stats-step-skip) 193 | (should 194 | (equal 195 | (ecukes-stats-summary) 196 | (format 197 | "%s\n%s" 198 | (format 199 | "2 scenarios (%s, %s)" 200 | (ansi-red "1 failed") 201 | (ansi-green "1 passed")) 202 | (format 203 | "3 steps (%s, %s, %s)" 204 | (ansi-red "1 failed") 205 | (ansi-cyan "1 skipped") 206 | (ansi-green "1 passed"))))))) 207 | -------------------------------------------------------------------------------- /ecukes-print.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-print.el --- Print various stuff on screen 2 | 3 | (require 'ansi) 4 | 5 | (require 'ecukes-stats) 6 | (require 'ecukes-def) 7 | (require 'ecukes-steps) 8 | (require 'ecukes-template) 9 | 10 | (defvar ecukes-print-offset 0 11 | "Current indentation offset (number of spaces).") 12 | 13 | (defun ecukes-print-missing-steps (steps) 14 | "Print missing steps" 15 | (ecukes-print-missing-steps-header) 16 | (let ((step-bodies)) 17 | (-each 18 | steps 19 | (lambda (step) 20 | (let ((step-body (ecukes-print-step-body step)) 21 | (step-string (ecukes-print-step-string step))) 22 | (unless 23 | (-any? 24 | (lambda (body) 25 | (equal step-body body)) step-bodies) 26 | (add-to-list 'step-bodies step-body) 27 | (ecukes-print-message "%s\n" step-string))))))) 28 | 29 | (defun ecukes-print-missing-steps-header () 30 | "Print missing steps header." 31 | (ecukes-print-message 32 | (ansi-yellow "Some steps does not have a matching definition. Please implement the following step definitions:\n"))) 33 | 34 | (defun ecukes-print-step-string (step) 35 | "Return missing step string." 36 | (let ((head (ecukes-step-head step)) 37 | (body (ecukes-print-step-body step)) 38 | (args (ecukes-print-step-args step))) 39 | (ansi-yellow 40 | (ecukes-template-get 41 | 'missing-step 42 | `(("head" . ,head) 43 | ("body" . ,body) 44 | ("args" . ,args)))))) 45 | 46 | (defun ecukes-print-step-args (step) 47 | "Return args from STEP." 48 | (let* ((result) 49 | (arg (ecukes-step-arg step)) 50 | (args (ecukes-steps-args step)) 51 | (type (ecukes-step-type step)) 52 | (args-count 53 | (+ 54 | (length args) 55 | (if (or 56 | (equal type 'table) 57 | (equal type 'py-string)) 1 0)))) 58 | (if (= args-count 1) 59 | "arg" 60 | (progn 61 | (-dotimes 62 | args-count 63 | (lambda (n) 64 | (add-to-list 'result (format "arg-%d" (1+ n)) t))) 65 | (s-join " " result))))) 66 | 67 | (defun ecukes-print-step-body (step) 68 | "Return body from STEP." 69 | (let* ((body (ecukes-step-body step)) 70 | (args (ecukes-steps-args step)) 71 | (result body)) 72 | (when args 73 | (-each 74 | args 75 | (lambda (arg) 76 | (setq result (s-replace (s-concat "\"" arg "\"") "\\\"\\\\([^\\\"]+\\\\)\\\"" result))))) 77 | result)) 78 | 79 | (defun ecukes-print-intro (intro) 80 | "Print INTRO." 81 | (let ((ecukes-print-offset 0) 82 | (header (ecukes-intro-header intro))) 83 | (ecukes-print-message "Feature: %s" header) 84 | (let ((ecukes-print-offset 2) 85 | (description (ecukes-intro-description intro))) 86 | (-each 87 | description 88 | (lambda (row) (ecukes-print-message row)))) 89 | (ecukes-print-newline))) 90 | 91 | (defun ecukes-print-background-header () 92 | "Print background header." 93 | (let ((ecukes-print-offset 2)) 94 | (ecukes-print-message "Background:"))) 95 | 96 | (defun ecukes-print-scenario-header (scenario) 97 | "Print SCENARIO header." 98 | (let ((name (ecukes-scenario-name scenario)) 99 | (tags (ecukes-scenario-tags scenario)) 100 | (ecukes-print-offset 2)) 101 | (when tags 102 | (ecukes-print-message 103 | (ansi-cyan 104 | (s-join " " (-map (lambda (tag) (s-concat "@" tag)) tags))))) 105 | (ecukes-print-message "Scenario: %s" name))) 106 | 107 | (defun ecukes-print-step (step status) 108 | "Print STEP in correct STATUS color." 109 | (let ((name (ecukes-step-name step)) 110 | (err (ecukes-step-err step))) 111 | (let ((ecukes-print-offset 4)) 112 | (ecukes-print-message 113 | (ecukes-print-status name status))) 114 | (if (eq (ecukes-step-type step) 'table) 115 | (ecukes-print-table step status) 116 | (if (eq (ecukes-step-type step) 'py-string) 117 | (ecukes-print-py-string step status))) 118 | (when (eq status 'failure) 119 | (let ((ecukes-print-offset 6)) 120 | (-each 121 | (s-lines (or err "Unknown error...")) 122 | (lambda (line) 123 | (ecukes-print-message 124 | (ansi-red line)))))))) 125 | 126 | (defun ecukes-print-table (step status) 127 | "Print STEP table." 128 | (let* ((table (ecukes-step-arg step)) 129 | (rows (length table)) 130 | (cols (length (car table))) 131 | (widths)) 132 | (-dotimes 133 | cols 134 | (lambda (col) 135 | (let ((width 0)) 136 | (-dotimes 137 | rows 138 | (lambda (row) 139 | (setq width (max width (length (nth col (nth row table))))))) 140 | (push width widths)))) 141 | (setq widths (reverse widths)) 142 | (-dotimes 143 | rows 144 | (lambda (row) 145 | (let ((col-strings)) 146 | (-dotimes 147 | cols 148 | (lambda (col) 149 | (let* ((orig (nth col (nth row table))) 150 | (pad (- (nth col widths) (length orig))) 151 | (col-string (s-concat (ecukes-print-status orig status) (s-repeat pad " ")))) 152 | (add-to-list 'col-strings col-string t 'eq)))) 153 | (let ((ecukes-print-offset 6)) 154 | (ecukes-print-message (s-concat "| " (s-join " | " col-strings) " |")))))))) 155 | 156 | (defun ecukes-print-py-string (step status) 157 | "Print STEP py-string." 158 | (let* ((arg (ecukes-step-arg step)) 159 | (lines (s-lines arg))) 160 | (let ((ecukes-print-offset 6)) 161 | (ecukes-print-message 162 | (ecukes-print-status "\"\"\"" status)) 163 | (-each 164 | lines 165 | (lambda (line) 166 | (ecukes-print-message 167 | (ecukes-print-status line status)))) 168 | (ecukes-print-message 169 | (ecukes-print-status "\"\"\"" status))))) 170 | 171 | (defun ecukes-print-status (string status) 172 | "Return STRING in correct color depending on STATUS." 173 | (let ((color 174 | (cond ((eq status 'success) 175 | 'ansi-green) 176 | ((eq status 'failure) 177 | 'ansi-red) 178 | ((eq status 'skipped) 179 | 'ansi-cyan)))) 180 | (funcall color string))) 181 | 182 | (defun ecukes-print-steps (&optional with-doc with-file) 183 | "Print all available steps defined for this project. 184 | Include docstring when WITH-DOC is non-nil." 185 | (-map 186 | (lambda (step-def) 187 | (let ((row)) 188 | (when with-file 189 | (let ((file (ecukes-step-file-name step-def t))) 190 | (setq row (s-concat row file ": ")))) 191 | (let ((regex (ecukes-step-def-regex step-def))) 192 | (setq row (s-concat row (ansi-green regex)))) 193 | (when with-doc 194 | (let ((doc (ecukes-step-def-doc step-def))) 195 | (when doc 196 | (setq row (s-concat row "\n" (ansi-cyan doc) "\n"))))) 197 | (ecukes-print-message row))) 198 | ecukes-steps-definitions)) 199 | 200 | (defun ecukes-print-stats-summary () 201 | "Print stats summary." 202 | (ecukes-print-message (ecukes-stats-summary))) 203 | 204 | (defun ecukes-print-newline () 205 | "Print newline." 206 | (ecukes-print-message " ")) 207 | 208 | (defun ecukes-print-message (format-string &rest args) 209 | "Print MESSAGE." 210 | (let ((ecukes-message t)) 211 | (message (apply 'ecukes-print-format (cons format-string args))))) 212 | 213 | (defun ecukes-print-format (format-string &rest args) 214 | "Return formatted message." 215 | (let ((message (apply 'format (cons format-string args))) 216 | (offset (s-repeat ecukes-print-offset " "))) 217 | (s-concat offset message))) 218 | 219 | (provide 'ecukes-print) 220 | 221 | ;;; ecukes-print.el ends here 222 | -------------------------------------------------------------------------------- /ecukes-parse.el: -------------------------------------------------------------------------------- 1 | ;;; ecukes-parse.el --- Simple line by line parser for feature files 2 | 3 | (eval-when-compile (require 'cl)) 4 | (require 'dash) 5 | (require 's) 6 | (require 'ecukes-def) 7 | 8 | (defconst ecukes-parse-intro-re 9 | "^\\s-*Feature:\\s-*\\(.+[^ ]\\)\\s-*$" 10 | "Regexp matching feature header.") 11 | 12 | (defconst ecukes-parse-background-re 13 | "^\\s-*Background:" 14 | "Regexp matching background header.") 15 | 16 | (defconst ecukes-parse-scenario-re 17 | "^[\t ]*Scenario:[\t ]*\\(.+?\\)[\t ]*$" 18 | "Regexp matching scenario header.") 19 | 20 | (defconst ecukes-parse-outline-re 21 | "^[\t ]*Scenario Outline:[\t ]*\\(.+?\\)[\t ]*$" 22 | "Regexp matching scenario outline header.") 23 | 24 | (defconst ecukes-parse-examples-re 25 | "^[\t ]*Examples:" 26 | "Regexp matching scenario outline examples header.") 27 | 28 | (defconst ecukes-parse-step-re 29 | "^\\s-*\\(Given\\|When\\|Then\\|And\\|But\\)\\s-+\\(.+[^ ]\\)\\s-*$" 30 | "Regexp matching step.") 31 | 32 | (defconst ecukes-parse-tags-re 33 | "^\\s-*@" 34 | "Regexp matching scenario tags.") 35 | 36 | (defconst ecukes-parse-py-string-re 37 | "^\\s-*\"\"\"" 38 | "Regexp matching py string.") 39 | 40 | (defconst ecukes-parse-table-re 41 | "^\\s-*|.+|" 42 | "Regexp matching table.") 43 | 44 | 45 | (defun ecukes-parse-feature (feature) 46 | "Parse FEATURE." 47 | (with-temp-buffer 48 | (insert-file-contents-literally feature) 49 | (goto-char (point-min)) 50 | (let* ((tags) 51 | (intro (ecukes-parse-intro)) 52 | (background (ecukes-parse-background)) 53 | (outlines (ecukes-parse-outlines)) 54 | (scenarios (append (ecukes-parse-scenarios) (-mapcat 'ecukes-generate-outlined-scenarios outlines)))) 55 | (goto-char (point-min)) 56 | (when (re-search-forward ecukes-parse-intro-re nil t) 57 | (setq tags (ecukes-parse-tags)) 58 | (-map 59 | (lambda (scenario) 60 | (setf 61 | (ecukes-scenario-tags scenario) 62 | (-concat tags (ecukes-scenario-tags scenario)))) 63 | scenarios)) 64 | (make-ecukes-feature :intro intro :background background :outlines outlines :scenarios scenarios)))) 65 | 66 | (defun ecukes-parse-intro () 67 | "Parse intro." 68 | (when (re-search-forward ecukes-parse-intro-re nil t) 69 | (let ((header (match-string 1)) (description)) 70 | (while (not (progn (forward-line 1) (ecukes-parse-new-section-p))) 71 | (let ((line (ecukes-parse-line t))) 72 | (if line (add-to-list 'description line t)))) 73 | (make-ecukes-intro :header header :description description)))) 74 | 75 | (defun ecukes-parse-background () 76 | "Parse background." 77 | (when (re-search-forward ecukes-parse-background-re nil t) 78 | (let ((steps (ecukes-parse-block-steps))) 79 | (make-ecukes-background :steps steps)))) 80 | 81 | (defun ecukes-parse-outlines () 82 | "Parse all scenario outlines." 83 | (goto-char (point-min)) 84 | (let ((outlines)) 85 | (while (re-search-forward ecukes-parse-outline-re nil t) 86 | (add-to-list 'outlines (ecukes-parse-outline) t)) 87 | outlines)) 88 | 89 | (defun ecukes-parse-outline () 90 | "Parse a single scenario outline." 91 | (let ((name (ecukes-parse-outline-name)) 92 | (tags (ecukes-parse-tags)) 93 | (steps (ecukes-parse-block-steps)) 94 | (table (ecukes-parse-outline-table))) 95 | (make-ecukes-outline :name name :tags tags :steps steps :table table))) 96 | 97 | (defun ecukes-parse-outline-name () 98 | "Parse scenario outline name." 99 | (save-excursion 100 | (let ((line (ecukes-parse-line))) 101 | (nth 1 (s-match ecukes-parse-outline-re line))))) 102 | 103 | (defun ecukes-parse-outline-table () 104 | "Parse examples table for a scenario outline." 105 | (save-excursion 106 | (catch 'table 107 | (let ((line (ecukes-parse-line))) 108 | (while (and (not (s-matches? ecukes-parse-examples-re (or line ""))) 109 | (not (ecukes-parse-new-section-p))) 110 | (forward-line 1) 111 | (setq line (ecukes-parse-line))) 112 | (when (s-matches? ecukes-parse-examples-re (or line "")) 113 | (throw 'table (ecukes-parse-table-step))))))) 114 | 115 | (defun ecukes-substitute-in-steps (steps subs) 116 | (-map (lambda (step) 117 | (let ((gen (copy-ecukes-step step)) 118 | (type (ecukes-step-type step))) 119 | (setf (ecukes-step-name gen) (ecukes-substitute-in-string (ecukes-step-name gen) subs) 120 | (ecukes-step-body gen) (ecukes-substitute-in-string (ecukes-step-body gen) subs)) 121 | (cond 122 | ((eq type 'py-string) 123 | (setf (ecukes-step-arg gen) (ecukes-substitute-in-string (ecukes-step-arg gen) subs))) 124 | ((eq type 'table) 125 | (setf (ecukes-step-arg gen) (ecukes-substitute-in-table (ecukes-step-arg gen) subs)))) 126 | gen)) 127 | steps)) 128 | 129 | (defun ecukes-substitute-in-string (string subs) 130 | (let ((new-s (copy-sequence string)) 131 | (reps (copy-sequence subs))) 132 | (while (not (zerop (length reps))) 133 | (setq new-s (s-replace (format "<%s>" (car reps)) (cadr reps) new-s)) 134 | (setq reps (cddr reps))) 135 | new-s)) 136 | 137 | (defun ecukes-substitute-in-table (table subs) 138 | (-map (lambda (row) 139 | (-map (lambda (cell) (ecukes-substitute-in-string cell subs)) row)) 140 | table)) 141 | 142 | (defun ecukes-generate-outlined-scenarios (outline) 143 | "Generate scenarios from an outline." 144 | (let* ((name (ecukes-outline-name outline)) 145 | (steps (ecukes-outline-steps outline)) 146 | (tags (ecukes-outline-tags outline)) 147 | (table (ecukes-outline-table outline)) 148 | (header (car table))) 149 | (-map (lambda (row) 150 | (make-ecukes-scenario :name name :tags tags :steps (ecukes-substitute-in-steps steps (-interleave header row)))) 151 | (cdr table)))) 152 | 153 | (defun ecukes-parse-scenarios () 154 | "Parse scenarios." 155 | (goto-char (point-min)) 156 | (let ((scenarios)) 157 | (while (re-search-forward ecukes-parse-scenario-re nil t) 158 | (add-to-list 'scenarios (ecukes-parse-scenario) t)) 159 | scenarios)) 160 | 161 | (defun ecukes-parse-scenario () 162 | "Parse scenario." 163 | (let ((name (ecukes-parse-scenario-name)) 164 | (tags (ecukes-parse-tags)) 165 | (steps (ecukes-parse-block-steps))) 166 | (make-ecukes-scenario :name name :steps steps :tags tags))) 167 | 168 | (defun ecukes-parse-scenario-name () 169 | "Parse scenario name." 170 | (save-excursion 171 | (let ((line (ecukes-parse-line))) 172 | (nth 1 (s-match ecukes-parse-scenario-re line))))) 173 | 174 | (defun ecukes-parse-tags () 175 | "Parse tags." 176 | (save-excursion 177 | (forward-line -1) 178 | (let ((line (ecukes-parse-line t))) 179 | (when (and line (s-matches? ecukes-parse-tags-re line)) 180 | (-distinct 181 | (-map 182 | (lambda (tag) 183 | (substring tag 1)) 184 | (split-string line "\\s-+"))))))) 185 | 186 | (defun ecukes-parse-block-steps () 187 | "Parse steps in block." 188 | (let ((steps)) 189 | (while (ecukes-forward-step) 190 | (let ((step (ecukes-parse-step))) 191 | (add-to-list 'steps step t 'eq))) 192 | steps)) 193 | 194 | (defun ecukes-parse-step () 195 | "Parse step." 196 | (let* ((name (ecukes-parse-line t)) 197 | (matches (s-match ecukes-parse-step-re name)) 198 | (head (nth 1 matches)) 199 | (body (nth 2 matches)) 200 | (arg) 201 | (type)) 202 | (cond 203 | ((ecukes-parse-py-string-step-p) 204 | (setq arg (ecukes-parse-py-string-step)) 205 | (setq type 'py-string)) 206 | ((ecukes-parse-table-step-p) 207 | (setq arg (ecukes-parse-table-step)) 208 | (setq type 'table)) 209 | (t (setq type 'regular))) 210 | (make-ecukes-step :name name :head head :body body :type type :arg arg))) 211 | 212 | (defun ecukes-parse-table-step-p () 213 | "Check if step is a table step or not." 214 | (save-excursion 215 | (forward-line 1) 216 | (let ((line (ecukes-parse-line))) 217 | (s-matches? ecukes-parse-table-re line)))) 218 | 219 | (defun ecukes-parse-table-step () 220 | "Parse table step." 221 | (save-excursion 222 | (forward-line 1) 223 | (let ((rows)) 224 | (while (s-matches? ecukes-parse-table-re (ecukes-parse-line)) 225 | (add-to-list 'rows (ecukes-parse-table-step-row) t 'eq) 226 | (forward-line 1)) 227 | rows))) 228 | 229 | (defun ecukes-parse-table-step-row () 230 | "Parse row in table." 231 | (let ((row (ecukes-parse-line))) 232 | (butlast (cdr (split-string row "\\s-*|\\s-*"))))) 233 | 234 | (defun ecukes-parse-py-string-step-p () 235 | "Check if step is a py string step or not." 236 | (save-excursion 237 | (forward-line 1) 238 | (let ((line (ecukes-parse-line))) 239 | (s-matches? ecukes-parse-py-string-re line)))) 240 | 241 | (defun ecukes-parse-py-string-step () 242 | "Parse py string step." 243 | (save-excursion 244 | (forward-line 1) 245 | (let ((whites 246 | (save-excursion 247 | (back-to-indentation) 248 | (current-column))) 249 | (lines)) 250 | (forward-line 1) 251 | (while (not (s-matches? ecukes-parse-py-string-re (ecukes-parse-line))) 252 | (let ((line (ecukes-parse-line))) 253 | (push (if (<= whites (length line)) (substring line whites) nil) lines)) 254 | (forward-line 1)) 255 | (s-join "\n" (nreverse lines))))) 256 | 257 | (defun ecukes-parse-line (&optional strip-whitespace) 258 | "Parse current line." 259 | (let* ((raw (buffer-substring (line-beginning-position) (line-end-position))) 260 | (line (if strip-whitespace (s-trim raw) raw))) 261 | (if (and strip-whitespace (equal line "")) nil line))) 262 | 263 | (defun ecukes-forward-step () 264 | "Go one step forward within current section." 265 | (forward-line 1) 266 | (let ((line (ecukes-parse-line t))) 267 | (unless (ecukes-parse-new-section-p) 268 | (if (s-matches? ecukes-parse-step-re (or line "")) 269 | (not (not line)) 270 | (ecukes-forward-step))))) 271 | 272 | (defun ecukes-parse-new-section-p () 273 | "Check if current line is the start of a new section." 274 | (let ((line (or (ecukes-parse-line t) ""))) 275 | (or 276 | (eobp) 277 | (s-matches? ecukes-parse-background-re line) 278 | (s-matches? ecukes-parse-outline-re line) 279 | (s-matches? ecukes-parse-examples-re line) 280 | (s-matches? ecukes-parse-scenario-re line) 281 | (s-matches? ecukes-parse-tags-re line)))) 282 | 283 | 284 | (provide 'ecukes-parse) 285 | 286 | ;;; ecukes-parse.el ends here 287 | -------------------------------------------------------------------------------- /test/ecukes-run-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-run) 2 | (require 'ecukes-hooks) 3 | (require 'ecukes-stats) 4 | 5 | (ert-deftest run-features-should-run-setup-hooks () 6 | "Should run setup hooks." 7 | (with-mock 8 | (stub ecukes-run-feature) 9 | (stub ecukes-print-stats-summary) 10 | (mock (setup-mock) :times 1) 11 | (with-hooks 12 | (Setup (setup-mock)) 13 | (with-parse-feature 14 | "simple" 15 | (lambda (feature intro scenarios background steps) 16 | (ecukes-run-features (list feature))))))) 17 | 18 | (ert-deftest run-features-should-run-teardown-hooks () 19 | "Should run teardown hooks." 20 | (with-mock 21 | (stub ecukes-run-feature) 22 | (stub ecukes-print-stats-summary) 23 | (mock (teardown-mock) :times 1) 24 | (with-hooks 25 | (Teardown (teardown-mock)) 26 | (with-parse-feature 27 | "simple" 28 | (lambda (feature intro scenarios background steps) 29 | (ecukes-run-features (list feature))))))) 30 | 31 | (ert-deftest run-features-print-summary-once () 32 | "Should print summary once." 33 | (with-mock 34 | (stub ecukes-run-feature) 35 | (mock (ecukes-print-stats-summary) :times 1) 36 | (let ((features 37 | (list 38 | (make-ecukes-feature) 39 | (make-ecukes-feature)))) 40 | (ecukes-run-features features)))) 41 | 42 | (ert-deftest run-feature-no-background () 43 | "Should run feature when no background." 44 | (with-messages 45 | (lambda (messages) 46 | (with-parse-feature 47 | "simple" 48 | (lambda (feature intro scenarios background steps) 49 | (with-mock 50 | (stub ecukes-feature-background => nil) 51 | (stub ecukes-feature-scenarios => nil) 52 | (mock (ecukes-print-intro intro) :times 1) 53 | (not-called ecukes-run-background) 54 | (ecukes-run-feature feature))))))) 55 | 56 | (ert-deftest run-feature () 57 | "Should run feature." 58 | (with-messages 59 | (lambda (messages) 60 | (with-parse-feature 61 | "simple" 62 | (lambda (feature intro scenarios background steps) 63 | (with-mock 64 | (mock (ecukes-print-intro intro) :times 1) 65 | (mock (ecukes-run-background) :times 1) 66 | (mock (ecukes-run-scenario) :times 1) 67 | (ecukes-run-feature feature))))))) 68 | 69 | (ert-deftest run-feature-hooks-with-background () 70 | "Should run feature hooks with background." 71 | (with-messages 72 | (lambda (messages) 73 | (with-mock 74 | (stub ecukes-print-intro) 75 | (mock (before-mock) :times 2) 76 | (mock (after-mock) :times 2) 77 | (with-hooks 78 | (Before (before-mock)) 79 | (After (after-mock)) 80 | (let ((feature 81 | (make-ecukes-feature 82 | :background (make-ecukes-background) 83 | :scenarios 84 | (list 85 | (make-ecukes-scenario) 86 | (make-ecukes-scenario))))) 87 | (ecukes-run-feature feature))))))) 88 | 89 | (ert-deftest run-feature-hooks-without-background () 90 | "Should run feature hooks without background." 91 | (with-messages 92 | (lambda (messages) 93 | (with-mock 94 | (stub ecukes-print-intro) 95 | (mock (before-mock) :times 2) 96 | (mock (after-mock) :times 2) 97 | (with-hooks 98 | (Before (before-mock)) 99 | (After (after-mock)) 100 | (let ((feature 101 | (make-ecukes-feature 102 | :scenarios 103 | (list 104 | (make-ecukes-scenario) 105 | (make-ecukes-scenario))))) 106 | (ecukes-run-feature feature))))))) 107 | 108 | (ert-deftest run-feature-no-intro () 109 | "Should run feature when no intro." 110 | (with-mock 111 | (not-called ecukes-print-intro) 112 | (let ((feature 113 | (make-ecukes-feature))) 114 | (ecukes-run-feature feature)))) 115 | 116 | (ert-deftest run-scenarios-with-include-tags () 117 | "Should run scenarios matching tags." 118 | (with-messages 119 | (lambda (messages) 120 | (with-mock 121 | (stub ecukes-print-intro) 122 | (mock (ecukes-run-scenario) :times 1) 123 | (let ((ecukes-include-tags (list "foo")) 124 | (feature 125 | (make-ecukes-feature 126 | :background (make-ecukes-background) 127 | :scenarios 128 | (list 129 | (make-ecukes-scenario :tags (list "foo")) 130 | (make-ecukes-scenario :tags (list "bar")) 131 | (make-ecukes-scenario :tags (list "baz")))))) 132 | (ecukes-run-feature feature)))))) 133 | 134 | (ert-deftest run-scenarios-with-exclude-tags () 135 | "Should run scenarios non-matching tags." 136 | (with-messages 137 | (lambda (messages) 138 | (with-mock 139 | (stub ecukes-print-intro) 140 | (mock (ecukes-run-scenario) :times 2) 141 | (let ((ecukes-exclude-tags (list "foo")) 142 | (feature 143 | (make-ecukes-feature 144 | :background (make-ecukes-background) 145 | :scenarios 146 | (list 147 | (make-ecukes-scenario :tags (list "foo")) 148 | (make-ecukes-scenario :tags (list "bar")) 149 | (make-ecukes-scenario :tags (list "baz")))))) 150 | (ecukes-run-feature feature)))))) 151 | 152 | (ert-deftest run-scenarios-with-complex-tags () 153 | "Should run scenarios matching tags." 154 | (with-messages 155 | (lambda (messages) 156 | (with-mock 157 | (stub ecukes-print-intro) 158 | (mock (ecukes-run-scenario) :times 1) 159 | (let ((ecukes-include-tags (list "foo")) 160 | (ecukes-exclude-tags (list "baz")) 161 | (feature 162 | (make-ecukes-feature 163 | :background (make-ecukes-background) 164 | :scenarios 165 | (list 166 | (make-ecukes-scenario :tags (list "foo")) 167 | (make-ecukes-scenario :tags (list "foo" "baz")) 168 | (make-ecukes-scenario :tags (list "bar" "baz")))))) 169 | (ecukes-run-feature feature)))))) 170 | 171 | (ert-deftest run-background () 172 | "Should run background." 173 | (with-mock 174 | (mock (ecukes-run-step) => t :times 2) 175 | (with-messages 176 | (lambda (messages) 177 | (let ((success 178 | (ecukes-run-background 179 | (make-ecukes-background 180 | :steps 181 | (list 182 | (mock-step "Given a known state") 183 | (mock-step "Given an unknown state"))))) 184 | (expected 185 | (list 186 | " Background:" 187 | (s-concat " " (ansi-green "Given a known state")) 188 | (s-concat " " (ansi-green "Given an unknown state")) 189 | " "))) 190 | (should (equal success t)) 191 | (should (equal expected messages))))))) 192 | 193 | (ert-deftest run-scenario () 194 | "Should run scenario." 195 | (with-mock 196 | (mock (ecukes-run-step) => t :times 2) 197 | (with-messages 198 | (lambda (messages) 199 | (ecukes-run-scenario 200 | (make-ecukes-scenario 201 | :name "Simple" 202 | :steps 203 | (list 204 | (mock-step "Given a known state") 205 | (mock-step "Given an unknown state"))) 206 | t) 207 | (let ((expected 208 | (list 209 | " Scenario: Simple" 210 | (s-concat " " (ansi-green "Given a known state")) 211 | (s-concat " " (ansi-green "Given an unknown state")) 212 | " "))) 213 | (should (equal expected messages))))))) 214 | 215 | (ert-deftest run-background-before-scenarios () 216 | "Should run background before each scenario." 217 | (with-mock 218 | (stub ecukes-print-intro) 219 | (stub ecukes-run-background => t) 220 | (mock (ecukes-run-scenario) :times 2) 221 | (mock (ecukes-run-background-steps) :times 1) 222 | (with-messages 223 | (lambda (messages) 224 | (with-steps 225 | (with-stats 226 | (let ((feature 227 | (make-ecukes-feature 228 | :background (make-ecukes-background) 229 | :scenarios 230 | (list 231 | (make-ecukes-scenario) 232 | (make-ecukes-scenario))))) 233 | (ecukes-run-feature feature)))))))) 234 | 235 | (ert-deftest run-background-steps () 236 | "Should run background steps." 237 | (with-mock 238 | (mock (ecukes-run-step) :times 2) 239 | (let ((background 240 | (make-ecukes-background 241 | :steps 242 | (list 243 | (make-ecukes-step) 244 | (make-ecukes-step))))) 245 | (ecukes-run-background-steps background)))) 246 | 247 | (ert-deftest run-background-with-successful-steps-stats () 248 | "Should update step stats count when successful steps." 249 | (with-messages 250 | (lambda (messages) 251 | (with-steps 252 | (with-stats 253 | (Given "a known state" 'ignore) 254 | (Given "an unknown state" 'ignore) 255 | (ecukes-run-background 256 | (make-ecukes-background 257 | :steps 258 | (list 259 | (mock-step "Given a known state") 260 | (mock-step "Given an unknown state")))) 261 | (should (equal ecukes-stats-steps 2)) 262 | (should (equal ecukes-stats-steps-passed 2)) 263 | (should (equal ecukes-stats-steps-failed 0)) 264 | (should (equal ecukes-stats-steps-skipped 0))))))) 265 | 266 | (ert-deftest run-background-with-failing-step-stats () 267 | "Should update step stats count when failing steps." 268 | (with-messages 269 | (lambda (messages) 270 | (with-steps 271 | (with-stats 272 | (Given "a known state" (lambda () (error "ERROR"))) 273 | (Given "an unknown state" 'ignore) 274 | (ecukes-run-background 275 | (make-ecukes-background 276 | :steps 277 | (list 278 | (mock-step "Given a known state") 279 | (mock-step "Given an unknown state")))) 280 | (should (equal ecukes-stats-steps 2)) 281 | (should (equal ecukes-stats-steps-passed 0)) 282 | (should (equal ecukes-stats-steps-failed 1)) 283 | (should (equal ecukes-stats-steps-skipped 1))))))) 284 | 285 | (ert-deftest run-scenario-stats () 286 | "Should update scenario stats count." 287 | (with-messages 288 | (lambda (messages) 289 | (with-stats 290 | (ecukes-run-scenario (make-ecukes-scenario) t) 291 | (should (equal ecukes-stats-scenarios 1)) 292 | (should (equal ecukes-stats-scenarios-passed 1)) 293 | (should (equal ecukes-stats-scenarios-failed 0)))))) 294 | 295 | (ert-deftest run-scenario-with-successful-steps-stats () 296 | "Should update scenario and step stats count when successful steps." 297 | (with-messages 298 | (lambda (messages) 299 | (with-steps 300 | (with-stats 301 | (Given "a known state" 'ignore) 302 | (Given "an unknown state" 'ignore) 303 | (ecukes-run-scenario 304 | (make-ecukes-scenario 305 | :steps 306 | (list 307 | (mock-step "Given a known state") 308 | (mock-step "Given an unknown state"))) 309 | t) 310 | (should (equal ecukes-stats-scenarios 1)) 311 | (should (equal ecukes-stats-scenarios-passed 1)) 312 | (should (equal ecukes-stats-scenarios-failed 0)) 313 | (should (equal ecukes-stats-steps 2)) 314 | (should (equal ecukes-stats-steps-passed 2)) 315 | (should (equal ecukes-stats-steps-failed 0)) 316 | (should (equal ecukes-stats-steps-skipped 0))))))) 317 | 318 | (ert-deftest run-scenario-with-failing-step-stats () 319 | "Should update scenario and step stats count when failing steps." 320 | (with-messages 321 | (lambda (messages) 322 | (with-steps 323 | (with-stats 324 | (Given "a known state" (lambda () (error "ERROR"))) 325 | (Given "an unknown state" 'ignore) 326 | (ecukes-run-scenario 327 | (make-ecukes-scenario 328 | :steps 329 | (list 330 | (mock-step "Given a known state") 331 | (mock-step "Given an unknown state"))) 332 | t) 333 | (should (equal ecukes-stats-scenarios 1)) 334 | (should (equal ecukes-stats-scenarios-passed 0)) 335 | (should (equal ecukes-stats-scenarios-failed 1)) 336 | (should (equal ecukes-stats-steps 2)) 337 | (should (equal ecukes-stats-steps-passed 0)) 338 | (should (equal ecukes-stats-steps-failed 1)) 339 | (should (equal ecukes-stats-steps-skipped 1))))))) 340 | 341 | (ert-deftest run-feature-successful-steps-stats () 342 | "Should update stats count when running feature all successful." 343 | (with-mock 344 | (stub ecukes-print-intro) 345 | (with-messages 346 | (lambda (messages) 347 | (with-steps 348 | (with-stats 349 | (Given "a known state" 'ignore) 350 | (Given "an unknown state" 'ignore) 351 | (let* ((background 352 | (make-ecukes-background 353 | :steps 354 | (list 355 | (mock-step "Given a known state")))) 356 | (scenarios 357 | (list 358 | (make-ecukes-scenario 359 | :steps 360 | (list 361 | (mock-step "Given an unknown state"))))) 362 | (feature 363 | (make-ecukes-feature 364 | :background background 365 | :scenarios scenarios))) 366 | (ecukes-run-feature feature)) 367 | (should (equal ecukes-stats-scenarios 1)) 368 | (should (equal ecukes-stats-scenarios-passed 1)) 369 | (should (equal ecukes-stats-scenarios-failed 0)) 370 | (should (equal ecukes-stats-steps 2)) 371 | (should (equal ecukes-stats-steps-passed 2)) 372 | (should (equal ecukes-stats-steps-failed 0)) 373 | (should (equal ecukes-stats-steps-skipped 0)))))))) 374 | 375 | (ert-deftest run-feature-with-failing-background-step-stats () 376 | "Should update stats count when running feature failing in background." 377 | (with-mock 378 | (stub ecukes-print-intro) 379 | (with-messages 380 | (lambda (messages) 381 | (with-steps 382 | (with-stats 383 | (Given "a known state" (lambda () (error "ERROR"))) 384 | (Given "an unknown state" 'ignore) 385 | (let* ((background 386 | (make-ecukes-background 387 | :steps 388 | (list 389 | (mock-step "Given a known state")))) 390 | (scenarios 391 | (list 392 | (make-ecukes-scenario 393 | :steps 394 | (list 395 | (mock-step "Given an unknown state"))))) 396 | (feature 397 | (make-ecukes-feature 398 | :background background 399 | :scenarios scenarios))) 400 | (ecukes-run-feature feature)) 401 | (should (equal ecukes-stats-scenarios 1)) 402 | (should (equal ecukes-stats-scenarios-passed 0)) 403 | (should (equal ecukes-stats-scenarios-failed 1)) 404 | (should (equal ecukes-stats-steps 2)) 405 | (should (equal ecukes-stats-steps-passed 0)) 406 | (should (equal ecukes-stats-steps-failed 1)) 407 | (should (equal ecukes-stats-steps-skipped 1)))))))) 408 | 409 | (ert-deftest run-steps-success () 410 | "Should run steps and return t when all successful." 411 | (with-mock 412 | (mock (ecukes-print-step) :times 2) 413 | (with-stats 414 | (with-steps 415 | (Given "a known state" 'ignore) 416 | (Given "an unknown state" 'ignore) 417 | (let ((steps 418 | (list 419 | (mock-step "Given a known state") 420 | (mock-step "Given an unknown state")))) 421 | (should (equal (ecukes-run-steps steps t) t))))))) 422 | 423 | (ert-deftest run-steps-failure () 424 | "Should run steps and return nil when failure." 425 | (with-mock 426 | (mock (ecukes-print-step) :times 2) 427 | (with-stats 428 | (with-steps 429 | (Given "a known state" 'ignore) 430 | (Given "an unknown state" (lambda () (error "ERROR"))) 431 | (let ((steps 432 | (list 433 | (mock-step "Given a known state") 434 | (mock-step "Given an unknown state")))) 435 | (should (equal (ecukes-run-steps steps t) nil))))))) 436 | 437 | (ert-deftest run-step-no-args () 438 | "Should run step when no args." 439 | (with-steps 440 | (with-mock 441 | (mock (run-mock) :times 1) 442 | (Given "a known state" 'run-mock) 443 | (should 444 | (ecukes-run-step 445 | (mock-step "Given a known state")))))) 446 | 447 | (ert-deftest run-step-when-args () 448 | "Should run step when args." 449 | (with-steps 450 | (with-mock 451 | (mock (run-mock "known" "unknown") :times 1) 452 | (Given "state \"\\(.+\\)\" and \"\\(.+\\)\"" 'run-mock) 453 | (should 454 | (ecukes-run-step 455 | (mock-step "Given state \"known\" and \"unknown\"")))))) 456 | 457 | (ert-deftest run-step-error () 458 | "Should run failing step and set error." 459 | (with-steps 460 | (Given "a known state" (lambda () (error "ERROR"))) 461 | (let ((step (mock-step "Given a known state"))) 462 | (should-not 463 | (ecukes-run-step step)) 464 | (should 465 | (equal (ecukes-step-err step) "ERROR"))))) 466 | 467 | (ert-deftest run-step-when-arg () 468 | "Should run step when arg." 469 | (with-steps 470 | (with-mock 471 | (mock (run-mock "py-string") :times 1) 472 | (Given "this:" 'run-mock) 473 | (should 474 | (ecukes-run-step 475 | (mock-step "Given this:" :type 'table :arg "py-string")))))) 476 | 477 | (ert-deftest run-step-with-callback () 478 | "Should run step with callback." 479 | (with-steps 480 | (Given "^command \\(.+\\)$" 481 | (lambda (command callback) 482 | (funcall callback))) 483 | (should 484 | (ecukes-run-step 485 | (mock-step "Given command azmzz"))))) 486 | -------------------------------------------------------------------------------- /test/ecukes-print-test.el: -------------------------------------------------------------------------------- 1 | (require 'ecukes-print) 2 | 3 | (ert-deftest print-missing-steps () 4 | "Should print header and steps." 5 | (with-mock 6 | (with-steps 7 | (Given "^state known$" 'ignore) 8 | (Given "^state unknown$" 'ignore) 9 | (let ((known (mock-step "Given state known")) 10 | (unknown (mock-step "And state unknown"))) 11 | (mock (ecukes-print-message) :times 2) 12 | (mock (ecukes-print-missing-steps-header) :times 1) 13 | (ecukes-print-missing-steps (list known unknown)))))) 14 | 15 | (ert-deftest print-missing-steps-same-step () 16 | "Should print header and same steps only once." 17 | (with-mock 18 | (with-steps 19 | (Given "^state known$" 'ignore) 20 | (Given "^state unknown$" 'ignore) 21 | (let ((known (mock-step "Given state known")) 22 | (unknown (mock-step "And state known"))) 23 | (mock (ecukes-print-message) :times 1) 24 | (mock (ecukes-print-missing-steps-header) :times 1) 25 | (ecukes-print-missing-steps (list known unknown)))))) 26 | 27 | (ert-deftest print-missing-steps-header () 28 | "Should print header." 29 | (with-mock 30 | (mock 31 | (ecukes-print-message 32 | (ansi-yellow 33 | "Some steps does not have a matching definition. Please implement the following step definitions:\n")) 34 | :times 1) 35 | (ecukes-print-missing-steps-header))) 36 | 37 | (ert-deftest print-step-string-no-args () 38 | "Should print step when no args." 39 | (with-steps 40 | (Given "^a state$" 'ignore) 41 | (let ((step (mock-step "Given a state")) 42 | (expected 43 | (ansi-yellow "(Given \"^a state$\"\n (lambda ()\n\n ))"))) 44 | (should (equal (ecukes-print-step-string step) expected))))) 45 | 46 | (ert-deftest print-step-string-single-arg () 47 | "Should print step when single arg." 48 | (with-steps 49 | (Given "^state \"\\(known\\)\"$" 'ignore) 50 | (let ((step (mock-step "Given state \"known\"")) 51 | (expected 52 | (ansi-yellow "(Given \"^state \\\"\\\\([^\\\"]+\\\\)\\\"$\"\n (lambda (arg)\n\n ))"))) 53 | (should (equal (ecukes-print-step-string step) expected))))) 54 | 55 | (ert-deftest print-step-string-multiple-args () 56 | "Should print step when multiple args." 57 | (with-steps 58 | (Given "^state \"\\(known\\)\" and \"\\(unknown\\)\"$" 'ignore) 59 | (let ((step (mock-step "Given state \"known\" and \"unknown\"")) 60 | (expected 61 | (ansi-yellow "(Given \"^state \\\"\\\\([^\\\"]+\\\\)\\\" and \\\"\\\\([^\\\"]+\\\\)\\\"$\"\n (lambda (arg-1 arg-2)\n\n ))"))) 62 | (should (equal (ecukes-print-step-string step) expected))))) 63 | 64 | (ert-deftest print-step-args-no-args () 65 | "Should be empty when no step args." 66 | (with-steps 67 | (Given "^a state$" 'ignore) 68 | (let ((step (mock-step "Given a state"))) 69 | (should (equal (ecukes-print-step-args step) ""))))) 70 | 71 | (ert-deftest print-step-args-single-arg () 72 | "Should return correct step args when single arg." 73 | (with-steps 74 | (Given "^state \"\\(known\\)\"$" 'ignore) 75 | (let ((step (mock-step "Given state \"known\""))) 76 | (should (equal (ecukes-print-step-args step) "arg"))))) 77 | 78 | (ert-deftest print-step-args-multiple-args () 79 | "Should return correct step args when multiple args." 80 | (with-steps 81 | (Given "^state \"\\(known\\)\" and \"\\(unknown\\)\"$" 'ignore) 82 | (let ((step (mock-step "Given state \"known\" and \"unknown\""))) 83 | (should (equal (ecukes-print-step-args step) "arg-1 arg-2"))))) 84 | 85 | (ert-deftest print-step-args-with-args-and-table () 86 | "Should return correct step args when args and table." 87 | (with-steps 88 | (Given "^state \"\\(known\\)\"$" 'ignore) 89 | (let ((step (mock-step "Given state \"known\"" :type 'table))) 90 | (should (equal (ecukes-print-step-args step) "arg-1 arg-2"))))) 91 | 92 | (ert-deftest print-step-args-py-string () 93 | "Should return correct step args when args and py-string." 94 | (with-steps 95 | (Given "^a known state$" 'ignore) 96 | (let ((step (mock-step "Given a known state" :type 'py-string))) 97 | (should (equal (ecukes-print-step-args step) "arg"))))) 98 | 99 | (ert-deftest print-step-args-table () 100 | "Should return correct step args when args and table." 101 | (with-steps 102 | (Given "^a known state$" 'ignore) 103 | (let ((step (mock-step "Given a known state" :type 'table))) 104 | (should (equal (ecukes-print-step-args step) "arg"))))) 105 | 106 | (ert-deftest print-step-args-with-args-and-py-string () 107 | "Should return correct step args when args and py-string." 108 | (with-steps 109 | (Given "^state \"\\(known\\)\"$" 'ignore) 110 | (let ((step (mock-step "Given state \"known\"" :type 'py-string))) 111 | (should (equal (ecukes-print-step-args step) "arg-1 arg-2"))))) 112 | 113 | (ert-deftest print-step-body-no-args () 114 | "Should be empty when no step args." 115 | (with-steps 116 | (Given "^a state$" 'ignore) 117 | (let ((step (mock-step "Given a state"))) 118 | (should (equal (ecukes-print-step-body step) "a state"))))) 119 | 120 | (ert-deftest print-step-body-single-arg () 121 | "Should return correct step body when single arg." 122 | (with-steps 123 | (Given "^state \"\\(known\\)\"$" 'ignore) 124 | (let ((step (mock-step "Given state \"known\"")) 125 | (expected "state \\\"\\\\([^\\\"]+\\\\)\\\"")) 126 | (should (equal (ecukes-print-step-body step) expected))))) 127 | 128 | (ert-deftest print-step-body-multiple-args () 129 | "Should return correct step body when multiple args." 130 | (with-steps 131 | (Given "^state \"\\(known\\)\" and \"\\(unknown\\)\"$" 'ignore) 132 | (let ((step (mock-step "Given state \"known\" and \"unknown\"")) 133 | (expected "state \\\"\\\\([^\\\"]+\\\\)\\\" and \\\"\\\\([^\\\"]+\\\\)\\\"")) 134 | (should (equal (ecukes-print-step-body step) expected))))) 135 | 136 | (ert-deftest print-intro () 137 | "Should print intro." 138 | (with-messages 139 | (lambda (messages) 140 | (with-parse-feature 141 | "simple" 142 | (lambda (feature intro scenarios background steps) 143 | (ecukes-print-intro intro) 144 | (let ((expected (list "Feature: Simple" " This" " Is" " Simple" " "))) 145 | (should (equal messages expected)))))))) 146 | 147 | (ert-deftest print-stats-summary () 148 | "Should print stats summary." 149 | (with-mock 150 | (stub ecukes-stats-summary => "SUMMARY") 151 | (with-messages 152 | (lambda (messages) 153 | (ecukes-print-stats-summary) 154 | (should 155 | (equal messages (list "SUMMARY"))))))) 156 | 157 | (ert-deftest print-newline () 158 | "Should print newline." 159 | (with-mock 160 | (mock (ecukes-print-message " ") :times 1) 161 | (ecukes-print-newline))) 162 | 163 | (ert-deftest print-message () 164 | "Should print formatted message." 165 | (with-mock 166 | (mock (ecukes-print-format "MESSAGE" nil) :times 1) 167 | (with-messages 168 | (lambda (messages) 169 | (ecukes-print-message "MESSAGE"))))) 170 | 171 | (ert-deftest format-message-simple () 172 | "Should format message when simple." 173 | (should (equal (ecukes-print-format "MESSAGE") "MESSAGE"))) 174 | 175 | (ert-deftest format-message () 176 | "Should format message." 177 | (should (equal (ecukes-print-format "MESSAGE %s" "EGASSEM") "MESSAGE EGASSEM"))) 178 | 179 | (ert-deftest format-message-offset () 180 | "Should format message with offset." 181 | (let ((ecukes-print-offset 2)) 182 | (should (equal (ecukes-print-format "MESSAGE") " MESSAGE")))) 183 | 184 | (ert-deftest print-background-header () 185 | "Should print background header." 186 | (with-messages 187 | (lambda (messages) 188 | (ecukes-print-background-header) 189 | (should (equal messages (list " Background:")))))) 190 | 191 | (ert-deftest print-scenario-header () 192 | "Should print scenario header." 193 | (with-messages 194 | (lambda (messages) 195 | (let ((scenario (make-ecukes-scenario :name "Simple"))) 196 | (ecukes-print-scenario-header scenario) 197 | (should (equal messages (list " Scenario: Simple"))))))) 198 | 199 | (ert-deftest print-scenario-header-tags () 200 | "Should print scenario header when tags." 201 | (with-messages 202 | (lambda (messages) 203 | (let ((scenario 204 | (make-ecukes-scenario :name "Simple" :tags (list "foo" "bar"))) 205 | (expected 206 | (list (format " %s" (ansi-cyan "@foo @bar")) " Scenario: Simple"))) 207 | (ecukes-print-scenario-header scenario) 208 | (should (equal messages expected)))))) 209 | 210 | (ert-deftest print-step-success () 211 | "Should print successful step." 212 | (with-messages 213 | (lambda (messages) 214 | (let ((step (mock-step "Given a state")) 215 | (expected (list (format " %s" (ansi-green "Given a state"))))) 216 | (ecukes-print-step step 'success) 217 | (should (equal messages expected)))))) 218 | 219 | (ert-deftest print-step-failure () 220 | "Should print successful step." 221 | (with-messages 222 | (lambda (messages) 223 | (let ((step (mock-step "Given a state" :err "ERROR")) 224 | (expected 225 | (list 226 | (format " %s" (ansi-red "Given a state")) 227 | (format " %s" (ansi-red "ERROR"))))) 228 | (ecukes-print-step step 'failure) 229 | (should (equal messages expected)))))) 230 | 231 | (ert-deftest print-step-skipped () 232 | "Should print skipped step." 233 | (with-messages 234 | (lambda (messages) 235 | (let ((step (mock-step "Given a state")) 236 | (expected 237 | (list 238 | (format " %s" (ansi-cyan "Given a state"))))) 239 | (ecukes-print-step step 'skipped) 240 | (should (equal messages expected)))))) 241 | 242 | (ert-deftest print-step-failure-no-error () 243 | "Should print failed step no error." 244 | (with-messages 245 | (lambda (messages) 246 | (let ((step (mock-step "Given a state")) 247 | (expected 248 | (list 249 | (format " %s" (ansi-red "Given a state")) 250 | (format " %s" (ansi-red "Unknown error..."))))) 251 | (ecukes-print-step step 'failure) 252 | (should (equal messages expected)))))) 253 | 254 | (ert-deftest print-step-failure-multi-line-error () 255 | "Should print successful failure multi line error." 256 | (with-messages 257 | (lambda (messages) 258 | (let ((step (mock-step "Given a state" :err "KNOWN\nERROR")) 259 | (expected 260 | (list 261 | (format " %s" (ansi-red "Given a state")) 262 | (format " %s" (ansi-red "KNOWN")) 263 | (format " %s" (ansi-red "ERROR"))))) 264 | (ecukes-print-step step 'failure) 265 | (should (equal messages expected)))))) 266 | 267 | (ert-deftest print-step-table () 268 | "Should print table if step is table step." 269 | (with-mock 270 | (mock (ecukes-print-table) :times 1) 271 | (with-messages 272 | (lambda (messages) 273 | (let ((step 274 | (mock-step "Given this:" :type 'table))) 275 | (ecukes-print-step step 'success)))))) 276 | 277 | (ert-deftest print-step-py-string () 278 | "Should print py-string if step is py-string step." 279 | (with-mock 280 | (mock (ecukes-print-py-string) :times 1) 281 | (with-messages 282 | (lambda (messages) 283 | (let ((step 284 | (mock-step "Given this:" :type 'py-string))) 285 | (ecukes-print-step step 'success)))))) 286 | 287 | (ert-deftest print-step-table-success () 288 | "Should print table if step is table step when success." 289 | (with-messages 290 | (lambda (messages) 291 | (let ((step (mock-step "Given this:" :type 'table :arg '(("x" "y") ("x1" "y1") ("x2" "y2")))) 292 | (expected 293 | (list 294 | (format " | %s | %s |" (ansi-green "x") (ansi-green "y")) 295 | (format " | %s | %s |" (ansi-green "x1") (ansi-green "y1")) 296 | (format " | %s | %s |" (ansi-green "x2") (ansi-green "y2"))))) 297 | (ecukes-print-table step 'success) 298 | (should (equal messages expected)))))) 299 | 300 | (ert-deftest print-step-table-failure () 301 | "Should print table if step is table step when failure." 302 | (with-messages 303 | (lambda (messages) 304 | (let ((step (mock-step "Given this:" :type 'table :arg '(("x" "y") ("x1" "y1") ("x2" "y2")))) 305 | (expected 306 | (list 307 | (format " | %s | %s |" (ansi-red "x") (ansi-red "y")) 308 | (format " | %s | %s |" (ansi-red "x1") (ansi-red "y1")) 309 | (format " | %s | %s |" (ansi-red "x2") (ansi-red "y2"))))) 310 | (ecukes-print-table step 'failure) 311 | (should (equal messages expected)))))) 312 | 313 | (ert-deftest print-step-table-skipped () 314 | "Should print table if step is table step when skipped." 315 | (with-messages 316 | (lambda (messages) 317 | (let ((step (mock-step "Given this:" :type 'table :arg '(("x" "y") ("x1" "y1") ("x2" "y2")))) 318 | (expected 319 | (list 320 | (format " | %s | %s |" (ansi-cyan "x") (ansi-cyan "y")) 321 | (format " | %s | %s |" (ansi-cyan "x1") (ansi-cyan "y1")) 322 | (format " | %s | %s |" (ansi-cyan "x2") (ansi-cyan "y2"))))) 323 | (ecukes-print-table step 'skipped) 324 | (should (equal messages expected)))))) 325 | 326 | (ert-deftest print-step-py-string-success () 327 | "Should print py-string if step is py-string step when success." 328 | (with-messages 329 | (lambda (messages) 330 | (let ((step (mock-step "Given this:" :type 'table :arg "foo\nbar\nbaz\nqux")) 331 | (expected 332 | (list 333 | (format " %s" (ansi-green "\"\"\"")) 334 | (format " %s" (ansi-green "foo")) 335 | (format " %s" (ansi-green "bar")) 336 | (format " %s" (ansi-green "baz")) 337 | (format " %s" (ansi-green "qux")) 338 | (format " %s" (ansi-green "\"\"\""))))) 339 | (ecukes-print-py-string step 'success) 340 | (should (equal messages expected)))))) 341 | 342 | (ert-deftest print-step-py-string-failure () 343 | "Should print py-string if step is py-string step when failure." 344 | (with-messages 345 | (lambda (messages) 346 | (let ((step (mock-step "Given this:" :type 'table :arg "foo\nbar\nbaz\nqux")) 347 | (expected 348 | (list 349 | (format " %s" (ansi-red "\"\"\"")) 350 | (format " %s" (ansi-red "foo")) 351 | (format " %s" (ansi-red "bar")) 352 | (format " %s" (ansi-red "baz")) 353 | (format " %s" (ansi-red "qux")) 354 | (format " %s" (ansi-red "\"\"\""))))) 355 | (ecukes-print-py-string step 'failure) 356 | (should (equal messages expected)))))) 357 | 358 | (ert-deftest print-step-py-string-skipped () 359 | "Should print py-string if step is py-string step when skipped." 360 | (with-messages 361 | (lambda (messages) 362 | (let ((step (mock-step "Given this:" :type 'table :arg "foo\nbar\nbaz\nqux")) 363 | (expected 364 | (list 365 | (format " %s" (ansi-cyan "\"\"\"")) 366 | (format " %s" (ansi-cyan "foo")) 367 | (format " %s" (ansi-cyan "bar")) 368 | (format " %s" (ansi-cyan "baz")) 369 | (format " %s" (ansi-cyan "qux")) 370 | (format " %s" (ansi-cyan "\"\"\""))))) 371 | (ecukes-print-py-string step 'skipped) 372 | (should (equal messages expected)))))) 373 | 374 | (ert-deftest print-steps-no-docs-or-file () 375 | "Should print list of steps with no docs or file." 376 | (with-steps 377 | (Given "^a known state$" 'ignore) 378 | (Given "^an unknown state$" 'ignore) 379 | (with-messages 380 | (lambda (messages) 381 | (let ((expected 382 | (list 383 | (ansi-green "^an unknown state$") 384 | (ansi-green "^a known state$")))) 385 | (ecukes-print-steps) 386 | (should (equal messages expected))))))) 387 | 388 | (ert-deftest print-steps-with-docs () 389 | "Should print list of steps with docs but no file." 390 | (with-steps 391 | (Given "^a known state$" "Will be in a known state" 'ignore) 392 | (Given "^an unknown state$" "Will be in an unknown state" 'ignore) 393 | (with-messages 394 | (lambda (messages) 395 | (let ((expected 396 | (list 397 | (concat (ansi-green "^an unknown state$") "\n" (ansi-cyan "Will be in an unknown state") "\n") 398 | (concat (ansi-green "^a known state$") "\n" (ansi-cyan "Will be in a known state") "\n")))) 399 | (ecukes-print-steps t) 400 | (should (equal messages expected))))))) 401 | 402 | (ert-deftest print-steps-with-file () 403 | "Should print list of steps with file but no docs." 404 | (with-steps 405 | (Given "^a known state$" "Will be in a known state" 'ignore) 406 | (Given "^an unknown state$" "Will be in an unknown state" 'ignore) 407 | (with-messages 408 | (lambda (messages) 409 | (let ((expected 410 | (list 411 | (concat "test/ecukes-test: " (ansi-green "^an unknown state$")) 412 | (concat "test/ecukes-test: " (ansi-green "^a known state$"))))) 413 | (ecukes-print-steps nil t) 414 | (should (equal messages expected))))))) 415 | 416 | (ert-deftest print-steps-with-file-and-docs () 417 | "Should print list of steps with file and docs." 418 | (with-steps 419 | (Given "^a known state$" "Will be in a known state" 'ignore) 420 | (Given "^an unknown state$" "Will be in an unknown state" 'ignore) 421 | (with-messages 422 | (lambda (messages) 423 | (let ((expected 424 | (list 425 | (concat "test/ecukes-test: " (ansi-green "^an unknown state$") "\n" (ansi-cyan "Will be in an unknown state") "\n") 426 | (concat "test/ecukes-test: " (ansi-green "^a known state$") "\n" (ansi-cyan "Will be in a known state") "\n")))) 427 | (ecukes-print-steps t t) 428 | (should (equal messages expected))))))) 429 | 430 | (ert-deftest print-status-success () 431 | "Should print in green when success." 432 | (with-mock 433 | (mock (ansi-green "SUCCESS") :times 1) 434 | (ecukes-print-status "SUCCESS" 'success))) 435 | 436 | (ert-deftest print-status-failure () 437 | "Should print in green when failure." 438 | (with-mock 439 | (mock (ansi-red "FAILURE") :times 1) 440 | (ecukes-print-status "FAILURE" 'failure))) 441 | 442 | (ert-deftest print-status-skipped () 443 | "Should print in green when skipped." 444 | (with-mock 445 | (mock (ansi-cyan "SKIPPED") :times 1) 446 | (ecukes-print-status "SKIPPED" 'skipped))) 447 | --------------------------------------------------------------------------------