diff --git a/blueprints-js/component-test/qunit-rfc-232-files/__root__/__testType__/__path__/__test__.gjs b/blueprints-js/component-test/qunit-rfc-232-files/__root__/__testType__/__path__/__test__.gjs new file mode 100644 index 00000000000..33449bb5877 --- /dev/null +++ b/blueprints-js/component-test/qunit-rfc-232-files/__root__/__testType__/__path__/__test__.gjs @@ -0,0 +1,38 @@ +<% if (testType === 'integration') { %>import { module, test } from 'qunit'; +import { setupRenderingTest } from '<%= modulePrefix %>/tests/helpers'; +import { render } from '@ember/test-helpers'; +import <%= componentName %> from '<%= modulePrefix %>/components/<%= componentPathName %>'; + +module('<%= friendlyTestDescription %>', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + // Updating values is achieved using autotracking, just like in app code. For example: + // class State { @tracked myProperty = 0; }; const state = new State(); + // and update using state.myProperty = 1; await rerender(); + // Handle any actions with function myAction(val) { ... }; + + await render(); + + assert.dom().hasText(''); + + // Template block usage: + await render(); + + assert.dom().hasText('template block text'); + }); +});<% } else if (testType === 'unit') { %>import { module, test } from 'qunit'; +import { setupTest } from '<%= modulePrefix %>/tests/helpers'; + +module('<%= friendlyTestDescription %>', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let component = this.owner.factoryFor('component:<%= componentPathName %>').create(); + assert.ok(component); + }); +}); <% } %> diff --git a/blueprints-js/component/files/__root__/__templatepath__/__templatename__.gjs b/blueprints-js/component/files/__root__/__templatepath__/__templatename__.gjs new file mode 100644 index 00000000000..e8d9d219abd --- /dev/null +++ b/blueprints-js/component/files/__root__/__templatepath__/__templatename__.gjs @@ -0,0 +1,3 @@ + diff --git a/blueprints/component-test/index.js b/blueprints/component-test/index.js index 1e89db6e6b0..9f95319b582 100644 --- a/blueprints/component-test/index.js +++ b/blueprints/component-test/index.js @@ -15,6 +15,12 @@ function invocationFor(options) { return parts.map((p) => stringUtil.classify(p)).join('::'); } +function invocationForStrictComponentAuthoringFormat(options) { + let parts = options.entity.name.split('/'); + let componentName = parts[parts.length - 1]; + return stringUtil.classify(componentName); +} + module.exports = useTestFrameworkDetector({ description: 'Generates a component integration or unit test.', @@ -37,6 +43,17 @@ module.exports = useTestFrameworkDetector({ { unit: 'unit' }, ], }, + { + name: 'component-authoring-format', + type: ['loose', 'strict'], + default: 'loose', + aliases: [ + { loose: 'loose' }, + { strict: 'strict' }, + { 'template-tag': 'strict' }, + { tt: 'strict' }, + ], + }, ], fileMapTokens: function () { @@ -56,6 +73,19 @@ module.exports = useTestFrameworkDetector({ }; }, + files() { + let files = this._super.files.apply(this, arguments); + + if (this.options.componentAuthoringFormat === 'strict') { + files = files.filter((file) => !(file.endsWith('.js') || file.endsWith('.ts'))); + } + if (this.options.componentAuthoringFormat === 'loose') { + files = files.filter((file) => !(file.endsWith('.gjs') || file.endsWith('.gts'))); + } + + return files; + }, + locals: function (options) { let dasherizedModuleName = stringUtil.dasherize(options.entity.name); let componentPathName = dasherizedModuleName; @@ -75,7 +105,10 @@ module.exports = useTestFrameworkDetector({ ? "import { hbs } from 'ember-cli-htmlbars';" : "import hbs from 'htmlbars-inline-precompile';"; - let templateInvocation = invocationFor(options); + let templateInvocation = + this.options.componentAuthoringFormat === 'strict' + ? invocationForStrictComponentAuthoringFormat(options) + : invocationFor(options); let componentName = templateInvocation; let openComponent = (descriptor) => `<${descriptor}>`; let closeComponent = (descriptor) => ``; diff --git a/blueprints/component-test/qunit-rfc-232-files/__root__/__testType__/__path__/__test__.gts b/blueprints/component-test/qunit-rfc-232-files/__root__/__testType__/__path__/__test__.gts new file mode 100644 index 00000000000..33449bb5877 --- /dev/null +++ b/blueprints/component-test/qunit-rfc-232-files/__root__/__testType__/__path__/__test__.gts @@ -0,0 +1,38 @@ +<% if (testType === 'integration') { %>import { module, test } from 'qunit'; +import { setupRenderingTest } from '<%= modulePrefix %>/tests/helpers'; +import { render } from '@ember/test-helpers'; +import <%= componentName %> from '<%= modulePrefix %>/components/<%= componentPathName %>'; + +module('<%= friendlyTestDescription %>', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + // Updating values is achieved using autotracking, just like in app code. For example: + // class State { @tracked myProperty = 0; }; const state = new State(); + // and update using state.myProperty = 1; await rerender(); + // Handle any actions with function myAction(val) { ... }; + + await render(); + + assert.dom().hasText(''); + + // Template block usage: + await render(); + + assert.dom().hasText('template block text'); + }); +});<% } else if (testType === 'unit') { %>import { module, test } from 'qunit'; +import { setupTest } from '<%= modulePrefix %>/tests/helpers'; + +module('<%= friendlyTestDescription %>', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let component = this.owner.factoryFor('component:<%= componentPathName %>').create(); + assert.ok(component); + }); +}); <% } %> diff --git a/blueprints/component/files/__root__/__templatepath__/__templatename__.gts b/blueprints/component/files/__root__/__templatepath__/__templatename__.gts new file mode 100644 index 00000000000..e8d9d219abd --- /dev/null +++ b/blueprints/component/files/__root__/__templatepath__/__templatename__.gts @@ -0,0 +1,3 @@ + diff --git a/blueprints/component/index.js b/blueprints/component/index.js index fffa4961bea..8d04d928d4b 100644 --- a/blueprints/component/index.js +++ b/blueprints/component/index.js @@ -52,6 +52,17 @@ module.exports = { default: OCTANE ? 'flat' : 'classic', aliases: OCTANE ? [{ fs: 'flat' }, { ns: 'nested' }, { cs: 'classic' }] : [{ cs: 'classic' }], }, + { + name: 'component-authoring-format', + type: ['loose', 'strict'], + default: 'loose', + aliases: [ + { loose: 'loose' }, + { strict: 'strict' }, + { 'template-tag': 'strict' }, + { tt: 'strict' }, + ], + }, ], /** @@ -135,14 +146,16 @@ module.exports = { afterInstall(options) { this._super.afterInstall.apply(this, arguments); - this.skippedJsFiles.forEach((file) => { - let mapped = this.mapFile(file, this.savedLocals); - this.ui.writeLine(` ${chalk.yellow('skip')} ${mapped}`); - }); + if (options.componentAuthoringFormat === 'loose') { + this.skippedJsFiles.forEach((file) => { + let mapped = this.mapFile(file, this.savedLocals); + this.ui.writeLine(` ${chalk.yellow('skip')} ${mapped}`); + }); - if (this.skippedJsFiles.size > 0) { - let command = `ember generate component-class ${options.entity.name}`; - this.ui.writeLine(` ${chalk.cyan('tip')} to add a class, run \`${command}\``); + if (this.skippedJsFiles.size > 0) { + let command = `ember generate component-class ${options.entity.name}`; + this.ui.writeLine(` ${chalk.cyan('tip')} to add a class, run \`${command}\``); + } } }, @@ -225,6 +238,14 @@ module.exports = { } }); } + if (this.options.componentAuthoringFormat === 'strict') { + files = files.filter( + (file) => !(file.endsWith('.js') || file.endsWith('.ts') || file.endsWith('.hbs')) + ); + } + if (this.options.componentAuthoringFormat === 'loose') { + files = files.filter((file) => !(file.endsWith('.gjs') || file.endsWith('.gts'))); + } return files; }, diff --git a/node-tests/blueprints/component-test-test.js b/node-tests/blueprints/component-test-test.js index 4c0b903af83..e42211f6a98 100644 --- a/node-tests/blueprints/component-test-test.js +++ b/node-tests/blueprints/component-test-test.js @@ -38,6 +38,14 @@ describe('Blueprint: component-test', function () { ); }); }); + + it('component-test x-foo --strict', function () { + return emberGenerateDestroy(['component-test', 'x-foo', '--strict'], (_file) => { + expect(_file('tests/integration/components/x-foo-test.gjs')).to.equal( + fixture('component-test/rfc232.gjs') + ); + }); + }); }); describe('with ember-cli-qunit@4.1.0', function () { diff --git a/node-tests/blueprints/component-test.js b/node-tests/blueprints/component-test.js index b8338928887..6be68c7c3fa 100644 --- a/node-tests/blueprints/component-test.js +++ b/node-tests/blueprints/component-test.js @@ -31,6 +31,11 @@ const templateOnlyContents = `import templateOnly from '@ember/component/templat export default templateOnly(); `; +const templateTagContents = ` +`; + describe('Blueprint: component', function () { setupTestHooks(this); @@ -324,6 +329,12 @@ describe('Blueprint: component', function () { ); }); + it('component foo --strict', function () { + return emberGenerateDestroy(['component', 'foo', '--strict'], (_file) => { + expect(_file('app/components/foo.gjs')).to.equal(templateTagContents); + }); + }); + describe('with podModulePrefix', function () { beforeEach(function () { setupPodConfig({ podModulePrefix: true }); diff --git a/node-tests/fixtures/component-test/rfc232.gjs b/node-tests/fixtures/component-test/rfc232.gjs new file mode 100644 index 00000000000..f030d391ba7 --- /dev/null +++ b/node-tests/fixtures/component-test/rfc232.gjs @@ -0,0 +1,28 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'my-app/tests/helpers'; +import { render } from '@ember/test-helpers'; +import XFoo from 'my-app/components/x-foo'; + +module('Integration | Component | x-foo', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + // Updating values is achieved using autotracking, just like in app code. For example: + // class State { @tracked myProperty = 0; }; const state = new State(); + // and update using state.myProperty = 1; await rerender(); + // Handle any actions with function myAction(val) { ... }; + + await render(); + + assert.dom().hasText(''); + + // Template block usage: + await render(); + + assert.dom().hasText('template block text'); + }); +});