Skip to content

Commit

Permalink
Merge pull request #158 from angrykoala/dev
Browse files Browse the repository at this point in the history
Wendigo 0.8.0
  • Loading branch information
angrykoala authored Jun 15, 2018
2 parents 901a53a + d5d9c30 commit cc1d92f
Show file tree
Hide file tree
Showing 13 changed files with 508 additions and 350 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
0.8.0 / 2018-06-15
==================

* ResponseBody request filter and assertion
* Regex support for expected value of assert.attribute
* Major refactor in request filter and request assertions

0.7.6 / 2018-06-13
==================

Expand Down
41 changes: 27 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ await browser.assert.text("#my-modal", "Button Clicked");

> **Warning:** Wendigo is under early stages of development and its interface may change
> Recommended node 8.11.3 lts
**Contents**
* [Api](#api)
* [Wendigo](#wendigo)
Expand Down Expand Up @@ -499,7 +501,7 @@ await browser.assert.elements("p.second", {atLeast: 1}); // Ok
```

**attribute(selector, attribute, expected?, msg?)**
Asserts that at least one element 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.
Asserts that at least one element 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. The expected value can be a string or regex

```js
await browser.assert.attribute(".hidden-class", "class", "hidden-class");
Expand Down Expand Up @@ -898,7 +900,7 @@ To filter the requests made by the browser, you can use `browser.request.filter`
For example, to filter requests with status code of 200:

```js
const filteredRequests = browser.requests.filter.status(200).requests;
const filteredRequests = await browser.requests.filter.status(200).requests;
```

The available filters are:
Expand All @@ -907,7 +909,7 @@ The available filters are:
Filters by the given url. The url can be a string or a regex.

```js
browser.requests.filter.url("http://localhost:8002/api").requests;
await browser.requests.filter.url("http://localhost:8002/api").requests;
```

**method(value)**
Expand All @@ -923,7 +925,7 @@ Filters whether the response comes from the browser cache or not.
Filters requests where the response has all the given headers with the given values. The expected value can be a string or regex.

```js
browser.requests.filter.responseHeaders({
await browser.requests.filter.responseHeaders({
'content-type': /html/,
})
```
Expand All @@ -936,18 +938,23 @@ Filters can be joined to perform a filter of several fields.

```js
//Filters all the POST requests made to any url with api that are not cached and returned a success code
browser.filter.url(/api/).method("POST").ok().fromCache(false).requests;
await browser.filter.url(/api/).method("POST").ok().fromCache(false).requests;
```

**postBody(expected)**
Filters requests by post body, the body can be a String, Object or regex.

```js
// Flters all DELETE requests made to with json body
browser.filter.url(/api/).method("DELETE").body({id: 5}).requests;
// Filters all DELETE requests made to with json body
await browser.filter.url(/api/).method("DELETE").body({id: 5}).requests;
```

Note that filtering requests don't require the use of `await`.
**responseBody(expected)**
Filters requests by response body, the body can be a String, Object or regex. This filter returns a promise, so either then or await is required. Also it cannot be concatenated directly.

```js
const byResponseFilter = await browser.requests.filter.url(/api/).responseBody({response: 'OK'}).requests;
```

> Keep in mind that some filters like status require the requests to be finished. Use `await browser.wait()` before filtering to make sure the requests was completed.
Expand All @@ -960,21 +967,21 @@ Like filters, request assertion don't need `await` and can be concatenated. All
Asserts that at least one request is made to the given url. The url can be a string or regex.

```js
browser.assert.request.url(/api/);
await browser.assert.request.url(/api/);
```

**method(expected, msg?)**
Asserts that at least one request was made with the given method (`GET`, `POST`, ...).

```js
browser.assert.request.method("GET");
await rowser.assert.request.method("GET");
```

**status(expected, msg?)**
Asserts that a response was received with the given status.

```js
browser.assert.request.status(200);
await browser.assert.request.status(200);
```

> Note that this method requires the request to be finished.
Expand All @@ -983,7 +990,7 @@ browser.assert.request.status(200);
Asserts that a response was received with the given headers. The expected variable is an object with one or more key values representing the expected headers. The value can be either a string or regex.

```js
browser.requests.assert.responseHeaders({
await browser.requests.assert.responseHeaders({
'content-type': /html/,
})
```
Expand All @@ -996,15 +1003,21 @@ Asserts that an successful response was received (status is between 200 and 299)
Asserts that a request contains the given post body (regardless of method). The expected value can be a string, regex or object.

```js
browser.assert.request.postBody({status: "OK"});
await browser.assert.request.postBody({status: "OK"});
```

**responseBody(expected, msg?)**
Asserts that a request response contains the given body. The expected value can be a string, regex or object.

```js
await browser.assert.request.responseBody({response: "OK"});
```

Concatenating multiple assertions is possible:

```js
// Asserts that a POST method was done to the api endpoint
browser.assert.request.method("POST").url("localhost:8000/api");
await browser.assert.request.method("POST").url("localhost:8000/api");
```

> Negative assertions are not supported for requests
Expand Down
10 changes: 9 additions & 1 deletion lib/modules/assertions/browser_assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@ const elementsAssertionUtils = require('./utils/assert_elements');
const assertUtils = require('./utils/assert_utils');
const ErrorFactory = require('../../errors/error_factory');
const BrowserModule = require('../browser_module');
const RequestAssertionsFilter = require('./request_assertions_filter');

module.exports = class BrowserAssertions extends BrowserModule {

get request() {
const requests = this._browser.requests.filter;
return new RequestAssertionsFilter((r) => {
r();
}, requests);
}

exists(selector, msg) {
if(!msg) msg = `Expected element "${selector}" to exists`;
return this._browser.query(selector).then((element) => {
Expand Down Expand Up @@ -141,7 +149,7 @@ module.exports = class BrowserAssertions extends BrowserModule {
}
for(const attr of attributes) {
if(attr !== null || expectedValue === null) {
if(expectedValue === undefined || expectedValue === attr) {
if(expectedValue === undefined || utils.matchText(attr, expectedValue)) {
return Promise.resolve();
}
}
Expand Down
10 changes: 0 additions & 10 deletions lib/modules/assertions/request_assertions.js

This file was deleted.

41 changes: 36 additions & 5 deletions lib/modules/assertions/request_assertions_filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,24 @@
const ErrorFactory = require('../../errors/error_factory');
const utils = require('../../utils');

module.exports = class RequestAssertionsFilter {
constructor(requestFilter) {
module.exports = class RequestAssertionsFilter extends Promise {
constructor(executor, requestFilter) {
super((resolve, reject) => {
return executor(resolve, reject);
});

this._requestFilter = requestFilter;
}


//
// then(onFulfilled, onRejected) {
// // before
// const returnValue = super.then(onFulfilled, onRejected);
// // after
// return returnValue;
// }

url(expected, msg) {
const urlFilter = this._requestFilter.url(expected);
if(!msg) msg = `Expected request with url "${expected}" to exist.`;
Expand Down Expand Up @@ -53,10 +66,28 @@ module.exports = class RequestAssertionsFilter {
return this._assertFilter(bodyFilter, msg);
}

responseBody(expected, msg) {
const responseBodyFilter = this._requestFilter.responseBody(expected);
if(!msg) {
const expectedString = utils.stringify(expected);
msg = `Expected request with response body "${expectedString}" to exist.`;
}
return this._assertFilter(responseBodyFilter, msg);
}

_assertFilter(filter, msg) {
if(filter.requests.length > 0) return new RequestAssertionsFilter(filter);
else throw ErrorFactory.generateAssertionError(msg);
return new RequestAssertionsFilter((resolve, reject) => {
return this.then(() => {
return filter.requests.then((reqs) => {
if(reqs.length > 0) resolve();
else {
const err = ErrorFactory.generateAssertionError(msg);
reject(err);
}
});
}).catch((err) => {
reject(err);
});
}, filter);
}

};
10 changes: 5 additions & 5 deletions lib/modules/browser_requests.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ module.exports = class BrowserRequests extends BrowserModule {
constructor(browser) {
super(browser);
this._requestMocker = new RequestMocker();
this._requests = new RequestFilter();
this._requests = [];
this._interceptorReady = false;
this.clearRequests();
}

get all() {
return this._requests._requests;
return this._requests;
}

get filter() {
return this._requests;
return new RequestFilter(Promise.resolve(this._requests));
}

mock(url, response, method) {
Expand All @@ -30,7 +30,7 @@ module.exports = class BrowserRequests extends BrowserModule {
}

clearRequests() {
this._requests._clear();
this._requests = [];
}

clearMocks() {
Expand All @@ -53,7 +53,7 @@ module.exports = class BrowserRequests extends BrowserModule {
this._interceptorReady = true;
return this._browser.page.setRequestInterception(true).then(() => {
return this._browser.page.on('request', request => {
this._requests._requests.push(request);
this._requests.push(request);
const mockedResponse = this._requestMocker.getMockedResponse(request);
if(mockedResponse) {
return request.respond(mockedResponse);
Expand Down
57 changes: 42 additions & 15 deletions lib/request_filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,20 @@
const utils = require('./utils');


function processBody(body) {
if(typeof body === 'object' && !(body instanceof RegExp)) {
return JSON.stringify(body);
} else return body;
}

function filterPromise(p, cb) {
return p.then((elements) => {
return elements.filter(cb);
});
}

module.exports = class RequestFilter {
constructor(requests = []) {
constructor(requests = Promise.resolve([])) {
this._requests = requests;
}

Expand All @@ -13,62 +25,69 @@ module.exports = class RequestFilter {
}

url(url) {
const requests = this._requests.filter(el => {
const requests = filterPromise(this._requests, el => {
return utils.matchText(el.url(), url);
});
return new RequestFilter(requests);
}

method(method) {
const requests = this._requests.filter(el => {
const requests = filterPromise(this._requests, el => {
return utils.matchText(el.method(), method);
});
return new RequestFilter(requests);
}

postBody(body) {
if(typeof body === 'object' && !(body instanceof RegExp)) {
body = JSON.stringify(body);
}
const requests = this._requests.filter(el => {
body = processBody(body);
const requests = filterPromise(this._requests, el => {
return utils.matchText(el.postData(), body);
});

return new RequestFilter(requests);
}

responseBody(body) { // This one returns a promise
body = processBody(body);
const requests = this._requests.then((requests) => {
return this._getResponsesBody(requests).then((reqBodyPairs) => {
return reqBodyPairs.filter(el => {
if(utils.matchText(el[1], body)) return el[0];
});
});
});

return new RequestFilter(requests);
}

status(status) {
const requests = this._requests.filter(el => {
const requests = filterPromise(this._requests, el => {
return el.response() !== null && el.response().status() === status;
});
return new RequestFilter(requests);
}

fromCache(isFromCache = true) {
const requests = this._requests.filter(el => {
const requests = filterPromise(this._requests, el => {
return el.response() !== null && el.response().fromCache() === isFromCache;
});
return new RequestFilter(requests);
}

responseHeaders(headers) {
const requests = this._requests.filter(el => {
const requests = filterPromise(this._requests, el => {
return this._responseHasHeader(el, headers);
});
return new RequestFilter(requests);
}

ok(isOk = true) {
const requests = this._requests.filter(el => {
const requests = filterPromise(this._requests, el => {
return el.response() !== null && el.response().ok() === isOk;
});
return new RequestFilter(requests);
}

_clear() {
this._requests = [];
}

_responseHasHeader(request, headers) {
if (request.response() === null) {
return false;
Expand All @@ -84,4 +103,12 @@ module.exports = class RequestFilter {
}
return true;
}

_getResponsesBody(requests) {
return Promise.all(requests.map((r) => {
return r.response().text().then((t) => {
return [r, t];
});
}));
}
};
Loading

0 comments on commit cc1d92f

Please sign in to comment.