diff --git a/generators/component/index.js b/generators/component/index.js index 42bc6d8..a27e012 100644 --- a/generators/component/index.js +++ b/generators/component/index.js @@ -19,23 +19,31 @@ class ComponentGenerator extends Generators.Base { let settings = utils.yeoman.getAllSettingsFromComponentName(this.name, this.config.get('style')); let componentType = this.options.stateless ? 'Stateless' : 'Base'; + // Set the template base. If it cannot be guessed, + // use files from the default directory. If it cannot be + // guessed, assume we have something REALLY REALLY old here... + let templateBase = this.config.get('generatedWithVersion'); + if(!templateBase) { + templateBase = 3; + } + // Create the style template this.fs.copyTpl( - this.templatePath(`styles/Component${settings.style.suffix}`), + this.templatePath(`${templateBase}/styles/Component${settings.style.suffix}`), this.destinationPath(settings.style.path + settings.style.fileName), settings ); // Create the component this.fs.copyTpl( - this.templatePath(`components/${componentType}.js`), + this.templatePath(`${templateBase}/components/${componentType}.js`), this.destinationPath(settings.component.path + settings.component.fileName), settings ); // Create the unit test this.fs.copyTpl( - this.templatePath('tests/Base.js'), + this.templatePath(`${templateBase}/tests/Base.js`), this.destinationPath(settings.test.path + settings.test.fileName), settings ); diff --git a/generators/component/templates/components/Base.js b/generators/component/templates/3/components/Base.js similarity index 100% rename from generators/component/templates/components/Base.js rename to generators/component/templates/3/components/Base.js diff --git a/generators/component/templates/components/Stateless.js b/generators/component/templates/3/components/Stateless.js similarity index 100% rename from generators/component/templates/components/Stateless.js rename to generators/component/templates/3/components/Stateless.js diff --git a/generators/component/templates/styles/Component.css b/generators/component/templates/3/styles/Component.css similarity index 100% rename from generators/component/templates/styles/Component.css rename to generators/component/templates/3/styles/Component.css diff --git a/generators/component/templates/styles/Component.less b/generators/component/templates/3/styles/Component.less similarity index 100% rename from generators/component/templates/styles/Component.less rename to generators/component/templates/3/styles/Component.less diff --git a/generators/component/templates/styles/Component.sass b/generators/component/templates/3/styles/Component.sass similarity index 100% rename from generators/component/templates/styles/Component.sass rename to generators/component/templates/3/styles/Component.sass diff --git a/generators/component/templates/styles/Component.scss b/generators/component/templates/3/styles/Component.scss similarity index 100% rename from generators/component/templates/styles/Component.scss rename to generators/component/templates/3/styles/Component.scss diff --git a/generators/component/templates/styles/Component.styl b/generators/component/templates/3/styles/Component.styl similarity index 100% rename from generators/component/templates/styles/Component.styl rename to generators/component/templates/3/styles/Component.styl diff --git a/generators/component/templates/tests/Base.js b/generators/component/templates/3/tests/Base.js similarity index 100% rename from generators/component/templates/tests/Base.js rename to generators/component/templates/3/tests/Base.js diff --git a/generators/component/templates/4/components/Base.js b/generators/component/templates/4/components/Base.js new file mode 100644 index 0000000..0665a96 --- /dev/null +++ b/generators/component/templates/4/components/Base.js @@ -0,0 +1,20 @@ +import React from 'react'; +import cssmodules from 'react-css-modules'; +import styles from '<%= style.webpackPath %>'; + +@cssmodules(styles) +class <%= component.className %> extends React.Component { + render() { + return ( +
+ Please edit <%= component.path %><%= component.fileName %> to update this component! +
+ ); + } +} + +<%= component.className %>.displayName = '<%= component.displayName %>'; +<%= component.className %>.propTypes = {}; +<%= component.className %>.defaultProps = {}; + +export default <%= component.className %>; diff --git a/generators/component/templates/4/components/Stateless.js b/generators/component/templates/4/components/Stateless.js new file mode 100644 index 0000000..c9cb461 --- /dev/null +++ b/generators/component/templates/4/components/Stateless.js @@ -0,0 +1,18 @@ +import React from 'react'; +import cssmodules from 'react-css-modules'; +import styles from '<%= style.webpackPath %>'; + +function <%= component.className %>() { + + return ( +
+ Please edit <%= component.path %><%= component.fileName %> to update this component! +
+ ); +} + +<%= component.className %>.displayName = '<%= component.displayName %>'; +<%= component.className %>.propTypes = {}; +<%= component.className %>.defaultProps = {}; + +export default cssmodules(<%= component.className %>, styles); diff --git a/generators/component/templates/4/styles/Component.css b/generators/component/templates/4/styles/Component.css new file mode 100644 index 0000000..0a1af92 --- /dev/null +++ b/generators/component/templates/4/styles/Component.css @@ -0,0 +1,3 @@ +.<%= style.className %> { + border: 1px dashed #f00; +} diff --git a/generators/component/templates/4/styles/Component.less b/generators/component/templates/4/styles/Component.less new file mode 100644 index 0000000..0a1af92 --- /dev/null +++ b/generators/component/templates/4/styles/Component.less @@ -0,0 +1,3 @@ +.<%= style.className %> { + border: 1px dashed #f00; +} diff --git a/generators/component/templates/4/styles/Component.sass b/generators/component/templates/4/styles/Component.sass new file mode 100644 index 0000000..9e9d772 --- /dev/null +++ b/generators/component/templates/4/styles/Component.sass @@ -0,0 +1,2 @@ +.<%= style.className %> + border: 1px dashed #f00 diff --git a/generators/component/templates/4/styles/Component.scss b/generators/component/templates/4/styles/Component.scss new file mode 100644 index 0000000..0a1af92 --- /dev/null +++ b/generators/component/templates/4/styles/Component.scss @@ -0,0 +1,3 @@ +.<%= style.className %> { + border: 1px dashed #f00; +} diff --git a/generators/component/templates/4/styles/Component.styl b/generators/component/templates/4/styles/Component.styl new file mode 100644 index 0000000..f37b496 --- /dev/null +++ b/generators/component/templates/4/styles/Component.styl @@ -0,0 +1,2 @@ +.<%= style.className %> + border 1px dashed #f00 diff --git a/generators/component/templates/4/tests/Base.js b/generators/component/templates/4/tests/Base.js new file mode 100644 index 0000000..256044b --- /dev/null +++ b/generators/component/templates/4/tests/Base.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import <%= component.className %> from '<%= component.webpackPath %>'; + +describe('<<%= component.className %> />', () => { + + let component; + beforeEach(() => { + component = shallow(<%= component.className %>); + }); + + describe('when rendering the component', () => { + + it('should have a className of "index"', () => { + expect(component.hasClass('<%= style.className %>')).to.equal(true); + }); + }); +}); diff --git a/test/generators/app/indexTest.js b/test/generators/app/indexTest.js index bdf9ef2..4ac8760 100644 --- a/test/generators/app/indexTest.js +++ b/test/generators/app/indexTest.js @@ -94,7 +94,7 @@ describe('react-webpack:app', () => { 'src/actions/README.md', 'src/index.js', 'src/components/App.js', - 'src/components/app.css', + 'src/components/app.cssmodule.css', 'src/favicon.ico', 'src/images/yeoman.png', 'src/index.html', @@ -116,6 +116,7 @@ describe('react-webpack:app', () => { }); }); +/* describe.skip('react-webpack:app with PostCSS support', () => { let prompts = {}; @@ -214,3 +215,4 @@ describe.skip('react-webpack:app with PostCSS support', () => { }); }); }); +*/ diff --git a/test/generators/component/indexTest.js b/test/generators/component/indexTest.js index 4749c05..20f3c49 100644 --- a/test/generators/component/indexTest.js +++ b/test/generators/component/indexTest.js @@ -5,177 +5,372 @@ let helpers = require('yeoman-test'); describe('react-webpack:component', () => { - let generatorComponent = path.join(__dirname, '../../../generators/component'); + const generatorComponent = path.join(__dirname, '../../../generators/component'); - // List of available style types. Please add a line that says - // testComponentWithStyle(styleTypes.KEY); to the bottom of the file - // to run all unit tests for this filetype. - const styleTypes = { - css: { - type: 'css', - fileName: 'src/styles/Mycomponent.css', - expandedFileName: 'src/styles/my/littleSpecial/Test.css', - assertions: { - componentImport: 'require(\'styles//Mycomponent.css\');', - styleContent: '.mycomponent-component' - } - }, - sass: { - type: 'sass', - fileName: 'src/styles/Mycomponent.sass', - expandedFileName: 'src/styles/my/littleSpecial/Test.sass', - assertions: { - componentImport: 'require(\'styles//Mycomponent.sass\');', - styleContent: '.mycomponent-component' - } - }, - scss: { - type: 'scss', - fileName: 'src/styles/Mycomponent.scss', - expandedFileName: 'src/styles/my/littleSpecial/Test.scss', - assertions: { - componentImport: 'require(\'styles//Mycomponent.scss\');', - styleContent: '.mycomponent-component' - } - }, - less: { - type: 'less', - fileName: 'src/styles/Mycomponent.less', - expandedFileName: 'src/styles/my/littleSpecial/Test.less', - assertions: { - componentImport: 'require(\'styles//Mycomponent.less\');', - styleContent: '.mycomponent-component' - } - }, - stylus: { - type: 'stylus', - fileName: 'src/styles/Mycomponent.styl', - expandedFileName: 'src/styles/my/littleSpecial/Test.styl', - assertions: { - componentImport: 'require(\'styles//Mycomponent.styl\');', - styleContent: '.mycomponent-component' - } - } - }; + describe('when using version 3 of the generator', () => { - /** - * Return a newly generated component with given name and style - * @param {String} name Name of the component - * @param {String} styleType Styling language to use - * @param {Object} options Options to use for the generator - * @param {Function} callback Test callback to run - */ - function createGeneratedComponent(name, styleType, options, callback) { - helpers.run(generatorComponent) - .withArguments([name]) - .withOptions(options) - .on('ready', (instance) => { - instance.config.set('style', styleType); - }) - .on('end', callback); - } + // List of available style types. Please add a line that says + // testComponentWithStyle(styleTypes.KEY); to the bottom of the file + // to run all unit tests for this filetype. + const styleTypes = { + css: { + type: 'css', + fileName: 'src/styles/Mycomponent.css', + expandedFileName: 'src/styles/my/littleSpecial/Test.css', + assertions: { + componentImport: 'require(\'styles//Mycomponent.css\');', + styleContent: '.mycomponent-component' + } + }, + sass: { + type: 'sass', + fileName: 'src/styles/Mycomponent.sass', + expandedFileName: 'src/styles/my/littleSpecial/Test.sass', + assertions: { + componentImport: 'require(\'styles//Mycomponent.sass\');', + styleContent: '.mycomponent-component' + } + }, + scss: { + type: 'scss', + fileName: 'src/styles/Mycomponent.scss', + expandedFileName: 'src/styles/my/littleSpecial/Test.scss', + assertions: { + componentImport: 'require(\'styles//Mycomponent.scss\');', + styleContent: '.mycomponent-component' + } + }, + less: { + type: 'less', + fileName: 'src/styles/Mycomponent.less', + expandedFileName: 'src/styles/my/littleSpecial/Test.less', + assertions: { + componentImport: 'require(\'styles//Mycomponent.less\');', + styleContent: '.mycomponent-component' + } + }, + stylus: { + type: 'stylus', + fileName: 'src/styles/Mycomponent.styl', + expandedFileName: 'src/styles/my/littleSpecial/Test.styl', + assertions: { + componentImport: 'require(\'styles//Mycomponent.styl\');', + styleContent: '.mycomponent-component' + } + } + }; - /** - * Test a component with styling applied - * @param {Object} style The style to apply (see styleTypes above) - * @param {Object} options Options to use [optional] - */ - function testComponentWithStyle(style, options) { - - // Make sure we always have options - if(!options) { - options = {}; + /** + * Return a newly generated component with given name and style + * @param {String} name Name of the component + * @param {String} styleType Styling language to use + * @param {Object} options Options to use for the generator + * @param {Function} callback Test callback to run + */ + function createGeneratedComponent(name, styleType, options, callback) { + helpers.run(generatorComponent) + .withArguments([name]) + .withOptions(options) + .on('ready', (instance) => { + instance.config.set('style', styleType); + instance.config.set('generatedWithVersion', 3); + }) + .on('end', callback); } - describe(`When using style type "${style.type}"`, () => { + /** + * Test a component with styling applied + * @param {Object} style The style to apply (see styleTypes above) + * @param {Object} options Options to use [optional] + */ + function testComponentWithStyle(style, options) { - describe('When writing is called', () => { + // Make sure we always have options + if(!options) { + options = {}; + } - it(`should create the react component, its ${style.type}-stylesheet and test file`, (done) => { - createGeneratedComponent('mycomponent', style.type, options, () => { + describe(`when using style type "${style.type}"`, () => { - assert.file([ - 'src/components/MycomponentComponent.js', - style.fileName, - 'test/components/MycomponentComponentTest.js' - ]); - done(); + describe('when writing is called', () => { + + it(`should create the react component, its ${style.type}-stylesheet and test file`, (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + + assert.file([ + 'src/components/MycomponentComponent.js', + style.fileName, + 'test/components/MycomponentComponentTest.js' + ]); + done(); + }); + }); + }); + + describe('when creating a component', () => { + + it('should always import REACT', (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('src/components/MycomponentComponent.js', 'import React from \'react\';'); + done(); + }); + }); + + it(`should require the created ${style.type} file`, (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('src/components/MycomponentComponent.js', style.assertions.componentImport); + done(); + }); + }); + + it('should have its displayName set per default', (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('src/components/MycomponentComponent.js', 'displayName = \'MycomponentComponent\';'); + done(); + }); + }); + + it('should export the created component', (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('src/components/MycomponentComponent.js', 'export default MycomponentComponent'); + done(); + }); + }); + + it('should be possible to create components in a subfolder', (done) => { + createGeneratedComponent('my/little !special/test', style.type, options, () => { + + assert.file([ + 'src/components/my/littleSpecial/TestComponent.js', + style.expandedFileName, + 'test/components/my/littleSpecial/TestComponentTest.js' + ]); + done(); + }); + }); + }); + + describe('Style', () => { + + it(`should add the components ${style.type} class to the stylesheet`, (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent(style.fileName, style.assertions.styleContent); + done(); + }); + }); + }); + + describe('Test', () => { + + it('should import the react component', (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';'); + done(); + }); }); }); }); + } - describe('When creating a component', () => { + // Run all tests for all available style types. + testComponentWithStyle(styleTypes.css); + testComponentWithStyle(styleTypes.sass); + testComponentWithStyle(styleTypes.scss); + testComponentWithStyle(styleTypes.less); + testComponentWithStyle(styleTypes.stylus); - it('should always import REACT', (done) => { - createGeneratedComponent('mycomponent', style.type, options, () => { - assert.fileContent('src/components/MycomponentComponent.js', 'import React from \'react\';'); - done(); + // Test stateless components (should be enough when testing with defaults) + testComponentWithStyle(styleTypes.css, { stateless: true }); + }); + + describe('when using version 4 of the generator', () => { + + // List of available style types. Please add a line that says + // testComponentWithStyle(styleTypes.KEY); to the bottom of the file + // to run all unit tests for this filetype. + const styleTypes = { + css: { + type: 'css', + fileName: 'src/styles/Mycomponent.css', + expandedFileName: 'src/styles/my/littleSpecial/Test.css', + assertions: { + componentImport: 'import styles from \'styles//Mycomponent.css\';', + styleContent: '.mycomponent-component' + } + }, + sass: { + type: 'sass', + fileName: 'src/styles/Mycomponent.sass', + expandedFileName: 'src/styles/my/littleSpecial/Test.sass', + assertions: { + componentImport: 'import styles from \'styles//Mycomponent.sass\';', + styleContent: '.mycomponent-component' + } + }, + scss: { + type: 'scss', + fileName: 'src/styles/Mycomponent.scss', + expandedFileName: 'src/styles/my/littleSpecial/Test.scss', + assertions: { + componentImport: 'import styles from \'styles//Mycomponent.scss\';', + styleContent: '.mycomponent-component' + } + }, + less: { + type: 'less', + fileName: 'src/styles/Mycomponent.less', + expandedFileName: 'src/styles/my/littleSpecial/Test.less', + assertions: { + componentImport: 'import styles from \'styles//Mycomponent.less\';', + styleContent: '.mycomponent-component' + } + }, + stylus: { + type: 'stylus', + fileName: 'src/styles/Mycomponent.styl', + expandedFileName: 'src/styles/my/littleSpecial/Test.styl', + assertions: { + componentImport: 'import styles from \'styles//Mycomponent.styl\';', + styleContent: '.mycomponent-component' + } + } + }; + + /** + * @var {yeoman.generator} generator + * Global generator instance, set by createGeneratedComponent + */ + let generator; + + /** + * Return a newly generated component with given name and style + * @param {String} name Name of the component + * @param {String} styleType Styling language to use + * @param {Object} options Options to use for the generator + * @param {Function} callback Test callback to run + */ + function createGeneratedComponent(name, styleType, options, callback) { + helpers.run(generatorComponent) + .withArguments([name]) + .withOptions(options) + .on('ready', (instance) => { + instance.config.set('style', styleType); + instance.config.set('generatedWithVersion', 4); + generator = instance; + }) + .on('end', callback); + } + + /** + * Test a component with styling applied + * @param {Object} style The style to apply (see styleTypes above) + * @param {Object} options Options to use [optional] + */ + function testComponentWithStyle(style, options) { + + // Make sure we always have options + if(!options) { + options = {}; + } + + describe(`when using style type "${style.type}"`, () => { + + describe('when writing is called', () => { + + it(`should create the react component, its ${style.type}-stylesheet and test file`, (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + + assert.file([ + 'src/components/MycomponentComponent.js', + style.fileName, + 'test/components/MycomponentComponentTest.js' + ]); + done(); + }); }); }); - it(`should require the created ${style.type} file`, (done) => { - createGeneratedComponent('mycomponent', style.type, options, () => { - assert.fileContent('src/components/MycomponentComponent.js', style.assertions.componentImport); - done(); + describe('when creating a component', () => { + + it('should always import REACT', (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('src/components/MycomponentComponent.js', 'import React from \'react\';'); + done(); + }); + }); + + it(`should require the created ${style.type} file`, (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('src/components/MycomponentComponent.js', style.assertions.componentImport); + done(); + }); + }); + + it('should have its displayName set per default', (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('src/components/MycomponentComponent.js', 'displayName = \'MycomponentComponent\';'); + done(); + }); + }); + + it('should export the created component', (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + + let exportAssertion; + if(generator.options.stateless) { + exportAssertion = 'export default cssmodules(MycomponentComponent, styles);'; + } else { + exportAssertion = 'export default MycomponentComponent'; + } + assert.fileContent('src/components/MycomponentComponent.js', exportAssertion); + done(); + }); + }); + + it('should be possible to create components in a subfolder', (done) => { + createGeneratedComponent('my/little !special/test', style.type, options, () => { + + assert.file([ + 'src/components/my/littleSpecial/TestComponent.js', + style.expandedFileName, + 'test/components/my/littleSpecial/TestComponentTest.js' + ]); + done(); + }); }); }); - it('should have its displayName set per default', (done) => { - createGeneratedComponent('mycomponent', style.type, options, () => { - assert.fileContent('src/components/MycomponentComponent.js', 'displayName = \'MycomponentComponent\';'); - done(); + describe('Style', () => { + + it(`should add the components ${style.type} class to the stylesheet`, (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent(style.fileName, style.assertions.styleContent); + done(); + }); }); }); - it('should export the created component', (done) => { - createGeneratedComponent('mycomponent', style.type, options, () => { - assert.fileContent('src/components/MycomponentComponent.js', 'export default MycomponentComponent'); - done(); - }); - }); + describe('Test', () => { - it('should be possible to create components in a subfolder', (done) => { - createGeneratedComponent('my/little !special/test', style.type, options, () => { - - assert.file([ - 'src/components/my/littleSpecial/TestComponent.js', - style.expandedFileName, - 'test/components/my/littleSpecial/TestComponentTest.js' - ]); - done(); + it('should import the react component', (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';'); + done(); + }); }); }); }); + } - describe('Style', () => { + // Run all tests for all available style types. + testComponentWithStyle(styleTypes.css); + testComponentWithStyle(styleTypes.sass); + testComponentWithStyle(styleTypes.scss); + testComponentWithStyle(styleTypes.less); + testComponentWithStyle(styleTypes.stylus); - it(`should add the components ${style.type} class to the stylesheet`, (done) => { - createGeneratedComponent('mycomponent', style.type, options, () => { - assert.fileContent(style.fileName, style.assertions.styleContent); - done(); - }); - }); - }); + // Test stateless components (should be enough when testing with defaults) + testComponentWithStyle(styleTypes.css, { stateless: true }); + }); - describe('Test', () => { - - it('should import the react component', (done) => { - createGeneratedComponent('mycomponent', style.type, options, () => { - assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';'); - done(); - }); - }); - }); - }); - } - - // Run all tests for all available style types. - testComponentWithStyle(styleTypes.css); - testComponentWithStyle(styleTypes.sass); - testComponentWithStyle(styleTypes.scss); - testComponentWithStyle(styleTypes.less); - testComponentWithStyle(styleTypes.stylus); - - // Test stateless components (should be enough when testing with defaults) - testComponentWithStyle(styleTypes.css, { stateless: true }); });