1 | # POM CukeTractor - Cucumber Protractor Runner with Setup for Page Object Model
|
2 |
|
3 | [![Build Status](https://travis-ci.org/canvaspixels/cucumber-protractor.svg?branch=master)](https://travis-ci.org/canvaspixels/cucumber-protractor)
|
4 |
|
5 | ![POM Cuke Tractor](https://raw.githubusercontent.com/canvaspixels/cucumber-protractor/master/pomCukeTractor.png)
|
6 |
|
7 | ## Create easy-to-read, functioning scenarios in seconds:
|
8 |
|
9 | ### Setup
|
10 |
|
11 | This assumes that you have an npm project. If you don't then make a new one with `npm init`. It also assumes you are on a Mac or Windows and have node 8+, npm 6+, and the latest version of Chrome installed.
|
12 |
|
13 | 1. Install the package: type `npm install cucumber-protractor` into your terminal
|
14 | 2. Copy: `node node_modules/cucumber-protractor/scripts/setup.js` into your terminal. This will create a `uiTests` folder with the sample in it, a sample config and add the `ct` script to your package.json. Note it's `node node_modules\cucumber-protractor\scripts\setup.js` on Windows.
|
15 | 3. Run the sample, type `npm run ct` into your terminal.
|
16 |
|
17 | ### Futher tips:
|
18 |
|
19 | 1. To improve organisation and scalability, easily compose Page Objects and Component Objects. Page Objects and Component Objects are composed of [Locators](https://www.protractortest.org/#/locators), custom methods, and other Component Objects. Components can compose Components which compose Components etc. The only difference between a Page Object and a Component Object is a Component Object does not have an URL. Use the [step definitions provided](https://github.com/canvaspixels/cucumber-protractor/blob/master/STEP_DEFINITIONS.md#step-definitions) (or create your own) to write your own first scenario.
|
20 | 2. If you're using source control such as git, add `uiTestResult` to your .gitignore file
|
21 | 3. As an improvement, to suppress deprecation warnings (if running node > 8) and also to type `cuketractor` or `ct` rather than typing `npm run ct` each time, you can add the following lines to your `~/.bash_profile` file:
|
22 |
|
23 | ```
|
24 | alias cuketractor="PATH=$(npm bin):$PATH NODE_OPTIONS=--no-deprecation cuketractor"
|
25 | alias ct="PATH=$(npm bin):$PATH NODE_OPTIONS=--no-deprecation cuketractor"
|
26 | ```
|
27 |
|
28 | This is the same command that was added to your package.json. This means you don't have to put npm run each time.
|
29 |
|
30 | ## Feature file by example
|
31 |
|
32 | ```gherkin
|
33 | @google-home
|
34 | Feature: Test feature
|
35 |
|
36 | @google-home-feeling-lucky
|
37 | Scenario: I am testing this out
|
38 | Given I am on the 'Google Home' page
|
39 | When I click 'I’m Feeling Lucky'
|
40 | Then I expect the url to contain 'google.com'
|
41 | ```
|
42 |
|
43 | All you need to be able to run the scenario above is a page object that looks like this inside a kebab-case yaml file e.g. `google-home.page`, placed in the `uiTests/pages` folder:
|
44 |
|
45 | ```yaml
|
46 | path: https://www.google.com/
|
47 |
|
48 | selectors:
|
49 | I’m Feeling Lucky: [value="I'm Feeling Lucky"]
|
50 | ```
|
51 |
|
52 | ...or if you want the boilerplate js code and need more flexibility place the following in `google-home.js` in the same folder (again kebab-case is important):
|
53 |
|
54 | ```js
|
55 | const createPage = require('cucumber-protractor/uiTestHelpers/createPage');
|
56 | const fileName = createPage.getFileName(__filename);
|
57 |
|
58 | module.exports = (world) => {
|
59 | const pagePath = 'https://www.google.com/';
|
60 | const locators = {
|
61 | 'I’m Feeling Lucky': by.css('[value="I\'m Feeling Lucky"]'),
|
62 | };
|
63 |
|
64 | return createPage(fileName, world, pagePath, locators);
|
65 | };
|
66 | ```
|
67 |
|
68 | Note that a `.page` file will take precedence over a `.js` page object file.
|
69 |
|
70 | You don't need to write any page object methods, nor step definitions. How easy is that!!?
|
71 |
|
72 | It's important that the page object name is kebab-case and lowercase. E.g. `about-us.js` or `about-something-else.js` or `google-home.js` as in the sample. `Given I am on the 'Google Home' page` sets the current page object and `Google Home` gets translated behind the scenes to `google-home.js` so make sure `Google Home` has the space in it.
|
73 |
|
74 | It’s advisable when writing your features to add a tag at the top of the Feature file and a tag to the beginning of each Scenario. A tag starts with a @. As a convention you can prefix each Scenario tag with whatever you've used at the top of the file (in this case @google-home). Try and keep them unique for your ease of use.
|
75 |
|
76 | Note you can add more than one tag to each scenario and you could tag them when a hook tag that you can hook into Before or After each scenario. [Read more about hooks](https://github.com/cucumber/cucumber-js/blob/master/docs/support_files/hooks.md) just add hooks to the existing ones in your conf.js file.
|
77 |
|
78 | ```gherkin
|
79 | @google-home
|
80 | Feature: Test feature
|
81 |
|
82 | @google-home-feeling-lucky
|
83 | Scenario: ...
|
84 | Given ...
|
85 | When ...
|
86 | Then ...
|
87 |
|
88 | @google-home-another-thing @some-special-hook-before-each-run
|
89 | Scenario: ...
|
90 | Given ...
|
91 | When ...
|
92 | Then ...
|
93 |
|
94 | @google-home-yet-another-thing @some-special-hook-before-each-run
|
95 | Scenario: ...
|
96 | Given ...
|
97 | When ...
|
98 | Then ...
|
99 | ```
|
100 |
|
101 | ## Running just one feature or one scenario
|
102 |
|
103 | Continuing on from the examples above...
|
104 |
|
105 | To run just one feature:
|
106 |
|
107 | ```console
|
108 | npm run ct --tags=@google-home
|
109 |
|
110 | OR SIMPLY JUST
|
111 |
|
112 | npm run ct @google-home
|
113 | ```
|
114 |
|
115 | To run just one scenario:
|
116 |
|
117 | ```console
|
118 | npm run ct --tags=@google-home-another-thing
|
119 |
|
120 | OR SIMPLY JUST
|
121 |
|
122 | npm run ct @google-home-another-thing
|
123 | ```
|
124 |
|
125 | To run a couple (comma separate):
|
126 |
|
127 | ```console
|
128 | npm run ct --tags=@google-home-feeling-lucky,@google-home-another-thing
|
129 |
|
130 | OR SIMPLY JUST
|
131 |
|
132 | npm run ct @google-home-feeling-lucky,@google-home-another-thing
|
133 | ```
|
134 |
|
135 | ## Viewing your test run
|
136 |
|
137 | If you get an error, you'll see a screenshot for each step error inside the `uiTestResult` folder and a link to each one from the console output in your terminal. However, if you want to view the browser running each step, you can put disableHeadless=true before the cuketractor command like this:
|
138 |
|
139 | ```console
|
140 | disableHeadless=true npm run ct
|
141 | ```
|
142 |
|
143 | That'll launch Chrome by default and you'll be able to see your tests run.
|
144 |
|
145 | ## conf.js file
|
146 |
|
147 | The conf file allows you to specify the following:
|
148 |
|
149 | * folder locations for the following
|
150 | - test results
|
151 | - page objects
|
152 | - component objects
|
153 | - feature files
|
154 | - hooks
|
155 | * step timeoutInSeconds - how long a step will wait to complete before it times out
|
156 | * baseUrl - the hostname will prefix the paths you set in your page objects
|
157 | * capabilities for different browsers, Chrome is the default. You can point to services like browserstack or saucelabs to test a matrix of platforms and browsers
|
158 |
|
159 | To point to a different configuration file:
|
160 |
|
161 | ```console
|
162 | npm run ct --confFile=staging.conf.js
|
163 | ```
|
164 |
|
165 | ## .page files and .component files
|
166 |
|
167 | ### .page file example
|
168 |
|
169 | Example of a page object located at `uiTests/pages/simple.page`:
|
170 |
|
171 | ```yaml
|
172 | path: /simple-page
|
173 |
|
174 | selectors:
|
175 | Go to home page by react router link: [data-test="go-to-home-link"]
|
176 | some other element selected by class: .my-class
|
177 | some other element selected by id: '#my-class'
|
178 |
|
179 | XPaths:
|
180 | main heading: //*[contains(@class, "main-heading")]
|
181 | another element: //*[contains(@class, "something-else")]
|
182 |
|
183 | components:
|
184 | - banner
|
185 | - footer
|
186 | ```
|
187 |
|
188 | A .page file is made up of:
|
189 |
|
190 | * path - the page's path, navigated to when you use the `Given I am on the 'simple' page` step
|
191 | * selectors - css selectors, the name on the left side of the : is what you put in your gherkin steps, the selector on the right references your html element. Note if you use id selectors (with a hash) you need to put it in quotes '' or the yaml file will think it's a comment
|
192 | * XPaths - same as selectors but using XPath selectors instead
|
193 | * components - a list of components which will be loaded in from the `uiTests/components` folder and composed into the page object (see .component file example of the footer component)
|
194 |
|
195 |
|
196 | ### .component file example
|
197 |
|
198 | Example of a component object located at `uiTests/components/footer.component`:
|
199 |
|
200 | ```yaml
|
201 | selectors:
|
202 | footer wrapper: .footer
|
203 |
|
204 | components:
|
205 | - footer-item1
|
206 | - footer-item2
|
207 | ```
|
208 |
|
209 | A .component file is made up of:
|
210 |
|
211 | * selectors - css selectors, the name on the left side of the : is what you put in your gherkin steps, the selector on the right references your html element. Note if you use id selectors (with a hash) you need to put it in quotes '' or the yaml file will think it's a comment
|
212 | * XPaths - same as selectors but using XPath selectors instead
|
213 | * components - a list of components which will be loaded in from the `uiTests/components` folder and composed into the component object
|
214 |
|
215 |
|
216 | ## Snippets
|
217 |
|
218 | Snippets are available for Sublime Text 3, Webstorm (live templates), VSCode and Atom. To add them to your editor do the following.
|
219 |
|
220 | For Sublime Text 3:
|
221 |
|
222 | ```console
|
223 | node node_modules/cucumber-protractor/scripts/generateSnippetsSublime.js --genFiles --justForIDE
|
224 | ```
|
225 |
|
226 | For Atom:
|
227 |
|
228 | ```console
|
229 | node node_modules/cucumber-protractor/scripts/generateSnippetsAtom.js --genFiles --justForIDE
|
230 | ```
|
231 |
|
232 | For VSCode:
|
233 |
|
234 | ```console
|
235 | node node_modules/cucumber-protractor/scripts/generateSnippetsVSCode.js --genFiles --justForIDE
|
236 | ```
|
237 |
|
238 | For Webstorm:
|
239 |
|
240 | ```console
|
241 | node node_modules/cucumber-protractor/scripts/generateSnippetsWebstorm.js --genFiles --justForIDE
|
242 | ```
|
243 |
|
244 | For IntelliJ:
|
245 |
|
246 | ```console
|
247 | node node_modules/cucumber-protractor/scripts/generateSnippetsIntelliJ.js --genFiles --justForIDE
|
248 | ```
|
249 |
|
250 | You may need to restart after running the commands for the JetBrains IDEs.
|
251 |
|
252 | ## Combining steps
|
253 |
|
254 | While the gherkin step examples in this repo are all single actions and assertions, you can easily combine a number of steps into one.
|
255 |
|
256 | For example in a scenario that has the following steps:
|
257 |
|
258 | ```gherkin
|
259 | Scenario: I expect to see items in the dashboard menu
|
260 | Given I am on the 'twitter login' page
|
261 | When I set 'username' to 'peoplesvote_uk'
|
262 | And I set 'password' to 'password~1'
|
263 | And I submit the 'login form'
|
264 | Then I expect to be on the 'dashboard' page
|
265 | When I click the 'dashboard menu'
|
266 | Then I expect the 'dashboard menu items' to be visible
|
267 | ```
|
268 |
|
269 | we could simplify it by combining them into one so that it is more obvious what we are intending to test:
|
270 |
|
271 | ```gherkin
|
272 | Scenario: I expect to see items in the dashboard menu
|
273 | Given I am logged in
|
274 | When I click the 'dashboard menu'
|
275 | Then I expect the 'dashboard menu items' to be visible
|
276 | ```
|
277 |
|
278 | This allows you to stop repeating yourself with the login steps, making them more reusable. Also it makes the test much more readable and focusses on the subject in test. It is a precondition that we need to be logged in and not really what we are testing. If you are in control of your mocks and are able to mock out a logged-in state, say with cookies, then that is preferable as it'll take a lot less time to run. But if you are doing system tests on a staging environment for example then you may have to login how a user would do, via the login form. Remember to keep your credentials out of your repository!
|
279 |
|
280 | To add a `Given I am logged in` step we’ll need to create our own custom step definition. Add this to the bottom of `uiTests/stepDefinitions/common-step-defintions.js` and change the username and password twitter credentials:
|
281 |
|
282 | ```js
|
283 | Given(/^I am logged in$/, async function() {
|
284 | await this.goToPage('twitter login');
|
285 | await this.setInputFieldValue('username', 'peoplesvote_uk');
|
286 | await this.setInputFieldValue('password', 'password~1');
|
287 | await this.submitForm('login form');
|
288 | await this.checkUrlIs('https://twitter.com');
|
289 | });
|
290 | ```
|
291 |
|
292 | Create yourself a new feature file, let's call it `twitter-dashboard-menu.feature` and save it in the `uiTests/features` folder. Paste the following into it:
|
293 |
|
294 | ```gherkin
|
295 | @twitter-dashboard-menu
|
296 | Feature: Test feature
|
297 |
|
298 | @twitter-dashboard-menu-items
|
299 | Scenario: I expect to see items in the dashboard menu
|
300 | Given I am logged in
|
301 | ```
|
302 |
|
303 | Then run that scenario: `npm run ct @twitter-dashboard-menu-items`
|
304 |
|
305 | Have a look at the [available methods](https://github.com/canvaspixels/cucumber-protractor/blob/master/METHODS_FOR_COMBINING.md#methods-for-combining-actions-and-assertions) that you can use to combine your steps.
|
306 |
|
307 | ## Contributing
|
308 |
|
309 | Please get in touch if you'd like to contribute to this project.
|
310 |
|
311 | To create the STEP_DEFINITIONS.md and snippets files, run the script: `npm run build-readme-and-snippets` |
\ | No newline at end of file |