cuke4php 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -2,4 +2,14 @@
2
2
  * first gem release
3
3
 
4
4
  2011-01-27 -- 0.9.1
5
- * allow cuke4php to accept a feature filename instead of a path name so users can run just one feature file at a time
5
+ * allow cuke4php to accept a feature filename instead of a path name so users can run just one feature file at a time
6
+
7
+ 2011-01-27 -- 0.9.2
8
+ * fix a bug preventing use by just passing a directory name
9
+
10
+ 2011-01-28
11
+ * trap and handle php errors, convert them to exceptions and then let them bubble up as usual.
12
+
13
+ 2011-01-30
14
+ * use magic setters and getters to allow assignment and retrieval of properties on steps
15
+ * implement step argument transformations
data/README.md CHANGED
@@ -33,6 +33,7 @@ Roadmap
33
33
 
34
34
  * support an option like 'cuke4php --init' which will generate the directory structure and support files necessary to use cuke4php with a php project.
35
35
  * autodetect an available port and then use it to run the cuke4php server pass this on to cucumber by setting an environment variable (requires a modification to cucumber)
36
+ * lint check all php files in features directory before starting the cuke4php server
36
37
 
37
38
  ### Gemfile
38
39
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.2
1
+ 0.9.3
data/bin/cuke4php CHANGED
@@ -5,6 +5,8 @@
5
5
  # TODO: autodetect an available port and then use it to run the cuke4php server pass this on to cucumber by setting an environment variable (requires a modification to cucumber)
6
6
  # see https://github.com/olbrich/cucumber/commit/0c11d206e08e8d5647e03bf78be93723d59529ef
7
7
 
8
+ # TODO: lint check all php files before starting the cuke4php server
9
+
8
10
  # Note that changing the port number currently requires updating the Cuke4Php.wire file in your project's step_definitions
9
11
  # or cucumber will look for the cuke4php server on the wrong port.
10
12
  CUKE4PHP_PORT=ENV['CUKE4PHP_PORT'] || 16816
data/cuke4php.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{cuke4php}
8
- s.version = "0.9.2"
8
+ s.version = "0.9.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Kevin Olbrich", "Alessandro Dal Grande"]
12
- s.date = %q{2011-01-27}
12
+ s.date = %q{2011-03-23}
13
13
  s.default_executable = %q{cuke4php}
14
14
  s.description = %q{Using this protocol it is possible to directly interact with PHP code at any level without the need for a web server. To accomplish this, when cucumber is running against a directory containing feature files and it cannot resolve a particular step definition, it will ask a known wire server (as defined in a .wire file) to interpret and run those steps.}
15
15
  s.email = ["kevin.olbrich+cuke4php@gmail.com", "aledalgrande@gmail.com"]
@@ -30,10 +30,12 @@ Gem::Specification.new do |s|
30
30
  "cucumber.yml",
31
31
  "cuke4php.gemspec",
32
32
  "features/Cuke4Php.feature",
33
+ "features/scenario.feature",
33
34
  "features/step_definitions/Cuke4Php.wire",
34
35
  "features/step_definitions/TestSteps.php",
35
36
  "features/step_definitions/WireSteps.php",
36
37
  "features/support/Env.php",
38
+ "features/transform.feature",
37
39
  "lib/Cucumber.php",
38
40
  "lib/CucumberScenario.php",
39
41
  "lib/CucumberSteps.php",
@@ -40,3 +40,18 @@ Scenario Outline: run several steps many times
40
40
  | shoot | messenger | dead |
41
41
  | understand | meaning | clear |
42
42
 
43
+ @error
44
+ Scenario Outline: Error Handling
45
+ When an error "<error_constant>" with message "<error_message>" occurs
46
+ Then an "<exception>" should be caught
47
+
48
+ Examples:
49
+ | error_constant | error_message | exception |
50
+ | E_USER_ERROR | an error has occurred | PHPUnit_Framework_Error |
51
+ | E_USER_WARNING | a warning message | PHPUnit_Framework_Error_Warning |
52
+ | E_USER_NOTICE | a notice message | PHPUnit_Framework_Error_Notice |
53
+
54
+ @exception
55
+ Scenario: Exception Handling
56
+ When an "Exception" is thrown with message "generic exception"
57
+ Then an "Exception" should be caught
@@ -0,0 +1,45 @@
1
+ Feature: Scenarios
2
+
3
+ Scenario Outline: I save values as properties and use them again later
4
+ Given I store "<value>" into "<value>"
5
+ Then "<value>" should equal "<value>"
6
+
7
+ Examples:
8
+ | value |
9
+ | ! |
10
+ | @ |
11
+ | # |
12
+ | $ |
13
+ | % |
14
+ | ^ |
15
+ | & |
16
+ | * |
17
+ | ( |
18
+ | ) |
19
+ | - |
20
+ | = |
21
+ | () |
22
+ | with spaces |
23
+ | $sVar |
24
+
25
+ Scenario: I save an array as a property
26
+ Given "value" is:
27
+ | key | value |
28
+ | key1 | value1 |
29
+ | key2 | value2 |
30
+ Then "value" should be set
31
+ Then "non-value" should not be set
32
+ And "value" should equal:
33
+ | key | value |
34
+ | key1 | value1 |
35
+ | key2 | value2 |
36
+
37
+ Scenario: I unset a property
38
+ Given I store "Foo" into "Bar"
39
+ When I unset "Bar"
40
+ Then "Bar" should not be set
41
+
42
+ Scenario: I try to read a property that was not set first
43
+ Given "Foo" should not be set
44
+ Then getting "Foo" should raise an "PHPUnit_Framework_Error"
45
+
@@ -45,7 +45,7 @@ class TestSteps extends CucumberSteps {
45
45
  * Given /^an exception is thrown$/
46
46
  */
47
47
  public function stepException() {
48
- throw new TestException('Exception');
48
+ throw new Exception('test exception');
49
49
  }
50
50
 
51
51
  /**
@@ -11,7 +11,7 @@ class WireSteps extends CucumberSteps {
11
11
  * @wire
12
12
  */
13
13
  function beforeWire() {
14
- $this->aGlobals['before'] = 'beforeWire';
14
+ $this->before = 'beforeWire';
15
15
  }
16
16
 
17
17
  function beforeAll() {
@@ -81,7 +81,137 @@ class WireSteps extends CucumberSteps {
81
81
 
82
82
  }
83
83
 
84
+ /**
85
+ * When /^an error "([^"]*)" with message "([^"]*)" occurs$/
86
+ **/
87
+ public function stepAnErrorParameterWithMessageParameterOccurs($sType,$sMessage) {
88
+ try {
89
+ trigger_error($sMessage, constant($sType));
90
+ } catch (Exception $e) {
91
+ $this->exception = $e;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * When /^an? "([^"]*)" is thrown with message "([^"]*)"$/
97
+ **/
98
+ public function stepAnExceptionParameterIsThrownWithMessageParameter($sExceptionClass,$sMessage) {
99
+ try {
100
+ throw new $sExceptionClass($sMessage);
101
+ } catch (Exception $e) {
102
+ $this->exception = $e;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Then /^an? "([^"]*)" should be caught$/
108
+ **/
109
+ public function stepAParameterExceptionShouldBeCaught($sExceptionType) {
110
+ self::assertInstanceOf($sExceptionType, $this->exception);
111
+ }
112
+
113
+ /**
114
+ * Given /^I store "([^"]*)" into "([^"]*)"$/
115
+ **/
116
+ public function stepIStoreParameterIntoParameter($sValue,$sKey) {
117
+ $this->$sKey = $sValue;
118
+ }
119
+
120
+
121
+ /**
122
+ * Then /^"([^"]*)" should equal "([^"]*)"$/
123
+ **/
124
+ public function stepParameterShouldEqualParameter($sKey,$sValue) {
125
+ self::assertEquals($sValue, $this->$sKey);
126
+ }
127
+
128
+ /**
129
+ * When /^I unset "([^"]*)"$/
130
+ **/
131
+ public function stepIUnsetParameter($sKey) {
132
+ unset($this->$sKey);
133
+ }
134
+
135
+ /**
136
+ * Then /^"([^"]*)" (should|should not) be set$/
137
+ **/
138
+ public function stepParameterShouldBeSet($sKey, $sShould) {
139
+ self::assertEquals(($sShould == "should"), isset($this->$sKey));
140
+ }
141
+
142
+ /**
143
+ * Given /^"([^"]*)" is\:$/
144
+ **/
145
+ public function stepParameterIs($sKey,$aTable) {
146
+ array_shift($aTable); // peel off the table column headings
147
+ $this->$sKey = $aTable;
148
+ }
84
149
 
150
+ /**
151
+ * Then /^"([^"]*)" should equal\:$/
152
+ **/
153
+ public function stepParameterShouldEqual($sKey,$aTable) {
154
+ array_shift($aTable); // peel off the table column headings
155
+ self::assertEquals($aTable, $this->$sKey);
156
+ }
157
+
158
+ /**
159
+ * Then /^getting "([^"]*)" should raise an? "([^"]*)"$/
160
+ **/
161
+ public function stepGettingParameterShouldRaiseAnParameter($sKey, $sExceptionClass) {
162
+ unset($this->exception);
163
+ try {
164
+ $this->$sKey;
165
+ self::fail("No Exception Caught");
166
+ } catch (Exception $e) {
167
+ $this->exception = $e;
168
+ }
169
+ self::assertInstanceOf($sExceptionClass, $this->exception);
170
+ }
171
+
172
+ /**
173
+ * Transform /^(\d+)$/
174
+ **/
175
+ public function transformToInteger($sArg) {
176
+ return intval($sArg);
177
+ }
178
+
179
+ /**
180
+ * Transform /^(abcd)$/
181
+ **/
182
+ public function transformReverse($sArg) {
183
+ return "dcba";
184
+ }
185
+
186
+ /**
187
+ * Transform /^(abcd)$/
188
+ **/
189
+ public function transformCapitalize($sArg) {
190
+ return "ABCD";
191
+ }
192
+
193
+ /**
194
+ * Transform /^\{(.*)\}$/
195
+ **/
196
+ public function transformSubstituteValues($sArg) {
197
+ return $this->$sArg;
198
+ }
199
+
200
+ /**
201
+ * Transform /^table:reverse$/
202
+ **/
203
+ public function transformReverseTable($aTable) {
204
+ return array_reverse($aTable);
205
+ }
206
+
207
+
208
+ /**
209
+ * Then /^"([^"]*)" should be a kind of "([^"]*)"$/
210
+ **/
211
+ public function stepParameterShouldBeAKindOfParameter($sKey,$sTypeName) {
212
+ self::assertInternalType($sTypeName, $this->$sKey);
213
+ }
214
+
85
215
  }
86
216
 
87
217
  ?>
@@ -0,0 +1,30 @@
1
+ Feature: Argument Transformation
2
+ As a user of cuke4php
3
+ I want to use step argument transforms
4
+ So that I can simplify my code
5
+ Example transforms can be found in WireSteps.php
6
+
7
+ Scenario: An argument is transformed to an integer
8
+ Given I store "123" into "Foo"
9
+ Then "Foo" should be a kind of "integer"
10
+
11
+ Scenario: use a transform to substitute variable in arguments
12
+ Given I store "foo" into "Bar"
13
+ And I store "{Bar}" into "Foo"
14
+ Then "Foo" should equal "foo"
15
+
16
+ Scenario: An argument is transformed, but the second defined transform wins
17
+ Given I store "abcd" into "Foo"
18
+ Then "Foo" should equal "ABCD"
19
+
20
+ Scenario: transform a table
21
+ Given "table" is:
22
+ | reverse |
23
+ | one |
24
+ | two |
25
+ | three |
26
+ Then "table" should equal:
27
+ | reverse |
28
+ | three |
29
+ | two |
30
+ | one |
@@ -58,38 +58,46 @@ class CucumberScenario {
58
58
  * @return array
59
59
  * invokes all the before hooks defined that either have no tags or tags corresponding to this scenario's tags
60
60
  */
61
- function invokeBeforeHooks($aTags) {
62
- foreach ($this->aWorld['before'] as $aBeforeHook) {
63
- if (array_key_exists('tags', $aBeforeHook))
64
- if (count($aBeforeHook['tags']) == 0 || count(array_intersect($aTags, $aBeforeHook['tags'])) > 0) {
65
- $oStep = CucumberSteps::getInstance($aBeforeHook['class'], $this->aGlobals);
66
- $oResult = $oStep->invoke($aBeforeHook['method']);
67
- if ($oResult === false) {
68
- return array('failure');
69
- }
70
- }
71
- }
72
- return array('success');
73
- }
61
+ function invokeBeforeHooks($aTags) {
62
+ if (is_null($aTags)) {
63
+ $aTags = array();
64
+ }
65
+ foreach ($this->aWorld['before'] as $aBeforeHook) {
66
+ if (array_key_exists('tags', $aBeforeHook)) {
67
+ if (count($aBeforeHook['tags']) == 0 || count(array_intersect($aTags, $aBeforeHook['tags'])) > 0) {
68
+ $oStep = CucumberSteps::getInstance($aBeforeHook['class'], $this->aGlobals);
69
+ $oResult = $oStep->invoke($aBeforeHook['method']);
70
+ if ($oResult === false) {
71
+ return array('failure');
72
+ }
73
+ }
74
+ }
75
+ }
76
+ return array('success');
77
+ }
74
78
 
75
79
  /**
76
80
  * @param $aTags
77
81
  * @return array
78
82
  * invoke all after hooks defined that either have no tags, or tags that match the tags of the current scenario
79
83
  */
80
- function invokeAfterHooks($aTags) {
81
- foreach ($this->aWorld['after'] as $aAfterHook) {
82
- if (array_key_exists('tags', $aAfterHook))
83
- if (count($aAfterHook['tags']) == 0 || count(array_intersect($aTags, $aAfterHook['tags'])) > 0) {
84
- $oStep = CucumberSteps::getInstance($aAfterHook['class'], $this->aGlobals);
85
- $oResult = $oStep->invoke($aAfterHook['method']);
86
- if ($oResult === false) {
87
- return array('failure');
88
- }
89
- }
90
- }
91
- return array('success');
92
- }
84
+ function invokeAfterHooks($aTags) {
85
+ if (is_null($aTags)) {
86
+ $aTags = array();
87
+ }
88
+ foreach ($this->aWorld['after'] as $aAfterHook) {
89
+ if (array_key_exists('tags', $aAfterHook)) {
90
+ if (count($aAfterHook['tags']) == 0 || count(array_intersect($aTags, $aAfterHook['tags'])) > 0) {
91
+ $oStep = CucumberSteps::getInstance($aAfterHook['class'], $this->aGlobals);
92
+ $oResult = $oStep->invoke($aAfterHook['method']);
93
+ if ($oResult === false) {
94
+ return array('failure');
95
+ }
96
+ }
97
+ }
98
+ }
99
+ return array('success');
100
+ }
93
101
 
94
102
  /**
95
103
  * @param $iStepId
@@ -104,6 +112,25 @@ class CucumberScenario {
104
112
  function invoke($iStepId, $aArgs) {
105
113
  $aStep = $this->aWorld['steps'][$iStepId];
106
114
  $oStep = new $aStep['class']($this->aGlobals);
115
+ foreach ($aArgs as &$sArg) {
116
+ $sArgTest = $sArg;
117
+ if (is_array($sArgTest)) {
118
+ $sArgTest = "table:" . implode(",",$sArgTest[0]);
119
+ }
120
+ foreach (array_reverse($this->aWorld['transform'],true) as $aTransform) {
121
+ $aMatches = array();
122
+ if (preg_match_all($aTransform['regexp'], $sArgTest, $aMatches, PREG_OFFSET_CAPTURE)) {
123
+ $oTransform = new $aTransform['class']($this->aGlobals);
124
+ if (is_array($sArg)) {
125
+ $sArg = call_user_func_array(array($oTransform, $aTransform['method']),$sArg);
126
+ } else {
127
+ $sArg = call_user_func_array(array($oTransform, $aTransform['method']),$aMatches[1][0]);
128
+ }
129
+ break;
130
+ }
131
+ }
132
+
133
+ }
107
134
  try {
108
135
  call_user_func_array(array($oStep, $aStep['method']),$aArgs);
109
136
  } catch (PHPUnit_Framework_IncompleteTestError $e) {
@@ -113,7 +140,7 @@ class CucumberScenario {
113
140
  } catch (PHPUnit_Framework_ExpectationFailedException $e) {
114
141
  return array('fail', array('message' => $e->getMessage()));
115
142
  } catch (Exception $e) {
116
- return array('fail', array('exception' => $e->__toString()));
143
+ return array('fail', array('message' => $e->getMessage() . " " . $e->getFile() . ":" . $e->getLine(), 'exception' => get_class($e), 'backtrace' => $e->getTraceAsString()));
117
144
  }
118
145
  return array('success');
119
146
  }
@@ -74,6 +74,26 @@ class CucumberSteps extends PHPUnit_Framework_Assert {
74
74
  return new $sClass($aGlobals);
75
75
  }
76
76
  }
77
+
78
+ public function __set($sName, $sValue) {
79
+ $this->aGlobals[$sName] = $sValue;
80
+ }
81
+
82
+ public function __get($sName) {
83
+ if (array_key_exists($sName, $this->aGlobals)) {
84
+ return $this->aGlobals[$sName];
85
+ } else {
86
+ trigger_error("Property ($sName) not defined", E_USER_ERROR);
87
+ }
88
+ }
89
+
90
+ public function __unset($sName) {
91
+ unset($this->aGlobals[$sName]);
92
+ }
93
+
94
+ public function __isset($sName) {
95
+ return isset($this->aGlobals[$sName]);
96
+ }
77
97
  }
78
98
 
79
99
  ?>
data/lib/Cuke4Php.php CHANGED
@@ -21,7 +21,8 @@ class Cuke4Php {
21
21
  public $aWorld = array(
22
22
  'steps' => array(),
23
23
  'before' => array(),
24
- 'after' => array()
24
+ 'after' => array(),
25
+ 'transform' => array()
25
26
  );
26
27
 
27
28
  function __construct($_sFeaturePath, $_iPort = 16816) {
@@ -33,10 +34,15 @@ class Cuke4Php {
33
34
  } else {
34
35
  $this->iPort = 16816;
35
36
  }
36
-
37
+
37
38
  foreach (self::rglob("*.php", 0, $_sFeaturePath . "/support") as $sFilename) {
38
39
  require_once $sFilename;
39
40
  }
41
+ set_error_handler(
42
+ array('PHPUnit_Util_ErrorHandler', 'handleError'),
43
+ E_ALL | E_STRICT
44
+ );
45
+
40
46
  require_once "Cucumber.php";
41
47
  foreach (self::rglob("*.php", 0, $_sFeaturePath . "/step_definitions") as $sFilename) {
42
48
  require_once $sFilename;
@@ -71,6 +77,12 @@ class Cuke4Php {
71
77
  $this->aWorld['after'][] = $aMethod;
72
78
  continue;
73
79
  }
80
+ if (substr($oMethod->name, 0, 9) == "transform") {
81
+ preg_match("/(?:Transform) (.+)$/im", $sComment, $aMatches);
82
+ $aMethod['regexp'] = $aMatches[1];
83
+ $this->aWorld['transform'][] = $aMethod;
84
+ continue;
85
+ }
74
86
  }
75
87
  }
76
88
  }
@@ -114,9 +126,9 @@ class Cuke4Php {
114
126
  $data = trim($input);
115
127
  if ($data !== "") {
116
128
  $output = json_encode($this->process($data)) . "\n";
117
- if ($this->bRun) {
118
- socket_write($connection, $output);
119
- }
129
+ if ($this->bRun) {
130
+ socket_write($connection, $output);
131
+ }
120
132
  }
121
133
  }
122
134
  socket_close($connection);
@@ -134,7 +146,8 @@ class Cuke4Php {
134
146
  default:
135
147
  $aCommand = json_decode($sInput);
136
148
  $sAction = $aCommand[0];
137
- $sData = NULL;
149
+ $sData = new stdClass;
150
+ $sData->tags = array();
138
151
  if (array_key_exists(1, $aCommand)) {
139
152
  $sData = $aCommand[1];
140
153
  }
@@ -35,6 +35,7 @@ class CucumberScenarioTest extends PHPUnit_Framework_TestCase {
35
35
  public function setup() {
36
36
  CucumberSteps::clearMocks();
37
37
  $this->aWorld = array(
38
+ 'transform' => array(),
38
39
  'before' => array(
39
40
  array(
40
41
  'tags' => array('one'),
@@ -149,7 +150,10 @@ class CucumberScenarioTest extends PHPUnit_Framework_TestCase {
149
150
  }
150
151
 
151
152
  public function testInvokeShouldFailWhenExceptionThrown() {
152
- self::assertEquals(array('fail',array('exception' => 'TestException')), $this->oScenario->invoke(5,array()));
153
+ $aResult = $this->oScenario->invoke(5,array());
154
+ self::assertEquals('fail',$aResult[0]);
155
+ self::assertEquals('Exception', $aResult[1]['exception']);
156
+ self::assertEquals('test exception /Users/kolbrich/local_src/cuke4php/features/step_definitions/TestSteps.php:48', $aResult[1]['message']);
153
157
  }
154
158
 
155
159
  public function testInvokeShouldSucceedWithParameters() {
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cuke4php
3
3
  version: !ruby/object:Gem::Version
4
- hash: 63
4
+ hash: 61
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 2
10
- version: 0.9.2
9
+ - 3
10
+ version: 0.9.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Kevin Olbrich
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-01-27 00:00:00 -05:00
19
+ date: 2011-03-23 00:00:00 -04:00
20
20
  default_executable: cuke4php
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -85,10 +85,12 @@ files:
85
85
  - cucumber.yml
86
86
  - cuke4php.gemspec
87
87
  - features/Cuke4Php.feature
88
+ - features/scenario.feature
88
89
  - features/step_definitions/Cuke4Php.wire
89
90
  - features/step_definitions/TestSteps.php
90
91
  - features/step_definitions/WireSteps.php
91
92
  - features/support/Env.php
93
+ - features/transform.feature
92
94
  - lib/Cucumber.php
93
95
  - lib/CucumberScenario.php
94
96
  - lib/CucumberSteps.php