Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Mikael Brandt committed May 12, 2016
0 parents commit 1969f65
Show file tree
Hide file tree
Showing 8 changed files with 542 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["es2015", "stage-2"]
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.idea
21 changes: 21 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "acast-test-helpers",
"version": "1.0.0-rc.1",
"description": "",
"main": "dist/index.js",
"scripts": {
"preinstall": "babel src --out-dir dist",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"babel-cli": "6.8.0",
"babel-preset-es2015": "6.6.0",
"babel-preset-stage-2": "6.5.0"
},
"dependencies": {
"jquery": "2.2.3"
}
}
56 changes: 56 additions & 0 deletions src/acceptance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import $ from 'jquery';
import { useRouterHistory } from 'react-router';
import { createMemoryHistory } from 'history';
import { unmountComponentAtNode } from 'react-dom';
import { setupAsync, andThen, waitUntil } from './async';

let history;
let root;

function setupApp(renderAppWithHistoryIntoElement) {
history = useRouterHistory(createMemoryHistory)({ queryKey: false });

root = document.createElement('div');
document.body.appendChild(root);

renderAppWithHistoryIntoElement(history, root);
}

function teardownApp() {
unmountComponentAtNode(root);
document.body.removeChild(root);
}

export function setupAndTeardownApp(renderAppWithHistoryIntoElement) {
setupAsync();

beforeEach('setup app', () => setupApp(renderAppWithHistoryIntoElement));

afterEach('teardown app', teardownApp);
}

export function visit(route) {
andThen(() => {
history.push(route);
});
}

export function click(selector) {
andThen(() => {
const jqueryElement = $(selector);
expect(jqueryElement.length).to.equal(1, `Cannot click selector '${selector}'`);
const rawElementToClick = jqueryElement.get(0);
const clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent('click', true /* bubble */, true /* cancelable */);
rawElementToClick.dispatchEvent(clickEvent);
});
}

export function waitUntilExists(selector, pollInterval = 100) {
waitUntil(() => {
const selected = $(selector);
return selected.length ? selected : false;
}, pollInterval);
}

export const find = $;
40 changes: 40 additions & 0 deletions src/async.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
let testPromise = null;

function setupAsync() {
beforeEach('create test promise', () => {
testPromise = Promise.resolve();
});

afterEach('check test promise for errors', () => {
let previousTestPromise = testPromise;
testPromise = null;
return previousTestPromise;
});
}

function andThen(doThis) {
if (!testPromise) {
throw new Error('You cannot use andThen() unless you call setupAsync() at the root of the appropriate describe()!');
}

testPromise = testPromise.then(doThis);
}

function resolveWhenPredicateReturnsTruthy(predicate, resolve, pollInterval) {
const returnValue = predicate();
if (!!returnValue) {
resolve(returnValue);
} else {
setTimeout(() => {
resolveWhenPredicateReturnsTruthy(predicate, resolve, pollInterval);
}, pollInterval);
}
}

function waitUntil(thisReturnsTruthy, pollInterval = 100) {
andThen(() => new Promise((resolve) => {
resolveWhenPredicateReturnsTruthy(thisReturnsTruthy, resolve, pollInterval);
}));
}

export { setupAsync, andThen, waitUntil };
93 changes: 93 additions & 0 deletions src/fetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
let originalFetch;
let pathToPromisesMap;

function createFakeFetch() {
return sinon.spy((path) => {
let resolve;
let reject;

const promise = new Promise((promiseResolve, promiseReject) => {
resolve = promiseResolve;
reject = promiseReject;
});

const promises = pathToPromisesMap[path] || [];

promises.push({ resolve, reject, promise });

pathToPromisesMap[path] = promises;
return promise;
});
}

function pathIsAwaitingResolution(path) {
return path in pathToPromisesMap && pathToPromisesMap[path].length;
}

function pathIsNotAwaitingResolution(path) {
return !pathIsAwaitingResolution(path);
}

function getFormattedPathsAwaitingResolution() {
let result = [];
for (let key in pathToPromisesMap) {
pathToPromisesMap[key].forEach(() => {
result.push(`'${key}'`);
});
}
return result.join(', ');
}

function notSetUp() {
return !pathToPromisesMap;
}

function throwIfNotSetUp() {
if (notSetUp()) {
throw new Error(
'fetchRespond has to be called after setupFakeFetch() and before teardownFakeFetch()'
);
}
}

function throwIfPathIsNotAwaitingResolution(path) {
if (pathIsNotAwaitingResolution(path)) {
throw new Error(
`Could not find '${path}' among the fetched paths: [${getFormattedPathsAwaitingResolution()}]`
);
}
}

export function setupFakeFetch() {
pathToPromisesMap = {};
originalFetch = window.fetch;

window.fetch = createFakeFetch();
}

export function teardownFakeFetch() {
window.fetch = originalFetch;
pathToPromisesMap = null;
}


export function fetchRespond(path) {
throwIfNotSetUp();
throwIfPathIsNotAwaitingResolution(path);

const { resolve, reject, promise } = pathToPromisesMap[path].shift();

return {
resolveWith: (returnValue) => {
resolve({
json() {
return returnValue;
},
});
return promise.then().then();
},
rejectWith: (error) => {
reject(error);
},
};
}
9 changes: 9 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as acceptance from './acceptance';
import * as async from './async';
import * as fetch from './fetch';

module.exports = {
...acceptance,
...async,
...fetch
};
Loading

0 comments on commit 1969f65

Please sign in to comment.