1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Copyright (c) 2011-present Mediasift Ltd |
5
|
|
|
* All rights reserved. |
6
|
|
|
* |
7
|
|
|
* Redistribution and use in source and binary forms, with or without |
8
|
|
|
* modification, are permitted provided that the following conditions |
9
|
|
|
* are met: |
10
|
|
|
* |
11
|
|
|
* * Redistributions of source code must retain the above copyright |
12
|
|
|
* notice, this list of conditions and the following disclaimer. |
13
|
|
|
* |
14
|
|
|
* * Redistributions in binary form must reproduce the above copyright |
15
|
|
|
* notice, this list of conditions and the following disclaimer in |
16
|
|
|
* the documentation and/or other materials provided with the |
17
|
|
|
* distribution. |
18
|
|
|
* |
19
|
|
|
* * Neither the names of the copyright holders nor the names of his |
20
|
|
|
* contributors may be used to endorse or promote products derived |
21
|
|
|
* from this software without specific prior written permission. |
22
|
|
|
* |
23
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
24
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
25
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
26
|
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
27
|
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
28
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
29
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
30
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
31
|
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
32
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
33
|
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
34
|
|
|
* POSSIBILITY OF SUCH DAMAGE. |
35
|
|
|
* |
36
|
|
|
* @category Libraries |
37
|
|
|
* @package Storyplayer/Cli |
38
|
|
|
* @author Stuart Herbert <[email protected]> |
39
|
|
|
* @copyright 2011-present Mediasift Ltd www.datasift.com |
40
|
|
|
* @license http://www.opensource.org/licenses/bsd-license.php BSD License |
41
|
|
|
* @link http://datasift.github.io/storyplayer |
42
|
|
|
*/ |
43
|
|
|
|
44
|
|
|
namespace DataSift\Storyplayer\Cli; |
45
|
|
|
|
46
|
|
|
use Exception; |
47
|
|
|
use Phix_Project\CliEngine; |
48
|
|
|
use Phix_Project\CliEngine\CliCommand; |
49
|
|
|
use Phix_Project\CliEngine\CliResult; |
50
|
|
|
use Phix_Project\ExceptionsLib1\Legacy_ErrorHandler; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* A command to create a new story to fill in |
54
|
|
|
* |
55
|
|
|
* @category Libraries |
56
|
|
|
* @package Storyplayer/Cli |
57
|
|
|
* @author Stuart Herbert <[email protected]> |
58
|
|
|
* @copyright 2011-present Mediasift Ltd www.datasift.com |
59
|
|
|
* @license http://www.opensource.org/licenses/bsd-license.php BSD License |
60
|
|
|
* @link http://datasift.github.io/storyplayer |
61
|
|
|
*/ |
62
|
|
View Code Duplication |
class CreateStory_Command extends CliCommand |
|
|
|
|
63
|
|
|
{ |
64
|
|
|
public function __construct() |
65
|
|
|
{ |
66
|
|
|
// define the command |
67
|
|
|
$this->setName('create-story'); |
68
|
|
|
$this->setShortDescription('create a new story'); |
69
|
|
|
$this->setLongDescription( |
70
|
|
|
"Use this command to create a new Story.php file, complete with " |
71
|
|
|
."the necessary PHP 'use' statement and comments to help guide you " |
72
|
|
|
."as you bring your story to life." |
73
|
|
|
.PHP_EOL |
74
|
|
|
); |
75
|
|
|
$this->setArgsList(array( |
76
|
|
|
"namedStory.php" => "the story.php file to create" |
77
|
|
|
)); |
78
|
|
|
$this->setSwitches(array( |
79
|
|
|
new CreateStory_BasedOnSwitch, |
80
|
|
|
new CreateStory_ForceSwitch |
81
|
|
|
)); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* |
86
|
|
|
* @param CliEngine $engine |
87
|
|
|
* @param array $params |
88
|
|
|
* @param mixed $additionalContext |
89
|
|
|
* @return int |
|
|
|
|
90
|
|
|
*/ |
91
|
|
|
public function processCommand(CliEngine $engine, $params = array(), $additionalContext = null) |
92
|
|
|
{ |
93
|
|
|
// do we have the name of the file to create? |
94
|
|
|
if (!isset($params[0])) { |
95
|
|
|
echo "*** error: you must specify which story to create\n"; |
96
|
|
|
exit(1); |
|
|
|
|
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
// we're going to be dealing with some prehistoric parts of PHP |
100
|
|
|
$legacyHandler = new Legacy_ErrorHandler(); |
101
|
|
|
|
102
|
|
|
// create the path to the story |
103
|
|
|
$storyFolder = dirname($params[0]); |
104
|
|
|
if (!file_exists($storyFolder)) { |
105
|
|
|
try { |
106
|
|
|
$legacyHandler->run(function() use ($storyFolder) { |
107
|
|
|
mkdir($storyFolder, 0755, true); |
108
|
|
|
}); |
109
|
|
|
} |
110
|
|
|
catch (Exception $e) { |
111
|
|
|
echo "*** error: unable to create folder '{$storyFolder}'\n"; |
112
|
|
|
exit(1); |
|
|
|
|
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
// create the story inside the folder |
117
|
|
|
$story = <<<EOS |
118
|
|
|
<?php |
119
|
|
|
|
120
|
|
|
use Storyplayer\SPv2\Modules\Asserts; |
121
|
|
|
use Storyplayer\SPv2\Modules\Checkpoint; |
122
|
|
|
use Storyplayer\SPv2\Modules\Log; |
123
|
|
|
use Storyplayer\SPv2\Stories\BuildStory; |
124
|
|
|
|
125
|
|
|
EOS; |
126
|
|
|
|
127
|
|
|
if (isset($engine->options->basedOn)) { |
128
|
|
|
foreach ($engine->options->basedOn as $templateClass) { |
129
|
|
|
$story .= "use {$templateClass};\n"; |
130
|
|
|
} |
131
|
|
|
} |
132
|
|
|
$story .= <<<EOS |
133
|
|
|
|
134
|
|
|
// ======================================================================== |
135
|
|
|
// |
136
|
|
|
// STORY DETAILS |
137
|
|
|
// |
138
|
|
|
// ------------------------------------------------------------------------ |
139
|
|
|
|
140
|
|
|
\$story = BuildStory::newStory(); |
141
|
|
|
EOS; |
142
|
|
|
|
143
|
|
|
if (isset($engine->options->basedOn)) { |
144
|
|
|
foreach ($engine->options->basedOn as $templateClass) { |
145
|
|
|
$story .= "\n\$story->basedOn(new " . basename(str_replace('\\', '/', $templateClass)) . ");"; |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
$story .= <<<EOS |
150
|
|
|
|
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* the scenario uses Ubquitous Language to describe what you are testing |
154
|
|
|
* |
155
|
|
|
* unlike other test tools, Storyplayer does not parse your scenario in any way |
156
|
|
|
* it is only used for display purposes, to make your test self-documenting |
157
|
|
|
* |
158
|
|
|
* INSTRUCTIONS: |
159
|
|
|
* |
160
|
|
|
* pick one of the formats below that best fits your test, |
161
|
|
|
* edit to suit, |
162
|
|
|
* and delete the other scenario format(s) |
163
|
|
|
*/ |
164
|
|
|
|
165
|
|
|
// this format suits a functional test |
166
|
|
|
\$story->setScenario([ |
167
|
|
|
// what are the pre-conditions? |
168
|
|
|
"given:", |
169
|
|
|
"- first pre-condition", |
170
|
|
|
"- second pre-condition", |
171
|
|
|
// how will the actions be performed? |
172
|
|
|
// e.g. using a browser, or an API, |
173
|
|
|
"using ...", |
174
|
|
|
// who or what is performing the actions? |
175
|
|
|
"as ...", |
176
|
|
|
// what functionality is being tested? |
177
|
|
|
"I can ...", |
178
|
|
|
"even if:", |
179
|
|
|
"- pre-condition", |
180
|
|
|
// has anything changed as a result of the actions? |
181
|
|
|
// if so, list each expected change here |
182
|
|
|
"afterwards:", |
183
|
|
|
"- XXX will exist in the database" |
184
|
|
|
]); |
185
|
|
|
|
186
|
|
|
// this format suits a test that focuses on checking for a specific consequence |
187
|
|
|
// of an action (e.g. testing robustness or correctness) |
188
|
|
|
\$story->setScenario([ |
189
|
|
|
// what are the pre-conditions? |
190
|
|
|
"given:", |
191
|
|
|
"- first pre-condition", |
192
|
|
|
"- second pre-condition", |
193
|
|
|
// how will the actions be performed? |
194
|
|
|
// e.g. using a browser, or an API, |
195
|
|
|
"using ...", |
196
|
|
|
// who or what is performing the actions? |
197
|
|
|
"as ...", |
198
|
|
|
// what output do I expect? |
199
|
|
|
"if I ...", |
200
|
|
|
"- I get XXX", // e.g. HTTP 404 |
201
|
|
|
"even if:", |
202
|
|
|
"- first pre-condition", |
203
|
|
|
// has anything changed as a result of the actions? |
204
|
|
|
// if so, list each expected change here |
205
|
|
|
"afterwards:", |
206
|
|
|
"- XXX will exist in the database" |
207
|
|
|
]); |
208
|
|
|
|
209
|
|
|
// ======================================================================== |
210
|
|
|
// |
211
|
|
|
// TEST SETUP / TEAR-DOWN |
212
|
|
|
// |
213
|
|
|
// ------------------------------------------------------------------------ |
214
|
|
|
|
215
|
|
|
/* |
216
|
|
|
\$story->addTestSetup(function() { |
217
|
|
|
// what are we doing? |
218
|
|
|
\$log = Log::usingLog()->startAction("describe what we are doing"); |
219
|
|
|
|
220
|
|
|
// setup the conditions for this specific test |
221
|
|
|
\$checkpoint = Checkpoint::getCheckpoint(); |
222
|
|
|
|
223
|
|
|
// all done |
224
|
|
|
\$log->endAction(); |
225
|
|
|
}); |
226
|
|
|
*/ |
227
|
|
|
|
228
|
|
|
/* |
229
|
|
|
\$story->addTestTeardown(function() { |
230
|
|
|
// what are we doing? |
231
|
|
|
\$log = Log::usingLog()->startAction("describe what we are doing"); |
232
|
|
|
|
233
|
|
|
// undo anything that you did in addTestSetup() |
234
|
|
|
|
235
|
|
|
// all done |
236
|
|
|
\$log->endAction(); |
237
|
|
|
}); |
238
|
|
|
*/ |
239
|
|
|
|
240
|
|
|
// ======================================================================== |
241
|
|
|
// |
242
|
|
|
// PRE-TEST PREDICTION |
243
|
|
|
// |
244
|
|
|
// ------------------------------------------------------------------------ |
245
|
|
|
|
246
|
|
|
/* |
247
|
|
|
\$story->addPreTestPrediction(function() { |
248
|
|
|
// what are we doing? |
249
|
|
|
\$log = Log::usingLog()->startAction("describe what we are doing"); |
250
|
|
|
|
251
|
|
|
// if it is okay for your story to fail, detect that here |
252
|
|
|
|
253
|
|
|
// all done |
254
|
|
|
\$log->endAction(); |
255
|
|
|
}); |
256
|
|
|
*/ |
257
|
|
|
|
258
|
|
|
// ======================================================================== |
259
|
|
|
// |
260
|
|
|
// PRE-TEST INSPECTION |
261
|
|
|
// |
262
|
|
|
// ------------------------------------------------------------------------ |
263
|
|
|
|
264
|
|
|
/* |
265
|
|
|
\$story->addPreTestInspection(function() { |
266
|
|
|
// what are we doing? |
267
|
|
|
\$log = Log::usingLog()->startAction("describe what we are doing"); |
268
|
|
|
|
269
|
|
|
// get the checkpoint - we're going to store data in here |
270
|
|
|
\$checkpoint = Checkpoint::getCheckpoint(); |
271
|
|
|
|
272
|
|
|
// store any data that your story is about to change, so that you |
273
|
|
|
// can do a before and after comparison |
274
|
|
|
|
275
|
|
|
// all done |
276
|
|
|
\$log->endAction(); |
277
|
|
|
}); |
278
|
|
|
*/ |
279
|
|
|
|
280
|
|
|
// ======================================================================== |
281
|
|
|
// |
282
|
|
|
// ACTIONS |
283
|
|
|
// |
284
|
|
|
// ------------------------------------------------------------------------ |
285
|
|
|
|
286
|
|
|
/* |
287
|
|
|
\$story->addAction(function() { |
288
|
|
|
// what are we doing? |
289
|
|
|
\$log = Log::usingLog()->startAction("describe what we are doing"); |
290
|
|
|
|
291
|
|
|
// use the checkpoint to store any data collected during the action |
292
|
|
|
// this data will be examined in the postTestInspection phase |
293
|
|
|
\$checkpoint = Checkpoint::getCheckpoint(); |
294
|
|
|
|
295
|
|
|
// this is where you perform the steps of your user story |
296
|
|
|
|
297
|
|
|
// all done |
298
|
|
|
\$log->endAction(); |
299
|
|
|
}); |
300
|
|
|
*/ |
301
|
|
|
|
302
|
|
|
// ======================================================================== |
303
|
|
|
// |
304
|
|
|
// POST-TEST INSPECTION |
305
|
|
|
// |
306
|
|
|
// ------------------------------------------------------------------------ |
307
|
|
|
|
308
|
|
|
\$story->addPostTestInspection(function() { |
309
|
|
|
// what are we doing? |
310
|
|
|
\$log = Log::usingLog()->startAction("describe what we are doing"); |
311
|
|
|
|
312
|
|
|
// the information to guide our checks is in the checkpoint |
313
|
|
|
\$checkpoint = Checkpoint::getCheckpoint(); |
314
|
|
|
|
315
|
|
|
// gather new data, and make sure that your action actually changed |
316
|
|
|
// something. never assume that the action worked just because it |
317
|
|
|
// completed to the end with no errors or exceptions! |
318
|
|
|
|
319
|
|
|
// all done |
320
|
|
|
\$log->endAction(); |
321
|
|
|
}); |
322
|
|
|
|
323
|
|
|
EOS; |
324
|
|
|
|
325
|
|
|
// does the file already exist? |
326
|
|
|
if (file_exists($params[0])) { |
327
|
|
|
// has the user used --force? |
328
|
|
|
if (!isset($engine->options->force) || !$engine->options->force) { |
329
|
|
|
echo "*** error: file '{$params[0]}' already exists\n"; |
330
|
|
|
echo "use --force to replace this file with the new story file\n"; |
331
|
|
|
exit(1); |
|
|
|
|
332
|
|
|
} |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
try { |
336
|
|
|
$legacyHandler->run(function() use($params, $story) { |
337
|
|
|
file_put_contents($params[0], $story); |
338
|
|
|
}); |
339
|
|
|
} |
340
|
|
|
catch (Exception $e) { |
341
|
|
|
echo "*** error: " . $e->getMessage() . "\n"; |
342
|
|
|
exit(1); |
|
|
|
|
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
// all done |
346
|
|
|
return 0; |
347
|
|
|
} |
348
|
|
|
} |
349
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.