diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3df306f0006..b200a278e50 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -202,7 +202,7 @@ jobs: strategy: fail-fast: false matrix: - scenario: [ember-lts-3.12, ember-lts-3.16] + scenario: [ember-lts-3.16, ember-lts-3.20] runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 diff --git a/packages/-ember-data/config/ember-try.js b/packages/-ember-data/config/ember-try.js index 92205ba61d8..0d78b750ac6 100644 --- a/packages/-ember-data/config/ember-try.js +++ b/packages/-ember-data/config/ember-try.js @@ -63,18 +63,18 @@ module.exports = function() { }, }, { - name: 'ember-lts-3.12', + name: 'ember-lts-3.16', npm: { devDependencies: { - 'ember-source': '~3.12.0', + 'ember-source': '~3.16.0', }, }, }, { - name: 'ember-lts-3.16', + name: 'ember-lts-3.20', npm: { devDependencies: { - 'ember-source': '~3.16.0', + 'ember-source': '~3.20.0', }, }, }, diff --git a/packages/-ember-data/tests/integration/model-errors-test.ts b/packages/-ember-data/tests/integration/model-errors-test.ts new file mode 100644 index 00000000000..ea588dfc45a --- /dev/null +++ b/packages/-ember-data/tests/integration/model-errors-test.ts @@ -0,0 +1,70 @@ +import 'qunit-dom'; // tell TS consider *.dom extension for assert + +// @ts-ignore +import { setComponentTemplate } from '@ember/component'; +import { get } from '@ember/object'; +import { render, settled } from '@ember/test-helpers'; +import Component from '@glimmer/component'; + +import { module, test } from 'qunit'; + +import { hbs } from 'ember-cli-htmlbars'; +import { setupRenderingTest } from 'ember-qunit'; +import { TestContext } from 'ember-test-helpers'; + +import Model, { attr } from '@ember-data/model'; + +class Tag extends Model { + @attr('string', {}) + name; +} + +class ErrorList extends Component<{ model: Model; field: string }> { + get errors() { + const { model, field } = this.args; + return model.errors.errorsFor(field).map(error => error.message); + } +} + +const template = hbs` + +`; + +interface CurrentTestContext extends TestContext { + tag: Tag; +} + +module('integration/model.errors', function(hooks) { + setupRenderingTest(hooks); + + hooks.beforeEach(function() { + let { owner } = this; + + owner.register('model:tag', Tag); + owner.register('component:error-list', setComponentTemplate(template, ErrorList)); + }); + + test('Model errors are autotracked', async function(this: CurrentTestContext, assert) { + this.tag = this.owner.lookup('service:store').createRecord('tag'); + // @ts-ignore + const errors = get(this.tag, 'errors'); + + await render(hbs``); + + assert.dom('.error-list__error').doesNotExist(); + + errors.add('name', 'the-error'); + await settled(); + + assert.dom('.error-list__error').hasText('the-error'); + + errors.remove('name'); + await settled(); + + assert.dom('.error-list__error').doesNotExist(); + }); +}); diff --git a/packages/model/addon/-private/errors.js b/packages/model/addon/-private/errors.js index d4d618f7dc9..b73ba6657ce 100644 --- a/packages/model/addon/-private/errors.js +++ b/packages/model/addon/-private/errors.js @@ -339,6 +339,16 @@ export default ArrayProxy.extend(DeprecatedEvented, { let content = this.rejectBy('attribute', attribute); get(this, 'content').setObjects(content); + + // Although errorsByAttributeName.delete is technically enough to sync errors state, we also + // must mutate the array as well for autotracking + let errors = this.errorsFor(attribute); + for (let i = 0; i < errors.length; i++) { + if (errors[i].attribute === attribute) { + // .replace from Ember.NativeArray is necessary. JS splice will not work. + errors.replace(i, 1); + } + } get(this, 'errorsByAttributeName').delete(attribute); this.notifyPropertyChange(attribute);