Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion packages/@ember/-internals/glimmer/lib/syntax/outlet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { InternalOwner } from '@ember/-internals/owner';
import { assert } from '@ember/debug';
import { getEngineParent } from '@ember/engine/parent';
import { DEBUG } from '@glimmer/env';
import type {
CapturedArguments,
Expand Down Expand Up @@ -57,8 +58,18 @@ export const outletHelper = internalHelper(
scope
);

let outletStateRef = scope.get('outletState') as Reference<OutletState | undefined>;

let isNonRoutableEngine =
getEngineParent(owner as any) !== undefined && (owner as any).routable !== true;

assert(
'{{outlet}} may only be used in route templates. It cannot be used in component templates or non-routable engine templates.',
!isNonRoutableEngine && valueForRef(outletStateRef) !== undefined
);

let outletRef = createComputeRef(() => {
let state = valueForRef(scope.get('outletState') as Reference<OutletState | undefined>);
let state = valueForRef(outletStateRef);
return state?.outlets?.main;
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
moduleFor,
ApplicationTestCase,
ModuleBasedTestResolver,
RenderingTestCase,
} from 'internal-test-helpers';
import { DEBUG } from '@glimmer/env';
import Engine from '@ember/engine';
import Controller from '@ember/controller';
import { precompileTemplate } from '@ember/template-compilation';

const OUTLET_ASSERTION =
'{{outlet}} may only be used in route templates. It cannot be used in component templates or non-routable engine templates.';

moduleFor(
'{{outlet}} assertion tests - component context',
class extends RenderingTestCase {
['@test it asserts when {{outlet}} is used in a component template'](assert) {
if (!DEBUG) {
assert.ok(true, 'Assertions disabled in production builds.');
return;
}

this.add('template:components/foo-bar', precompileTemplate('{{outlet}}'));

assert.throwsAssertion(() => {
this.render('<FooBar />');
}, OUTLET_ASSERTION);
}
}
);

moduleFor(
'{{outlet}} assertion tests - route context',
class extends ApplicationTestCase {
['@test valid {{outlet}} in route templates continues to work'](assert) {
this.add('template:application', precompileTemplate('{{outlet}}'));
this.add('template:index', precompileTemplate('Hello from route template'));

return this.visit('/').then(() => {
assert.strictEqual(this.element.textContent.trim(), 'Hello from route template');
});
}
}
);

moduleFor(
'{{outlet}} assertion tests - non-routable engine',
class extends ApplicationTestCase {
constructor() {
super(...arguments);

let engineRegistrations = (this.engineRegistrations = {});

this.add(
'engine:chat',
class extends Engine {
router = null;
Resolver = ModuleBasedTestResolver;

init() {
super.init(...arguments);

Object.keys(engineRegistrations).forEach((fullName) => {
this.register(fullName, engineRegistrations[fullName]);
});
}
}
);

this.add('template:index', precompileTemplate('{{mount "chat"}}'));
}

['@test it asserts when {{outlet}} is used in a non-routable engine'](assert) {
if (!DEBUG) {
assert.ok(true, 'Assertions disabled in production builds.');
return;
}

this.engineRegistrations['template:application'] = precompileTemplate('{{outlet}}');
this.engineRegistrations['controller:application'] = class extends Controller {};

return assert.rejectsAssertion(this.visit('/'), OUTLET_ASSERTION);
}
}
);
Loading