diff --git a/.eslintrc.js b/.eslintrc.js index d90703fee..95320bf4d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -62,6 +62,7 @@ module.exports = { files: ['tests/**/*'], rules: { 'node/no-missing-require': 'off', + 'import/no-extraneous-dependencies': 'off', }, }, ], diff --git a/packages/compat/src/resolver-transform.ts b/packages/compat/src/resolver-transform.ts index ea5528d97..2bf4396e4 100644 --- a/packages/compat/src/resolver-transform.ts +++ b/packages/compat/src/resolver-transform.ts @@ -323,14 +323,23 @@ class TemplateResolver implements ASTPlugin { } private get staticComponentsEnabled(): boolean { + if (!this.config?.options) { + return true; + } return this.config.options.staticComponents || Boolean(this.auditHandler); } private get staticHelpersEnabled(): boolean { + if (!this.config?.options) { + return true; + } return this.config.options.staticHelpers || Boolean(this.auditHandler); } private get staticModifiersEnabled(): boolean { + if (!this.config?.options) { + return true; + } return this.config.options.staticModifiers || Boolean(this.auditHandler); } @@ -349,7 +358,7 @@ class TemplateResolver implements ASTPlugin { // we're not responsible for filtering out rules for inactive packages here, // that is done before getting to us. So we should assume these are all in // force. - for (let rule of this.config.activePackageRules) { + for (let rule of this.config?.activePackageRules ?? []) { if (rule.components) { for (let [snippet, rules] of Object.entries(rule.components)) { let processedRules = preprocessComponentRule(rules); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 44c31def3..034134a2c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1739,6 +1739,84 @@ importers: specifier: ^5.0.9 version: 5.4.11(terser@5.37.0) + tests/app-template-minimal: + devDependencies: + '@babel/core': + specifier: ^7.19.3 + version: 7.26.0 + '@babel/plugin-transform-runtime': + specifier: ^7.25.4 + version: 7.25.9(@babel/core@7.26.0) + '@babel/runtime': + specifier: ^7.25.6 + version: 7.26.0 + '@ember/optional-features': + specifier: ^2.0.0 + version: 2.2.0 + '@ember/string': + specifier: ^3.1.1 + version: 3.1.1 + '@ember/test-helpers': + specifier: ^4.0.4 + version: 4.0.4(@babel/core@7.26.0)(ember-source@6.3.0-alpha.3) + '@embroider/compat': + specifier: workspace:* + version: link:../../packages/compat + '@embroider/core': + specifier: workspace:* + version: link:../../packages/core + '@embroider/router': + specifier: workspace:* + version: link:../../packages/router + '@embroider/test-setup': + specifier: workspace:* + version: link:../../packages/test-setup + '@embroider/vite': + specifier: workspace:* + version: link:../../packages/vite + '@glimmer/component': + specifier: ^2.0.0 + version: 2.0.0 + '@glimmer/tracking': + specifier: ^1.1.2 + version: 1.1.2 + '@rollup/plugin-babel': + specifier: ^5.3.1 + version: 5.3.1(@babel/core@7.26.0)(rollup@3.29.5) + babel-plugin-ember-template-compilation: + specifier: ^2.3.0 + version: 2.3.0 + decorator-transforms: + specifier: ^2.0.0 + version: 2.3.0(@babel/core@7.26.0) + ember-modifier: + specifier: ^4.1.0 + version: 4.2.0(@babel/core@7.26.0)(ember-source@6.3.0-alpha.3) + ember-page-title: + specifier: ^7.0.0 + version: 7.0.0 + ember-qunit: + specifier: ^9.0.1 + version: 9.0.1(@ember/test-helpers@4.0.4)(ember-source@6.3.0-alpha.3)(qunit@2.23.1) + ember-resolver: + specifier: ^13.1.0 + version: 13.1.0(ember-source@6.3.0-alpha.3) + ember-source: + specifier: 6.3.0-alpha.3 + version: 6.3.0-alpha.3(@glimmer/component@2.0.0) + qunit: + specifier: ^2.19.4 + version: 2.23.1 + qunit-dom: + specifier: ^3.4.0 + version: 3.4.0 + tracked-built-ins: + specifier: ^3.1.1 + version: 3.4.0(@babel/core@7.26.0) + vite: + specifier: ^5.0.9 + version: 5.4.11(terser@5.37.0) + tests/fixtures: {} tests/scenarios: @@ -5732,6 +5810,24 @@ packages: - supports-color dev: true + /@ember/test-helpers@4.0.4(@babel/core@7.26.0)(ember-source@6.3.0-alpha.3): + resolution: {integrity: sha512-1mbOVyVEcLxYOGzBaeeaQkCrL1o9Av86QaHk/1RvrVBW24I6YUj1ILLEi2qLZT5PzcCy0TdfadHT3hKJwJ0GcQ==} + peerDependencies: + ember-source: '>= 4.0.0' + dependencies: + '@ember/test-waiters': 3.1.0 + '@embroider/addon-shim': 1.9.0 + '@embroider/macros': 1.16.10(@glint/template@1.5.1) + '@simple-dom/interface': 1.4.0 + decorator-transforms: 2.3.0(@babel/core@7.26.0) + dom-element-descriptors: 0.5.1 + ember-source: 6.3.0-alpha.3(@glimmer/component@2.0.0) + transitivePeerDependencies: + - '@babel/core' + - '@glint/template' + - supports-color + dev: true + /@ember/test-waiters@3.1.0: resolution: {integrity: sha512-bb9h95ktG2wKY9+ja1sdsFBdOms2lB19VWs8wmNpzgHv1NCetonBoV5jHBV4DHt0uS1tg9z66cZqhUVlYs96KQ==} engines: {node: 10.* || 12.* || >= 14.*} @@ -6351,6 +6447,16 @@ packages: - '@babel/core' - supports-color + /@glimmer/component@2.0.0: + resolution: {integrity: sha512-eATSzBOUm0MZ9+YfJx7Y5p3gbwnaeMzLSSsCDn1ihDtUOIm5YYEV0ee0G7tXt/uKxowt8tXYn/EMbI9OlRF0CA==} + engines: {node: '>= 18'} + dependencies: + '@embroider/addon-shim': 1.9.0 + '@glimmer/env': 0.1.7 + transitivePeerDependencies: + - supports-color + dev: true + /@glimmer/debug@0.87.1: resolution: {integrity: sha512-rja9/Hofv1NEjIqp8P2eQuHY3+orlS3BL4fbFyvrE+Pw4lRwQPLm6UdgCMHZGGe9yweZAGvNVH6CimDBq7biwA==} dependencies: @@ -15793,7 +15899,7 @@ packages: decorator-transforms: 2.3.0(@babel/core@7.26.0) ember-cli-normalize-entity-name: 1.0.0 ember-cli-string-utils: 1.1.0 - ember-source: 6.3.0-alpha.3(webpack@5.97.1) + ember-source: 6.3.0-alpha.3(@glimmer/component@2.0.0) transitivePeerDependencies: - '@babel/core' - supports-color @@ -16043,6 +16149,24 @@ packages: - supports-color dev: true + /ember-qunit@9.0.1(@ember/test-helpers@4.0.4)(ember-source@6.3.0-alpha.3)(qunit@2.23.1): + resolution: {integrity: sha512-9DgjczFG7ZjINmwWFYDtUF8McbYqQir82hyFp/ZbMOLkpFvHCKPw1mtKcpcdLnLAAYJpwR2/MCyPNiEMkR11aA==} + peerDependencies: + '@ember/test-helpers': '>=3.0.3' + ember-source: '>=4.0.0' + qunit: ^2.13.0 + dependencies: + '@ember/test-helpers': 4.0.4(@babel/core@7.26.0)(ember-source@6.3.0-alpha.3) + '@embroider/addon-shim': 1.9.0 + '@embroider/macros': 1.16.10(@glint/template@1.5.1) + ember-source: 6.3.0-alpha.3(@glimmer/component@2.0.0) + qunit: 2.23.1 + qunit-theme-ember: 1.0.0 + transitivePeerDependencies: + - '@glint/template' + - supports-color + dev: true + /ember-ref-bucket@4.1.0(@babel/core@7.26.0): resolution: {integrity: sha512-oEUU2mDtuYuMM039U9YEqrrOCVHH6rQfvbFOmh3WxOVEgubmLVyKEpGgU4P/6j0B/JxTqqTwM3ULTQyDto8dKg==} engines: {node: 10.* || >= 12} @@ -16129,6 +16253,21 @@ packages: - supports-color dev: true + /ember-resolver@13.1.0(ember-source@6.3.0-alpha.3): + resolution: {integrity: sha512-t/PjXLCl5tM9EQXGIFoBgHiA41HkLJpfo17Nud5Cy9eyUPGcnsMjWJqQ+O5QHA0E63Sp+zTn4y/RS5Tu2v2ydg==} + engines: {node: 14.* || 16.* || >= 18} + peerDependencies: + ember-source: ^4.12.0 || >= 5.0.0 + peerDependenciesMeta: + ember-source: + optional: true + dependencies: + ember-cli-babel: 7.26.11 + ember-source: 6.3.0-alpha.3(@glimmer/component@2.0.0) + transitivePeerDependencies: + - supports-color + dev: true + /ember-rfc176-data@0.3.18: resolution: {integrity: sha512-JtuLoYGSjay1W3MQAxt3eINWXNYYQliK90tLwtb8aeCuQK8zKGCRbBodVIrkcTqshULMnRuTOS6t1P7oQk3g6Q==} @@ -16831,6 +16970,62 @@ packages: - webpack dev: true + /ember-source@6.3.0-alpha.3(@glimmer/component@2.0.0): + resolution: {integrity: sha512-qToUyqaFUonUzDDNC0POxeZwwbjjSmJONOkdRg0/qQ0zZySgdfdhFns8jC6ubXgunPl3cseMETh0gU+hHEncdw==} + engines: {node: '>= 18.*'} + peerDependencies: + '@glimmer/component': '>= 1.1.2' + dependencies: + '@babel/core': 7.26.0 + '@ember/edition-utils': 1.2.0 + '@embroider/addon-shim': 1.9.0 + '@glimmer/compiler': 0.92.4 + '@glimmer/component': 2.0.0 + '@glimmer/destroyable': 0.92.3 + '@glimmer/env': 0.1.7 + '@glimmer/global-context': 0.92.3 + '@glimmer/interfaces': 0.92.3 + '@glimmer/manager': 0.92.4 + '@glimmer/node': 0.92.4 + '@glimmer/opcode-compiler': 0.92.4 + '@glimmer/owner': 0.92.3 + '@glimmer/program': 0.92.4 + '@glimmer/reference': 0.92.3 + '@glimmer/runtime': 0.92.4 + '@glimmer/syntax': 0.92.3 + '@glimmer/util': 0.92.3 + '@glimmer/validator': 0.92.3 + '@glimmer/vm': 0.92.3 + '@glimmer/vm-babel-plugins': 0.92.3(@babel/core@7.26.0) + '@simple-dom/interface': 1.4.0 + backburner.js: 2.8.0 + broccoli-file-creator: 2.1.1 + broccoli-funnel: 3.0.8 + broccoli-merge-trees: 4.2.0 + chalk: 4.1.2 + ember-auto-import: 2.10.0(@glint/template@1.5.1) + ember-cli-babel: 8.2.0(@babel/core@7.26.0) + ember-cli-get-component-path-option: 1.0.0 + ember-cli-is-package-missing: 1.0.0 + ember-cli-normalize-entity-name: 1.0.0 + ember-cli-path-utils: 1.0.0 + ember-cli-string-utils: 1.1.0 + ember-cli-typescript-blueprint-polyfill: 0.1.0 + ember-cli-version-checker: 5.1.2 + ember-router-generator: 2.0.0 + inflection: 2.0.1 + route-recognizer: 0.3.4 + router_js: 8.0.6(route-recognizer@0.3.4) + semver: 7.6.3 + silent-error: 1.1.1 + simple-html-tokenizer: 0.5.11 + transitivePeerDependencies: + - '@glint/template' + - rsvp + - supports-color + - webpack + dev: true + /ember-source@6.3.0-alpha.3(webpack@5.97.1): resolution: {integrity: sha512-qToUyqaFUonUzDDNC0POxeZwwbjjSmJONOkdRg0/qQ0zZySgdfdhFns8jC6ubXgunPl3cseMETh0gU+hHEncdw==} engines: {node: '>= 18.*'} @@ -23095,6 +23290,12 @@ packages: - supports-color dev: true + /qunit-dom@3.4.0: + resolution: {integrity: sha512-N5PYbJ20RD3JZN4whINdl7dDfxScUy7eNuO8IwUtBWC7d6SH+BqtBqVZdRn9evxLQVzuask6OGvMy4gdpiCceg==} + dependencies: + dom-element-descriptors: 0.5.1 + dev: true + /qunit-theme-ember@1.0.0: resolution: {integrity: sha512-vdMVVo6ecdCkWttMTKeyq1ZTLGHcA6zdze2zhguNuc3ritlJMhOXY5RDseqazOwqZVfCg3rtlmL3fMUyIzUyFQ==} dev: true diff --git a/tests/app-template-minimal/.editorconfig b/tests/app-template-minimal/.editorconfig new file mode 100644 index 000000000..219985c22 --- /dev/null +++ b/tests/app-template-minimal/.editorconfig @@ -0,0 +1,20 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 + +[*.hbs] +insert_final_newline = false + +[*.{diff,md}] +trim_trailing_whitespace = false diff --git a/tests/app-template-minimal/.ember-cli b/tests/app-template-minimal/.ember-cli new file mode 100644 index 000000000..8c1812cff --- /dev/null +++ b/tests/app-template-minimal/.ember-cli @@ -0,0 +1,15 @@ +{ + /** + Ember CLI sends analytics information by default. The data is completely + anonymous, but there are times when you might want to disable this behavior. + + Setting `disableAnalytics` to true will prevent any data from being sent. + */ + "disableAnalytics": false, + + /** + Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript + rather than JavaScript by default, when a TypeScript version of a given blueprint is available. + */ + "isTypeScriptProject": false +} diff --git a/tests/app-template-minimal/.gitignore b/tests/app-template-minimal/.gitignore new file mode 100644 index 000000000..8dcec07ec --- /dev/null +++ b/tests/app-template-minimal/.gitignore @@ -0,0 +1,26 @@ +# compiled output +/dist/ + +# dependencies +/node_modules/ + +# misc +/.env* +/.pnp* +/.sass-cache +/connect.lock +/.eslintcache +/coverage/ +/npm-debug.log* +/testem.log +/yarn-error.log + +# ember-try +/.node_modules.ember-try/ +/npm-shrinkwrap.json.ember-try +/package.json.ember-try +/package-lock.json.ember-try +/yarn.lock.ember-try + +# broccoli-debug +/DEBUG/ diff --git a/tests/app-template-minimal/.watchmanconfig b/tests/app-template-minimal/.watchmanconfig new file mode 100644 index 000000000..f9c3d8f84 --- /dev/null +++ b/tests/app-template-minimal/.watchmanconfig @@ -0,0 +1,3 @@ +{ + "ignore_dirs": ["dist"] +} diff --git a/tests/app-template-minimal/README.md b/tests/app-template-minimal/README.md new file mode 100644 index 000000000..3d5527987 --- /dev/null +++ b/tests/app-template-minimal/README.md @@ -0,0 +1,58 @@ +# app-template + +This README outlines the details of collaborating on this Ember application. +A short introduction of this app could easily go here. + +## Prerequisites + +You will need the following things properly installed on your computer. + +* [Git](https://git-scm.com/) +* [Node.js](https://nodejs.org/) +* [Yarn](https://yarnpkg.com/) +* [Ember CLI](https://cli.emberjs.com/release/) +* [Google Chrome](https://google.com/chrome/) + +## Installation + +* `git clone ` this repository +* `cd app-template` +* `yarn install` + +## Running / Development + +* `ember serve` +* Visit your app at [http://localhost:4200](http://localhost:4200). +* Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests). + +### Code Generators + +Make use of the many generators for code, try `ember help generate` for more details + +### Running Tests + +* `ember test` +* `ember test --server` + +### Linting + +* `yarn lint:hbs` +* `yarn lint:js` +* `yarn lint:js --fix` + +### Building + +* `ember build` (development) +* `ember build --environment production` (production) + +### Deploying + +Specify what it takes to deploy your app. + +## Further Reading / Useful Links + +* [ember.js](https://emberjs.com/) +* [ember-cli](https://cli.emberjs.com/release/) +* Development Browser Extensions + * [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi) + * [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/) diff --git a/tests/app-template-minimal/app/app.js b/tests/app-template-minimal/app/app.js new file mode 100644 index 000000000..06f8ae0a9 --- /dev/null +++ b/tests/app-template-minimal/app/app.js @@ -0,0 +1,10 @@ +import Application from '@ember/application'; +import Resolver from 'ember-resolver'; +import config from './config/environment'; +import compatModules from '@embroider/virtual/compat-modules'; + +export default class App extends Application { + modulePrefix = config.modulePrefix; + podModulePrefix = config.podModulePrefix; + Resolver = Resolver.withModules(compatModules); +} diff --git a/tests/app-template-minimal/app/config/environment.js b/tests/app-template-minimal/app/config/environment.js new file mode 100644 index 000000000..b05932371 --- /dev/null +++ b/tests/app-template-minimal/app/config/environment.js @@ -0,0 +1,25 @@ +const ENV = { + modulePrefix: 'app-template-minimal', + environment: import.meta.env.DEV ? 'development' : 'production', + rootURL: '/', + locationType: 'history', + EmberENV: { + EXTEND_PROTOTYPES: false, + FEATURES: { + // Here you can enable experimental features on an ember canary build + // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true + }, + }, + APP: { + // Here you can pass flags/options to your application instance + // when it is created + }, +}; + +export default ENV; + +export function enterTestMode() { + ENV.locationType = 'none'; + ENV.APP.rootElement = '#ember-testing'; + ENV.APP.autoboot = false; +} diff --git a/tests/app-template-minimal/app/router.js b/tests/app-template-minimal/app/router.js new file mode 100644 index 000000000..9f79253ba --- /dev/null +++ b/tests/app-template-minimal/app/router.js @@ -0,0 +1,9 @@ +import EmberRouter from '@ember/routing/router'; +import config from 'app-template-minimal/config/environment'; + +export default class Router extends EmberRouter { + location = config.locationType; + rootURL = config.rootURL; +} + +Router.map(function () {}); diff --git a/tests/app-template-minimal/app/styles/app.css b/tests/app-template-minimal/app/styles/app.css new file mode 100644 index 000000000..2763afa4c --- /dev/null +++ b/tests/app-template-minimal/app/styles/app.css @@ -0,0 +1 @@ +/* Ember supports plain CSS out of the box. More info: https://cli.emberjs.com/release/advanced-use/stylesheets/ */ diff --git a/tests/app-template-minimal/app/templates/application.gjs b/tests/app-template-minimal/app/templates/application.gjs new file mode 100644 index 000000000..164350f41 --- /dev/null +++ b/tests/app-template-minimal/app/templates/application.gjs @@ -0,0 +1,6 @@ + diff --git a/tests/app-template-minimal/babel.config.cjs b/tests/app-template-minimal/babel.config.cjs new file mode 100644 index 000000000..6328a8aab --- /dev/null +++ b/tests/app-template-minimal/babel.config.cjs @@ -0,0 +1,39 @@ +const { babelCompatSupport, templateCompatSupport } = require('@embroider/compat/babel'); + +module.exports = { + plugins: [ + [ + 'babel-plugin-ember-template-compilation', + { + compilerPath: 'ember-source/dist/ember-template-compiler.js', + enableLegacyModules: [ + 'ember-cli-htmlbars', + 'ember-cli-htmlbars-inline-precompile', + 'htmlbars-inline-precompile', + ], + transforms: [...templateCompatSupport()], + }, + ], + [ + 'module:decorator-transforms', + { + runtime: { + import: require.resolve('decorator-transforms/runtime-esm'), + }, + }, + ], + [ + '@babel/plugin-transform-runtime', + { + absoluteRuntime: __dirname, + useESModules: true, + regenerator: false, + }, + ], + ...babelCompatSupport(), + ], + + generatorOpts: { + compact: false, + }, +}; diff --git a/tests/app-template-minimal/config/ember-cli-update.json b/tests/app-template-minimal/config/ember-cli-update.json new file mode 100644 index 000000000..592c332da --- /dev/null +++ b/tests/app-template-minimal/config/ember-cli-update.json @@ -0,0 +1,20 @@ +{ + "schemaVersion": "1.0.0", + "packages": [ + { + "name": "ember-cli", + "version": "5.0.0", + "blueprints": [ + { + "name": "app", + "outputRepo": "https://github.com/ember-cli/ember-new-output", + "codemodsSource": "ember-app-codemods-manifest@1", + "isBaseBlueprint": true, + "options": [ + "--yarn" + ] + } + ] + } + ] +} diff --git a/tests/app-template-minimal/config/environment.js b/tests/app-template-minimal/config/environment.js new file mode 100644 index 000000000..fa79c86da --- /dev/null +++ b/tests/app-template-minimal/config/environment.js @@ -0,0 +1,48 @@ +'use strict'; + +module.exports = function (environment) { + const ENV = { + modulePrefix: 'app-template-minimal', + environment, + rootURL: '/', + locationType: 'history', + EmberENV: { + EXTEND_PROTOTYPES: false, + FEATURES: { + // Here you can enable experimental features on an ember canary build + // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true + }, + }, + + APP: { + // Here you can pass flags/options to your application instance + // when it is created + }, + }; + + if (environment === 'development') { + // ENV.APP.LOG_RESOLVER = true; + // ENV.APP.LOG_ACTIVE_GENERATION = true; + // ENV.APP.LOG_TRANSITIONS = true; + // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; + // ENV.APP.LOG_VIEW_LOOKUPS = true; + } + + if (environment === 'test') { + // Testem prefers this... + ENV.locationType = 'none'; + + // keep test console output quieter + ENV.APP.LOG_ACTIVE_GENERATION = false; + ENV.APP.LOG_VIEW_LOOKUPS = false; + + ENV.APP.rootElement = '#ember-testing'; + ENV.APP.autoboot = false; + } + + if (environment === 'production') { + // here you can enable a production-specific feature + } + + return ENV; +}; diff --git a/tests/app-template-minimal/config/optional-features.json b/tests/app-template-minimal/config/optional-features.json new file mode 100644 index 000000000..b26286e2e --- /dev/null +++ b/tests/app-template-minimal/config/optional-features.json @@ -0,0 +1,6 @@ +{ + "application-template-wrapper": false, + "default-async-observers": true, + "jquery-integration": false, + "template-only-glimmer-components": true +} diff --git a/tests/app-template-minimal/config/targets.js b/tests/app-template-minimal/config/targets.js new file mode 100644 index 000000000..9f6cc6396 --- /dev/null +++ b/tests/app-template-minimal/config/targets.js @@ -0,0 +1,7 @@ +'use strict'; + +const browsers = ['last 1 Chrome versions', 'last 1 Firefox versions', 'last 1 Safari versions']; + +module.exports = { + browsers, +}; diff --git a/tests/app-template-minimal/index.html b/tests/app-template-minimal/index.html new file mode 100644 index 000000000..00c884a4a --- /dev/null +++ b/tests/app-template-minimal/index.html @@ -0,0 +1,17 @@ + + + + + AppTemplateMinimal + + + + + + + diff --git a/tests/app-template-minimal/package.json b/tests/app-template-minimal/package.json new file mode 100644 index 000000000..ebf1cf496 --- /dev/null +++ b/tests/app-template-minimal/package.json @@ -0,0 +1,56 @@ +{ + "name": "app-template-minimal", + "version": "0.0.0", + "private": true, + "description": "Small description for app-template goes here", + "repository": "", + "license": "MIT", + "author": "", + "exports": { + "./tests/*": "./tests/*", + "./*": "./app/*" + }, + "scripts": { + "build": "vite build", + "start": "vite", + "test": "vite build --mode test && ember test --path dist", + "test:ember": "vite build --mode test && ember test --path dist" + }, + "devDependencies": { + "@babel/core": "^7.19.3", + "@babel/plugin-transform-runtime": "^7.25.4", + "@babel/runtime": "^7.25.6", + "@ember/optional-features": "^2.0.0", + "@ember/string": "^3.1.1", + "@ember/test-helpers": "^4.0.4", + "@embroider/compat": "workspace:*", + "@embroider/core": "workspace:*", + "@embroider/router": "workspace:*", + "@embroider/test-setup": "workspace:*", + "@embroider/vite": "workspace:*", + "@glimmer/component": "^2.0.0", + "@glimmer/tracking": "^1.1.2", + "@rollup/plugin-babel": "^5.3.1", + "babel-plugin-ember-template-compilation": "^2.3.0", + "decorator-transforms": "^2.0.0", + "ember-modifier": "^4.1.0", + "ember-page-title": "^7.0.0", + "ember-qunit": "^9.0.1", + "ember-resolver": "^13.1.0", + "ember-source": "6.3.0-alpha.3", + "qunit": "^2.19.4", + "qunit-dom": "^3.4.0", + "tracked-built-ins": "^3.1.1", + "vite": "^5.0.9" + }, + "engines": { + "node": ">= 18" + }, + "ember": { + "edition": "octane" + }, + "ember-addon": { + "type": "app", + "version": 2 + } +} diff --git a/tests/app-template-minimal/public/robots.txt b/tests/app-template-minimal/public/robots.txt new file mode 100644 index 000000000..f5916452e --- /dev/null +++ b/tests/app-template-minimal/public/robots.txt @@ -0,0 +1,3 @@ +# http://www.robotstxt.org +User-agent: * +Disallow: diff --git a/tests/app-template-minimal/testem.js b/tests/app-template-minimal/testem.js new file mode 100644 index 000000000..8fe868923 --- /dev/null +++ b/tests/app-template-minimal/testem.js @@ -0,0 +1,26 @@ +'use strict'; + +if (typeof module !== 'undefined') { + module.exports = { + test_page: 'tests/index.html?hidepassed', + disable_watching: true, + launch_in_ci: ['Chrome'], + launch_in_dev: ['Chrome'], + browser_start_timeout: 120, + browser_args: { + Chrome: { + ci: [ + // --no-sandbox is needed when running Chrome inside a container + process.env.CI ? '--no-sandbox' : null, + '--disable-gpu', + '--headless', + '--disable-dev-shm-usage', + '--disable-software-rasterizer', + '--mute-audio', + '--remote-debugging-port=0', + '--window-size=1440,900', + ].filter(Boolean), + }, + }, + }; +} diff --git a/tests/app-template-minimal/tests/helpers/index.js b/tests/app-template-minimal/tests/helpers/index.js new file mode 100644 index 000000000..7f70de80f --- /dev/null +++ b/tests/app-template-minimal/tests/helpers/index.js @@ -0,0 +1,42 @@ +import { + setupApplicationTest as upstreamSetupApplicationTest, + setupRenderingTest as upstreamSetupRenderingTest, + setupTest as upstreamSetupTest, +} from 'ember-qunit'; + +// This file exists to provide wrappers around ember-qunit's / ember-mocha's +// test setup functions. This way, you can easily extend the setup that is +// needed per test type. + +function setupApplicationTest(hooks, options) { + upstreamSetupApplicationTest(hooks, options); + + // Additional setup for application tests can be done here. + // + // For example, if you need an authenticated session for each + // application test, you could do: + // + // hooks.beforeEach(async function () { + // await authenticateSession(); // ember-simple-auth + // }); + // + // This is also a good place to call test setup functions coming + // from other addons: + // + // setupIntl(hooks); // ember-intl + // setupMirage(hooks); // ember-cli-mirage +} + +function setupRenderingTest(hooks, options) { + upstreamSetupRenderingTest(hooks, options); + + // Additional setup for rendering tests can be done here. +} + +function setupTest(hooks, options) { + upstreamSetupTest(hooks, options); + + // Additional setup for unit tests can be done here. +} + +export { setupApplicationTest, setupRenderingTest, setupTest }; diff --git a/tests/app-template-minimal/tests/index.html b/tests/app-template-minimal/tests/index.html new file mode 100644 index 000000000..b7ecca6f1 --- /dev/null +++ b/tests/app-template-minimal/tests/index.html @@ -0,0 +1,28 @@ + + + + + AppTemplate Tests + + + + +
+
+
+
+
+
+ + + + + + + diff --git a/tests/app-template-minimal/tests/integration/.gitkeep b/tests/app-template-minimal/tests/integration/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/tests/app-template-minimal/tests/test-helper.js b/tests/app-template-minimal/tests/test-helper.js new file mode 100644 index 000000000..c8443742c --- /dev/null +++ b/tests/app-template-minimal/tests/test-helper.js @@ -0,0 +1,15 @@ +import Application from 'app-template-minimal/app'; +import config from 'app-template-minimal/config/environment'; +import * as QUnit from 'qunit'; +import { setApplication } from '@ember/test-helpers'; +import { setup } from 'qunit-dom'; +import { start as qunitStart, setupEmberOnerrorValidation } from 'ember-qunit'; +import { enterTestMode } from 'app-template-minimal/config/environment'; + +export function start() { + enterTestMode(); + setApplication(Application.create(config.APP)); + setup(QUnit.assert); + setupEmberOnerrorValidation(); + qunitStart(); +} diff --git a/tests/app-template-minimal/tests/unit/.gitkeep b/tests/app-template-minimal/tests/unit/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/tests/app-template-minimal/vite.config.mjs b/tests/app-template-minimal/vite.config.mjs new file mode 100644 index 000000000..c3cda93f0 --- /dev/null +++ b/tests/app-template-minimal/vite.config.mjs @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite'; +import { extensions, ember, hbs } from '@embroider/vite'; +import { babel } from '@rollup/plugin-babel'; + +export default defineConfig({ + plugins: [ + hbs(), + ember(), + // extra plugins here + babel({ + babelHelpers: 'runtime', + extensions, + }), + ], +}); diff --git a/tests/scenarios/minimal-app-test.ts b/tests/scenarios/minimal-app-test.ts new file mode 100644 index 000000000..af536c35a --- /dev/null +++ b/tests/scenarios/minimal-app-test.ts @@ -0,0 +1,211 @@ +import { minimalAppScenarios } from './scenarios'; +import type { PreparedApp } from 'scenario-tester'; +import QUnit from 'qunit'; +import fetch from 'node-fetch'; +import CommandWatcher from './helpers/command-watcher'; +import { setupAuditTest } from '@embroider/test-support/audit-assertions'; + +const { module: Qmodule, test } = QUnit; + +/** + * We use canary because this test depends on v6.3.0-alpha-3 or above. We should update this accordingly as the release + * train progresses + */ +minimalAppScenarios + .only('canary') + .map('minimal-app', app => { + // These are for a custom testem setup that will let us do runtime tests + // inside `vite dev` rather than only against the output of `vite build`. + // + // Most apps should run their CI against `vite build`, as that's closer to + // production. And they can do development tests directly in brower against + // `vite dev` at `/tests/index.html`. We're doing `vite dev` in CI here + // because we're testing the development experience itself. + app.linkDevDependency('testem', { baseDir: __dirname }); + app.linkDevDependency('@embroider/test-support', { baseDir: __dirname }); + + app.linkDevDependency('ember-page-title', { baseDir: __dirname }); + app.linkDevDependency('ember-welcome-page', { baseDir: __dirname }); + app.mergeFiles({ + 'testem-dev.js': ` + 'use strict'; + + module.exports = { + test_page: 'tests?hidepassed', + disable_watching: true, + launch_in_ci: ['Chrome'], + launch_in_dev: ['Chrome'], + browser_start_timeout: 120, + browser_args: { + Chrome: { + ci: [ + // --no-sandbox is needed when running Chrome inside a container + process.env.CI ? '--no-sandbox' : null, + '--headless', + '--disable-dev-shm-usage', + '--disable-software-rasterizer', + '--mute-audio', + '--remote-debugging-port=0', + '--window-size=1440,900', + ].filter(Boolean), + }, + }, + middleware: [ + require('@embroider/test-support/testem-proxy').testemProxy('http://localhost:4200') + ], + }; + `, + + app: { + components: { + 'fancy-component.gjs': ` + import Component from '@glimmer/component'; + export default class extends Component { + message = "fancy gts"; + + } + `, + 'fancy-button.gjs': ``, + }, + templates: { + 'application.gjs': ` + import pageTitle from 'ember-page-title/helpers/page-title'; + + + `, + 'index.gjs': ` + import FancyButton from '../components/fancy-button'; + import FancyComponent from 'app-template-minimal/components/fancy-component'; + import WelcomePage from 'ember-welcome-page/components/welcome-page' + + + `, + }, + lib: { + 'app-lib-one.js': ` + globalThis.appLibOneLoaded = (globalThis.appLibOneLoaded ?? 0) + 1; + export default function() { return 'app-lib-one'; } + `, + 'app-lib-two.js': ` + globalThis.appLibTwoLoaded = (globalThis.appLibTwoLoaded ?? 0) + 1; + export default function() { return 'app-lib-two'; } + `, + }, + }, + tests: { + integration: { + components: { + 'fancy-component-test.gjs': ` + import { module, test } from 'qunit'; + import { setupRenderingTest } from 'app-template-minimal/tests/helpers'; + import { render } from '@ember/test-helpers'; + import FancyComponent from 'app-template-minimal/components/fancy-component'; + + module('Integration | Component | fancy-component', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(); + + assert.dom().hasText(''); + + // Template block usage: + await render(); + + assert.dom().hasText('template block text'); + }); + }); + `, + }, + }, + }, + }); + }) + .forEachScenario(scenario => { + Qmodule(scenario.name, function (hooks) { + let app: PreparedApp; + let server: CommandWatcher; + let appURL: string; + + hooks.before(async () => { + app = await scenario.prepare(); + }); + + Qmodule('vite dev', function (hooks) { + hooks.before(async () => { + server = CommandWatcher.launch('vite', ['--clearScreen', 'false'], { cwd: app.dir }); + [, appURL] = await server.waitFor(/Local:\s+(https?:\/\/.*)\//g); + }); + + let expectAudit = setupAuditTest(hooks, () => ({ + appURL, + startingFrom: ['index.html'], + fetch: fetch as unknown as typeof globalThis.fetch, + })); + + hooks.after(async () => { + await server?.shutdown(); + }); + + test(`dep optimization of a v2 addon`, async function (assert) { + expectAudit + .module('./index.html') + .resolves(/\/index.html.*/) // in-html app-boot script + .toModule() + .resolves(/\/app\.js.*/) + .toModule() + .resolves(/\/app\/templates\/application.gjs.*/) // page-title is being imported by this template so we should go through here + .toModule() + .withContents((src, imports) => { + let pageTitleImports = imports.filter(imp => /page-title/.test(imp.source)); + assert.ok(pageTitleImports.length > 0, `should have at least one import from page-title. Source: ${src}`); + for (let pageTitleImport of pageTitleImports) { + assert.ok( + /\.vite\/deps/.test(pageTitleImport.source), + `expected ${pageTitleImport.source} to be in vite deps` + ); + } + return true; + }); + }); + + test('run test suite against vite dev', async function (assert) { + let result = await app.execute('pnpm testem --file testem-dev.js ci'); + assert.equal(result.exitCode, 0, result.output); + }); + }); + + Qmodule('vite optimize', function () { + test('vite optimize should succeed', async function (assert) { + let result = await app.execute('pnpm vite optimize --force'); + + assert.equal(result.exitCode, 0, result.output); + }); + }); + + Qmodule('vite build', function () { + test('run tests suite against vite build output', async function (assert) { + let result = await app.execute('pnpm vite build --mode test'); + assert.equal(result.exitCode, 0, result.output); + result = await app.execute('pnpm ember test --path dist'); + assert.equal(result.exitCode, 0, result.output); + }); + }); + }); + }); diff --git a/tests/scenarios/scenarios.ts b/tests/scenarios/scenarios.ts index 4ee261f96..1bf2c12f6 100644 --- a/tests/scenarios/scenarios.ts +++ b/tests/scenarios/scenarios.ts @@ -136,6 +136,10 @@ export function baseTSApp() { return Project.fromDir(dirname(require.resolve('../ts-app-template/package.json')), { linkDevDeps: true }); } +export function baseMinimalApp() { + return Project.fromDir(dirname(require.resolve('../app-template-minimal/package.json')), { linkDevDeps: true }); +} + export function baseTSAppClassic() { return Project.fromDir(dirname(require.resolve('../ts-app-template-classic/package.json')), { linkDevDeps: true }); } @@ -153,6 +157,8 @@ export const wideAppScenarios = fullSupportMatrix(Scenarios.fromProject(baseApp) // support them. export const tsAppScenarios = supportMatrix(Scenarios.fromProject(baseTSApp)).skip('lts_3_28').skip('lts_4_4'); +export const minimalAppScenarios = supportMatrix(Scenarios.fromProject(baseMinimalApp)); + export const tsAppClassicScenarios = supportMatrix(Scenarios.fromProject(baseTSAppClassic)) .skip('lts_3_28') .skip('lts_4_4');