diff --git a/tests/scenarios/vite-internals-test.ts b/tests/scenarios/vite-internals-test.ts
index e7c6f94438..f1db3bbf01 100644
--- a/tests/scenarios/vite-internals-test.ts
+++ b/tests/scenarios/vite-internals-test.ts
@@ -292,3 +292,328 @@ tsAppScenarios
});
});
});
+
+tsAppScenarios
+ .only('release')
+ .map('vite-internals-with-base-path', 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: 'sub-dir/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,
+ '--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')
+ ],
+ };
+ `,
+
+ 'testem.js': `
+ 'use strict';
+
+ if (typeof module !== 'undefined') {
+ module.exports = {
+ test_page: 'sub-dir/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,
+ '--headless',
+ '--disable-dev-shm-usage',
+ '--disable-software-rasterizer',
+ '--mute-audio',
+ '--remote-debugging-port=0',
+ '--window-size=1440,900',
+ ].filter(Boolean),
+ },
+ },
+ };
+ }
+
+ `,
+
+ config: {
+ 'environment.js': `
+ 'use strict';
+
+ module.exports = function (environment) {
+ const ENV = {
+ modulePrefix: 'app-template',
+ environment,
+ rootURL: '/sub-dir/',
+ 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;
+ };
+ `,
+ },
+
+ app: {
+ components: {
+ 'alpha.js': `
+ import Component from '@glimmer/component';
+ export default class extends Component {
+ message = "alpha";
+ }
+ `,
+ 'alpha.hbs': `
+
{{this.message}}
+
+ `,
+ 'gamma.js': `
+ globalThis.gammaLoaded = (globalThis.gammaLoaded ?? 0) + 1;
+ import Component from '@glimmer/component';
+ export default class extends Component {
+ message = "gamma";
+ }
+ `,
+ 'gamma.hbs': `
+ {{this.message}}
+ `,
+ 'epsilon.hbs': `Epsilon
`,
+ 'fancy-button.hbs': `I'm fancy
`,
+ 'delta.js': `
+ import Component from '@glimmer/component';
+ export default class extends Component {
+ message = "delta";
+ }
+ `,
+ },
+ templates: {
+ 'application.hbs': `
+ {{page-title "MyApp"}}
+ {{outlet}}
+ `,
+ 'index.hbs': `
+
+
+ `,
+ },
+ 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: {
+ 'example-test.js': `
+ import { module, test } from 'qunit';
+ import { setupRenderingTest } from 'app-template/tests/helpers';
+ import { render } from '@ember/test-helpers';
+ import { hbs } from 'ember-cli-htmlbars';
+ import { appLibOne as libOneViaAddon, appLibTwo as libTwoViaAddon } from 'app-template/v1-example-addon';
+ import appLibOne from 'app-template/lib/app-lib-one';
+ import appLibTwo from 'app-template/lib/app-lib-two';
+
+ module('Integration | Component | example', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('nesting between app and addon components', async function (assert) {
+ await render(hbs\`\`);
+
+ // Alpha in the app...
+ assert.dom('.alpha').hasText('alpha');
+
+ // calls beta in the addon...
+ assert.dom('.beta').hasText('beta');
+
+ // which calls gamma in the app
+ // while the app itself also directly galls Gamma.
+ // We want to ensure that we get the same copy of Gamma via both paths.
+ assert.dom('.gamma').exists({ count: 2 })
+
+ assert.strictEqual(globalThis.gammaLoaded, 1, 'gamma only evaluated once');
+ });
+
+ test("addon depends on an app's hbs-only component", async function (assert) {
+ await render(hbs\`\`);
+ assert.dom('.zeta').hasText('Zeta');
+ assert.dom('.epsilon').hasText('Epsilon');
+ });
+
+ test("paired component between app and addon", async function (assert) {
+ await render(hbs\`\`);
+ assert.dom('.delta').hasText('delta');
+ });
+
+ test("addon depends on an app's module via relative import", async function (assert) {
+ assert.strictEqual(appLibOne(), libOneViaAddon(), 'lib one works the same');
+ assert.strictEqual(globalThis.appLibOneLoaded, 1, 'app lib one loaded once');
+ });
+
+ test("addon depends on an app's module via named import", async function (assert) {
+ assert.strictEqual(appLibTwo(), libTwoViaAddon(), 'lib two works the same');
+ assert.strictEqual(globalThis.appLibTwoLoaded, 1, 'app lib two loaded once');
+ });
+ });
+ `,
+ },
+ },
+ },
+ });
+
+ let v1ExampleAddon = baseAddon();
+ v1ExampleAddon.name = 'v1-example-addon';
+ v1ExampleAddon.mergeFiles({
+ addon: {
+ components: {
+ 'beta.js': `
+ import Component from '@glimmer/component';
+ export default class extends Component {
+ message = "beta";
+ }
+ `,
+ 'beta.hbs': `
+ {{this.message}}
+
+ `,
+ 'zeta.hbs': `
+ Zeta
+
+ `,
+ },
+ },
+ app: {
+ 'v1-example-addon.js': `
+ import appLibOne from './lib/app-lib-one';
+ import appLibTwo from 'app-template/lib/app-lib-two';
+ export { appLibOne, appLibTwo };
+ `,
+ templates: {
+ components: {
+ 'delta.hbs': `
+ delta
+ `,
+ },
+ },
+ components: {
+ 'beta.js': `
+ export { default } from 'v1-example-addon/components/beta';
+ `,
+ 'zeta.js': `
+ export { default } from 'v1-example-addon/components/zeta';
+ `,
+ },
+ },
+ });
+ app.addDevDependency(v1ExampleAddon);
+ })
+ .forEachScenario(scenario => {
+ Qmodule(scenario.name, function (hooks) {
+ let app: PreparedApp;
+ let server: CommandWatcher;
+
+ hooks.before(async () => {
+ app = await scenario.prepare();
+ });
+
+ Qmodule('vite dev', function (hooks) {
+ hooks.before(async () => {
+ server = CommandWatcher.launch('vite', ['--clearScreen', 'false', '--base', '/sub-dir/'], { cwd: app.dir });
+ });
+
+ hooks.after(async () => {
+ await server?.shutdown();
+ });
+
+ 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 build', function (hooks) {
+ hooks.before(async () => {
+ await app.execute('pnpm vite build --mode test --base /sub-dir/');
+ await app.execute('mkdir ./sub-dir');
+ await app.execute('mv dist/* ./sub-dir');
+ await app.execute('mkdir ./dist/sub-dir');
+ await app.execute('mv sub-dir/* ./dist/sub-dir');
+ });
+
+ test('run test suite against vite dist with sub-dir', async function (assert) {
+ let result = await app.execute('ember test --path dist');
+ assert.equal(result.exitCode, 0, result.output);
+ });
+ });
+ });
+ });