Completed
Pull Request — master (#18)
by
unknown
48:00
created

EmailContext::clickLink()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 0
cts 10
cp 0
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 6
nc 5
nop 2
crap 30
1
<?php
2
/**
3
 * @author Sergii Bondarenko, <[email protected]>
4
 */
5
namespace Drupal\TqExtension\Context\Email;
6
7
// Helpers.
8
use Behat\DebugExtension\Message;
9
use Behat\Gherkin\Node\TableNode;
10
use WebDriver\Exception\NoSuchElement;
11
12
class EmailContext extends RawEmailContext
13
{
14
    const PARSE_STRING = '/^(.+?)$/i';
15
16
    private $originalMailSystem = [
17
        'default-system' => 'DefaultMailSystem',
18
    ];
19
20
    /**
21
     * @example
22
     * I check that email for "[email protected]" was sent
23
     *
24
     * @param string $to
25
     *   Recipient.
26
     *
27
     * @throws \RuntimeException
28
     *   When any message was not sent.
29
     *
30
     * @Given /^(?:|I )check that email for "([^"]*)" was sent$/
31
     */
32
    public function wasSent($to)
33
    {
34
        $this->getEmailMessages($to);
35
    }
36
37
    /**
38
     * @example
39
     * I check that email for "[email protected]" contains:
40
     *   | subject | New email letter   |
41
     *   | body    | The body of letter |
42
     * I also check that email contains:
43
     *   | from    | [email protected]  |
44
     *
45
     * @param string $to
46
     *   Recipient.
47
     * @param TableNode $values
48
     *   Left column - is a header key, right - value.
49
     *
50
     * @throws \RuntimeException
51
     *   When any message was not sent.
52
     * @throws \InvalidArgumentException
53
     * @throws \RuntimeException
54
     *
55
     * @Given /^(?:|I )check that email for "([^"]*)" contains:$/
56
     */
57
    public function contains($to, TableNode $values)
58
    {
59
        $rows = $values->getRowsHash();
60
61
        foreach ($this->getEmailMessages($to) as $message) {
62
            foreach ($rows as $field => $value) {
63
                if (empty($message[$field])) {
64
                    throw new \InvalidArgumentException(sprintf('Message does not contain "%s" header.', $field));
65
                }
66
67
                if (strpos($message[$field], $value) === false) {
68
                    throw new \RuntimeException(sprintf('Value of "%s" does not contain "%s".', $field, $value));
69
                }
70
            }
71
        }
72
    }
73
74
    /**
75
     * @param string $link
76
     *   Link text or value of "href" attribute.
77
     * @param string $to
78
     *   Try to find in specific email.
79
     *
80
     * @Given /^(?:|I )click on link "([^"]*)" in email(?:| that was sent on "([^"]*)")$/
81
     */
82
    public function clickLink($link, $to = '')
83
    {
84
        foreach ($this->getEmailMessages($to) as $message) {
85
            if (!isset($message['links'][$link]) && isset($message['links'])) {
86
                $link = array_search($link, $message['links']);
87
            }
88
89
            if (isset($message['links'][$link])) {
90
                $this->visitPath($message['links'][$link]);
91
            }
92
        }
93
    }
94
95
    /**
96
     * @param string $to
97
     *
98
     * @throws \Exception
99
     *   When parameter "parse_mail_callback" was not specified.
100
     * @throws \InvalidArgumentException
101
     *   When parameter "parse_mail_callback" is not callable.
102
     * @throws NoSuchElement
103
     *   When "Log in" button cannot be found on the page.
104
     * @throws \RuntimeException
105
     *   When credentials cannot be parsed or does not exist.
106
     *
107
     * @Given /^(?:|I )login with credentials that was sent on (?:"([^"]*)"|email)$/
108
     */
109
    public function loginWithCredentialsThatWasSentByEmail($to = '')
110
    {
111
        /**
112
         * Function must return an associative array with two keys: "username" and "password". The
113
         * value of each key should be a string with placeholder that will be replaced with user
114
         * login and password from an account. In testing, placeholders will be replaced by regular
115
         * expressions for parse the message that was sent.
116
         *
117
         * @example
118
         * @code
119
         * function mail_account_strings($name, $pass) {
120
         *     return array(
121
         *       'username' => t('Username: !mail', array('!mail' => $name)),
122
         *       'password' => t('Password: !pass', array('!pass' => $pass)),
123
         *     );
124
         * }
125
         *
126
         * // Drupal module.
127
         * function hook_mail($key, &$message, $params) {
128
         *     switch ($key) {
129
         *         case 'account':
130
         *             $message['subject'] = t('Website Account');
131
         *             $message['body'][] = t('You can login on the site using next credentials:');
132
         *             $message['body'] += mail_account_strings($params['mail'], $params['pass']);
133
         *         break;
134
         *     }
135
         * }
136
         *
137
         * // Behat usage.
138
         * mail_account_strings('(.+?)', '(.+?)');
139
         * @endcode
140
         *
141
         * @var callable $callback
142
         */
143
        $param = 'email_account_strings';
144
        $callback = $this->getTqParameter($param);
145
146
        if (empty($callback)) {
147
            throw new \Exception(sprintf('Parameter "%s" was not specified in "behat.yml"', $param));
148
        }
149
150
        if (!is_callable($callback)) {
151
            throw new \InvalidArgumentException(sprintf('The value of "%s" parameter is not callable.', $param));
152
        }
153
154
        $regexps = array_filter(call_user_func($callback, self::PARSE_STRING, self::PARSE_STRING));
155
156
        if (count($regexps) < 2) {
157
            throw new \RuntimeException(sprintf('Unfortunately you have wrong "%s" function.', $callback));
158
        }
159
160
        $userContext = $this->getUserContext();
161
162
        foreach ($this->getEmailMessages($to) as $message) {
163
            if (!empty($message['body'])) {
164
                $matches = [];
165
166
                // Process every line.
167
                foreach (explode("\n", $message['body']) as $string) {
168
                    foreach ($regexps as $name => $regexp) {
169
                        preg_match($regexp, $string, $match);
170
171
                        if (!empty($match[1])) {
172
                            $matches[$name] = $match[1];
173
                        }
174
                    }
175
                }
176
177
                if (!empty($matches['username']) && !empty($matches['password'])) {
178
                    $userContext->fillLoginForm($matches);
179
                    break;
180
                }
181
            }
182
        }
183
184
        if (!$userContext->isLoggedIn()) {
185
            throw new \RuntimeException(
186
                'Failed to login because email does not contain user credentials or they are was not parsed correctly.'
187
            );
188
        }
189
    }
190
191
    /**
192
     * @BeforeScenario @email&&@api&&~@imap
193
     */
194
    public function beforeScenarioEmailApi()
195
    {
196
        new Message('comment', 2, [
197
            "Sending messages will be tested by storing them in a database instead of sending.",
198
            "This is the good choice, because you testing the application, not web-server.\n",
199
        ]);
200
201
        // Store original mail system to restore it after scenario.
202
        $this->originalMailSystem = variable_get('mail_system', $this->originalMailSystem);
203
        $this->setDrupalVariables([
204
            // Set the mail system for testing. It will store an emails in
205
            // "drupal_test_email_collector" Drupal variable instead of sending.
206
            'mail_system' => ['default-system' => 'TestingMailSystem'],
207
        ]);
208
    }
209
210
    /**
211
     * @AfterScenario @email&&@api&&~@imap
212
     */
213
    public function afterScenarioEmailApi()
214
    {
215
        $this->setDrupalVariables([
216
            // Bring back the original mail system.
217
            'mail_system' => $this->originalMailSystem,
218
            // Flush the email buffer, allowing us to reuse this step
219
            // definition to clear existing mail.
220
            'drupal_test_email_collector' => [],
221
        ]);
222
    }
223
224
    /**
225
     * @BeforeScenario @email&&@imap
226
     */
227
    public function beforeScenarioEmailImap()
228
    {
229
        if (!extension_loaded('imap')) {
230
            throw new \Exception('PHP configured without IMAP extension.');
231
        }
232
233
        new Message('comment', 2, [
234
            "Sending messages will be tested via IMAP protocol. You'll need to know, that the message",
235
            "simply cannot be delivered due to incorrect server configuration or third-party service",
236
            "problems. Would be better if you'll test this functionality using the <info>@api</info>.\n",
237
        ]);
238
239
        // Restore original mail system.
240
        $this->afterScenarioEmailApi();
241
    }
242
243
    /**
244
     * @AfterScenario @email&&@imap
245
     */
246
    public function afterScenarioEmailImap()
247
    {
248
        $this->closeConnections();
249
    }
250
}
251