Skip to content

Commit

Permalink
Merge pull request #64 from angrykoala/dev
Browse files Browse the repository at this point in the history
0.4.0
  • Loading branch information
angrykoala authored Feb 21, 2018
2 parents 3291ed3 + 31bb793 commit 6591f56
Show file tree
Hide file tree
Showing 24 changed files with 711 additions and 358 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
0.4.0 / 2018-02-21
==================

* Browser style and not style assertions
* browser.styles will return the computed css styles
* Type will trigger the keyboard events
* Type behaviour changed
* Readme improvements

0.3.2 / 2018-02-16
==================

Expand Down
82 changes: 55 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,20 @@ Will create and return a [Browser](#Browser) instance. It will automatically lau

> **Warning:** the settings will only take effect the first time a browser page is created, to fully restart the settings you must close the browser connection using `Wendigo.stop()` before executing createBrowser again
Example:
Examples:
```js
const Wendigo=require('wendigo');
const browser=Wendigo.createBrowser(); // Using default options
```

```js
const Wendigo=require('wendigo');
const browser=Wendigo.createBrowser({
headless: false,
slowMo: 500
}); // Using options to see what's happening
```

**static stop()**
Will stop and disconnect all the browsers. It should be called after finishing all the tests.

Expand Down Expand Up @@ -126,7 +134,7 @@ const elements = await browser.queryXPath('//p[contains(text(),"My first paragra
elements[0].textContent; // "My first paragraph"
```

**class(selector)**
**class(selector)**
Returns and array with the classes of the first element returned from the given css selector.

```js
Expand All @@ -139,22 +147,29 @@ const node=await browser.query("div.container.main");
const classes=await browser.class(node); // Returns ["container", "main", "another-class"]
```

**value(selector)**
**value(selector)**
Returns the value of the first element with given selector. Returns _null_ if no element or value found.

```js
const value = await browser.value("input.my-input");
```

**attribute(selector, attributeName)**
**attribute(selector, attributeName)**
Return the attribute value of the first element found with given selector. Throws if no element is found. Returns `""` if the attribute is set but no value is given and `null` if the attribute doesn't exists.

```js
const classAttribute = await browser.attribute(".my-element", "class"); // Returns "my-element another-class"

const hiddentAttr = await browser.attribute(".my-hidden-element", "hidden"); // Returns ""
const hiddentAttr2 = await browser.attribute(".not-hidden-element", "hidden"); // Returns null
```

**styles(selector)**
Returns an object with all the computed css styles of the first element matching the given selector.

```js
const styles=await browser.styles("h1.my-title");
styles.color; // 'rgb(255, 0, 0)'
```

**text(selector)**
Expand Down Expand Up @@ -201,27 +216,27 @@ await browser.waitFor(".popup");
Returns an array with the elements with text content matching the given text.

```js
const elements=await browser.findByText("My First Paragraph");
const elements = await browser.findByText("My First Paragraph");
elements.length; // 1
```

**findByTextContaining(text)**
Returns an array with all the elements with a text that contains the given text.

```js
const elements=await browser.findByTextContaining("Paragraph");
const elements = await browser.findByTextContaining("Paragraph");
elements.length; // 2
```

**type(selector, text)**
Types given text (as element value) in all the elements (input) with given selector. If a value is already present, appends the new text at the end.
**type(selector, text)**
Types given text in the first element matching given selector. If a value is already present, writes the new value at the beginning.


```js
await browser.type("input.my-input", "My Input");
```

**clearValue(selector)**
**clearValue(selector)**
Clears any value that exists in any of the elements matched by the given selector. Setting the value to "".

```js
Expand Down Expand Up @@ -272,18 +287,18 @@ await browser.assert.class("div.container.main-div", "container");
**url(expected, msg)**
Asserts that the current url matches the given string.

**value(selector, expected, msg)**
**value(selector, expected, msg)**
Asserts that the first element matching the selector has the expected value.

```js
await browser.type("input.my-input", "Dont Panic");
await browser.assert.value("input.my-input", "Dont Panic");
```

**element(selector, msg)**
**element(selector, msg)**
Asserts that exactly one element matches given selector. Same as `elements(selector, 1)`.

**elements(selector, count, msg)**
**elements(selector, count, msg)**
Asserts the number of element that matches given selector.

The count parameter can be a number of the exact number of elements expected or an object with the following properties:
Expand All @@ -297,30 +312,37 @@ The count parameter can be a number of the exact number of elements expected or
```

```js
browser.assert.elements("p", 2); // Ok
browser.assert.elements("p", {equal: 2}); // Ok
browser.assert.elements("p", {atLeast: 1, atMost:3}); // Ok
browser.assert.elements("p.first", 0); //Ok
await browser.assert.elements("p", 2); // Ok
await browser.assert.elements("p", {equal: 2}); // Ok
await browser.assert.elements("p", {atLeast: 1, atMost:3}); // Ok
await browser.assert.elements("p.first", 0); //Ok

browser.assert.elements("p.second", 2); // Fails
browser.assert.elements("p.second", {atLeast: 1}); // Ok
await browser.assert.elements("p.second", 2); // Fails
await browser.assert.elements("p.second", {atLeast: 1}); // Ok
```

**attribute(selector, attribute, expected, msg)**
**attribute(selector, attribute, expected, msg)**
Asserts that the first element matching the given selector contains an attribute matching the expected value. If no expected value is given, any not null value for the attribute will pass.

```js
browser.assert.attribute(".hidden-class", "class", "hidden-class");
browser.assert.attribute(".hidden-class", "hidden");
await browser.assert.attribute(".hidden-class", "class", "hidden-class");
await browser.assert.attribute(".hidden-class", "hidden");
```

To pass a custom message without specifying an expected value, you can pass null:
```js
browser.assert.attribute(".hidden-class", "hidden", null, "hidden-class doesn't have attribute hidden");
await browser.assert.attribute(".hidden-class", "hidden", null, "hidden-class doesn't have attribute hidden");
```

If the element doesn't exists, the assertion will fail.

**style(selector, style, expected, msg)**
Asserts that the first element matching the given selector has an style with the expected value. The assertion will throw an error if no element is found.

```js
await browser.assert.style("h1", "color", "rgb(0, 0, 0)");
```

### Negative assertions
Most of the browser assertions have a negative version that can be used with `browser.assert.not`. Most of the behaviours of the "not" assertions are simply the inverse of the positive version.

Expand All @@ -347,23 +369,26 @@ Asserts that the title of the page is not the expected string.
**not.url(expected, msgs)**
Asserts that the url of the page doesn't match the expected string.

**not.value(selector, expected, msg)**
**not.value(selector, expected, msg)**
Asserts that the first element with the given selector doesn't have the expected value.

**not.attribute(selector, attribute, expected, msg)**
**not.attribute(selector, attribute, expected, msg)**
Asserts that the first element matching the given selector doesn't contain an attribute with the expected value. If no expected value is given, any not null value on the attribute will fail.

```js
browser.assert.not.attribute(".not-hidden-class", "class", "hidden-class");
browser.assert.not.attribute(".not-hidden-class", "hidden");
await browser.assert.not.attribute(".not-hidden-class", "class", "hidden-class");
await browser.assert.not.attribute(".not-hidden-class", "hidden");
```

To pass a custom message without specifying an expected value, you can pass null:
```js
browser.assert.not.attribute(".hidden-class", "href", null, "hidden-class has attribute href");
await browser.assert.not.attribute(".hidden-class", "href", null, "hidden-class has attribute href");
```
If the element doesn't exists, the assertion will fail.

**not.style(selector, style, expected, msg)**
Asserts the first element matching the selector doesn't has a style with given value.

## Examples

**Testing a simple page with Mocha and Wendigo**
Expand Down Expand Up @@ -446,6 +471,9 @@ test:
script:
- npm test
```
_Example of .gitlab-ci.yml_

> Remember to check [Puppeteer Troubleshooting](https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md)

## Acknowledgements

Expand Down
20 changes: 16 additions & 4 deletions injection_scripts/wendigo_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
window.WendigoUtils = {
isVisible(element) {
if(!element) return false;
let style = window.getComputedStyle(element, "");
const style = window.getComputedStyle(element);
if (style.display === 'none') return false;
if (style.visibility === 'hidden') return false;
else return true;
Expand All @@ -16,9 +16,12 @@ window.WendigoUtils = {
queryAll(selector) {
if(typeof selector === 'string') {
return document.querySelectorAll(selector);
} else if(!Array.isArray(selector)) {
return [selector];
} else return selector;
} else{
if(!Array.isArray(selector)) {
selector = [selector];
}
return selector;
}
},
xPathQuery: function(xPath) {
let xPathResult = document.evaluate(xPath, document, null, XPathResult.ANY_TYPE, null);
Expand All @@ -29,5 +32,14 @@ window.WendigoUtils = {
r = xPathResult.iterateNext();
}
return result;
},
getStyles(element) {
const rawStyles = getComputedStyle(element);
const result = {};
for(let i = 0;i < rawStyles.length;i++) {
const name = rawStyles[i];
result[name] = rawStyles.getPropertyValue(name);
}
return result;
}
};
23 changes: 21 additions & 2 deletions lib/browser_assertions.js → lib/assertions/browser_assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

const assert = require('assert');
const BrowserNotAssertions = require('./browser_not_assertions');
const elementsAssertionUtils = require('./assertions_utils/assert_elements');
const utils = require('./utils');
const elementsAssertionUtils = require('./utils/assert_elements');
const utils = require('../utils');

module.exports = class BrowserAssertions {

Expand Down Expand Up @@ -126,4 +126,23 @@ module.exports = class BrowserAssertions {
return utils.rejectAssertion(msg);
});
}

style(selector, style, expected, msg) {
return this._browser.page.evaluate((selector, style) => {
const element = WendigoUtils.queryElement(selector);
if(!element) return Promise.reject();
const styles = getComputedStyle(element);
return styles.getPropertyValue(style);
}, selector, style).catch(() => {
return Promise.reject(new Error(`Element "${selector}" not found when trying to assert style.`));
}).then((value) => {
if(!msg) {
msg = `Expected element "${selector}" to have style "${style}" with value "${expected}"`;
if(value) msg = `${msg}, "${value}" found.`;
else msg = `${msg}, style not found.`;
}
assert.strictEqual(value, expected, msg);
});

}
};
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
"use strict";

const assert = require('assert');
const utils = require('./utils');
const utils = require('../utils');

function invertify(cb, msg) {
return cb().then(() => {
return Promise.reject(new assert.AssertionError({message: msg}));
}, () => {
return Promise.resolve();
}, (err) => {
if(err instanceof assert.AssertionError) return Promise.resolve();
else return Promise.reject(err);
});
}

Expand Down Expand Up @@ -88,4 +89,12 @@ module.exports = class BrowserAssertions {
return utils.rejectAssertion(msg);
});
}

style(selector, style, expected, msg) {
if(!msg) msg = `Expected element "${selector}" not to have style "${style}" with value "${expected}".`;
return invertify(() => {
return this._assertions.style(selector, style, expected);
}, msg);

}
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use strict";
const utils = require('../utils');
const utils = require('../../utils');
const assert = require('assert');

const countCases = {
Expand Down
16 changes: 7 additions & 9 deletions lib/browser.js → lib/browser/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"use strict";

const path = require('path');
const BrowserAssertions = require('./browser_assertions');
const BrowserAssertions = require('../assertions/browser_assertions');
const BrowserBase = require('./browser_base');

module.exports = class Browser extends BrowserBase {
Expand All @@ -19,7 +19,7 @@ module.exports = class Browser extends BrowserBase {
return this.page.goto(url).then(() => {
return this.page.content().then((content) => {
this._originalHtml = content;
return this.page.addScriptTag({path: path.join(__dirname, "..", "injection_scripts/wendigo_utils.js")});
return this.page.addScriptTag({path: path.join(__dirname, "../..", "injection_scripts/wendigo_utils.js")});
});
});
}
Expand Down Expand Up @@ -102,13 +102,11 @@ module.exports = class Browser extends BrowserBase {
}

type(selector, text) {
return this.page.evaluate((q, text) => {
const elements = WendigoUtils.queryAll(q);
for(const element of elements) {
const currentText = element.value;
element.value = currentText + text;
}
}, selector, text);
if(typeof selector === "string") {
return this.page.type(selector, text);
}else {
return selector.type(text);
}
}

clearValue(selector) {
Expand Down
12 changes: 11 additions & 1 deletion lib/browser_base.js → lib/browser/browser_base.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,18 @@ module.exports = class BrowserBase {
const element = WendigoUtils.queryElement(q);
if(!element) return Promise.reject();
return element.getAttribute(attributeName);
}, selector, attributeName).catch(() => {
}, selector, attributeName).catch(() => { // To avoid Error: Evaluation Failed
return Promise.reject(new Error(`Element "${selector}" not found when trying to get attribute "${attributeName}".`));
});
}

styles(selector) {
return this.page.evaluate((selector) => {
const element = WendigoUtils.queryElement(selector);
if(!element) return Promise.reject();
return WendigoUtils.getStyles(element);
}, selector).catch(() => { // To avoid Error: Evaluation Failed
return Promise.reject(new Error(`Element "${selector}" not found when trying to get styles.`));
});
}
};
2 changes: 1 addition & 1 deletion lib/wendigo.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use strict";

const process = require('process');
const Browser = require('./browser');
const Browser = require('./browser/browser');
const puppeteer = require('puppeteer');


Expand Down
Loading

0 comments on commit 6591f56

Please sign in to comment.