Skip to content

Commit

Permalink
backward compatibility + more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cafreeman committed Mar 23, 2022
1 parent d0767e9 commit 4a3814e
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,35 @@ import type { InternalComponentManager } from '@glimmer/interfaces';

let getComponentManager: (
definition: object,
isOptional?: boolean
owner: object
) => InternalComponentManager | null;

if (macroCondition(dependencySatisfies('ember-source', '>=3.27.0'))) {
getComponentManager =
let _getComponentManager =
//@ts-ignore
importSync('@glimmer/manager').getInternalComponentManager;

getComponentManager = (definition: object, owner: object) => {
return _getComponentManager(definition, true);
};
} else if (
macroCondition(dependencySatisfies('ember-source', '>=3.25.0-beta.1'))
) {
let _getComponentManager = (Ember as any).__loader.require(
'@glimmer/manager'
).getInternalComponentManager;

getComponentManager = (definition: object, owner: object) => {
return _getComponentManager(definition, true);
};
} else {
getComponentManager = (Ember as any).__loader.require(
let _getComponentManager = (Ember as any).__loader.require(
'@glimmer/runtime'
).getComponentManager;

getComponentManager = (definition: object, owner: object) => {
return _getComponentManager(owner, definition);
};
}

export default getComponentManager;
25 changes: 25 additions & 0 deletions addon-test-support/@ember/test-helpers/-internal/is-component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { macroCondition, dependencySatisfies } from '@embroider/macros';

import getComponentManager from './get-component-manager';

/**
*
* @private
* @param {Object} maybeComponent blah
* @param {Object} owner blah
* @returns {boolean} a boolean
*/
function isComponent(maybeComponent: any, owner: object): boolean {
if (macroCondition(dependencySatisfies('ember-source', '>=3.25.0-beta.1'))) {
return !!getComponentManager(maybeComponent, owner);
} else {
return (
!!getComponentManager(maybeComponent, owner) ||
['@ember/component', '@ember/component/template-only'].includes(
maybeComponent.toString()
)
);
}
}

export default isComponent;
36 changes: 28 additions & 8 deletions addon-test-support/@ember/test-helpers/setup-rendering-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import getTestMetadata, { ITestMetadata } from './test-metadata';
import { deprecate } from '@ember/debug';
import { runHooks } from './-internal/helper-hooks';
import hasEmberVersion from './has-ember-version';
import getComponentManager from './-internal/get-component-manager';
import isComponent from './-internal/is-component';
import { macroCondition, dependencySatisfies } from '@embroider/macros';
import type { ComponentInstance } from '@glimmer/interfaces';

const OUTLET_TEMPLATE = hbs`{{outlet}}`;
Expand Down Expand Up @@ -101,8 +102,6 @@ export function render(
throw new Error('you must pass a template to `render()`');
}

const wasPassedComponent = !!getComponentManager(templateOrComponent, true);

return Promise.resolve()
.then(() => runHooks('render', 'start'))
.then(() => {
Expand All @@ -120,11 +119,32 @@ export function render(
let OutletTemplate = lookupOutletTemplate(owner);
let ownerToRenderFrom = options?.owner || owner;

if (wasPassedComponent) {
context = {
ProvidedComponent: templateOrComponent,
};
templateOrComponent = INVOKE_PROVIDED_COMPONENT;
if (isComponent(templateOrComponent, owner)) {
if (
macroCondition(dependencySatisfies('ember-source', '3.25.0-beta.1'))
) {
// In 3.25+, we can treat components as one big object and just pass them around/invoke them
// wherever, so we just assign the component to the `ProvidedComponent` property and invoke it
// in the test's template
context = {
ProvidedComponent: templateOrComponent,
};
templateOrComponent = INVOKE_PROVIDED_COMPONENT;
} else {
// Below 3.25, however, we *cannot* treat components as one big object and instead have to
// register their class and template independently and then invoke them with the `component`
// helper so they can actually be found by the resolver and rendered
templateId += 1;
let name = `-undertest-${templateId}`;
let componentFullName = `component:${name}`;
let templateFullName = `template:components/${name}`;
context = {
ProvidedComponent: name,
};
ownerToRenderFrom.register(componentFullName, templateOrComponent);
templateOrComponent = hbs`{{component this.ProvidedComponent}}`;
ownerToRenderFrom.register(templateFullName, templateOrComponent);
}
} else {
templateId += 1;
let templateFullName = `template:-undertest-${templateId}`;
Expand Down
185 changes: 121 additions & 64 deletions tests/unit/setup-rendering-context-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { getOwner } from '@ember/application';
import Engine from '@ember/engine';
import { precompileTemplate } from '@ember/template-compilation';
import templateOnly from '@ember/component/template-only';
import hasEmberVersion from '@ember/test-helpers/has-ember-version';

async function buildEngineOwner(parentOwner, registry) {
parentOwner.register(
Expand Down Expand Up @@ -558,83 +559,139 @@ module('setupRenderingContext', function (hooks) {
assert.equal(getOwner(this), this.owner);
});

module('using render with a component', function () {
test('works with a template-only component', async function (assert) {
const name = 'Chris';
const template = precompileTemplate(
'<p>hello my name is {{name}}</p>',
{
scope() {
return {
name,
};
},
}
);
const component = setComponentTemplate(template, templateOnly());

await render(component);
assert.equal(
this.element.textContent,
'hello my name is Chris',
'has rendered content'
);
});
if (hasEmberVersion(3, 25)) {
// render tests for components in 3.25+ where we can use lexical scope
module('using render with a component in Ember >= 3.25', function () {
test('works with a template-only component', async function (assert) {
const name = 'Chris';
const template = precompileTemplate(
'<p>hello my name is {{name}}</p>',
{
scope() {
return {
name,
};
},
}
);
const component = setComponentTemplate(template, templateOnly());
await render(component);
assert.equal(
this.element.textContent,
'hello my name is Chris',
'has rendered content'
);
});

test('works with a glimmer component', async function (assert) {
const name = 'Chris';
const template = precompileTemplate(
'<p>hello my name is {{name}} and my favorite color is {{this.favoriteColor}}</p>',
{
scope() {
return {
name,
};
},
test('works with a glimmer component', async function (assert) {
const name = 'Chris';
const template = precompileTemplate(
'<p>hello my name is {{name}} and my favorite color is {{this.favoriteColor}}</p>',
{
scope() {
return {
name,
};
},
}
);

class Foo extends GlimmerComponent {
favoriteColor = 'red';
}
);

class Foo extends GlimmerComponent {
favoriteColor = 'red';
}
setComponentTemplate(template, Foo);
await render(Foo);

const component = setComponentTemplate(template, Foo);
assert.equal(
this.element.textContent,
'hello my name is Chris and my favorite color is red',
'has rendered content'
);
});

await render(component);
assert.equal(
this.element.textContent,
'hello my name is Chris and my favorite color is red',
'has rendered content'
);
test('works with a classic component', async function (assert) {
const name = 'Chris';
const template = precompileTemplate(
'<p>hello my name is {{name}} and my favorite color is {{this.favoriteColor}}</p>',
{
scope() {
return {
name,
};
},
}
);

const Foo = Component.extend({
favoriteColor: 'red',
});

setComponentTemplate(template, Foo);
await render(Foo);

assert.equal(
this.element.textContent,
'hello my name is Chris and my favorite color is red',
'has rendered content'
);
});
});
} else {
module('using render with a component in Ember < 3.25', function () {
test('works with a template-only component', async function (assert) {
const template = precompileTemplate(
'<p>this is a template-only component with no dynamic content</p>'
);
const component = setComponentTemplate(template, templateOnly());
await render(component);
assert.equal(
this.element.textContent,
'this is a template-only component with no dynamic content',
'has rendered content'
);
});

test('works with a classic component', async function (assert) {
const name = 'Chris';
const template = precompileTemplate(
'<p>hello my name is {{name}} and my favorite color is {{this.favoriteColor}}</p>',
{
scope() {
return {
name,
};
},
test('works with a glimmer component', async function (assert) {
const template = precompileTemplate(
'<p>hello my favorite color is {{this.favoriteColor}}</p>'
);

class Foo extends GlimmerComponent {
favoriteColor = 'red';
}
);

const Foo = Component.extend({
favoriteColor: 'red',
const component = setComponentTemplate(template, Foo);

layout: template,
await render(component);
assert.equal(
this.element.textContent,
'hello my favorite color is red',
'has rendered content'
);
});

await render(Foo);
assert.equal(
this.element.textContent,
'hello my name is Chris and my favorite color is red',
'has rendered content'
);
test('works with a classic component', async function (assert) {
const template = precompileTemplate(
'<p>hello my favorite color is {{this.favoriteColor}}</p>'
);

const Foo = Component.extend({
favoriteColor: 'red',
});

const component = setComponentTemplate(template, Foo);

await render(component);

assert.equal(
this.element.textContent,
'hello my favorite color is red',
'has rendered content'
);
});
});
});
}

module('this.render and this.clearRender deprecations', function () {
test('this.render() and this.clearRender deprecation message', async function (assert) {
Expand Down

0 comments on commit 4a3814e

Please sign in to comment.