Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add async/await to unit tests; modify server rendering tests to use async/await. #9089

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"babel-jest": "^19.0.0",
"babel-plugin-check-es2015-constants": "^6.5.0",
"babel-plugin-syntax-trailing-function-commas": "^6.5.0",
"babel-plugin-transform-async-to-generator": "^6.22.0",
"babel-plugin-transform-class-properties": "^6.11.5",
"babel-plugin-transform-es2015-arrow-functions": "^6.5.2",
"babel-plugin-transform-es2015-block-scoped-functions": "^6.5.0",
Expand Down
9 changes: 8 additions & 1 deletion scripts/jest/preprocessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var pathToBabel = path.join(require.resolve('babel-core'), '..', 'package.json')
var pathToModuleMap = require.resolve('fbjs/module-map');
var pathToBabelPluginDevWithCode = require.resolve('../error-codes/dev-expression-with-codes');
var pathToBabelPluginModules = require.resolve('fbjs-scripts/babel-6/rewrite-modules');
var pathToBabelPluginAsyncToGenerator = require.resolve('babel-plugin-transform-async-to-generator');
var pathToBabelrc = path.join(__dirname, '..', '..', '.babelrc');
var pathToErrorCodes = require.resolve('../error-codes/codes.json');

Expand Down Expand Up @@ -54,11 +55,17 @@ module.exports = {
!filePath.match(/\/node_modules\//) &&
!filePath.match(/\/third_party\//)
) {
// for test files, we also apply the async-await transform, but we want to
// make sure we don't accidentally apply that transform to product code.
var isTestFile = !!filePath.match(/\/__tests__\//);
return babel.transform(
src,
Object.assign(
{filename: path.relative(process.cwd(), filePath)},
babelOptions
babelOptions,
isTestFile ? {
plugins: [pathToBabelPluginAsyncToGenerator].concat(babelOptions.plugins),
} : {}
)
).code;
}
Expand Down
105 changes: 54 additions & 51 deletions src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,70 +19,71 @@ let ReactDOMServer;
// Helper functions for rendering tests
// ====================================

// promisified version of ReactDOM.render()
function asyncReactDOMRender(reactElement, domElement) {
return new Promise(resolve =>
ReactDOM.render(reactElement, domElement, resolve));
}
// performs fn asynchronously and expects count errors logged to console.error.
// will fail the test if the count of errors logged is not equal to count.
function expectErrors(fn, count) {
async function expectErrors(fn, count) {
if (console.error.calls && console.error.calls.reset) {
console.error.calls.reset();
} else {
spyOn(console, 'error');
}

return fn().then((result) => {
if (console.error.calls.count() !== count) {
console.log(`We expected ${count} warning(s), but saw ${console.error.calls.count()} warning(s).`);
if (console.error.calls.count() > 0) {
console.log(`We saw these warnings:`);
for (var i = 0; i < console.error.calls.count(); i++) {
console.log(console.error.calls.argsFor(i)[0]);
}
const result = await fn();
if (console.error.calls.count() !== count && console.error.calls.count() !== 0) {
console.log(`We expected ${count} warning(s), but saw ${console.error.calls.count()} warning(s).`);
if (console.error.calls.count() > 0) {
console.log(`We saw these warnings:`);
for (var i = 0; i < console.error.calls.count(); i++) {
console.log(console.error.calls.argsFor(i)[0]);
}
}
expectDev(console.error.calls.count()).toBe(count);
return result;
});
}
expectDev(console.error.calls.count()).toBe(count);
return result;
}

// renders the reactElement into domElement, and expects a certain number of errors.
// returns a Promise that resolves when the render is complete.
function renderIntoDom(reactElement, domElement, errorCount = 0) {
return expectErrors(
() => new Promise((resolve) => {
async () => {
ExecutionEnvironment.canUseDOM = true;
ReactDOM.render(reactElement, domElement, () => {
ExecutionEnvironment.canUseDOM = false;
resolve(domElement.firstChild);
});
}),
await asyncReactDOMRender(reactElement, domElement);
ExecutionEnvironment.canUseDOM = false;
return domElement.firstChild;
},
errorCount
);
}

// Renders text using SSR and then stuffs it into a DOM node; returns the DOM
// element that corresponds with the reactElement.
// Does not render on client or perform client-side revival.
function serverRender(reactElement, errorCount = 0) {
return expectErrors(
async function serverRender(reactElement, errorCount = 0) {
const markup = await expectErrors(
() => Promise.resolve(ReactDOMServer.renderToString(reactElement)),
errorCount)
.then((markup) => {
var domElement = document.createElement('div');
domElement.innerHTML = markup;
return domElement.firstChild;
});
errorCount
);
var domElement = document.createElement('div');
domElement.innerHTML = markup;
return domElement.firstChild;
}

const clientCleanRender = (element, errorCount = 0) => {
const div = document.createElement('div');
return renderIntoDom(element, div, errorCount);
};

const clientRenderOnServerString = (element, errorCount = 0) => {
return serverRender(element, errorCount).then((markup) => {
var domElement = document.createElement('div');
domElement.innerHTML = markup;
return renderIntoDom(element, domElement, errorCount);
});
const clientRenderOnServerString = async (element, errorCount = 0) => {
const markup = await serverRender(element, errorCount);
var domElement = document.createElement('div');
domElement.innerHTML = markup;
return renderIntoDom(element, domElement, errorCount);
};

const clientRenderOnBadMarkup = (element, errorCount = 0) => {
Expand Down Expand Up @@ -143,24 +144,26 @@ describe('ReactDOMServerIntegration', () => {
});

describe('basic rendering', function() {
itRenders('a blank div', render =>
render(<div />).then(e => expect(e.tagName).toBe('DIV')));

itRenders('a div with inline styles', render =>
render(<div style={{color:'red', width:'30px'}} />).then(e => {
expect(e.style.color).toBe('red');
expect(e.style.width).toBe('30px');
})
);

itRenders('a self-closing tag', render =>
render(<br />).then(e => expect(e.tagName).toBe('BR')));

itRenders('a self-closing tag as a child', render =>
render(<div><br /></div>).then(e => {
expect(e.childNodes.length).toBe(1);
expect(e.firstChild.tagName).toBe('BR');
})
);
itRenders('a blank div', async render => {
const e = await render(<div />);
expect(e.tagName).toBe('DIV');
});

itRenders('a div with inline styles', async render => {
const e = await render(<div style={{color:'red', width:'30px'}} />);
expect(e.style.color).toBe('red');
expect(e.style.width).toBe('30px');
});

itRenders('a self-closing tag', async render => {
const e = await render(<br />);
expect(e.tagName).toBe('BR');
});

itRenders('a self-closing tag as a child', async render => {
const e = await render(<div><br /></div>);
expect(e.childNodes.length).toBe(1);
expect(e.firstChild.tagName).toBe('BR');
});
});
});