nx/packages/react/src/utils/ast-utils.spec.ts
Jonathan Cammisuli d9aef75bd5
chore(react): move react schematics to generators (#4745)
* chore(react): move react schematics to generators

* chore(react): update lib generators

* chore(react): update redux generators

* chore(react): move react story book generators

* chore(react): add old implementation for update babel in next

* chore(react): rename tsconfig json template files to include __tmpl__

* chore(react): update deps

* chore(react): fix component template file

* chore(react): remove angular-devkit deps

* chore(react): remove angular-devkit deps
2021-02-10 21:30:55 -05:00

476 lines
11 KiB
TypeScript

import * as utils from './ast-utils';
import * as ts from 'typescript';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { applyChangesToString, Tree } from '@nrwl/devkit';
describe('findDefaultExport', () => {
it('should find exported variable', () => {
const sourceCode = `
const main = () => {};
export default main;
`;
const source = ts.createSourceFile(
'test.ts',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const result = utils.findDefaultExport(source) as any;
expect(result).toBeDefined();
expect(result.name.text).toEqual('main');
});
it('should find exported function', () => {
const sourceCode = `
function main() {}
export default main;
`;
const source = ts.createSourceFile(
'test.ts',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const result = utils.findDefaultExport(source) as any;
expect(result).toBeDefined();
expect(result.name.text).toEqual('main');
});
it('should find default export function', () => {
const sourceCode = `
export default function main() {};
`;
const source = ts.createSourceFile(
'test.ts',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const result = utils.findDefaultExport(source) as any;
expect(result).toBeDefined();
expect(result.name.text).toEqual('main');
});
it('should find default class export', () => {
const sourceCode = `
export default class Main {};
`;
const source = ts.createSourceFile(
'test.ts',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const result = utils.findDefaultExport(source) as any;
expect(result).toBeDefined();
expect(result.name.text).toEqual('Main');
});
it('should find exported class', () => {
const sourceCode = `
class Main {};
export default Main;
`;
const source = ts.createSourceFile(
'test.ts',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const result = utils.findDefaultExport(source) as any;
expect(result).toBeDefined();
expect(result.name.text).toEqual('Main');
});
});
describe('addRoute', () => {
let tree: Tree;
let context: any;
beforeEach(() => {
context = {
warn: jest.fn(),
};
tree = createTreeWithEmptyWorkspace();
});
it('should add links and routes if they are not present', async () => {
const sourceCode = `
import React from 'react';
const App = () => (
<>
<h1>Hello</h1>
</>
);
export default App;
`;
tree.write('/app.tsx', sourceCode);
const source = ts.createSourceFile(
'/app.tsx',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const result = applyChangesToString(
sourceCode,
utils.addInitialRoutes('/app.tsx', source)
);
expect(result).toMatch(/role="navigation"/);
expect(result).toMatch(/<Link\s+to="\/page-2"/);
expect(result).toMatch(/<Route\s+path="\/page-2"/);
});
it('should update existing routes', async () => {
const sourceCode = `
import React from 'react';
import { Home } from '@example/home';
const App = () => (
<>
<header>
<h1>Hello</h1>
<div role="navigation">
<ul>
<li><Link to="/">Home</Link></li>
</ul>
</div>
</header>
<p>Hello World!</p>
<Route path="/" component={Home}/>
</>
);
export default App;
`;
tree.write('/app.tsx', sourceCode);
const source = ts.createSourceFile(
'/app.tsx',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const result = applyChangesToString(
sourceCode,
utils.addRoute('/app.tsx', source, {
routePath: '/about',
componentName: 'About',
moduleName: '@example/about',
})
);
expect(result).toMatch(/<li><Link\s+to="\/about"/);
expect(result).toMatch(/<Route\s+path="\/about"\s+component={About}/);
});
});
describe('addBrowserRouter', () => {
let tree: Tree;
let context: any;
beforeEach(() => {
context = {
warn: jest.fn(),
};
tree = createTreeWithEmptyWorkspace();
});
it('should wrap around App component', () => {
const sourceCode = `
import React from 'react';
import ReactDOM from 'react-dom';
import { App } from '@example/my-app';
ReactDOM.render(<App/>, document.getElementById('root'));
`;
tree.write('/main.tsx', sourceCode);
const source = ts.createSourceFile(
'/main.tsx',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const result = applyChangesToString(
sourceCode,
utils.addBrowserRouter('/main.tsx', source)
);
expect(result).toContain('<BrowserRouter><App/></BrowserRouter>');
});
});
describe('findMainRenderStatement', () => {
let tree: Tree;
let context: any;
beforeEach(() => {
context = {
warn: jest.fn(),
};
tree = createTreeWithEmptyWorkspace();
});
it('should return render(...)', () => {
const sourceCode = `
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(<div/>, document.getElementById('root'));
`;
tree.write('/main.tsx', sourceCode);
const source = ts.createSourceFile(
'/main.tsx',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const node = utils.findMainRenderStatement(source);
expect(node).toBeTruthy();
});
it('should return render(...)', () => {
const sourceCode = `
import React from 'react';
import { render } from 'react-dom';
render(<div/>, document.getElementById('root'));
`;
tree.write('/main.tsx', sourceCode);
const source = ts.createSourceFile(
'/main.tsx',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const node = utils.findMainRenderStatement(source);
expect(node).toBeTruthy();
});
it('should return null when not found', () => {
const sourceCode = ``;
tree.write('/main.tsx', sourceCode);
const source = ts.createSourceFile(
'/main.tsx',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const node = utils.findMainRenderStatement(source);
expect(node).toBe(null);
});
});
describe('addReduxStoreToMain', () => {
let tree: Tree;
let context: any;
beforeEach(() => {
context = {
warn: jest.fn(),
};
tree = createTreeWithEmptyWorkspace();
});
it('should wrap around App component', () => {
const sourceCode = `
import React from 'react';
import ReactDOM from 'react-dom';
import { App } from '@example/my-app';
ReactDOM.render(<App/>, document.getElementById('root'));
`;
tree.write('/main.tsx', sourceCode);
const source = ts.createSourceFile(
'/main.tsx',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const result = applyChangesToString(
sourceCode,
utils.addReduxStoreToMain('/main.tsx', source)
);
expect(result).toContain('@reduxjs/toolkit');
expect(result).toContain('const store = configureStore');
expect(result).toContain('<Provider store={store}>');
});
});
describe('updateReduxStore', () => {
let tree: Tree;
let context: any;
beforeEach(() => {
context = {
warn: jest.fn(),
};
tree = createTreeWithEmptyWorkspace();
});
it('should update configureStore call', () => {
const sourceCode = `
import { configureStore } from '@reduxjs/toolkit';
const store = configureStore({
reducer: {}
});
`;
tree.write('/main.tsx', sourceCode);
const source = ts.createSourceFile(
'/main.tsx',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const result = applyChangesToString(
sourceCode,
utils.updateReduxStore('/main.tsx', source, {
keyName: 'SLICE_KEY',
reducerName: 'sliceReducer',
modulePath: '@test/slice',
})
);
expect(result).toContain(
"import { SLICE_KEY, sliceReducer } from '@test/slice'"
);
expect(result).toContain('[SLICE_KEY]: sliceReducer');
});
it('should update combineReducer call', () => {
const sourceCode = `
import { createStore, combineReducer } from 'redux';
const store = createStore(combineReducer({}));
`;
tree.write('/main.tsx', sourceCode);
const source = ts.createSourceFile(
'/main.tsx',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const result = applyChangesToString(
sourceCode,
utils.updateReduxStore('/main.tsx', source, {
keyName: 'SLICE_KEY',
reducerName: 'sliceReducer',
modulePath: '@test/slice',
})
);
expect(result).toContain(
"import { SLICE_KEY, sliceReducer } from '@test/slice'"
);
expect(result).toContain('[SLICE_KEY]: sliceReducer');
});
});
describe('getComponentName', () => {
[
{
testName: 'exporting a function',
src: `export default function Test(props: TestProps) {
return (
<div>
<h1>Welcome to test component, {props.name}</h1>
</div>
);`,
expectedName: 'Test',
},
{
testName: 'defining a function and then default exporting it',
src: `
function Test(props: TestProps) {
return (
<div>
<h1>Welcome to test component, {props.name}</h1>
</div>
);
};
export default Test;
`,
expectedName: 'Test',
},
{
testName: 'defining an arrow function and then exporting it',
src: `
const Test = (props: TestProps) => {
return (
<div>
<h1>Welcome to test component, {props.name}</h1>
</div>
);
};
export default Test;
`,
expectedName: 'Test',
},
{
testName: 'defining an arrow function that directly returns JSX',
src: `
const Test = (props: TestProps) => <div><h1>Welcome to test component, {props.name}</h1></div>;
export default Test
`,
expectedName: 'Test',
},
{
testName: 'exporting a react class component',
src: `
export default class Test extends React.Component<TestProps> {
render() {
return <div><h1>Welcome to test component, {this.props.name}</h1></div>;
}
}
`,
expectedName: 'Test',
},
{
testName: 'defining a react class component & then default exporting it',
src: `
export default class Test extends React.Component<TestProps> {
render() {
return <div><h1>Welcome to test component, {this.props.name}</h1></div>;
}
}
`,
expectedName: 'Test',
},
].forEach((testConfig) => {
it(`should find the component when ${testConfig.testName}`, () => {
const source = ts.createSourceFile(
'some-component.tsx',
testConfig.src,
ts.ScriptTarget.Latest,
true
);
const result = utils.getComponentName(source) as any;
expect(result).toBeDefined();
expect((result as any).name.text).toEqual(testConfig.expectedName);
});
});
it('should return null if there is no component', () => {
const source = ts.createSourceFile(
'some-component.tsx',
`console.log('hi there');`,
ts.ScriptTarget.Latest,
true
);
const result = utils.getComponentName(source) as any;
expect(result).toBeNull();
});
});