Skip to content

Commit

Permalink
Merge pull request #20577 from emberjs/restore-deprecate-outlet-templ…
Browse files Browse the repository at this point in the history
…ate-factory

[BUGFIX canary] Restore/deprecate outlet `TemplateFactory` support
  • Loading branch information
chancancode committed Nov 18, 2023
2 parents 704447b + 3384cc1 commit 196eb4c
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 1 deletion.
37 changes: 36 additions & 1 deletion packages/@ember/-internals/glimmer/lib/syntax/outlet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { InternalOwner } from '@ember/-internals/owner';
import { assert } from '@ember/debug';
import { assert, deprecate } from '@ember/debug';
import { DEBUG } from '@glimmer/env';
import type { CapturedArguments, DynamicScope } from '@glimmer/interfaces';
import { CurriedType } from '@glimmer/vm';
Expand All @@ -17,6 +17,7 @@ import type { OutletDefinitionState } from '../component-managers/outlet';
import { OutletComponentDefinition } from '../component-managers/outlet';
import { internalHelper } from '../helpers/internal-helper';
import type { OutletState } from '../utils/outlet';
import { isTemplateFactory } from '../template';

/**
The `{{outlet}}` helper lets you specify where a child route will render in
Expand Down Expand Up @@ -120,6 +121,40 @@ function stateFor(
let template = render.template;
if (template === undefined) return null;

if (isTemplateFactory(template)) {
template = template(render.owner);

if (DEBUG) {
let message =
'The `template` property of `OutletState` should be a ' +
'`Template` rather than a `TemplateFactory`. This is known to be a ' +
"problem in older versions of `@ember/test-helpers`. If you haven't " +
'done so already, try upgrading to the latest version.\n\n';

if (template.result === 'ok' && typeof template.moduleName === 'string') {
message +=
'The offending template has a moduleName `' +
template.moduleName +
'`, which might be helpful for identifying ' +
'source of this issue.\n\n';
}

message +=
'Please note that `OutletState` is a private API in Ember.js ' +
"and not meant to be used outside of the framework's internal code.";

deprecate(message, false, {
id: 'outlet-state-template-factory',
until: '5.9.0',
for: 'ember-source',
since: {
available: '5.6.0',
enabled: '5.6.0',
},
});
}
}

return {
ref,
name: render.name,
Expand Down
120 changes: 120 additions & 0 deletions packages/ember/tests/ember-test-helpers-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,124 @@ module('@ember/test-helpers emulation test', function () {
});
});
});

module('v1.6.0', function () {
let EMPTY_TEMPLATE = compile('');

function settled() {
return new Promise(function (resolve) {
let watcher = setInterval(() => {
if (_getCurrentRunLoop() || _hasScheduledTimers()) {
return;
}

// Stop polling
clearInterval(watcher);

// Synchronously resolve the promise
run(null, resolve);
}, 10);
});
}

async function setupContext(context) {
// condensed version of https://github.com/emberjs/ember-test-helpers/blob/v1.6.0/addon-test-support/%40ember/test-helpers/build-owner.ts#L38
// without support for "custom resolver"
await context.application.boot();

context.owner = await context.application.buildInstance().boot();
}

function setupRenderingContext(context) {
let { owner } = context;
let OutletView = owner.factoryFor('view:-outlet');
let environment = owner.lookup('-environment:main');
let outletTemplateFactory = owner.lookup('template:-outlet');
let toplevelView = OutletView.create({ environment, template: outletTemplateFactory });

owner.register('-top-level-view:main', {
create() {
return toplevelView;
},
});

// initially render a simple empty template
return render(EMPTY_TEMPLATE, context).then(() => {
let rootElement = document.querySelector(owner.rootElement);
run(toplevelView, 'appendTo', rootElement);

context.element = rootElement;

return settled();
});
}

let templateId = 0;
function render(template, context) {
let { owner } = context;
let toplevelView = owner.lookup('-top-level-view:main');
templateId += 1;
let templateFullName = `template:-undertest-${templateId}`;
owner.register(templateFullName, template);

let outletState = {
render: {
owner,
into: undefined,
outlet: 'main',
name: 'application',
controller: undefined,
ViewClass: undefined,
template: owner.lookup('template:-outlet'),
},

outlets: {
main: {
render: {
owner,
into: undefined,
outlet: 'main',
name: 'index',
controller: context,
ViewClass: undefined,
template: owner.lookup(templateFullName),
outlets: {},
},
outlets: {},
},
},
};
toplevelView.setOutletState(outletState);

return settled();
}

module('setupRenderingContext', function (hooks) {
hooks.beforeEach(async function () {
expectDeprecation(
/The `template` property of `OutletState` should be a `Template` rather than a `TemplateFactory`/
);

this.application = Application.create({
rootElement: '#qunit-fixture',
autoboot: false,
Resolver: ModuleBasedTestResolver,
});

await setupContext(this);
await setupRenderingContext(this);
});

hooks.afterEach(function () {
run(this.owner, 'destroy');
run(this.application, 'destroy');
});

test('it basically works', async function (assert) {
await render(compile('Hi!'), this);

assert.equal(this.element.textContent, 'Hi!');
});
});
});
});

0 comments on commit 196eb4c

Please sign in to comment.