Completed
Push — master ( 527efe...c8c92c )
by Toni
02:56
created

MultilingualContext::parseLanguageCodes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 3
nc 1
nop 0
1
<?php
2
3
namespace kolev\MultilingualExtension\Context;
4
5
use Behat\Behat\Context\Context;
6
use Symfony\Component\Yaml\Yaml;
7
use Drupal\DrupalExtension\Context\DrupalContext;
8
use Behat\MinkExtension;
9
use Behat\Behat\Context\TranslatableContext;
10
use Behat\Mink\Element\Element;
11
use Behat\Gherkin\Node\TableNode;
12
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
13
use Behat\MinkExtension\Context\RawMinkContext;
14
15
/**
16
 * This is the file for Multilingual context for Drupal. The context is working based on specifications
17
 * in the profile. The user needs to provide the language of the site and the translation file.
18
 *
19
 * The user is free to provide either full "english" or shortened "en" prefix for new languages
20
 * but all translations should follow common pattern. For example if the site's language
21
 * is marked as "en" you should define your translations with "en".
22
 *
23
 * It is important to note that the English language is used as a base.
24
 *
25
 * This is very early version of the context so it probably has some bugs/issues and points to be improved.
26
 *
27
 * @author Toni Kolev <[email protected]>
28
 * @skype k-o-l-e-v
29
 * @github https://github.com/byKolev
30
 */
31
32
class MultilingualContext extends RawMultilingualContext {
33
34
    /** Multilanguage implementation */
35
36
    // Declaring translations variable to store all translations
37
    public $translations = array();
38
39
    public $languages_iso_codes = array();
40
41
42
    // Parse the YAML translations to PHP array variable
43
    public function parseTranslationFile() {
44
        $base_path = $this->getMinkParameter('files_path');
45
        $base_path = $base_path."/";
46
        $file_path = $base_path.$this->multilingual_parameters['translations'];
47
        $yaml = file_get_contents($file_path);
48
        $yaml_parse_array_check = Yaml::parse($yaml);
49
        if(is_array($yaml_parse_array_check)) {
50
            $this->translations = $yaml_parse_array_check;
51
        }
52
    }
53
54
    public function initializeMultilanguage() {
55
        if(isset($this->multilingual_parameters['translations'])) {
56
            $this->parseTranslationFile();
57
        }
58
        $this->parseLanguageCodes();
59
    }
60
61
    /*
62
     * This function parses the languages_iso_codes.json file to an array
63
     */
64
65
    public function parseLanguageCodes() {
66
        $languages_iso_codes_string = file_get_contents("vendor/kolev/multilingual-extension/src/Resources/languages_iso_codes.json");
67
        $this->languages_iso_codes = json_decode($languages_iso_codes_string, true);
0 ignored issues
show
Documentation Bug introduced by
It seems like json_decode($languages_iso_codes_string, true) of type * is incompatible with the declared type array of property $languages_iso_codes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
68
    }
69
70
    /*
71
     * This function detects site's language based on URL. If no URL language is detected
72
     * the default_language is used.
73
     */
74
75
    public function languageDetection() {
76
        $current_url = $this->getSession()->getCurrentUrl();
77
        $base_url = $this->getMinkParameter('base_url');
78
        $base_url_length = strlen($base_url);
79
        $clean_url_language_code = substr($current_url,$base_url_length,2);
80
        $not_clean_url_language_code = substr($current_url,$base_url_length+3,2);
81
82
        if(in_array($clean_url_language_code,$this->languages_iso_codes)) {
83
            return $clean_url_language_code;
84
        }
85
        else if (in_array($not_clean_url_language_code,$this->languages_iso_codes)){
86
            return $not_clean_url_language_code;
87
        }
88
        else return $this->multilingual_parameters['default_language'];
89
    }
90
91
    /**
92
     * This function localizes the targeted string. It tries to find a definition of the provided text (in English)
93
     * in the translations file that is provided within the profile parameters. If it fails to find translation
94
     * for the requested language it falls back to English. If the string is not defined at all in the translations
95
     * file there will be an exception thrown.
96
     */
97
98
    public function localizeTarget($target) {
99
        $translations = $this->multilingual_parameters['translations'];
100
        if(isset($this->translations[$target][$this->multilingual_parameters['default_language']])){
101
            $target = $this->translations[$target][$this->languageDetection()];
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $target. This often makes code more readable.
Loading history...
102
            return $target;
103
        }
104
        elseif (isset($this->translations[$target])) {
105
            return $target;
106
        }
107
        else throw new \Exception ("The text '$target'' is not defined in '$translations' translation file.");
108
    }
109
110
    /**
111
     * This function localizes the field based on Drupal standards. English language is used as a base.
112
     */
113
114
    public function localizeField($field) {
115
        $re = "/(?:[-])(".$this->multilingual_parameters['default_language'].")(?:[-])/";
116
        $language = "-".$this->languageDetection()."-";
117
        $field = preg_replace($re, $language,$field);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $field. This often makes code more readable.
Loading history...
118
        return $field;
119
    }
120
121
122
    /**
123
     * Initialize the multilingual context a.k.a parses the YAML file translations into an array.
124
     * @BeforeScenario
125
     * @Given /^I initialize multilingual context/
126
     */
127
    public function iInitializeMultilingualContext() {
128
        $this->initializeMultilanguage();
129
    }
130
131
    /**
132
     *
133
     * @Given /^I follow localized "(?P<link>(?:[^"]|\\")*)"/
134
     */
135
    public function iFollowLocalized($target) {
136
137
        $pos = strpos($target, '-en-');
138
139
        if ($pos === false) {
140
            $target = $this->localizeTarget($target);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $target. This often makes code more readable.
Loading history...
141
        } else {
142
            $target = $this->localizeField($target);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $target. This often makes code more readable.
Loading history...
143
        }
144
        
145
        $this->getSession()->getPage()->clickLink($target);
146
    }
147
148
    /**
149
     *
150
     * @Given /^I follow second localized "(?P<link>(?:[^"]|\\")*)"/
151
     */
152
    public function iFollowLocalizedSecond($target) {
153
        $target = $this->localizeTarget($target);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $target. This often makes code more readable.
Loading history...
154
        $this->getSession()->getPage()->clickLink($target);
155
    }
156
157
    /**
158
     * Click on some text.
159
     *
160
     * @When /^I click on the localized text "([^"]*)"$/
161
     */
162
163
    public function iClickOnTheLocalizedText($target) {
164
        $target = $this->localizeTarget($target);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $target. This often makes code more readable.
Loading history...
165
        $this->iClickOnTheText($target);
166
    }
167
168
    /**
169
     * Checks, that page contains specified text in input
170
     *
171
     * @Then /^(?:|I )should see localized value "(?P<text>(?:[^"]|\\")*)" in input "([^"]*)"$/
172
     */
173
174
    public function iShouldSeeLocalizedValueInInput($value, $input) {
175
        $value = $this->localizeTarget($value);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $value. This often makes code more readable.
Loading history...
176
        $this->assertValueInInput($value, $input);
177
    }
178
179
    /**
180
     * Checks, that page contains specified text
181
     *
182
     * @Then /^(?:|I )should see localized "(?P<text>(?:[^"]|\\")*)"$/
183
     */
184
185
    public function iShouldSeeLocalized($target) {
186
        $target = $this->localizeTarget($target);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $target. This often makes code more readable.
Loading history...
187
        $this->assertSession()->pageTextContains($target);
188
    }
189
190
    /**
191
     * Checks, that page doesn't contain specified text
192
     *
193
     * @Then /^(?:|I )should not see localized "(?P<text>(?:[^"]|\\")*)"$/
194
     */
195
    public function iShouldNotSeeLocalized($target)
196
    {
197
        $target = $this->localizeTarget($target);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $target. This often makes code more readable.
Loading history...
198
        $this->assertSession()->pageTextNotContains($target);
199
    }
200
201
    /**
202
     * Waiting for text to appear on a page with certain execution time
203
     *
204
     * @When /^I wait for localized text "([^"]*)" to appear with max time "([^"]+)"(?: seconds)?$/
205
     */
206
    public function iWaitForLocalizedTextToAppearWithMaxTime($target, $maxExecutionTime){
207
        $target = $this->localizeTarget($target);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $target. This often makes code more readable.
Loading history...
208
        $this->iWaitForTextToAppearWithMaxTime($target, $maxExecutionTime);
209
    }
210
211
    /**
212
     * Fills in form field with specified id|name|label|value
213
     * Example: When I fill in "username" with: "bwayne"
214
     * Example: And I fill in "bwayne" for "username"
215
     *
216
     * @When /^(?:|I )fill in localized "(?P<field>(?:[^"]|\\")*)" with "(?P<value>(?:[^"]|\\")*)"$/
217
     * @When /^(?:|I )fill in localized "(?P<field>(?:[^"]|\\")*)" with:$/
218
     * @When /^(?:|I )fill in localized "(?P<value>(?:[^"]|\\")*)" for "(?P<field>(?:[^"]|\\")*)"$/
219
     */
220
    public function fillLocalizedField($field, $value)
221
    {
222
        $field = $this->localizeField($field);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $field. This often makes code more readable.
Loading history...
223
        $this->getSession()->getPage()->fillField($field, $value);
224
    }
225
226
    /**
227
     * @Given I click localized :link in the :rowText row
228
     * @Then I (should )see the localized :link in the :rowText row
229
     */
230
    public function assertLocalizedClickInTableRow($link, $rowText){
231
        $link = $this->localizeTarget($link);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $link. This often makes code more readable.
Loading history...
232
        $page = $this->getSession()->getPage();
233
        if ($link_element = $this->getTableRow($page, $rowText)->findLink($link)) {
234
            // Click the link and return.
235
            $link_element->click();
236
            return;
237
        }
238
        throw new \Exception(sprintf('Found a row containing "%s", but no "%s" link on the page %s', $rowText, $link, $this->getSession()->getCurrentUrl()));
239
    }
240
241
    public function getTableRow(Element $element, $search) {
242
        $rows = $element->findAll('css', 'tr');
243
        if (empty($rows)) {
244
            throw new \Exception(sprintf('No rows found on the page %s', $this->getSession()->getCurrentUrl()));
245
        }
246
        foreach ($rows as $row) {
247
            if (strpos($row->getText(), $search) !== FALSE) {
248
                return $row;
249
            }
250
        }
251
        throw new \Exception(sprintf('Failed to find a row containing "%s" on the page %s', $search, $this->getSession()->getCurrentUrl()));
252
    }
253
254
    /**
255
     * Click on text in specified region
256
     *
257
     * @When /^I click on the localized text "([^"]*)" in the "(?P<region>[^"]*)"(?:| region)$/
258
     */
259
    public function iClickOnTheLocalizedTextInRegion($text, $region){
260
        $text = $this->localizeTarget($text);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $text. This often makes code more readable.
Loading history...
261
        $this->iClickOnTheTextInRegion($text, $region);
262
    }
263
264
    /**
265
     * Choose certain option from given selector
266
     *
267
     * @When I select localized :option from chosen :selector
268
     */
269
    public function lselectLocalizedOptionWithJavascript($selector, $option) {
270
        $localizedOption = $this->localizeTarget($option);
271
        $this->selectOptionWithJavascript($selector, $localizedOption);
272
    }
273
274
    /**
275
     * @When I select the localized radio button :label with the id :id
276
     * @When I select the localized radio button :label
277
     *
278
     */
279
    public function assertSelectLocalizedRadioById($label, $id = '') {
280
        $label = $this->localizeTarget($label);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $label. This often makes code more readable.
Loading history...
281
        $this->assertSelectRadioById($label, $id);
282
    }
283
}
284