Skip to content

Commit

Permalink
[BUGFIX lts] Enable _some_ recovery of errors thrown during render.
Browse files Browse the repository at this point in the history
If `env.commit()` is not called after it has started, and another
`env.begin()` starts Glimmer throws an error:

```
Error: a glimmer transaction was begun, but one already exists. You may have a nested transaction
```

This commit works around the issue by ensuring that if a transaction had
been started, it is cleaned up even if an error was thrown during render.

(cherry picked from commit 11bec3f)
  • Loading branch information
rwjblue committed Feb 14, 2018
1 parent 8d6600b commit 8639d6a
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 0 deletions.
3 changes: 3 additions & 0 deletions packages/ember-glimmer/lib/renderer.ts
Expand Up @@ -417,6 +417,9 @@ export abstract class Renderer {
} finally {
if (!completedWithoutError) {
this._lastRevision = CURRENT_TAG.value();
if (this._env.inTransaction === true) {
this._env.commit();
}
}
this._isRenderingRoots = false;
}
Expand Down
@@ -0,0 +1,72 @@
import { set } from 'ember-metal';
import { Component } from '../../utils/helpers';
import { moduleFor, RenderingTest } from '../../utils/test-case';

moduleFor('Errors thrown during render', class extends RenderingTest {
['@test it can recover resets the transaction when an error is thrown during initial render'](assert) {
let shouldThrow = true;
let FooBarComponent = Component.extend({
init() {
this._super(...arguments);
if (shouldThrow) {
throw new Error('silly mistake in init!');
}
}
});

this.registerComponent('foo-bar', { ComponentClass: FooBarComponent, template: 'hello' });

assert.throws(() => {
this.render('{{#if switch}}{{#foo-bar}}{{foo-bar}}{{/foo-bar}}{{/if}}', { switch: true });
}, /silly mistake in init/);

assert.equal(this.env.inTransaction, false, 'should not be in a transaction even though an error was thrown');

this.assertText('');

this.runTask(() => set(this.context, 'switch', false));

shouldThrow = false;

this.runTask(() => set(this.context, 'switch', true));

this.assertText('hello');
}

['@test it can recover resets the transaction when an error is thrown during rerender'](assert) {
let shouldThrow = false;
let FooBarComponent = Component.extend({
init() {
this._super(...arguments);
if (shouldThrow) {
throw new Error('silly mistake in init!');
}
}
});

this.registerComponent('foo-bar', { ComponentClass: FooBarComponent, template: 'hello' });

this.render('{{#if switch}}{{#foo-bar}}{{foo-bar}}{{/foo-bar}}{{/if}}', { switch: true });

this.assertText('hello');

this.runTask(() => set(this.context, 'switch', false));

shouldThrow = true;

assert.throws(() => {
this.runTask(() => set(this.context, 'switch', true));
}, /silly mistake in init/);

assert.equal(this.env.inTransaction, false, 'should not be in a transaction even though an error was thrown');

this.assertText('');

this.runTask(() => set(this.context, 'switch', false));
shouldThrow = false;

this.runTask(() => set(this.context, 'switch', true));

this.assertText('hello');
}
});

0 comments on commit 8639d6a

Please sign in to comment.