Completed
Branch master (11f43c)
by
unknown
01:28
created

GherkinParam::getValueFromArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Before step hook that provide parameter syntax notation
7
 * for accessing fixture data between Gherkin steps/tests
8
 * example:
9
 *  I see "{{param}}"
10
 *  {{param}} will be replaced by the value of Fixtures::get('param')
11
 *
12
 */
13
namespace Codeception\Extension;
14
15
use Codeception\Util\Fixtures;
16
use Behat\Gherkin\Node\TableNode;
17
use ReflectionProperty;
18
19
class GherkinParam extends \Codeception\Extension
20
{
21
  /**
22
   * @var array List events to listen to
23
   */
24
  public static $events = [
25
    //run before any suite
26
    'suite.before' => 'beforeSuite',
27
    //run before any steps
28
    'step.before' => 'beforeStep'
29
  ];
30
31
  /**
32
   * @var array Current test suite config
33
   */
34
  private static $suiteConfig;
35
36
  /**
37
   * @var array RegExp for parsing steps
38
   */
39
  private static $regEx = [
40
    'match'  => '/{{\s?[A-z0-9_:-]+\s?}}/',
41
    'filter' => '/[{}]/',
42
    'config' => '/(?:^config)?:([A-z0-9_-]+)+(?=:|$)/',
43
    'array'  => '/^(?P<var>[A-z0-9_-]+)(?:\[(?P<key>.+)])$/'
44
  ];
45
46
  /**
47
   * Parse param and replace {{.*}} by its Fixtures::get() value if exists
48
   *
49
   * @param string $param
50
   *
51
   * @return \mixed|null Returns parameter's value if exists, else parameter's name
52
   */
53
  final protected function getValueFromParam(string $param)
54
  {
55
    if (preg_match_all(self::$regEx['match'], $param, $variables)){
56
      $values = [];
57
      foreach ($variables[0] as $variable)
58
      {
59
        $variableName = trim(preg_filter(self::$regEx['filter'], '', $variable));
60
        if (preg_match(self::$regEx['config'], $variableName)) {
61
          $values[] = $this->getValueFromConfig($variableName);
62
        } elseif (preg_match(self::$regEx['array'], $variableName)) {
63
          $values[] = $this->getValueFromArray($variableName);
64
        } else {
65
          $values[] = Fixtures::get($variableName);
66
        }
67
      }
68
      $param = str_replace($variables[0], $values, $param);
69
70
      if (count($values) === 1 && $values[0] === null) {
71
        return null;
72
      }
73
    }
74
75
    return $param;
76
  }
77
78
  /**
79
   * Retrieve param value from current suite config
80
   *
81
   * @param string $param
82
   *
83
   * @return \mixed|null Returns parameter's value if exists, else null
84
   */
85
  final protected function getValueFromConfig(string $param)
86
  {
87
    $value = null;
88
    $config = self::$suiteConfig;
89
90
    preg_match_all(self::$regEx['config'], $param, $args, PREG_PATTERN_ORDER);
91
    foreach ($args[1] as $arg) {
92
      if (array_key_exists($arg, $config)) {
93
        $value = $config[$arg];
94
        if (is_array($value)) {
95
          $config = $value;
96
        } else {
97
          break;
98
        }
99
      }
100
    }
101
    return $value;
102
  }
103
104
  /**
105
   * Retrieve param value from array in Fixtures
106
   *
107
   * @param string $param
108
   *
109
   * @return \mixed|null Returns parameter's value if exists, else null
110
   */
111
  final protected function getValueFromArray(string $param)
112
  {
113
    $value = null;
114
115
    preg_match_all(self::$regEx['array'], $param, $args);
116
    $array = Fixtures::get($args['var'][0]);
117
    if (array_key_exists($args['key'][0], $array)) {
118
        $value = $array[$args['key'][0]];
119
    }
120
    return $value;
121
  }
122
123
  /**
124
   * Capture suite's config before any execution
125
   *
126
   * @param \Codeception\Event\SuiteEvent $e
127
   * @return void
128
   *
129
   * @codeCoverageIgnore
130
   * @ignore Codeception specific
131
   */
132
  final public function beforeSuite(\Codeception\Event\SuiteEvent $e)
133
  {
134
    self::$suiteConfig = $e->getSettings();
135
  }
136
137
  /**
138
   * Parse scenario's step before execution
139
   *
140
   * @param \Codeception\Event\StepEvent $e
141
   * @return void
142
   */
143
  final public function beforeStep(\Codeception\Event\StepEvent $e)
144
  {
145
    $step = $e->getStep();
146
    // access to the protected property using reflection
147
    $refArgs = new ReflectionProperty(get_class($step), 'arguments');
148
    // change property accessibility to public
149
    $refArgs->setAccessible(true);
150
    // retrieve 'arguments' value
151
    $args = $refArgs->getValue($step);
152
    foreach ($args as $index => $arg) {
153
      if (is_string($arg)) {
154
      // case if arg is a string
155
      // e.g. I see "{{param}}"
156
        $args[$index] = $this->getValueFromParam($arg);
157
      } elseif (is_a($arg, '\Behat\Gherkin\Node\TableNode')) {
158
      // case if arg is a table
159
      // e.g. I see :
160
      //  | paramater |
161
      //  | {{param}} |
162
        $table = [];
163
        foreach ($arg->getRows() as $i => $row) {
164
          foreach ($row as $j => $cell) {
165
              $table[$i][$j] = $this->getValueFromParam($cell);
166
          }
167
        }
168
        $args[$index] = new TableNode($table);
169
      } elseif (is_array($arg)) {
170
        foreach ($arg as $k => $v) {
171
          if (is_string($v)) {
172
             $args[$index][$k] = $this->getValueFromParam($v);
173
          }
174
        }
175
      }
176
    }
177
    // set new arguments value
178
    $refArgs->setValue($step, $args);
179
  }
180
181
}
182